1// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5import 'dart:collection';
6import 'dart:convert';
7import 'dart:math';
8import 'dart:typed_data';
9
10const int _sizeofUint8 = 1;
11const int _sizeofUint16 = 2;
12const int _sizeofUint32 = 4;
13const int _sizeofUint64 = 8;
14const int _sizeofInt8 = 1;
15const int _sizeofInt16 = 2;
16const int _sizeofInt32 = 4;
17const int _sizeofInt64 = 8;
18const int _sizeofFloat32 = 4;
19const int _sizeofFloat64 = 8;
20
21/// Callback used to invoke a struct builder's finish method.
22///
23/// This callback is used by other struct's `finish` methods to write the nested
24/// struct's fields inline.
25typedef void StructBuilder();
26
27/// Buffer with data and some context about it.
28class BufferContext {
29  final ByteData _buffer;
30
31  factory BufferContext.fromBytes(List<int> byteList) {
32    Uint8List uint8List = _asUint8List(byteList);
33    ByteData buf = new ByteData.view(uint8List.buffer, uint8List.offsetInBytes);
34    return new BufferContext._(buf);
35  }
36
37  BufferContext._(this._buffer);
38
39  int derefObject(int offset) {
40    return offset + _getUint32(offset);
41  }
42
43  Uint8List _asUint8LIst(int offset, int length) =>
44      _buffer.buffer.asUint8List(_buffer.offsetInBytes + offset, length);
45
46  double _getFloat64(int offset) =>
47      _buffer.getFloat64(offset, Endian.little);
48
49  double _getFloat32(int offset) =>
50      _buffer.getFloat32(offset, Endian.little);
51
52  int _getInt64(int offset) =>
53      _buffer.getInt64(offset, Endian.little);
54
55  int _getInt32(int offset) =>
56      _buffer.getInt32(offset, Endian.little);
57
58  int _getInt16(int offset) =>
59      _buffer.getInt16(offset, Endian.little);
60
61  int _getInt8(int offset) => _buffer.getInt8(offset);
62
63  int _getUint64(int offset) =>
64      _buffer.getUint64(offset, Endian.little);
65
66  int _getUint32(int offset) =>
67      _buffer.getUint32(offset, Endian.little);
68
69  int _getUint16(int offset) =>
70      _buffer.getUint16(offset, Endian.little);
71
72  int _getUint8(int offset) => _buffer.getUint8(offset);
73
74  /// If the [byteList] is already a [Uint8List] return it.
75  /// Otherwise return a [Uint8List] copy of the [byteList].
76  static Uint8List _asUint8List(List<int> byteList) {
77    if (byteList is Uint8List) {
78      return byteList;
79    } else {
80      return new Uint8List.fromList(byteList);
81    }
82  }
83}
84
85/// Class implemented by typed builders generated by flatc.
86abstract class ObjectBuilder {
87  int _firstOffset;
88
89  /// Can be used to write the data represented by this builder to the [Builder]
90  /// and reuse the offset created in multiple tables.
91  ///
92  /// Note that this method assumes you call it using the same [Builder] instance
93  /// every time. The returned offset is only good for the [Builder] used in the
94  /// first call to this method.
95  int getOrCreateOffset(Builder fbBuilder) {
96    _firstOffset ??= finish(fbBuilder);
97    return _firstOffset;
98  }
99
100  /// Writes the data in this helper to the [Builder].
101  int finish(Builder fbBuilder);
102
103  /// Convenience method that will create a new [Builder], [finish]es the data,
104  /// and returns the buffer as a [Uint8List] of bytes.
105  Uint8List toBytes();
106}
107
108/// Class that helps building flat buffers.
109class Builder {
110  final int initialSize;
111
112  /// The list of existing VTable(s).
113  //final List<_VTable> _vTables = <_VTable>[];
114  final List<int> _vTables = <int>[];
115
116  ByteData _buf;
117
118  /// The maximum alignment that has been seen so far.  If [_buf] has to be
119  /// reallocated in the future (to insert room at its start for more bytes) the
120  /// reallocation will need to be a multiple of this many bytes.
121  int _maxAlign;
122
123  /// The number of bytes that have been written to the buffer so far.  The
124  /// most recently written byte is this many bytes from the end of [_buf].
125  int _tail;
126
127  /// The location of the end of the current table, measured in bytes from the
128  /// end of [_buf], or `null` if a table is not currently being built.
129  int _currentTableEndTail;
130
131  _VTable _currentVTable;
132
133  /// Map containing all strings that have been written so far.  This allows us
134  /// to avoid duplicating strings.
135  ///
136  /// Allocated only if `internStrings` is set to true on the constructor.
137  Map<String, int> _strings;
138
139  /// Creates a new FlatBuffers Builder.
140  ///
141  /// `initialSize` is the initial array size in bytes.  The [Builder] will
142  /// automatically grow the array if/as needed.  `internStrings`, if set to
143  /// true, will cause [writeString] to pool strings in the buffer so that
144  /// identical strings will always use the same offset in tables.
145  Builder({this.initialSize: 1024, bool internStrings = false}) {
146    if (internStrings == true) {
147      _strings = new Map<String, int>();
148    }
149    reset();
150  }
151
152  /// Add the [field] with the given boolean [value].  The field is not added if
153  /// the [value] is equal to [def].  Booleans are stored as 8-bit fields with
154  /// `0` for `false` and `1` for `true`.
155  void addBool(int field, bool value, [bool def]) {
156    _ensureCurrentVTable();
157    if (value != null && value != def) {
158      _prepare(_sizeofUint8, 1);
159      _trackField(field);
160      _buf.setInt8(_buf.lengthInBytes - _tail, value ? 1 : 0);
161    }
162  }
163
164  /// Add the [field] with the given 32-bit signed integer [value].  The field is
165  /// not added if the [value] is equal to [def].
166  void addInt32(int field, int value, [int def]) {
167    _ensureCurrentVTable();
168    if (value != null && value != def) {
169      _prepare(_sizeofInt32, 1);
170      _trackField(field);
171      _setInt32AtTail(_buf, _tail, value);
172    }
173  }
174
175  /// Add the [field] with the given 32-bit signed integer [value].  The field is
176  /// not added if the [value] is equal to [def].
177  void addInt16(int field, int value, [int def]) {
178    _ensureCurrentVTable();
179    if (value != null && value != def) {
180      _prepare(_sizeofInt16, 1);
181      _trackField(field);
182      _setInt16AtTail(_buf, _tail, value);
183    }
184  }
185
186  /// Add the [field] with the given 8-bit signed integer [value].  The field is
187  /// not added if the [value] is equal to [def].
188  void addInt8(int field, int value, [int def]) {
189    _ensureCurrentVTable();
190    if (value != null && value != def) {
191      _prepare(_sizeofInt8, 1);
192      _trackField(field);
193      _setInt8AtTail(_buf, _tail, value);
194    }
195  }
196
197  void addStruct(int field, int offset) {
198    _ensureCurrentVTable();
199    _trackField(field);
200    _currentVTable.addField(field, offset);
201  }
202
203  /// Add the [field] referencing an object with the given [offset].
204  void addOffset(int field, int offset) {
205    _ensureCurrentVTable();
206    if (offset != null) {
207      _prepare(_sizeofUint32, 1);
208      _trackField(field);
209      _setUint32AtTail(_buf, _tail, _tail - offset);
210    }
211  }
212
213  /// Add the [field] with the given 32-bit unsigned integer [value].  The field
214  /// is not added if the [value] is equal to [def].
215  void addUint32(int field, int value, [int def]) {
216    _ensureCurrentVTable();
217    if (value != null && value != def) {
218      _prepare(_sizeofUint32, 1);
219      _trackField(field);
220      _setUint32AtTail(_buf, _tail, value);
221    }
222  }
223
224  /// Add the [field] with the given 32-bit unsigned integer [value].  The field
225  /// is not added if the [value] is equal to [def].
226  void addUint16(int field, int value, [int def]) {
227    _ensureCurrentVTable();
228    if (value != null && value != def) {
229      _prepare(_sizeofUint16, 1);
230      _trackField(field);
231      _setUint16AtTail(_buf, _tail, value);
232    }
233  }
234
235  /// Add the [field] with the given 8-bit unsigned integer [value].  The field
236  /// is not added if the [value] is equal to [def].
237  void addUint8(int field, int value, [int def]) {
238    _ensureCurrentVTable();
239    if (value != null && value != def) {
240      _prepare(_sizeofUint8, 1);
241      _trackField(field);
242      _setUint8AtTail(_buf, _tail, value);
243    }
244  }
245
246  /// Add the [field] with the given 32-bit float [value].  The field
247  /// is not added if the [value] is equal to [def].
248  void addFloat32(int field, double value, [double def]) {
249    _ensureCurrentVTable();
250    if (value != null && value != def) {
251      _prepare(_sizeofFloat32, 1);
252      _trackField(field);
253      _setFloat32AtTail(_buf, _tail, value);
254    }
255  }
256
257  /// Add the [field] with the given 64-bit double [value].  The field
258  /// is not added if the [value] is equal to [def].
259  void addFloat64(int field, double value, [double def]) {
260    _ensureCurrentVTable();
261    if (value != null && value != def) {
262      _prepare(_sizeofFloat64, 1);
263      _trackField(field);
264      _setFloat64AtTail(_buf, _tail, value);
265    }
266  }
267
268  /// Add the [field] with the given 64-bit unsigned integer [value].  The field
269  /// is not added if the [value] is equal to [def].
270  void addUint64(int field, int value, [double def]) {
271    _ensureCurrentVTable();
272    if (value != null && value != def) {
273      _prepare(_sizeofUint64, 1);
274      _trackField(field);
275      _setUint64AtTail(_buf, _tail, value);
276    }
277  }
278
279  /// Add the [field] with the given 64-bit unsigned integer [value].  The field
280  /// is not added if the [value] is equal to [def].
281  void addInt64(int field, int value, [double def]) {
282    _ensureCurrentVTable();
283    if (value != null && value != def) {
284      _prepare(_sizeofInt64, 1);
285      _trackField(field);
286      _setInt64AtTail(_buf, _tail, value);
287    }
288  }
289
290  /// End the current table and return its offset.
291  int endTable() {
292    if (_currentVTable == null) {
293      throw new StateError('Start a table before ending it.');
294    }
295    // Prepare for writing the VTable.
296    _prepare(_sizeofInt32, 1);
297    int tableTail = _tail;
298    // Prepare the size of the current table.
299    _currentVTable.tableSize = tableTail - _currentTableEndTail;
300    // Prepare the VTable to use for the current table.
301    int vTableTail;
302    {
303      _currentVTable.computeFieldOffsets(tableTail);
304      // Try to find an existing compatible VTable.
305      // Search backward - more likely to have recently used one
306      for (int i = _vTables.length - 1; i >= 0; i--) {
307        final int vt2Offset = _vTables[i];
308        final int vt2Start = _buf.lengthInBytes - vt2Offset;
309        final int vt2Size = _buf.getUint16(vt2Start, Endian.little);
310
311        if (_currentVTable._vTableSize == vt2Size &&
312            _currentVTable._offsetsMatch(vt2Start, _buf)) {
313          vTableTail = vt2Offset;
314          break;
315        }
316      }
317      // Write a new VTable.
318      if (vTableTail == null) {
319        _prepare(_sizeofUint16, _currentVTable.numOfUint16);
320        vTableTail = _tail;
321        _currentVTable.tail = vTableTail;
322        _currentVTable.output(_buf, _buf.lengthInBytes - _tail);
323        _vTables.add(_currentVTable.tail);
324      }
325    }
326    // Set the VTable offset.
327    _setInt32AtTail(_buf, tableTail, vTableTail - tableTail);
328    // Done with this table.
329    _currentVTable = null;
330    return tableTail;
331  }
332
333  /// This method low level method can be used to return a raw piece of the buffer
334  /// after using the the put* methods.
335  ///
336  /// Most clients should prefer calling [finish].
337  Uint8List lowFinish() {
338    int alignedTail = _tail + ((-_tail) % _maxAlign);
339    return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail);
340  }
341
342  /// Finish off the creation of the buffer.  The given [offset] is used as the
343  /// root object offset, and usually references directly or indirectly every
344  /// written object.  If [fileIdentifier] is specified (and not `null`), it is
345  /// interpreted as a 4-byte Latin-1 encoded string that should be placed at
346  /// bytes 4-7 of the file.
347  Uint8List finish(int offset, [String fileIdentifier]) {
348    _prepare(max(_sizeofUint32, _maxAlign), fileIdentifier == null ? 1 : 2);
349    int alignedTail = _tail + ((-_tail) % _maxAlign);
350    _setUint32AtTail(_buf, alignedTail, alignedTail - offset);
351    if (fileIdentifier != null) {
352      for (int i = 0; i < 4; i++) {
353        _setUint8AtTail(_buf, alignedTail - _sizeofUint32 - i,
354            fileIdentifier.codeUnitAt(i));
355      }
356    }
357    return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail);
358  }
359
360  /// Writes a Float64 to the tail of the buffer after preparing space for it.
361  ///
362  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
363  void putFloat64(double value) {
364    _prepare(_sizeofFloat64, 1);
365    _setFloat32AtTail(_buf, _tail, value);
366  }
367
368  /// Writes a Float32 to the tail of the buffer after preparing space for it.
369  ///
370  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
371  void putFloat32(double value) {
372    _prepare(_sizeofFloat32, 1);
373    _setFloat32AtTail(_buf, _tail, value);
374  }
375
376  /// Writes a Int64 to the tail of the buffer after preparing space for it.
377  ///
378  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
379  void putInt64(int value) {
380    _prepare(_sizeofInt64, 1);
381    _setInt64AtTail(_buf, _tail, value);
382  }
383
384  /// Writes a Uint32 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 putInt32(int value) {
388    _prepare(_sizeofInt32, 1);
389    _setInt32AtTail(_buf, _tail, value);
390  }
391
392  /// Writes a Uint16 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 putInt16(int value) {
396    _prepare(_sizeofInt16, 1);
397    _setInt16AtTail(_buf, _tail, value);
398  }
399
400  /// Writes a Uint8 to the tail of the buffer after preparing space for it.
401  ///
402  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
403  void putInt8(int value) {
404    _prepare(_sizeofInt8, 1);
405    _buf.setInt8(_buf.lengthInBytes - _tail, value);
406  }
407
408  /// Writes a Uint64 to the tail of the buffer after preparing space for it.
409  ///
410  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
411  void putUint64(int value) {
412    _prepare(_sizeofUint64, 1);
413    _setUint64AtTail(_buf, _tail, value);
414  }
415
416  /// Writes a Uint32 to the tail of the buffer after preparing space for it.
417  ///
418  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
419  void putUint32(int value) {
420    _prepare(_sizeofUint32, 1);
421    _setUint32AtTail(_buf, _tail, value);
422  }
423
424  /// Writes a Uint16 to the tail of the buffer after preparing space for it.
425  ///
426  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
427  void putUint16(int value) {
428    _prepare(_sizeofUint16, 1);
429    _setUint16AtTail(_buf, _tail, value);
430  }
431
432  /// Writes a Uint8 to the tail of the buffer after preparing space for it.
433  ///
434  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
435  void putUint8(int value) {
436    _prepare(_sizeofUint8, 1);
437    _buf.setUint8(_buf.lengthInBytes - _tail, value);
438  }
439
440  /// Reset the builder and make it ready for filling a new buffer.
441  void reset() {
442    _buf = new ByteData(initialSize);
443    _maxAlign = 1;
444    _tail = 0;
445    _currentVTable = null;
446    if (_strings != null) {
447      _strings = new Map<String, int>();
448    }
449  }
450
451  /// Start a new table.  Must be finished with [endTable] invocation.
452  void startTable() {
453    if (_currentVTable != null) {
454      throw new StateError('Inline tables are not supported.');
455    }
456    _currentVTable = new _VTable();
457    _currentTableEndTail = _tail;
458  }
459
460  /// Finish a Struct vector.  Most callers should preferto use [writeListOfStructs].
461  ///
462  /// Most callers should prefer [writeListOfStructs].
463  int endStructVector(int count) {
464    putUint32(count);
465    return _tail;
466  }
467
468  /// Writes a list of Structs to the buffer, returning the offset
469  int writeListOfStructs(List<ObjectBuilder> structBuilders) {
470    _ensureNoVTable();
471    for (int i = structBuilders.length - 1; i >= 0; i--) {
472      structBuilders[i].finish(this);
473    }
474    return endStructVector(structBuilders.length);
475  }
476
477  /// Write the given list of [values].
478  int writeList(List<int> values) {
479    _ensureNoVTable();
480    _prepare(_sizeofUint32, 1 + values.length);
481    final int result = _tail;
482    int tail = _tail;
483    _setUint32AtTail(_buf, tail, values.length);
484    tail -= _sizeofUint32;
485    for (int value in values) {
486      _setUint32AtTail(_buf, tail, tail - value);
487      tail -= _sizeofUint32;
488    }
489    return result;
490  }
491
492  /// Write the given list of 64-bit float [values].
493  int writeListFloat64(List<double> values) {
494    _ensureNoVTable();
495    _prepare(_sizeofFloat64, values.length, additionalBytes: _sizeofUint32);
496    final int result = _tail;
497    int tail = _tail;
498    _setUint32AtTail(_buf, tail, values.length);
499    tail -= _sizeofUint32;
500    for (double value in values) {
501      _setFloat64AtTail(_buf, tail, value);
502      tail -= _sizeofFloat64;
503    }
504    return result;
505  }
506
507  /// Write the given list of 32-bit float [values].
508  int writeListFloat32(List<double> values) {
509    _ensureNoVTable();
510    _prepare(_sizeofFloat32, 1 + values.length);
511    final int result = _tail;
512    int tail = _tail;
513    _setUint32AtTail(_buf, tail, values.length);
514    tail -= _sizeofUint32;
515    for (double value in values) {
516      _setFloat32AtTail(_buf, tail, value);
517      tail -= _sizeofFloat32;
518    }
519    return result;
520  }
521
522  /// Write the given list of signed 64-bit integer [values].
523  int writeListInt64(List<int> values) {
524    _ensureNoVTable();
525    _prepare(_sizeofInt64, values.length, additionalBytes: _sizeofUint32);
526    final int result = _tail;
527    int tail = _tail;
528    _setUint32AtTail(_buf, tail, values.length);
529    tail -= _sizeofUint32;
530    for (int value in values) {
531      _setInt64AtTail(_buf, tail, value);
532      tail -= _sizeofInt64;
533    }
534    return result;
535  }
536
537  /// Write the given list of signed 64-bit integer [values].
538  int writeListUint64(List<int> values) {
539    _ensureNoVTable();
540    _prepare(_sizeofUint64, values.length, additionalBytes: _sizeofUint32);
541    final int result = _tail;
542    int tail = _tail;
543    _setUint32AtTail(_buf, tail, values.length);
544    tail -= _sizeofUint32;
545    for (int value in values) {
546      _setUint64AtTail(_buf, tail, value);
547      tail -= _sizeofUint64;
548    }
549    return result;
550  }
551
552  /// Write the given list of signed 32-bit integer [values].
553  int writeListInt32(List<int> values) {
554    _ensureNoVTable();
555    _prepare(_sizeofUint32, 1 + values.length);
556    final int result = _tail;
557    int tail = _tail;
558    _setUint32AtTail(_buf, tail, values.length);
559    tail -= _sizeofUint32;
560    for (int value in values) {
561      _setInt32AtTail(_buf, tail, value);
562      tail -= _sizeofInt32;
563    }
564    return result;
565  }
566
567  /// Write the given list of unsigned 32-bit integer [values].
568  int writeListUint32(List<int> values) {
569    _ensureNoVTable();
570    _prepare(_sizeofUint32, 1 + values.length);
571    final int result = _tail;
572    int tail = _tail;
573    _setUint32AtTail(_buf, tail, values.length);
574    tail -= _sizeofUint32;
575    for (int value in values) {
576      _setUint32AtTail(_buf, tail, value);
577      tail -= _sizeofUint32;
578    }
579    return result;
580  }
581
582  /// Write the given list of signed 16-bit integer [values].
583  int writeListInt16(List<int> values) {
584    _ensureNoVTable();
585    _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
586    final int result = _tail;
587    int tail = _tail;
588    _setUint32AtTail(_buf, tail, values.length);
589    tail -= _sizeofUint32;
590    for (int value in values) {
591      _setInt16AtTail(_buf, tail, value);
592      tail -= _sizeofInt16;
593    }
594    return result;
595  }
596
597  /// Write the given list of unsigned 16-bit integer [values].
598  int writeListUint16(List<int> values) {
599    _ensureNoVTable();
600    _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
601    final int result = _tail;
602    int tail = _tail;
603    _setUint32AtTail(_buf, tail, values.length);
604    tail -= _sizeofUint32;
605    for (int value in values) {
606      _setUint16AtTail(_buf, tail, value);
607      tail -= _sizeofUint16;
608    }
609    return result;
610  }
611
612  /// Write the given list of bools as unsigend 8-bit integer [values].
613  int writeListBool(List<bool> values) {
614    return writeListUint8(values?.map((b) => b ? 1 : 0)?.toList());
615  }
616
617  /// Write the given list of signed 8-bit integer [values].
618  int writeListInt8(List<int> values) {
619    _ensureNoVTable();
620    _prepare(_sizeofUint32, 1, additionalBytes: values.length);
621    final int result = _tail;
622    int tail = _tail;
623    _setUint32AtTail(_buf, tail, values.length);
624    tail -= _sizeofUint32;
625    for (int value in values) {
626      _setInt8AtTail(_buf, tail, value);
627      tail -= _sizeofUint8;
628    }
629    return result;
630  }
631
632  /// Write the given list of unsigned 8-bit integer [values].
633  int writeListUint8(List<int> values) {
634    _ensureNoVTable();
635    _prepare(_sizeofUint32, 1, additionalBytes: values.length);
636    final int result = _tail;
637    int tail = _tail;
638    _setUint32AtTail(_buf, tail, values.length);
639    tail -= _sizeofUint32;
640    for (int value in values) {
641      _setUint8AtTail(_buf, tail, value);
642      tail -= _sizeofUint8;
643    }
644    return result;
645  }
646
647  /// Write the given string [value] and return its offset, or `null` if
648  /// the [value] is `null`.
649  int writeString(String value) {
650    _ensureNoVTable();
651    if (value != null) {
652      if (_strings != null) {
653        return _strings.putIfAbsent(value, () => _writeString(value));
654      } else {
655        return _writeString(value);
656      }
657    }
658    return null;
659  }
660
661  int _writeString(String value) {
662    // TODO(scheglov) optimize for ASCII strings
663    List<int> bytes = utf8.encode(value);
664    int length = bytes.length;
665    _prepare(4, 1, additionalBytes: length + 1);
666    final int result = _tail;
667    _setUint32AtTail(_buf, _tail, length);
668    int offset = _buf.lengthInBytes - _tail + 4;
669    for (int i = 0; i < length; i++) {
670      _buf.setUint8(offset++, bytes[i]);
671    }
672    return result;
673  }
674
675  /// Throw an exception if there is not currently a vtable.
676  void _ensureCurrentVTable() {
677    if (_currentVTable == null) {
678      throw new StateError('Start a table before adding values.');
679    }
680  }
681
682  /// Throw an exception if there is currently a vtable.
683  void _ensureNoVTable() {
684    if (_currentVTable != null) {
685      throw new StateError(
686          'Cannot write a non-scalar value while writing a table.');
687    }
688  }
689
690  /// The number of bytes that have been written to the buffer so far.  The
691  /// most recently written byte is this many bytes from the end of the buffer.
692  int get offset => _tail;
693
694  /// Zero-pads the buffer, which may be required for some struct layouts.
695  void pad(int howManyBytes) {
696    for (int i = 0; i < howManyBytes; i++) putUint8(0);
697  }
698
699  /// Prepare for writing the given `count` of scalars of the given `size`.
700  /// Additionally allocate the specified `additionalBytes`. Update the current
701  /// tail pointer to point at the allocated space.
702  void _prepare(int size, int count, {int additionalBytes = 0}) {
703    // Update the alignment.
704    if (_maxAlign < size) {
705      _maxAlign = size;
706    }
707    // Prepare amount of required space.
708    int dataSize = size * count + additionalBytes;
709    int alignDelta = (-(_tail + dataSize)) % size;
710    int bufSize = alignDelta + dataSize;
711    // Ensure that we have the required amount of space.
712    {
713      int oldCapacity = _buf.lengthInBytes;
714      if (_tail + bufSize > oldCapacity) {
715        int desiredNewCapacity = (oldCapacity + bufSize) * 2;
716        int deltaCapacity = desiredNewCapacity - oldCapacity;
717        deltaCapacity += (-deltaCapacity) % _maxAlign;
718        int newCapacity = oldCapacity + deltaCapacity;
719        ByteData newBuf = new ByteData(newCapacity);
720        newBuf.buffer
721            .asUint8List()
722            .setAll(deltaCapacity, _buf.buffer.asUint8List());
723        _buf = newBuf;
724      }
725    }
726    // Update the tail pointer.
727    _tail += bufSize;
728  }
729
730  /// Record the offset of the given [field].
731  void _trackField(int field) {
732    _currentVTable.addField(field, _tail);
733  }
734
735  static void _setFloat64AtTail(ByteData _buf, int tail, double x) {
736    _buf.setFloat64(_buf.lengthInBytes - tail, x, Endian.little);
737  }
738
739  static void _setFloat32AtTail(ByteData _buf, int tail, double x) {
740    _buf.setFloat32(_buf.lengthInBytes - tail, x, Endian.little);
741  }
742
743  static void _setUint64AtTail(ByteData _buf, int tail, int x) {
744    _buf.setUint64(_buf.lengthInBytes - tail, x, Endian.little);
745  }
746
747  static void _setInt64AtTail(ByteData _buf, int tail, int x) {
748    _buf.setInt64(_buf.lengthInBytes - tail, x, Endian.little);
749  }
750
751  static void _setInt32AtTail(ByteData _buf, int tail, int x) {
752    _buf.setInt32(_buf.lengthInBytes - tail, x, Endian.little);
753  }
754
755  static void _setUint32AtTail(ByteData _buf, int tail, int x) {
756    _buf.setUint32(_buf.lengthInBytes - tail, x, Endian.little);
757  }
758
759  static void _setInt16AtTail(ByteData _buf, int tail, int x) {
760    _buf.setInt16(_buf.lengthInBytes - tail, x, Endian.little);
761  }
762
763  static void _setUint16AtTail(ByteData _buf, int tail, int x) {
764    _buf.setUint16(_buf.lengthInBytes - tail, x, Endian.little);
765  }
766
767  static void _setInt8AtTail(ByteData _buf, int tail, int x) {
768    _buf.setInt8(_buf.lengthInBytes - tail, x);
769  }
770
771  static void _setUint8AtTail(ByteData _buf, int tail, int x) {
772    _buf.setUint8(_buf.lengthInBytes - tail, x);
773  }
774}
775
776/// Reader of lists of boolean values.
777///
778/// The returned unmodifiable lists lazily read values on access.
779class BoolListReader extends Reader<List<bool>> {
780  const BoolListReader();
781
782  @override
783  int get size => _sizeofUint32;
784
785  @override
786  List<bool> read(BufferContext bc, int offset) =>
787      new _FbBoolList(bc, bc.derefObject(offset));
788}
789
790/// The reader of booleans.
791class BoolReader extends Reader<bool> {
792  const BoolReader() : super();
793
794  @override
795  int get size => _sizeofUint8;
796
797  @override
798  bool read(BufferContext bc, int offset) => bc._getInt8(offset) != 0;
799}
800
801/// The reader of lists of 64-bit float values.
802///
803/// The returned unmodifiable lists lazily read values on access.
804class Float64ListReader extends Reader<List<double>> {
805  const Float64ListReader();
806
807  @override
808  int get size => _sizeofFloat64;
809
810  @override
811  List<double> read(BufferContext bc, int offset) =>
812      new _FbFloat64List(bc, bc.derefObject(offset));
813}
814
815class Float32ListReader extends Reader<List<double>> {
816  const Float32ListReader();
817
818  @override
819  int get size => _sizeofFloat32;
820
821  @override
822  List<double> read(BufferContext bc, int offset) =>
823      new _FbFloat32List(bc, bc.derefObject(offset));
824}
825
826class Float64Reader extends Reader<double> {
827  const Float64Reader();
828
829  @override
830  int get size => _sizeofFloat64;
831
832  @override
833  double read(BufferContext bc, int offset) => bc._getFloat64(offset);
834}
835
836class Float32Reader extends Reader<double> {
837  const Float32Reader();
838
839  @override
840  int get size => _sizeofFloat32;
841
842  @override
843  double read(BufferContext bc, int offset) => bc._getFloat32(offset);
844}
845
846class Int64Reader extends Reader<int> {
847  const Int64Reader() : super();
848  @override
849  int get size => _sizeofInt64;
850
851  @override
852  int read(BufferContext bc, int offset) => bc._getInt64(offset);
853}
854
855/// The reader of signed 32-bit integers.
856class Int32Reader extends Reader<int> {
857  const Int32Reader() : super();
858
859  @override
860  int get size => _sizeofInt32;
861
862  @override
863  int read(BufferContext bc, int offset) => bc._getInt32(offset);
864}
865
866/// The reader of signed 32-bit integers.
867class Int16Reader extends Reader<int> {
868  const Int16Reader() : super();
869
870  @override
871  int get size => _sizeofInt16;
872
873  @override
874  int read(BufferContext bc, int offset) => bc._getInt16(offset);
875}
876
877/// The reader of 8-bit signed integers.
878class Int8Reader extends Reader<int> {
879  const Int8Reader() : super();
880
881  @override
882  int get size => _sizeofInt8;
883
884  @override
885  int read(BufferContext bc, int offset) => bc._getInt8(offset);
886}
887
888/// The reader of lists of objects.
889///
890/// The returned unmodifiable lists lazily read objects on access.
891class ListReader<E> extends Reader<List<E>> {
892  final Reader<E> _elementReader;
893
894  const ListReader(this._elementReader);
895
896  @override
897  int get size => _sizeofUint32;
898
899  @override
900  List<E> read(BufferContext bc, int offset) =>
901      new _FbGenericList<E>(_elementReader, bc, bc.derefObject(offset));
902}
903
904/// Object that can read a value at a [BufferContext].
905abstract class Reader<T> {
906  const Reader();
907
908  /// The size of the value in bytes.
909  int get size;
910
911  /// Read the value at the given [offset] in [bc].
912  T read(BufferContext bc, int offset);
913
914  /// Read the value of the given [field] in the given [object].
915  T vTableGet(BufferContext object, int offset, int field, [T defaultValue]) {
916    int vTableSOffset = object._getInt32(offset);
917    int vTableOffset = offset - vTableSOffset;
918    int vTableSize = object._getUint16(vTableOffset);
919    int vTableFieldOffset = field;
920    if (vTableFieldOffset < vTableSize) {
921      int fieldOffsetInObject =
922          object._getUint16(vTableOffset + vTableFieldOffset);
923      if (fieldOffsetInObject != 0) {
924        return read(object, offset + fieldOffsetInObject);
925      }
926    }
927    return defaultValue;
928  }
929}
930
931/// The reader of string values.
932class StringReader extends Reader<String> {
933  const StringReader() : super();
934
935  @override
936  int get size => 4;
937
938  @override
939  String read(BufferContext bc, int offset) {
940    int strOffset = bc.derefObject(offset);
941    int length = bc._getUint32(strOffset);
942    Uint8List bytes = bc._asUint8LIst(strOffset + 4, length);
943    if (_isLatin(bytes)) {
944      return new String.fromCharCodes(bytes);
945    }
946    return utf8.decode(bytes);
947  }
948
949  static bool _isLatin(Uint8List bytes) {
950    int length = bytes.length;
951    for (int i = 0; i < length; i++) {
952      if (bytes[i] > 127) {
953        return false;
954      }
955    }
956    return true;
957  }
958}
959
960/// An abstract reader for structs.
961abstract class StructReader<T> extends Reader<T> {
962  const StructReader();
963
964  /// Return the object at `offset`.
965  T createObject(BufferContext bc, int offset);
966
967  T read(BufferContext bp, int offset) {
968    return createObject(bp, offset);
969  }
970}
971
972/// An abstract reader for tables.
973abstract class TableReader<T> extends Reader<T> {
974  const TableReader();
975
976  @override
977  int get size => 4;
978
979  /// Return the object at [offset].
980  T createObject(BufferContext bc, int offset);
981
982  @override
983  T read(BufferContext bp, int offset) {
984    int objectOffset = bp.derefObject(offset);
985    return createObject(bp, objectOffset);
986  }
987}
988
989/// Reader of lists of unsigned 32-bit integer values.
990///
991/// The returned unmodifiable lists lazily read values on access.
992class Uint32ListReader extends Reader<List<int>> {
993  const Uint32ListReader();
994
995  @override
996  int get size => _sizeofUint32;
997
998  @override
999  List<int> read(BufferContext bc, int offset) =>
1000      new _FbUint32List(bc, bc.derefObject(offset));
1001}
1002
1003/// The reader of unsigned 64-bit integers.
1004///
1005/// WARNING: May have compatibility issues with JavaScript
1006class Uint64Reader extends Reader<int> {
1007  const Uint64Reader() : super();
1008
1009  @override
1010  int get size => _sizeofUint64;
1011
1012  @override
1013  int read(BufferContext bc, int offset) => bc._getUint64(offset);
1014}
1015
1016/// The reader of unsigned 32-bit integers.
1017class Uint32Reader extends Reader<int> {
1018  const Uint32Reader() : super();
1019
1020  @override
1021  int get size => _sizeofUint32;
1022
1023  @override
1024  int read(BufferContext bc, int offset) => bc._getUint32(offset);
1025}
1026
1027/// Reader of lists of unsigned 32-bit integer values.
1028///
1029/// The returned unmodifiable lists lazily read values on access.
1030class Uint16ListReader extends Reader<List<int>> {
1031  const Uint16ListReader();
1032
1033  @override
1034  int get size => _sizeofUint32;
1035
1036  @override
1037  List<int> read(BufferContext bc, int offset) =>
1038      new _FbUint16List(bc, bc.derefObject(offset));
1039}
1040
1041/// The reader of unsigned 32-bit integers.
1042class Uint16Reader extends Reader<int> {
1043  const Uint16Reader() : super();
1044
1045  @override
1046  int get size => _sizeofUint16;
1047
1048  @override
1049  int read(BufferContext bc, int offset) => bc._getUint16(offset);
1050}
1051
1052/// Reader of lists of unsigned 8-bit integer values.
1053///
1054/// The returned unmodifiable lists lazily read values on access.
1055class Uint8ListReader extends Reader<List<int>> {
1056  const Uint8ListReader();
1057
1058  @override
1059  int get size => _sizeofUint32;
1060
1061  @override
1062  List<int> read(BufferContext bc, int offset) =>
1063      new _FbUint8List(bc, bc.derefObject(offset));
1064}
1065
1066/// The reader of unsigned 8-bit integers.
1067class Uint8Reader extends Reader<int> {
1068  const Uint8Reader() : super();
1069
1070  @override
1071  int get size => _sizeofUint8;
1072
1073  @override
1074  int read(BufferContext bc, int offset) => bc._getUint8(offset);
1075}
1076
1077/// The list backed by 64-bit values - Uint64 length and Float64.
1078class _FbFloat64List extends _FbList<double> {
1079  _FbFloat64List(BufferContext bc, int offset) : super(bc, offset);
1080
1081  @override
1082  double operator [](int i) {
1083    return bc._getFloat64(offset + 4 + 8 * i);
1084  }
1085}
1086
1087/// The list backed by 32-bit values - Float32.
1088class _FbFloat32List extends _FbList<double> {
1089  _FbFloat32List(BufferContext bc, int offset) : super(bc, offset);
1090
1091  @override
1092  double operator [](int i) {
1093    return bc._getFloat32(offset + 4 + 4 * i);
1094  }
1095}
1096
1097/// List backed by a generic object which may have any size.
1098class _FbGenericList<E> extends _FbList<E> {
1099  final Reader<E> elementReader;
1100
1101  List<E> _items;
1102
1103  _FbGenericList(this.elementReader, BufferContext bp, int offset)
1104      : super(bp, offset);
1105
1106  @override
1107  E operator [](int i) {
1108    _items ??= new List<E>(length);
1109    E item = _items[i];
1110    if (item == null) {
1111      item = elementReader.read(bc, offset + 4 + elementReader.size * i);
1112      _items[i] = item;
1113    }
1114    return item;
1115  }
1116}
1117
1118/// The base class for immutable lists read from flat buffers.
1119abstract class _FbList<E> extends Object with ListMixin<E> implements List<E> {
1120  final BufferContext bc;
1121  final int offset;
1122  int _length;
1123
1124  _FbList(this.bc, this.offset);
1125
1126  @override
1127  int get length {
1128    _length ??= bc._getUint32(offset);
1129    return _length;
1130  }
1131
1132  @override
1133  void set length(int i) =>
1134      throw new StateError('Attempt to modify immutable list');
1135
1136  @override
1137  void operator []=(int i, E e) =>
1138      throw new StateError('Attempt to modify immutable list');
1139}
1140
1141/// List backed by 32-bit unsigned integers.
1142class _FbUint32List extends _FbList<int> {
1143  _FbUint32List(BufferContext bc, int offset) : super(bc, offset);
1144
1145  @override
1146  int operator [](int i) {
1147    return bc._getUint32(offset + 4 + 4 * i);
1148  }
1149}
1150
1151/// List backed by 16-bit unsigned integers.
1152class _FbUint16List extends _FbList<int> {
1153  _FbUint16List(BufferContext bc, int offset) : super(bc, offset);
1154
1155  @override
1156  int operator [](int i) {
1157    return bc._getUint16(offset + 4 + 2 * i);
1158  }
1159}
1160
1161/// List backed by 8-bit unsigned integers.
1162class _FbUint8List extends _FbList<int> {
1163  _FbUint8List(BufferContext bc, int offset) : super(bc, offset);
1164
1165  @override
1166  int operator [](int i) {
1167    return bc._getUint8(offset + 4 + i);
1168  }
1169}
1170
1171/// List backed by 8-bit unsigned integers.
1172class _FbBoolList extends _FbList<bool> {
1173  _FbBoolList(BufferContext bc, int offset) : super(bc, offset);
1174
1175  @override
1176  bool operator [](int i) {
1177    return bc._getUint8(offset + 4 + i) == 1 ? true : false;
1178  }
1179}
1180
1181/// Class that describes the structure of a table.
1182class _VTable {
1183  static const int _metadataLength = 4;
1184
1185  final List<int> fieldTails = <int>[];
1186  final List<int> fieldOffsets = <int>[];
1187
1188  /// The size of the table that uses this VTable.
1189  int tableSize;
1190
1191  /// The tail of this VTable.  It is used to share the same VTable between
1192  /// multiple tables of identical structure.
1193  int tail;
1194
1195  int get _vTableSize => numOfUint16 * _sizeofUint16;
1196
1197  int get numOfUint16 => 1 + 1 + fieldTails.length;
1198
1199  void addField(int field, int offset) {
1200    while (fieldTails.length <= field) {
1201      fieldTails.add(null);
1202    }
1203    fieldTails[field] = offset;
1204  }
1205
1206  bool _offsetsMatch(int vt2Start, ByteData buf) {
1207    for (int i = 0; i < fieldOffsets.length; i++) {
1208      if (fieldOffsets[i] !=
1209          buf.getUint16(
1210              vt2Start + _metadataLength + (2 * i), Endian.little)) {
1211        return false;
1212      }
1213    }
1214    return true;
1215  }
1216
1217  /// Fill the [fieldOffsets] field.
1218  void computeFieldOffsets(int tableTail) {
1219    assert(fieldOffsets.isEmpty);
1220    for (int fieldTail in fieldTails) {
1221      int fieldOffset = fieldTail == null ? 0 : tableTail - fieldTail;
1222      fieldOffsets.add(fieldOffset);
1223    }
1224  }
1225
1226  /// Outputs this VTable to [buf], which is is expected to be aligned to 16-bit
1227  /// and have at least [numOfUint16] 16-bit words available.
1228  void output(ByteData buf, int bufOffset) {
1229    // VTable size.
1230    buf.setUint16(bufOffset, numOfUint16 * 2, Endian.little);
1231    bufOffset += 2;
1232    // Table size.
1233    buf.setUint16(bufOffset, tableSize, Endian.little);
1234    bufOffset += 2;
1235    // Field offsets.
1236    for (int fieldOffset in fieldOffsets) {
1237      buf.setUint16(bufOffset, fieldOffset, Endian.little);
1238      bufOffset += 2;
1239    }
1240  }
1241}
1242