diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ebc4799933..896e327a323 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,11 @@ [#41653]: https://github.com/dart-lang/sdk/issues/41653 +#### `dart:typed_data` + +* Class `BytesBuilder` originally from `dart:io` is now available + in `dart:typed_data`. It's still exported from `dart:io` as well. + ### Tools #### dartfmt diff --git a/sdk/lib/_internal/vm/bin/common_patch.dart b/sdk/lib/_internal/vm/bin/common_patch.dart index 9899f2e4a5a..cd9f149e080 100644 --- a/sdk/lib/_internal/vm/bin/common_patch.dart +++ b/sdk/lib/_internal/vm/bin/common_patch.dart @@ -33,7 +33,7 @@ import "dart:math" show min; import "dart:nativewrappers" show NativeFieldWrapperClass1; -import "dart:typed_data" show Uint8List; +import "dart:typed_data" show Uint8List, BytesBuilder; /// These are the additional parts of this patch library: // part "directory_patch.dart"; diff --git a/sdk/lib/internal/bytes_builder.dart b/sdk/lib/internal/bytes_builder.dart new file mode 100644 index 00000000000..ea195764d10 --- /dev/null +++ b/sdk/lib/internal/bytes_builder.dart @@ -0,0 +1,240 @@ +// 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 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(); + } +} diff --git a/sdk/lib/internal/internal.dart b/sdk/lib/internal/internal.dart index dad3b05cc14..3070b3080db 100644 --- a/sdk/lib/internal/internal.dart +++ b/sdk/lib/internal/internal.dart @@ -21,6 +21,7 @@ import 'dart:math' show Random; import 'dart:typed_data' show Uint8List; part 'async_cast.dart'; +part 'bytes_builder.dart'; part 'cast.dart'; part 'errors.dart'; part 'iterable.dart'; diff --git a/sdk/lib/internal/internal_sources.gni b/sdk/lib/internal/internal_sources.gni index 8510ab2d881..c35e678ed51 100644 --- a/sdk/lib/internal/internal_sources.gni +++ b/sdk/lib/internal/internal_sources.gni @@ -8,6 +8,7 @@ internal_sdk_sources = [ # The above file needs to be first as it lists the parts below. "async_cast.dart", + "bytes_builder.dart", "cast.dart", "errors.dart", "iterable.dart", diff --git a/sdk/lib/io/bytes_builder.dart b/sdk/lib/io/bytes_builder.dart deleted file mode 100644 index 6f13693a031..00000000000 --- a/sdk/lib/io/bytes_builder.dart +++ /dev/null @@ -1,234 +0,0 @@ -// 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.io; - -/** - * 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 class BytesBuilder { - /** - * Construct a new empty [BytesBuilder]. - * - * If [copy] is true, the data is always copied when added to the list. If - * it [copy] is false, the data is only copied if needed. That means that if - * the lists are changed after added to the [BytesBuilder], it may effect the - * output. Default is `true`. - */ - factory BytesBuilder({bool copy: true}) { - if (copy) { - return new _CopyingBytesBuilder(); - } else { - return new _BytesBuilder(); - } - } - - /** - * Appends [bytes] to the current contents of the builder. - * - * Each value of [bytes] will be bit-representation truncated to the range - * 0 .. 255. - */ - void add(List bytes); - - /** - * Append [byte] to the current contents of the builder. - * - * The [byte] will be bit-representation truncated to the range 0 .. 255. - */ - void addByte(int byte); - - /** - * Returns the contents of `this` and clears `this`. - * - * The list returned is a view of the internal buffer, limited to the - * [length]. - */ - Uint8List takeBytes(); - - /** - * Returns a copy of the current contents of the builder. - * - * Leaves the contents of the builder intact. - */ - Uint8List toBytes(); - - /** - * The number of bytes in the builder. - */ - int get length; - - /** - * Returns `true` if the buffer is empty. - */ - bool get isEmpty; - - /** - * Returns `true` if the buffer is not empty. - */ - bool get isNotEmpty; - - /** - * Clear the contents of the builder. - */ - void clear(); -} - -class _CopyingBytesBuilder implements BytesBuilder { - // Start with 1024 bytes. - static const int _initSize = 1024; - - // Safe for reuse because a fixed-length empty list is immutable. - static final _emptyList = new Uint8List(0); - - int _length = 0; - Uint8List _buffer; - - _CopyingBytesBuilder([int initialCapacity = 0]) - : _buffer = (initialCapacity <= 0) - ? _emptyList - : new Uint8List(_pow2roundup(initialCapacity)); - - void add(List bytes) { - int bytesLength = bytes.length; - if (bytesLength == 0) return; - int required = _length + bytesLength; - 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 < bytesLength; 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 = new Uint8List(newSize); - newBuffer.setRange(0, _buffer.length, _buffer); - _buffer = newBuffer; - } - - Uint8List takeBytes() { - if (_length == 0) return _emptyList; - var buffer = - new Uint8List.view(_buffer.buffer, _buffer.offsetInBytes, _length); - clear(); - return buffer; - } - - Uint8List toBytes() { - if (_length == 0) return _emptyList; - return new Uint8List.fromList( - new Uint8List.view(_buffer.buffer, _buffer.offsetInBytes, _length)); - } - - int get length => _length; - - bool get isEmpty => _length == 0; - - bool get isNotEmpty => _length != 0; - - void clear() { - _length = 0; - _buffer = _emptyList; - } - - 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; - } -} - -class _BytesBuilder implements BytesBuilder { - int _length = 0; - final List _chunks = []; - - void add(List bytes) { - Uint8List typedBytes; - if (bytes is Uint8List) { - typedBytes = bytes; - } else { - typedBytes = new Uint8List.fromList(bytes); - } - _chunks.add(typedBytes); - _length += typedBytes.length; - } - - void addByte(int byte) { - _chunks.add(new 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 = new 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 = new 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() { - _length = 0; - _chunks.clear(); - } -} diff --git a/sdk/lib/io/io.dart b/sdk/lib/io/io.dart index 7dcabd83d91..c667cfe5852 100644 --- a/sdk/lib/io/io.dart +++ b/sdk/lib/io/io.dart @@ -197,9 +197,10 @@ import 'dart:math'; import 'dart:typed_data'; export 'dart:_http'; +@Deprecated("Import BytesBuilder from dart:typed_data instead") +export 'dart:_internal' show BytesBuilder; export 'dart:_internal' show HttpStatus; -part 'bytes_builder.dart'; part 'common.dart'; part 'data_transformer.dart'; part 'directory.dart'; diff --git a/sdk/lib/io/io_sources.gni b/sdk/lib/io/io_sources.gni index 59b2ca957d0..7410a13530e 100644 --- a/sdk/lib/io/io_sources.gni +++ b/sdk/lib/io/io_sources.gni @@ -6,7 +6,6 @@ io_sdk_sources = [ "io.dart", # The above file needs to be first if additional parts are added to the lib. - "bytes_builder.dart", "common.dart", "data_transformer.dart", "directory.dart", diff --git a/sdk/lib/typed_data/typed_data.dart b/sdk/lib/typed_data/typed_data.dart index 8852ab89545..eeafdc62b08 100644 --- a/sdk/lib/typed_data/typed_data.dart +++ b/sdk/lib/typed_data/typed_data.dart @@ -14,6 +14,8 @@ library dart.typed_data; import "dart:_internal" show Since, UnmodifiableListBase; +export "dart:_internal" show BytesBuilder; + part "unmodifiable_typed_data.dart"; /** diff --git a/tests/standalone/io/bytes_builder_test.dart b/tests/lib/typed_data/bytes_builder_test.dart similarity index 98% rename from tests/standalone/io/bytes_builder_test.dart rename to tests/lib/typed_data/bytes_builder_test.dart index 5b6a6b9158e..5c1dd693de0 100644 --- a/tests/standalone/io/bytes_builder_test.dart +++ b/tests/lib/typed_data/bytes_builder_test.dart @@ -2,7 +2,6 @@ // 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. -import "dart:io"; import "dart:typed_data"; import "package:expect/expect.dart"; diff --git a/tests/standalone_2/io/bytes_builder_test.dart b/tests/lib_2/typed_data/bytes_builder_test.dart similarity index 98% rename from tests/standalone_2/io/bytes_builder_test.dart rename to tests/lib_2/typed_data/bytes_builder_test.dart index 5b6a6b9158e..5c1dd693de0 100644 --- a/tests/standalone_2/io/bytes_builder_test.dart +++ b/tests/lib_2/typed_data/bytes_builder_test.dart @@ -2,7 +2,6 @@ // 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. -import "dart:io"; import "dart:typed_data"; import "package:expect/expect.dart";