-
Notifications
You must be signed in to change notification settings - Fork 50
Faster ChunkedStreamReader. #182
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,9 +38,34 @@ import 'byte_collector.dart' show collectBytes; | |
/// The read-operations [readChunk] and [readStream] must not be invoked until | ||
/// the future from a previous call has completed. | ||
class ChunkedStreamReader<T> { | ||
/// Iterator over underlying stream. | ||
/// | ||
/// The reader requests data from this input whenever requests on the | ||
/// reader cannot be fulfilled with the already fetched data. | ||
final StreamIterator<List<T>> _input; | ||
|
||
/// Sentinel value used for [_buffer] when we have no value. | ||
final List<T> _emptyList = const []; | ||
|
||
/// Last partially consumed chunk received from [_input]. | ||
/// | ||
/// Elements up to [_offset] have already been consumed and should not be | ||
/// consumed again. | ||
List<T> _buffer = <T>[]; | ||
|
||
/// Offset into [_buffer] after data which have already been emitted. | ||
/// | ||
/// The offset is between `0` and `_buffer.length`, both inclusive. | ||
/// The data in [_buffer] from [_offset] and forward have not yet been | ||
/// emitted by the chunked stream reader, the data before [_offset] has. | ||
int _offset = 0; | ||
jonasfj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// Whether a read request is currently being processed. | ||
/// | ||
/// Is `true` while a request is in progress. | ||
/// While a read request, like [readChunk] or [readStream], is being processed, | ||
/// no new requests can be made. | ||
/// New read attempts will throw instead. | ||
bool _reading = false; | ||
jonasfj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
factory ChunkedStreamReader(Stream<List<T>> stream) => | ||
|
@@ -96,30 +121,40 @@ class ChunkedStreamReader<T> { | |
final substream = () async* { | ||
// While we have data to read | ||
while (size > 0) { | ||
// Read something into the buffer, if it's empty | ||
if (_buffer.isEmpty) { | ||
// Read something into the buffer, if buffer has been consumed. | ||
assert(_offset <= _buffer.length); | ||
if (_offset == _buffer.length) { | ||
if (!(await _input.moveNext())) { | ||
// Don't attempt to read more data, as there is no more data. | ||
size = 0; | ||
_reading = false; | ||
break; | ||
} | ||
_buffer = _input.current; | ||
_offset = 0; | ||
} | ||
|
||
if (_buffer.isNotEmpty) { | ||
if (size < _buffer.length) { | ||
final output = _buffer.sublist(0, size); | ||
_buffer = _buffer.sublist(size); | ||
final remainingBuffer = _buffer.length - _offset; | ||
if (remainingBuffer > 0) { | ||
if (remainingBuffer >= size) { | ||
List<T> output; | ||
if (_buffer is Uint8List) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use a local variable to avoid having to do both var buffer = _buffer;
if (buffer is Uint8List) {
output = Uint8List.sublistView(buffer, _offset, _offset + size) as List<T>;
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer a specialized There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's why we have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ones that are ... are probably |
||
output = Uint8List.sublistView( | ||
_buffer as Uint8List, _offset, _offset + size) as List<T>; | ||
} else { | ||
output = _buffer.sublist(_offset, _offset + size); | ||
} | ||
_offset += size; | ||
size = 0; | ||
yield output; | ||
_reading = false; | ||
break; | ||
} | ||
|
||
final output = _buffer; | ||
size -= _buffer.length; | ||
final output = _offset == 0 ? _buffer : _buffer.sublist(_offset); | ||
size -= remainingBuffer; | ||
_buffer = _emptyList; | ||
_offset = 0; | ||
yield output; | ||
} | ||
} | ||
|
@@ -129,22 +164,26 @@ class ChunkedStreamReader<T> { | |
c.onListen = () => c.addStream(substream()).whenComplete(c.close); | ||
c.onCancel = () async { | ||
while (size > 0) { | ||
jonasfj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (_buffer.isEmpty) { | ||
assert(_offset <= _buffer.length); | ||
if (_buffer.length == _offset) { | ||
if (!await _input.moveNext()) { | ||
size = 0; // no more data | ||
break; | ||
} | ||
_buffer = _input.current; | ||
_offset = 0; | ||
} | ||
|
||
if (size < _buffer.length) { | ||
_buffer = _buffer.sublist(size); | ||
final remainingBuffer = _buffer.length - _offset; | ||
if (remainingBuffer >= size) { | ||
_offset += size; | ||
size = 0; | ||
break; | ||
} | ||
|
||
size -= _buffer.length; | ||
size -= remainingBuffer; | ||
_buffer = _emptyList; | ||
_offset = 0; | ||
} | ||
_reading = false; | ||
}; | ||
|
Uh oh!
There was an error while loading. Please reload this page.