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