1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:math' as math;
6import 'dart:typed_data';
7
8export 'dart:typed_data' show ByteData, Endian, Float32List, Float64List, Int32List, Int64List, Uint8List;
9
10/// Write-only buffer for incrementally building a [ByteData] instance.
11///
12/// A WriteBuffer instance can be used only once. Attempts to reuse will result
13/// in [StateError]s being thrown.
14///
15/// The byte order used is [Endian.host] throughout.
16class WriteBuffer {
17 /// Creates an interface for incrementally building a [ByteData] instance.
18 /// [startCapacity] determines the start size of the [WriteBuffer] in bytes.
19 /// The closer that value is to the real size used, the better the
20 /// performance.
21 factory WriteBuffer({int startCapacity = 8}) {
22 assert(startCapacity > 0);
23 final ByteData eightBytes = ByteData(8);
24 final Uint8List eightBytesAsList = eightBytes.buffer.asUint8List();
25 return WriteBuffer._(Uint8List(startCapacity), eightBytes, eightBytesAsList);
26 }
27
28 WriteBuffer._(this._buffer, this._eightBytes, this._eightBytesAsList);
29
30 Uint8List _buffer;
31 int _currentSize = 0;
32 bool _isDone = false;
33 final ByteData _eightBytes;
34 final Uint8List _eightBytesAsList;
35 static final Uint8List _zeroBuffer = Uint8List(8);
36
37 void _add(int byte) {
38 if (_currentSize == _buffer.length) {
39 _resize();
40 }
41 _buffer[_currentSize] = byte;
42 _currentSize += 1;
43 }
44
45 void _append(Uint8List other) {
46 final int newSize = _currentSize + other.length;
47 if (newSize >= _buffer.length) {
48 _resize(newSize);
49 }
50 _buffer.setRange(_currentSize, newSize, other);
51 _currentSize += other.length;
52 }
53
54 void _addAll(Uint8List data, [int start = 0, int? end]) {
55 final int newEnd = end ?? _eightBytesAsList.length;
56 final int newSize = _currentSize + (newEnd - start);
57 if (newSize >= _buffer.length) {
58 _resize(newSize);
59 }
60 _buffer.setRange(_currentSize, newSize, data);
61 _currentSize = newSize;
62 }
63
64 void _resize([int? requiredLength]) {
65 final int doubleLength = _buffer.length * 2;
66 final int newLength = math.max(requiredLength ?? 0, doubleLength);
67 final Uint8List newBuffer = Uint8List(newLength);
68 newBuffer.setRange(0, _buffer.length, _buffer);
69 _buffer = newBuffer;
70 }
71
72 /// Write a Uint8 into the buffer.
73 void putUint8(int byte) {
74 assert(!_isDone);
75 _add(byte);
76 }
77
78 /// Write a Uint16 into the buffer.
79 void putUint16(int value, {Endian? endian}) {
80 assert(!_isDone);
81 _eightBytes.setUint16(0, value, endian ?? Endian.host);
82 _addAll(_eightBytesAsList, 0, 2);
83 }
84
85 /// Write a Uint32 into the buffer.
86 void putUint32(int value, {Endian? endian}) {
87 assert(!_isDone);
88 _eightBytes.setUint32(0, value, endian ?? Endian.host);
89 _addAll(_eightBytesAsList, 0, 4);
90 }
91
92 /// Write an Int32 into the buffer.
93 void putInt32(int value, {Endian? endian}) {
94 assert(!_isDone);
95 _eightBytes.setInt32(0, value, endian ?? Endian.host);
96 _addAll(_eightBytesAsList, 0, 4);
97 }
98
99 /// Write an Int64 into the buffer.
100 void putInt64(int value, {Endian? endian}) {
101 assert(!_isDone);
102 _eightBytes.setInt64(0, value, endian ?? Endian.host);
103 _addAll(_eightBytesAsList, 0, 8);
104 }
105
106 /// Write an Float64 into the buffer.
107 void putFloat64(double value, {Endian? endian}) {
108 assert(!_isDone);
109 _alignTo(8);
110 _eightBytes.setFloat64(0, value, endian ?? Endian.host);
111 _addAll(_eightBytesAsList);
112 }
113
114 /// Write all the values from a [Uint8List] into the buffer.
115 void putUint8List(Uint8List list) {
116 assert(!_isDone);
117 _append(list);
118 }
119
120 /// Write all the values from an [Int32List] into the buffer.
121 void putInt32List(Int32List list) {
122 assert(!_isDone);
123 _alignTo(4);
124 _append(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length));
125 }
126
127 /// Write all the values from an [Int64List] into the buffer.
128 void putInt64List(Int64List list) {
129 assert(!_isDone);
130 _alignTo(8);
131 _append(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length));
132 }
133
134 /// Write all the values from a [Float32List] into the buffer.
135 void putFloat32List(Float32List list) {
136 assert(!_isDone);
137 _alignTo(4);
138 _append(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length));
139 }
140
141 /// Write all the values from a [Float64List] into the buffer.
142 void putFloat64List(Float64List list) {
143 assert(!_isDone);
144 _alignTo(8);
145 _append(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length));
146 }
147
148 void _alignTo(int alignment) {
149 assert(!_isDone);
150 final int mod = _currentSize % alignment;
151 if (mod != 0) {
152 _addAll(_zeroBuffer, 0, alignment - mod);
153 }
154 }
155
156 /// Finalize and return the written [ByteData].
157 ByteData done() {
158 if (_isDone) {
159 throw StateError('done() must not be called more than once on the same $runtimeType.');
160 }
161 final ByteData result = _buffer.buffer.asByteData(0, _currentSize);
162 _buffer = Uint8List(0);
163 _isDone = true;
164 return result;
165 }
166}
167
168/// Read-only buffer for reading sequentially from a [ByteData] instance.
169///
170/// The byte order used is [Endian.host] throughout.
171class ReadBuffer {
172 /// Creates a [ReadBuffer] for reading from the specified [data].
173 ReadBuffer(this.data);
174
175 /// The underlying data being read.
176 final ByteData data;
177
178 /// The position to read next.
179 int _position = 0;
180
181 /// Whether the buffer has data remaining to read.
182 bool get hasRemaining => _position < data.lengthInBytes;
183
184 /// Reads a Uint8 from the buffer.
185 int getUint8() {
186 return data.getUint8(_position++);
187 }
188
189 /// Reads a Uint16 from the buffer.
190 int getUint16({Endian? endian}) {
191 final int value = data.getUint16(_position, endian ?? Endian.host);
192 _position += 2;
193 return value;
194 }
195
196 /// Reads a Uint32 from the buffer.
197 int getUint32({Endian? endian}) {
198 final int value = data.getUint32(_position, endian ?? Endian.host);
199 _position += 4;
200 return value;
201 }
202
203 /// Reads an Int32 from the buffer.
204 int getInt32({Endian? endian}) {
205 final int value = data.getInt32(_position, endian ?? Endian.host);
206 _position += 4;
207 return value;
208 }
209
210 /// Reads an Int64 from the buffer.
211 int getInt64({Endian? endian}) {
212 final int value = data.getInt64(_position, endian ?? Endian.host);
213 _position += 8;
214 return value;
215 }
216
217 /// Reads a Float64 from the buffer.
218 double getFloat64({Endian? endian}) {
219 _alignTo(8);
220 final double value = data.getFloat64(_position, endian ?? Endian.host);
221 _position += 8;
222 return value;
223 }
224
225 /// Reads the given number of Uint8s from the buffer.
226 Uint8List getUint8List(int length) {
227 final Uint8List list = data.buffer.asUint8List(data.offsetInBytes + _position, length);
228 _position += length;
229 return list;
230 }
231
232 /// Reads the given number of Int32s from the buffer.
233 Int32List getInt32List(int length) {
234 _alignTo(4);
235 final Int32List list = data.buffer.asInt32List(data.offsetInBytes + _position, length);
236 _position += 4 * length;
237 return list;
238 }
239
240 /// Reads the given number of Int64s from the buffer.
241 Int64List getInt64List(int length) {
242 _alignTo(8);
243 final Int64List list = data.buffer.asInt64List(data.offsetInBytes + _position, length);
244 _position += 8 * length;
245 return list;
246 }
247
248 /// Reads the given number of Float32s from the buffer
249 Float32List getFloat32List(int length) {
250 _alignTo(4);
251 final Float32List list = data.buffer.asFloat32List(data.offsetInBytes + _position, length);
252 _position += 4 * length;
253 return list;
254 }
255
256 /// Reads the given number of Float64s from the buffer.
257 Float64List getFloat64List(int length) {
258 _alignTo(8);
259 final Float64List list = data.buffer.asFloat64List(data.offsetInBytes + _position, length);
260 _position += 8 * length;
261 return list;
262 }
263
264 void _alignTo(int alignment) {
265 final int mod = _position % alignment;
266 if (mod != 0) {
267 _position += alignment - mod;
268 }
269 }
270}
271