| OLD | NEW |
| (Empty) | |
| 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 |
| 5 library analyzer.src.summary.flat_buffers; |
| 6 |
| 7 import 'dart:collection'; |
| 8 import 'dart:convert'; |
| 9 import 'dart:math'; |
| 10 import 'dart:typed_data'; |
| 11 |
| 12 /** |
| 13 * Reader of lists of boolean values. |
| 14 * |
| 15 * The returned unmodifiable lists lazily read values on access. |
| 16 */ |
| 17 class BoolListReader extends Reader<List<bool>> { |
| 18 const BoolListReader(); |
| 19 |
| 20 @override |
| 21 int get size => 4; |
| 22 |
| 23 @override |
| 24 List<bool> read(BufferContext bc, int offset) => |
| 25 new _FbBoolList(bc, bc.derefObject(offset)); |
| 26 } |
| 27 |
| 28 /** |
| 29 * The reader of booleans. |
| 30 */ |
| 31 class BoolReader extends Reader<bool> { |
| 32 const BoolReader() : super(); |
| 33 |
| 34 @override |
| 35 int get size => 1; |
| 36 |
| 37 @override |
| 38 bool read(BufferContext bc, int offset) => bc._getInt8(offset) != 0; |
| 39 } |
| 40 |
| 41 /** |
| 42 * Buffer with data and some context about it. |
| 43 */ |
| 44 class BufferContext { |
| 45 final ByteData _buffer; |
| 46 |
| 47 factory BufferContext.fromBytes(List<int> byteList) { |
| 48 Uint8List uint8List = _asUint8List(byteList); |
| 49 ByteData buf = new ByteData.view(uint8List.buffer, uint8List.offsetInBytes); |
| 50 return new BufferContext._(buf); |
| 51 } |
| 52 |
| 53 BufferContext._(this._buffer); |
| 54 |
| 55 int derefObject(int offset) { |
| 56 return offset + _getUint32(offset); |
| 57 } |
| 58 |
| 59 Uint8List _asUint8LIst(int offset, int length) => |
| 60 _buffer.buffer.asUint8List(_buffer.offsetInBytes + offset, length); |
| 61 |
| 62 double _getFloat64(int offset) => |
| 63 _buffer.getFloat64(offset, Endianness.LITTLE_ENDIAN); |
| 64 |
| 65 int _getInt32(int offset) => |
| 66 _buffer.getInt32(offset, Endianness.LITTLE_ENDIAN); |
| 67 |
| 68 int _getInt8(int offset) => _buffer.getInt8(offset); |
| 69 |
| 70 int _getUint16(int offset) => |
| 71 _buffer.getUint16(offset, Endianness.LITTLE_ENDIAN); |
| 72 |
| 73 int _getUint32(int offset) => |
| 74 _buffer.getUint32(offset, Endianness.LITTLE_ENDIAN); |
| 75 |
| 76 int _getUint8(int offset) => _buffer.getUint8(offset); |
| 77 |
| 78 /** |
| 79 * If the [byteList] is already a [Uint8List] return it. |
| 80 * Otherwise return a [Uint8List] copy of the [byteList]. |
| 81 */ |
| 82 static Uint8List _asUint8List(List<int> byteList) { |
| 83 if (byteList is Uint8List) { |
| 84 return byteList; |
| 85 } else { |
| 86 return new Uint8List.fromList(byteList); |
| 87 } |
| 88 } |
| 89 } |
| 90 |
| 91 /** |
| 92 * Class that helps building flat buffers. |
| 93 */ |
| 94 class Builder { |
| 95 final int initialSize; |
| 96 |
| 97 /** |
| 98 * The list of existing VTable(s). |
| 99 */ |
| 100 final List<_VTable> _vTables = <_VTable>[]; |
| 101 |
| 102 ByteData _buf; |
| 103 |
| 104 /** |
| 105 * The maximum alignment that has been seen so far. If [_buf] has to be |
| 106 * reallocated in the future (to insert room at its start for more bytes) the |
| 107 * reallocation will need to be a multiple of this many bytes. |
| 108 */ |
| 109 int _maxAlign; |
| 110 |
| 111 /** |
| 112 * The number of bytes that have been written to the buffer so far. The |
| 113 * most recently written byte is this many bytes from the end of [_buf]. |
| 114 */ |
| 115 int _tail; |
| 116 |
| 117 /** |
| 118 * The location of the end of the current table, measured in bytes from the |
| 119 * end of [_buf], or `null` if a table is not currently being built. |
| 120 */ |
| 121 int _currentTableEndTail; |
| 122 |
| 123 _VTable _currentVTable; |
| 124 |
| 125 /** |
| 126 * Map containing all strings that have been written so far. This allows us |
| 127 * to avoid duplicating strings. |
| 128 */ |
| 129 Map<String, Offset<String>> _strings = <String, Offset<String>>{}; |
| 130 |
| 131 Builder({this.initialSize: 1024}) { |
| 132 reset(); |
| 133 } |
| 134 |
| 135 /** |
| 136 * Add the [field] with the given boolean [value]. The field is not added if |
| 137 * the [value] is equal to [def]. Booleans are stored as 8-bit fields with |
| 138 * `0` for `false` and `1` for `true`. |
| 139 */ |
| 140 void addBool(int field, bool value, [bool def]) { |
| 141 _ensureCurrentVTable(); |
| 142 if (value != null && value != def) { |
| 143 int size = 1; |
| 144 _prepare(size, 1); |
| 145 _trackField(field); |
| 146 _buf.setInt8(_buf.lengthInBytes - _tail, value ? 1 : 0); |
| 147 } |
| 148 } |
| 149 |
| 150 /** |
| 151 * Add the [field] with the given 32-bit signed integer [value]. The field is |
| 152 * not added if the [value] is equal to [def]. |
| 153 */ |
| 154 void addInt32(int field, int value, [int def]) { |
| 155 _ensureCurrentVTable(); |
| 156 if (value != null && value != def) { |
| 157 int size = 4; |
| 158 _prepare(size, 1); |
| 159 _trackField(field); |
| 160 _setInt32AtTail(_buf, _tail, value); |
| 161 } |
| 162 } |
| 163 |
| 164 /** |
| 165 * Add the [field] with the given 8-bit signed integer [value]. The field is |
| 166 * not added if the [value] is equal to [def]. |
| 167 */ |
| 168 void addInt8(int field, int value, [int def]) { |
| 169 _ensureCurrentVTable(); |
| 170 if (value != null && value != def) { |
| 171 int size = 1; |
| 172 _prepare(size, 1); |
| 173 _trackField(field); |
| 174 _buf.setInt8(_buf.lengthInBytes - _tail, value); |
| 175 } |
| 176 } |
| 177 |
| 178 /** |
| 179 * Add the [field] referencing an object with the given [offset]. |
| 180 */ |
| 181 void addOffset(int field, Offset offset) { |
| 182 _ensureCurrentVTable(); |
| 183 if (offset != null) { |
| 184 _prepare(4, 1); |
| 185 _trackField(field); |
| 186 _setUint32AtTail(_buf, _tail, _tail - offset._tail); |
| 187 } |
| 188 } |
| 189 |
| 190 /** |
| 191 * Add the [field] with the given 32-bit unsigned integer [value]. The field |
| 192 * is not added if the [value] is equal to [def]. |
| 193 */ |
| 194 void addUint32(int field, int value, [int def]) { |
| 195 _ensureCurrentVTable(); |
| 196 if (value != null && value != def) { |
| 197 int size = 4; |
| 198 _prepare(size, 1); |
| 199 _trackField(field); |
| 200 _setUint32AtTail(_buf, _tail, value); |
| 201 } |
| 202 } |
| 203 |
| 204 /** |
| 205 * Add the [field] with the given 8-bit unsigned integer [value]. The field |
| 206 * is not added if the [value] is equal to [def]. |
| 207 */ |
| 208 void addUint8(int field, int value, [int def]) { |
| 209 _ensureCurrentVTable(); |
| 210 if (value != null && value != def) { |
| 211 int size = 1; |
| 212 _prepare(size, 1); |
| 213 _trackField(field); |
| 214 _setUint8AtTail(_buf, _tail, value); |
| 215 } |
| 216 } |
| 217 |
| 218 /** |
| 219 * End the current table and return its offset. |
| 220 */ |
| 221 Offset endTable() { |
| 222 if (_currentVTable == null) { |
| 223 throw new StateError('Start a table before ending it.'); |
| 224 } |
| 225 // Prepare for writing the VTable. |
| 226 _prepare(4, 1); |
| 227 int tableTail = _tail; |
| 228 // Prepare the size of the current table. |
| 229 _currentVTable.tableSize = tableTail - _currentTableEndTail; |
| 230 // Prepare the VTable to use for the current table. |
| 231 int vTableTail; |
| 232 { |
| 233 _currentVTable.computeFieldOffsets(tableTail); |
| 234 // Try to find an existing compatible VTable. |
| 235 for (int i = 0; i < _vTables.length; i++) { |
| 236 _VTable vTable = _vTables[i]; |
| 237 if (_currentVTable.canUseExistingVTable(vTable)) { |
| 238 vTableTail = vTable.tail; |
| 239 break; |
| 240 } |
| 241 } |
| 242 // Write a new VTable. |
| 243 if (vTableTail == null) { |
| 244 _prepare(2, _currentVTable.numOfUint16); |
| 245 vTableTail = _tail; |
| 246 _currentVTable.tail = vTableTail; |
| 247 _currentVTable.output(_buf, _buf.lengthInBytes - _tail); |
| 248 _vTables.add(_currentVTable); |
| 249 } |
| 250 } |
| 251 // Set the VTable offset. |
| 252 _setInt32AtTail(_buf, tableTail, vTableTail - tableTail); |
| 253 // Done with this table. |
| 254 _currentVTable = null; |
| 255 return new Offset(tableTail); |
| 256 } |
| 257 |
| 258 /** |
| 259 * Finish off the creation of the buffer. The given [offset] is used as the |
| 260 * root object offset, and usually references directly or indirectly every |
| 261 * written object. If [fileIdentifier] is specified (and not `null`), it is |
| 262 * interpreted as a 4-byte Latin-1 encoded string that should be placed at |
| 263 * bytes 4-7 of the file. |
| 264 */ |
| 265 Uint8List finish(Offset offset, [String fileIdentifier]) { |
| 266 _prepare(max(4, _maxAlign), fileIdentifier == null ? 1 : 2); |
| 267 int alignedTail = _tail + ((-_tail) % _maxAlign); |
| 268 _setUint32AtTail(_buf, alignedTail, alignedTail - offset._tail); |
| 269 if (fileIdentifier != null) { |
| 270 for (int i = 0; i < 4; i++) { |
| 271 _setUint8AtTail( |
| 272 _buf, alignedTail - 4 - i, fileIdentifier.codeUnitAt(i)); |
| 273 } |
| 274 } |
| 275 return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail); |
| 276 } |
| 277 |
| 278 /** |
| 279 * This is a low-level method, it should not be invoked by clients. |
| 280 */ |
| 281 Uint8List lowFinish() { |
| 282 int alignedTail = _tail + ((-_tail) % _maxAlign); |
| 283 return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail); |
| 284 } |
| 285 |
| 286 /** |
| 287 * This is a low-level method, it should not be invoked by clients. |
| 288 */ |
| 289 void lowReset() { |
| 290 _buf = new ByteData(initialSize); |
| 291 _maxAlign = 1; |
| 292 _tail = 0; |
| 293 } |
| 294 |
| 295 /** |
| 296 * This is a low-level method, it should not be invoked by clients. |
| 297 */ |
| 298 void lowWriteUint32(int value) { |
| 299 _prepare(4, 1); |
| 300 _setUint32AtTail(_buf, _tail, value); |
| 301 } |
| 302 |
| 303 /** |
| 304 * This is a low-level method, it should not be invoked by clients. |
| 305 */ |
| 306 void lowWriteUint8(int value) { |
| 307 _prepare(1, 1); |
| 308 _buf.setUint8(_buf.lengthInBytes - _tail, value); |
| 309 } |
| 310 |
| 311 /** |
| 312 * Reset the builder and make it ready for filling a new buffer. |
| 313 */ |
| 314 void reset() { |
| 315 _buf = new ByteData(initialSize); |
| 316 _maxAlign = 1; |
| 317 _tail = 0; |
| 318 _currentVTable = null; |
| 319 } |
| 320 |
| 321 /** |
| 322 * Start a new table. Must be finished with [endTable] invocation. |
| 323 */ |
| 324 void startTable() { |
| 325 if (_currentVTable != null) { |
| 326 throw new StateError('Inline tables are not supported.'); |
| 327 } |
| 328 _currentVTable = new _VTable(); |
| 329 _currentTableEndTail = _tail; |
| 330 } |
| 331 |
| 332 /** |
| 333 * Write the given list of [values]. |
| 334 */ |
| 335 Offset writeList(List<Offset> values) { |
| 336 _ensureNoVTable(); |
| 337 _prepare(4, 1 + values.length); |
| 338 Offset result = new Offset(_tail); |
| 339 int tail = _tail; |
| 340 _setUint32AtTail(_buf, tail, values.length); |
| 341 tail -= 4; |
| 342 for (Offset value in values) { |
| 343 _setUint32AtTail(_buf, tail, tail - value._tail); |
| 344 tail -= 4; |
| 345 } |
| 346 return result; |
| 347 } |
| 348 |
| 349 /** |
| 350 * Write the given list of boolean [values]. |
| 351 */ |
| 352 Offset writeListBool(List<bool> values) { |
| 353 int bitLength = values.length; |
| 354 int padding = (-bitLength) % 8; |
| 355 int byteLength = (bitLength + padding) ~/ 8; |
| 356 // Prepare the backing Uint8List. |
| 357 Uint8List bytes = new Uint8List(byteLength + 1); |
| 358 // Record every bit. |
| 359 int byteIndex = 0; |
| 360 int byte = 0; |
| 361 int mask = 1; |
| 362 for (int bitIndex = 0; bitIndex < bitLength; bitIndex++) { |
| 363 if (bitIndex != 0 && (bitIndex % 8 == 0)) { |
| 364 bytes[byteIndex++] = byte; |
| 365 byte = 0; |
| 366 mask = 1; |
| 367 } |
| 368 if (values[bitIndex]) { |
| 369 byte |= mask; |
| 370 } |
| 371 mask <<= 1; |
| 372 } |
| 373 // Write the last byte, even if it may be on the padding. |
| 374 bytes[byteIndex] = byte; |
| 375 // Write the padding length. |
| 376 bytes[byteLength] = padding; |
| 377 // Write as a Uint8 list. |
| 378 return writeListUint8(bytes); |
| 379 } |
| 380 |
| 381 /** |
| 382 * Write the given list of 64-bit float [values]. |
| 383 */ |
| 384 Offset writeListFloat64(List<double> values) { |
| 385 _ensureNoVTable(); |
| 386 _prepare(8, 1 + values.length); |
| 387 Offset result = new Offset(_tail); |
| 388 int tail = _tail; |
| 389 _setUint32AtTail(_buf, tail, values.length); |
| 390 tail -= 8; |
| 391 for (double value in values) { |
| 392 _setFloat64AtTail(_buf, tail, value); |
| 393 tail -= 8; |
| 394 } |
| 395 return result; |
| 396 } |
| 397 |
| 398 /** |
| 399 * Write the given list of signed 32-bit integer [values]. |
| 400 */ |
| 401 Offset writeListInt32(List<int> values) { |
| 402 _ensureNoVTable(); |
| 403 _prepare(4, 1 + values.length); |
| 404 Offset result = new Offset(_tail); |
| 405 int tail = _tail; |
| 406 _setUint32AtTail(_buf, tail, values.length); |
| 407 tail -= 4; |
| 408 for (int value in values) { |
| 409 _setInt32AtTail(_buf, tail, value); |
| 410 tail -= 4; |
| 411 } |
| 412 return result; |
| 413 } |
| 414 |
| 415 /** |
| 416 * Write the given list of unsigned 32-bit integer [values]. |
| 417 */ |
| 418 Offset writeListUint32(List<int> values) { |
| 419 _ensureNoVTable(); |
| 420 _prepare(4, 1 + values.length); |
| 421 Offset result = new Offset(_tail); |
| 422 int tail = _tail; |
| 423 _setUint32AtTail(_buf, tail, values.length); |
| 424 tail -= 4; |
| 425 for (int value in values) { |
| 426 _setUint32AtTail(_buf, tail, value); |
| 427 tail -= 4; |
| 428 } |
| 429 return result; |
| 430 } |
| 431 |
| 432 /** |
| 433 * Write the given list of unsigned 8-bit integer [values]. |
| 434 */ |
| 435 Offset writeListUint8(List<int> values) { |
| 436 _ensureNoVTable(); |
| 437 _prepare(4, 1, additionalBytes: values.length); |
| 438 Offset result = new Offset(_tail); |
| 439 int tail = _tail; |
| 440 _setUint32AtTail(_buf, tail, values.length); |
| 441 tail -= 4; |
| 442 for (int value in values) { |
| 443 _setUint8AtTail(_buf, tail, value); |
| 444 tail -= 1; |
| 445 } |
| 446 return result; |
| 447 } |
| 448 |
| 449 /** |
| 450 * Write the given string [value] and return its [Offset], or `null` if |
| 451 * the [value] is equal to [def]. |
| 452 */ |
| 453 Offset<String> writeString(String value, [String def]) { |
| 454 _ensureNoVTable(); |
| 455 if (value != def) { |
| 456 return _strings.putIfAbsent(value, () { |
| 457 // TODO(scheglov) optimize for ASCII strings |
| 458 List<int> bytes = UTF8.encode(value); |
| 459 int length = bytes.length; |
| 460 _prepare(4, 1, additionalBytes: length); |
| 461 Offset<String> result = new Offset(_tail); |
| 462 _setUint32AtTail(_buf, _tail, length); |
| 463 int offset = _buf.lengthInBytes - _tail + 4; |
| 464 for (int i = 0; i < length; i++) { |
| 465 _buf.setUint8(offset++, bytes[i]); |
| 466 } |
| 467 return result; |
| 468 }); |
| 469 } |
| 470 return null; |
| 471 } |
| 472 |
| 473 /** |
| 474 * Throw an exception if there is not currently a vtable. |
| 475 */ |
| 476 void _ensureCurrentVTable() { |
| 477 if (_currentVTable == null) { |
| 478 throw new StateError('Start a table before adding values.'); |
| 479 } |
| 480 } |
| 481 |
| 482 /** |
| 483 * Throw an exception if there is currently a vtable. |
| 484 */ |
| 485 void _ensureNoVTable() { |
| 486 if (_currentVTable != null) { |
| 487 throw new StateError( |
| 488 'Cannot write a non-scalar value while writing a table.'); |
| 489 } |
| 490 } |
| 491 |
| 492 /** |
| 493 * Prepare for writing the given [count] of scalars of the given [size]. |
| 494 * Additionally allocate the specified [additionalBytes]. Update the current |
| 495 * tail pointer to point at the allocated space. |
| 496 */ |
| 497 void _prepare(int size, int count, {int additionalBytes: 0}) { |
| 498 // Update the alignment. |
| 499 if (_maxAlign < size) { |
| 500 _maxAlign = size; |
| 501 } |
| 502 // Prepare amount of required space. |
| 503 int dataSize = size * count + additionalBytes; |
| 504 int alignDelta = (-(_tail + dataSize)) % size; |
| 505 int bufSize = alignDelta + dataSize; |
| 506 // Ensure that we have the required amount of space. |
| 507 { |
| 508 int oldCapacity = _buf.lengthInBytes; |
| 509 if (_tail + bufSize > oldCapacity) { |
| 510 int desiredNewCapacity = (oldCapacity + bufSize) * 2; |
| 511 int deltaCapacity = desiredNewCapacity - oldCapacity; |
| 512 deltaCapacity += (-deltaCapacity) % _maxAlign; |
| 513 int newCapacity = oldCapacity + deltaCapacity; |
| 514 ByteData newBuf = new ByteData(newCapacity); |
| 515 newBuf.buffer |
| 516 .asUint8List() |
| 517 .setAll(deltaCapacity, _buf.buffer.asUint8List()); |
| 518 _buf = newBuf; |
| 519 } |
| 520 } |
| 521 // Update the tail pointer. |
| 522 _tail += bufSize; |
| 523 } |
| 524 |
| 525 /** |
| 526 * Record the offset of the given [field]. |
| 527 */ |
| 528 void _trackField(int field) { |
| 529 _currentVTable.addField(field, _tail); |
| 530 } |
| 531 |
| 532 static void _setFloat64AtTail(ByteData _buf, int tail, double x) { |
| 533 _buf.setFloat64(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); |
| 534 } |
| 535 |
| 536 static void _setInt32AtTail(ByteData _buf, int tail, int x) { |
| 537 _buf.setInt32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); |
| 538 } |
| 539 |
| 540 static void _setUint32AtTail(ByteData _buf, int tail, int x) { |
| 541 _buf.setUint32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); |
| 542 } |
| 543 |
| 544 static void _setUint8AtTail(ByteData _buf, int tail, int x) { |
| 545 _buf.setUint8(_buf.lengthInBytes - tail, x); |
| 546 } |
| 547 } |
| 548 |
| 549 /** |
| 550 * The reader of lists of 64-bit float values. |
| 551 * |
| 552 * The returned unmodifiable lists lazily read values on access. |
| 553 */ |
| 554 class Float64ListReader extends Reader<List<double>> { |
| 555 const Float64ListReader(); |
| 556 |
| 557 @override |
| 558 int get size => 4; |
| 559 |
| 560 @override |
| 561 List<double> read(BufferContext bc, int offset) => |
| 562 new _FbFloat64List(bc, bc.derefObject(offset)); |
| 563 } |
| 564 |
| 565 /** |
| 566 * The reader of signed 32-bit integers. |
| 567 */ |
| 568 class Int32Reader extends Reader<int> { |
| 569 const Int32Reader() : super(); |
| 570 |
| 571 @override |
| 572 int get size => 4; |
| 573 |
| 574 @override |
| 575 int read(BufferContext bc, int offset) => bc._getInt32(offset); |
| 576 } |
| 577 |
| 578 /** |
| 579 * The reader of 8-bit signed integers. |
| 580 */ |
| 581 class Int8Reader extends Reader<int> { |
| 582 const Int8Reader() : super(); |
| 583 |
| 584 @override |
| 585 int get size => 1; |
| 586 |
| 587 @override |
| 588 int read(BufferContext bc, int offset) => bc._getInt8(offset); |
| 589 } |
| 590 |
| 591 /** |
| 592 * The reader of lists of objects. |
| 593 * |
| 594 * The returned unmodifiable lists lazily read objects on access. |
| 595 */ |
| 596 class ListReader<E> extends Reader<List<E>> { |
| 597 final Reader<E> _elementReader; |
| 598 |
| 599 const ListReader(this._elementReader); |
| 600 |
| 601 @override |
| 602 int get size => 4; |
| 603 |
| 604 @override |
| 605 List<E> read(BufferContext bc, int offset) => |
| 606 new _FbGenericList<E>(_elementReader, bc, bc.derefObject(offset)); |
| 607 } |
| 608 |
| 609 /** |
| 610 * The offset from the end of the buffer to a serialized object of the type [T]. |
| 611 */ |
| 612 class Offset<T> { |
| 613 final int _tail; |
| 614 |
| 615 Offset(this._tail); |
| 616 } |
| 617 |
| 618 /** |
| 619 * Object that can read a value at a [BufferContext]. |
| 620 */ |
| 621 abstract class Reader<T> { |
| 622 const Reader(); |
| 623 |
| 624 /** |
| 625 * The size of the value in bytes. |
| 626 */ |
| 627 int get size; |
| 628 |
| 629 /** |
| 630 * Read the value at the given [offset] in [bc]. |
| 631 */ |
| 632 T read(BufferContext bc, int offset); |
| 633 |
| 634 /** |
| 635 * Read the value of the given [field] in the given [object]. |
| 636 */ |
| 637 T vTableGet(BufferContext object, int offset, int field, [T defaultValue]) { |
| 638 int vTableSOffset = object._getInt32(offset); |
| 639 int vTableOffset = offset - vTableSOffset; |
| 640 int vTableSize = object._getUint16(vTableOffset); |
| 641 int vTableFieldOffset = (1 + 1 + field) * 2; |
| 642 if (vTableFieldOffset < vTableSize) { |
| 643 int fieldOffsetInObject = |
| 644 object._getUint16(vTableOffset + vTableFieldOffset); |
| 645 if (fieldOffsetInObject != 0) { |
| 646 return read(object, offset + fieldOffsetInObject); |
| 647 } |
| 648 } |
| 649 return defaultValue; |
| 650 } |
| 651 } |
| 652 |
| 653 /** |
| 654 * The reader of string values. |
| 655 */ |
| 656 class StringReader extends Reader<String> { |
| 657 const StringReader() : super(); |
| 658 |
| 659 @override |
| 660 int get size => 4; |
| 661 |
| 662 @override |
| 663 String read(BufferContext bc, int offset) { |
| 664 int strOffset = bc.derefObject(offset); |
| 665 int length = bc._getUint32(strOffset); |
| 666 Uint8List bytes = bc._asUint8LIst(strOffset + 4, length); |
| 667 if (_isLatin(bytes)) { |
| 668 return new String.fromCharCodes(bytes); |
| 669 } |
| 670 return UTF8.decode(bytes); |
| 671 } |
| 672 |
| 673 static bool _isLatin(Uint8List bytes) { |
| 674 int length = bytes.length; |
| 675 for (int i = 0; i < length; i++) { |
| 676 if (bytes[i] > 127) { |
| 677 return false; |
| 678 } |
| 679 } |
| 680 return true; |
| 681 } |
| 682 } |
| 683 |
| 684 /** |
| 685 * An abstract reader for tables. |
| 686 */ |
| 687 abstract class TableReader<T> extends Reader<T> { |
| 688 const TableReader(); |
| 689 |
| 690 @override |
| 691 int get size => 4; |
| 692 |
| 693 /** |
| 694 * Return the object at [offset]. |
| 695 */ |
| 696 T createObject(BufferContext bc, int offset); |
| 697 |
| 698 @override |
| 699 T read(BufferContext bp, int offset) { |
| 700 int objectOffset = bp.derefObject(offset); |
| 701 return createObject(bp, objectOffset); |
| 702 } |
| 703 } |
| 704 |
| 705 /** |
| 706 * Reader of lists of unsigned 32-bit integer values. |
| 707 * |
| 708 * The returned unmodifiable lists lazily read values on access. |
| 709 */ |
| 710 class Uint32ListReader extends Reader<List<int>> { |
| 711 const Uint32ListReader(); |
| 712 |
| 713 @override |
| 714 int get size => 4; |
| 715 |
| 716 @override |
| 717 List<int> read(BufferContext bc, int offset) => |
| 718 new _FbUint32List(bc, bc.derefObject(offset)); |
| 719 } |
| 720 |
| 721 /** |
| 722 * The reader of unsigned 32-bit integers. |
| 723 */ |
| 724 class Uint32Reader extends Reader<int> { |
| 725 const Uint32Reader() : super(); |
| 726 |
| 727 @override |
| 728 int get size => 4; |
| 729 |
| 730 @override |
| 731 int read(BufferContext bc, int offset) => bc._getUint32(offset); |
| 732 } |
| 733 |
| 734 /** |
| 735 * Reader of lists of unsigned 8-bit integer values. |
| 736 * |
| 737 * The returned unmodifiable lists lazily read values on access. |
| 738 */ |
| 739 class Uint8ListReader extends Reader<List<int>> { |
| 740 const Uint8ListReader(); |
| 741 |
| 742 @override |
| 743 int get size => 4; |
| 744 |
| 745 @override |
| 746 List<int> read(BufferContext bc, int offset) => |
| 747 new _FbUint8List(bc, bc.derefObject(offset)); |
| 748 } |
| 749 |
| 750 /** |
| 751 * The reader of unsigned 8-bit integers. |
| 752 */ |
| 753 class Uint8Reader extends Reader<int> { |
| 754 const Uint8Reader() : super(); |
| 755 |
| 756 @override |
| 757 int get size => 1; |
| 758 |
| 759 @override |
| 760 int read(BufferContext bc, int offset) => bc._getUint8(offset); |
| 761 } |
| 762 |
| 763 /** |
| 764 * List of booleans backed by 8-bit unsigned integers. |
| 765 */ |
| 766 class _FbBoolList extends Object with ListMixin<bool> implements List<bool> { |
| 767 final BufferContext bc; |
| 768 final int offset; |
| 769 int _length; |
| 770 |
| 771 _FbBoolList(this.bc, this.offset); |
| 772 |
| 773 @override |
| 774 int get length { |
| 775 if (_length == null) { |
| 776 int byteLength = bc._getUint32(offset); |
| 777 _length = (byteLength - 1) * 8 - _getByte(byteLength - 1); |
| 778 } |
| 779 return _length; |
| 780 } |
| 781 |
| 782 @override |
| 783 void set length(int i) => |
| 784 throw new StateError('Attempt to modify immutable list'); |
| 785 |
| 786 @override |
| 787 bool operator [](int i) { |
| 788 int index = i ~/ 8; |
| 789 int mask = 1 << i % 8; |
| 790 return _getByte(index) & mask != 0; |
| 791 } |
| 792 |
| 793 @override |
| 794 void operator []=(int i, bool e) => |
| 795 throw new StateError('Attempt to modify immutable list'); |
| 796 |
| 797 int _getByte(int index) => bc._getUint8(offset + 4 + index); |
| 798 } |
| 799 |
| 800 /** |
| 801 * The list backed by 64-bit values - Uint64 length and Float64. |
| 802 */ |
| 803 class _FbFloat64List extends _FbList<double> { |
| 804 _FbFloat64List(BufferContext bc, int offset) : super(bc, offset); |
| 805 |
| 806 @override |
| 807 double operator [](int i) { |
| 808 return bc._getFloat64(offset + 8 + 8 * i); |
| 809 } |
| 810 } |
| 811 |
| 812 /** |
| 813 * List backed by a generic object which may have any size. |
| 814 */ |
| 815 class _FbGenericList<E> extends _FbList<E> { |
| 816 final Reader<E> elementReader; |
| 817 |
| 818 List<E> _items; |
| 819 |
| 820 _FbGenericList(this.elementReader, BufferContext bp, int offset) |
| 821 : super(bp, offset); |
| 822 |
| 823 @override |
| 824 E operator [](int i) { |
| 825 _items ??= new List<E>(length); |
| 826 E item = _items[i]; |
| 827 if (item == null) { |
| 828 item = elementReader.read(bc, offset + 4 + elementReader.size * i); |
| 829 _items[i] = item; |
| 830 } |
| 831 return item; |
| 832 } |
| 833 } |
| 834 |
| 835 /** |
| 836 * The base class for immutable lists read from flat buffers. |
| 837 */ |
| 838 abstract class _FbList<E> extends Object with ListMixin<E> implements List<E> { |
| 839 final BufferContext bc; |
| 840 final int offset; |
| 841 int _length; |
| 842 |
| 843 _FbList(this.bc, this.offset); |
| 844 |
| 845 @override |
| 846 int get length { |
| 847 _length ??= bc._getUint32(offset); |
| 848 return _length; |
| 849 } |
| 850 |
| 851 @override |
| 852 void set length(int i) => |
| 853 throw new StateError('Attempt to modify immutable list'); |
| 854 |
| 855 @override |
| 856 void operator []=(int i, E e) => |
| 857 throw new StateError('Attempt to modify immutable list'); |
| 858 } |
| 859 |
| 860 /** |
| 861 * List backed by 32-bit unsigned integers. |
| 862 */ |
| 863 class _FbUint32List extends _FbList<int> { |
| 864 _FbUint32List(BufferContext bc, int offset) : super(bc, offset); |
| 865 |
| 866 @override |
| 867 int operator [](int i) { |
| 868 return bc._getUint32(offset + 4 + 4 * i); |
| 869 } |
| 870 } |
| 871 |
| 872 /** |
| 873 * List backed by 8-bit unsigned integers. |
| 874 */ |
| 875 class _FbUint8List extends _FbList<int> { |
| 876 _FbUint8List(BufferContext bc, int offset) : super(bc, offset); |
| 877 |
| 878 @override |
| 879 int operator [](int i) { |
| 880 return bc._getUint8(offset + 4 + i); |
| 881 } |
| 882 } |
| 883 |
| 884 /** |
| 885 * Class that describes the structure of a table. |
| 886 */ |
| 887 class _VTable { |
| 888 final List<int> fieldTails = <int>[]; |
| 889 final List<int> fieldOffsets = <int>[]; |
| 890 |
| 891 /** |
| 892 * The size of the table that uses this VTable. |
| 893 */ |
| 894 int tableSize; |
| 895 |
| 896 /** |
| 897 * The tail of this VTable. It is used to share the same VTable between |
| 898 * multiple tables of identical structure. |
| 899 */ |
| 900 int tail; |
| 901 |
| 902 int get numOfUint16 => 1 + 1 + fieldTails.length; |
| 903 |
| 904 void addField(int field, int offset) { |
| 905 while (fieldTails.length <= field) { |
| 906 fieldTails.add(null); |
| 907 } |
| 908 fieldTails[field] = offset; |
| 909 } |
| 910 |
| 911 /** |
| 912 * Return `true` if the [existing] VTable can be used instead of this. |
| 913 */ |
| 914 bool canUseExistingVTable(_VTable existing) { |
| 915 assert(tail == null); |
| 916 assert(existing.tail != null); |
| 917 if (tableSize == existing.tableSize && |
| 918 fieldOffsets.length == existing.fieldOffsets.length) { |
| 919 for (int i = 0; i < fieldOffsets.length; i++) { |
| 920 if (fieldOffsets[i] != existing.fieldOffsets[i]) { |
| 921 return false; |
| 922 } |
| 923 } |
| 924 return true; |
| 925 } |
| 926 return false; |
| 927 } |
| 928 |
| 929 /** |
| 930 * Fill the [fieldOffsets] field. |
| 931 */ |
| 932 void computeFieldOffsets(int tableTail) { |
| 933 assert(fieldOffsets.isEmpty); |
| 934 for (int fieldTail in fieldTails) { |
| 935 int fieldOffset = fieldTail == null ? 0 : tableTail - fieldTail; |
| 936 fieldOffsets.add(fieldOffset); |
| 937 } |
| 938 } |
| 939 |
| 940 /** |
| 941 * Outputs this VTable to [buf], which is is expected to be aligned to 16-bit |
| 942 * and have at least [numOfUint16] 16-bit words available. |
| 943 */ |
| 944 void output(ByteData buf, int bufOffset) { |
| 945 // VTable size. |
| 946 buf.setUint16(bufOffset, numOfUint16 * 2, Endianness.LITTLE_ENDIAN); |
| 947 bufOffset += 2; |
| 948 // Table size. |
| 949 buf.setUint16(bufOffset, tableSize, Endianness.LITTLE_ENDIAN); |
| 950 bufOffset += 2; |
| 951 // Field offsets. |
| 952 for (int fieldOffset in fieldOffsets) { |
| 953 buf.setUint16(bufOffset, fieldOffset, Endianness.LITTLE_ENDIAN); |
| 954 bufOffset += 2; |
| 955 } |
| 956 } |
| 957 } |
| OLD | NEW |