blob: cdcf9b3994f509f2a32699f5f34fe7df7bc144a3 [file] [log] [blame]
// 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;
}
}
}