blob: d27d4bf397459a19bdfdfce4bc5293c4a4a9a667 [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5import 'dart:collection';
6import 'dart:convert';
7import 'dart:math';
8import 'dart:typed_data';
9
10const int _sizeofUint8 = 1;
11const int _sizeofUint16 = 2;
12const int _sizeofUint32 = 4;
13const int _sizeofUint64 = 8;
14const int _sizeofInt8 = 1;
15const int _sizeofInt16 = 2;
16const int _sizeofInt32 = 4;
17const int _sizeofInt64 = 8;
18const int _sizeofFloat32 = 4;
19const int _sizeofFloat64 = 8;
20
21/// Callback used to invoke a struct builder's finish method.
22///
23/// This callback is used by other struct's `finish` methods to write the nested
24/// struct's fields inline.
James Kuszmaul8e62b022022-03-22 09:33:25 -070025typedef StructBuilder = void Function();
Austin Schuhe89fa2d2019-08-14 20:24:23 -070026
27/// Buffer with data and some context about it.
28class BufferContext {
29 final ByteData _buffer;
30
James Kuszmaul8e62b022022-03-22 09:33:25 -070031 ByteData get buffer => _buffer;
Austin Schuhe89fa2d2019-08-14 20:24:23 -070032
James Kuszmaul8e62b022022-03-22 09:33:25 -070033 /// Create from a FlatBuffer represented by a list of bytes (uint8).
34 factory BufferContext.fromBytes(List<int> byteList) =>
35 BufferContext(byteList is Uint8List
36 ? byteList.buffer.asByteData(byteList.offsetInBytes)
37 : ByteData.view(Uint8List.fromList(byteList).buffer));
Austin Schuhe89fa2d2019-08-14 20:24:23 -070038
James Kuszmaul8e62b022022-03-22 09:33:25 -070039 /// Create from a FlatBuffer represented by ByteData.
40 BufferContext(this._buffer);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070041
James Kuszmaul8e62b022022-03-22 09:33:25 -070042 @pragma('vm:prefer-inline')
43 int derefObject(int offset) => offset + _getUint32(offset);
44
45 @pragma('vm:prefer-inline')
46 Uint8List _asUint8List(int offset, int length) =>
Austin Schuhe89fa2d2019-08-14 20:24:23 -070047 _buffer.buffer.asUint8List(_buffer.offsetInBytes + offset, length);
48
James Kuszmaul8e62b022022-03-22 09:33:25 -070049 @pragma('vm:prefer-inline')
50 double _getFloat64(int offset) => _buffer.getFloat64(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070051
James Kuszmaul8e62b022022-03-22 09:33:25 -070052 @pragma('vm:prefer-inline')
53 double _getFloat32(int offset) => _buffer.getFloat32(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070054
James Kuszmaul8e62b022022-03-22 09:33:25 -070055 @pragma('vm:prefer-inline')
56 int _getInt64(int offset) => _buffer.getInt64(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070057
James Kuszmaul8e62b022022-03-22 09:33:25 -070058 @pragma('vm:prefer-inline')
59 int _getInt32(int offset) => _buffer.getInt32(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070060
James Kuszmaul8e62b022022-03-22 09:33:25 -070061 @pragma('vm:prefer-inline')
62 int _getInt16(int offset) => _buffer.getInt16(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070063
James Kuszmaul8e62b022022-03-22 09:33:25 -070064 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -070065 int _getInt8(int offset) => _buffer.getInt8(offset);
66
James Kuszmaul8e62b022022-03-22 09:33:25 -070067 @pragma('vm:prefer-inline')
68 int _getUint64(int offset) => _buffer.getUint64(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070069
James Kuszmaul8e62b022022-03-22 09:33:25 -070070 @pragma('vm:prefer-inline')
71 int _getUint32(int offset) => _buffer.getUint32(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070072
James Kuszmaul8e62b022022-03-22 09:33:25 -070073 @pragma('vm:prefer-inline')
74 int _getUint16(int offset) => _buffer.getUint16(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070075
James Kuszmaul8e62b022022-03-22 09:33:25 -070076 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -070077 int _getUint8(int offset) => _buffer.getUint8(offset);
James Kuszmaul8e62b022022-03-22 09:33:25 -070078}
Austin Schuhe89fa2d2019-08-14 20:24:23 -070079
James Kuszmaul8e62b022022-03-22 09:33:25 -070080/// Interface implemented by the "object-api" classes (ending with "T").
81abstract class Packable {
82 /// Serialize the object using the given builder, returning the offset.
83 int pack(Builder fbBuilder);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070084}
85
86/// Class implemented by typed builders generated by flatc.
87abstract class ObjectBuilder {
James Kuszmaul8e62b022022-03-22 09:33:25 -070088 int? _firstOffset;
Austin Schuhe89fa2d2019-08-14 20:24:23 -070089
90 /// Can be used to write the data represented by this builder to the [Builder]
91 /// and reuse the offset created in multiple tables.
92 ///
93 /// Note that this method assumes you call it using the same [Builder] instance
94 /// every time. The returned offset is only good for the [Builder] used in the
95 /// first call to this method.
96 int getOrCreateOffset(Builder fbBuilder) {
97 _firstOffset ??= finish(fbBuilder);
James Kuszmaul8e62b022022-03-22 09:33:25 -070098 return _firstOffset!;
Austin Schuhe89fa2d2019-08-14 20:24:23 -070099 }
100
101 /// Writes the data in this helper to the [Builder].
102 int finish(Builder fbBuilder);
103
104 /// Convenience method that will create a new [Builder], [finish]es the data,
105 /// and returns the buffer as a [Uint8List] of bytes.
106 Uint8List toBytes();
107}
108
109/// Class that helps building flat buffers.
110class Builder {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700111 bool _finished = false;
112
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700113 final int initialSize;
114
115 /// The list of existing VTable(s).
James Kuszmaul8e62b022022-03-22 09:33:25 -0700116 final List<int> _vTables;
117
118 final bool deduplicateTables;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700119
120 ByteData _buf;
121
James Kuszmaul8e62b022022-03-22 09:33:25 -0700122 final Allocator _allocator;
123
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700124 /// The maximum alignment that has been seen so far. If [_buf] has to be
125 /// reallocated in the future (to insert room at its start for more bytes) the
126 /// reallocation will need to be a multiple of this many bytes.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700127 int _maxAlign = 1;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700128
129 /// The number of bytes that have been written to the buffer so far. The
130 /// most recently written byte is this many bytes from the end of [_buf].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700131 int _tail = 0;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700132
133 /// The location of the end of the current table, measured in bytes from the
James Kuszmaul8e62b022022-03-22 09:33:25 -0700134 /// end of [_buf].
135 int _currentTableEndTail = 0;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700136
James Kuszmaul8e62b022022-03-22 09:33:25 -0700137 _VTable? _currentVTable;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700138
139 /// Map containing all strings that have been written so far. This allows us
140 /// to avoid duplicating strings.
141 ///
142 /// Allocated only if `internStrings` is set to true on the constructor.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700143 Map<String, int>? _strings;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700144
145 /// Creates a new FlatBuffers Builder.
146 ///
147 /// `initialSize` is the initial array size in bytes. The [Builder] will
148 /// automatically grow the array if/as needed. `internStrings`, if set to
149 /// true, will cause [writeString] to pool strings in the buffer so that
150 /// identical strings will always use the same offset in tables.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700151 Builder({
152 this.initialSize = 1024,
153 bool internStrings = false,
154 Allocator allocator = const DefaultAllocator(),
155 this.deduplicateTables = true,
156 }) : _allocator = allocator,
157 _buf = allocator.allocate(initialSize),
158 _vTables = deduplicateTables ? [] : const [] {
159 if (internStrings) {
160 _strings = <String, int>{};
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700161 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700162 }
163
James Kuszmaul8e62b022022-03-22 09:33:25 -0700164 /// Calculate the finished buffer size (aligned).
165 @pragma('vm:prefer-inline')
166 int size() => _tail + ((-_tail) & (_maxAlign - 1));
167
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700168 /// Add the [field] with the given boolean [value]. The field is not added if
169 /// the [value] is equal to [def]. Booleans are stored as 8-bit fields with
170 /// `0` for `false` and `1` for `true`.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700171 void addBool(int field, bool? value, [bool? def]) {
172 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700173 if (value != null && value != def) {
174 _prepare(_sizeofUint8, 1);
175 _trackField(field);
176 _buf.setInt8(_buf.lengthInBytes - _tail, value ? 1 : 0);
177 }
178 }
179
180 /// Add the [field] with the given 32-bit signed integer [value]. The field is
181 /// not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700182 void addInt32(int field, int? value, [int? def]) {
183 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700184 if (value != null && value != def) {
185 _prepare(_sizeofInt32, 1);
186 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700187 _setInt32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700188 }
189 }
190
191 /// Add the [field] with the given 32-bit signed integer [value]. The field is
192 /// not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700193 void addInt16(int field, int? value, [int? def]) {
194 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700195 if (value != null && value != def) {
196 _prepare(_sizeofInt16, 1);
197 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700198 _setInt16AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700199 }
200 }
201
202 /// Add the [field] with the given 8-bit signed integer [value]. The field is
203 /// not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700204 void addInt8(int field, int? value, [int? def]) {
205 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700206 if (value != null && value != def) {
207 _prepare(_sizeofInt8, 1);
208 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700209 _setInt8AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700210 }
211 }
212
213 void addStruct(int field, int offset) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700214 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700215 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700216 _currentVTable!.addField(field, offset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700217 }
218
219 /// Add the [field] referencing an object with the given [offset].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700220 void addOffset(int field, int? offset) {
221 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700222 if (offset != null) {
223 _prepare(_sizeofUint32, 1);
224 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700225 _setUint32AtTail(_tail, _tail - offset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700226 }
227 }
228
229 /// Add the [field] with the given 32-bit unsigned integer [value]. The field
230 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700231 void addUint32(int field, int? value, [int? def]) {
232 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700233 if (value != null && value != def) {
234 _prepare(_sizeofUint32, 1);
235 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700236 _setUint32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700237 }
238 }
239
240 /// Add the [field] with the given 32-bit unsigned integer [value]. The field
241 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700242 void addUint16(int field, int? value, [int? def]) {
243 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700244 if (value != null && value != def) {
245 _prepare(_sizeofUint16, 1);
246 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700247 _setUint16AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700248 }
249 }
250
251 /// Add the [field] with the given 8-bit unsigned integer [value]. The field
252 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700253 void addUint8(int field, int? value, [int? def]) {
254 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700255 if (value != null && value != def) {
256 _prepare(_sizeofUint8, 1);
257 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700258 _setUint8AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700259 }
260 }
261
262 /// Add the [field] with the given 32-bit float [value]. The field
263 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700264 void addFloat32(int field, double? value, [double? def]) {
265 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700266 if (value != null && value != def) {
267 _prepare(_sizeofFloat32, 1);
268 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700269 _setFloat32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700270 }
271 }
272
273 /// Add the [field] with the given 64-bit double [value]. The field
274 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700275 void addFloat64(int field, double? value, [double? def]) {
276 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700277 if (value != null && value != def) {
278 _prepare(_sizeofFloat64, 1);
279 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700280 _setFloat64AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700281 }
282 }
283
284 /// Add the [field] with the given 64-bit unsigned integer [value]. The field
285 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700286 void addUint64(int field, int? value, [double? def]) {
287 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700288 if (value != null && value != def) {
289 _prepare(_sizeofUint64, 1);
290 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700291 _setUint64AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700292 }
293 }
294
295 /// Add the [field] with the given 64-bit unsigned integer [value]. The field
296 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700297 void addInt64(int field, int? value, [double? def]) {
298 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700299 if (value != null && value != def) {
300 _prepare(_sizeofInt64, 1);
301 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700302 _setInt64AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700303 }
304 }
305
306 /// End the current table and return its offset.
307 int endTable() {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700308 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700309 // Prepare for writing the VTable.
310 _prepare(_sizeofInt32, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700311 var tableTail = _tail;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700312 // Prepare the size of the current table.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700313 final currentVTable = _currentVTable!;
314 currentVTable.tableSize = tableTail - _currentTableEndTail;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700315 // Prepare the VTable to use for the current table.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700316 int? vTableTail;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700317 {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700318 currentVTable.computeFieldOffsets(tableTail);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700319
James Kuszmaul8e62b022022-03-22 09:33:25 -0700320 // Try to find an existing compatible VTable.
321 if (deduplicateTables) {
322 // Search backward - more likely to have recently used one
323 for (var i = _vTables.length - 1; i >= 0; i--) {
324 final vt2Offset = _vTables[i];
325 final vt2Start = _buf.lengthInBytes - vt2Offset;
326 final vt2Size = _buf.getUint16(vt2Start, Endian.little);
327
328 if (currentVTable._vTableSize == vt2Size &&
329 currentVTable._offsetsMatch(vt2Start, _buf)) {
330 vTableTail = vt2Offset;
331 break;
332 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700333 }
334 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700335
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700336 // Write a new VTable.
337 if (vTableTail == null) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700338 _prepare(_sizeofUint16, _currentVTable!.numOfUint16);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700339 vTableTail = _tail;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700340 currentVTable.tail = vTableTail;
341 currentVTable.output(_buf, _buf.lengthInBytes - _tail);
342 if (deduplicateTables) _vTables.add(currentVTable.tail);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700343 }
344 }
345 // Set the VTable offset.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700346 _setInt32AtTail(tableTail, vTableTail - tableTail);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700347 // Done with this table.
348 _currentVTable = null;
349 return tableTail;
350 }
351
James Kuszmaul8e62b022022-03-22 09:33:25 -0700352 /// Returns the finished buffer. You must call [finish] before accessing this.
353 @pragma('vm:prefer-inline')
354 Uint8List get buffer {
355 assert(_finished);
356 final finishedSize = size();
357 return _buf.buffer
358 .asUint8List(_buf.lengthInBytes - finishedSize, finishedSize);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700359 }
360
361 /// Finish off the creation of the buffer. The given [offset] is used as the
362 /// root object offset, and usually references directly or indirectly every
363 /// written object. If [fileIdentifier] is specified (and not `null`), it is
364 /// interpreted as a 4-byte Latin-1 encoded string that should be placed at
365 /// bytes 4-7 of the file.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700366 void finish(int offset, [String? fileIdentifier]) {
367 final sizeBeforePadding = size();
368 final requiredBytes = _sizeofUint32 * (fileIdentifier == null ? 1 : 2);
369 _prepare(max(requiredBytes, _maxAlign), 1);
370 final finishedSize = size();
371 _setUint32AtTail(finishedSize, finishedSize - offset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700372 if (fileIdentifier != null) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700373 for (var i = 0; i < 4; i++) {
374 _setUint8AtTail(
375 finishedSize - _sizeofUint32 - i, fileIdentifier.codeUnitAt(i));
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700376 }
377 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700378
379 // zero out the added padding
380 for (var i = sizeBeforePadding + 1;
381 i <= finishedSize - requiredBytes;
382 i++) {
383 _setUint8AtTail(i, 0);
384 }
385 _finished = true;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700386 }
387
388 /// Writes a Float64 to the tail of the buffer after preparing space for it.
389 ///
390 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
391 void putFloat64(double value) {
392 _prepare(_sizeofFloat64, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700393 _setFloat32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700394 }
395
396 /// Writes a Float32 to the tail of the buffer after preparing space for it.
397 ///
398 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
399 void putFloat32(double value) {
400 _prepare(_sizeofFloat32, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700401 _setFloat32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700402 }
403
404 /// Writes a Int64 to the tail of the buffer after preparing space for it.
405 ///
406 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
407 void putInt64(int value) {
408 _prepare(_sizeofInt64, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700409 _setInt64AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700410 }
411
412 /// Writes a Uint32 to the tail of the buffer after preparing space for it.
413 ///
414 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
415 void putInt32(int value) {
416 _prepare(_sizeofInt32, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700417 _setInt32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700418 }
419
420 /// Writes a Uint16 to the tail of the buffer after preparing space for it.
421 ///
422 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
423 void putInt16(int value) {
424 _prepare(_sizeofInt16, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700425 _setInt16AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700426 }
427
428 /// Writes a Uint8 to the tail of the buffer after preparing space for it.
429 ///
430 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
431 void putInt8(int value) {
432 _prepare(_sizeofInt8, 1);
433 _buf.setInt8(_buf.lengthInBytes - _tail, value);
434 }
435
436 /// Writes a Uint64 to the tail of the buffer after preparing space for it.
437 ///
438 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
439 void putUint64(int value) {
440 _prepare(_sizeofUint64, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700441 _setUint64AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700442 }
443
444 /// Writes a Uint32 to the tail of the buffer after preparing space for it.
445 ///
446 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
447 void putUint32(int value) {
448 _prepare(_sizeofUint32, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700449 _setUint32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700450 }
451
452 /// Writes a Uint16 to the tail of the buffer after preparing space for it.
453 ///
454 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
455 void putUint16(int value) {
456 _prepare(_sizeofUint16, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700457 _setUint16AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700458 }
459
460 /// Writes a Uint8 to the tail of the buffer after preparing space for it.
461 ///
462 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
463 void putUint8(int value) {
464 _prepare(_sizeofUint8, 1);
465 _buf.setUint8(_buf.lengthInBytes - _tail, value);
466 }
467
468 /// Reset the builder and make it ready for filling a new buffer.
469 void reset() {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700470 _finished = false;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700471 _maxAlign = 1;
472 _tail = 0;
473 _currentVTable = null;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700474 if (deduplicateTables) _vTables.clear();
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700475 if (_strings != null) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700476 _strings = <String, int>{};
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700477 }
478 }
479
James Kuszmaul8e62b022022-03-22 09:33:25 -0700480 /// Start a new table. Must be finished with [endTable] invocation.
481 void startTable(int numFields) {
482 assert(!_inVTable); // Inline tables are not supported.
483 _currentVTable = _VTable(numFields);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700484 _currentTableEndTail = _tail;
485 }
486
487 /// Finish a Struct vector. Most callers should preferto use [writeListOfStructs].
488 ///
489 /// Most callers should prefer [writeListOfStructs].
490 int endStructVector(int count) {
491 putUint32(count);
492 return _tail;
493 }
494
495 /// Writes a list of Structs to the buffer, returning the offset
496 int writeListOfStructs(List<ObjectBuilder> structBuilders) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700497 assert(!_inVTable);
498 for (var i = structBuilders.length - 1; i >= 0; i--) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700499 structBuilders[i].finish(this);
500 }
501 return endStructVector(structBuilders.length);
502 }
503
504 /// Write the given list of [values].
505 int writeList(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700506 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700507 _prepare(_sizeofUint32, 1 + values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700508 final result = _tail;
509 var tail = _tail;
510 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700511 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700512 for (var value in values) {
513 _setUint32AtTail(tail, tail - value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700514 tail -= _sizeofUint32;
515 }
516 return result;
517 }
518
519 /// Write the given list of 64-bit float [values].
520 int writeListFloat64(List<double> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700521 assert(!_inVTable);
Austin Schuh272c6132020-11-14 16:37:52 -0800522 _prepare(_sizeofFloat64, values.length, additionalBytes: _sizeofUint32);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700523 final result = _tail;
524 var tail = _tail;
525 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700526 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700527 for (var value in values) {
528 _setFloat64AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700529 tail -= _sizeofFloat64;
530 }
531 return result;
532 }
533
534 /// Write the given list of 32-bit float [values].
535 int writeListFloat32(List<double> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700536 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700537 _prepare(_sizeofFloat32, 1 + values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700538 final result = _tail;
539 var tail = _tail;
540 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700541 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700542 for (var value in values) {
543 _setFloat32AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700544 tail -= _sizeofFloat32;
545 }
546 return result;
547 }
548
549 /// Write the given list of signed 64-bit integer [values].
550 int writeListInt64(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700551 assert(!_inVTable);
Austin Schuh272c6132020-11-14 16:37:52 -0800552 _prepare(_sizeofInt64, values.length, additionalBytes: _sizeofUint32);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700553 final result = _tail;
554 var tail = _tail;
555 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700556 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700557 for (var value in values) {
558 _setInt64AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700559 tail -= _sizeofInt64;
560 }
561 return result;
562 }
563
564 /// Write the given list of signed 64-bit integer [values].
565 int writeListUint64(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700566 assert(!_inVTable);
Austin Schuh272c6132020-11-14 16:37:52 -0800567 _prepare(_sizeofUint64, values.length, additionalBytes: _sizeofUint32);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700568 final result = _tail;
569 var tail = _tail;
570 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700571 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700572 for (var value in values) {
573 _setUint64AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700574 tail -= _sizeofUint64;
575 }
576 return result;
577 }
578
579 /// Write the given list of signed 32-bit integer [values].
580 int writeListInt32(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700581 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700582 _prepare(_sizeofUint32, 1 + values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700583 final result = _tail;
584 var tail = _tail;
585 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700586 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700587 for (var value in values) {
588 _setInt32AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700589 tail -= _sizeofInt32;
590 }
591 return result;
592 }
593
594 /// Write the given list of unsigned 32-bit integer [values].
595 int writeListUint32(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700596 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700597 _prepare(_sizeofUint32, 1 + values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700598 final result = _tail;
599 var tail = _tail;
600 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700601 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700602 for (var value in values) {
603 _setUint32AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700604 tail -= _sizeofUint32;
605 }
606 return result;
607 }
608
609 /// Write the given list of signed 16-bit integer [values].
610 int writeListInt16(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700611 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700612 _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700613 final result = _tail;
614 var tail = _tail;
615 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700616 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700617 for (var value in values) {
618 _setInt16AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700619 tail -= _sizeofInt16;
620 }
621 return result;
622 }
623
624 /// Write the given list of unsigned 16-bit integer [values].
625 int writeListUint16(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700626 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700627 _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700628 final result = _tail;
629 var tail = _tail;
630 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700631 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700632 for (var value in values) {
633 _setUint16AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700634 tail -= _sizeofUint16;
635 }
636 return result;
637 }
638
639 /// Write the given list of bools as unsigend 8-bit integer [values].
640 int writeListBool(List<bool> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700641 return writeListUint8(values.map((b) => b ? 1 : 0).toList());
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700642 }
643
644 /// Write the given list of signed 8-bit integer [values].
645 int writeListInt8(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700646 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700647 _prepare(_sizeofUint32, 1, additionalBytes: values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700648 final result = _tail;
649 var tail = _tail;
650 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700651 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700652 for (var value in values) {
653 _setInt8AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700654 tail -= _sizeofUint8;
655 }
656 return result;
657 }
658
659 /// Write the given list of unsigned 8-bit integer [values].
660 int writeListUint8(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700661 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700662 _prepare(_sizeofUint32, 1, additionalBytes: values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700663 final result = _tail;
664 var tail = _tail;
665 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700666 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700667 for (var value in values) {
668 _setUint8AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700669 tail -= _sizeofUint8;
670 }
671 return result;
672 }
673
James Kuszmaul8e62b022022-03-22 09:33:25 -0700674 /// Write the given string [value] and return its offset.
675 ///
676 /// Dart strings are UTF-16 but must be stored as UTF-8 in FlatBuffers.
677 /// If the given string consists only of ASCII characters, you can indicate
678 /// enable [asciiOptimization]. In this mode, [writeString()] first tries to
679 /// copy the ASCII string directly to the output buffer and if that fails
680 /// (because there are no-ASCII characters in the string) it falls back and to
681 /// the default UTF-16 -> UTF-8 conversion (with slight performance penalty).
682 int writeString(String value, {bool asciiOptimization = false}) {
683 assert(!_inVTable);
684 if (_strings != null) {
685 return _strings!
686 .putIfAbsent(value, () => _writeString(value, asciiOptimization));
687 } else {
688 return _writeString(value, asciiOptimization);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700689 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700690 }
691
James Kuszmaul8e62b022022-03-22 09:33:25 -0700692 int _writeString(String value, bool asciiOptimization) {
693 if (asciiOptimization) {
694 // [utf8.encode()] is slow (up to at least Dart SDK 2.13). If the given
695 // string is ASCII we can just write it directly, without any conversion.
696 final originalTail = _tail;
697 if (_tryWriteASCIIString(value)) return _tail;
698 // if non-ASCII: reset the output buffer position for [_writeUTFString()]
699 _tail = originalTail;
700 }
701 _writeUTFString(value);
702 return _tail;
703 }
704
705 // Try to write the string as ASCII, return false if there's a non-ascii char.
706 @pragma('vm:prefer-inline')
707 bool _tryWriteASCIIString(String value) {
708 _prepare(4, 1, additionalBytes: value.length + 1);
709 final length = value.length;
710 var offset = _buf.lengthInBytes - _tail + 4;
711 for (var i = 0; i < length; i++) {
712 // utf16 code unit, e.g. for '†' it's [0x20 0x20], which is 8224 decimal.
713 // ASCII characters go from 0x00 to 0x7F (which is 0 to 127 decimal).
714 final char = value.codeUnitAt(i);
715 if ((char & ~0x7F) != 0) {
716 return false;
717 }
718 _buf.setUint8(offset++, char);
719 }
720 _buf.setUint8(offset, 0); // trailing zero
721 _setUint32AtTail(_tail, value.length);
722 return true;
723 }
724
725 @pragma('vm:prefer-inline')
726 void _writeUTFString(String value) {
727 final bytes = utf8.encode(value) as Uint8List;
728 final length = bytes.length;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700729 _prepare(4, 1, additionalBytes: length + 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700730 _setUint32AtTail(_tail, length);
731 var offset = _buf.lengthInBytes - _tail + 4;
732 for (var i = 0; i < length; i++) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700733 _buf.setUint8(offset++, bytes[i]);
734 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700735 _buf.setUint8(offset, 0); // trailing zero
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700736 }
737
James Kuszmaul8e62b022022-03-22 09:33:25 -0700738 /// Used to assert whether a "Table" is currently being built.
739 ///
740 /// If you hit `assert(!_inVTable())`, you're trying to add table fields
741 /// without starting a table with [Builder.startTable()].
742 ///
743 /// If you hit `assert(_inVTable())`, you're trying to construct a
744 /// Table/Vector/String during the construction of its parent table,
745 /// between the MyTableBuilder and [Builder.endTable()].
746 /// Move the creation of these sub-objects to before the MyTableBuilder to
747 /// not get this assert.
748 @pragma('vm:prefer-inline')
749 bool get _inVTable => _currentVTable != null;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700750
751 /// The number of bytes that have been written to the buffer so far. The
752 /// most recently written byte is this many bytes from the end of the buffer.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700753 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700754 int get offset => _tail;
755
756 /// Zero-pads the buffer, which may be required for some struct layouts.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700757 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700758 void pad(int howManyBytes) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700759 for (var i = 0; i < howManyBytes; i++) {
760 putUint8(0);
761 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700762 }
763
764 /// Prepare for writing the given `count` of scalars of the given `size`.
765 /// Additionally allocate the specified `additionalBytes`. Update the current
766 /// tail pointer to point at the allocated space.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700767 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700768 void _prepare(int size, int count, {int additionalBytes = 0}) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700769 assert(!_finished);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700770 // Update the alignment.
771 if (_maxAlign < size) {
772 _maxAlign = size;
773 }
774 // Prepare amount of required space.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700775 var dataSize = size * count + additionalBytes;
776 var alignDelta = (-(_tail + dataSize)) & (size - 1);
777 var bufSize = alignDelta + dataSize;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700778 // Ensure that we have the required amount of space.
779 {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700780 var oldCapacity = _buf.lengthInBytes;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700781 if (_tail + bufSize > oldCapacity) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700782 var desiredNewCapacity = (oldCapacity + bufSize) * 2;
783 var deltaCapacity = desiredNewCapacity - oldCapacity;
784 deltaCapacity += (-deltaCapacity) & (_maxAlign - 1);
785 var newCapacity = oldCapacity + deltaCapacity;
786 _buf = _allocator.resize(_buf, newCapacity, _tail, 0);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700787 }
788 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700789
790 // zero out the added padding
791 for (var i = _tail + 1; i <= _tail + alignDelta; i++) {
792 _setUint8AtTail(i, 0);
793 }
794
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700795 // Update the tail pointer.
796 _tail += bufSize;
797 }
798
799 /// Record the offset of the given [field].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700800 @pragma('vm:prefer-inline')
801 void _trackField(int field) => _currentVTable!.addField(field, _tail);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700802
James Kuszmaul8e62b022022-03-22 09:33:25 -0700803 @pragma('vm:prefer-inline')
804 void _setFloat64AtTail(int tail, double x) =>
805 _buf.setFloat64(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700806
James Kuszmaul8e62b022022-03-22 09:33:25 -0700807 @pragma('vm:prefer-inline')
808 void _setFloat32AtTail(int tail, double x) =>
809 _buf.setFloat32(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700810
James Kuszmaul8e62b022022-03-22 09:33:25 -0700811 @pragma('vm:prefer-inline')
812 void _setUint64AtTail(int tail, int x) =>
813 _buf.setUint64(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700814
James Kuszmaul8e62b022022-03-22 09:33:25 -0700815 @pragma('vm:prefer-inline')
816 void _setInt64AtTail(int tail, int x) =>
817 _buf.setInt64(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700818
James Kuszmaul8e62b022022-03-22 09:33:25 -0700819 @pragma('vm:prefer-inline')
820 void _setInt32AtTail(int tail, int x) =>
821 _buf.setInt32(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700822
James Kuszmaul8e62b022022-03-22 09:33:25 -0700823 @pragma('vm:prefer-inline')
824 void _setUint32AtTail(int tail, int x) =>
825 _buf.setUint32(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700826
James Kuszmaul8e62b022022-03-22 09:33:25 -0700827 @pragma('vm:prefer-inline')
828 void _setInt16AtTail(int tail, int x) =>
829 _buf.setInt16(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700830
James Kuszmaul8e62b022022-03-22 09:33:25 -0700831 @pragma('vm:prefer-inline')
832 void _setUint16AtTail(int tail, int x) =>
833 _buf.setUint16(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700834
James Kuszmaul8e62b022022-03-22 09:33:25 -0700835 @pragma('vm:prefer-inline')
836 void _setInt8AtTail(int tail, int x) =>
837 _buf.setInt8(_buf.lengthInBytes - tail, x);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700838
James Kuszmaul8e62b022022-03-22 09:33:25 -0700839 @pragma('vm:prefer-inline')
840 void _setUint8AtTail(int tail, int x) =>
841 _buf.setUint8(_buf.lengthInBytes - tail, x);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700842}
843
844/// Reader of lists of boolean values.
845///
846/// The returned unmodifiable lists lazily read values on access.
847class BoolListReader extends Reader<List<bool>> {
848 const BoolListReader();
849
850 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700851 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700852 int get size => _sizeofUint32;
853
854 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700855 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700856 List<bool> read(BufferContext bc, int offset) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -0700857 _FbBoolList(bc, bc.derefObject(offset));
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700858}
859
860/// The reader of booleans.
861class BoolReader extends Reader<bool> {
862 const BoolReader() : super();
863
864 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700865 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700866 int get size => _sizeofUint8;
867
868 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700869 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700870 bool read(BufferContext bc, int offset) => bc._getInt8(offset) != 0;
871}
872
873/// The reader of lists of 64-bit float values.
874///
875/// The returned unmodifiable lists lazily read values on access.
876class Float64ListReader extends Reader<List<double>> {
877 const Float64ListReader();
878
879 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700880 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700881 int get size => _sizeofFloat64;
882
883 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700884 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700885 List<double> read(BufferContext bc, int offset) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -0700886 _FbFloat64List(bc, bc.derefObject(offset));
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700887}
888
889class Float32ListReader extends Reader<List<double>> {
890 const Float32ListReader();
891
892 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700893 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700894 int get size => _sizeofFloat32;
895
896 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700897 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700898 List<double> read(BufferContext bc, int offset) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -0700899 _FbFloat32List(bc, bc.derefObject(offset));
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700900}
901
902class Float64Reader extends Reader<double> {
903 const Float64Reader();
904
905 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700906 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700907 int get size => _sizeofFloat64;
908
909 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700910 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700911 double read(BufferContext bc, int offset) => bc._getFloat64(offset);
912}
913
914class Float32Reader extends Reader<double> {
915 const Float32Reader();
916
917 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700918 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700919 int get size => _sizeofFloat32;
920
921 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700922 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700923 double read(BufferContext bc, int offset) => bc._getFloat32(offset);
924}
925
926class Int64Reader extends Reader<int> {
927 const Int64Reader() : super();
James Kuszmaul8e62b022022-03-22 09:33:25 -0700928
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700929 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700930 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700931 int get size => _sizeofInt64;
932
933 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700934 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700935 int read(BufferContext bc, int offset) => bc._getInt64(offset);
936}
937
938/// The reader of signed 32-bit integers.
939class Int32Reader extends Reader<int> {
940 const Int32Reader() : super();
941
942 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700943 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700944 int get size => _sizeofInt32;
945
946 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700947 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700948 int read(BufferContext bc, int offset) => bc._getInt32(offset);
949}
950
951/// The reader of signed 32-bit integers.
952class Int16Reader extends Reader<int> {
953 const Int16Reader() : super();
954
955 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700956 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700957 int get size => _sizeofInt16;
958
959 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700960 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700961 int read(BufferContext bc, int offset) => bc._getInt16(offset);
962}
963
964/// The reader of 8-bit signed integers.
965class Int8Reader extends Reader<int> {
966 const Int8Reader() : super();
967
968 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700969 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700970 int get size => _sizeofInt8;
971
972 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700973 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700974 int read(BufferContext bc, int offset) => bc._getInt8(offset);
975}
976
James Kuszmaul8e62b022022-03-22 09:33:25 -0700977/// The reader of lists of objects. Lazy by default - see [lazy].
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700978class ListReader<E> extends Reader<List<E>> {
979 final Reader<E> _elementReader;
980
James Kuszmaul8e62b022022-03-22 09:33:25 -0700981 /// Enables lazy reading of the list
982 ///
983 /// If true, the returned unmodifiable list lazily reads objects on access.
984 /// Therefore, the underlying buffer must not change while accessing the list.
985 ///
986 /// If false, reads the whole list immediately on access.
987 final bool lazy;
988
989 const ListReader(this._elementReader, {this.lazy = true});
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700990
991 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700992 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700993 int get size => _sizeofUint32;
994
995 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700996 List<E> read(BufferContext bc, int offset) {
997 final listOffset = bc.derefObject(offset);
998 return lazy
999 ? _FbGenericList<E>(_elementReader, bc, listOffset)
1000 : List<E>.generate(
1001 bc.buffer.getUint32(listOffset, Endian.little),
1002 (int index) => _elementReader.read(
1003 bc, listOffset + size + _elementReader.size * index),
1004 growable: true);
1005 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001006}
1007
1008/// Object that can read a value at a [BufferContext].
1009abstract class Reader<T> {
1010 const Reader();
1011
1012 /// The size of the value in bytes.
1013 int get size;
1014
1015 /// Read the value at the given [offset] in [bc].
1016 T read(BufferContext bc, int offset);
1017
1018 /// Read the value of the given [field] in the given [object].
James Kuszmaul8e62b022022-03-22 09:33:25 -07001019 @pragma('vm:prefer-inline')
1020 T vTableGet(BufferContext object, int offset, int field, T defaultValue) {
1021 var fieldOffset = _vTableFieldOffset(object, offset, field);
1022 return fieldOffset == 0 ? defaultValue : read(object, offset + fieldOffset);
1023 }
1024
1025 /// Read the value of the given [field] in the given [object].
1026 @pragma('vm:prefer-inline')
1027 T? vTableGetNullable(BufferContext object, int offset, int field) {
1028 var fieldOffset = _vTableFieldOffset(object, offset, field);
1029 return fieldOffset == 0 ? null : read(object, offset + fieldOffset);
1030 }
1031
1032 @pragma('vm:prefer-inline')
1033 int _vTableFieldOffset(BufferContext object, int offset, int field) {
1034 var vTableSOffset = object._getInt32(offset);
1035 var vTableOffset = offset - vTableSOffset;
1036 var vTableSize = object._getUint16(vTableOffset);
1037 if (field >= vTableSize) return 0;
1038 return object._getUint16(vTableOffset + field);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001039 }
1040}
1041
1042/// The reader of string values.
1043class StringReader extends Reader<String> {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001044 final bool asciiOptimization;
1045
1046 const StringReader({this.asciiOptimization = false}) : super();
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001047
1048 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001049 @pragma('vm:prefer-inline')
1050 int get size => _sizeofUint32;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001051
1052 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001053 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001054 String read(BufferContext bc, int offset) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001055 var strOffset = bc.derefObject(offset);
1056 var length = bc._getUint32(strOffset);
1057 var bytes = bc._asUint8List(strOffset + _sizeofUint32, length);
1058 if (asciiOptimization && _isLatin(bytes)) {
1059 return String.fromCharCodes(bytes);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001060 }
1061 return utf8.decode(bytes);
1062 }
1063
James Kuszmaul8e62b022022-03-22 09:33:25 -07001064 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001065 static bool _isLatin(Uint8List bytes) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001066 var length = bytes.length;
1067 for (var i = 0; i < length; i++) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001068 if (bytes[i] > 127) {
1069 return false;
1070 }
1071 }
1072 return true;
1073 }
1074}
1075
1076/// An abstract reader for structs.
1077abstract class StructReader<T> extends Reader<T> {
1078 const StructReader();
1079
1080 /// Return the object at `offset`.
1081 T createObject(BufferContext bc, int offset);
1082
James Kuszmaul8e62b022022-03-22 09:33:25 -07001083 @override
1084 T read(BufferContext bc, int offset) {
1085 return createObject(bc, offset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001086 }
1087}
1088
1089/// An abstract reader for tables.
1090abstract class TableReader<T> extends Reader<T> {
1091 const TableReader();
1092
1093 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001094 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001095 int get size => 4;
1096
1097 /// Return the object at [offset].
1098 T createObject(BufferContext bc, int offset);
1099
1100 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001101 T read(BufferContext bc, int offset) {
1102 var objectOffset = bc.derefObject(offset);
1103 return createObject(bc, objectOffset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001104 }
1105}
1106
1107/// Reader of lists of unsigned 32-bit integer values.
1108///
1109/// The returned unmodifiable lists lazily read values on access.
1110class Uint32ListReader extends Reader<List<int>> {
1111 const Uint32ListReader();
1112
1113 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001114 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001115 int get size => _sizeofUint32;
1116
1117 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001118 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001119 List<int> read(BufferContext bc, int offset) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -07001120 _FbUint32List(bc, bc.derefObject(offset));
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001121}
1122
1123/// The reader of unsigned 64-bit integers.
1124///
1125/// WARNING: May have compatibility issues with JavaScript
1126class Uint64Reader extends Reader<int> {
1127 const Uint64Reader() : super();
1128
1129 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001130 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001131 int get size => _sizeofUint64;
1132
1133 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001134 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001135 int read(BufferContext bc, int offset) => bc._getUint64(offset);
1136}
1137
1138/// The reader of unsigned 32-bit integers.
1139class Uint32Reader extends Reader<int> {
1140 const Uint32Reader() : super();
1141
1142 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001143 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001144 int get size => _sizeofUint32;
1145
1146 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001147 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001148 int read(BufferContext bc, int offset) => bc._getUint32(offset);
1149}
1150
1151/// Reader of lists of unsigned 32-bit integer values.
1152///
1153/// The returned unmodifiable lists lazily read values on access.
1154class Uint16ListReader extends Reader<List<int>> {
1155 const Uint16ListReader();
1156
1157 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001158 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001159 int get size => _sizeofUint32;
1160
1161 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001162 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001163 List<int> read(BufferContext bc, int offset) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -07001164 _FbUint16List(bc, bc.derefObject(offset));
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001165}
1166
1167/// The reader of unsigned 32-bit integers.
1168class Uint16Reader extends Reader<int> {
1169 const Uint16Reader() : super();
1170
1171 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001172 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001173 int get size => _sizeofUint16;
1174
1175 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001176 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001177 int read(BufferContext bc, int offset) => bc._getUint16(offset);
1178}
1179
James Kuszmaul8e62b022022-03-22 09:33:25 -07001180/// Reader of unmodifiable binary data (a list of unsigned 8-bit integers).
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001181class Uint8ListReader extends Reader<List<int>> {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001182 /// Enables lazy reading of the list
1183 ///
1184 /// If true, the returned unmodifiable list lazily reads bytes on access.
1185 /// Therefore, the underlying buffer must not change while accessing the list.
1186 ///
1187 /// If false, reads the whole list immediately as an Uint8List.
1188 final bool lazy;
1189
1190 const Uint8ListReader({this.lazy = true});
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001191
1192 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001193 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001194 int get size => _sizeofUint32;
1195
1196 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001197 @pragma('vm:prefer-inline')
1198 List<int> read(BufferContext bc, int offset) {
1199 final listOffset = bc.derefObject(offset);
1200 if (lazy) return _FbUint8List(bc, listOffset);
1201
1202 final length = bc._getUint32(listOffset);
1203 final result = Uint8List(length);
1204 var pos = listOffset + _sizeofUint32;
1205 for (var i = 0; i < length; i++, pos++) {
1206 result[i] = bc._getUint8(pos);
1207 }
1208 return result;
1209 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001210}
1211
1212/// The reader of unsigned 8-bit integers.
1213class Uint8Reader extends Reader<int> {
1214 const Uint8Reader() : super();
1215
1216 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001217 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001218 int get size => _sizeofUint8;
1219
1220 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001221 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001222 int read(BufferContext bc, int offset) => bc._getUint8(offset);
1223}
1224
James Kuszmaul8e62b022022-03-22 09:33:25 -07001225/// Reader of unmodifiable binary data (a list of signed 8-bit integers).
1226class Int8ListReader extends Reader<List<int>> {
1227 /// Enables lazy reading of the list
1228 ///
1229 /// If true, the returned unmodifiable list lazily reads bytes on access.
1230 /// Therefore, the underlying buffer must not change while accessing the list.
1231 ///
1232 /// If false, reads the whole list immediately as an Uint8List.
1233 final bool lazy;
1234
1235 const Int8ListReader({this.lazy = true});
1236
1237 @override
1238 @pragma('vm:prefer-inline')
1239 int get size => _sizeofUint32;
1240
1241 @override
1242 @pragma('vm:prefer-inline')
1243 List<int> read(BufferContext bc, int offset) {
1244 final listOffset = bc.derefObject(offset);
1245 if (lazy) return _FbUint8List(bc, listOffset);
1246
1247 final length = bc._getUint32(listOffset);
1248 final result = Int8List(length);
1249 var pos = listOffset + _sizeofUint32;
1250 for (var i = 0; i < length; i++, pos++) {
1251 result[i] = bc._getInt8(pos);
1252 }
1253 return result;
1254 }
1255}
1256
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001257/// The list backed by 64-bit values - Uint64 length and Float64.
1258class _FbFloat64List extends _FbList<double> {
1259 _FbFloat64List(BufferContext bc, int offset) : super(bc, offset);
1260
1261 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001262 @pragma('vm:prefer-inline')
1263 double operator [](int i) => bc._getFloat64(offset + 4 + 8 * i);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001264}
1265
1266/// The list backed by 32-bit values - Float32.
1267class _FbFloat32List extends _FbList<double> {
1268 _FbFloat32List(BufferContext bc, int offset) : super(bc, offset);
1269
1270 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001271 @pragma('vm:prefer-inline')
1272 double operator [](int i) => bc._getFloat32(offset + 4 + 4 * i);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001273}
1274
1275/// List backed by a generic object which may have any size.
1276class _FbGenericList<E> extends _FbList<E> {
1277 final Reader<E> elementReader;
1278
James Kuszmaul8e62b022022-03-22 09:33:25 -07001279 List<E?>? _items;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001280
1281 _FbGenericList(this.elementReader, BufferContext bp, int offset)
1282 : super(bp, offset);
1283
1284 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001285 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001286 E operator [](int i) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001287 _items ??= List<E?>.filled(length, null);
1288 var item = _items![i];
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001289 if (item == null) {
1290 item = elementReader.read(bc, offset + 4 + elementReader.size * i);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001291 _items![i] = item;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001292 }
James Kuszmaul8e62b022022-03-22 09:33:25 -07001293 return item!;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001294 }
1295}
1296
1297/// The base class for immutable lists read from flat buffers.
1298abstract class _FbList<E> extends Object with ListMixin<E> implements List<E> {
1299 final BufferContext bc;
1300 final int offset;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001301 int? _length;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001302
1303 _FbList(this.bc, this.offset);
1304
1305 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001306 @pragma('vm:prefer-inline')
1307 int get length => _length ??= bc._getUint32(offset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001308
1309 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001310 set length(int i) => throw StateError('Attempt to modify immutable list');
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001311
1312 @override
1313 void operator []=(int i, E e) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -07001314 throw StateError('Attempt to modify immutable list');
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001315}
1316
1317/// List backed by 32-bit unsigned integers.
1318class _FbUint32List extends _FbList<int> {
1319 _FbUint32List(BufferContext bc, int offset) : super(bc, offset);
1320
1321 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001322 @pragma('vm:prefer-inline')
1323 int operator [](int i) => bc._getUint32(offset + 4 + 4 * i);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001324}
1325
1326/// List backed by 16-bit unsigned integers.
1327class _FbUint16List extends _FbList<int> {
1328 _FbUint16List(BufferContext bc, int offset) : super(bc, offset);
1329
1330 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001331 @pragma('vm:prefer-inline')
1332 int operator [](int i) => bc._getUint16(offset + 4 + 2 * i);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001333}
1334
1335/// List backed by 8-bit unsigned integers.
1336class _FbUint8List extends _FbList<int> {
1337 _FbUint8List(BufferContext bc, int offset) : super(bc, offset);
1338
1339 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001340 @pragma('vm:prefer-inline')
1341 int operator [](int i) => bc._getUint8(offset + 4 + i);
1342}
1343
1344/// List backed by 8-bit signed integers.
1345class _FbInt8List extends _FbList<int> {
1346 _FbInt8List(BufferContext bc, int offset) : super(bc, offset);
1347
1348 @override
1349 @pragma('vm:prefer-inline')
1350 int operator [](int i) => bc._getInt8(offset + 4 + i);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001351}
1352
1353/// List backed by 8-bit unsigned integers.
1354class _FbBoolList extends _FbList<bool> {
1355 _FbBoolList(BufferContext bc, int offset) : super(bc, offset);
1356
1357 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001358 @pragma('vm:prefer-inline')
1359 bool operator [](int i) => bc._getUint8(offset + 4 + i) == 1 ? true : false;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001360}
1361
1362/// Class that describes the structure of a table.
1363class _VTable {
1364 static const int _metadataLength = 4;
1365
James Kuszmaul8e62b022022-03-22 09:33:25 -07001366 final int numFields;
1367
1368 // Note: fieldOffsets start as "tail offsets" and are then transformed by
1369 // [computeFieldOffsets()] to actual offsets when a table is finished.
1370 final Uint32List fieldOffsets;
1371 bool offsetsComputed = false;
1372
1373 _VTable(this.numFields) : fieldOffsets = Uint32List(numFields);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001374
1375 /// The size of the table that uses this VTable.
James Kuszmaul8e62b022022-03-22 09:33:25 -07001376 int tableSize = 0;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001377
James Kuszmaul8e62b022022-03-22 09:33:25 -07001378 /// The tail of this VTable. It is used to share the same VTable between
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001379 /// multiple tables of identical structure.
James Kuszmaul8e62b022022-03-22 09:33:25 -07001380 int tail = 0;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001381
1382 int get _vTableSize => numOfUint16 * _sizeofUint16;
1383
James Kuszmaul8e62b022022-03-22 09:33:25 -07001384 int get numOfUint16 => 1 + 1 + numFields;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001385
James Kuszmaul8e62b022022-03-22 09:33:25 -07001386 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001387 void addField(int field, int offset) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001388 assert(!offsetsComputed);
1389 assert(offset > 0); // it's impossible for field to start at the buffer end
1390 assert(offset <= 4294967295); // uint32 max
1391 fieldOffsets[field] = offset;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001392 }
1393
James Kuszmaul8e62b022022-03-22 09:33:25 -07001394 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001395 bool _offsetsMatch(int vt2Start, ByteData buf) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001396 assert(offsetsComputed);
1397 for (var i = 0; i < numFields; i++) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001398 if (fieldOffsets[i] !=
James Kuszmaul8e62b022022-03-22 09:33:25 -07001399 buf.getUint16(vt2Start + _metadataLength + (2 * i), Endian.little)) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001400 return false;
1401 }
1402 }
1403 return true;
1404 }
1405
1406 /// Fill the [fieldOffsets] field.
James Kuszmaul8e62b022022-03-22 09:33:25 -07001407 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001408 void computeFieldOffsets(int tableTail) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001409 assert(!offsetsComputed);
1410 offsetsComputed = true;
1411 for (var i = 0; i < numFields; i++) {
1412 if (fieldOffsets[i] != 0) {
1413 fieldOffsets[i] = tableTail - fieldOffsets[i];
1414 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001415 }
1416 }
1417
1418 /// Outputs this VTable to [buf], which is is expected to be aligned to 16-bit
1419 /// and have at least [numOfUint16] 16-bit words available.
James Kuszmaul8e62b022022-03-22 09:33:25 -07001420 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001421 void output(ByteData buf, int bufOffset) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001422 assert(offsetsComputed);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001423 // VTable size.
1424 buf.setUint16(bufOffset, numOfUint16 * 2, Endian.little);
1425 bufOffset += 2;
1426 // Table size.
1427 buf.setUint16(bufOffset, tableSize, Endian.little);
1428 bufOffset += 2;
1429 // Field offsets.
James Kuszmaul8e62b022022-03-22 09:33:25 -07001430 for (var i = 0; i < numFields; i++) {
1431 buf.setUint16(bufOffset, fieldOffsets[i], Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001432 bufOffset += 2;
1433 }
1434 }
1435}
James Kuszmaul8e62b022022-03-22 09:33:25 -07001436
1437/// The interface that [Builder] uses to allocate buffers for encoding.
1438abstract class Allocator {
1439 const Allocator();
1440
1441 /// Allocate a [ByteData] buffer of a given size.
1442 ByteData allocate(int size);
1443
1444 /// Free the given [ByteData] buffer previously allocated by [allocate].
1445 void deallocate(ByteData data);
1446
1447 /// Reallocate [newSize] bytes of memory, replacing the old [oldData]. This
1448 /// grows downwards, and is intended specifically for use with [Builder].
1449 /// Params [inUseBack] and [inUseFront] indicate how much of [oldData] is
1450 /// actually in use at each end, and needs to be copied.
1451 ByteData resize(
1452 ByteData oldData, int newSize, int inUseBack, int inUseFront) {
1453 final newData = allocate(newSize);
1454 _copyDownward(oldData, newData, inUseBack, inUseFront);
1455 deallocate(oldData);
1456 return newData;
1457 }
1458
1459 /// Called by [resize] to copy memory from [oldData] to [newData]. Only
1460 /// memory of size [inUseFront] and [inUseBack] will be copied from the front
1461 /// and back of the old memory allocation.
1462 void _copyDownward(
1463 ByteData oldData, ByteData newData, int inUseBack, int inUseFront) {
1464 if (inUseBack != 0) {
1465 newData.buffer.asUint8List().setAll(
1466 newData.lengthInBytes - inUseBack,
1467 oldData.buffer.asUint8List().getRange(
1468 oldData.lengthInBytes - inUseBack, oldData.lengthInBytes));
1469 }
1470 if (inUseFront != 0) {
1471 newData.buffer
1472 .asUint8List()
1473 .setAll(0, oldData.buffer.asUint8List().getRange(0, inUseFront));
1474 }
1475 }
1476}
1477
1478class DefaultAllocator extends Allocator {
1479 const DefaultAllocator();
1480
1481 @override
1482 ByteData allocate(int size) => ByteData(size);
1483
1484 @override
1485 void deallocate(ByteData data) {
1486 // nothing to do, it's garbage-collected
1487 }
1488}