Squashed 'third_party/flatbuffers/' content from commit acc9990ab

Change-Id: I48550d40d78fea996ebe74e9723a5d1f910de491
git-subtree-dir: third_party/flatbuffers
git-subtree-split: acc9990abd2206491480291b0f85f925110102ea
diff --git a/dart/lib/flat_buffers.dart b/dart/lib/flat_buffers.dart
new file mode 100644
index 0000000..e251260
--- /dev/null
+++ b/dart/lib/flat_buffers.dart
@@ -0,0 +1,1241 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:collection';
+import 'dart:convert';
+import 'dart:math';
+import 'dart:typed_data';
+
+const int _sizeofUint8 = 1;
+const int _sizeofUint16 = 2;
+const int _sizeofUint32 = 4;
+const int _sizeofUint64 = 8;
+const int _sizeofInt8 = 1;
+const int _sizeofInt16 = 2;
+const int _sizeofInt32 = 4;
+const int _sizeofInt64 = 8;
+const int _sizeofFloat32 = 4;
+const int _sizeofFloat64 = 8;
+
+/// Callback used to invoke a struct builder's finish method.
+///
+/// This callback is used by other struct's `finish` methods to write the nested
+/// struct's fields inline.
+typedef void StructBuilder();
+
+/// Buffer with data and some context about it.
+class BufferContext {
+  final ByteData _buffer;
+
+  factory BufferContext.fromBytes(List<int> byteList) {
+    Uint8List uint8List = _asUint8List(byteList);
+    ByteData buf = new ByteData.view(uint8List.buffer, uint8List.offsetInBytes);
+    return new BufferContext._(buf);
+  }
+
+  BufferContext._(this._buffer);
+
+  int derefObject(int offset) {
+    return offset + _getUint32(offset);
+  }
+
+  Uint8List _asUint8LIst(int offset, int length) =>
+      _buffer.buffer.asUint8List(_buffer.offsetInBytes + offset, length);
+
+  double _getFloat64(int offset) =>
+      _buffer.getFloat64(offset, Endian.little);
+
+  double _getFloat32(int offset) =>
+      _buffer.getFloat32(offset, Endian.little);
+
+  int _getInt64(int offset) =>
+      _buffer.getInt64(offset, Endian.little);
+
+  int _getInt32(int offset) =>
+      _buffer.getInt32(offset, Endian.little);
+
+  int _getInt16(int offset) =>
+      _buffer.getInt16(offset, Endian.little);
+
+  int _getInt8(int offset) => _buffer.getInt8(offset);
+
+  int _getUint64(int offset) =>
+      _buffer.getUint64(offset, Endian.little);
+
+  int _getUint32(int offset) =>
+      _buffer.getUint32(offset, Endian.little);
+
+  int _getUint16(int offset) =>
+      _buffer.getUint16(offset, Endian.little);
+
+  int _getUint8(int offset) => _buffer.getUint8(offset);
+
+  /// If the [byteList] is already a [Uint8List] return it.
+  /// Otherwise return a [Uint8List] copy of the [byteList].
+  static Uint8List _asUint8List(List<int> byteList) {
+    if (byteList is Uint8List) {
+      return byteList;
+    } else {
+      return new Uint8List.fromList(byteList);
+    }
+  }
+}
+
+/// Class implemented by typed builders generated by flatc.
+abstract class ObjectBuilder {
+  int _firstOffset;
+
+  /// Can be used to write the data represented by this builder to the [Builder]
+  /// and reuse the offset created in multiple tables.
+  ///
+  /// Note that this method assumes you call it using the same [Builder] instance
+  /// every time. The returned offset is only good for the [Builder] used in the
+  /// first call to this method.
+  int getOrCreateOffset(Builder fbBuilder) {
+    _firstOffset ??= finish(fbBuilder);
+    return _firstOffset;
+  }
+
+  /// Writes the data in this helper to the [Builder].
+  int finish(Builder fbBuilder);
+
+  /// Convenience method that will create a new [Builder], [finish]es the data,
+  /// and returns the buffer as a [Uint8List] of bytes.
+  Uint8List toBytes();
+}
+
+/// Class that helps building flat buffers.
+class Builder {
+  final int initialSize;
+
+  /// The list of existing VTable(s).
+  //final List<_VTable> _vTables = <_VTable>[];
+  final List<int> _vTables = <int>[];
+
+  ByteData _buf;
+
+  /// The maximum alignment that has been seen so far.  If [_buf] has to be
+  /// reallocated in the future (to insert room at its start for more bytes) the
+  /// reallocation will need to be a multiple of this many bytes.
+  int _maxAlign;
+
+  /// The number of bytes that have been written to the buffer so far.  The
+  /// most recently written byte is this many bytes from the end of [_buf].
+  int _tail;
+
+  /// The location of the end of the current table, measured in bytes from the
+  /// end of [_buf], or `null` if a table is not currently being built.
+  int _currentTableEndTail;
+
+  _VTable _currentVTable;
+
+  /// Map containing all strings that have been written so far.  This allows us
+  /// to avoid duplicating strings.
+  ///
+  /// Allocated only if `internStrings` is set to true on the constructor.
+  Map<String, int> _strings;
+
+  /// Creates a new FlatBuffers Builder.
+  ///
+  /// `initialSize` is the initial array size in bytes.  The [Builder] will
+  /// automatically grow the array if/as needed.  `internStrings`, if set to
+  /// true, will cause [writeString] to pool strings in the buffer so that
+  /// identical strings will always use the same offset in tables.
+  Builder({this.initialSize: 1024, bool internStrings = false}) {
+    if (internStrings == true) {
+      _strings = new Map<String, int>();
+    }
+    reset();
+  }
+
+  /// Add the [field] with the given boolean [value].  The field is not added if
+  /// the [value] is equal to [def].  Booleans are stored as 8-bit fields with
+  /// `0` for `false` and `1` for `true`.
+  void addBool(int field, bool value, [bool def]) {
+    _ensureCurrentVTable();
+    if (value != null && value != def) {
+      _prepare(_sizeofUint8, 1);
+      _trackField(field);
+      _buf.setInt8(_buf.lengthInBytes - _tail, value ? 1 : 0);
+    }
+  }
+
+  /// Add the [field] with the given 32-bit signed integer [value].  The field is
+  /// not added if the [value] is equal to [def].
+  void addInt32(int field, int value, [int def]) {
+    _ensureCurrentVTable();
+    if (value != null && value != def) {
+      _prepare(_sizeofInt32, 1);
+      _trackField(field);
+      _setInt32AtTail(_buf, _tail, value);
+    }
+  }
+
+  /// Add the [field] with the given 32-bit signed integer [value].  The field is
+  /// not added if the [value] is equal to [def].
+  void addInt16(int field, int value, [int def]) {
+    _ensureCurrentVTable();
+    if (value != null && value != def) {
+      _prepare(_sizeofInt16, 1);
+      _trackField(field);
+      _setInt16AtTail(_buf, _tail, value);
+    }
+  }
+
+  /// Add the [field] with the given 8-bit signed integer [value].  The field is
+  /// not added if the [value] is equal to [def].
+  void addInt8(int field, int value, [int def]) {
+    _ensureCurrentVTable();
+    if (value != null && value != def) {
+      _prepare(_sizeofInt8, 1);
+      _trackField(field);
+      _setInt8AtTail(_buf, _tail, value);
+    }
+  }
+
+  void addStruct(int field, int offset) {
+    _ensureCurrentVTable();
+    _trackField(field);
+    _currentVTable.addField(field, offset);
+  }
+
+  /// Add the [field] referencing an object with the given [offset].
+  void addOffset(int field, int offset) {
+    _ensureCurrentVTable();
+    if (offset != null) {
+      _prepare(_sizeofUint32, 1);
+      _trackField(field);
+      _setUint32AtTail(_buf, _tail, _tail - offset);
+    }
+  }
+
+  /// Add the [field] with the given 32-bit unsigned integer [value].  The field
+  /// is not added if the [value] is equal to [def].
+  void addUint32(int field, int value, [int def]) {
+    _ensureCurrentVTable();
+    if (value != null && value != def) {
+      _prepare(_sizeofUint32, 1);
+      _trackField(field);
+      _setUint32AtTail(_buf, _tail, value);
+    }
+  }
+
+  /// Add the [field] with the given 32-bit unsigned integer [value].  The field
+  /// is not added if the [value] is equal to [def].
+  void addUint16(int field, int value, [int def]) {
+    _ensureCurrentVTable();
+    if (value != null && value != def) {
+      _prepare(_sizeofUint16, 1);
+      _trackField(field);
+      _setUint16AtTail(_buf, _tail, value);
+    }
+  }
+
+  /// Add the [field] with the given 8-bit unsigned integer [value].  The field
+  /// is not added if the [value] is equal to [def].
+  void addUint8(int field, int value, [int def]) {
+    _ensureCurrentVTable();
+    if (value != null && value != def) {
+      _prepare(_sizeofUint8, 1);
+      _trackField(field);
+      _setUint8AtTail(_buf, _tail, value);
+    }
+  }
+
+  /// Add the [field] with the given 32-bit float [value].  The field
+  /// is not added if the [value] is equal to [def].
+  void addFloat32(int field, double value, [double def]) {
+    _ensureCurrentVTable();
+    if (value != null && value != def) {
+      _prepare(_sizeofFloat32, 1);
+      _trackField(field);
+      _setFloat32AtTail(_buf, _tail, value);
+    }
+  }
+
+  /// Add the [field] with the given 64-bit double [value].  The field
+  /// is not added if the [value] is equal to [def].
+  void addFloat64(int field, double value, [double def]) {
+    _ensureCurrentVTable();
+    if (value != null && value != def) {
+      _prepare(_sizeofFloat64, 1);
+      _trackField(field);
+      _setFloat64AtTail(_buf, _tail, value);
+    }
+  }
+
+  /// Add the [field] with the given 64-bit unsigned integer [value].  The field
+  /// is not added if the [value] is equal to [def].
+  void addUint64(int field, int value, [double def]) {
+    _ensureCurrentVTable();
+    if (value != null && value != def) {
+      _prepare(_sizeofUint64, 1);
+      _trackField(field);
+      _setUint64AtTail(_buf, _tail, value);
+    }
+  }
+
+  /// Add the [field] with the given 64-bit unsigned integer [value].  The field
+  /// is not added if the [value] is equal to [def].
+  void addInt64(int field, int value, [double def]) {
+    _ensureCurrentVTable();
+    if (value != null && value != def) {
+      _prepare(_sizeofInt64, 1);
+      _trackField(field);
+      _setInt64AtTail(_buf, _tail, value);
+    }
+  }
+
+  /// End the current table and return its offset.
+  int endTable() {
+    if (_currentVTable == null) {
+      throw new StateError('Start a table before ending it.');
+    }
+    // Prepare for writing the VTable.
+    _prepare(_sizeofInt32, 1);
+    int tableTail = _tail;
+    // Prepare the size of the current table.
+    _currentVTable.tableSize = tableTail - _currentTableEndTail;
+    // Prepare the VTable to use for the current table.
+    int vTableTail;
+    {
+      _currentVTable.computeFieldOffsets(tableTail);
+      // Try to find an existing compatible VTable.
+      // Search backward - more likely to have recently used one
+      for (int i = _vTables.length - 1; i >= 0; i--) {
+        final int vt2Offset = _vTables[i];
+        final int vt2Start = _buf.lengthInBytes - vt2Offset;
+        final int vt2Size = _buf.getUint16(vt2Start, Endian.little);
+
+        if (_currentVTable._vTableSize == vt2Size &&
+            _currentVTable._offsetsMatch(vt2Start, _buf)) {
+          vTableTail = vt2Offset;
+          break;
+        }
+      }
+      // Write a new VTable.
+      if (vTableTail == null) {
+        _prepare(_sizeofUint16, _currentVTable.numOfUint16);
+        vTableTail = _tail;
+        _currentVTable.tail = vTableTail;
+        _currentVTable.output(_buf, _buf.lengthInBytes - _tail);
+        _vTables.add(_currentVTable.tail);
+      }
+    }
+    // Set the VTable offset.
+    _setInt32AtTail(_buf, tableTail, vTableTail - tableTail);
+    // Done with this table.
+    _currentVTable = null;
+    return tableTail;
+  }
+
+  /// This method low level method can be used to return a raw piece of the buffer
+  /// after using the the put* methods.
+  ///
+  /// Most clients should prefer calling [finish].
+  Uint8List lowFinish() {
+    int alignedTail = _tail + ((-_tail) % _maxAlign);
+    return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail);
+  }
+
+  /// Finish off the creation of the buffer.  The given [offset] is used as the
+  /// root object offset, and usually references directly or indirectly every
+  /// written object.  If [fileIdentifier] is specified (and not `null`), it is
+  /// interpreted as a 4-byte Latin-1 encoded string that should be placed at
+  /// bytes 4-7 of the file.
+  Uint8List finish(int offset, [String fileIdentifier]) {
+    _prepare(max(_sizeofUint32, _maxAlign), fileIdentifier == null ? 1 : 2);
+    int alignedTail = _tail + ((-_tail) % _maxAlign);
+    _setUint32AtTail(_buf, alignedTail, alignedTail - offset);
+    if (fileIdentifier != null) {
+      for (int i = 0; i < 4; i++) {
+        _setUint8AtTail(_buf, alignedTail - _sizeofUint32 - i,
+            fileIdentifier.codeUnitAt(i));
+      }
+    }
+    return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail);
+  }
+
+  /// Writes a Float64 to the tail of the buffer after preparing space for it.
+  ///
+  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
+  void putFloat64(double value) {
+    _prepare(_sizeofFloat64, 1);
+    _setFloat32AtTail(_buf, _tail, value);
+  }
+
+  /// Writes a Float32 to the tail of the buffer after preparing space for it.
+  ///
+  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
+  void putFloat32(double value) {
+    _prepare(_sizeofFloat32, 1);
+    _setFloat32AtTail(_buf, _tail, value);
+  }
+
+  /// Writes a Int64 to the tail of the buffer after preparing space for it.
+  ///
+  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
+  void putInt64(int value) {
+    _prepare(_sizeofInt64, 1);
+    _setInt64AtTail(_buf, _tail, value);
+  }
+
+  /// Writes a Uint32 to the tail of the buffer after preparing space for it.
+  ///
+  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
+  void putInt32(int value) {
+    _prepare(_sizeofInt32, 1);
+    _setInt32AtTail(_buf, _tail, value);
+  }
+
+  /// Writes a Uint16 to the tail of the buffer after preparing space for it.
+  ///
+  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
+  void putInt16(int value) {
+    _prepare(_sizeofInt16, 1);
+    _setInt16AtTail(_buf, _tail, value);
+  }
+
+  /// Writes a Uint8 to the tail of the buffer after preparing space for it.
+  ///
+  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
+  void putInt8(int value) {
+    _prepare(_sizeofInt8, 1);
+    _buf.setInt8(_buf.lengthInBytes - _tail, value);
+  }
+
+  /// Writes a Uint64 to the tail of the buffer after preparing space for it.
+  ///
+  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
+  void putUint64(int value) {
+    _prepare(_sizeofUint64, 1);
+    _setUint64AtTail(_buf, _tail, value);
+  }
+
+  /// Writes a Uint32 to the tail of the buffer after preparing space for it.
+  ///
+  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
+  void putUint32(int value) {
+    _prepare(_sizeofUint32, 1);
+    _setUint32AtTail(_buf, _tail, value);
+  }
+
+  /// Writes a Uint16 to the tail of the buffer after preparing space for it.
+  ///
+  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
+  void putUint16(int value) {
+    _prepare(_sizeofUint16, 1);
+    _setUint16AtTail(_buf, _tail, value);
+  }
+
+  /// Writes a Uint8 to the tail of the buffer after preparing space for it.
+  ///
+  /// Updates the [offset] pointer.  This method is intended for use when writing structs to the buffer.
+  void putUint8(int value) {
+    _prepare(_sizeofUint8, 1);
+    _buf.setUint8(_buf.lengthInBytes - _tail, value);
+  }
+
+  /// Reset the builder and make it ready for filling a new buffer.
+  void reset() {
+    _buf = new ByteData(initialSize);
+    _maxAlign = 1;
+    _tail = 0;
+    _currentVTable = null;
+    if (_strings != null) {
+      _strings = new Map<String, int>();
+    }
+  }
+
+  /// Start a new table.  Must be finished with [endTable] invocation.
+  void startTable() {
+    if (_currentVTable != null) {
+      throw new StateError('Inline tables are not supported.');
+    }
+    _currentVTable = new _VTable();
+    _currentTableEndTail = _tail;
+  }
+
+  /// Finish a Struct vector.  Most callers should preferto use [writeListOfStructs].
+  ///
+  /// Most callers should prefer [writeListOfStructs].
+  int endStructVector(int count) {
+    putUint32(count);
+    return _tail;
+  }
+
+  /// Writes a list of Structs to the buffer, returning the offset
+  int writeListOfStructs(List<ObjectBuilder> structBuilders) {
+    _ensureNoVTable();
+    for (int i = structBuilders.length - 1; i >= 0; i--) {
+      structBuilders[i].finish(this);
+    }
+    return endStructVector(structBuilders.length);
+  }
+
+  /// Write the given list of [values].
+  int writeList(List<int> values) {
+    _ensureNoVTable();
+    _prepare(_sizeofUint32, 1 + values.length);
+    final int result = _tail;
+    int tail = _tail;
+    _setUint32AtTail(_buf, tail, values.length);
+    tail -= _sizeofUint32;
+    for (int value in values) {
+      _setUint32AtTail(_buf, tail, tail - value);
+      tail -= _sizeofUint32;
+    }
+    return result;
+  }
+
+  /// Write the given list of 64-bit float [values].
+  int writeListFloat64(List<double> values) {
+    _ensureNoVTable();
+    _prepare(4, 1 + (2 * values.length));
+    final int result = _tail;
+    int tail = _tail;
+    _setUint32AtTail(_buf, tail, values.length);
+    tail -= _sizeofUint32;
+    for (double value in values) {
+      _setFloat64AtTail(_buf, tail, value);
+      tail -= _sizeofFloat64;
+    }
+    return result;
+  }
+
+  /// Write the given list of 32-bit float [values].
+  int writeListFloat32(List<double> values) {
+    _ensureNoVTable();
+    _prepare(_sizeofFloat32, 1 + values.length);
+    final int result = _tail;
+    int tail = _tail;
+    _setUint32AtTail(_buf, tail, values.length);
+    tail -= _sizeofUint32;
+    for (double value in values) {
+      _setFloat32AtTail(_buf, tail, value);
+      tail -= _sizeofFloat32;
+    }
+    return result;
+  }
+
+  /// Write the given list of signed 64-bit integer [values].
+  int writeListInt64(List<int> values) {
+    _ensureNoVTable();
+    _prepare(_sizeofUint32, 2 * values.length);
+    final int result = _tail;
+    int tail = _tail;
+    _setUint32AtTail(_buf, tail, values.length);
+    tail -= _sizeofUint32;
+    for (int value in values) {
+      _setInt64AtTail(_buf, tail, value);
+      tail -= _sizeofInt64;
+    }
+    return result;
+  }
+
+  /// Write the given list of signed 64-bit integer [values].
+  int writeListUint64(List<int> values) {
+    _ensureNoVTable();
+    _prepare(_sizeofUint32, 2 * values.length);
+    final int result = _tail;
+    int tail = _tail;
+    _setUint32AtTail(_buf, tail, values.length);
+    tail -= _sizeofUint32;
+    for (int value in values) {
+      _setUint64AtTail(_buf, tail, value);
+      tail -= _sizeofUint64;
+    }
+    return result;
+  }
+
+  /// Write the given list of signed 32-bit integer [values].
+  int writeListInt32(List<int> values) {
+    _ensureNoVTable();
+    _prepare(_sizeofUint32, 1 + values.length);
+    final int result = _tail;
+    int tail = _tail;
+    _setUint32AtTail(_buf, tail, values.length);
+    tail -= _sizeofUint32;
+    for (int value in values) {
+      _setInt32AtTail(_buf, tail, value);
+      tail -= _sizeofInt32;
+    }
+    return result;
+  }
+
+  /// Write the given list of unsigned 32-bit integer [values].
+  int writeListUint32(List<int> values) {
+    _ensureNoVTable();
+    _prepare(_sizeofUint32, 1 + values.length);
+    final int result = _tail;
+    int tail = _tail;
+    _setUint32AtTail(_buf, tail, values.length);
+    tail -= _sizeofUint32;
+    for (int value in values) {
+      _setUint32AtTail(_buf, tail, value);
+      tail -= _sizeofUint32;
+    }
+    return result;
+  }
+
+  /// Write the given list of signed 16-bit integer [values].
+  int writeListInt16(List<int> values) {
+    _ensureNoVTable();
+    _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
+    final int result = _tail;
+    int tail = _tail;
+    _setUint32AtTail(_buf, tail, values.length);
+    tail -= _sizeofUint32;
+    for (int value in values) {
+      _setInt16AtTail(_buf, tail, value);
+      tail -= _sizeofInt16;
+    }
+    return result;
+  }
+
+  /// Write the given list of unsigned 16-bit integer [values].
+  int writeListUint16(List<int> values) {
+    _ensureNoVTable();
+    _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
+    final int result = _tail;
+    int tail = _tail;
+    _setUint32AtTail(_buf, tail, values.length);
+    tail -= _sizeofUint32;
+    for (int value in values) {
+      _setUint16AtTail(_buf, tail, value);
+      tail -= _sizeofUint16;
+    }
+    return result;
+  }
+
+  /// Write the given list of bools as unsigend 8-bit integer [values].
+  int writeListBool(List<bool> values) {
+    return writeListUint8(values?.map((b) => b ? 1 : 0)?.toList());
+  }
+
+  /// Write the given list of signed 8-bit integer [values].
+  int writeListInt8(List<int> values) {
+    _ensureNoVTable();
+    _prepare(_sizeofUint32, 1, additionalBytes: values.length);
+    final int result = _tail;
+    int tail = _tail;
+    _setUint32AtTail(_buf, tail, values.length);
+    tail -= _sizeofUint32;
+    for (int value in values) {
+      _setInt8AtTail(_buf, tail, value);
+      tail -= _sizeofUint8;
+    }
+    return result;
+  }
+
+  /// Write the given list of unsigned 8-bit integer [values].
+  int writeListUint8(List<int> values) {
+    _ensureNoVTable();
+    _prepare(_sizeofUint32, 1, additionalBytes: values.length);
+    final int result = _tail;
+    int tail = _tail;
+    _setUint32AtTail(_buf, tail, values.length);
+    tail -= _sizeofUint32;
+    for (int value in values) {
+      _setUint8AtTail(_buf, tail, value);
+      tail -= _sizeofUint8;
+    }
+    return result;
+  }
+
+  /// Write the given string [value] and return its offset, or `null` if
+  /// the [value] is `null`.
+  int writeString(String value) {
+    _ensureNoVTable();
+    if (value != null) {
+      if (_strings != null) {
+        return _strings.putIfAbsent(value, () => _writeString(value));
+      } else {
+        return _writeString(value);
+      }
+    }
+    return null;
+  }
+
+  int _writeString(String value) {
+    // TODO(scheglov) optimize for ASCII strings
+    List<int> bytes = utf8.encode(value);
+    int length = bytes.length;
+    _prepare(4, 1, additionalBytes: length + 1);
+    final int result = _tail;
+    _setUint32AtTail(_buf, _tail, length);
+    int offset = _buf.lengthInBytes - _tail + 4;
+    for (int i = 0; i < length; i++) {
+      _buf.setUint8(offset++, bytes[i]);
+    }
+    return result;
+  }
+
+  /// Throw an exception if there is not currently a vtable.
+  void _ensureCurrentVTable() {
+    if (_currentVTable == null) {
+      throw new StateError('Start a table before adding values.');
+    }
+  }
+
+  /// Throw an exception if there is currently a vtable.
+  void _ensureNoVTable() {
+    if (_currentVTable != null) {
+      throw new StateError(
+          'Cannot write a non-scalar value while writing a table.');
+    }
+  }
+
+  /// The number of bytes that have been written to the buffer so far.  The
+  /// most recently written byte is this many bytes from the end of the buffer.
+  int get offset => _tail;
+
+  /// Zero-pads the buffer, which may be required for some struct layouts.
+  void pad(int howManyBytes) {
+    for (int i = 0; i < howManyBytes; i++) putUint8(0);
+  }
+
+  /// Prepare for writing the given `count` of scalars of the given `size`.
+  /// Additionally allocate the specified `additionalBytes`. Update the current
+  /// tail pointer to point at the allocated space.
+  void _prepare(int size, int count, {int additionalBytes = 0}) {
+    // Update the alignment.
+    if (_maxAlign < size) {
+      _maxAlign = size;
+    }
+    // Prepare amount of required space.
+    int dataSize = size * count + additionalBytes;
+    int alignDelta = (-(_tail + dataSize)) % size;
+    int bufSize = alignDelta + dataSize;
+    // Ensure that we have the required amount of space.
+    {
+      int oldCapacity = _buf.lengthInBytes;
+      if (_tail + bufSize > oldCapacity) {
+        int desiredNewCapacity = (oldCapacity + bufSize) * 2;
+        int deltaCapacity = desiredNewCapacity - oldCapacity;
+        deltaCapacity += (-deltaCapacity) % _maxAlign;
+        int newCapacity = oldCapacity + deltaCapacity;
+        ByteData newBuf = new ByteData(newCapacity);
+        newBuf.buffer
+            .asUint8List()
+            .setAll(deltaCapacity, _buf.buffer.asUint8List());
+        _buf = newBuf;
+      }
+    }
+    // Update the tail pointer.
+    _tail += bufSize;
+  }
+
+  /// Record the offset of the given [field].
+  void _trackField(int field) {
+    _currentVTable.addField(field, _tail);
+  }
+
+  static void _setFloat64AtTail(ByteData _buf, int tail, double x) {
+    _buf.setFloat64(_buf.lengthInBytes - tail, x, Endian.little);
+  }
+
+  static void _setFloat32AtTail(ByteData _buf, int tail, double x) {
+    _buf.setFloat32(_buf.lengthInBytes - tail, x, Endian.little);
+  }
+
+  static void _setUint64AtTail(ByteData _buf, int tail, int x) {
+    _buf.setUint64(_buf.lengthInBytes - tail, x, Endian.little);
+  }
+
+  static void _setInt64AtTail(ByteData _buf, int tail, int x) {
+    _buf.setInt64(_buf.lengthInBytes - tail, x, Endian.little);
+  }
+
+  static void _setInt32AtTail(ByteData _buf, int tail, int x) {
+    _buf.setInt32(_buf.lengthInBytes - tail, x, Endian.little);
+  }
+
+  static void _setUint32AtTail(ByteData _buf, int tail, int x) {
+    _buf.setUint32(_buf.lengthInBytes - tail, x, Endian.little);
+  }
+
+  static void _setInt16AtTail(ByteData _buf, int tail, int x) {
+    _buf.setInt16(_buf.lengthInBytes - tail, x, Endian.little);
+  }
+
+  static void _setUint16AtTail(ByteData _buf, int tail, int x) {
+    _buf.setUint16(_buf.lengthInBytes - tail, x, Endian.little);
+  }
+
+  static void _setInt8AtTail(ByteData _buf, int tail, int x) {
+    _buf.setInt8(_buf.lengthInBytes - tail, x);
+  }
+
+  static void _setUint8AtTail(ByteData _buf, int tail, int x) {
+    _buf.setUint8(_buf.lengthInBytes - tail, x);
+  }
+}
+
+/// Reader of lists of boolean values.
+///
+/// The returned unmodifiable lists lazily read values on access.
+class BoolListReader extends Reader<List<bool>> {
+  const BoolListReader();
+
+  @override
+  int get size => _sizeofUint32;
+
+  @override
+  List<bool> read(BufferContext bc, int offset) =>
+      new _FbBoolList(bc, bc.derefObject(offset));
+}
+
+/// The reader of booleans.
+class BoolReader extends Reader<bool> {
+  const BoolReader() : super();
+
+  @override
+  int get size => _sizeofUint8;
+
+  @override
+  bool read(BufferContext bc, int offset) => bc._getInt8(offset) != 0;
+}
+
+/// The reader of lists of 64-bit float values.
+///
+/// The returned unmodifiable lists lazily read values on access.
+class Float64ListReader extends Reader<List<double>> {
+  const Float64ListReader();
+
+  @override
+  int get size => _sizeofFloat64;
+
+  @override
+  List<double> read(BufferContext bc, int offset) =>
+      new _FbFloat64List(bc, bc.derefObject(offset));
+}
+
+class Float32ListReader extends Reader<List<double>> {
+  const Float32ListReader();
+
+  @override
+  int get size => _sizeofFloat32;
+
+  @override
+  List<double> read(BufferContext bc, int offset) =>
+      new _FbFloat32List(bc, bc.derefObject(offset));
+}
+
+class Float64Reader extends Reader<double> {
+  const Float64Reader();
+
+  @override
+  int get size => _sizeofFloat64;
+
+  @override
+  double read(BufferContext bc, int offset) => bc._getFloat64(offset);
+}
+
+class Float32Reader extends Reader<double> {
+  const Float32Reader();
+
+  @override
+  int get size => _sizeofFloat32;
+
+  @override
+  double read(BufferContext bc, int offset) => bc._getFloat32(offset);
+}
+
+class Int64Reader extends Reader<int> {
+  const Int64Reader() : super();
+  @override
+  int get size => _sizeofInt64;
+
+  @override
+  int read(BufferContext bc, int offset) => bc._getInt64(offset);
+}
+
+/// The reader of signed 32-bit integers.
+class Int32Reader extends Reader<int> {
+  const Int32Reader() : super();
+
+  @override
+  int get size => _sizeofInt32;
+
+  @override
+  int read(BufferContext bc, int offset) => bc._getInt32(offset);
+}
+
+/// The reader of signed 32-bit integers.
+class Int16Reader extends Reader<int> {
+  const Int16Reader() : super();
+
+  @override
+  int get size => _sizeofInt16;
+
+  @override
+  int read(BufferContext bc, int offset) => bc._getInt16(offset);
+}
+
+/// The reader of 8-bit signed integers.
+class Int8Reader extends Reader<int> {
+  const Int8Reader() : super();
+
+  @override
+  int get size => _sizeofInt8;
+
+  @override
+  int read(BufferContext bc, int offset) => bc._getInt8(offset);
+}
+
+/// The reader of lists of objects.
+///
+/// The returned unmodifiable lists lazily read objects on access.
+class ListReader<E> extends Reader<List<E>> {
+  final Reader<E> _elementReader;
+
+  const ListReader(this._elementReader);
+
+  @override
+  int get size => _sizeofUint32;
+
+  @override
+  List<E> read(BufferContext bc, int offset) =>
+      new _FbGenericList<E>(_elementReader, bc, bc.derefObject(offset));
+}
+
+/// Object that can read a value at a [BufferContext].
+abstract class Reader<T> {
+  const Reader();
+
+  /// The size of the value in bytes.
+  int get size;
+
+  /// Read the value at the given [offset] in [bc].
+  T read(BufferContext bc, int offset);
+
+  /// Read the value of the given [field] in the given [object].
+  T vTableGet(BufferContext object, int offset, int field, [T defaultValue]) {
+    int vTableSOffset = object._getInt32(offset);
+    int vTableOffset = offset - vTableSOffset;
+    int vTableSize = object._getUint16(vTableOffset);
+    int vTableFieldOffset = field;
+    if (vTableFieldOffset < vTableSize) {
+      int fieldOffsetInObject =
+          object._getUint16(vTableOffset + vTableFieldOffset);
+      if (fieldOffsetInObject != 0) {
+        return read(object, offset + fieldOffsetInObject);
+      }
+    }
+    return defaultValue;
+  }
+}
+
+/// The reader of string values.
+class StringReader extends Reader<String> {
+  const StringReader() : super();
+
+  @override
+  int get size => 4;
+
+  @override
+  String read(BufferContext bc, int offset) {
+    int strOffset = bc.derefObject(offset);
+    int length = bc._getUint32(strOffset);
+    Uint8List bytes = bc._asUint8LIst(strOffset + 4, length);
+    if (_isLatin(bytes)) {
+      return new String.fromCharCodes(bytes);
+    }
+    return utf8.decode(bytes);
+  }
+
+  static bool _isLatin(Uint8List bytes) {
+    int length = bytes.length;
+    for (int i = 0; i < length; i++) {
+      if (bytes[i] > 127) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
+
+/// An abstract reader for structs.
+abstract class StructReader<T> extends Reader<T> {
+  const StructReader();
+
+  /// Return the object at `offset`.
+  T createObject(BufferContext bc, int offset);
+
+  T read(BufferContext bp, int offset) {
+    return createObject(bp, offset);
+  }
+}
+
+/// An abstract reader for tables.
+abstract class TableReader<T> extends Reader<T> {
+  const TableReader();
+
+  @override
+  int get size => 4;
+
+  /// Return the object at [offset].
+  T createObject(BufferContext bc, int offset);
+
+  @override
+  T read(BufferContext bp, int offset) {
+    int objectOffset = bp.derefObject(offset);
+    return createObject(bp, objectOffset);
+  }
+}
+
+/// Reader of lists of unsigned 32-bit integer values.
+///
+/// The returned unmodifiable lists lazily read values on access.
+class Uint32ListReader extends Reader<List<int>> {
+  const Uint32ListReader();
+
+  @override
+  int get size => _sizeofUint32;
+
+  @override
+  List<int> read(BufferContext bc, int offset) =>
+      new _FbUint32List(bc, bc.derefObject(offset));
+}
+
+/// The reader of unsigned 64-bit integers.
+///
+/// WARNING: May have compatibility issues with JavaScript
+class Uint64Reader extends Reader<int> {
+  const Uint64Reader() : super();
+
+  @override
+  int get size => _sizeofUint64;
+
+  @override
+  int read(BufferContext bc, int offset) => bc._getUint64(offset);
+}
+
+/// The reader of unsigned 32-bit integers.
+class Uint32Reader extends Reader<int> {
+  const Uint32Reader() : super();
+
+  @override
+  int get size => _sizeofUint32;
+
+  @override
+  int read(BufferContext bc, int offset) => bc._getUint32(offset);
+}
+
+/// Reader of lists of unsigned 32-bit integer values.
+///
+/// The returned unmodifiable lists lazily read values on access.
+class Uint16ListReader extends Reader<List<int>> {
+  const Uint16ListReader();
+
+  @override
+  int get size => _sizeofUint32;
+
+  @override
+  List<int> read(BufferContext bc, int offset) =>
+      new _FbUint16List(bc, bc.derefObject(offset));
+}
+
+/// The reader of unsigned 32-bit integers.
+class Uint16Reader extends Reader<int> {
+  const Uint16Reader() : super();
+
+  @override
+  int get size => _sizeofUint16;
+
+  @override
+  int read(BufferContext bc, int offset) => bc._getUint16(offset);
+}
+
+/// Reader of lists of unsigned 8-bit integer values.
+///
+/// The returned unmodifiable lists lazily read values on access.
+class Uint8ListReader extends Reader<List<int>> {
+  const Uint8ListReader();
+
+  @override
+  int get size => _sizeofUint32;
+
+  @override
+  List<int> read(BufferContext bc, int offset) =>
+      new _FbUint8List(bc, bc.derefObject(offset));
+}
+
+/// The reader of unsigned 8-bit integers.
+class Uint8Reader extends Reader<int> {
+  const Uint8Reader() : super();
+
+  @override
+  int get size => _sizeofUint8;
+
+  @override
+  int read(BufferContext bc, int offset) => bc._getUint8(offset);
+}
+
+/// The list backed by 64-bit values - Uint64 length and Float64.
+class _FbFloat64List extends _FbList<double> {
+  _FbFloat64List(BufferContext bc, int offset) : super(bc, offset);
+
+  @override
+  double operator [](int i) {
+    return bc._getFloat64(offset + 4 + 8 * i);
+  }
+}
+
+/// The list backed by 32-bit values - Float32.
+class _FbFloat32List extends _FbList<double> {
+  _FbFloat32List(BufferContext bc, int offset) : super(bc, offset);
+
+  @override
+  double operator [](int i) {
+    return bc._getFloat32(offset + 4 + 4 * i);
+  }
+}
+
+/// List backed by a generic object which may have any size.
+class _FbGenericList<E> extends _FbList<E> {
+  final Reader<E> elementReader;
+
+  List<E> _items;
+
+  _FbGenericList(this.elementReader, BufferContext bp, int offset)
+      : super(bp, offset);
+
+  @override
+  E operator [](int i) {
+    _items ??= new List<E>(length);
+    E item = _items[i];
+    if (item == null) {
+      item = elementReader.read(bc, offset + 4 + elementReader.size * i);
+      _items[i] = item;
+    }
+    return item;
+  }
+}
+
+/// The base class for immutable lists read from flat buffers.
+abstract class _FbList<E> extends Object with ListMixin<E> implements List<E> {
+  final BufferContext bc;
+  final int offset;
+  int _length;
+
+  _FbList(this.bc, this.offset);
+
+  @override
+  int get length {
+    _length ??= bc._getUint32(offset);
+    return _length;
+  }
+
+  @override
+  void set length(int i) =>
+      throw new StateError('Attempt to modify immutable list');
+
+  @override
+  void operator []=(int i, E e) =>
+      throw new StateError('Attempt to modify immutable list');
+}
+
+/// List backed by 32-bit unsigned integers.
+class _FbUint32List extends _FbList<int> {
+  _FbUint32List(BufferContext bc, int offset) : super(bc, offset);
+
+  @override
+  int operator [](int i) {
+    return bc._getUint32(offset + 4 + 4 * i);
+  }
+}
+
+/// List backed by 16-bit unsigned integers.
+class _FbUint16List extends _FbList<int> {
+  _FbUint16List(BufferContext bc, int offset) : super(bc, offset);
+
+  @override
+  int operator [](int i) {
+    return bc._getUint16(offset + 4 + 2 * i);
+  }
+}
+
+/// List backed by 8-bit unsigned integers.
+class _FbUint8List extends _FbList<int> {
+  _FbUint8List(BufferContext bc, int offset) : super(bc, offset);
+
+  @override
+  int operator [](int i) {
+    return bc._getUint8(offset + 4 + i);
+  }
+}
+
+/// List backed by 8-bit unsigned integers.
+class _FbBoolList extends _FbList<bool> {
+  _FbBoolList(BufferContext bc, int offset) : super(bc, offset);
+
+  @override
+  bool operator [](int i) {
+    return bc._getUint8(offset + 4 + i) == 1 ? true : false;
+  }
+}
+
+/// Class that describes the structure of a table.
+class _VTable {
+  static const int _metadataLength = 4;
+
+  final List<int> fieldTails = <int>[];
+  final List<int> fieldOffsets = <int>[];
+
+  /// The size of the table that uses this VTable.
+  int tableSize;
+
+  /// The tail of this VTable.  It is used to share the same VTable between
+  /// multiple tables of identical structure.
+  int tail;
+
+  int get _vTableSize => numOfUint16 * _sizeofUint16;
+
+  int get numOfUint16 => 1 + 1 + fieldTails.length;
+
+  void addField(int field, int offset) {
+    while (fieldTails.length <= field) {
+      fieldTails.add(null);
+    }
+    fieldTails[field] = offset;
+  }
+
+  bool _offsetsMatch(int vt2Start, ByteData buf) {
+    for (int i = 0; i < fieldOffsets.length; i++) {
+      if (fieldOffsets[i] !=
+          buf.getUint16(
+              vt2Start + _metadataLength + (2 * i), Endian.little)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /// Fill the [fieldOffsets] field.
+  void computeFieldOffsets(int tableTail) {
+    assert(fieldOffsets.isEmpty);
+    for (int fieldTail in fieldTails) {
+      int fieldOffset = fieldTail == null ? 0 : tableTail - fieldTail;
+      fieldOffsets.add(fieldOffset);
+    }
+  }
+
+  /// Outputs this VTable to [buf], which is is expected to be aligned to 16-bit
+  /// and have at least [numOfUint16] 16-bit words available.
+  void output(ByteData buf, int bufOffset) {
+    // VTable size.
+    buf.setUint16(bufOffset, numOfUint16 * 2, Endian.little);
+    bufOffset += 2;
+    // Table size.
+    buf.setUint16(bufOffset, tableSize, Endian.little);
+    bufOffset += 2;
+    // Field offsets.
+    for (int fieldOffset in fieldOffsets) {
+      buf.setUint16(bufOffset, fieldOffset, Endian.little);
+      bufOffset += 2;
+    }
+  }
+}