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