blob: 4c1ede46e91026579325360826b5bcbf31d5b2b7 [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart.convert;
/**
* The [ByteConversionSink] provides an interface for converters to
* efficiently transmit byte data.
*
* Instead of limiting the interface to one non-chunked list of bytes it
* accepts its input in chunks (themselves being lists of bytes).
*
* This abstract class will likely get more methods over time. Implementers are
* urged to extend or mix in [ByteConversionSinkBase] to ensure that their
* class covers the newly added methods.
*/
abstract class ByteConversionSink extends ChunkedConversionSink<List<int>> {
ByteConversionSink();
factory ByteConversionSink.withCallback(void callback(List<int> accumulated))
= _ByteCallbackSink;
factory ByteConversionSink.from(Sink<List<int>> sink)
= _ByteAdapterSink;
/**
* Adds the next [chunk] to `this`.
*
* Adds the bytes defined by [start] and [end]-exclusive to `this`.
*
* If [isLast] is `true` closes `this`.
*
* Contrary to `add` the given [chunk] must not be held onto. Once the method
* returns, it is safe to overwrite the data in it.
*/
void addSlice(List<int> chunk, int start, int end, bool isLast);
// TODO(floitsch): add more methods:
// - iterateBytes.
}
/**
* This class provides a base-class for converters that need to accept byte
* inputs.
*/
abstract class ByteConversionSinkBase extends ByteConversionSink {
void add(List<int> chunk);
void close();
void addSlice(List<int> chunk, int start, int end, bool isLast) {
add(chunk.sublist(start, end));
if (isLast) close();
}
}
/**
* This class adapts a simple [Sink] to a [ByteConversionSink].
*
* All additional methods of the [ByteConversionSink] (compared to the
* ChunkedConversionSink) are redirected to the `add` method.
*/
class _ByteAdapterSink extends ByteConversionSinkBase {
final Sink<List<int>> _sink;
_ByteAdapterSink(this._sink);
void add(List<int> chunk) { _sink.add(chunk); }
void close() { _sink.close(); }
}
/**
* This class accumulates all chunks into one list of bytes
* and invokes a callback when the sink is closed.
*
* This class can be used to terminate a chunked conversion.
*/
class _ByteCallbackSink extends ByteConversionSinkBase {
static const _INITIAL_BUFFER_SIZE = 1024;
final _ChunkedConversionCallback<List<int>> _callback;
List<int> _buffer = new Uint8List(_INITIAL_BUFFER_SIZE);
int _bufferIndex = 0;
_ByteCallbackSink(void callback(List<int> accumulated))
: this._callback = callback;
void add(Iterable<int> chunk) {
int freeCount = _buffer.length - _bufferIndex;
if (chunk.length > freeCount) {
// Grow the buffer.
int oldLength = _buffer.length;
int newLength = _roundToPowerOf2(chunk.length + oldLength) * 2;
List<int> grown = new Uint8List(newLength);
grown.setRange(0, _buffer.length, _buffer);
_buffer = grown;
}
_buffer.setRange(_bufferIndex, _bufferIndex + chunk.length, chunk);
_bufferIndex += chunk.length;
}
static int _roundToPowerOf2(int v) {
assert(v > 0);
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
void close() {
_callback(_buffer.sublist(0, _bufferIndex));
}
}