...

Text file src/github.com/google/flatbuffers/dart/lib/flat_buffers.dart

Documentation: github.com/google/flatbuffers/dart/lib

     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    final 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 (final 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 (final 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 (final 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 (final 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 (final 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 (final 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 (final 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 (final 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 (final 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 (final 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 (final 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    final dataSize = size * count + additionalBytes;
   781    final alignDelta = (-(_tail + dataSize)) & (size - 1);
   782    final bufSize = alignDelta + dataSize;
   783    // Ensure that we have the required amount of space.
   784    {
   785      final oldCapacity = _buf.lengthInBytes;
   786      if (_tail + bufSize > oldCapacity) {
   787        final desiredNewCapacity = (oldCapacity + bufSize) * 2;
   788        var deltaCapacity = desiredNewCapacity - oldCapacity;
   789        deltaCapacity += (-deltaCapacity) & (_maxAlign - 1);
   790        final 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    final 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    final 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    final vTableSOffset = object._getInt32(offset);
  1040    final vTableOffset = offset - vTableSOffset;
  1041    final 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    final strOffset = bc.derefObject(offset);
  1061    final length = bc._getUint32(strOffset);
  1062    final 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    final 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    final 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}

View as plain text