// 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). abstract mixin class ByteConversionSink implements ChunkedConversionSink> { const ByteConversionSink(); factory ByteConversionSink.withCallback( void callback(List accumulated), ) = _ByteCallbackSink; factory ByteConversionSink.from(Sink> 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 chunk, int start, int end, bool isLast) { add(chunk.sublist(start, end)); if (isLast) close(); } } /// This class provides a base-class for converters that need to accept byte /// inputs. // TODO(lrn): @Deprecated("Use ByteConversionSink instead") typedef ByteConversionSinkBase = ByteConversionSink; /// 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 ByteConversionSink { final Sink> _sink; _ByteAdapterSink(this._sink); void add(List 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 ByteConversionSink { static const _INITIAL_BUFFER_SIZE = 1024; final void Function(List) _callback; List _buffer = Uint8List(_INITIAL_BUFFER_SIZE); int _bufferIndex = 0; _ByteCallbackSink(void callback(List accumulated)) : _callback = callback; void add(Iterable chunk) { var freeCount = _buffer.length - _bufferIndex; if (chunk.length > freeCount) { // Grow the buffer. var oldLength = _buffer.length; var newLength = _roundToPowerOf2(chunk.length + oldLength) * 2; var grown = 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)); } }