mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 15:21:54 +00:00
Move flat_buffers to package:analyzer
Change-Id: Ie9e6c8d3c56a17e689f23434a3045c23de59eeee Reviewed-on: https://dart-review.googlesource.com/c/77821 Auto-Submit: Peter von der Ahé <ahe@google.com> Commit-Queue: Jens Johansen <jensj@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
This commit is contained in:
parent
27b17c76d8
commit
b8cef2cefe
|
@ -1,8 +1,951 @@
|
|||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
// This library re-exports front_end's flat_buffers.dart, since its API is
|
||||
// stable enough to be relied upon by clients.
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
export 'package:front_end/src/base/flat_buffers.dart';
|
||||
/**
|
||||
* 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 => 4;
|
||||
|
||||
@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 => 1;
|
||||
|
||||
@override
|
||||
bool read(BufferContext bc, int offset) => bc._getInt8(offset) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
int _getInt32(int offset) => _buffer.getInt32(offset, Endian.little);
|
||||
|
||||
int _getInt8(int offset) => _buffer.getInt8(offset);
|
||||
|
||||
int _getUint16(int offset) => _buffer.getUint16(offset, Endian.little);
|
||||
|
||||
int _getUint32(int offset) => _buffer.getUint32(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 that helps building flat buffers.
|
||||
*/
|
||||
class Builder {
|
||||
final int initialSize;
|
||||
|
||||
/**
|
||||
* The list of existing VTable(s).
|
||||
*/
|
||||
final List<_VTable> _vTables = <_VTable>[];
|
||||
|
||||
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.
|
||||
*/
|
||||
Map<String, Offset<String>> _strings = <String, Offset<String>>{};
|
||||
|
||||
Builder({this.initialSize: 1024}) {
|
||||
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) {
|
||||
int size = 1;
|
||||
_prepare(size, 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) {
|
||||
int size = 4;
|
||||
_prepare(size, 1);
|
||||
_trackField(field);
|
||||
_setInt32AtTail(_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) {
|
||||
int size = 1;
|
||||
_prepare(size, 1);
|
||||
_trackField(field);
|
||||
_buf.setInt8(_buf.lengthInBytes - _tail, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the [field] referencing an object with the given [offset].
|
||||
*/
|
||||
void addOffset(int field, Offset offset) {
|
||||
_ensureCurrentVTable();
|
||||
if (offset != null) {
|
||||
_prepare(4, 1);
|
||||
_trackField(field);
|
||||
_setUint32AtTail(_buf, _tail, _tail - offset._tail);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
int size = 4;
|
||||
_prepare(size, 1);
|
||||
_trackField(field);
|
||||
_setUint32AtTail(_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) {
|
||||
int size = 1;
|
||||
_prepare(size, 1);
|
||||
_trackField(field);
|
||||
_setUint8AtTail(_buf, _tail, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End the current table and return its offset.
|
||||
*/
|
||||
Offset endTable() {
|
||||
if (_currentVTable == null) {
|
||||
throw new StateError('Start a table before ending it.');
|
||||
}
|
||||
// Prepare for writing the VTable.
|
||||
_prepare(4, 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.
|
||||
for (int i = 0; i < _vTables.length; i++) {
|
||||
_VTable vTable = _vTables[i];
|
||||
if (_currentVTable.canUseExistingVTable(vTable)) {
|
||||
vTableTail = vTable.tail;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Write a new VTable.
|
||||
if (vTableTail == null) {
|
||||
_prepare(2, _currentVTable.numOfUint16);
|
||||
vTableTail = _tail;
|
||||
_currentVTable.tail = vTableTail;
|
||||
_currentVTable.output(_buf, _buf.lengthInBytes - _tail);
|
||||
_vTables.add(_currentVTable);
|
||||
}
|
||||
}
|
||||
// Set the VTable offset.
|
||||
_setInt32AtTail(_buf, tableTail, vTableTail - tableTail);
|
||||
// Done with this table.
|
||||
_currentVTable = null;
|
||||
return new Offset(tableTail);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(Offset offset, [String fileIdentifier]) {
|
||||
_prepare(max(4, _maxAlign), fileIdentifier == null ? 1 : 2);
|
||||
int alignedTail = _tail + ((-_tail) % _maxAlign);
|
||||
_setUint32AtTail(_buf, alignedTail, alignedTail - offset._tail);
|
||||
if (fileIdentifier != null) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
_setUint8AtTail(
|
||||
_buf, alignedTail - 4 - i, fileIdentifier.codeUnitAt(i));
|
||||
}
|
||||
}
|
||||
return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a low-level method, it should not be invoked by clients.
|
||||
*/
|
||||
Uint8List lowFinish() {
|
||||
int alignedTail = _tail + ((-_tail) % _maxAlign);
|
||||
return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a low-level method, it should not be invoked by clients.
|
||||
*/
|
||||
void lowReset() {
|
||||
_buf = new ByteData(initialSize);
|
||||
_maxAlign = 1;
|
||||
_tail = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a low-level method, it should not be invoked by clients.
|
||||
*/
|
||||
void lowWriteUint32(int value) {
|
||||
_prepare(4, 1);
|
||||
_setUint32AtTail(_buf, _tail, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a low-level method, it should not be invoked by clients.
|
||||
*/
|
||||
void lowWriteUint8(int value) {
|
||||
_prepare(1, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of [values].
|
||||
*/
|
||||
Offset writeList(List<Offset> values) {
|
||||
_ensureNoVTable();
|
||||
_prepare(4, 1 + values.length);
|
||||
Offset result = new Offset(_tail);
|
||||
int tail = _tail;
|
||||
_setUint32AtTail(_buf, tail, values.length);
|
||||
tail -= 4;
|
||||
for (Offset value in values) {
|
||||
_setUint32AtTail(_buf, tail, tail - value._tail);
|
||||
tail -= 4;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of boolean [values].
|
||||
*/
|
||||
Offset writeListBool(List<bool> values) {
|
||||
int bitLength = values.length;
|
||||
int padding = (-bitLength) % 8;
|
||||
int byteLength = (bitLength + padding) ~/ 8;
|
||||
// Prepare the backing Uint8List.
|
||||
Uint8List bytes = new Uint8List(byteLength + 1);
|
||||
// Record every bit.
|
||||
int byteIndex = 0;
|
||||
int byte = 0;
|
||||
int mask = 1;
|
||||
for (int bitIndex = 0; bitIndex < bitLength; bitIndex++) {
|
||||
if (bitIndex != 0 && (bitIndex % 8 == 0)) {
|
||||
bytes[byteIndex++] = byte;
|
||||
byte = 0;
|
||||
mask = 1;
|
||||
}
|
||||
if (values[bitIndex]) {
|
||||
byte |= mask;
|
||||
}
|
||||
mask <<= 1;
|
||||
}
|
||||
// Write the last byte, even if it may be on the padding.
|
||||
bytes[byteIndex] = byte;
|
||||
// Write the padding length.
|
||||
bytes[byteLength] = padding;
|
||||
// Write as a Uint8 list.
|
||||
return writeListUint8(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of 64-bit float [values].
|
||||
*/
|
||||
Offset writeListFloat64(List<double> values) {
|
||||
_ensureNoVTable();
|
||||
_prepare(8, 1 + values.length);
|
||||
Offset result = new Offset(_tail);
|
||||
int tail = _tail;
|
||||
_setUint32AtTail(_buf, tail, values.length);
|
||||
tail -= 8;
|
||||
for (double value in values) {
|
||||
_setFloat64AtTail(_buf, tail, value);
|
||||
tail -= 8;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of signed 32-bit integer [values].
|
||||
*/
|
||||
Offset writeListInt32(List<int> values) {
|
||||
_ensureNoVTable();
|
||||
_prepare(4, 1 + values.length);
|
||||
Offset result = new Offset(_tail);
|
||||
int tail = _tail;
|
||||
_setUint32AtTail(_buf, tail, values.length);
|
||||
tail -= 4;
|
||||
for (int value in values) {
|
||||
_setInt32AtTail(_buf, tail, value);
|
||||
tail -= 4;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of unsigned 32-bit integer [values].
|
||||
*/
|
||||
Offset writeListUint32(List<int> values) {
|
||||
_ensureNoVTable();
|
||||
_prepare(4, 1 + values.length);
|
||||
Offset result = new Offset(_tail);
|
||||
int tail = _tail;
|
||||
_setUint32AtTail(_buf, tail, values.length);
|
||||
tail -= 4;
|
||||
for (int value in values) {
|
||||
_setUint32AtTail(_buf, tail, value);
|
||||
tail -= 4;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of unsigned 8-bit integer [values].
|
||||
*/
|
||||
Offset writeListUint8(List<int> values) {
|
||||
_ensureNoVTable();
|
||||
_prepare(4, 1, additionalBytes: values.length);
|
||||
Offset result = new Offset(_tail);
|
||||
int tail = _tail;
|
||||
_setUint32AtTail(_buf, tail, values.length);
|
||||
tail -= 4;
|
||||
for (int value in values) {
|
||||
_setUint8AtTail(_buf, tail, value);
|
||||
tail -= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given string [value] and return its [Offset], or `null` if
|
||||
* the [value] is equal to [def].
|
||||
*/
|
||||
Offset<String> writeString(String value, [String def]) {
|
||||
_ensureNoVTable();
|
||||
if (value != def) {
|
||||
return _strings.putIfAbsent(value, () {
|
||||
// TODO(scheglov) optimize for ASCII strings
|
||||
List<int> bytes = utf8.encode(value);
|
||||
int length = bytes.length;
|
||||
_prepare(4, 1, additionalBytes: length);
|
||||
Offset<String> result = new Offset(_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;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 _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 _setUint8AtTail(ByteData _buf, int tail, int x) {
|
||||
_buf.setUint8(_buf.lengthInBytes - tail, x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 => 4;
|
||||
|
||||
@override
|
||||
List<double> read(BufferContext bc, int offset) =>
|
||||
new _FbFloat64List(bc, bc.derefObject(offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* The reader of signed 32-bit integers.
|
||||
*/
|
||||
class Int32Reader extends Reader<int> {
|
||||
const Int32Reader() : super();
|
||||
|
||||
@override
|
||||
int get size => 4;
|
||||
|
||||
@override
|
||||
int read(BufferContext bc, int offset) => bc._getInt32(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* The reader of 8-bit signed integers.
|
||||
*/
|
||||
class Int8Reader extends Reader<int> {
|
||||
const Int8Reader() : super();
|
||||
|
||||
@override
|
||||
int get size => 1;
|
||||
|
||||
@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 => 4;
|
||||
|
||||
@override
|
||||
List<E> read(BufferContext bc, int offset) =>
|
||||
new _FbGenericList<E>(_elementReader, bc, bc.derefObject(offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset from the end of the buffer to a serialized object of the type [T].
|
||||
*/
|
||||
class Offset<T> {
|
||||
final int _tail;
|
||||
|
||||
Offset(this._tail);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = (1 + 1 + field) * 2;
|
||||
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 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 => 4;
|
||||
|
||||
@override
|
||||
List<int> read(BufferContext bc, int offset) =>
|
||||
new _FbUint32List(bc, bc.derefObject(offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* The reader of unsigned 32-bit integers.
|
||||
*/
|
||||
class Uint32Reader extends Reader<int> {
|
||||
const Uint32Reader() : super();
|
||||
|
||||
@override
|
||||
int get size => 4;
|
||||
|
||||
@override
|
||||
int read(BufferContext bc, int offset) => bc._getUint32(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 => 4;
|
||||
|
||||
@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 => 1;
|
||||
|
||||
@override
|
||||
int read(BufferContext bc, int offset) => bc._getUint8(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* List of booleans backed by 8-bit unsigned integers.
|
||||
*/
|
||||
class _FbBoolList extends Object with ListMixin<bool> implements List<bool> {
|
||||
final BufferContext bc;
|
||||
final int offset;
|
||||
int _length;
|
||||
|
||||
_FbBoolList(this.bc, this.offset);
|
||||
|
||||
@override
|
||||
int get length {
|
||||
if (_length == null) {
|
||||
int byteLength = bc._getUint32(offset);
|
||||
_length = (byteLength - 1) * 8 - _getByte(byteLength - 1);
|
||||
}
|
||||
return _length;
|
||||
}
|
||||
|
||||
@override
|
||||
void set length(int i) =>
|
||||
throw new StateError('Attempt to modify immutable list');
|
||||
|
||||
@override
|
||||
bool operator [](int i) {
|
||||
int index = i ~/ 8;
|
||||
int mask = 1 << i % 8;
|
||||
return _getByte(index) & mask != 0;
|
||||
}
|
||||
|
||||
@override
|
||||
void operator []=(int i, bool e) =>
|
||||
throw new StateError('Attempt to modify immutable list');
|
||||
|
||||
int _getByte(int index) => bc._getUint8(offset + 4 + index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 + 8 + 8 * 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 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that describes the structure of a table.
|
||||
*/
|
||||
class _VTable {
|
||||
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 numOfUint16 => 1 + 1 + fieldTails.length;
|
||||
|
||||
void addField(int field, int offset) {
|
||||
while (fieldTails.length <= field) {
|
||||
fieldTails.add(null);
|
||||
}
|
||||
fieldTails[field] = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the [existing] VTable can be used instead of this.
|
||||
*/
|
||||
bool canUseExistingVTable(_VTable existing) {
|
||||
assert(tail == null);
|
||||
assert(existing.tail != null);
|
||||
if (tableSize == existing.tableSize &&
|
||||
fieldOffsets.length == existing.fieldOffsets.length) {
|
||||
for (int i = 0; i < fieldOffsets.length; i++) {
|
||||
if (fieldOffsets[i] != existing.fieldOffsets[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:front_end/src/base/flat_buffers.dart';
|
||||
import 'package:analyzer/src/summary/flat_buffers.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
|
@ -1,951 +0,0 @@
|
|||
// 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';
|
||||
|
||||
/**
|
||||
* 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 => 4;
|
||||
|
||||
@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 => 1;
|
||||
|
||||
@override
|
||||
bool read(BufferContext bc, int offset) => bc._getInt8(offset) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
int _getInt32(int offset) => _buffer.getInt32(offset, Endian.little);
|
||||
|
||||
int _getInt8(int offset) => _buffer.getInt8(offset);
|
||||
|
||||
int _getUint16(int offset) => _buffer.getUint16(offset, Endian.little);
|
||||
|
||||
int _getUint32(int offset) => _buffer.getUint32(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 that helps building flat buffers.
|
||||
*/
|
||||
class Builder {
|
||||
final int initialSize;
|
||||
|
||||
/**
|
||||
* The list of existing VTable(s).
|
||||
*/
|
||||
final List<_VTable> _vTables = <_VTable>[];
|
||||
|
||||
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.
|
||||
*/
|
||||
Map<String, Offset<String>> _strings = <String, Offset<String>>{};
|
||||
|
||||
Builder({this.initialSize: 1024}) {
|
||||
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) {
|
||||
int size = 1;
|
||||
_prepare(size, 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) {
|
||||
int size = 4;
|
||||
_prepare(size, 1);
|
||||
_trackField(field);
|
||||
_setInt32AtTail(_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) {
|
||||
int size = 1;
|
||||
_prepare(size, 1);
|
||||
_trackField(field);
|
||||
_buf.setInt8(_buf.lengthInBytes - _tail, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the [field] referencing an object with the given [offset].
|
||||
*/
|
||||
void addOffset(int field, Offset offset) {
|
||||
_ensureCurrentVTable();
|
||||
if (offset != null) {
|
||||
_prepare(4, 1);
|
||||
_trackField(field);
|
||||
_setUint32AtTail(_buf, _tail, _tail - offset._tail);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
int size = 4;
|
||||
_prepare(size, 1);
|
||||
_trackField(field);
|
||||
_setUint32AtTail(_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) {
|
||||
int size = 1;
|
||||
_prepare(size, 1);
|
||||
_trackField(field);
|
||||
_setUint8AtTail(_buf, _tail, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End the current table and return its offset.
|
||||
*/
|
||||
Offset endTable() {
|
||||
if (_currentVTable == null) {
|
||||
throw new StateError('Start a table before ending it.');
|
||||
}
|
||||
// Prepare for writing the VTable.
|
||||
_prepare(4, 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.
|
||||
for (int i = 0; i < _vTables.length; i++) {
|
||||
_VTable vTable = _vTables[i];
|
||||
if (_currentVTable.canUseExistingVTable(vTable)) {
|
||||
vTableTail = vTable.tail;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Write a new VTable.
|
||||
if (vTableTail == null) {
|
||||
_prepare(2, _currentVTable.numOfUint16);
|
||||
vTableTail = _tail;
|
||||
_currentVTable.tail = vTableTail;
|
||||
_currentVTable.output(_buf, _buf.lengthInBytes - _tail);
|
||||
_vTables.add(_currentVTable);
|
||||
}
|
||||
}
|
||||
// Set the VTable offset.
|
||||
_setInt32AtTail(_buf, tableTail, vTableTail - tableTail);
|
||||
// Done with this table.
|
||||
_currentVTable = null;
|
||||
return new Offset(tableTail);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(Offset offset, [String fileIdentifier]) {
|
||||
_prepare(max(4, _maxAlign), fileIdentifier == null ? 1 : 2);
|
||||
int alignedTail = _tail + ((-_tail) % _maxAlign);
|
||||
_setUint32AtTail(_buf, alignedTail, alignedTail - offset._tail);
|
||||
if (fileIdentifier != null) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
_setUint8AtTail(
|
||||
_buf, alignedTail - 4 - i, fileIdentifier.codeUnitAt(i));
|
||||
}
|
||||
}
|
||||
return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a low-level method, it should not be invoked by clients.
|
||||
*/
|
||||
Uint8List lowFinish() {
|
||||
int alignedTail = _tail + ((-_tail) % _maxAlign);
|
||||
return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a low-level method, it should not be invoked by clients.
|
||||
*/
|
||||
void lowReset() {
|
||||
_buf = new ByteData(initialSize);
|
||||
_maxAlign = 1;
|
||||
_tail = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a low-level method, it should not be invoked by clients.
|
||||
*/
|
||||
void lowWriteUint32(int value) {
|
||||
_prepare(4, 1);
|
||||
_setUint32AtTail(_buf, _tail, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a low-level method, it should not be invoked by clients.
|
||||
*/
|
||||
void lowWriteUint8(int value) {
|
||||
_prepare(1, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of [values].
|
||||
*/
|
||||
Offset writeList(List<Offset> values) {
|
||||
_ensureNoVTable();
|
||||
_prepare(4, 1 + values.length);
|
||||
Offset result = new Offset(_tail);
|
||||
int tail = _tail;
|
||||
_setUint32AtTail(_buf, tail, values.length);
|
||||
tail -= 4;
|
||||
for (Offset value in values) {
|
||||
_setUint32AtTail(_buf, tail, tail - value._tail);
|
||||
tail -= 4;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of boolean [values].
|
||||
*/
|
||||
Offset writeListBool(List<bool> values) {
|
||||
int bitLength = values.length;
|
||||
int padding = (-bitLength) % 8;
|
||||
int byteLength = (bitLength + padding) ~/ 8;
|
||||
// Prepare the backing Uint8List.
|
||||
Uint8List bytes = new Uint8List(byteLength + 1);
|
||||
// Record every bit.
|
||||
int byteIndex = 0;
|
||||
int byte = 0;
|
||||
int mask = 1;
|
||||
for (int bitIndex = 0; bitIndex < bitLength; bitIndex++) {
|
||||
if (bitIndex != 0 && (bitIndex % 8 == 0)) {
|
||||
bytes[byteIndex++] = byte;
|
||||
byte = 0;
|
||||
mask = 1;
|
||||
}
|
||||
if (values[bitIndex]) {
|
||||
byte |= mask;
|
||||
}
|
||||
mask <<= 1;
|
||||
}
|
||||
// Write the last byte, even if it may be on the padding.
|
||||
bytes[byteIndex] = byte;
|
||||
// Write the padding length.
|
||||
bytes[byteLength] = padding;
|
||||
// Write as a Uint8 list.
|
||||
return writeListUint8(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of 64-bit float [values].
|
||||
*/
|
||||
Offset writeListFloat64(List<double> values) {
|
||||
_ensureNoVTable();
|
||||
_prepare(8, 1 + values.length);
|
||||
Offset result = new Offset(_tail);
|
||||
int tail = _tail;
|
||||
_setUint32AtTail(_buf, tail, values.length);
|
||||
tail -= 8;
|
||||
for (double value in values) {
|
||||
_setFloat64AtTail(_buf, tail, value);
|
||||
tail -= 8;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of signed 32-bit integer [values].
|
||||
*/
|
||||
Offset writeListInt32(List<int> values) {
|
||||
_ensureNoVTable();
|
||||
_prepare(4, 1 + values.length);
|
||||
Offset result = new Offset(_tail);
|
||||
int tail = _tail;
|
||||
_setUint32AtTail(_buf, tail, values.length);
|
||||
tail -= 4;
|
||||
for (int value in values) {
|
||||
_setInt32AtTail(_buf, tail, value);
|
||||
tail -= 4;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of unsigned 32-bit integer [values].
|
||||
*/
|
||||
Offset writeListUint32(List<int> values) {
|
||||
_ensureNoVTable();
|
||||
_prepare(4, 1 + values.length);
|
||||
Offset result = new Offset(_tail);
|
||||
int tail = _tail;
|
||||
_setUint32AtTail(_buf, tail, values.length);
|
||||
tail -= 4;
|
||||
for (int value in values) {
|
||||
_setUint32AtTail(_buf, tail, value);
|
||||
tail -= 4;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given list of unsigned 8-bit integer [values].
|
||||
*/
|
||||
Offset writeListUint8(List<int> values) {
|
||||
_ensureNoVTable();
|
||||
_prepare(4, 1, additionalBytes: values.length);
|
||||
Offset result = new Offset(_tail);
|
||||
int tail = _tail;
|
||||
_setUint32AtTail(_buf, tail, values.length);
|
||||
tail -= 4;
|
||||
for (int value in values) {
|
||||
_setUint8AtTail(_buf, tail, value);
|
||||
tail -= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given string [value] and return its [Offset], or `null` if
|
||||
* the [value] is equal to [def].
|
||||
*/
|
||||
Offset<String> writeString(String value, [String def]) {
|
||||
_ensureNoVTable();
|
||||
if (value != def) {
|
||||
return _strings.putIfAbsent(value, () {
|
||||
// TODO(scheglov) optimize for ASCII strings
|
||||
List<int> bytes = utf8.encode(value);
|
||||
int length = bytes.length;
|
||||
_prepare(4, 1, additionalBytes: length);
|
||||
Offset<String> result = new Offset(_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;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 _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 _setUint8AtTail(ByteData _buf, int tail, int x) {
|
||||
_buf.setUint8(_buf.lengthInBytes - tail, x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 => 4;
|
||||
|
||||
@override
|
||||
List<double> read(BufferContext bc, int offset) =>
|
||||
new _FbFloat64List(bc, bc.derefObject(offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* The reader of signed 32-bit integers.
|
||||
*/
|
||||
class Int32Reader extends Reader<int> {
|
||||
const Int32Reader() : super();
|
||||
|
||||
@override
|
||||
int get size => 4;
|
||||
|
||||
@override
|
||||
int read(BufferContext bc, int offset) => bc._getInt32(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* The reader of 8-bit signed integers.
|
||||
*/
|
||||
class Int8Reader extends Reader<int> {
|
||||
const Int8Reader() : super();
|
||||
|
||||
@override
|
||||
int get size => 1;
|
||||
|
||||
@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 => 4;
|
||||
|
||||
@override
|
||||
List<E> read(BufferContext bc, int offset) =>
|
||||
new _FbGenericList<E>(_elementReader, bc, bc.derefObject(offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset from the end of the buffer to a serialized object of the type [T].
|
||||
*/
|
||||
class Offset<T> {
|
||||
final int _tail;
|
||||
|
||||
Offset(this._tail);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = (1 + 1 + field) * 2;
|
||||
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 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 => 4;
|
||||
|
||||
@override
|
||||
List<int> read(BufferContext bc, int offset) =>
|
||||
new _FbUint32List(bc, bc.derefObject(offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* The reader of unsigned 32-bit integers.
|
||||
*/
|
||||
class Uint32Reader extends Reader<int> {
|
||||
const Uint32Reader() : super();
|
||||
|
||||
@override
|
||||
int get size => 4;
|
||||
|
||||
@override
|
||||
int read(BufferContext bc, int offset) => bc._getUint32(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 => 4;
|
||||
|
||||
@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 => 1;
|
||||
|
||||
@override
|
||||
int read(BufferContext bc, int offset) => bc._getUint8(offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* List of booleans backed by 8-bit unsigned integers.
|
||||
*/
|
||||
class _FbBoolList extends Object with ListMixin<bool> implements List<bool> {
|
||||
final BufferContext bc;
|
||||
final int offset;
|
||||
int _length;
|
||||
|
||||
_FbBoolList(this.bc, this.offset);
|
||||
|
||||
@override
|
||||
int get length {
|
||||
if (_length == null) {
|
||||
int byteLength = bc._getUint32(offset);
|
||||
_length = (byteLength - 1) * 8 - _getByte(byteLength - 1);
|
||||
}
|
||||
return _length;
|
||||
}
|
||||
|
||||
@override
|
||||
void set length(int i) =>
|
||||
throw new StateError('Attempt to modify immutable list');
|
||||
|
||||
@override
|
||||
bool operator [](int i) {
|
||||
int index = i ~/ 8;
|
||||
int mask = 1 << i % 8;
|
||||
return _getByte(index) & mask != 0;
|
||||
}
|
||||
|
||||
@override
|
||||
void operator []=(int i, bool e) =>
|
||||
throw new StateError('Attempt to modify immutable list');
|
||||
|
||||
int _getByte(int index) => bc._getUint8(offset + 4 + index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 + 8 + 8 * 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 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that describes the structure of a table.
|
||||
*/
|
||||
class _VTable {
|
||||
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 numOfUint16 => 1 + 1 + fieldTails.length;
|
||||
|
||||
void addField(int field, int offset) {
|
||||
while (fieldTails.length <= field) {
|
||||
fieldTails.add(null);
|
||||
}
|
||||
fieldTails[field] = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if the [existing] VTable can be used instead of this.
|
||||
*/
|
||||
bool canUseExistingVTable(_VTable existing) {
|
||||
assert(tail == null);
|
||||
assert(existing.tail != null);
|
||||
if (tableSize == existing.tableSize &&
|
||||
fieldOffsets.length == existing.fieldOffsets.length) {
|
||||
for (int i = 0; i < fieldOffsets.length; i++) {
|
||||
if (fieldOffsets[i] != existing.fieldOffsets[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue