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