Skip to content

Commit a058925

Browse files
committed
GODRIVER-1806 Fix and test DownloadStream.Skip() (#557)
1 parent 3ffdf76 commit a058925

File tree

2 files changed

+87
-12
lines changed

2 files changed

+87
-12
lines changed

mongo/gridfs/download_stream.go

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -193,29 +193,26 @@ func (ds *DownloadStream) Skip(skip int64) (int64, error) {
193193
var err error
194194

195195
for skipped < skip {
196-
if ds.bufferStart == 0 {
196+
if ds.bufferStart >= ds.bufferEnd {
197+
// Buffer is empty and can load in data from new chunk.
197198
err = ds.fillBuffer(ctx)
198199
if err != nil {
199200
if err == errNoMoreChunks {
200201
return skipped, nil
201202
}
202-
203203
return skipped, err
204204
}
205205
}
206206

207-
// try to skip whole chunk if possible
208-
toSkip := 0
209-
if skip-skipped >= int64(len(ds.buffer)) {
210-
// can skip whole chunk
211-
toSkip = len(ds.buffer)
212-
} else {
213-
// can only skip part of buffer
214-
toSkip = int(skip - skipped)
207+
toSkip := skip - skipped
208+
// Cap the amount to skip to the remaining bytes in the buffer to be consumed.
209+
bufferRemaining := ds.bufferEnd - ds.bufferStart
210+
if toSkip > int64(bufferRemaining) {
211+
toSkip = int64(bufferRemaining)
215212
}
216213

217-
skipped += int64(toSkip)
218-
ds.bufferStart = (ds.bufferStart + toSkip) % (int(ds.chunkSize))
214+
skipped += toSkip
215+
ds.bufferStart += int(toSkip)
219216
}
220217

221218
return skip, nil

mongo/integration/gridfs_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package integration
99
import (
1010
"bytes"
1111
"context"
12+
"io"
1213
"math/rand"
1314
"runtime"
1415
"testing"
@@ -29,6 +30,83 @@ func TestGridFS(x *testing.T) {
2930
mt := mtest.New(x, noClientOpts)
3031
defer mt.Close()
3132

33+
mt.Run("skipping download", func(mt *mtest.T) {
34+
data := []byte("abc.def.ghi")
35+
var chunkSize int32 = 4
36+
37+
testcases := []struct {
38+
name string
39+
40+
read int
41+
skip int64
42+
expectedSkipN int64
43+
expectedSkipErr error
44+
expectedRemaining int
45+
}{
46+
{
47+
"read 0, skip 0", 0, 0, 0, nil, 11,
48+
},
49+
{
50+
"read 0, skip to end of chunk", 0, 4, 4, nil, 7,
51+
},
52+
{
53+
"read 0, skip 1", 0, 1, 1, nil, 10,
54+
},
55+
{
56+
"read 1, skip to end of chunk", 1, 3, 3, nil, 7,
57+
},
58+
{
59+
"read all, skip beyond", 11, 1, 0, nil, 0,
60+
},
61+
{
62+
"skip all", 0, 11, 11, nil, 0,
63+
},
64+
{
65+
"read 1, skip to last chunk", 1, 8, 8, nil, 2,
66+
},
67+
{
68+
"read to last chunk, skip to end", 9, 2, 2, nil, 0,
69+
},
70+
{
71+
"read to last chunk, skip beyond", 9, 4, 2, nil, 0,
72+
},
73+
}
74+
75+
for _, tc := range testcases {
76+
mt.Run(tc.name, func(mt *mtest.T) {
77+
bucket, err := gridfs.NewBucket(mt.DB, options.GridFSBucket().SetChunkSizeBytes(chunkSize))
78+
assert.Nil(mt, err, "NewBucket error: %v", err)
79+
80+
ustream, err := bucket.OpenUploadStream("foo")
81+
assert.Nil(mt, err, "OpenUploadStream error: %v", err)
82+
83+
id := ustream.FileID
84+
_, err = ustream.Write(data)
85+
assert.Nil(mt, err, "Write error: %v", err)
86+
err = ustream.Close()
87+
assert.Nil(mt, err, "Close error: %v", err)
88+
89+
dstream, err := bucket.OpenDownloadStream(id)
90+
assert.Nil(mt, err, "OpenDownloadStream error")
91+
dst := make([]byte, tc.read)
92+
_, err = dstream.Read(dst)
93+
assert.Nil(mt, err, "Read error: %v", err)
94+
95+
n, err := dstream.Skip(tc.skip)
96+
assert.Equal(mt, tc.expectedSkipErr, err, "expected error on Skip: %v, got %v", tc.expectedSkipErr, err)
97+
assert.Equal(mt, tc.expectedSkipN, n, "expected Skip to return: %v, got %v", tc.expectedSkipN, n)
98+
99+
// Read the rest.
100+
dst = make([]byte, len(data))
101+
remaining, err := dstream.Read(dst)
102+
if err != nil {
103+
assert.Equal(mt, err, io.EOF, "unexpected Read error: %v", err)
104+
}
105+
assert.Equal(mt, tc.expectedRemaining, remaining, "expected remaining data to be: %v, got %v", tc.expectedRemaining, remaining)
106+
})
107+
}
108+
})
109+
32110
mt.Run("index creation", func(mt *mtest.T) {
33111
// Unit tests showing that UploadFromStream creates indexes on the chunks and files collections.
34112
bucket, err := gridfs.NewBucket(mt.DB)

0 commit comments

Comments
 (0)