Skip to content

Commit 0723d16

Browse files
authored
Merge pull request #6 from ahrav/optimize-seek
Optimize the seek method
2 parents bcca20e + 650e8f1 commit 0723d16

File tree

2 files changed

+60
-30
lines changed

2 files changed

+60
-30
lines changed

disk_buffer_reader.go

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -98,44 +98,46 @@ func (dbr *DiskBufferReader) Reset() error {
9898

9999
// Seek sets the offset for the next Read or Write to offset.
100100
func (dbr *DiskBufferReader) Seek(offset int64, whence int) (int64, error) {
101+
newIndex := dbr.index
102+
101103
switch whence {
102104
case io.SeekStart:
103-
switch {
104-
case offset < 0:
105-
return 0, fmt.Errorf("can not seek to before start of reader")
106-
case offset > dbr.bytesRead:
107-
trashBytes := make([]byte, offset-dbr.bytesRead)
108-
dbr.Read(trashBytes)
109-
}
110-
dbr.index = offset
105+
newIndex = offset
111106
case io.SeekCurrent:
112-
switch {
113-
case dbr.index+offset < 0:
114-
return 0, fmt.Errorf("can not seek to before start of reader")
115-
case offset > 0:
116-
trashBytes := make([]byte, offset)
117-
dbr.Read(trashBytes)
118-
}
119-
dbr.index += offset
107+
newIndex += offset
120108
case io.SeekEnd:
121-
trashBytes := make([]byte, 1024)
122-
for {
123-
_, err := dbr.Read(trashBytes)
124-
if err != nil {
125-
if errors.Is(err, io.EOF) {
126-
break
127-
}
128-
return dbr.index, err
129-
}
109+
newIndex = dbr.bytesRead + offset
110+
}
111+
112+
if newIndex < 0 {
113+
return 0, fmt.Errorf("can not seek to before start of reader")
114+
}
115+
116+
// If seeking past the bytes read and recording is on, fill the gap by reading the necessary bytes.
117+
if newIndex > dbr.bytesRead && dbr.recording {
118+
_, err := dbr.tmpFile.Seek(0, io.SeekEnd)
119+
if err != nil {
120+
return 0, err
130121
}
131-
if dbr.index+offset < 0 {
132-
return 0, fmt.Errorf("can not seek to before start of reader")
122+
123+
bytesToRead := int(newIndex - dbr.bytesRead)
124+
trashBytes := make([]byte, bytesToRead)
125+
126+
n, err := dbr.reader.Read(trashBytes)
127+
if err != nil && !errors.Is(err, io.EOF) {
128+
return 0, err
129+
}
130+
131+
m, err := dbr.tmpFile.Write(trashBytes[:n])
132+
if err != nil {
133+
return 0, err
133134
}
134-
dbr.index += offset
135-
return dbr.index, nil
135+
136+
dbr.bytesRead += int64(m)
136137
}
137138

138-
return dbr.index, nil
139+
dbr.index = newIndex
140+
return newIndex, nil
139141
}
140142

141143
// ReadAt reads len(p) bytes into p starting at offset off in the underlying input source.

disk_buffer_reader_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,31 @@ func TestReadAllLarge(t *testing.T) {
180180
t.Fatalf("Unexpected error. Got: %s, expected: %s", testErr, baseErr)
181181
}
182182
}
183+
184+
func BenchmarkSeek(b *testing.B) {
185+
// Use a fixed data source for consistent benchmarking.
186+
data := make([]byte, 100000) // Example: 100KB of data
187+
for i := range data {
188+
data[i] = byte(i % 256)
189+
}
190+
191+
tmpReader := bytes.NewBuffer(data)
192+
193+
dbr, err := New(tmpReader)
194+
if err != nil {
195+
b.Fatal(err)
196+
}
197+
defer dbr.Close()
198+
199+
whenceOptions := []int{io.SeekStart, io.SeekCurrent, io.SeekEnd}
200+
offset := int64(100) // Example offset value
201+
202+
b.ResetTimer()
203+
for i := 0; i < b.N; i++ {
204+
whence := whenceOptions[i%len(whenceOptions)] // Vary whence to cover different cases
205+
_, err := dbr.Seek(offset, whence)
206+
if err != nil {
207+
b.Fatal(err)
208+
}
209+
}
210+
}

0 commit comments

Comments
 (0)