public function substr($start, $length)
{
$document = $this->getDocument();
if ($start < 0) {
$start = max($document['length'] + $start, 0);
}
if ($start > $document['length']) {
return false;
}
if ($length < 0) {
$length = $document['length'] - $start + $length;
if ($length < 0) {
return false;
}
}
$chunkSize = $document['chunkSize'];
$startChunkNumber = floor($start / $chunkSize);
$chunkIterator = $this->getChunkIterator();
if (!$chunkIterator->valid()) {
// invalid iterator state - recreate iterator
// unable to use `rewind` due to error "Cursors cannot rewind after starting iteration"
$chunkIterator = $this->getChunkIterator(true);
}
if ($chunkIterator->key() > $startChunkNumber) {
// unable to go back by iterator
// unable to use `rewind` due to error "Cursors cannot rewind after starting iteration"
$chunkIterator = $this->getChunkIterator(true);
}
$result = '';
$chunkDataOffset = $start - $startChunkNumber * $chunkSize;
while ($chunkIterator->valid()) {
if ($chunkIterator->key() >= $startChunkNumber) {
$chunk = $chunkIterator->current();
$data = $chunk['data']->getData();
$readLength = min($chunkSize - $chunkDataOffset, $length);
$result .= StringHelper::byteSubstr($data, $chunkDataOffset, $readLength);
$length -= $readLength;
if ($length <= 0) {
break;
}
$chunkDataOffset = 0;
}
$chunkIterator->next();
}
return $result;
}