blob: 8ce1ab907e81ac910e88e7e8b2dc7410adeaf27f [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001import 'dart:collection';
2import 'dart:convert';
3import 'dart:math';
4import 'dart:typed_data';
5
6const int _sizeofUint8 = 1;
7const int _sizeofUint16 = 2;
8const int _sizeofUint32 = 4;
9const int _sizeofUint64 = 8;
10const int _sizeofInt8 = 1;
11const int _sizeofInt16 = 2;
12const int _sizeofInt32 = 4;
13const int _sizeofInt64 = 8;
14const int _sizeofFloat32 = 4;
15const int _sizeofFloat64 = 8;
16
17/// Callback used to invoke a struct builder's finish method.
18///
19/// This callback is used by other struct's `finish` methods to write the nested
20/// struct's fields inline.
James Kuszmaul8e62b022022-03-22 09:33:25 -070021typedef StructBuilder = void Function();
Austin Schuhe89fa2d2019-08-14 20:24:23 -070022
23/// Buffer with data and some context about it.
24class BufferContext {
25 final ByteData _buffer;
26
James Kuszmaul8e62b022022-03-22 09:33:25 -070027 ByteData get buffer => _buffer;
Austin Schuhe89fa2d2019-08-14 20:24:23 -070028
James Kuszmaul8e62b022022-03-22 09:33:25 -070029 /// Create from a FlatBuffer represented by a list of bytes (uint8).
30 factory BufferContext.fromBytes(List<int> byteList) =>
31 BufferContext(byteList is Uint8List
32 ? byteList.buffer.asByteData(byteList.offsetInBytes)
33 : ByteData.view(Uint8List.fromList(byteList).buffer));
Austin Schuhe89fa2d2019-08-14 20:24:23 -070034
James Kuszmaul8e62b022022-03-22 09:33:25 -070035 /// Create from a FlatBuffer represented by ByteData.
36 BufferContext(this._buffer);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070037
James Kuszmaul8e62b022022-03-22 09:33:25 -070038 @pragma('vm:prefer-inline')
39 int derefObject(int offset) => offset + _getUint32(offset);
40
41 @pragma('vm:prefer-inline')
42 Uint8List _asUint8List(int offset, int length) =>
Austin Schuhe89fa2d2019-08-14 20:24:23 -070043 _buffer.buffer.asUint8List(_buffer.offsetInBytes + offset, length);
44
James Kuszmaul8e62b022022-03-22 09:33:25 -070045 @pragma('vm:prefer-inline')
46 double _getFloat64(int offset) => _buffer.getFloat64(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070047
James Kuszmaul8e62b022022-03-22 09:33:25 -070048 @pragma('vm:prefer-inline')
49 double _getFloat32(int offset) => _buffer.getFloat32(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070050
James Kuszmaul8e62b022022-03-22 09:33:25 -070051 @pragma('vm:prefer-inline')
52 int _getInt64(int offset) => _buffer.getInt64(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070053
James Kuszmaul8e62b022022-03-22 09:33:25 -070054 @pragma('vm:prefer-inline')
55 int _getInt32(int offset) => _buffer.getInt32(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070056
James Kuszmaul8e62b022022-03-22 09:33:25 -070057 @pragma('vm:prefer-inline')
58 int _getInt16(int offset) => _buffer.getInt16(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070059
James Kuszmaul8e62b022022-03-22 09:33:25 -070060 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -070061 int _getInt8(int offset) => _buffer.getInt8(offset);
62
James Kuszmaul8e62b022022-03-22 09:33:25 -070063 @pragma('vm:prefer-inline')
64 int _getUint64(int offset) => _buffer.getUint64(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070065
James Kuszmaul8e62b022022-03-22 09:33:25 -070066 @pragma('vm:prefer-inline')
67 int _getUint32(int offset) => _buffer.getUint32(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070068
James Kuszmaul8e62b022022-03-22 09:33:25 -070069 @pragma('vm:prefer-inline')
70 int _getUint16(int offset) => _buffer.getUint16(offset, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070071
James Kuszmaul8e62b022022-03-22 09:33:25 -070072 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -070073 int _getUint8(int offset) => _buffer.getUint8(offset);
James Kuszmaul8e62b022022-03-22 09:33:25 -070074}
Austin Schuhe89fa2d2019-08-14 20:24:23 -070075
James Kuszmaul8e62b022022-03-22 09:33:25 -070076/// Interface implemented by the "object-api" classes (ending with "T").
77abstract class Packable {
78 /// Serialize the object using the given builder, returning the offset.
79 int pack(Builder fbBuilder);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070080}
81
82/// Class implemented by typed builders generated by flatc.
83abstract class ObjectBuilder {
James Kuszmaul8e62b022022-03-22 09:33:25 -070084 int? _firstOffset;
Austin Schuhe89fa2d2019-08-14 20:24:23 -070085
86 /// Can be used to write the data represented by this builder to the [Builder]
87 /// and reuse the offset created in multiple tables.
88 ///
89 /// Note that this method assumes you call it using the same [Builder] instance
90 /// every time. The returned offset is only good for the [Builder] used in the
91 /// first call to this method.
92 int getOrCreateOffset(Builder fbBuilder) {
93 _firstOffset ??= finish(fbBuilder);
James Kuszmaul8e62b022022-03-22 09:33:25 -070094 return _firstOffset!;
Austin Schuhe89fa2d2019-08-14 20:24:23 -070095 }
96
97 /// Writes the data in this helper to the [Builder].
98 int finish(Builder fbBuilder);
99
100 /// Convenience method that will create a new [Builder], [finish]es the data,
101 /// and returns the buffer as a [Uint8List] of bytes.
102 Uint8List toBytes();
103}
104
105/// Class that helps building flat buffers.
106class Builder {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700107 bool _finished = false;
108
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700109 final int initialSize;
110
111 /// The list of existing VTable(s).
James Kuszmaul8e62b022022-03-22 09:33:25 -0700112 final List<int> _vTables;
113
114 final bool deduplicateTables;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700115
116 ByteData _buf;
117
James Kuszmaul8e62b022022-03-22 09:33:25 -0700118 final Allocator _allocator;
119
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700120 /// The maximum alignment that has been seen so far. If [_buf] has to be
121 /// reallocated in the future (to insert room at its start for more bytes) the
122 /// reallocation will need to be a multiple of this many bytes.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700123 int _maxAlign = 1;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700124
125 /// The number of bytes that have been written to the buffer so far. The
126 /// most recently written byte is this many bytes from the end of [_buf].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700127 int _tail = 0;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700128
129 /// The location of the end of the current table, measured in bytes from the
James Kuszmaul8e62b022022-03-22 09:33:25 -0700130 /// end of [_buf].
131 int _currentTableEndTail = 0;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700132
James Kuszmaul8e62b022022-03-22 09:33:25 -0700133 _VTable? _currentVTable;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700134
135 /// Map containing all strings that have been written so far. This allows us
136 /// to avoid duplicating strings.
137 ///
138 /// Allocated only if `internStrings` is set to true on the constructor.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700139 Map<String, int>? _strings;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700140
141 /// Creates a new FlatBuffers Builder.
142 ///
143 /// `initialSize` is the initial array size in bytes. The [Builder] will
144 /// automatically grow the array if/as needed. `internStrings`, if set to
145 /// true, will cause [writeString] to pool strings in the buffer so that
146 /// identical strings will always use the same offset in tables.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700147 Builder({
148 this.initialSize = 1024,
149 bool internStrings = false,
150 Allocator allocator = const DefaultAllocator(),
151 this.deduplicateTables = true,
152 }) : _allocator = allocator,
153 _buf = allocator.allocate(initialSize),
154 _vTables = deduplicateTables ? [] : const [] {
155 if (internStrings) {
156 _strings = <String, int>{};
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700157 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700158 }
159
James Kuszmaul8e62b022022-03-22 09:33:25 -0700160 /// Calculate the finished buffer size (aligned).
161 @pragma('vm:prefer-inline')
162 int size() => _tail + ((-_tail) & (_maxAlign - 1));
163
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700164 /// Add the [field] with the given boolean [value]. The field is not added if
165 /// the [value] is equal to [def]. Booleans are stored as 8-bit fields with
166 /// `0` for `false` and `1` for `true`.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700167 void addBool(int field, bool? value, [bool? def]) {
168 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700169 if (value != null && value != def) {
170 _prepare(_sizeofUint8, 1);
171 _trackField(field);
172 _buf.setInt8(_buf.lengthInBytes - _tail, value ? 1 : 0);
173 }
174 }
175
176 /// Add the [field] with the given 32-bit signed integer [value]. The field is
177 /// not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700178 void addInt32(int field, int? value, [int? def]) {
179 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700180 if (value != null && value != def) {
181 _prepare(_sizeofInt32, 1);
182 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700183 _setInt32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700184 }
185 }
186
187 /// Add the [field] with the given 32-bit signed integer [value]. The field is
188 /// not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700189 void addInt16(int field, int? value, [int? def]) {
190 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700191 if (value != null && value != def) {
192 _prepare(_sizeofInt16, 1);
193 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700194 _setInt16AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700195 }
196 }
197
198 /// Add the [field] with the given 8-bit signed integer [value]. The field is
199 /// not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700200 void addInt8(int field, int? value, [int? def]) {
201 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700202 if (value != null && value != def) {
203 _prepare(_sizeofInt8, 1);
204 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700205 _setInt8AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700206 }
207 }
208
209 void addStruct(int field, int offset) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700210 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700211 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700212 _currentVTable!.addField(field, offset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700213 }
214
215 /// Add the [field] referencing an object with the given [offset].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700216 void addOffset(int field, int? offset) {
217 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700218 if (offset != null) {
219 _prepare(_sizeofUint32, 1);
220 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700221 _setUint32AtTail(_tail, _tail - offset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700222 }
223 }
224
225 /// Add the [field] with the given 32-bit unsigned integer [value]. The field
226 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700227 void addUint32(int field, int? value, [int? def]) {
228 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700229 if (value != null && value != def) {
230 _prepare(_sizeofUint32, 1);
231 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700232 _setUint32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700233 }
234 }
235
236 /// Add the [field] with the given 32-bit unsigned integer [value]. The field
237 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700238 void addUint16(int field, int? value, [int? def]) {
239 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700240 if (value != null && value != def) {
241 _prepare(_sizeofUint16, 1);
242 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700243 _setUint16AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700244 }
245 }
246
247 /// Add the [field] with the given 8-bit unsigned integer [value]. The field
248 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700249 void addUint8(int field, int? value, [int? def]) {
250 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700251 if (value != null && value != def) {
252 _prepare(_sizeofUint8, 1);
253 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700254 _setUint8AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700255 }
256 }
257
258 /// Add the [field] with the given 32-bit float [value]. The field
259 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700260 void addFloat32(int field, double? value, [double? def]) {
261 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700262 if (value != null && value != def) {
263 _prepare(_sizeofFloat32, 1);
264 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700265 _setFloat32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700266 }
267 }
268
269 /// Add the [field] with the given 64-bit double [value]. The field
270 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700271 void addFloat64(int field, double? value, [double? def]) {
272 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700273 if (value != null && value != def) {
274 _prepare(_sizeofFloat64, 1);
275 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700276 _setFloat64AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700277 }
278 }
279
280 /// Add the [field] with the given 64-bit unsigned integer [value]. The field
281 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700282 void addUint64(int field, int? value, [double? def]) {
283 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700284 if (value != null && value != def) {
285 _prepare(_sizeofUint64, 1);
286 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700287 _setUint64AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700288 }
289 }
290
291 /// Add the [field] with the given 64-bit unsigned integer [value]. The field
292 /// is not added if the [value] is equal to [def].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700293 void addInt64(int field, int? value, [double? def]) {
294 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700295 if (value != null && value != def) {
296 _prepare(_sizeofInt64, 1);
297 _trackField(field);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700298 _setInt64AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700299 }
300 }
301
302 /// End the current table and return its offset.
303 int endTable() {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700304 assert(_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700305 // Prepare for writing the VTable.
306 _prepare(_sizeofInt32, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700307 var tableTail = _tail;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700308 // Prepare the size of the current table.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700309 final currentVTable = _currentVTable!;
310 currentVTable.tableSize = tableTail - _currentTableEndTail;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700311 // Prepare the VTable to use for the current table.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700312 int? vTableTail;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700313 {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700314 currentVTable.computeFieldOffsets(tableTail);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700315
James Kuszmaul8e62b022022-03-22 09:33:25 -0700316 // Try to find an existing compatible VTable.
317 if (deduplicateTables) {
318 // Search backward - more likely to have recently used one
319 for (var i = _vTables.length - 1; i >= 0; i--) {
320 final vt2Offset = _vTables[i];
321 final vt2Start = _buf.lengthInBytes - vt2Offset;
322 final vt2Size = _buf.getUint16(vt2Start, Endian.little);
323
324 if (currentVTable._vTableSize == vt2Size &&
325 currentVTable._offsetsMatch(vt2Start, _buf)) {
326 vTableTail = vt2Offset;
327 break;
328 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700329 }
330 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700331
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700332 // Write a new VTable.
333 if (vTableTail == null) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700334 _prepare(_sizeofUint16, _currentVTable!.numOfUint16);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700335 vTableTail = _tail;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700336 currentVTable.tail = vTableTail;
337 currentVTable.output(_buf, _buf.lengthInBytes - _tail);
338 if (deduplicateTables) _vTables.add(currentVTable.tail);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700339 }
340 }
341 // Set the VTable offset.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700342 _setInt32AtTail(tableTail, vTableTail - tableTail);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700343 // Done with this table.
344 _currentVTable = null;
345 return tableTail;
346 }
347
James Kuszmaul8e62b022022-03-22 09:33:25 -0700348 /// Returns the finished buffer. You must call [finish] before accessing this.
349 @pragma('vm:prefer-inline')
350 Uint8List get buffer {
351 assert(_finished);
352 final finishedSize = size();
353 return _buf.buffer
354 .asUint8List(_buf.lengthInBytes - finishedSize, finishedSize);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700355 }
356
357 /// Finish off the creation of the buffer. The given [offset] is used as the
358 /// root object offset, and usually references directly or indirectly every
359 /// written object. If [fileIdentifier] is specified (and not `null`), it is
360 /// interpreted as a 4-byte Latin-1 encoded string that should be placed at
361 /// bytes 4-7 of the file.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700362 void finish(int offset, [String? fileIdentifier]) {
363 final sizeBeforePadding = size();
364 final requiredBytes = _sizeofUint32 * (fileIdentifier == null ? 1 : 2);
365 _prepare(max(requiredBytes, _maxAlign), 1);
366 final finishedSize = size();
367 _setUint32AtTail(finishedSize, finishedSize - offset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700368 if (fileIdentifier != null) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700369 for (var i = 0; i < 4; i++) {
370 _setUint8AtTail(
371 finishedSize - _sizeofUint32 - i, fileIdentifier.codeUnitAt(i));
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700372 }
373 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700374
375 // zero out the added padding
376 for (var i = sizeBeforePadding + 1;
377 i <= finishedSize - requiredBytes;
378 i++) {
379 _setUint8AtTail(i, 0);
380 }
381 _finished = true;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700382 }
383
384 /// Writes a Float64 to the tail of the buffer after preparing space for it.
385 ///
386 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
387 void putFloat64(double value) {
388 _prepare(_sizeofFloat64, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700389 _setFloat32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700390 }
391
392 /// Writes a Float32 to the tail of the buffer after preparing space for it.
393 ///
394 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
395 void putFloat32(double value) {
396 _prepare(_sizeofFloat32, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700397 _setFloat32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700398 }
399
Austin Schuh2dd86a92022-09-14 21:19:23 -0700400 /// Writes a bool to the tail of the buffer after preparing space for it.
401 /// Bools are represented as a Uint8, with the value set to '1' for true, and '0' for false
402 ///
403 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
404 void putBool(bool value) {
405 _prepare(_sizeofUint8, 1);
406 _buf.setInt8(_buf.lengthInBytes - _tail, value ? 1 : 0);
407 }
408
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700409 /// Writes a Int64 to the tail of the buffer after preparing space for it.
410 ///
411 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
412 void putInt64(int value) {
413 _prepare(_sizeofInt64, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700414 _setInt64AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700415 }
416
417 /// Writes a Uint32 to the tail of the buffer after preparing space for it.
418 ///
419 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
420 void putInt32(int value) {
421 _prepare(_sizeofInt32, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700422 _setInt32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700423 }
424
425 /// Writes a Uint16 to the tail of the buffer after preparing space for it.
426 ///
427 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
428 void putInt16(int value) {
429 _prepare(_sizeofInt16, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700430 _setInt16AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700431 }
432
433 /// Writes a Uint8 to the tail of the buffer after preparing space for it.
434 ///
435 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
436 void putInt8(int value) {
437 _prepare(_sizeofInt8, 1);
438 _buf.setInt8(_buf.lengthInBytes - _tail, value);
439 }
440
441 /// Writes a Uint64 to the tail of the buffer after preparing space for it.
442 ///
443 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
444 void putUint64(int value) {
445 _prepare(_sizeofUint64, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700446 _setUint64AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700447 }
448
449 /// Writes a Uint32 to the tail of the buffer after preparing space for it.
450 ///
451 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
452 void putUint32(int value) {
453 _prepare(_sizeofUint32, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700454 _setUint32AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700455 }
456
457 /// Writes a Uint16 to the tail of the buffer after preparing space for it.
458 ///
459 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
460 void putUint16(int value) {
461 _prepare(_sizeofUint16, 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700462 _setUint16AtTail(_tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700463 }
464
465 /// Writes a Uint8 to the tail of the buffer after preparing space for it.
466 ///
467 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
468 void putUint8(int value) {
469 _prepare(_sizeofUint8, 1);
470 _buf.setUint8(_buf.lengthInBytes - _tail, value);
471 }
472
473 /// Reset the builder and make it ready for filling a new buffer.
474 void reset() {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700475 _finished = false;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700476 _maxAlign = 1;
477 _tail = 0;
478 _currentVTable = null;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700479 if (deduplicateTables) _vTables.clear();
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700480 if (_strings != null) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700481 _strings = <String, int>{};
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700482 }
483 }
484
James Kuszmaul8e62b022022-03-22 09:33:25 -0700485 /// Start a new table. Must be finished with [endTable] invocation.
486 void startTable(int numFields) {
487 assert(!_inVTable); // Inline tables are not supported.
488 _currentVTable = _VTable(numFields);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700489 _currentTableEndTail = _tail;
490 }
491
492 /// Finish a Struct vector. Most callers should preferto use [writeListOfStructs].
493 ///
494 /// Most callers should prefer [writeListOfStructs].
495 int endStructVector(int count) {
496 putUint32(count);
497 return _tail;
498 }
499
500 /// Writes a list of Structs to the buffer, returning the offset
501 int writeListOfStructs(List<ObjectBuilder> structBuilders) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700502 assert(!_inVTable);
503 for (var i = structBuilders.length - 1; i >= 0; i--) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700504 structBuilders[i].finish(this);
505 }
506 return endStructVector(structBuilders.length);
507 }
508
509 /// Write the given list of [values].
510 int writeList(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700511 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700512 _prepare(_sizeofUint32, 1 + values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700513 final result = _tail;
514 var tail = _tail;
515 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700516 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700517 for (var value in values) {
518 _setUint32AtTail(tail, tail - value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700519 tail -= _sizeofUint32;
520 }
521 return result;
522 }
523
524 /// Write the given list of 64-bit float [values].
525 int writeListFloat64(List<double> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700526 assert(!_inVTable);
Austin Schuh272c6132020-11-14 16:37:52 -0800527 _prepare(_sizeofFloat64, values.length, additionalBytes: _sizeofUint32);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700528 final result = _tail;
529 var tail = _tail;
530 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700531 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700532 for (var value in values) {
533 _setFloat64AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700534 tail -= _sizeofFloat64;
535 }
536 return result;
537 }
538
539 /// Write the given list of 32-bit float [values].
540 int writeListFloat32(List<double> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700541 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700542 _prepare(_sizeofFloat32, 1 + values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700543 final result = _tail;
544 var tail = _tail;
545 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700546 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700547 for (var value in values) {
548 _setFloat32AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700549 tail -= _sizeofFloat32;
550 }
551 return result;
552 }
553
554 /// Write the given list of signed 64-bit integer [values].
555 int writeListInt64(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700556 assert(!_inVTable);
Austin Schuh272c6132020-11-14 16:37:52 -0800557 _prepare(_sizeofInt64, values.length, additionalBytes: _sizeofUint32);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700558 final result = _tail;
559 var tail = _tail;
560 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700561 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700562 for (var value in values) {
563 _setInt64AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700564 tail -= _sizeofInt64;
565 }
566 return result;
567 }
568
569 /// Write the given list of signed 64-bit integer [values].
570 int writeListUint64(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700571 assert(!_inVTable);
Austin Schuh272c6132020-11-14 16:37:52 -0800572 _prepare(_sizeofUint64, values.length, additionalBytes: _sizeofUint32);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700573 final result = _tail;
574 var tail = _tail;
575 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700576 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700577 for (var value in values) {
578 _setUint64AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700579 tail -= _sizeofUint64;
580 }
581 return result;
582 }
583
584 /// Write the given list of signed 32-bit integer [values].
585 int writeListInt32(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700586 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700587 _prepare(_sizeofUint32, 1 + values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700588 final result = _tail;
589 var tail = _tail;
590 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700591 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700592 for (var value in values) {
593 _setInt32AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700594 tail -= _sizeofInt32;
595 }
596 return result;
597 }
598
599 /// Write the given list of unsigned 32-bit integer [values].
600 int writeListUint32(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700601 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700602 _prepare(_sizeofUint32, 1 + values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700603 final result = _tail;
604 var tail = _tail;
605 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700606 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700607 for (var value in values) {
608 _setUint32AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700609 tail -= _sizeofUint32;
610 }
611 return result;
612 }
613
614 /// Write the given list of signed 16-bit integer [values].
615 int writeListInt16(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700616 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700617 _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700618 final result = _tail;
619 var tail = _tail;
620 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700621 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700622 for (var value in values) {
623 _setInt16AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700624 tail -= _sizeofInt16;
625 }
626 return result;
627 }
628
629 /// Write the given list of unsigned 16-bit integer [values].
630 int writeListUint16(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700631 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700632 _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700633 final result = _tail;
634 var tail = _tail;
635 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700636 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700637 for (var value in values) {
638 _setUint16AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700639 tail -= _sizeofUint16;
640 }
641 return result;
642 }
643
644 /// Write the given list of bools as unsigend 8-bit integer [values].
645 int writeListBool(List<bool> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700646 return writeListUint8(values.map((b) => b ? 1 : 0).toList());
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700647 }
648
649 /// Write the given list of signed 8-bit integer [values].
650 int writeListInt8(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700651 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700652 _prepare(_sizeofUint32, 1, additionalBytes: values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700653 final result = _tail;
654 var tail = _tail;
655 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700656 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700657 for (var value in values) {
658 _setInt8AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700659 tail -= _sizeofUint8;
660 }
661 return result;
662 }
663
664 /// Write the given list of unsigned 8-bit integer [values].
665 int writeListUint8(List<int> values) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700666 assert(!_inVTable);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700667 _prepare(_sizeofUint32, 1, additionalBytes: values.length);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700668 final result = _tail;
669 var tail = _tail;
670 _setUint32AtTail(tail, values.length);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700671 tail -= _sizeofUint32;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700672 for (var value in values) {
673 _setUint8AtTail(tail, value);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700674 tail -= _sizeofUint8;
675 }
676 return result;
677 }
678
James Kuszmaul8e62b022022-03-22 09:33:25 -0700679 /// Write the given string [value] and return its offset.
680 ///
681 /// Dart strings are UTF-16 but must be stored as UTF-8 in FlatBuffers.
682 /// If the given string consists only of ASCII characters, you can indicate
683 /// enable [asciiOptimization]. In this mode, [writeString()] first tries to
684 /// copy the ASCII string directly to the output buffer and if that fails
685 /// (because there are no-ASCII characters in the string) it falls back and to
686 /// the default UTF-16 -> UTF-8 conversion (with slight performance penalty).
687 int writeString(String value, {bool asciiOptimization = false}) {
688 assert(!_inVTable);
689 if (_strings != null) {
690 return _strings!
691 .putIfAbsent(value, () => _writeString(value, asciiOptimization));
692 } else {
693 return _writeString(value, asciiOptimization);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700694 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700695 }
696
James Kuszmaul8e62b022022-03-22 09:33:25 -0700697 int _writeString(String value, bool asciiOptimization) {
698 if (asciiOptimization) {
699 // [utf8.encode()] is slow (up to at least Dart SDK 2.13). If the given
700 // string is ASCII we can just write it directly, without any conversion.
701 final originalTail = _tail;
702 if (_tryWriteASCIIString(value)) return _tail;
703 // if non-ASCII: reset the output buffer position for [_writeUTFString()]
704 _tail = originalTail;
705 }
706 _writeUTFString(value);
707 return _tail;
708 }
709
710 // Try to write the string as ASCII, return false if there's a non-ascii char.
711 @pragma('vm:prefer-inline')
712 bool _tryWriteASCIIString(String value) {
713 _prepare(4, 1, additionalBytes: value.length + 1);
714 final length = value.length;
715 var offset = _buf.lengthInBytes - _tail + 4;
716 for (var i = 0; i < length; i++) {
717 // utf16 code unit, e.g. for '†' it's [0x20 0x20], which is 8224 decimal.
718 // ASCII characters go from 0x00 to 0x7F (which is 0 to 127 decimal).
719 final char = value.codeUnitAt(i);
720 if ((char & ~0x7F) != 0) {
721 return false;
722 }
723 _buf.setUint8(offset++, char);
724 }
725 _buf.setUint8(offset, 0); // trailing zero
726 _setUint32AtTail(_tail, value.length);
727 return true;
728 }
729
730 @pragma('vm:prefer-inline')
731 void _writeUTFString(String value) {
732 final bytes = utf8.encode(value) as Uint8List;
733 final length = bytes.length;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700734 _prepare(4, 1, additionalBytes: length + 1);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700735 _setUint32AtTail(_tail, length);
736 var offset = _buf.lengthInBytes - _tail + 4;
737 for (var i = 0; i < length; i++) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700738 _buf.setUint8(offset++, bytes[i]);
739 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700740 _buf.setUint8(offset, 0); // trailing zero
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700741 }
742
James Kuszmaul8e62b022022-03-22 09:33:25 -0700743 /// Used to assert whether a "Table" is currently being built.
744 ///
745 /// If you hit `assert(!_inVTable())`, you're trying to add table fields
746 /// without starting a table with [Builder.startTable()].
747 ///
748 /// If you hit `assert(_inVTable())`, you're trying to construct a
749 /// Table/Vector/String during the construction of its parent table,
750 /// between the MyTableBuilder and [Builder.endTable()].
751 /// Move the creation of these sub-objects to before the MyTableBuilder to
752 /// not get this assert.
753 @pragma('vm:prefer-inline')
754 bool get _inVTable => _currentVTable != null;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700755
756 /// The number of bytes that have been written to the buffer so far. The
757 /// most recently written byte is this many bytes from the end of the buffer.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700758 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700759 int get offset => _tail;
760
761 /// Zero-pads the buffer, which may be required for some struct layouts.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700762 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700763 void pad(int howManyBytes) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700764 for (var i = 0; i < howManyBytes; i++) {
765 putUint8(0);
766 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700767 }
768
769 /// Prepare for writing the given `count` of scalars of the given `size`.
770 /// Additionally allocate the specified `additionalBytes`. Update the current
771 /// tail pointer to point at the allocated space.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700772 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700773 void _prepare(int size, int count, {int additionalBytes = 0}) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700774 assert(!_finished);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700775 // Update the alignment.
776 if (_maxAlign < size) {
777 _maxAlign = size;
778 }
779 // Prepare amount of required space.
James Kuszmaul8e62b022022-03-22 09:33:25 -0700780 var dataSize = size * count + additionalBytes;
781 var alignDelta = (-(_tail + dataSize)) & (size - 1);
782 var bufSize = alignDelta + dataSize;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700783 // Ensure that we have the required amount of space.
784 {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700785 var oldCapacity = _buf.lengthInBytes;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700786 if (_tail + bufSize > oldCapacity) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700787 var desiredNewCapacity = (oldCapacity + bufSize) * 2;
788 var deltaCapacity = desiredNewCapacity - oldCapacity;
789 deltaCapacity += (-deltaCapacity) & (_maxAlign - 1);
790 var newCapacity = oldCapacity + deltaCapacity;
791 _buf = _allocator.resize(_buf, newCapacity, _tail, 0);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700792 }
793 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700794
795 // zero out the added padding
796 for (var i = _tail + 1; i <= _tail + alignDelta; i++) {
797 _setUint8AtTail(i, 0);
798 }
799
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700800 // Update the tail pointer.
801 _tail += bufSize;
802 }
803
804 /// Record the offset of the given [field].
James Kuszmaul8e62b022022-03-22 09:33:25 -0700805 @pragma('vm:prefer-inline')
806 void _trackField(int field) => _currentVTable!.addField(field, _tail);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700807
James Kuszmaul8e62b022022-03-22 09:33:25 -0700808 @pragma('vm:prefer-inline')
809 void _setFloat64AtTail(int tail, double x) =>
810 _buf.setFloat64(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700811
James Kuszmaul8e62b022022-03-22 09:33:25 -0700812 @pragma('vm:prefer-inline')
813 void _setFloat32AtTail(int tail, double x) =>
814 _buf.setFloat32(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700815
James Kuszmaul8e62b022022-03-22 09:33:25 -0700816 @pragma('vm:prefer-inline')
817 void _setUint64AtTail(int tail, int x) =>
818 _buf.setUint64(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700819
James Kuszmaul8e62b022022-03-22 09:33:25 -0700820 @pragma('vm:prefer-inline')
821 void _setInt64AtTail(int tail, int x) =>
822 _buf.setInt64(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700823
James Kuszmaul8e62b022022-03-22 09:33:25 -0700824 @pragma('vm:prefer-inline')
825 void _setInt32AtTail(int tail, int x) =>
826 _buf.setInt32(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700827
James Kuszmaul8e62b022022-03-22 09:33:25 -0700828 @pragma('vm:prefer-inline')
829 void _setUint32AtTail(int tail, int x) =>
830 _buf.setUint32(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700831
James Kuszmaul8e62b022022-03-22 09:33:25 -0700832 @pragma('vm:prefer-inline')
833 void _setInt16AtTail(int tail, int x) =>
834 _buf.setInt16(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700835
James Kuszmaul8e62b022022-03-22 09:33:25 -0700836 @pragma('vm:prefer-inline')
837 void _setUint16AtTail(int tail, int x) =>
838 _buf.setUint16(_buf.lengthInBytes - tail, x, Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700839
James Kuszmaul8e62b022022-03-22 09:33:25 -0700840 @pragma('vm:prefer-inline')
841 void _setInt8AtTail(int tail, int x) =>
842 _buf.setInt8(_buf.lengthInBytes - tail, x);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700843
James Kuszmaul8e62b022022-03-22 09:33:25 -0700844 @pragma('vm:prefer-inline')
845 void _setUint8AtTail(int tail, int x) =>
846 _buf.setUint8(_buf.lengthInBytes - tail, x);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700847}
848
849/// Reader of lists of boolean values.
850///
851/// The returned unmodifiable lists lazily read values on access.
852class BoolListReader extends Reader<List<bool>> {
853 const BoolListReader();
854
855 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700856 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700857 int get size => _sizeofUint32;
858
859 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700860 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700861 List<bool> read(BufferContext bc, int offset) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -0700862 _FbBoolList(bc, bc.derefObject(offset));
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700863}
864
865/// The reader of booleans.
866class BoolReader extends Reader<bool> {
867 const BoolReader() : super();
868
869 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700870 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700871 int get size => _sizeofUint8;
872
873 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700874 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700875 bool read(BufferContext bc, int offset) => bc._getInt8(offset) != 0;
876}
877
878/// The reader of lists of 64-bit float values.
879///
880/// The returned unmodifiable lists lazily read values on access.
881class Float64ListReader extends Reader<List<double>> {
882 const Float64ListReader();
883
884 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700885 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700886 int get size => _sizeofFloat64;
887
888 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700889 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700890 List<double> read(BufferContext bc, int offset) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -0700891 _FbFloat64List(bc, bc.derefObject(offset));
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700892}
893
894class Float32ListReader extends Reader<List<double>> {
895 const Float32ListReader();
896
897 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700898 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700899 int get size => _sizeofFloat32;
900
901 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700902 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700903 List<double> read(BufferContext bc, int offset) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -0700904 _FbFloat32List(bc, bc.derefObject(offset));
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700905}
906
907class Float64Reader extends Reader<double> {
908 const Float64Reader();
909
910 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700911 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700912 int get size => _sizeofFloat64;
913
914 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700915 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700916 double read(BufferContext bc, int offset) => bc._getFloat64(offset);
917}
918
919class Float32Reader extends Reader<double> {
920 const Float32Reader();
921
922 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700923 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700924 int get size => _sizeofFloat32;
925
926 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700927 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700928 double read(BufferContext bc, int offset) => bc._getFloat32(offset);
929}
930
931class Int64Reader extends Reader<int> {
932 const Int64Reader() : super();
James Kuszmaul8e62b022022-03-22 09:33:25 -0700933
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700934 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700935 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700936 int get size => _sizeofInt64;
937
938 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700939 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700940 int read(BufferContext bc, int offset) => bc._getInt64(offset);
941}
942
943/// The reader of signed 32-bit integers.
944class Int32Reader extends Reader<int> {
945 const Int32Reader() : super();
946
947 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700948 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700949 int get size => _sizeofInt32;
950
951 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700952 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700953 int read(BufferContext bc, int offset) => bc._getInt32(offset);
954}
955
956/// The reader of signed 32-bit integers.
957class Int16Reader extends Reader<int> {
958 const Int16Reader() : super();
959
960 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700961 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700962 int get size => _sizeofInt16;
963
964 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700965 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700966 int read(BufferContext bc, int offset) => bc._getInt16(offset);
967}
968
969/// The reader of 8-bit signed integers.
970class Int8Reader extends Reader<int> {
971 const Int8Reader() : super();
972
973 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700974 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700975 int get size => _sizeofInt8;
976
977 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700978 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700979 int read(BufferContext bc, int offset) => bc._getInt8(offset);
980}
981
James Kuszmaul8e62b022022-03-22 09:33:25 -0700982/// The reader of lists of objects. Lazy by default - see [lazy].
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700983class ListReader<E> extends Reader<List<E>> {
984 final Reader<E> _elementReader;
985
James Kuszmaul8e62b022022-03-22 09:33:25 -0700986 /// Enables lazy reading of the list
987 ///
988 /// If true, the returned unmodifiable list lazily reads objects on access.
989 /// Therefore, the underlying buffer must not change while accessing the list.
990 ///
991 /// If false, reads the whole list immediately on access.
992 final bool lazy;
993
994 const ListReader(this._elementReader, {this.lazy = true});
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700995
996 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -0700997 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700998 int get size => _sizeofUint32;
999
1000 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001001 List<E> read(BufferContext bc, int offset) {
1002 final listOffset = bc.derefObject(offset);
1003 return lazy
1004 ? _FbGenericList<E>(_elementReader, bc, listOffset)
1005 : List<E>.generate(
1006 bc.buffer.getUint32(listOffset, Endian.little),
1007 (int index) => _elementReader.read(
1008 bc, listOffset + size + _elementReader.size * index),
1009 growable: true);
1010 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001011}
1012
1013/// Object that can read a value at a [BufferContext].
1014abstract class Reader<T> {
1015 const Reader();
1016
1017 /// The size of the value in bytes.
1018 int get size;
1019
1020 /// Read the value at the given [offset] in [bc].
1021 T read(BufferContext bc, int offset);
1022
1023 /// Read the value of the given [field] in the given [object].
James Kuszmaul8e62b022022-03-22 09:33:25 -07001024 @pragma('vm:prefer-inline')
1025 T vTableGet(BufferContext object, int offset, int field, T defaultValue) {
1026 var fieldOffset = _vTableFieldOffset(object, offset, field);
1027 return fieldOffset == 0 ? defaultValue : read(object, offset + fieldOffset);
1028 }
1029
1030 /// Read the value of the given [field] in the given [object].
1031 @pragma('vm:prefer-inline')
1032 T? vTableGetNullable(BufferContext object, int offset, int field) {
1033 var fieldOffset = _vTableFieldOffset(object, offset, field);
1034 return fieldOffset == 0 ? null : read(object, offset + fieldOffset);
1035 }
1036
1037 @pragma('vm:prefer-inline')
1038 int _vTableFieldOffset(BufferContext object, int offset, int field) {
1039 var vTableSOffset = object._getInt32(offset);
1040 var vTableOffset = offset - vTableSOffset;
1041 var vTableSize = object._getUint16(vTableOffset);
1042 if (field >= vTableSize) return 0;
1043 return object._getUint16(vTableOffset + field);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001044 }
1045}
1046
1047/// The reader of string values.
1048class StringReader extends Reader<String> {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001049 final bool asciiOptimization;
1050
1051 const StringReader({this.asciiOptimization = false}) : super();
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001052
1053 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001054 @pragma('vm:prefer-inline')
1055 int get size => _sizeofUint32;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001056
1057 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001058 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001059 String read(BufferContext bc, int offset) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001060 var strOffset = bc.derefObject(offset);
1061 var length = bc._getUint32(strOffset);
1062 var bytes = bc._asUint8List(strOffset + _sizeofUint32, length);
1063 if (asciiOptimization && _isLatin(bytes)) {
1064 return String.fromCharCodes(bytes);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001065 }
1066 return utf8.decode(bytes);
1067 }
1068
James Kuszmaul8e62b022022-03-22 09:33:25 -07001069 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001070 static bool _isLatin(Uint8List bytes) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001071 var length = bytes.length;
1072 for (var i = 0; i < length; i++) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001073 if (bytes[i] > 127) {
1074 return false;
1075 }
1076 }
1077 return true;
1078 }
1079}
1080
1081/// An abstract reader for structs.
1082abstract class StructReader<T> extends Reader<T> {
1083 const StructReader();
1084
1085 /// Return the object at `offset`.
1086 T createObject(BufferContext bc, int offset);
1087
James Kuszmaul8e62b022022-03-22 09:33:25 -07001088 @override
1089 T read(BufferContext bc, int offset) {
1090 return createObject(bc, offset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001091 }
1092}
1093
1094/// An abstract reader for tables.
1095abstract class TableReader<T> extends Reader<T> {
1096 const TableReader();
1097
1098 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001099 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001100 int get size => 4;
1101
1102 /// Return the object at [offset].
1103 T createObject(BufferContext bc, int offset);
1104
1105 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001106 T read(BufferContext bc, int offset) {
1107 var objectOffset = bc.derefObject(offset);
1108 return createObject(bc, objectOffset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001109 }
1110}
1111
1112/// Reader of lists of unsigned 32-bit integer values.
1113///
1114/// The returned unmodifiable lists lazily read values on access.
1115class Uint32ListReader extends Reader<List<int>> {
1116 const Uint32ListReader();
1117
1118 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001119 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001120 int get size => _sizeofUint32;
1121
1122 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001123 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001124 List<int> read(BufferContext bc, int offset) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -07001125 _FbUint32List(bc, bc.derefObject(offset));
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001126}
1127
1128/// The reader of unsigned 64-bit integers.
1129///
1130/// WARNING: May have compatibility issues with JavaScript
1131class Uint64Reader extends Reader<int> {
1132 const Uint64Reader() : super();
1133
1134 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001135 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001136 int get size => _sizeofUint64;
1137
1138 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001139 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001140 int read(BufferContext bc, int offset) => bc._getUint64(offset);
1141}
1142
1143/// The reader of unsigned 32-bit integers.
1144class Uint32Reader extends Reader<int> {
1145 const Uint32Reader() : super();
1146
1147 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001148 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001149 int get size => _sizeofUint32;
1150
1151 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001152 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001153 int read(BufferContext bc, int offset) => bc._getUint32(offset);
1154}
1155
1156/// Reader of lists of unsigned 32-bit integer values.
1157///
1158/// The returned unmodifiable lists lazily read values on access.
1159class Uint16ListReader extends Reader<List<int>> {
1160 const Uint16ListReader();
1161
1162 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001163 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001164 int get size => _sizeofUint32;
1165
1166 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001167 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001168 List<int> read(BufferContext bc, int offset) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -07001169 _FbUint16List(bc, bc.derefObject(offset));
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001170}
1171
1172/// The reader of unsigned 32-bit integers.
1173class Uint16Reader extends Reader<int> {
1174 const Uint16Reader() : super();
1175
1176 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001177 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001178 int get size => _sizeofUint16;
1179
1180 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001181 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001182 int read(BufferContext bc, int offset) => bc._getUint16(offset);
1183}
1184
James Kuszmaul8e62b022022-03-22 09:33:25 -07001185/// Reader of unmodifiable binary data (a list of unsigned 8-bit integers).
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001186class Uint8ListReader extends Reader<List<int>> {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001187 /// Enables lazy reading of the list
1188 ///
1189 /// If true, the returned unmodifiable list lazily reads bytes on access.
1190 /// Therefore, the underlying buffer must not change while accessing the list.
1191 ///
1192 /// If false, reads the whole list immediately as an Uint8List.
1193 final bool lazy;
1194
1195 const Uint8ListReader({this.lazy = true});
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001196
1197 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001198 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001199 int get size => _sizeofUint32;
1200
1201 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001202 @pragma('vm:prefer-inline')
1203 List<int> read(BufferContext bc, int offset) {
1204 final listOffset = bc.derefObject(offset);
1205 if (lazy) return _FbUint8List(bc, listOffset);
1206
1207 final length = bc._getUint32(listOffset);
1208 final result = Uint8List(length);
1209 var pos = listOffset + _sizeofUint32;
1210 for (var i = 0; i < length; i++, pos++) {
1211 result[i] = bc._getUint8(pos);
1212 }
1213 return result;
1214 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001215}
1216
1217/// The reader of unsigned 8-bit integers.
1218class Uint8Reader extends Reader<int> {
1219 const Uint8Reader() : super();
1220
1221 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001222 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001223 int get size => _sizeofUint8;
1224
1225 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001226 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001227 int read(BufferContext bc, int offset) => bc._getUint8(offset);
1228}
1229
James Kuszmaul8e62b022022-03-22 09:33:25 -07001230/// Reader of unmodifiable binary data (a list of signed 8-bit integers).
1231class Int8ListReader extends Reader<List<int>> {
1232 /// Enables lazy reading of the list
1233 ///
1234 /// If true, the returned unmodifiable list lazily reads bytes on access.
1235 /// Therefore, the underlying buffer must not change while accessing the list.
1236 ///
1237 /// If false, reads the whole list immediately as an Uint8List.
1238 final bool lazy;
1239
1240 const Int8ListReader({this.lazy = true});
1241
1242 @override
1243 @pragma('vm:prefer-inline')
1244 int get size => _sizeofUint32;
1245
1246 @override
1247 @pragma('vm:prefer-inline')
1248 List<int> read(BufferContext bc, int offset) {
1249 final listOffset = bc.derefObject(offset);
1250 if (lazy) return _FbUint8List(bc, listOffset);
1251
1252 final length = bc._getUint32(listOffset);
1253 final result = Int8List(length);
1254 var pos = listOffset + _sizeofUint32;
1255 for (var i = 0; i < length; i++, pos++) {
1256 result[i] = bc._getInt8(pos);
1257 }
1258 return result;
1259 }
1260}
1261
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001262/// The list backed by 64-bit values - Uint64 length and Float64.
1263class _FbFloat64List extends _FbList<double> {
1264 _FbFloat64List(BufferContext bc, int offset) : super(bc, offset);
1265
1266 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001267 @pragma('vm:prefer-inline')
1268 double operator [](int i) => bc._getFloat64(offset + 4 + 8 * i);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001269}
1270
1271/// The list backed by 32-bit values - Float32.
1272class _FbFloat32List extends _FbList<double> {
1273 _FbFloat32List(BufferContext bc, int offset) : super(bc, offset);
1274
1275 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001276 @pragma('vm:prefer-inline')
1277 double operator [](int i) => bc._getFloat32(offset + 4 + 4 * i);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001278}
1279
1280/// List backed by a generic object which may have any size.
1281class _FbGenericList<E> extends _FbList<E> {
1282 final Reader<E> elementReader;
1283
James Kuszmaul8e62b022022-03-22 09:33:25 -07001284 List<E?>? _items;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001285
1286 _FbGenericList(this.elementReader, BufferContext bp, int offset)
1287 : super(bp, offset);
1288
1289 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001290 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001291 E operator [](int i) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001292 _items ??= List<E?>.filled(length, null);
1293 var item = _items![i];
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001294 if (item == null) {
1295 item = elementReader.read(bc, offset + 4 + elementReader.size * i);
James Kuszmaul8e62b022022-03-22 09:33:25 -07001296 _items![i] = item;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001297 }
James Kuszmaul8e62b022022-03-22 09:33:25 -07001298 return item!;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001299 }
1300}
1301
1302/// The base class for immutable lists read from flat buffers.
1303abstract class _FbList<E> extends Object with ListMixin<E> implements List<E> {
1304 final BufferContext bc;
1305 final int offset;
James Kuszmaul8e62b022022-03-22 09:33:25 -07001306 int? _length;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001307
1308 _FbList(this.bc, this.offset);
1309
1310 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001311 @pragma('vm:prefer-inline')
1312 int get length => _length ??= bc._getUint32(offset);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001313
1314 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001315 set length(int i) => throw StateError('Attempt to modify immutable list');
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001316
1317 @override
1318 void operator []=(int i, E e) =>
James Kuszmaul8e62b022022-03-22 09:33:25 -07001319 throw StateError('Attempt to modify immutable list');
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001320}
1321
1322/// List backed by 32-bit unsigned integers.
1323class _FbUint32List extends _FbList<int> {
1324 _FbUint32List(BufferContext bc, int offset) : super(bc, offset);
1325
1326 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001327 @pragma('vm:prefer-inline')
1328 int operator [](int i) => bc._getUint32(offset + 4 + 4 * i);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001329}
1330
1331/// List backed by 16-bit unsigned integers.
1332class _FbUint16List extends _FbList<int> {
1333 _FbUint16List(BufferContext bc, int offset) : super(bc, offset);
1334
1335 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001336 @pragma('vm:prefer-inline')
1337 int operator [](int i) => bc._getUint16(offset + 4 + 2 * i);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001338}
1339
1340/// List backed by 8-bit unsigned integers.
1341class _FbUint8List extends _FbList<int> {
1342 _FbUint8List(BufferContext bc, int offset) : super(bc, offset);
1343
1344 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001345 @pragma('vm:prefer-inline')
1346 int operator [](int i) => bc._getUint8(offset + 4 + i);
1347}
1348
1349/// List backed by 8-bit signed integers.
1350class _FbInt8List extends _FbList<int> {
1351 _FbInt8List(BufferContext bc, int offset) : super(bc, offset);
1352
1353 @override
1354 @pragma('vm:prefer-inline')
1355 int operator [](int i) => bc._getInt8(offset + 4 + i);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001356}
1357
1358/// List backed by 8-bit unsigned integers.
1359class _FbBoolList extends _FbList<bool> {
1360 _FbBoolList(BufferContext bc, int offset) : super(bc, offset);
1361
1362 @override
James Kuszmaul8e62b022022-03-22 09:33:25 -07001363 @pragma('vm:prefer-inline')
1364 bool operator [](int i) => bc._getUint8(offset + 4 + i) == 1 ? true : false;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001365}
1366
1367/// Class that describes the structure of a table.
1368class _VTable {
1369 static const int _metadataLength = 4;
1370
James Kuszmaul8e62b022022-03-22 09:33:25 -07001371 final int numFields;
1372
1373 // Note: fieldOffsets start as "tail offsets" and are then transformed by
1374 // [computeFieldOffsets()] to actual offsets when a table is finished.
1375 final Uint32List fieldOffsets;
1376 bool offsetsComputed = false;
1377
1378 _VTable(this.numFields) : fieldOffsets = Uint32List(numFields);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001379
1380 /// The size of the table that uses this VTable.
James Kuszmaul8e62b022022-03-22 09:33:25 -07001381 int tableSize = 0;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001382
James Kuszmaul8e62b022022-03-22 09:33:25 -07001383 /// The tail of this VTable. It is used to share the same VTable between
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001384 /// multiple tables of identical structure.
James Kuszmaul8e62b022022-03-22 09:33:25 -07001385 int tail = 0;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001386
1387 int get _vTableSize => numOfUint16 * _sizeofUint16;
1388
James Kuszmaul8e62b022022-03-22 09:33:25 -07001389 int get numOfUint16 => 1 + 1 + numFields;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001390
James Kuszmaul8e62b022022-03-22 09:33:25 -07001391 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001392 void addField(int field, int offset) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001393 assert(!offsetsComputed);
1394 assert(offset > 0); // it's impossible for field to start at the buffer end
1395 assert(offset <= 4294967295); // uint32 max
1396 fieldOffsets[field] = offset;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001397 }
1398
James Kuszmaul8e62b022022-03-22 09:33:25 -07001399 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001400 bool _offsetsMatch(int vt2Start, ByteData buf) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001401 assert(offsetsComputed);
1402 for (var i = 0; i < numFields; i++) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001403 if (fieldOffsets[i] !=
James Kuszmaul8e62b022022-03-22 09:33:25 -07001404 buf.getUint16(vt2Start + _metadataLength + (2 * i), Endian.little)) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001405 return false;
1406 }
1407 }
1408 return true;
1409 }
1410
1411 /// Fill the [fieldOffsets] field.
James Kuszmaul8e62b022022-03-22 09:33:25 -07001412 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001413 void computeFieldOffsets(int tableTail) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001414 assert(!offsetsComputed);
1415 offsetsComputed = true;
1416 for (var i = 0; i < numFields; i++) {
1417 if (fieldOffsets[i] != 0) {
1418 fieldOffsets[i] = tableTail - fieldOffsets[i];
1419 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001420 }
1421 }
1422
1423 /// Outputs this VTable to [buf], which is is expected to be aligned to 16-bit
1424 /// and have at least [numOfUint16] 16-bit words available.
James Kuszmaul8e62b022022-03-22 09:33:25 -07001425 @pragma('vm:prefer-inline')
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001426 void output(ByteData buf, int bufOffset) {
James Kuszmaul8e62b022022-03-22 09:33:25 -07001427 assert(offsetsComputed);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001428 // VTable size.
1429 buf.setUint16(bufOffset, numOfUint16 * 2, Endian.little);
1430 bufOffset += 2;
1431 // Table size.
1432 buf.setUint16(bufOffset, tableSize, Endian.little);
1433 bufOffset += 2;
1434 // Field offsets.
James Kuszmaul8e62b022022-03-22 09:33:25 -07001435 for (var i = 0; i < numFields; i++) {
1436 buf.setUint16(bufOffset, fieldOffsets[i], Endian.little);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001437 bufOffset += 2;
1438 }
1439 }
1440}
James Kuszmaul8e62b022022-03-22 09:33:25 -07001441
1442/// The interface that [Builder] uses to allocate buffers for encoding.
1443abstract class Allocator {
1444 const Allocator();
1445
1446 /// Allocate a [ByteData] buffer of a given size.
1447 ByteData allocate(int size);
1448
1449 /// Free the given [ByteData] buffer previously allocated by [allocate].
1450 void deallocate(ByteData data);
1451
1452 /// Reallocate [newSize] bytes of memory, replacing the old [oldData]. This
1453 /// grows downwards, and is intended specifically for use with [Builder].
1454 /// Params [inUseBack] and [inUseFront] indicate how much of [oldData] is
1455 /// actually in use at each end, and needs to be copied.
1456 ByteData resize(
1457 ByteData oldData, int newSize, int inUseBack, int inUseFront) {
1458 final newData = allocate(newSize);
1459 _copyDownward(oldData, newData, inUseBack, inUseFront);
1460 deallocate(oldData);
1461 return newData;
1462 }
1463
1464 /// Called by [resize] to copy memory from [oldData] to [newData]. Only
1465 /// memory of size [inUseFront] and [inUseBack] will be copied from the front
1466 /// and back of the old memory allocation.
1467 void _copyDownward(
1468 ByteData oldData, ByteData newData, int inUseBack, int inUseFront) {
1469 if (inUseBack != 0) {
1470 newData.buffer.asUint8List().setAll(
1471 newData.lengthInBytes - inUseBack,
1472 oldData.buffer.asUint8List().getRange(
1473 oldData.lengthInBytes - inUseBack, oldData.lengthInBytes));
1474 }
1475 if (inUseFront != 0) {
1476 newData.buffer
1477 .asUint8List()
1478 .setAll(0, oldData.buffer.asUint8List().getRange(0, inUseFront));
1479 }
1480 }
1481}
1482
1483class DefaultAllocator extends Allocator {
1484 const DefaultAllocator();
1485
1486 @override
1487 ByteData allocate(int size) => ByteData(size);
1488
1489 @override
1490 void deallocate(ByteData data) {
1491 // nothing to do, it's garbage-collected
1492 }
1493}