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 var 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 (var 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 (var 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 (var 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 (var 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 (var 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 (var 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 (var 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 (var 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 (var 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 (var 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 (var 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 var dataSize = size * count + additionalBytes; 781 var alignDelta = (-(_tail + dataSize)) & (size - 1); 782 var bufSize = alignDelta + dataSize; 783 // Ensure that we have the required amount of space. 784 { 785 var oldCapacity = _buf.lengthInBytes; 786 if (_tail + bufSize > oldCapacity) { 787 var desiredNewCapacity = (oldCapacity + bufSize) * 2; 788 var deltaCapacity = desiredNewCapacity - oldCapacity; 789 deltaCapacity += (-deltaCapacity) & (_maxAlign - 1); 790 var 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 var 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 var 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 var vTableSOffset = object._getInt32(offset); 1040 var vTableOffset = offset - vTableSOffset; 1041 var 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 var strOffset = bc.derefObject(offset); 1061 var length = bc._getUint32(strOffset); 1062 var 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 var 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 var 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} 1494