// 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:_internal"; /// Builds a list of bytes, allowing bytes and lists of bytes to be added at the /// end. /// /// Used to efficiently collect bytes and lists of bytes. abstract interface class BytesBuilder { /// Construct a new empty [BytesBuilder]. /// /// If [copy] is true (the default), the created builder is a *copying* /// builder. A copying builder maintains its own internal buffer and copies /// the bytes added to it eagerly. /// /// If [copy] set to false, the created builder assumes that lists added /// to it will not change. /// Any [Uint8List] added using [add] is kept until /// [toBytes] or [takeBytes] is called, /// and only then are their contents copied. /// A non-[Uint8List] may be copied eagerly. /// If only a single [Uint8List] is added to the builder, /// that list is returned by [toBytes] or [takeBytes] directly, without any copying. /// A list added to a non-copying builder *should not* change its content /// after being added, and it *must not* change its length after being added. /// (Normal [Uint8List]s are fixed length lists, but growing lists implementing /// [Uint8List] exist.) factory BytesBuilder({bool copy = true}) => copy ? _CopyingBytesBuilder() : _BytesBuilder(); /// Appends [bytes] to the current contents of this builder. /// /// Each value of [bytes] will be truncated /// to an 8-bit value in the range 0 .. 255. void add(List bytes); /// Appends [byte] to the current contents of this builder. /// /// The [byte] will be truncated to an 8-bit value in the range 0 .. 255. void addByte(int byte); /// Returns the bytes currently contained in this builder and clears it. /// /// The returned list may be a view of a larger buffer. Uint8List takeBytes(); /// Returns a copy of the current byte contents of this builder. /// /// Leaves the contents of this builder intact. Uint8List toBytes(); /// The number of bytes in this builder. int get length; /// Whether the buffer is empty. bool get isEmpty; /// Whether the buffer is non-empty. bool get isNotEmpty; /// Clears the contents of this builder. /// /// The current contents are discarded and this builder becomes empty. void clear(); } /// A [BytesBuilder] which appends bytes to a growing internal buffer. class _CopyingBytesBuilder implements BytesBuilder { /// Initial size of internal buffer. static const int _initSize = 1024; /// Reusable empty [Uint8List]. /// /// Safe for reuse because a fixed-length empty list is immutable. static final _emptyList = Uint8List(0); /// Current count of bytes written to buffer. int _length = 0; /// Internal buffer accumulating bytes. /// /// Will grow as necessary Uint8List _buffer; _CopyingBytesBuilder() : _buffer = _emptyList; void add(List bytes) { int byteCount = bytes.length; if (byteCount == 0) return; int required = _length + byteCount; if (_buffer.length < required) { _grow(required); } assert(_buffer.length >= required); if (bytes is Uint8List) { _buffer.setRange(_length, required, bytes); } else { for (int i = 0; i < byteCount; i++) { _buffer[_length + i] = bytes[i]; } } _length = required; } void addByte(int byte) { if (_buffer.length == _length) { // The grow algorithm always at least doubles. // If we added one to _length it would quadruple unnecessarily. _grow(_length); } assert(_buffer.length > _length); _buffer[_length] = byte; _length++; } void _grow(int required) { // We will create a list in the range of 2-4 times larger than // required. int newSize = required * 2; if (newSize < _initSize) { newSize = _initSize; } else { newSize = _pow2roundup(newSize); } var newBuffer = Uint8List(newSize); newBuffer.setRange(0, _buffer.length, _buffer); _buffer = newBuffer; } Uint8List takeBytes() { if (_length == 0) return _emptyList; var buffer = Uint8List.view(_buffer.buffer, _buffer.offsetInBytes, _length); _clear(); return buffer; } Uint8List toBytes() { if (_length == 0) return _emptyList; return Uint8List.fromList( Uint8List.view(_buffer.buffer, _buffer.offsetInBytes, _length), ); } int get length => _length; bool get isEmpty => _length == 0; bool get isNotEmpty => _length != 0; void clear() { _clear(); } void _clear() { _length = 0; _buffer = _emptyList; } /// Rounds numbers <= 2^32 up to the nearest power of 2. static int _pow2roundup(int x) { assert(x > 0); --x; x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; return x + 1; } } /// A non-copying [BytesBuilder]. /// /// Accumulates lists of integers and lazily builds /// a collected list with all the bytes when requested. class _BytesBuilder implements BytesBuilder { int _length = 0; final List _chunks = []; void add(List bytes) { Uint8List typedBytes; if (bytes is Uint8List) { typedBytes = bytes; } else { typedBytes = Uint8List.fromList(bytes); } _chunks.add(typedBytes); _length += typedBytes.length; } void addByte(int byte) { // TODO(lrn): Optimize repeated `addByte` calls. _chunks.add(Uint8List(1)..[0] = byte); _length++; } Uint8List takeBytes() { if (_length == 0) return _CopyingBytesBuilder._emptyList; if (_chunks.length == 1) { var buffer = _chunks[0]; _clear(); return buffer; } var buffer = Uint8List(_length); int offset = 0; for (var chunk in _chunks) { buffer.setRange(offset, offset + chunk.length, chunk); offset += chunk.length; } _clear(); return buffer; } Uint8List toBytes() { if (_length == 0) return _CopyingBytesBuilder._emptyList; var buffer = Uint8List(_length); int offset = 0; for (var chunk in _chunks) { buffer.setRange(offset, offset + chunk.length, chunk); offset += chunk.length; } return buffer; } int get length => _length; bool get isEmpty => _length == 0; bool get isNotEmpty => _length != 0; void clear() { _clear(); } void _clear() { _length = 0; _chunks.clear(); } }