Skip to content

Commit be949d9

Browse files
committed
Fix buffer bug
1 parent ec223d3 commit be949d9

File tree

2 files changed

+107
-22
lines changed

2 files changed

+107
-22
lines changed

src/GridFS/ReadableStream.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,21 @@ public function seek($offset)
186186
$this->chunkOffset = (integer) floor($offset / $this->chunkSize);
187187
$this->bufferOffset = $offset % $this->chunkSize;
188188

189+
if ($lastChunkOffset === $this->chunkOffset) {
190+
return;
191+
}
192+
193+
if ($this->chunksIterator === null) {
194+
return;
195+
}
196+
197+
// Clear the buffer since the current chunk will be changed
198+
$this->buffer = null;
199+
189200
/* If we are seeking to a previous chunk, we need to reinitialize the
190201
* chunk iterator.
191202
*/
192203
if ($lastChunkOffset > $this->chunkOffset) {
193-
$this->buffer = null;
194204
$this->chunksIterator = null;
195205
return;
196206
}
@@ -200,12 +210,8 @@ public function seek($offset)
200210
* to $this->chunkOffset.
201211
*/
202212
$numChunks = $this->chunkOffset - $lastChunkOffset;
203-
$i = 0;
204-
if ($this->chunksIterator !== null) {
205-
while ($i < $numChunks) {
206-
$this->chunksIterator->next();
207-
$i++;
208-
}
213+
for ($i = 0; $i < $numChunks; $i++) {
214+
$this->chunksIterator->next();
209215
}
210216
}
211217

tests/GridFS/ReadableStreamFunctionalTest.php

Lines changed: 94 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use MongoDB\BSON\Binary;
66
use MongoDB\GridFS\CollectionWrapper;
77
use MongoDB\GridFS\ReadableStream;
8+
use MongoDB\Tests\CommandObserver;
9+
use stdClass;
810

911
/**
1012
* Functional tests for the internal ReadableStream class.
@@ -200,31 +202,108 @@ public function testSeekOutOfRange()
200202
$stream->seek(11);
201203
}
202204

203-
public function testSeekPreviousChunk()
205+
/**
206+
* @dataProvider providePreviousChunkSeekOffsetAndBytes
207+
*/
208+
public function testSeekPreviousChunk($offset, $length, $expectedBytes)
204209
{
205210
$fileDocument = $this->collectionWrapper->findFileById('length-10');
206211
$stream = new ReadableStream($this->collectionWrapper, $fileDocument);
207212

208-
$stream->readBytes(1);
209-
$stream->seek(5);
210-
$stream->seek(2);
211-
$stream->readBytes(1);
213+
// Read to initialize and advance the chunk iterator to the last chunk
214+
$this->assertSame('abcdefghij', $stream->readBytes(10));
215+
216+
$commands = [];
217+
218+
(new CommandObserver)->observe(
219+
function() use ($stream, $offset, $length, $expectedBytes) {
220+
$stream->seek($offset);
221+
$this->assertSame($expectedBytes, $stream->readBytes($length));
222+
},
223+
function(stdClass $command) use (&$commands) {
224+
$commands[] = key((array) $command);
225+
}
226+
);
227+
228+
$this->assertSame(['find'], $commands);
212229
}
213230

214-
public function testSeekSubsequentChunk()
231+
public function providePreviousChunkSeekOffsetAndBytes()
232+
{
233+
return [
234+
[0, 4, 'abcd'],
235+
[2, 4, 'cdef'],
236+
[4, 4, 'efgh'],
237+
[6, 4, 'ghij'],
238+
];
239+
}
240+
241+
/**
242+
* @dataProvider provideSameChunkSeekOffsetAndBytes
243+
*/
244+
public function testSeekSameChunk($offset, $length, $expectedBytes)
215245
{
216246
$fileDocument = $this->collectionWrapper->findFileById('length-10');
247+
$stream = new ReadableStream($this->collectionWrapper, $fileDocument);
248+
249+
// Read to initialize and advance the chunk iterator to the middle chunk
250+
$this->assertSame('abcdef', $stream->readBytes(6));
217251

218-
$observer = $this->getMockBuilder(ReadableStream::class)
219-
->setConstructorArgs(array($this->collectionWrapper, $fileDocument))
220-
->getMock();
252+
$commands = [];
221253

222-
$observer->expects($this->never())
223-
->method('initChunksIterator');
254+
(new CommandObserver)->observe(
255+
function() use ($stream, $offset, $length, $expectedBytes) {
256+
$stream->seek($offset);
257+
$this->assertSame($expectedBytes, $stream->readBytes($length));
258+
},
259+
function(stdClass $command) use (&$commands) {
260+
$commands[] = key((array) $command);
261+
}
262+
);
224263

225-
$observer->readBytes(1);
226-
$observer->seek(5);
227-
$observer->seek(2);
228-
$observer->readBytes(1);
264+
$this->assertSame([], $commands);
265+
}
266+
267+
public function provideSameChunkSeekOffsetAndBytes()
268+
{
269+
return [
270+
[4, 4, 'efgh'],
271+
[6, 4, 'ghij'],
272+
];
273+
}
274+
275+
/**
276+
* @dataProvider provideSubsequentChunkSeekOffsetAndBytes
277+
*/
278+
public function testSeekSubsequentChunk($offset, $length, $expectedBytes)
279+
{
280+
$fileDocument = $this->collectionWrapper->findFileById('length-10');
281+
$stream = new ReadableStream($this->collectionWrapper, $fileDocument);
282+
283+
// Read to initialize the chunk iterator to the first chunk
284+
$this->assertSame('a', $stream->readBytes(1));
285+
286+
$commands = [];
287+
288+
(new CommandObserver)->observe(
289+
function() use ($stream, $offset, $length, $expectedBytes) {
290+
$stream->seek($offset);
291+
$this->assertSame($expectedBytes, $stream->readBytes($length));
292+
},
293+
function(stdClass $command) use (&$commands) {
294+
$commands[] = key((array) $command);
295+
}
296+
);
297+
298+
$this->assertSame([], $commands);
299+
}
300+
301+
public function provideSubsequentChunkSeekOffsetAndBytes()
302+
{
303+
return [
304+
[4, 4, 'efgh'],
305+
[6, 4, 'ghij'],
306+
[8, 2, 'ij'],
307+
];
229308
}
230309
}

0 commit comments

Comments
 (0)