1import 'dart:collection';
2import 'dart:convert';
3import 'dart:math';
4import 'dart:typed_data';
5
6const int _sizeofUint8 = 1;
7const int _sizeofUint16 = 2;
8const int _sizeofUint32 = 4;
9const int _sizeofUint64 = 8;
10const int _sizeofInt8 = 1;
11const int _sizeofInt16 = 2;
12const int _sizeofInt32 = 4;
13const int _sizeofInt64 = 8;
14const int _sizeofFloat32 = 4;
15const int _sizeofFloat64 = 8;
16
17/// Callback used to invoke a struct builder's finish method.
18///
19/// This callback is used by other struct's `finish` methods to write the nested
20/// struct's fields inline.
21typedef StructBuilder = void Function();
22
23/// Buffer with data and some context about it.
24class BufferContext {
25 final ByteData _buffer;
26
27 ByteData get buffer => _buffer;
28
29 /// Create from a FlatBuffer represented by a list of bytes (uint8).
30 factory BufferContext.fromBytes(List<int> byteList) =>
31 BufferContext(byteList is Uint8List
32 ? byteList.buffer.asByteData(byteList.offsetInBytes)
33 : ByteData.view(Uint8List.fromList(byteList).buffer));
34
35 /// Create from a FlatBuffer represented by ByteData.
36 BufferContext(this._buffer);
37
38 @pragma('vm:prefer-inline')
39 int derefObject(int offset) => offset + _getUint32(offset);
40
41 @pragma('vm:prefer-inline')
42 Uint8List _asUint8List(int offset, int length) =>
43 _buffer.buffer.asUint8List(_buffer.offsetInBytes + offset, length);
44
45 @pragma('vm:prefer-inline')
46 double _getFloat64(int offset) => _buffer.getFloat64(offset, Endian.little);
47
48 @pragma('vm:prefer-inline')
49 double _getFloat32(int offset) => _buffer.getFloat32(offset, Endian.little);
50
51 @pragma('vm:prefer-inline')
52 int _getInt64(int offset) => _buffer.getInt64(offset, Endian.little);
53
54 @pragma('vm:prefer-inline')
55 int _getInt32(int offset) => _buffer.getInt32(offset, Endian.little);
56
57 @pragma('vm:prefer-inline')
58 int _getInt16(int offset) => _buffer.getInt16(offset, Endian.little);
59
60 @pragma('vm:prefer-inline')
61 int _getInt8(int offset) => _buffer.getInt8(offset);
62
63 @pragma('vm:prefer-inline')
64 int _getUint64(int offset) => _buffer.getUint64(offset, Endian.little);
65
66 @pragma('vm:prefer-inline')
67 int _getUint32(int offset) => _buffer.getUint32(offset, Endian.little);
68
69 @pragma('vm:prefer-inline')
70 int _getUint16(int offset) => _buffer.getUint16(offset, Endian.little);
71
72 @pragma('vm:prefer-inline')
73 int _getUint8(int offset) => _buffer.getUint8(offset);
74}
75
76/// Interface implemented by the "object-api" classes (ending with "T").
77abstract class Packable {
78 /// Serialize the object using the given builder, returning the offset.
79 int pack(Builder fbBuilder);
80}
81
82/// Class implemented by typed builders generated by flatc.
83abstract class ObjectBuilder {
84 int? _firstOffset;
85
86 /// Can be used to write the data represented by this builder to the [Builder]
87 /// and reuse the offset created in multiple tables.
88 ///
89 /// Note that this method assumes you call it using the same [Builder] instance
90 /// every time. The returned offset is only good for the [Builder] used in the
91 /// first call to this method.
92 int getOrCreateOffset(Builder fbBuilder) {
93 _firstOffset ??= finish(fbBuilder);
94 return _firstOffset!;
95 }
96
97 /// Writes the data in this helper to the [Builder].
98 int finish(Builder fbBuilder);
99
100 /// Convenience method that will create a new [Builder], [finish]es the data,
101 /// and returns the buffer as a [Uint8List] of bytes.
102 Uint8List toBytes();
103}
104
105/// Class that helps building flat buffers.
106class Builder {
107 bool _finished = false;
108
109 final int initialSize;
110
111 /// The list of existing VTable(s).
112 final List<int> _vTables;
113
114 final bool deduplicateTables;
115
116 ByteData _buf;
117
118 final Allocator _allocator;
119
120 /// The maximum alignment that has been seen so far. If [_buf] has to be
121 /// reallocated in the future (to insert room at its start for more bytes) the
122 /// reallocation will need to be a multiple of this many bytes.
123 int _maxAlign = 1;
124
125 /// The number of bytes that have been written to the buffer so far. The
126 /// most recently written byte is this many bytes from the end of [_buf].
127 int _tail = 0;
128
129 /// The location of the end of the current table, measured in bytes from the
130 /// end of [_buf].
131 int _currentTableEndTail = 0;
132
133 _VTable? _currentVTable;
134
135 /// Map containing all strings that have been written so far. This allows us
136 /// to avoid duplicating strings.
137 ///
138 /// Allocated only if `internStrings` is set to true on the constructor.
139 Map<String, int>? _strings;
140
141 /// Creates a new FlatBuffers Builder.
142 ///
143 /// `initialSize` is the initial array size in bytes. The [Builder] will
144 /// automatically grow the array if/as needed. `internStrings`, if set to
145 /// true, will cause [writeString] to pool strings in the buffer so that
146 /// identical strings will always use the same offset in tables.
147 Builder({
148 this.initialSize = 1024,
149 bool internStrings = false,
150 Allocator allocator = const DefaultAllocator(),
151 this.deduplicateTables = true,
152 }) : _allocator = allocator,
153 _buf = allocator.allocate(initialSize),
154 _vTables = deduplicateTables ? [] : const [] {
155 if (internStrings) {
156 _strings = <String, int>{};
157 }
158 }
159
160 /// Calculate the finished buffer size (aligned).
161 @pragma('vm:prefer-inline')
162 int size() => _tail + ((-_tail) & (_maxAlign - 1));
163
164 /// Add the [field] with the given boolean [value]. The field is not added if
165 /// the [value] is equal to [def]. Booleans are stored as 8-bit fields with
166 /// `0` for `false` and `1` for `true`.
167 void addBool(int field, bool? value, [bool? def]) {
168 assert(_inVTable);
169 if (value != null && value != def) {
170 _prepare(_sizeofUint8, 1);
171 _trackField(field);
172 _buf.setInt8(_buf.lengthInBytes - _tail, value ? 1 : 0);
173 }
174 }
175
176 /// Add the [field] with the given 32-bit signed integer [value]. The field is
177 /// not added if the [value] is equal to [def].
178 void addInt32(int field, int? value, [int? def]) {
179 assert(_inVTable);
180 if (value != null && value != def) {
181 _prepare(_sizeofInt32, 1);
182 _trackField(field);
183 _setInt32AtTail(_tail, value);
184 }
185 }
186
187 /// Add the [field] with the given 32-bit signed integer [value]. The field is
188 /// not added if the [value] is equal to [def].
189 void addInt16(int field, int? value, [int? def]) {
190 assert(_inVTable);
191 if (value != null && value != def) {
192 _prepare(_sizeofInt16, 1);
193 _trackField(field);
194 _setInt16AtTail(_tail, value);
195 }
196 }
197
198 /// Add the [field] with the given 8-bit signed integer [value]. The field is
199 /// not added if the [value] is equal to [def].
200 void addInt8(int field, int? value, [int? def]) {
201 assert(_inVTable);
202 if (value != null && value != def) {
203 _prepare(_sizeofInt8, 1);
204 _trackField(field);
205 _setInt8AtTail(_tail, value);
206 }
207 }
208
209 void addStruct(int field, int offset) {
210 assert(_inVTable);
211 _trackField(field);
212 _currentVTable!.addField(field, offset);
213 }
214
215 /// Add the [field] referencing an object with the given [offset].
216 void addOffset(int field, int? offset) {
217 assert(_inVTable);
218 if (offset != null) {
219 _prepare(_sizeofUint32, 1);
220 _trackField(field);
221 _setUint32AtTail(_tail, _tail - offset);
222 }
223 }
224
225 /// Add the [field] with the given 32-bit unsigned integer [value]. The field
226 /// is not added if the [value] is equal to [def].
227 void addUint32(int field, int? value, [int? def]) {
228 assert(_inVTable);
229 if (value != null && value != def) {
230 _prepare(_sizeofUint32, 1);
231 _trackField(field);
232 _setUint32AtTail(_tail, value);
233 }
234 }
235
236 /// Add the [field] with the given 32-bit unsigned integer [value]. The field
237 /// is not added if the [value] is equal to [def].
238 void addUint16(int field, int? value, [int? def]) {
239 assert(_inVTable);
240 if (value != null && value != def) {
241 _prepare(_sizeofUint16, 1);
242 _trackField(field);
243 _setUint16AtTail(_tail, value);
244 }
245 }
246
247 /// Add the [field] with the given 8-bit unsigned integer [value]. The field
248 /// is not added if the [value] is equal to [def].
249 void addUint8(int field, int? value, [int? def]) {
250 assert(_inVTable);
251 if (value != null && value != def) {
252 _prepare(_sizeofUint8, 1);
253 _trackField(field);
254 _setUint8AtTail(_tail, value);
255 }
256 }
257
258 /// Add the [field] with the given 32-bit float [value]. The field
259 /// is not added if the [value] is equal to [def].
260 void addFloat32(int field, double? value, [double? def]) {
261 assert(_inVTable);
262 if (value != null && value != def) {
263 _prepare(_sizeofFloat32, 1);
264 _trackField(field);
265 _setFloat32AtTail(_tail, value);
266 }
267 }
268
269 /// Add the [field] with the given 64-bit double [value]. The field
270 /// is not added if the [value] is equal to [def].
271 void addFloat64(int field, double? value, [double? def]) {
272 assert(_inVTable);
273 if (value != null && value != def) {
274 _prepare(_sizeofFloat64, 1);
275 _trackField(field);
276 _setFloat64AtTail(_tail, value);
277 }
278 }
279
280 /// Add the [field] with the given 64-bit unsigned integer [value]. The field
281 /// is not added if the [value] is equal to [def].
282 void addUint64(int field, int? value, [double? def]) {
283 assert(_inVTable);
284 if (value != null && value != def) {
285 _prepare(_sizeofUint64, 1);
286 _trackField(field);
287 _setUint64AtTail(_tail, value);
288 }
289 }
290
291 /// Add the [field] with the given 64-bit unsigned integer [value]. The field
292 /// is not added if the [value] is equal to [def].
293 void addInt64(int field, int? value, [double? def]) {
294 assert(_inVTable);
295 if (value != null && value != def) {
296 _prepare(_sizeofInt64, 1);
297 _trackField(field);
298 _setInt64AtTail(_tail, value);
299 }
300 }
301
302 /// End the current table and return its offset.
303 int endTable() {
304 assert(_inVTable);
305 // Prepare for writing the VTable.
306 _prepare(_sizeofInt32, 1);
307 final tableTail = _tail;
308 // Prepare the size of the current table.
309 final currentVTable = _currentVTable!;
310 currentVTable.tableSize = tableTail - _currentTableEndTail;
311 // Prepare the VTable to use for the current table.
312 int? vTableTail;
313 {
314 currentVTable.computeFieldOffsets(tableTail);
315
316 // Try to find an existing compatible VTable.
317 if (deduplicateTables) {
318 // Search backward - more likely to have recently used one
319 for (var i = _vTables.length - 1; i >= 0; i--) {
320 final vt2Offset = _vTables[i];
321 final vt2Start = _buf.lengthInBytes - vt2Offset;
322 final vt2Size = _buf.getUint16(vt2Start, Endian.little);
323
324 if (currentVTable._vTableSize == vt2Size &&
325 currentVTable._offsetsMatch(vt2Start, _buf)) {
326 vTableTail = vt2Offset;
327 break;
328 }
329 }
330 }
331
332 // Write a new VTable.
333 if (vTableTail == null) {
334 _prepare(_sizeofUint16, _currentVTable!.numOfUint16);
335 vTableTail = _tail;
336 currentVTable.tail = vTableTail;
337 currentVTable.output(_buf, _buf.lengthInBytes - _tail);
338 if (deduplicateTables) _vTables.add(currentVTable.tail);
339 }
340 }
341 // Set the VTable offset.
342 _setInt32AtTail(tableTail, vTableTail - tableTail);
343 // Done with this table.
344 _currentVTable = null;
345 return tableTail;
346 }
347
348 /// Returns the finished buffer. You must call [finish] before accessing this.
349 @pragma('vm:prefer-inline')
350 Uint8List get buffer {
351 assert(_finished);
352 final finishedSize = size();
353 return _buf.buffer
354 .asUint8List(_buf.lengthInBytes - finishedSize, finishedSize);
355 }
356
357 /// Finish off the creation of the buffer. The given [offset] is used as the
358 /// root object offset, and usually references directly or indirectly every
359 /// written object. If [fileIdentifier] is specified (and not `null`), it is
360 /// interpreted as a 4-byte Latin-1 encoded string that should be placed at
361 /// bytes 4-7 of the file.
362 void finish(int offset, [String? fileIdentifier]) {
363 final sizeBeforePadding = size();
364 final requiredBytes = _sizeofUint32 * (fileIdentifier == null ? 1 : 2);
365 _prepare(max(requiredBytes, _maxAlign), 1);
366 final finishedSize = size();
367 _setUint32AtTail(finishedSize, finishedSize - offset);
368 if (fileIdentifier != null) {
369 for (var i = 0; i < 4; i++) {
370 _setUint8AtTail(
371 finishedSize - _sizeofUint32 - i, fileIdentifier.codeUnitAt(i));
372 }
373 }
374
375 // zero out the added padding
376 for (var i = sizeBeforePadding + 1;
377 i <= finishedSize - requiredBytes;
378 i++) {
379 _setUint8AtTail(i, 0);
380 }
381 _finished = true;
382 }
383
384 /// Writes a Float64 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 putFloat64(double value) {
388 _prepare(_sizeofFloat64, 1);
389 _setFloat32AtTail(_tail, value);
390 }
391
392 /// Writes a Float32 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 putFloat32(double value) {
396 _prepare(_sizeofFloat32, 1);
397 _setFloat32AtTail(_tail, value);
398 }
399
400 /// Writes a bool to the tail of the buffer after preparing space for it.
401 /// Bools are represented as a Uint8, with the value set to '1' for true, and '0' for false
402 ///
403 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
404 void putBool(bool value) {
405 _prepare(_sizeofUint8, 1);
406 _buf.setInt8(_buf.lengthInBytes - _tail, value ? 1 : 0);
407 }
408
409 /// Writes a Int64 to the tail of the buffer after preparing space for it.
410 ///
411 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
412 void putInt64(int value) {
413 _prepare(_sizeofInt64, 1);
414 _setInt64AtTail(_tail, value);
415 }
416
417 /// Writes a Uint32 to the tail of the buffer after preparing space for it.
418 ///
419 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
420 void putInt32(int value) {
421 _prepare(_sizeofInt32, 1);
422 _setInt32AtTail(_tail, value);
423 }
424
425 /// Writes a Uint16 to the tail of the buffer after preparing space for it.
426 ///
427 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
428 void putInt16(int value) {
429 _prepare(_sizeofInt16, 1);
430 _setInt16AtTail(_tail, value);
431 }
432
433 /// Writes a Uint8 to the tail of the buffer after preparing space for it.
434 ///
435 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
436 void putInt8(int value) {
437 _prepare(_sizeofInt8, 1);
438 _buf.setInt8(_buf.lengthInBytes - _tail, value);
439 }
440
441 /// Writes a Uint64 to the tail of the buffer after preparing space for it.
442 ///
443 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
444 void putUint64(int value) {
445 _prepare(_sizeofUint64, 1);
446 _setUint64AtTail(_tail, value);
447 }
448
449 /// Writes a Uint32 to the tail of the buffer after preparing space for it.
450 ///
451 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
452 void putUint32(int value) {
453 _prepare(_sizeofUint32, 1);
454 _setUint32AtTail(_tail, value);
455 }
456
457 /// Writes a Uint16 to the tail of the buffer after preparing space for it.
458 ///
459 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
460 void putUint16(int value) {
461 _prepare(_sizeofUint16, 1);
462 _setUint16AtTail(_tail, value);
463 }
464
465 /// Writes a Uint8 to the tail of the buffer after preparing space for it.
466 ///
467 /// Updates the [offset] pointer. This method is intended for use when writing structs to the buffer.
468 void putUint8(int value) {
469 _prepare(_sizeofUint8, 1);
470 _buf.setUint8(_buf.lengthInBytes - _tail, value);
471 }
472
473 /// Reset the builder and make it ready for filling a new buffer.
474 void reset() {
475 _finished = false;
476 _maxAlign = 1;
477 _tail = 0;
478 _currentVTable = null;
479 if (deduplicateTables) _vTables.clear();
480 if (_strings != null) {
481 _strings = <String, int>{};
482 }
483 }
484
485 /// Start a new table. Must be finished with [endTable] invocation.
486 void startTable(int numFields) {
487 assert(!_inVTable); // Inline tables are not supported.
488 _currentVTable = _VTable(numFields);
489 _currentTableEndTail = _tail;
490 }
491
492 /// Finish a Struct vector. Most callers should preferto use [writeListOfStructs].
493 ///
494 /// Most callers should prefer [writeListOfStructs].
495 int endStructVector(int count) {
496 putUint32(count);
497 return _tail;
498 }
499
500 /// Writes a list of Structs to the buffer, returning the offset
501 int writeListOfStructs(List<ObjectBuilder> structBuilders) {
502 assert(!_inVTable);
503 for (var i = structBuilders.length - 1; i >= 0; i--) {
504 structBuilders[i].finish(this);
505 }
506 return endStructVector(structBuilders.length);
507 }
508
509 /// Write the given list of [values].
510 int writeList(List<int> values) {
511 assert(!_inVTable);
512 _prepare(_sizeofUint32, 1 + values.length);
513 final result = _tail;
514 var tail = _tail;
515 _setUint32AtTail(tail, values.length);
516 tail -= _sizeofUint32;
517 for (final value in values) {
518 _setUint32AtTail(tail, tail - value);
519 tail -= _sizeofUint32;
520 }
521 return result;
522 }
523
524 /// Write the given list of 64-bit float [values].
525 int writeListFloat64(List<double> values) {
526 assert(!_inVTable);
527 _prepare(_sizeofFloat64, values.length, additionalBytes: _sizeofUint32);
528 final result = _tail;
529 var tail = _tail;
530 _setUint32AtTail(tail, values.length);
531 tail -= _sizeofUint32;
532 for (final value in values) {
533 _setFloat64AtTail(tail, value);
534 tail -= _sizeofFloat64;
535 }
536 return result;
537 }
538
539 /// Write the given list of 32-bit float [values].
540 int writeListFloat32(List<double> values) {
541 assert(!_inVTable);
542 _prepare(_sizeofFloat32, 1 + values.length);
543 final result = _tail;
544 var tail = _tail;
545 _setUint32AtTail(tail, values.length);
546 tail -= _sizeofUint32;
547 for (final value in values) {
548 _setFloat32AtTail(tail, value);
549 tail -= _sizeofFloat32;
550 }
551 return result;
552 }
553
554 /// Write the given list of signed 64-bit integer [values].
555 int writeListInt64(List<int> values) {
556 assert(!_inVTable);
557 _prepare(_sizeofInt64, values.length, additionalBytes: _sizeofUint32);
558 final result = _tail;
559 var tail = _tail;
560 _setUint32AtTail(tail, values.length);
561 tail -= _sizeofUint32;
562 for (final value in values) {
563 _setInt64AtTail(tail, value);
564 tail -= _sizeofInt64;
565 }
566 return result;
567 }
568
569 /// Write the given list of signed 64-bit integer [values].
570 int writeListUint64(List<int> values) {
571 assert(!_inVTable);
572 _prepare(_sizeofUint64, values.length, additionalBytes: _sizeofUint32);
573 final result = _tail;
574 var tail = _tail;
575 _setUint32AtTail(tail, values.length);
576 tail -= _sizeofUint32;
577 for (final value in values) {
578 _setUint64AtTail(tail, value);
579 tail -= _sizeofUint64;
580 }
581 return result;
582 }
583
584 /// Write the given list of signed 32-bit integer [values].
585 int writeListInt32(List<int> values) {
586 assert(!_inVTable);
587 _prepare(_sizeofUint32, 1 + values.length);
588 final result = _tail;
589 var tail = _tail;
590 _setUint32AtTail(tail, values.length);
591 tail -= _sizeofUint32;
592 for (final value in values) {
593 _setInt32AtTail(tail, value);
594 tail -= _sizeofInt32;
595 }
596 return result;
597 }
598
599 /// Write the given list of unsigned 32-bit integer [values].
600 int writeListUint32(List<int> values) {
601 assert(!_inVTable);
602 _prepare(_sizeofUint32, 1 + values.length);
603 final result = _tail;
604 var tail = _tail;
605 _setUint32AtTail(tail, values.length);
606 tail -= _sizeofUint32;
607 for (final value in values) {
608 _setUint32AtTail(tail, value);
609 tail -= _sizeofUint32;
610 }
611 return result;
612 }
613
614 /// Write the given list of signed 16-bit integer [values].
615 int writeListInt16(List<int> values) {
616 assert(!_inVTable);
617 _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
618 final result = _tail;
619 var tail = _tail;
620 _setUint32AtTail(tail, values.length);
621 tail -= _sizeofUint32;
622 for (final value in values) {
623 _setInt16AtTail(tail, value);
624 tail -= _sizeofInt16;
625 }
626 return result;
627 }
628
629 /// Write the given list of unsigned 16-bit integer [values].
630 int writeListUint16(List<int> values) {
631 assert(!_inVTable);
632 _prepare(_sizeofUint32, 1, additionalBytes: 2 * values.length);
633 final result = _tail;
634 var tail = _tail;
635 _setUint32AtTail(tail, values.length);
636 tail -= _sizeofUint32;
637 for (final value in values) {
638 _setUint16AtTail(tail, value);
639 tail -= _sizeofUint16;
640 }
641 return result;
642 }
643
644 /// Write the given list of bools as unsigend 8-bit integer [values].
645 int writeListBool(List<bool> values) {
646 return writeListUint8(values.map((b) => b ? 1 : 0).toList());
647 }
648
649 /// Write the given list of signed 8-bit integer [values].
650 int writeListInt8(List<int> values) {
651 assert(!_inVTable);
652 _prepare(_sizeofUint32, 1, additionalBytes: values.length);
653 final result = _tail;
654 var tail = _tail;
655 _setUint32AtTail(tail, values.length);
656 tail -= _sizeofUint32;
657 for (final value in values) {
658 _setInt8AtTail(tail, value);
659 tail -= _sizeofUint8;
660 }
661 return result;
662 }
663
664 /// Write the given list of unsigned 8-bit integer [values].
665 int writeListUint8(List<int> values) {
666 assert(!_inVTable);
667 _prepare(_sizeofUint32, 1, additionalBytes: values.length);
668 final result = _tail;
669 var tail = _tail;
670 _setUint32AtTail(tail, values.length);
671 tail -= _sizeofUint32;
672 for (final value in values) {
673 _setUint8AtTail(tail, value);
674 tail -= _sizeofUint8;
675 }
676 return result;
677 }
678
679 /// Write the given string [value] and return its offset.
680 ///
681 /// Dart strings are UTF-16 but must be stored as UTF-8 in FlatBuffers.
682 /// If the given string consists only of ASCII characters, you can indicate
683 /// enable [asciiOptimization]. In this mode, [writeString()] first tries to
684 /// copy the ASCII string directly to the output buffer and if that fails
685 /// (because there are no-ASCII characters in the string) it falls back and to
686 /// the default UTF-16 -> UTF-8 conversion (with slight performance penalty).
687 int writeString(String value, {bool asciiOptimization = false}) {
688 assert(!_inVTable);
689 if (_strings != null) {
690 return _strings!
691 .putIfAbsent(value, () => _writeString(value, asciiOptimization));
692 } else {
693 return _writeString(value, asciiOptimization);
694 }
695 }
696
697 int _writeString(String value, bool asciiOptimization) {
698 if (asciiOptimization) {
699 // [utf8.encode()] is slow (up to at least Dart SDK 2.13). If the given
700 // string is ASCII we can just write it directly, without any conversion.
701 final originalTail = _tail;
702 if (_tryWriteASCIIString(value)) return _tail;
703 // if non-ASCII: reset the output buffer position for [_writeUTFString()]
704 _tail = originalTail;
705 }
706 _writeUTFString(value);
707 return _tail;
708 }
709
710 // Try to write the string as ASCII, return false if there's a non-ascii char.
711 @pragma('vm:prefer-inline')
712 bool _tryWriteASCIIString(String value) {
713 _prepare(4, 1, additionalBytes: value.length + 1);
714 final length = value.length;
715 var offset = _buf.lengthInBytes - _tail + 4;
716 for (var i = 0; i < length; i++) {
717 // utf16 code unit, e.g. for '†' it's [0x20 0x20], which is 8224 decimal.
718 // ASCII characters go from 0x00 to 0x7F (which is 0 to 127 decimal).
719 final char = value.codeUnitAt(i);
720 if ((char & ~0x7F) != 0) {
721 return false;
722 }
723 _buf.setUint8(offset++, char);
724 }
725 _buf.setUint8(offset, 0); // trailing zero
726 _setUint32AtTail(_tail, value.length);
727 return true;
728 }
729
730 @pragma('vm:prefer-inline')
731 void _writeUTFString(String value) {
732 final bytes = utf8.encode(value) as Uint8List;
733 final length = bytes.length;
734 _prepare(4, 1, additionalBytes: length + 1);
735 _setUint32AtTail(_tail, length);
736 var offset = _buf.lengthInBytes - _tail + 4;
737 for (var i = 0; i < length; i++) {
738 _buf.setUint8(offset++, bytes[i]);
739 }
740 _buf.setUint8(offset, 0); // trailing zero
741 }
742
743 /// Used to assert whether a "Table" is currently being built.
744 ///
745 /// If you hit `assert(!_inVTable())`, you're trying to add table fields
746 /// without starting a table with [Builder.startTable()].
747 ///
748 /// If you hit `assert(_inVTable())`, you're trying to construct a
749 /// Table/Vector/String during the construction of its parent table,
750 /// between the MyTableBuilder and [Builder.endTable()].
751 /// Move the creation of these sub-objects to before the MyTableBuilder to
752 /// not get this assert.
753 @pragma('vm:prefer-inline')
754 bool get _inVTable => _currentVTable != null;
755
756 /// The number of bytes that have been written to the buffer so far. The
757 /// most recently written byte is this many bytes from the end of the buffer.
758 @pragma('vm:prefer-inline')
759 int get offset => _tail;
760
761 /// Zero-pads the buffer, which may be required for some struct layouts.
762 @pragma('vm:prefer-inline')
763 void pad(int howManyBytes) {
764 for (var i = 0; i < howManyBytes; i++) {
765 putUint8(0);
766 }
767 }
768
769 /// Prepare for writing the given `count` of scalars of the given `size`.
770 /// Additionally allocate the specified `additionalBytes`. Update the current
771 /// tail pointer to point at the allocated space.
772 @pragma('vm:prefer-inline')
773 void _prepare(int size, int count, {int additionalBytes = 0}) {
774 assert(!_finished);
775 // Update the alignment.
776 if (_maxAlign < size) {
777 _maxAlign = size;
778 }
779 // Prepare amount of required space.
780 final dataSize = size * count + additionalBytes;
781 final alignDelta = (-(_tail + dataSize)) & (size - 1);
782 final bufSize = alignDelta + dataSize;
783 // Ensure that we have the required amount of space.
784 {
785 final oldCapacity = _buf.lengthInBytes;
786 if (_tail + bufSize > oldCapacity) {
787 final desiredNewCapacity = (oldCapacity + bufSize) * 2;
788 var deltaCapacity = desiredNewCapacity - oldCapacity;
789 deltaCapacity += (-deltaCapacity) & (_maxAlign - 1);
790 final newCapacity = oldCapacity + deltaCapacity;
791 _buf = _allocator.resize(_buf, newCapacity, _tail, 0);
792 }
793 }
794
795 // zero out the added padding
796 for (var i = _tail + 1; i <= _tail + alignDelta; i++) {
797 _setUint8AtTail(i, 0);
798 }
799
800 // Update the tail pointer.
801 _tail += bufSize;
802 }
803
804 /// Record the offset of the given [field].
805 @pragma('vm:prefer-inline')
806 void _trackField(int field) => _currentVTable!.addField(field, _tail);
807
808 @pragma('vm:prefer-inline')
809 void _setFloat64AtTail(int tail, double x) =>
810 _buf.setFloat64(_buf.lengthInBytes - tail, x, Endian.little);
811
812 @pragma('vm:prefer-inline')
813 void _setFloat32AtTail(int tail, double x) =>
814 _buf.setFloat32(_buf.lengthInBytes - tail, x, Endian.little);
815
816 @pragma('vm:prefer-inline')
817 void _setUint64AtTail(int tail, int x) =>
818 _buf.setUint64(_buf.lengthInBytes - tail, x, Endian.little);
819
820 @pragma('vm:prefer-inline')
821 void _setInt64AtTail(int tail, int x) =>
822 _buf.setInt64(_buf.lengthInBytes - tail, x, Endian.little);
823
824 @pragma('vm:prefer-inline')
825 void _setInt32AtTail(int tail, int x) =>
826 _buf.setInt32(_buf.lengthInBytes - tail, x, Endian.little);
827
828 @pragma('vm:prefer-inline')
829 void _setUint32AtTail(int tail, int x) =>
830 _buf.setUint32(_buf.lengthInBytes - tail, x, Endian.little);
831
832 @pragma('vm:prefer-inline')
833 void _setInt16AtTail(int tail, int x) =>
834 _buf.setInt16(_buf.lengthInBytes - tail, x, Endian.little);
835
836 @pragma('vm:prefer-inline')
837 void _setUint16AtTail(int tail, int x) =>
838 _buf.setUint16(_buf.lengthInBytes - tail, x, Endian.little);
839
840 @pragma('vm:prefer-inline')
841 void _setInt8AtTail(int tail, int x) =>
842 _buf.setInt8(_buf.lengthInBytes - tail, x);
843
844 @pragma('vm:prefer-inline')
845 void _setUint8AtTail(int tail, int x) =>
846 _buf.setUint8(_buf.lengthInBytes - tail, x);
847}
848
849/// Reader of lists of boolean values.
850///
851/// The returned unmodifiable lists lazily read values on access.
852class BoolListReader extends Reader<List<bool>> {
853 const BoolListReader();
854
855 @override
856 @pragma('vm:prefer-inline')
857 int get size => _sizeofUint32;
858
859 @override
860 @pragma('vm:prefer-inline')
861 List<bool> read(BufferContext bc, int offset) =>
862 _FbBoolList(bc, bc.derefObject(offset));
863}
864
865/// The reader of booleans.
866class BoolReader extends Reader<bool> {
867 const BoolReader() : super();
868
869 @override
870 @pragma('vm:prefer-inline')
871 int get size => _sizeofUint8;
872
873 @override
874 @pragma('vm:prefer-inline')
875 bool read(BufferContext bc, int offset) => bc._getInt8(offset) != 0;
876}
877
878/// The reader of lists of 64-bit float values.
879///
880/// The returned unmodifiable lists lazily read values on access.
881class Float64ListReader extends Reader<List<double>> {
882 const Float64ListReader();
883
884 @override
885 @pragma('vm:prefer-inline')
886 int get size => _sizeofFloat64;
887
888 @override
889 @pragma('vm:prefer-inline')
890 List<double> read(BufferContext bc, int offset) =>
891 _FbFloat64List(bc, bc.derefObject(offset));
892}
893
894class Float32ListReader extends Reader<List<double>> {
895 const Float32ListReader();
896
897 @override
898 @pragma('vm:prefer-inline')
899 int get size => _sizeofFloat32;
900
901 @override
902 @pragma('vm:prefer-inline')
903 List<double> read(BufferContext bc, int offset) =>
904 _FbFloat32List(bc, bc.derefObject(offset));
905}
906
907class Float64Reader extends Reader<double> {
908 const Float64Reader();
909
910 @override
911 @pragma('vm:prefer-inline')
912 int get size => _sizeofFloat64;
913
914 @override
915 @pragma('vm:prefer-inline')
916 double read(BufferContext bc, int offset) => bc._getFloat64(offset);
917}
918
919class Float32Reader extends Reader<double> {
920 const Float32Reader();
921
922 @override
923 @pragma('vm:prefer-inline')
924 int get size => _sizeofFloat32;
925
926 @override
927 @pragma('vm:prefer-inline')
928 double read(BufferContext bc, int offset) => bc._getFloat32(offset);
929}
930
931class Int64Reader extends Reader<int> {
932 const Int64Reader() : super();
933
934 @override
935 @pragma('vm:prefer-inline')
936 int get size => _sizeofInt64;
937
938 @override
939 @pragma('vm:prefer-inline')
940 int read(BufferContext bc, int offset) => bc._getInt64(offset);
941}
942
943/// The reader of signed 32-bit integers.
944class Int32Reader extends Reader<int> {
945 const Int32Reader() : super();
946
947 @override
948 @pragma('vm:prefer-inline')
949 int get size => _sizeofInt32;
950
951 @override
952 @pragma('vm:prefer-inline')
953 int read(BufferContext bc, int offset) => bc._getInt32(offset);
954}
955
956/// The reader of signed 32-bit integers.
957class Int16Reader extends Reader<int> {
958 const Int16Reader() : super();
959
960 @override
961 @pragma('vm:prefer-inline')
962 int get size => _sizeofInt16;
963
964 @override
965 @pragma('vm:prefer-inline')
966 int read(BufferContext bc, int offset) => bc._getInt16(offset);
967}
968
969/// The reader of 8-bit signed integers.
970class Int8Reader extends Reader<int> {
971 const Int8Reader() : super();
972
973 @override
974 @pragma('vm:prefer-inline')
975 int get size => _sizeofInt8;
976
977 @override
978 @pragma('vm:prefer-inline')
979 int read(BufferContext bc, int offset) => bc._getInt8(offset);
980}
981
982/// The reader of lists of objects. Lazy by default - see [lazy].
983class ListReader<E> extends Reader<List<E>> {
984 final Reader<E> _elementReader;
985
986 /// Enables lazy reading of the list
987 ///
988 /// If true, the returned unmodifiable list lazily reads objects on access.
989 /// Therefore, the underlying buffer must not change while accessing the list.
990 ///
991 /// If false, reads the whole list immediately on access.
992 final bool lazy;
993
994 const ListReader(this._elementReader, {this.lazy = true});
995
996 @override
997 @pragma('vm:prefer-inline')
998 int get size => _sizeofUint32;
999
1000 @override
1001 List<E> read(BufferContext bc, int offset) {
1002 final listOffset = bc.derefObject(offset);
1003 return lazy
1004 ? _FbGenericList<E>(_elementReader, bc, listOffset)
1005 : List<E>.generate(
1006 bc.buffer.getUint32(listOffset, Endian.little),
1007 (int index) => _elementReader.read(
1008 bc, listOffset + size + _elementReader.size * index),
1009 growable: true);
1010 }
1011}
1012
1013/// Object that can read a value at a [BufferContext].
1014abstract class Reader<T> {
1015 const Reader();
1016
1017 /// The size of the value in bytes.
1018 int get size;
1019
1020 /// Read the value at the given [offset] in [bc].
1021 T read(BufferContext bc, int offset);
1022
1023 /// Read the value of the given [field] in the given [object].
1024 @pragma('vm:prefer-inline')
1025 T vTableGet(BufferContext object, int offset, int field, T defaultValue) {
1026 final fieldOffset = _vTableFieldOffset(object, offset, field);
1027 return fieldOffset == 0 ? defaultValue : read(object, offset + fieldOffset);
1028 }
1029
1030 /// Read the value of the given [field] in the given [object].
1031 @pragma('vm:prefer-inline')
1032 T? vTableGetNullable(BufferContext object, int offset, int field) {
1033 final fieldOffset = _vTableFieldOffset(object, offset, field);
1034 return fieldOffset == 0 ? null : read(object, offset + fieldOffset);
1035 }
1036
1037 @pragma('vm:prefer-inline')
1038 int _vTableFieldOffset(BufferContext object, int offset, int field) {
1039 final vTableSOffset = object._getInt32(offset);
1040 final vTableOffset = offset - vTableSOffset;
1041 final vTableSize = object._getUint16(vTableOffset);
1042 if (field >= vTableSize) return 0;
1043 return object._getUint16(vTableOffset + field);
1044 }
1045}
1046
1047/// The reader of string values.
1048class StringReader extends Reader<String> {
1049 final bool asciiOptimization;
1050
1051 const StringReader({this.asciiOptimization = false}) : super();
1052
1053 @override
1054 @pragma('vm:prefer-inline')
1055 int get size => _sizeofUint32;
1056
1057 @override
1058 @pragma('vm:prefer-inline')
1059 String read(BufferContext bc, int offset) {
1060 final strOffset = bc.derefObject(offset);
1061 final length = bc._getUint32(strOffset);
1062 final bytes = bc._asUint8List(strOffset + _sizeofUint32, length);
1063 if (asciiOptimization && _isLatin(bytes)) {
1064 return String.fromCharCodes(bytes);
1065 }
1066 return utf8.decode(bytes);
1067 }
1068
1069 @pragma('vm:prefer-inline')
1070 static bool _isLatin(Uint8List bytes) {
1071 final length = bytes.length;
1072 for (var i = 0; i < length; i++) {
1073 if (bytes[i] > 127) {
1074 return false;
1075 }
1076 }
1077 return true;
1078 }
1079}
1080
1081/// An abstract reader for structs.
1082abstract class StructReader<T> extends Reader<T> {
1083 const StructReader();
1084
1085 /// Return the object at `offset`.
1086 T createObject(BufferContext bc, int offset);
1087
1088 @override
1089 T read(BufferContext bc, int offset) {
1090 return createObject(bc, offset);
1091 }
1092}
1093
1094/// An abstract reader for tables.
1095abstract class TableReader<T> extends Reader<T> {
1096 const TableReader();
1097
1098 @override
1099 @pragma('vm:prefer-inline')
1100 int get size => 4;
1101
1102 /// Return the object at [offset].
1103 T createObject(BufferContext bc, int offset);
1104
1105 @override
1106 T read(BufferContext bc, int offset) {
1107 final objectOffset = bc.derefObject(offset);
1108 return createObject(bc, objectOffset);
1109 }
1110}
1111
1112/// Reader of lists of unsigned 32-bit integer values.
1113///
1114/// The returned unmodifiable lists lazily read values on access.
1115class Uint32ListReader extends Reader<List<int>> {
1116 const Uint32ListReader();
1117
1118 @override
1119 @pragma('vm:prefer-inline')
1120 int get size => _sizeofUint32;
1121
1122 @override
1123 @pragma('vm:prefer-inline')
1124 List<int> read(BufferContext bc, int offset) =>
1125 _FbUint32List(bc, bc.derefObject(offset));
1126}
1127
1128/// The reader of unsigned 64-bit integers.
1129///
1130/// WARNING: May have compatibility issues with JavaScript
1131class Uint64Reader extends Reader<int> {
1132 const Uint64Reader() : super();
1133
1134 @override
1135 @pragma('vm:prefer-inline')
1136 int get size => _sizeofUint64;
1137
1138 @override
1139 @pragma('vm:prefer-inline')
1140 int read(BufferContext bc, int offset) => bc._getUint64(offset);
1141}
1142
1143/// The reader of unsigned 32-bit integers.
1144class Uint32Reader extends Reader<int> {
1145 const Uint32Reader() : super();
1146
1147 @override
1148 @pragma('vm:prefer-inline')
1149 int get size => _sizeofUint32;
1150
1151 @override
1152 @pragma('vm:prefer-inline')
1153 int read(BufferContext bc, int offset) => bc._getUint32(offset);
1154}
1155
1156/// Reader of lists of unsigned 32-bit integer values.
1157///
1158/// The returned unmodifiable lists lazily read values on access.
1159class Uint16ListReader extends Reader<List<int>> {
1160 const Uint16ListReader();
1161
1162 @override
1163 @pragma('vm:prefer-inline')
1164 int get size => _sizeofUint32;
1165
1166 @override
1167 @pragma('vm:prefer-inline')
1168 List<int> read(BufferContext bc, int offset) =>
1169 _FbUint16List(bc, bc.derefObject(offset));
1170}
1171
1172/// The reader of unsigned 32-bit integers.
1173class Uint16Reader extends Reader<int> {
1174 const Uint16Reader() : super();
1175
1176 @override
1177 @pragma('vm:prefer-inline')
1178 int get size => _sizeofUint16;
1179
1180 @override
1181 @pragma('vm:prefer-inline')
1182 int read(BufferContext bc, int offset) => bc._getUint16(offset);
1183}
1184
1185/// Reader of unmodifiable binary data (a list of unsigned 8-bit integers).
1186class Uint8ListReader extends Reader<List<int>> {
1187 /// Enables lazy reading of the list
1188 ///
1189 /// If true, the returned unmodifiable list lazily reads bytes on access.
1190 /// Therefore, the underlying buffer must not change while accessing the list.
1191 ///
1192 /// If false, reads the whole list immediately as an Uint8List.
1193 final bool lazy;
1194
1195 const Uint8ListReader({this.lazy = true});
1196
1197 @override
1198 @pragma('vm:prefer-inline')
1199 int get size => _sizeofUint32;
1200
1201 @override
1202 @pragma('vm:prefer-inline')
1203 List<int> read(BufferContext bc, int offset) {
1204 final listOffset = bc.derefObject(offset);
1205 if (lazy) return _FbUint8List(bc, listOffset);
1206
1207 final length = bc._getUint32(listOffset);
1208 final result = Uint8List(length);
1209 var pos = listOffset + _sizeofUint32;
1210 for (var i = 0; i < length; i++, pos++) {
1211 result[i] = bc._getUint8(pos);
1212 }
1213 return result;
1214 }
1215}
1216
1217/// The reader of unsigned 8-bit integers.
1218class Uint8Reader extends Reader<int> {
1219 const Uint8Reader() : super();
1220
1221 @override
1222 @pragma('vm:prefer-inline')
1223 int get size => _sizeofUint8;
1224
1225 @override
1226 @pragma('vm:prefer-inline')
1227 int read(BufferContext bc, int offset) => bc._getUint8(offset);
1228}
1229
1230/// Reader of unmodifiable binary data (a list of signed 8-bit integers).
1231class Int8ListReader extends Reader<List<int>> {
1232 /// Enables lazy reading of the list
1233 ///
1234 /// If true, the returned unmodifiable list lazily reads bytes on access.
1235 /// Therefore, the underlying buffer must not change while accessing the list.
1236 ///
1237 /// If false, reads the whole list immediately as an Uint8List.
1238 final bool lazy;
1239
1240 const Int8ListReader({this.lazy = true});
1241
1242 @override
1243 @pragma('vm:prefer-inline')
1244 int get size => _sizeofUint32;
1245
1246 @override
1247 @pragma('vm:prefer-inline')
1248 List<int> read(BufferContext bc, int offset) {
1249 final listOffset = bc.derefObject(offset);
1250 if (lazy) return _FbUint8List(bc, listOffset);
1251
1252 final length = bc._getUint32(listOffset);
1253 final result = Int8List(length);
1254 var pos = listOffset + _sizeofUint32;
1255 for (var i = 0; i < length; i++, pos++) {
1256 result[i] = bc._getInt8(pos);
1257 }
1258 return result;
1259 }
1260}
1261
1262/// The list backed by 64-bit values - Uint64 length and Float64.
1263class _FbFloat64List extends _FbList<double> {
1264 _FbFloat64List(BufferContext bc, int offset) : super(bc, offset);
1265
1266 @override
1267 @pragma('vm:prefer-inline')
1268 double operator [](int i) => bc._getFloat64(offset + 4 + 8 * i);
1269}
1270
1271/// The list backed by 32-bit values - Float32.
1272class _FbFloat32List extends _FbList<double> {
1273 _FbFloat32List(BufferContext bc, int offset) : super(bc, offset);
1274
1275 @override
1276 @pragma('vm:prefer-inline')
1277 double operator [](int i) => bc._getFloat32(offset + 4 + 4 * i);
1278}
1279
1280/// List backed by a generic object which may have any size.
1281class _FbGenericList<E> extends _FbList<E> {
1282 final Reader<E> elementReader;
1283
1284 List<E?>? _items;
1285
1286 _FbGenericList(this.elementReader, BufferContext bp, int offset)
1287 : super(bp, offset);
1288
1289 @override
1290 @pragma('vm:prefer-inline')
1291 E operator [](int i) {
1292 _items ??= List<E?>.filled(length, null);
1293 var item = _items![i];
1294 if (item == null) {
1295 item = elementReader.read(bc, offset + 4 + elementReader.size * i);
1296 _items![i] = item;
1297 }
1298 return item!;
1299 }
1300}
1301
1302/// The base class for immutable lists read from flat buffers.
1303abstract class _FbList<E> extends Object with ListMixin<E> implements List<E> {
1304 final BufferContext bc;
1305 final int offset;
1306 int? _length;
1307
1308 _FbList(this.bc, this.offset);
1309
1310 @override
1311 @pragma('vm:prefer-inline')
1312 int get length => _length ??= bc._getUint32(offset);
1313
1314 @override
1315 set length(int i) => throw StateError('Attempt to modify immutable list');
1316
1317 @override
1318 void operator []=(int i, E e) =>
1319 throw StateError('Attempt to modify immutable list');
1320}
1321
1322/// List backed by 32-bit unsigned integers.
1323class _FbUint32List extends _FbList<int> {
1324 _FbUint32List(BufferContext bc, int offset) : super(bc, offset);
1325
1326 @override
1327 @pragma('vm:prefer-inline')
1328 int operator [](int i) => bc._getUint32(offset + 4 + 4 * i);
1329}
1330
1331/// List backed by 16-bit unsigned integers.
1332class _FbUint16List extends _FbList<int> {
1333 _FbUint16List(BufferContext bc, int offset) : super(bc, offset);
1334
1335 @override
1336 @pragma('vm:prefer-inline')
1337 int operator [](int i) => bc._getUint16(offset + 4 + 2 * i);
1338}
1339
1340/// List backed by 8-bit unsigned integers.
1341class _FbUint8List extends _FbList<int> {
1342 _FbUint8List(BufferContext bc, int offset) : super(bc, offset);
1343
1344 @override
1345 @pragma('vm:prefer-inline')
1346 int operator [](int i) => bc._getUint8(offset + 4 + i);
1347}
1348
1349/// List backed by 8-bit signed integers.
1350class _FbInt8List extends _FbList<int> {
1351 _FbInt8List(BufferContext bc, int offset) : super(bc, offset);
1352
1353 @override
1354 @pragma('vm:prefer-inline')
1355 int operator [](int i) => bc._getInt8(offset + 4 + i);
1356}
1357
1358/// List backed by 8-bit unsigned integers.
1359class _FbBoolList extends _FbList<bool> {
1360 _FbBoolList(BufferContext bc, int offset) : super(bc, offset);
1361
1362 @override
1363 @pragma('vm:prefer-inline')
1364 bool operator [](int i) => bc._getUint8(offset + 4 + i) == 1 ? true : false;
1365}
1366
1367/// Class that describes the structure of a table.
1368class _VTable {
1369 static const int _metadataLength = 4;
1370
1371 final int numFields;
1372
1373 // Note: fieldOffsets start as "tail offsets" and are then transformed by
1374 // [computeFieldOffsets()] to actual offsets when a table is finished.
1375 final Uint32List fieldOffsets;
1376 bool offsetsComputed = false;
1377
1378 _VTable(this.numFields) : fieldOffsets = Uint32List(numFields);
1379
1380 /// The size of the table that uses this VTable.
1381 int tableSize = 0;
1382
1383 /// The tail of this VTable. It is used to share the same VTable between
1384 /// multiple tables of identical structure.
1385 int tail = 0;
1386
1387 int get _vTableSize => numOfUint16 * _sizeofUint16;
1388
1389 int get numOfUint16 => 1 + 1 + numFields;
1390
1391 @pragma('vm:prefer-inline')
1392 void addField(int field, int offset) {
1393 assert(!offsetsComputed);
1394 assert(offset > 0); // it's impossible for field to start at the buffer end
1395 assert(offset <= 4294967295); // uint32 max
1396 fieldOffsets[field] = offset;
1397 }
1398
1399 @pragma('vm:prefer-inline')
1400 bool _offsetsMatch(int vt2Start, ByteData buf) {
1401 assert(offsetsComputed);
1402 for (var i = 0; i < numFields; i++) {
1403 if (fieldOffsets[i] !=
1404 buf.getUint16(vt2Start + _metadataLength + (2 * i), Endian.little)) {
1405 return false;
1406 }
1407 }
1408 return true;
1409 }
1410
1411 /// Fill the [fieldOffsets] field.
1412 @pragma('vm:prefer-inline')
1413 void computeFieldOffsets(int tableTail) {
1414 assert(!offsetsComputed);
1415 offsetsComputed = true;
1416 for (var i = 0; i < numFields; i++) {
1417 if (fieldOffsets[i] != 0) {
1418 fieldOffsets[i] = tableTail - fieldOffsets[i];
1419 }
1420 }
1421 }
1422
1423 /// Outputs this VTable to [buf], which is is expected to be aligned to 16-bit
1424 /// and have at least [numOfUint16] 16-bit words available.
1425 @pragma('vm:prefer-inline')
1426 void output(ByteData buf, int bufOffset) {
1427 assert(offsetsComputed);
1428 // VTable size.
1429 buf.setUint16(bufOffset, numOfUint16 * 2, Endian.little);
1430 bufOffset += 2;
1431 // Table size.
1432 buf.setUint16(bufOffset, tableSize, Endian.little);
1433 bufOffset += 2;
1434 // Field offsets.
1435 for (var i = 0; i < numFields; i++) {
1436 buf.setUint16(bufOffset, fieldOffsets[i], Endian.little);
1437 bufOffset += 2;
1438 }
1439 }
1440}
1441
1442/// The interface that [Builder] uses to allocate buffers for encoding.
1443abstract class Allocator {
1444 const Allocator();
1445
1446 /// Allocate a [ByteData] buffer of a given size.
1447 ByteData allocate(int size);
1448
1449 /// Free the given [ByteData] buffer previously allocated by [allocate].
1450 void deallocate(ByteData data);
1451
1452 /// Reallocate [newSize] bytes of memory, replacing the old [oldData]. This
1453 /// grows downwards, and is intended specifically for use with [Builder].
1454 /// Params [inUseBack] and [inUseFront] indicate how much of [oldData] is
1455 /// actually in use at each end, and needs to be copied.
1456 ByteData resize(
1457 ByteData oldData, int newSize, int inUseBack, int inUseFront) {
1458 final newData = allocate(newSize);
1459 _copyDownward(oldData, newData, inUseBack, inUseFront);
1460 deallocate(oldData);
1461 return newData;
1462 }
1463
1464 /// Called by [resize] to copy memory from [oldData] to [newData]. Only
1465 /// memory of size [inUseFront] and [inUseBack] will be copied from the front
1466 /// and back of the old memory allocation.
1467 void _copyDownward(
1468 ByteData oldData, ByteData newData, int inUseBack, int inUseFront) {
1469 if (inUseBack != 0) {
1470 newData.buffer.asUint8List().setAll(
1471 newData.lengthInBytes - inUseBack,
1472 oldData.buffer.asUint8List().getRange(
1473 oldData.lengthInBytes - inUseBack, oldData.lengthInBytes));
1474 }
1475 if (inUseFront != 0) {
1476 newData.buffer
1477 .asUint8List()
1478 .setAll(0, oldData.buffer.asUint8List().getRange(0, inUseFront));
1479 }
1480 }
1481}
1482
1483class DefaultAllocator extends Allocator {
1484 const DefaultAllocator();
1485
1486 @override
1487 ByteData allocate(int size) => ByteData(size);
1488
1489 @override
1490 void deallocate(ByteData data) {
1491 // nothing to do, it's garbage-collected
1492 }
1493}
View as plain text