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