xref: /aosp_15_r20/external/flatbuffers/dart/lib/flat_buffers.dart (revision 890232f25432b36107d06881e0a25aaa6b473652)
1import '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.
21typedef StructBuilder = void Function();
22
23/// Buffer with data and some context about it.
24class BufferContext {
25  final ByteData _buffer;
26
27  ByteData get buffer => _buffer;
28
29  /// 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));
34
35  /// Create from a FlatBuffer represented by ByteData.
36  BufferContext(this._buffer);
37
38  @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) =>
43      _buffer.buffer.asUint8List(_buffer.offsetInBytes + offset, length);
44
45  @pragma('vm:prefer-inline')
46  double _getFloat64(int offset) => _buffer.getFloat64(offset, Endian.little);
47
48  @pragma('vm:prefer-inline')
49  double _getFloat32(int offset) => _buffer.getFloat32(offset, Endian.little);
50
51  @pragma('vm:prefer-inline')
52  int _getInt64(int offset) => _buffer.getInt64(offset, Endian.little);
53
54  @pragma('vm:prefer-inline')
55  int _getInt32(int offset) => _buffer.getInt32(offset, Endian.little);
56
57  @pragma('vm:prefer-inline')
58  int _getInt16(int offset) => _buffer.getInt16(offset, Endian.little);
59
60  @pragma('vm:prefer-inline')
61  int _getInt8(int offset) => _buffer.getInt8(offset);
62
63  @pragma('vm:prefer-inline')
64  int _getUint64(int offset) => _buffer.getUint64(offset, Endian.little);
65
66  @pragma('vm:prefer-inline')
67  int _getUint32(int offset) => _buffer.getUint32(offset, Endian.little);
68
69  @pragma('vm:prefer-inline')
70  int _getUint16(int offset) => _buffer.getUint16(offset, Endian.little);
71
72  @pragma('vm:prefer-inline')
73  int _getUint8(int offset) => _buffer.getUint8(offset);
74}
75
76/// 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);
80}
81
82/// Class implemented by typed builders generated by flatc.
83abstract class ObjectBuilder {
84  int? _firstOffset;
85
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);
94    return _firstOffset!;
95  }
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 {
107  bool _finished = false;
108
109  final int initialSize;
110
111  /// The list of existing VTable(s).
112  final List<int> _vTables;
113
114  final bool deduplicateTables;
115
116  ByteData _buf;
117
118  final Allocator _allocator;
119
120  /// 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.
123  int _maxAlign = 1;
124
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].
127  int _tail = 0;
128
129  /// The location of the end of the current table, measured in bytes from the
130  /// end of [_buf].
131  int _currentTableEndTail = 0;
132
133  _VTable? _currentVTable;
134
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.
139  Map<String, int>? _strings;
140
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.
147  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>{};
157    }
158  }
159
160  /// Calculate the finished buffer size (aligned).
161  @pragma('vm:prefer-inline')
162  int size() => _tail + ((-_tail) & (_maxAlign - 1));
163
164  /// 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`.
167  void addBool(int field, bool? value, [bool? def]) {
168    assert(_inVTable);
169    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].
178  void addInt32(int field, int? value, [int? def]) {
179    assert(_inVTable);
180    if (value != null && value != def) {
181      _prepare(_sizeofInt32, 1);
182      _trackField(field);
183      _setInt32AtTail(_tail, value);
184    }
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].
189  void addInt16(int field, int? value, [int? def]) {
190    assert(_inVTable);
191    if (value != null && value != def) {
192      _prepare(_sizeofInt16, 1);
193      _trackField(field);
194      _setInt16AtTail(_tail, value);
195    }
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].
200  void addInt8(int field, int? value, [int? def]) {
201    assert(_inVTable);
202    if (value != null && value != def) {
203      _prepare(_sizeofInt8, 1);
204      _trackField(field);
205      _setInt8AtTail(_tail, value);
206    }
207  }
208
209  void addStruct(int field, int offset) {
210    assert(_inVTable);
211    _trackField(field);
212    _currentVTable!.addField(field, offset);
213  }
214
215  /// Add the [field] referencing an object with the given [offset].
216  void addOffset(int field, int? offset) {
217    assert(_inVTable);
218    if (offset != null) {
219      _prepare(_sizeofUint32, 1);
220      _trackField(field);
221      _setUint32AtTail(_tail, _tail - offset);
222    }
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].
227  void addUint32(int field, int? value, [int? def]) {
228    assert(_inVTable);
229    if (value != null && value != def) {
230      _prepare(_sizeofUint32, 1);
231      _trackField(field);
232      _setUint32AtTail(_tail, value);
233    }
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].
238  void addUint16(int field, int? value, [int? def]) {
239    assert(_inVTable);
240    if (value != null && value != def) {
241      _prepare(_sizeofUint16, 1);
242      _trackField(field);
243      _setUint16AtTail(_tail, value);
244    }
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].
249  void addUint8(int field, int? value, [int? def]) {
250    assert(_inVTable);
251    if (value != null && value != def) {
252      _prepare(_sizeofUint8, 1);
253      _trackField(field);
254      _setUint8AtTail(_tail, value);
255    }
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].
260  void addFloat32(int field, double? value, [double? def]) {
261    assert(_inVTable);
262    if (value != null && value != def) {
263      _prepare(_sizeofFloat32, 1);
264      _trackField(field);
265      _setFloat32AtTail(_tail, value);
266    }
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].
271  void addFloat64(int field, double? value, [double? def]) {
272    assert(_inVTable);
273    if (value != null && value != def) {
274      _prepare(_sizeofFloat64, 1);
275      _trackField(field);
276      _setFloat64AtTail(_tail, value);
277    }
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].
282  void addUint64(int field, int? value, [double? def]) {
283    assert(_inVTable);
284    if (value != null && value != def) {
285      _prepare(_sizeofUint64, 1);
286      _trackField(field);
287      _setUint64AtTail(_tail, value);
288    }
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].
293  void addInt64(int field, int? value, [double? def]) {
294    assert(_inVTable);
295    if (value != null && value != def) {
296      _prepare(_sizeofInt64, 1);
297      _trackField(field);
298      _setInt64AtTail(_tail, value);
299    }
300  }
301
302  /// End the current table and return its offset.
303  int endTable() {
304    assert(_inVTable);
305    // Prepare for writing the VTable.
306    _prepare(_sizeofInt32, 1);
307    var tableTail = _tail;
308    // Prepare the size of the current table.
309    final currentVTable = _currentVTable!;
310    currentVTable.tableSize = tableTail - _currentTableEndTail;
311    // Prepare the VTable to use for the current table.
312    int? vTableTail;
313    {
314      currentVTable.computeFieldOffsets(tableTail);
315
316      // 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          }
329        }
330      }
331
332      // Write a new VTable.
333      if (vTableTail == null) {
334        _prepare(_sizeofUint16, _currentVTable!.numOfUint16);
335        vTableTail = _tail;
336        currentVTable.tail = vTableTail;
337        currentVTable.output(_buf, _buf.lengthInBytes - _tail);
338        if (deduplicateTables) _vTables.add(currentVTable.tail);
339      }
340    }
341    // Set the VTable offset.
342    _setInt32AtTail(tableTail, vTableTail - tableTail);
343    // Done with this table.
344    _currentVTable = null;
345    return tableTail;
346  }
347
348  /// 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);
355  }
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.
362  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);
368    if (fileIdentifier != null) {
369      for (var i = 0; i < 4; i++) {
370        _setUint8AtTail(
371            finishedSize - _sizeofUint32 - i, fileIdentifier.codeUnitAt(i));
372      }
373    }
374
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;
382  }
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);
389    _setFloat32AtTail(_tail, value);
390  }
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);
397    _setFloat32AtTail(_tail, value);
398  }
399
400  /// 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
409  /// 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);
414    _setInt64AtTail(_tail, value);
415  }
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);
422    _setInt32AtTail(_tail, value);
423  }
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);
430    _setInt16AtTail(_tail, value);
431  }
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);
446    _setUint64AtTail(_tail, value);
447  }
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);
454    _setUint32AtTail(_tail, value);
455  }
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);
462    _setUint16AtTail(_tail, value);
463  }
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() {
475    _finished = false;
476    _maxAlign = 1;
477    _tail = 0;
478    _currentVTable = null;
479    if (deduplicateTables) _vTables.clear();
480    if (_strings != null) {
481      _strings = <String, int>{};
482    }
483  }
484
485  /// 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);
489    _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) {
502    assert(!_inVTable);
503    for (var i = structBuilders.length - 1; i >= 0; i--) {
504      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) {
511    assert(!_inVTable);
512    _prepare(_sizeofUint32, 1 + values.length);
513    final result = _tail;
514    var tail = _tail;
515    _setUint32AtTail(tail, values.length);
516    tail -= _sizeofUint32;
517    for (var value in values) {
518      _setUint32AtTail(tail, tail - value);
519      tail -= _sizeofUint32;
520    }
521    return result;
522  }
523
524  /// Write the given list of 64-bit float [values].
525  int writeListFloat64(List<double> values) {
526    assert(!_inVTable);
527    _prepare(_sizeofFloat64, values.length, additionalBytes: _sizeofUint32);
528    final result = _tail;
529    var tail = _tail;
530    _setUint32AtTail(tail, values.length);
531    tail -= _sizeofUint32;
532    for (var value in values) {
533      _setFloat64AtTail(tail, value);
534      tail -= _sizeofFloat64;
535    }
536    return result;
537  }
538
539  /// Write the given list of 32-bit float [values].
540  int writeListFloat32(List<double> values) {
541    assert(!_inVTable);
542    _prepare(_sizeofFloat32, 1 + values.length);
543    final result = _tail;
544    var tail = _tail;
545    _setUint32AtTail(tail, values.length);
546    tail -= _sizeofUint32;
547    for (var value in values) {
548      _setFloat32AtTail(tail, value);
549      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) {
556    assert(!_inVTable);
557    _prepare(_sizeofInt64, values.length, additionalBytes: _sizeofUint32);
558    final result = _tail;
559    var tail = _tail;
560    _setUint32AtTail(tail, values.length);
561    tail -= _sizeofUint32;
562    for (var value in values) {
563      _setInt64AtTail(tail, value);
564      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) {
571    assert(!_inVTable);
572    _prepare(_sizeofUint64, values.length, additionalBytes: _sizeofUint32);
573    final result = _tail;
574    var tail = _tail;
575    _setUint32AtTail(tail, values.length);
576    tail -= _sizeofUint32;
577    for (var value in values) {
578      _setUint64AtTail(tail, value);
579      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) {
586    assert(!_inVTable);
587    _prepare(_sizeofUint32, 1 + values.length);
588    final result = _tail;
589    var tail = _tail;
590    _setUint32AtTail(tail, values.length);
591    tail -= _sizeofUint32;
592    for (var value in values) {
593      _setInt32AtTail(tail, value);
594      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) {
601    assert(!_inVTable);
602    _prepare(_sizeofUint32, 1 + values.length);
603    final result = _tail;
604    var tail = _tail;
605    _setUint32AtTail(tail, values.length);
606    tail -= _sizeofUint32;
607    for (var value in values) {
608      _setUint32AtTail(tail, value);
609      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) {
616    assert(!_inVTable);
617    _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
618    final result = _tail;
619    var tail = _tail;
620    _setUint32AtTail(tail, values.length);
621    tail -= _sizeofUint32;
622    for (var value in values) {
623      _setInt16AtTail(tail, value);
624      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) {
631    assert(!_inVTable);
632    _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
633    final result = _tail;
634    var tail = _tail;
635    _setUint32AtTail(tail, values.length);
636    tail -= _sizeofUint32;
637    for (var value in values) {
638      _setUint16AtTail(tail, value);
639      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) {
646    return writeListUint8(values.map((b) => b ? 1 : 0).toList());
647  }
648
649  /// Write the given list of signed 8-bit integer [values].
650  int writeListInt8(List<int> values) {
651    assert(!_inVTable);
652    _prepare(_sizeofUint32, 1, additionalBytes: values.length);
653    final result = _tail;
654    var tail = _tail;
655    _setUint32AtTail(tail, values.length);
656    tail -= _sizeofUint32;
657    for (var value in values) {
658      _setInt8AtTail(tail, value);
659      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) {
666    assert(!_inVTable);
667    _prepare(_sizeofUint32, 1, additionalBytes: values.length);
668    final result = _tail;
669    var tail = _tail;
670    _setUint32AtTail(tail, values.length);
671    tail -= _sizeofUint32;
672    for (var value in values) {
673      _setUint8AtTail(tail, value);
674      tail -= _sizeofUint8;
675    }
676    return result;
677  }
678
679  /// 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);
694    }
695  }
696
697  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;
734    _prepare(4, 1, additionalBytes: length + 1);
735    _setUint32AtTail(_tail, length);
736    var offset = _buf.lengthInBytes - _tail + 4;
737    for (var i = 0; i < length; i++) {
738      _buf.setUint8(offset++, bytes[i]);
739    }
740    _buf.setUint8(offset, 0); // trailing zero
741  }
742
743  /// 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;
755
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.
758  @pragma('vm:prefer-inline')
759  int get offset => _tail;
760
761  /// Zero-pads the buffer, which may be required for some struct layouts.
762  @pragma('vm:prefer-inline')
763  void pad(int howManyBytes) {
764    for (var i = 0; i < howManyBytes; i++) {
765      putUint8(0);
766    }
767  }
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.
772  @pragma('vm:prefer-inline')
773  void _prepare(int size, int count, {int additionalBytes = 0}) {
774    assert(!_finished);
775    // Update the alignment.
776    if (_maxAlign < size) {
777      _maxAlign = size;
778    }
779    // Prepare amount of required space.
780    var dataSize = size * count + additionalBytes;
781    var alignDelta = (-(_tail + dataSize)) & (size - 1);
782    var bufSize = alignDelta + dataSize;
783    // Ensure that we have the required amount of space.
784    {
785      var oldCapacity = _buf.lengthInBytes;
786      if (_tail + bufSize > oldCapacity) {
787        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);
792      }
793    }
794
795    // zero out the added padding
796    for (var i = _tail + 1; i <= _tail + alignDelta; i++) {
797      _setUint8AtTail(i, 0);
798    }
799
800    // Update the tail pointer.
801    _tail += bufSize;
802  }
803
804  /// Record the offset of the given [field].
805  @pragma('vm:prefer-inline')
806  void _trackField(int field) => _currentVTable!.addField(field, _tail);
807
808  @pragma('vm:prefer-inline')
809  void _setFloat64AtTail(int tail, double x) =>
810      _buf.setFloat64(_buf.lengthInBytes - tail, x, Endian.little);
811
812  @pragma('vm:prefer-inline')
813  void _setFloat32AtTail(int tail, double x) =>
814      _buf.setFloat32(_buf.lengthInBytes - tail, x, Endian.little);
815
816  @pragma('vm:prefer-inline')
817  void _setUint64AtTail(int tail, int x) =>
818      _buf.setUint64(_buf.lengthInBytes - tail, x, Endian.little);
819
820  @pragma('vm:prefer-inline')
821  void _setInt64AtTail(int tail, int x) =>
822      _buf.setInt64(_buf.lengthInBytes - tail, x, Endian.little);
823
824  @pragma('vm:prefer-inline')
825  void _setInt32AtTail(int tail, int x) =>
826      _buf.setInt32(_buf.lengthInBytes - tail, x, Endian.little);
827
828  @pragma('vm:prefer-inline')
829  void _setUint32AtTail(int tail, int x) =>
830      _buf.setUint32(_buf.lengthInBytes - tail, x, Endian.little);
831
832  @pragma('vm:prefer-inline')
833  void _setInt16AtTail(int tail, int x) =>
834      _buf.setInt16(_buf.lengthInBytes - tail, x, Endian.little);
835
836  @pragma('vm:prefer-inline')
837  void _setUint16AtTail(int tail, int x) =>
838      _buf.setUint16(_buf.lengthInBytes - tail, x, Endian.little);
839
840  @pragma('vm:prefer-inline')
841  void _setInt8AtTail(int tail, int x) =>
842      _buf.setInt8(_buf.lengthInBytes - tail, x);
843
844  @pragma('vm:prefer-inline')
845  void _setUint8AtTail(int tail, int x) =>
846      _buf.setUint8(_buf.lengthInBytes - tail, x);
847}
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
856  @pragma('vm:prefer-inline')
857  int get size => _sizeofUint32;
858
859  @override
860  @pragma('vm:prefer-inline')
861  List<bool> read(BufferContext bc, int offset) =>
862      _FbBoolList(bc, bc.derefObject(offset));
863}
864
865/// The reader of booleans.
866class BoolReader extends Reader<bool> {
867  const BoolReader() : super();
868
869  @override
870  @pragma('vm:prefer-inline')
871  int get size => _sizeofUint8;
872
873  @override
874  @pragma('vm:prefer-inline')
875  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
885  @pragma('vm:prefer-inline')
886  int get size => _sizeofFloat64;
887
888  @override
889  @pragma('vm:prefer-inline')
890  List<double> read(BufferContext bc, int offset) =>
891      _FbFloat64List(bc, bc.derefObject(offset));
892}
893
894class Float32ListReader extends Reader<List<double>> {
895  const Float32ListReader();
896
897  @override
898  @pragma('vm:prefer-inline')
899  int get size => _sizeofFloat32;
900
901  @override
902  @pragma('vm:prefer-inline')
903  List<double> read(BufferContext bc, int offset) =>
904      _FbFloat32List(bc, bc.derefObject(offset));
905}
906
907class Float64Reader extends Reader<double> {
908  const Float64Reader();
909
910  @override
911  @pragma('vm:prefer-inline')
912  int get size => _sizeofFloat64;
913
914  @override
915  @pragma('vm:prefer-inline')
916  double read(BufferContext bc, int offset) => bc._getFloat64(offset);
917}
918
919class Float32Reader extends Reader<double> {
920  const Float32Reader();
921
922  @override
923  @pragma('vm:prefer-inline')
924  int get size => _sizeofFloat32;
925
926  @override
927  @pragma('vm:prefer-inline')
928  double read(BufferContext bc, int offset) => bc._getFloat32(offset);
929}
930
931class Int64Reader extends Reader<int> {
932  const Int64Reader() : super();
933
934  @override
935  @pragma('vm:prefer-inline')
936  int get size => _sizeofInt64;
937
938  @override
939  @pragma('vm:prefer-inline')
940  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
948  @pragma('vm:prefer-inline')
949  int get size => _sizeofInt32;
950
951  @override
952  @pragma('vm:prefer-inline')
953  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
961  @pragma('vm:prefer-inline')
962  int get size => _sizeofInt16;
963
964  @override
965  @pragma('vm:prefer-inline')
966  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
974  @pragma('vm:prefer-inline')
975  int get size => _sizeofInt8;
976
977  @override
978  @pragma('vm:prefer-inline')
979  int read(BufferContext bc, int offset) => bc._getInt8(offset);
980}
981
982/// The reader of lists of objects. Lazy by default - see [lazy].
983class ListReader<E> extends Reader<List<E>> {
984  final Reader<E> _elementReader;
985
986  /// 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});
995
996  @override
997  @pragma('vm:prefer-inline')
998  int get size => _sizeofUint32;
999
1000  @override
1001  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  }
1011}
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].
1024  @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);
1044  }
1045}
1046
1047/// The reader of string values.
1048class StringReader extends Reader<String> {
1049  final bool asciiOptimization;
1050
1051  const StringReader({this.asciiOptimization = false}) : super();
1052
1053  @override
1054  @pragma('vm:prefer-inline')
1055  int get size => _sizeofUint32;
1056
1057  @override
1058  @pragma('vm:prefer-inline')
1059  String read(BufferContext bc, int offset) {
1060    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);
1065    }
1066    return utf8.decode(bytes);
1067  }
1068
1069  @pragma('vm:prefer-inline')
1070  static bool _isLatin(Uint8List bytes) {
1071    var length = bytes.length;
1072    for (var i = 0; i < length; i++) {
1073      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
1088  @override
1089  T read(BufferContext bc, int offset) {
1090    return createObject(bc, offset);
1091  }
1092}
1093
1094/// An abstract reader for tables.
1095abstract class TableReader<T> extends Reader<T> {
1096  const TableReader();
1097
1098  @override
1099  @pragma('vm:prefer-inline')
1100  int get size => 4;
1101
1102  /// Return the object at [offset].
1103  T createObject(BufferContext bc, int offset);
1104
1105  @override
1106  T read(BufferContext bc, int offset) {
1107    var objectOffset = bc.derefObject(offset);
1108    return createObject(bc, objectOffset);
1109  }
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
1119  @pragma('vm:prefer-inline')
1120  int get size => _sizeofUint32;
1121
1122  @override
1123  @pragma('vm:prefer-inline')
1124  List<int> read(BufferContext bc, int offset) =>
1125      _FbUint32List(bc, bc.derefObject(offset));
1126}
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
1135  @pragma('vm:prefer-inline')
1136  int get size => _sizeofUint64;
1137
1138  @override
1139  @pragma('vm:prefer-inline')
1140  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
1148  @pragma('vm:prefer-inline')
1149  int get size => _sizeofUint32;
1150
1151  @override
1152  @pragma('vm:prefer-inline')
1153  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
1163  @pragma('vm:prefer-inline')
1164  int get size => _sizeofUint32;
1165
1166  @override
1167  @pragma('vm:prefer-inline')
1168  List<int> read(BufferContext bc, int offset) =>
1169      _FbUint16List(bc, bc.derefObject(offset));
1170}
1171
1172/// The reader of unsigned 32-bit integers.
1173class Uint16Reader extends Reader<int> {
1174  const Uint16Reader() : super();
1175
1176  @override
1177  @pragma('vm:prefer-inline')
1178  int get size => _sizeofUint16;
1179
1180  @override
1181  @pragma('vm:prefer-inline')
1182  int read(BufferContext bc, int offset) => bc._getUint16(offset);
1183}
1184
1185/// Reader of unmodifiable binary data (a list of unsigned 8-bit integers).
1186class Uint8ListReader extends Reader<List<int>> {
1187  /// 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});
1196
1197  @override
1198  @pragma('vm:prefer-inline')
1199  int get size => _sizeofUint32;
1200
1201  @override
1202  @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  }
1215}
1216
1217/// The reader of unsigned 8-bit integers.
1218class Uint8Reader extends Reader<int> {
1219  const Uint8Reader() : super();
1220
1221  @override
1222  @pragma('vm:prefer-inline')
1223  int get size => _sizeofUint8;
1224
1225  @override
1226  @pragma('vm:prefer-inline')
1227  int read(BufferContext bc, int offset) => bc._getUint8(offset);
1228}
1229
1230/// 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
1262/// 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
1267  @pragma('vm:prefer-inline')
1268  double operator [](int i) => bc._getFloat64(offset + 4 + 8 * i);
1269}
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
1276  @pragma('vm:prefer-inline')
1277  double operator [](int i) => bc._getFloat32(offset + 4 + 4 * i);
1278}
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
1284  List<E?>? _items;
1285
1286  _FbGenericList(this.elementReader, BufferContext bp, int offset)
1287      : super(bp, offset);
1288
1289  @override
1290  @pragma('vm:prefer-inline')
1291  E operator [](int i) {
1292    _items ??= List<E?>.filled(length, null);
1293    var item = _items![i];
1294    if (item == null) {
1295      item = elementReader.read(bc, offset + 4 + elementReader.size * i);
1296      _items![i] = item;
1297    }
1298    return item!;
1299  }
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;
1306  int? _length;
1307
1308  _FbList(this.bc, this.offset);
1309
1310  @override
1311  @pragma('vm:prefer-inline')
1312  int get length => _length ??= bc._getUint32(offset);
1313
1314  @override
1315  set length(int i) => throw StateError('Attempt to modify immutable list');
1316
1317  @override
1318  void operator []=(int i, E e) =>
1319      throw StateError('Attempt to modify immutable list');
1320}
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
1327  @pragma('vm:prefer-inline')
1328  int operator [](int i) => bc._getUint32(offset + 4 + 4 * i);
1329}
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
1336  @pragma('vm:prefer-inline')
1337  int operator [](int i) => bc._getUint16(offset + 4 + 2 * i);
1338}
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
1345  @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);
1356}
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
1363  @pragma('vm:prefer-inline')
1364  bool operator [](int i) => bc._getUint8(offset + 4 + i) == 1 ? true : false;
1365}
1366
1367/// Class that describes the structure of a table.
1368class _VTable {
1369  static const int _metadataLength = 4;
1370
1371  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);
1379
1380  /// The size of the table that uses this VTable.
1381  int tableSize = 0;
1382
1383  /// The tail of this VTable. It is used to share the same VTable between
1384  /// multiple tables of identical structure.
1385  int tail = 0;
1386
1387  int get _vTableSize => numOfUint16 * _sizeofUint16;
1388
1389  int get numOfUint16 => 1 + 1 + numFields;
1390
1391  @pragma('vm:prefer-inline')
1392  void addField(int field, int offset) {
1393    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;
1397  }
1398
1399  @pragma('vm:prefer-inline')
1400  bool _offsetsMatch(int vt2Start, ByteData buf) {
1401    assert(offsetsComputed);
1402    for (var i = 0; i < numFields; i++) {
1403      if (fieldOffsets[i] !=
1404          buf.getUint16(vt2Start + _metadataLength + (2 * i), Endian.little)) {
1405        return false;
1406      }
1407    }
1408    return true;
1409  }
1410
1411  /// Fill the [fieldOffsets] field.
1412  @pragma('vm:prefer-inline')
1413  void computeFieldOffsets(int tableTail) {
1414    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      }
1420    }
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.
1425  @pragma('vm:prefer-inline')
1426  void output(ByteData buf, int bufOffset) {
1427    assert(offsetsComputed);
1428    // 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.
1435    for (var i = 0; i < numFields; i++) {
1436      buf.setUint16(bufOffset, fieldOffsets[i], Endian.little);
1437      bufOffset += 2;
1438    }
1439  }
1440}
1441
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}
1494