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 = _getInt32(); | |
|
Paul Berry
2016/01/07 22:44:54
Should be _getUint32().
scheglov
2016/01/07 23:21:08
Done.
| |
| 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 int _maxAlign; | |
|
Paul Berry
2016/01/07 22:44:53
To help future maintainers, let's document what _m
scheglov
2016/01/07 23:21:08
Done.
| |
| 70 int _tail; | |
| 71 | |
| 72 int _currentTableEndTail; | |
| 73 _VTableBuilder _currentVTableBuilder; | |
| 74 | |
| 75 Builder({this.initialSize: 1024}) { | |
| 76 reset(); | |
| 77 } | |
| 78 | |
| 79 /** | |
| 80 * Add the [field] with the given 32-bit signed integer [value]. The field is | |
| 81 * not added if the [value] is equal to [def]. | |
| 82 */ | |
| 83 void addInt32(int field, int value, [int def]) { | |
| 84 if (_currentVTableBuilder == null) { | |
| 85 throw new StateError('Start a table before adding values.'); | |
| 86 } | |
| 87 if (value != def) { | |
| 88 int size = 4; | |
| 89 _prepare(size, 1); | |
| 90 _trackField(field); | |
| 91 _setInt32AtTail(_buf, _tail, value); | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 /** | |
| 96 * Add the [field] with the given 8-bit signed integer [value]. The field is | |
| 97 * not added if the [value] is equal to [def]. | |
| 98 */ | |
| 99 void addInt8(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 = 1; | |
| 105 _prepare(size, 1); | |
| 106 _trackField(field); | |
| 107 _buf.setInt8(_buf.lengthInBytes - _tail, value); | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 /** | |
| 112 * Add the [field] referencing an object with the given [offset]. | |
| 113 */ | |
| 114 void addOffset(int field, Offset offset) { | |
| 115 if (_currentVTableBuilder == null) { | |
| 116 throw new StateError('Start a table before adding values.'); | |
| 117 } | |
| 118 if (offset != null) { | |
| 119 _prepare(4, 1); | |
| 120 _trackField(field); | |
| 121 _setUint32AtTail(_buf, _tail, _tail - offset._tail); | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 /** | |
| 126 * End the current table and return its offset. | |
| 127 */ | |
| 128 Offset endTable() { | |
| 129 if (_currentVTableBuilder == null) { | |
| 130 throw new StateError('Start a table before ending it.'); | |
| 131 } | |
| 132 // Prepare the size of the current table. | |
| 133 int tableSize = _tail - _currentTableEndTail; | |
| 134 // Prepare for writing the VTable. | |
| 135 _prepare(4, 1); | |
| 136 int tableTail = _tail; | |
| 137 // Write the VTable. | |
| 138 // TODO(scheglov) implement VTable(s) sharing | |
| 139 _prepare(2, _currentVTableBuilder.numOfUint16); | |
| 140 _currentVTableBuilder.output( | |
| 141 _buf, _buf.lengthInBytes - _tail, tableTail, tableSize); | |
| 142 // Set the VTable offset. | |
| 143 _setInt32AtTail(_buf, tableTail, _tail - tableTail); | |
| 144 // Done with this table. | |
| 145 _currentVTableBuilder = null; | |
| 146 return new Offset(tableTail); | |
| 147 } | |
| 148 | |
| 149 /** | |
| 150 * Finish off the creation of the buffer. The given [offset] is used as the | |
| 151 * root object offset, and usually references directly or indirectly every | |
| 152 * written object. | |
| 153 */ | |
| 154 Uint8List finish(Offset offset) { | |
| 155 _prepare(4, 1); | |
| 156 _setUint32AtTail(_buf, _tail, _tail - offset._tail); | |
| 157 return _buf.buffer.asUint8List(_buf.lengthInBytes - _tail); | |
| 158 } | |
| 159 | |
| 160 /** | |
| 161 * Reset the builder and make it ready for filling a new buffer. | |
| 162 */ | |
| 163 void reset() { | |
| 164 _buf = new ByteData(initialSize); | |
| 165 _maxAlign = 1; | |
| 166 _tail = 0; | |
| 167 _currentVTableBuilder = null; | |
| 168 } | |
| 169 | |
| 170 /** | |
| 171 * Start a new table. Must be finished with [endTable] invocation. | |
| 172 */ | |
| 173 void startTable() { | |
| 174 if (_currentVTableBuilder != null) { | |
| 175 throw new StateError('Inline tables are not supported.'); | |
| 176 } | |
| 177 _currentVTableBuilder = new _VTableBuilder(); | |
| 178 _currentTableEndTail = _tail; | |
| 179 } | |
| 180 | |
| 181 /** | |
| 182 * Write the given list of [values]. | |
| 183 */ | |
| 184 Offset writeList(List<Offset> values) { | |
| 185 if (_currentVTableBuilder != null) { | |
| 186 throw new StateError( | |
| 187 'Cannot write a non-scalar value while writing a table.'); | |
| 188 } | |
| 189 _prepare(4, 1 + values.length); | |
| 190 Offset result = new Offset(_tail); | |
| 191 int tail = _tail; | |
| 192 _setUint32AtTail(_buf, tail, values.length); | |
| 193 tail -= 4; | |
| 194 for (Offset value in values) { | |
| 195 _setUint32AtTail(_buf, tail, tail - value._tail); | |
| 196 tail -= 4; | |
| 197 } | |
| 198 return result; | |
| 199 } | |
| 200 | |
| 201 /** | |
| 202 * Write the given string [value] and return its [Offset], or `null` if | |
| 203 * the [value] is equal to [def]. | |
| 204 */ | |
| 205 Offset<String> writeString(String value, [String def]) { | |
| 206 if (_currentVTableBuilder != null) { | |
| 207 throw new StateError( | |
| 208 'Cannot write a non-scalar value while writing a table.'); | |
| 209 } | |
| 210 if (value != def) { | |
| 211 List<int> bytes = UTF8.encode(value); | |
| 212 int length = bytes.length; | |
| 213 _prepare(4, 1, additionalBytes: length); | |
| 214 Offset<String> result = new Offset(_tail); | |
| 215 _setUint32AtTail(_buf, _tail, length); | |
| 216 int offset = _buf.lengthInBytes - _tail + 4; | |
| 217 for (int i = 0; i < length; i++) { | |
| 218 _buf.setInt8(offset++, bytes[i]); | |
| 219 } | |
| 220 return result; | |
| 221 } | |
| 222 return null; | |
| 223 } | |
| 224 | |
| 225 /** | |
| 226 * Prepare for writing the given [count] of scalars of the given [size]. | |
| 227 * Additionally allocate the specified [additionalBytes]. Update the current | |
| 228 * tail pointer to point at the allocated space. | |
| 229 */ | |
| 230 void _prepare(int size, int count, {int additionalBytes: 0}) { | |
| 231 // Update the alignment. | |
| 232 if (_maxAlign < size) { | |
| 233 _maxAlign = size; | |
| 234 } | |
| 235 // Prepare amount of required space. | |
| 236 int dataSize = size * count + additionalBytes; | |
| 237 int alignDelta = (-(_tail + dataSize)) % size; | |
| 238 int bufSize = alignDelta + dataSize; | |
| 239 // Ensure that we have the required amount of space. | |
| 240 { | |
| 241 int oldCapacity = _buf.lengthInBytes; | |
| 242 if (_tail + bufSize > oldCapacity) { | |
| 243 int newCapacity = (oldCapacity + bufSize) * 2; | |
| 244 newCapacity += (-newCapacity) % _maxAlign; | |
|
Paul Berry
2016/01/07 22:44:53
There's a bug here. The important thing is *not*
scheglov
2016/01/07 23:21:08
Done.
Thank you for catching this!
| |
| 245 ByteData newBuf = new ByteData(newCapacity); | |
| 246 int deltaCapacity = newCapacity - oldCapacity; | |
| 247 newBuf.buffer | |
| 248 .asUint8List() | |
| 249 .setAll(deltaCapacity, _buf.buffer.asUint8List()); | |
| 250 _buf = newBuf; | |
| 251 } | |
| 252 } | |
| 253 // Update the tail pointer. | |
| 254 _tail += bufSize; | |
| 255 } | |
| 256 | |
| 257 /** | |
| 258 * Record the offset of the given [field]. | |
| 259 */ | |
| 260 void _trackField(int field) { | |
| 261 _currentVTableBuilder.addField(field, _tail); | |
| 262 } | |
| 263 | |
| 264 static void _setInt32AtTail(ByteData _buf, int tail, int x) { | |
| 265 _buf.setInt32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); | |
| 266 } | |
| 267 | |
| 268 static void _setUint32AtTail(ByteData _buf, int tail, int x) { | |
| 269 _buf.setUint32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); | |
| 270 } | |
| 271 } | |
| 272 | |
| 273 /** | |
| 274 * The reader of 32-bit signed integers. | |
| 275 */ | |
| 276 class Int32Reader extends Reader<int> { | |
| 277 const Int32Reader() : super(); | |
| 278 | |
| 279 @override | |
| 280 int get size => 2; | |
| 281 | |
| 282 @override | |
| 283 int read(BufferPointer bp) => bp._getInt32(); | |
| 284 } | |
| 285 | |
| 286 /** | |
| 287 * The reader of 8-bit signed integers. | |
| 288 */ | |
| 289 class Int8Reader extends Reader<int> { | |
| 290 const Int8Reader() : super(); | |
| 291 | |
| 292 @override | |
| 293 int get size => 1; | |
| 294 | |
| 295 @override | |
| 296 int read(BufferPointer bp) => bp._getInt8(); | |
| 297 } | |
| 298 | |
| 299 /** | |
| 300 * The reader of object. | |
| 301 * | |
| 302 * The returned unmodifiable lists lazily read objects on access. | |
| 303 */ | |
| 304 class ListReader<E> extends Reader<List<E>> { | |
| 305 final Reader<E> _elementReader; | |
| 306 | |
| 307 const ListReader(this._elementReader); | |
| 308 | |
| 309 @override | |
| 310 int get size => 4; | |
| 311 | |
| 312 @override | |
| 313 List<E> read(BufferPointer bp) => | |
| 314 new _FbList<E>(_elementReader, bp.derefObject()); | |
| 315 } | |
| 316 | |
| 317 /** | |
| 318 * The offset from the end of the buffer to a serialized object of the type [T]. | |
| 319 */ | |
| 320 class Offset<T> { | |
| 321 final int _tail; | |
| 322 | |
| 323 Offset(this._tail); | |
| 324 } | |
| 325 | |
| 326 /** | |
| 327 * Object that can read a value at a [BufferPointer]. | |
| 328 */ | |
| 329 abstract class Reader<T> { | |
| 330 const Reader(); | |
| 331 | |
| 332 /** | |
| 333 * The size of the value in bytes. | |
| 334 */ | |
| 335 int get size; | |
| 336 | |
| 337 /** | |
| 338 * Read the value at the given pointer. | |
| 339 */ | |
| 340 T read(BufferPointer bp); | |
| 341 | |
| 342 /** | |
| 343 * Read the value of the given [field] in the given [object]. | |
| 344 */ | |
| 345 T vTableGet(BufferPointer object, int field, [T defaultValue]) { | |
| 346 int vTableSOffset = object._getInt32(); | |
| 347 BufferPointer vTable = object._advance(-vTableSOffset); | |
| 348 int vTableSize = vTable._getUint16(); | |
| 349 int vTableFieldOffset = (1 + 1 + field) * 2; | |
| 350 if (vTableFieldOffset < vTableSize) { | |
| 351 int fieldOffsetInObject = vTable._getUint16(vTableFieldOffset); | |
| 352 if (fieldOffsetInObject != 0) { | |
| 353 BufferPointer fieldPointer = object._advance(fieldOffsetInObject); | |
| 354 return read(fieldPointer); | |
| 355 } | |
| 356 } | |
| 357 return defaultValue; | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 /** | |
| 362 * The reader of string values. | |
| 363 */ | |
| 364 class StringReader extends Reader<String> { | |
| 365 const StringReader() : super(); | |
| 366 | |
| 367 @override | |
| 368 int get size => 4; | |
| 369 | |
| 370 @override | |
| 371 String read(BufferPointer ref) { | |
| 372 BufferPointer object = ref.derefObject(); | |
| 373 int length = object._getUint32(); | |
| 374 return UTF8 | |
| 375 .decode(ref._buffer.buffer.asUint8List(object._offset + 4, length)); | |
| 376 } | |
| 377 } | |
| 378 | |
| 379 /** | |
| 380 * An abstract reader for tables. | |
| 381 */ | |
| 382 abstract class TableReader<T extends TableReader<T>> extends Reader<T> { | |
| 383 const TableReader(); | |
| 384 | |
| 385 @override | |
| 386 int get size => 4; | |
| 387 | |
| 388 /** | |
| 389 * Return the [Reader] for reading fields of the object at [bp]. | |
| 390 */ | |
| 391 T createReader(BufferPointer bp); | |
| 392 | |
| 393 @override | |
| 394 T read(BufferPointer bp) { | |
| 395 bp = bp.derefObject(); | |
| 396 return createReader(bp); | |
| 397 } | |
| 398 } | |
| 399 | |
| 400 class _FbList<E> extends Object with ListMixin<E> implements List<E> { | |
| 401 final Reader<E> elementReader; | |
| 402 final BufferPointer bp; | |
| 403 | |
| 404 _FbList(this.elementReader, this.bp); | |
| 405 | |
| 406 @override | |
| 407 int get length => bp._getUint32(); | |
| 408 | |
| 409 @override | |
| 410 void set length(int i) => | |
| 411 throw new StateError('Attempt to modify immutable list'); | |
| 412 | |
| 413 @override | |
| 414 E operator [](int i) { | |
| 415 BufferPointer ref = bp._advance(4 + elementReader.size * i); | |
| 416 return elementReader.read(ref); | |
| 417 } | |
| 418 | |
| 419 @override | |
| 420 void operator []=(int i, E e) => | |
| 421 throw new StateError('Attempt to modify immutable list'); | |
| 422 } | |
| 423 | |
| 424 /** | |
| 425 * Class for building VTable(s). | |
| 426 */ | |
| 427 class _VTableBuilder { | |
| 428 final List<int> fieldTails = <int>[]; | |
| 429 | |
| 430 int get numOfUint16 => 1 + 1 + fieldTails.length; | |
| 431 | |
| 432 void addField(int field, int offset) { | |
| 433 while (fieldTails.length <= field) { | |
| 434 fieldTails.add(null); | |
| 435 } | |
| 436 fieldTails[field] = offset; | |
| 437 } | |
| 438 | |
| 439 /** | |
| 440 * Outputs this VTable to [buf], which is is expected to be aligned to 16-bit | |
| 441 * and have at least [numOfUint16] 16-bit words available. | |
| 442 */ | |
| 443 void output(ByteData buf, int bufOffset, int tableTail, int tableSize) { | |
| 444 // VTable size. | |
| 445 buf.setUint16(bufOffset, numOfUint16 * 2, Endianness.LITTLE_ENDIAN); | |
| 446 bufOffset += 2; | |
| 447 // Table size. | |
| 448 buf.setUint16(bufOffset, tableSize, Endianness.LITTLE_ENDIAN); | |
| 449 bufOffset += 2; | |
| 450 // Field offsets. | |
| 451 for (int fieldTail in fieldTails) { | |
| 452 int fieldOffset = fieldTail == null ? 0 : tableTail - fieldTail; | |
| 453 buf.setUint16(bufOffset, fieldOffset, Endianness.LITTLE_ENDIAN); | |
| 454 bufOffset += 2; | |
| 455 } | |
| 456 } | |
| 457 } | |
| OLD | NEW |