Skip to content

Commit 13d9368

Browse files
committed
Correct blockReader.Skip logic
The existing logic had a rare edge case where it would seek incorrectly. Most of the time, it wouldn't skip at all.
1 parent be7d224 commit 13d9368

File tree

3 files changed

+52
-26
lines changed

3 files changed

+52
-26
lines changed

file_reader.go

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -156,25 +156,20 @@ func (f *FileReader) Seek(offset int64, whence int) (int64, error) {
156156
return f.offset, fmt.Errorf("invalid resulting offset: %d", off)
157157
}
158158

159-
if f.offset != off {
160-
f.offset = off
161-
162-
if f.blockReader != nil {
163-
// If the seek is within the next few chunks, it's much more
164-
// efficient to throw away a few bytes than to reconnect and start
165-
// a read at the new offset.
166-
err := f.blockReader.Skip(f.offset - f.blockReader.Offset)
167-
if err == nil {
168-
return f.offset, nil
169-
}
170-
171-
// It isn't possible to seek within the current block, so reset such
172-
// that we can connect to the new block.
159+
if f.blockReader != nil {
160+
// If the seek is within the next few chunks, it's much more
161+
// efficient to throw away a few bytes than to reconnect and start
162+
// a read at the new offset.
163+
err := f.blockReader.Skip(off - f.offset)
164+
if err != nil {
165+
// It isn't possible to skip forward in the current block, so reset such
166+
// that we can reconnect at the new offset.
173167
f.blockReader.Close()
174168
f.blockReader = nil
175169
}
176170
}
177171

172+
f.offset = off
178173
return f.offset, nil
179174
}
180175

file_reader_test.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@ const (
2323
testStr = "Abominable are the tumblers into which he pours his poison."
2424
testStrOff = 48847
2525

26-
testStr2 = "tumblers"
27-
testStr2Off = 48866
28-
testStr2RelativeOff = 19
26+
testStr2 = "tumblers"
27+
testStr2Off = 48866
2928

3029
testStr3 = "http://www.gutenberg.org"
3130
testStr3Off = 1256988
3231
testStr3NegativeOff = -288
3332

33+
testStr4 = "Moby Dick"
34+
testStr4Off = 34
35+
36+
testStr5 = "LEVIATHAN."
37+
testStr5Off = 8234
38+
3439
testChecksum = "27c076e4987344253650d3335a5d08ce"
3540
)
3641

@@ -250,7 +255,7 @@ func TestFileSeek(t *testing.T) {
250255
assert.EqualValues(t, testStrOff, off)
251256
br := file.blockReader
252257

253-
off, err = file.Seek(testStr2RelativeOff, 1)
258+
off, err = file.Seek((testStr2Off - testStrOff), 1)
254259
assert.NoError(t, err)
255260
assert.EqualValues(t, testStr2Off, off)
256261

@@ -263,7 +268,7 @@ func TestFileSeek(t *testing.T) {
263268
assert.EqualValues(t, len(testStr2), n)
264269
assert.EqualValues(t, testStr2, string(buf.Bytes()))
265270

266-
// now seek forward to another block and read a string
271+
// Now seek forward to another block and read.
267272
off, err = file.Seek(testStr3NegativeOff, 2)
268273
assert.NoError(t, err)
269274
assert.EqualValues(t, testStr3Off, off)
@@ -275,6 +280,33 @@ func TestFileSeek(t *testing.T) {
275280
assert.EqualValues(t, testStr3, string(buf.Bytes()))
276281
}
277282

283+
func TestFileSeekReadSkip(t *testing.T) {
284+
client := getClient(t)
285+
286+
file, err := client.Open("/_test/mobydick.txt")
287+
require.NoError(t, err)
288+
289+
buf := make([]byte, len(testStr4))
290+
n, err := file.ReadAt(buf, testStr4Off)
291+
assert.NoError(t, err)
292+
assert.Equal(t, len(buf), n)
293+
assert.Equal(t, testStr4, string(buf))
294+
br := file.blockReader
295+
296+
off, err := file.Seek(testStr5Off, 0)
297+
assert.NoError(t, err)
298+
assert.EqualValues(t, testStr5Off, off)
299+
300+
// Make sure we didn't reconnect.
301+
assert.Equal(t, br, file.blockReader)
302+
303+
buf = make([]byte, len(testStr5))
304+
n, err = io.ReadFull(file, buf)
305+
assert.NoError(t, err)
306+
assert.Equal(t, len(buf), n)
307+
assert.Equal(t, testStr5, string(buf))
308+
}
309+
278310
func TestFileReadDir(t *testing.T) {
279311
client := getClient(t)
280312

internal/transfer/block_reader.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,17 @@ func (br *BlockReader) Read(b []byte) (int, error) {
120120
// Skip attempts to discard bytes in the stream in order to skip forward. This
121121
// is an optimization for the case that the amount to skip is very small. It
122122
// returns an error if skip was not attempted at all (because the BlockReader
123-
// isn't connected, or the offset is out of bounds or too far ahead) or the seek
124-
// failed for some other reason.
125-
func (br *BlockReader) Skip(off int64) error {
123+
// isn't connected, or the resulting offset would be out of bounds or too far
124+
// ahead) or the copy failed for some other reason.
125+
func (br *BlockReader) Skip(n int64) error {
126126
blockSize := int64(br.Block.GetB().GetNumBytes())
127-
amountToSkip := off - br.Offset
127+
resultingOffset := br.Offset + n
128128

129-
if br.stream == nil || off < 0 || off >= blockSize ||
130-
amountToSkip < 0 || amountToSkip > maxSkip {
129+
if br.stream == nil || n <= 0 || n > maxSkip || resultingOffset >= blockSize {
131130
return errors.New("unable to skip")
132131
}
133132

134-
_, err := io.CopyN(io.Discard, br.stream, amountToSkip)
133+
_, err := io.CopyN(io.Discard, br.stream, n)
135134
if err != nil {
136135
if err == io.EOF {
137136
err = io.ErrUnexpectedEOF

0 commit comments

Comments
 (0)