| // Copyright 2013 The Flutter Authors. 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:math' as math; |
| import 'dart:typed_data'; |
| |
| export 'dart:typed_data' |
| show |
| ByteData, |
| Endian, |
| Float32List, |
| Float64List, |
| Int32List, |
| Int64List, |
| Uint8List; |
| |
| /// Write-only buffer for incrementally building a [ByteData] instance. |
| /// |
| /// A WriteBuffer instance can be used only once. Attempts to reuse will result |
| /// in [StateError]s being thrown. |
| /// |
| /// The byte order used is [Endian.host] throughout. |
| class WriteBuffer { |
| /// Creates an interface for incrementally building a [ByteData] instance. |
| /// [startCapacity] determines the start size of the [WriteBuffer] in bytes. |
| /// The closer that value is to the real size used, the better the |
| /// performance. |
| factory WriteBuffer({int startCapacity = 8}) { |
| assert(startCapacity > 0); |
| final ByteData eightBytes = ByteData(8); |
| final Uint8List eightBytesAsList = eightBytes.buffer.asUint8List(); |
| return WriteBuffer._( |
| Uint8List(startCapacity), eightBytes, eightBytesAsList); |
| } |
| |
| WriteBuffer._(this._buffer, this._eightBytes, this._eightBytesAsList); |
| |
| Uint8List _buffer; |
| int _currentSize = 0; |
| bool _isDone = false; |
| final ByteData _eightBytes; |
| final Uint8List _eightBytesAsList; |
| static final Uint8List _zeroBuffer = Uint8List(8); |
| |
| void _add(int byte) { |
| if (_currentSize == _buffer.length) { |
| _resize(); |
| } |
| _buffer[_currentSize] = byte; |
| _currentSize += 1; |
| } |
| |
| void _append(Uint8List other) { |
| final int newSize = _currentSize + other.length; |
| if (newSize >= _buffer.length) { |
| _resize(newSize); |
| } |
| _buffer.setRange(_currentSize, newSize, other); |
| _currentSize += other.length; |
| } |
| |
| void _addAll(Uint8List data, [int start = 0, int? end]) { |
| final int newEnd = end ?? _eightBytesAsList.length; |
| final int newSize = _currentSize + (newEnd - start); |
| if (newSize >= _buffer.length) { |
| _resize(newSize); |
| } |
| _buffer.setRange(_currentSize, newSize, data); |
| _currentSize = newSize; |
| } |
| |
| void _resize([int? requiredLength]) { |
| final int doubleLength = _buffer.length * 2; |
| final int newLength = math.max(requiredLength ?? 0, doubleLength); |
| final Uint8List newBuffer = Uint8List(newLength); |
| newBuffer.setRange(0, _buffer.length, _buffer); |
| _buffer = newBuffer; |
| } |
| |
| /// Write a Uint8 into the buffer. |
| void putUint8(int byte) { |
| assert(!_isDone); |
| _add(byte); |
| } |
| |
| /// Write a Uint16 into the buffer. |
| void putUint16(int value, {Endian? endian}) { |
| assert(!_isDone); |
| _eightBytes.setUint16(0, value, endian ?? Endian.host); |
| _addAll(_eightBytesAsList, 0, 2); |
| } |
| |
| /// Write a Uint32 into the buffer. |
| void putUint32(int value, {Endian? endian}) { |
| assert(!_isDone); |
| _eightBytes.setUint32(0, value, endian ?? Endian.host); |
| _addAll(_eightBytesAsList, 0, 4); |
| } |
| |
| /// Write an Int32 into the buffer. |
| void putInt32(int value, {Endian? endian}) { |
| assert(!_isDone); |
| _eightBytes.setInt32(0, value, endian ?? Endian.host); |
| _addAll(_eightBytesAsList, 0, 4); |
| } |
| |
| /// Write an Int64 into the buffer. |
| void putInt64(int value, {Endian? endian}) { |
| assert(!_isDone); |
| _eightBytes.setInt64(0, value, endian ?? Endian.host); |
| _addAll(_eightBytesAsList, 0, 8); |
| } |
| |
| /// Write an Float64 into the buffer. |
| void putFloat64(double value, {Endian? endian}) { |
| assert(!_isDone); |
| _alignTo(8); |
| _eightBytes.setFloat64(0, value, endian ?? Endian.host); |
| _addAll(_eightBytesAsList); |
| } |
| |
| /// Write all the values from a [Uint8List] into the buffer. |
| void putUint8List(Uint8List list) { |
| assert(!_isDone); |
| _append(list); |
| } |
| |
| /// Write all the values from an [Int32List] into the buffer. |
| void putInt32List(Int32List list) { |
| assert(!_isDone); |
| _alignTo(4); |
| _append(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length)); |
| } |
| |
| /// Write all the values from an [Int64List] into the buffer. |
| void putInt64List(Int64List list) { |
| assert(!_isDone); |
| _alignTo(8); |
| _append(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); |
| } |
| |
| /// Write all the values from a [Float32List] into the buffer. |
| void putFloat32List(Float32List list) { |
| assert(!_isDone); |
| _alignTo(4); |
| _append(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length)); |
| } |
| |
| /// Write all the values from a [Float64List] into the buffer. |
| void putFloat64List(Float64List list) { |
| assert(!_isDone); |
| _alignTo(8); |
| _append(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); |
| } |
| |
| void _alignTo(int alignment) { |
| assert(!_isDone); |
| final int mod = _currentSize % alignment; |
| if (mod != 0) { |
| _addAll(_zeroBuffer, 0, alignment - mod); |
| } |
| } |
| |
| /// Finalize and return the written [ByteData]. |
| ByteData done() { |
| if (_isDone) { |
| throw StateError( |
| 'done() must not be called more than once on the same $runtimeType.'); |
| } |
| final ByteData result = _buffer.buffer.asByteData(0, _currentSize); |
| _buffer = Uint8List(0); |
| _isDone = true; |
| return result; |
| } |
| } |
| |
| /// Read-only buffer for reading sequentially from a [ByteData] instance. |
| /// |
| /// The byte order used is [Endian.host] throughout. |
| class ReadBuffer { |
| /// Creates a [ReadBuffer] for reading from the specified [data]. |
| ReadBuffer(this.data); |
| |
| /// The underlying data being read. |
| final ByteData data; |
| |
| /// The position to read next. |
| int _position = 0; |
| |
| /// Whether the buffer has data remaining to read. |
| bool get hasRemaining => _position < data.lengthInBytes; |
| |
| /// Reads a Uint8 from the buffer. |
| int getUint8() { |
| return data.getUint8(_position++); |
| } |
| |
| /// Reads a Uint16 from the buffer. |
| int getUint16({Endian? endian}) { |
| final int value = data.getUint16(_position, endian ?? Endian.host); |
| _position += 2; |
| return value; |
| } |
| |
| /// Reads a Uint32 from the buffer. |
| int getUint32({Endian? endian}) { |
| final int value = data.getUint32(_position, endian ?? Endian.host); |
| _position += 4; |
| return value; |
| } |
| |
| /// Reads an Int32 from the buffer. |
| int getInt32({Endian? endian}) { |
| final int value = data.getInt32(_position, endian ?? Endian.host); |
| _position += 4; |
| return value; |
| } |
| |
| /// Reads an Int64 from the buffer. |
| int getInt64({Endian? endian}) { |
| final int value = data.getInt64(_position, endian ?? Endian.host); |
| _position += 8; |
| return value; |
| } |
| |
| /// Reads a Float64 from the buffer. |
| double getFloat64({Endian? endian}) { |
| _alignTo(8); |
| final double value = data.getFloat64(_position, endian ?? Endian.host); |
| _position += 8; |
| return value; |
| } |
| |
| /// Reads the given number of Uint8s from the buffer. |
| Uint8List getUint8List(int length) { |
| final Uint8List list = |
| data.buffer.asUint8List(data.offsetInBytes + _position, length); |
| _position += length; |
| return list; |
| } |
| |
| /// Reads the given number of Int32s from the buffer. |
| Int32List getInt32List(int length) { |
| _alignTo(4); |
| final Int32List list = |
| data.buffer.asInt32List(data.offsetInBytes + _position, length); |
| _position += 4 * length; |
| return list; |
| } |
| |
| /// Reads the given number of Int64s from the buffer. |
| Int64List getInt64List(int length) { |
| _alignTo(8); |
| final Int64List list = |
| data.buffer.asInt64List(data.offsetInBytes + _position, length); |
| _position += 8 * length; |
| return list; |
| } |
| |
| /// Reads the given number of Float32s from the buffer |
| Float32List getFloat32List(int length) { |
| _alignTo(4); |
| final Float32List list = |
| data.buffer.asFloat32List(data.offsetInBytes + _position, length); |
| _position += 4 * length; |
| return list; |
| } |
| |
| /// Reads the given number of Float64s from the buffer. |
| Float64List getFloat64List(int length) { |
| _alignTo(8); |
| final Float64List list = |
| data.buffer.asFloat64List(data.offsetInBytes + _position, length); |
| _position += 8 * length; |
| return list; |
| } |
| |
| void _alignTo(int alignment) { |
| final int mod = _position % alignment; |
| if (mod != 0) { |
| _position += alignment - mod; |
| } |
| } |
| } |