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 | |
5 | import 'dart:math' as math; |
6 | import 'dart:typed_data'; |
7 | |
8 | export '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. |
16 | class 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. |
171 | class 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 | |