| 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 |