Chromium Code Reviews| 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:typed_data'; | |
| 10 | |
| 11 /** | |
| 12 * A pointer to some data. | |
| 13 */ | |
| 14 class BufferPointer { | |
| 15 final ByteData _buffer; | |
| 16 final int _offset; | |
| 17 | |
| 18 factory BufferPointer.fromBytes(List<int> byteList, [int offset = 0]) { | |
| 19 Uint8List uint8List = _asUint8List(byteList); | |
| 20 ByteData buf = new ByteData.view(uint8List.buffer); | |
| 21 return new BufferPointer._(buf, uint8List.offsetInBytes + offset); | |
| 22 } | |
| 23 | |
| 24 BufferPointer._(this._buffer, this._offset); | |
| 25 | |
| 26 BufferPointer derefObject() { | |
| 27 int uOffset = _getUint32(); | |
| 28 return _advance(uOffset); | |
| 29 } | |
| 30 | |
| 31 @override | |
| 32 String toString() => _offset.toString(); | |
| 33 | |
| 34 BufferPointer _advance(int delta) { | |
| 35 return new BufferPointer._(_buffer, _offset + delta); | |
| 36 } | |
| 37 | |
| 38 int _getInt32([int delta = 0]) => | |
| 39 _buffer.getInt32(_offset + delta, Endianness.LITTLE_ENDIAN); | |
| 40 | |
| 41 int _getInt8([int delta = 0]) => _buffer.getInt8(_offset + delta); | |
| 42 | |
| 43 int _getUint16([int delta = 0]) => | |
| 44 _buffer.getUint16(_offset + delta, Endianness.LITTLE_ENDIAN); | |
| 45 | |
| 46 int _getUint32([int delta = 0]) => | |
| 47 _buffer.getUint32(_offset + delta, Endianness.LITTLE_ENDIAN); | |
| 48 | |
| 49 /** | |
| 50 * If the [byteList] is already a [Uint8List] return it. | |
| 51 * Otherwise return a [Uint8List] copy of the [byteList]. | |
| 52 */ | |
| 53 static Uint8List _asUint8List(List<int> byteList) { | |
| 54 if (byteList is Uint8List) { | |
| 55 return byteList; | |
| 56 } else { | |
| 57 return new Uint8List.fromList(byteList); | |
| 58 } | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 /** | |
| 63 * Class that helps building flat buffers. | |
| 64 */ | |
| 65 class Builder { | |
| 66 final int initialSize; | |
| 67 | |
| 68 ByteData _buf; | |
| 69 | |
| 70 /** | |
| 71 * The maximum alignment that has been seen so far. If [_buf] has to be | |
| 72 * reallocated in the future (to insert room at its start for more bytes) the | |
| 73 * reallocation will need to be a multiple of this many bytes. | |
| 74 */ | |
| 75 int _maxAlign; | |
| 76 | |
| 77 /** | |
| 78 * The number of bytes that have been written to the buffer so far. The | |
| 79 * most recently written byte is this many bytes from the end of [_buf]. | |
| 80 */ | |
| 81 int _tail; | |
| 82 | |
| 83 /** | |
| 84 * The location of the end of the current table, measured in bytes from the | |
| 85 * end of [_buf], or `null` if a table is not currently being built. | |
| 86 */ | |
| 87 int _currentTableEndTail; | |
| 88 | |
| 89 _VTableBuilder _currentVTableBuilder; | |
| 90 | |
| 91 Builder({this.initialSize: 1024}) { | |
| 92 reset(); | |
| 93 } | |
| 94 | |
| 95 /** | |
| 96 * Add the [field] with the given 32-bit signed integer [value]. The field is | |
| 97 * not added if the [value] is equal to [def]. | |
| 98 */ | |
| 99 void addInt32(int field, int value, [int def]) { | |
| 100 if (_currentVTableBuilder == null) { | |
| 101 throw new StateError('Start a table before adding values.'); | |
| 102 } | |
| 103 if (value != def) { | |
| 104 int size = 4; | |
| 105 _prepare(size, 1); | |
| 106 _trackField(field); | |
| 107 _setInt32AtTail(_buf, _tail, value); | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 /** | |
| 112 * Add the [field] with the given 8-bit signed integer [value]. The field is | |
| 113 * not added if the [value] is equal to [def]. | |
| 114 */ | |
| 115 void addInt8(int field, int value, [int def]) { | |
| 116 if (_currentVTableBuilder == null) { | |
| 117 throw new StateError('Start a table before adding values.'); | |
| 118 } | |
| 119 if (value != def) { | |
| 120 int size = 1; | |
| 121 _prepare(size, 1); | |
| 122 _trackField(field); | |
| 123 _buf.setInt8(_buf.lengthInBytes - _tail, value); | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 /** | |
| 128 * Add the [field] referencing an object with the given [offset]. | |
| 129 */ | |
| 130 void addOffset(int field, Offset offset) { | |
| 131 if (_currentVTableBuilder == null) { | |
| 132 throw new StateError('Start a table before adding values.'); | |
| 133 } | |
| 134 if (offset != null) { | |
| 135 _prepare(4, 1); | |
| 136 _trackField(field); | |
| 137 _setUint32AtTail(_buf, _tail, _tail - offset._tail); | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 /** | |
| 142 * End the current table and return its offset. | |
| 143 */ | |
| 144 Offset endTable() { | |
| 145 if (_currentVTableBuilder == null) { | |
| 146 throw new StateError('Start a table before ending it.'); | |
| 147 } | |
| 148 // Prepare the size of the current table. | |
| 149 int tableSize = _tail - _currentTableEndTail; | |
| 150 // Prepare for writing the VTable. | |
| 151 _prepare(4, 1); | |
| 152 int tableTail = _tail; | |
| 153 // Write the VTable. | |
| 154 // TODO(scheglov) implement VTable(s) sharing | |
| 155 _prepare(2, _currentVTableBuilder.numOfUint16); | |
| 156 _currentVTableBuilder.output( | |
| 157 _buf, _buf.lengthInBytes - _tail, tableTail, tableSize); | |
| 158 // Set the VTable offset. | |
| 159 _setInt32AtTail(_buf, tableTail, _tail - tableTail); | |
| 160 // Done with this table. | |
| 161 _currentVTableBuilder = null; | |
| 162 return new Offset(tableTail); | |
| 163 } | |
| 164 | |
| 165 /** | |
| 166 * Finish off the creation of the buffer. The given [offset] is used as the | |
| 167 * root object offset, and usually references directly or indirectly every | |
| 168 * written object. | |
| 169 */ | |
| 170 Uint8List finish(Offset offset) { | |
| 171 _prepare(4, 1); | |
| 172 _setUint32AtTail(_buf, _tail, _tail - offset._tail); | |
| 173 int alignedTail = _tail + ((-_tail) % _maxAlign); | |
|
Paul Berry
2016/01/07 23:50:28
I think this isn't going to do what you want. Con
scheglov
2016/01/08 03:58:59
Ah... Right.
At the moment we don't cannot write I
| |
| 174 return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail); | |
| 175 } | |
| 176 | |
| 177 /** | |
| 178 * This is a low-level method, it should not be invoked by clients. | |
| 179 */ | |
| 180 Uint8List lowFinish() { | |
| 181 int alignedTail = _tail + ((-_tail) % _maxAlign); | |
| 182 return _buf.buffer.asUint8List(_buf.lengthInBytes - alignedTail); | |
| 183 } | |
| 184 | |
| 185 /** | |
| 186 * This is a low-level method, it should not be invoked by clients. | |
| 187 */ | |
| 188 void lowReset() { | |
| 189 _buf = new ByteData(initialSize); | |
| 190 _maxAlign = 1; | |
| 191 _tail = 0; | |
| 192 } | |
| 193 | |
| 194 /** | |
| 195 * This is a low-level method, it should not be invoked by clients. | |
| 196 */ | |
| 197 void lowWriteUint32(int value) { | |
| 198 _prepare(4, 1); | |
| 199 _setUint32AtTail(_buf, _tail, value); | |
| 200 } | |
| 201 | |
| 202 /** | |
| 203 * This is a low-level method, it should not be invoked by clients. | |
| 204 */ | |
| 205 void lowWriteUint8(int value) { | |
| 206 _prepare(1, 1); | |
| 207 _buf.setUint8(_buf.lengthInBytes - _tail, value); | |
| 208 } | |
| 209 | |
| 210 /** | |
| 211 * Reset the builder and make it ready for filling a new buffer. | |
| 212 */ | |
| 213 void reset() { | |
| 214 _buf = new ByteData(initialSize); | |
| 215 _maxAlign = 1; | |
| 216 _tail = 0; | |
| 217 _currentVTableBuilder = null; | |
| 218 } | |
| 219 | |
| 220 /** | |
| 221 * Start a new table. Must be finished with [endTable] invocation. | |
| 222 */ | |
| 223 void startTable() { | |
| 224 if (_currentVTableBuilder != null) { | |
| 225 throw new StateError('Inline tables are not supported.'); | |
| 226 } | |
| 227 _currentVTableBuilder = new _VTableBuilder(); | |
| 228 _currentTableEndTail = _tail; | |
| 229 } | |
| 230 | |
| 231 /** | |
| 232 * Write the given list of [values]. | |
| 233 */ | |
| 234 Offset writeList(List<Offset> values) { | |
| 235 if (_currentVTableBuilder != null) { | |
| 236 throw new StateError( | |
| 237 'Cannot write a non-scalar value while writing a table.'); | |
| 238 } | |
| 239 _prepare(4, 1 + values.length); | |
| 240 Offset result = new Offset(_tail); | |
| 241 int tail = _tail; | |
| 242 _setUint32AtTail(_buf, tail, values.length); | |
| 243 tail -= 4; | |
| 244 for (Offset value in values) { | |
| 245 _setUint32AtTail(_buf, tail, tail - value._tail); | |
| 246 tail -= 4; | |
| 247 } | |
| 248 return result; | |
| 249 } | |
| 250 | |
| 251 /** | |
| 252 * Write the given string [value] and return its [Offset], or `null` if | |
| 253 * the [value] is equal to [def]. | |
| 254 */ | |
| 255 Offset<String> writeString(String value, [String def]) { | |
| 256 if (_currentVTableBuilder != null) { | |
| 257 throw new StateError( | |
| 258 'Cannot write a non-scalar value while writing a table.'); | |
| 259 } | |
| 260 if (value != def) { | |
| 261 // TODO(scheglov) optimize for ASCII strings | |
| 262 List<int> bytes = UTF8.encode(value); | |
| 263 int length = bytes.length; | |
| 264 _prepare(4, 1, additionalBytes: length); | |
| 265 Offset<String> result = new Offset(_tail); | |
| 266 _setUint32AtTail(_buf, _tail, length); | |
| 267 int offset = _buf.lengthInBytes - _tail + 4; | |
| 268 for (int i = 0; i < length; i++) { | |
| 269 _buf.setUint8(offset++, bytes[i]); | |
| 270 } | |
| 271 return result; | |
| 272 } | |
| 273 return null; | |
| 274 } | |
| 275 | |
| 276 /** | |
| 277 * Prepare for writing the given [count] of scalars of the given [size]. | |
| 278 * Additionally allocate the specified [additionalBytes]. Update the current | |
| 279 * tail pointer to point at the allocated space. | |
| 280 */ | |
| 281 void _prepare(int size, int count, {int additionalBytes: 0}) { | |
| 282 // Update the alignment. | |
| 283 if (_maxAlign < size) { | |
| 284 _maxAlign = size; | |
| 285 } | |
| 286 // Prepare amount of required space. | |
| 287 int dataSize = size * count + additionalBytes; | |
| 288 int alignDelta = (-(_tail + dataSize)) % size; | |
| 289 int bufSize = alignDelta + dataSize; | |
| 290 // Ensure that we have the required amount of space. | |
| 291 { | |
| 292 int oldCapacity = _buf.lengthInBytes; | |
| 293 if (_tail + bufSize > oldCapacity) { | |
| 294 int desiredNewCapacity = (oldCapacity + bufSize) * 2; | |
| 295 int deltaCapacity = desiredNewCapacity - oldCapacity; | |
| 296 deltaCapacity += (-deltaCapacity) % _maxAlign; | |
| 297 int newCapacity = oldCapacity + deltaCapacity; | |
| 298 ByteData newBuf = new ByteData(newCapacity); | |
| 299 newBuf.buffer | |
| 300 .asUint8List() | |
| 301 .setAll(deltaCapacity, _buf.buffer.asUint8List()); | |
| 302 _buf = newBuf; | |
| 303 } | |
| 304 } | |
| 305 // Update the tail pointer. | |
| 306 _tail += bufSize; | |
| 307 } | |
| 308 | |
| 309 /** | |
| 310 * Record the offset of the given [field]. | |
| 311 */ | |
| 312 void _trackField(int field) { | |
| 313 _currentVTableBuilder.addField(field, _tail); | |
| 314 } | |
| 315 | |
| 316 static void _setInt32AtTail(ByteData _buf, int tail, int x) { | |
| 317 _buf.setInt32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); | |
| 318 } | |
| 319 | |
| 320 static void _setUint32AtTail(ByteData _buf, int tail, int x) { | |
| 321 _buf.setUint32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 /** | |
| 326 * The reader of 32-bit signed integers. | |
| 327 */ | |
| 328 class Int32Reader extends Reader<int> { | |
| 329 const Int32Reader() : super(); | |
| 330 | |
| 331 @override | |
| 332 int get size => 2; | |
| 333 | |
| 334 @override | |
| 335 int read(BufferPointer bp) => bp._getInt32(); | |
| 336 } | |
| 337 | |
| 338 /** | |
| 339 * The reader of 8-bit signed integers. | |
| 340 */ | |
| 341 class Int8Reader extends Reader<int> { | |
| 342 const Int8Reader() : super(); | |
| 343 | |
| 344 @override | |
| 345 int get size => 1; | |
| 346 | |
| 347 @override | |
| 348 int read(BufferPointer bp) => bp._getInt8(); | |
| 349 } | |
| 350 | |
| 351 /** | |
| 352 * The reader of object. | |
| 353 * | |
| 354 * The returned unmodifiable lists lazily read objects on access. | |
| 355 */ | |
| 356 class ListReader<E> extends Reader<List<E>> { | |
| 357 final Reader<E> _elementReader; | |
| 358 | |
| 359 const ListReader(this._elementReader); | |
| 360 | |
| 361 @override | |
| 362 int get size => 4; | |
| 363 | |
| 364 @override | |
| 365 List<E> read(BufferPointer bp) => | |
| 366 new _FbList<E>(_elementReader, bp.derefObject()); | |
| 367 } | |
| 368 | |
| 369 /** | |
| 370 * The offset from the end of the buffer to a serialized object of the type [T]. | |
| 371 */ | |
| 372 class Offset<T> { | |
| 373 final int _tail; | |
| 374 | |
| 375 Offset(this._tail); | |
| 376 } | |
| 377 | |
| 378 /** | |
| 379 * Object that can read a value at a [BufferPointer]. | |
| 380 */ | |
| 381 abstract class Reader<T> { | |
| 382 const Reader(); | |
| 383 | |
| 384 /** | |
| 385 * The size of the value in bytes. | |
| 386 */ | |
| 387 int get size; | |
| 388 | |
| 389 /** | |
| 390 * Read the value at the given pointer. | |
| 391 */ | |
| 392 T read(BufferPointer bp); | |
| 393 | |
| 394 /** | |
| 395 * Read the value of the given [field] in the given [object]. | |
| 396 */ | |
| 397 T vTableGet(BufferPointer object, int field, [T defaultValue]) { | |
| 398 int vTableSOffset = object._getInt32(); | |
| 399 BufferPointer vTable = object._advance(-vTableSOffset); | |
| 400 int vTableSize = vTable._getUint16(); | |
| 401 int vTableFieldOffset = (1 + 1 + field) * 2; | |
| 402 if (vTableFieldOffset < vTableSize) { | |
| 403 int fieldOffsetInObject = vTable._getUint16(vTableFieldOffset); | |
| 404 if (fieldOffsetInObject != 0) { | |
| 405 BufferPointer fieldPointer = object._advance(fieldOffsetInObject); | |
| 406 return read(fieldPointer); | |
| 407 } | |
| 408 } | |
| 409 return defaultValue; | |
| 410 } | |
| 411 } | |
| 412 | |
| 413 /** | |
| 414 * The reader of string values. | |
| 415 */ | |
| 416 class StringReader extends Reader<String> { | |
| 417 const StringReader() : super(); | |
| 418 | |
| 419 @override | |
| 420 int get size => 4; | |
| 421 | |
| 422 @override | |
| 423 String read(BufferPointer ref) { | |
| 424 BufferPointer object = ref.derefObject(); | |
| 425 int length = object._getUint32(); | |
| 426 return UTF8 | |
| 427 .decode(ref._buffer.buffer.asUint8List(object._offset + 4, length)); | |
| 428 } | |
| 429 } | |
| 430 | |
| 431 /** | |
| 432 * An abstract reader for tables. | |
| 433 */ | |
| 434 abstract class TableReader<T extends TableReader<T>> extends Reader<T> { | |
| 435 const TableReader(); | |
| 436 | |
| 437 @override | |
| 438 int get size => 4; | |
| 439 | |
| 440 /** | |
| 441 * Return the [Reader] for reading fields of the object at [bp]. | |
| 442 */ | |
| 443 T createReader(BufferPointer bp); | |
| 444 | |
| 445 @override | |
| 446 T read(BufferPointer bp) { | |
| 447 bp = bp.derefObject(); | |
| 448 return createReader(bp); | |
| 449 } | |
| 450 } | |
| 451 | |
| 452 class _FbList<E> extends Object with ListMixin<E> implements List<E> { | |
| 453 final Reader<E> elementReader; | |
| 454 final BufferPointer bp; | |
| 455 | |
| 456 _FbList(this.elementReader, this.bp); | |
| 457 | |
| 458 @override | |
| 459 int get length => bp._getUint32(); | |
| 460 | |
| 461 @override | |
| 462 void set length(int i) => | |
| 463 throw new StateError('Attempt to modify immutable list'); | |
| 464 | |
| 465 @override | |
| 466 E operator [](int i) { | |
| 467 BufferPointer ref = bp._advance(4 + elementReader.size * i); | |
| 468 return elementReader.read(ref); | |
| 469 } | |
| 470 | |
| 471 @override | |
| 472 void operator []=(int i, E e) => | |
| 473 throw new StateError('Attempt to modify immutable list'); | |
| 474 } | |
| 475 | |
| 476 /** | |
| 477 * Class for building VTable(s). | |
| 478 */ | |
| 479 class _VTableBuilder { | |
| 480 final List<int> fieldTails = <int>[]; | |
| 481 | |
| 482 int get numOfUint16 => 1 + 1 + fieldTails.length; | |
| 483 | |
| 484 void addField(int field, int offset) { | |
| 485 while (fieldTails.length <= field) { | |
| 486 fieldTails.add(null); | |
| 487 } | |
| 488 fieldTails[field] = offset; | |
| 489 } | |
| 490 | |
| 491 /** | |
| 492 * Outputs this VTable to [buf], which is is expected to be aligned to 16-bit | |
| 493 * and have at least [numOfUint16] 16-bit words available. | |
| 494 */ | |
| 495 void output(ByteData buf, int bufOffset, int tableTail, int tableSize) { | |
| 496 // VTable size. | |
| 497 buf.setUint16(bufOffset, numOfUint16 * 2, Endianness.LITTLE_ENDIAN); | |
| 498 bufOffset += 2; | |
| 499 // Table size. | |
| 500 buf.setUint16(bufOffset, tableSize, Endianness.LITTLE_ENDIAN); | |
| 501 bufOffset += 2; | |
| 502 // Field offsets. | |
| 503 for (int fieldTail in fieldTails) { | |
| 504 int fieldOffset = fieldTail == null ? 0 : tableTail - fieldTail; | |
| 505 buf.setUint16(bufOffset, fieldOffset, Endianness.LITTLE_ENDIAN); | |
| 506 bufOffset += 2; | |
| 507 } | |
| 508 } | |
| 509 } | |
| OLD | NEW |