Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library analyzer.src.summary.flat_buffers; | 5 library analyzer.src.summary.flat_buffers; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 import 'dart:convert'; | 8 import 'dart:convert'; |
| 9 import 'dart:typed_data'; | 9 import 'dart:typed_data'; |
| 10 | 10 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 58 } | 58 } |
| 59 } | 59 } |
| 60 } | 60 } |
| 61 | 61 |
| 62 /** | 62 /** |
| 63 * Class that helps building flat buffers. | 63 * Class that helps building flat buffers. |
| 64 */ | 64 */ |
| 65 class Builder { | 65 class Builder { |
| 66 final int initialSize; | 66 final int initialSize; |
| 67 | 67 |
| 68 /** | |
| 69 * The list of existing VTable(s). | |
| 70 */ | |
| 71 final List<_VTable> _vTables = <_VTable>[]; | |
| 72 | |
| 68 ByteData _buf; | 73 ByteData _buf; |
| 69 | 74 |
| 70 /** | 75 /** |
| 71 * The maximum alignment that has been seen so far. If [_buf] has to be | 76 * 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 | 77 * 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. | 78 * reallocation will need to be a multiple of this many bytes. |
| 74 */ | 79 */ |
| 75 int _maxAlign; | 80 int _maxAlign; |
| 76 | 81 |
| 77 /** | 82 /** |
| 78 * The number of bytes that have been written to the buffer so far. The | 83 * 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]. | 84 * most recently written byte is this many bytes from the end of [_buf]. |
| 80 */ | 85 */ |
| 81 int _tail; | 86 int _tail; |
| 82 | 87 |
| 83 /** | 88 /** |
| 84 * The location of the end of the current table, measured in bytes from the | 89 * 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. | 90 * end of [_buf], or `null` if a table is not currently being built. |
| 86 */ | 91 */ |
| 87 int _currentTableEndTail; | 92 int _currentTableEndTail; |
| 88 | 93 |
| 89 _VTableBuilder _currentVTableBuilder; | 94 _VTable _currentVTable; |
| 90 | 95 |
| 91 Builder({this.initialSize: 1024}) { | 96 Builder({this.initialSize: 1024}) { |
| 92 reset(); | 97 reset(); |
| 93 } | 98 } |
| 94 | 99 |
| 95 /** | 100 /** |
| 96 * Add the [field] with the given 32-bit signed integer [value]. The field is | 101 * Add the [field] with the given 32-bit signed integer [value]. The field is |
| 97 * not added if the [value] is equal to [def]. | 102 * not added if the [value] is equal to [def]. |
| 98 */ | 103 */ |
| 99 void addInt32(int field, int value, [int def]) { | 104 void addInt32(int field, int value, [int def]) { |
| 100 if (_currentVTableBuilder == null) { | 105 if (_currentVTable == null) { |
| 101 throw new StateError('Start a table before adding values.'); | 106 throw new StateError('Start a table before adding values.'); |
| 102 } | 107 } |
| 103 if (value != def) { | 108 if (value != def) { |
| 104 int size = 4; | 109 int size = 4; |
| 105 _prepare(size, 1); | 110 _prepare(size, 1); |
| 106 _trackField(field); | 111 _trackField(field); |
| 107 _setInt32AtTail(_buf, _tail, value); | 112 _setInt32AtTail(_buf, _tail, value); |
| 108 } | 113 } |
| 109 } | 114 } |
| 110 | 115 |
| 111 /** | 116 /** |
| 112 * Add the [field] with the given 8-bit signed integer [value]. The field is | 117 * Add the [field] with the given 8-bit signed integer [value]. The field is |
| 113 * not added if the [value] is equal to [def]. | 118 * not added if the [value] is equal to [def]. |
| 114 */ | 119 */ |
| 115 void addInt8(int field, int value, [int def]) { | 120 void addInt8(int field, int value, [int def]) { |
| 116 if (_currentVTableBuilder == null) { | 121 if (_currentVTable == null) { |
| 117 throw new StateError('Start a table before adding values.'); | 122 throw new StateError('Start a table before adding values.'); |
| 118 } | 123 } |
| 119 if (value != def) { | 124 if (value != def) { |
| 120 int size = 1; | 125 int size = 1; |
| 121 _prepare(size, 1); | 126 _prepare(size, 1); |
| 122 _trackField(field); | 127 _trackField(field); |
| 123 _buf.setInt8(_buf.lengthInBytes - _tail, value); | 128 _buf.setInt8(_buf.lengthInBytes - _tail, value); |
| 124 } | 129 } |
| 125 } | 130 } |
| 126 | 131 |
| 127 /** | 132 /** |
| 128 * Add the [field] referencing an object with the given [offset]. | 133 * Add the [field] referencing an object with the given [offset]. |
| 129 */ | 134 */ |
| 130 void addOffset(int field, Offset offset) { | 135 void addOffset(int field, Offset offset) { |
| 131 if (_currentVTableBuilder == null) { | 136 if (_currentVTable == null) { |
| 132 throw new StateError('Start a table before adding values.'); | 137 throw new StateError('Start a table before adding values.'); |
| 133 } | 138 } |
| 134 if (offset != null) { | 139 if (offset != null) { |
| 135 _prepare(4, 1); | 140 _prepare(4, 1); |
| 136 _trackField(field); | 141 _trackField(field); |
| 137 _setUint32AtTail(_buf, _tail, _tail - offset._tail); | 142 _setUint32AtTail(_buf, _tail, _tail - offset._tail); |
| 138 } | 143 } |
| 139 } | 144 } |
| 140 | 145 |
| 141 /** | 146 /** |
| 142 * End the current table and return its offset. | 147 * End the current table and return its offset. |
| 143 */ | 148 */ |
| 144 Offset endTable() { | 149 Offset endTable() { |
| 145 if (_currentVTableBuilder == null) { | 150 if (_currentVTable == null) { |
| 146 throw new StateError('Start a table before ending it.'); | 151 throw new StateError('Start a table before ending it.'); |
| 147 } | 152 } |
| 148 // Prepare the size of the current table. | 153 // Prepare the size of the current table. |
| 149 int tableSize = _tail - _currentTableEndTail; | 154 _currentVTable.tableSize = _tail - _currentTableEndTail; |
| 150 // Prepare for writing the VTable. | 155 // Prepare for writing the VTable. |
| 151 _prepare(4, 1); | 156 _prepare(4, 1); |
| 152 int tableTail = _tail; | 157 int tableTail = _tail; |
| 153 // Write the VTable. | 158 // Prepare the VTable to use for the current table. |
| 154 // TODO(scheglov) implement VTable(s) sharing | 159 int vTableTail; |
| 155 _prepare(2, _currentVTableBuilder.numOfUint16); | 160 { |
| 156 _currentVTableBuilder.output( | 161 _currentVTable.computeFieldOffsets(tableTail); |
| 157 _buf, _buf.lengthInBytes - _tail, tableTail, tableSize); | 162 // Try to find an existing compatible VTable. |
| 163 for (_VTable vTable in _vTables) { | |
|
Paul Berry
2016/01/08 18:31:35
If this linear search proves too expensive, I have
| |
| 164 if (_currentVTable.canUseExistingVTable(vTable)) { | |
| 165 vTableTail = vTable.tail; | |
| 166 } | |
| 167 } | |
| 168 // Write a new VTable. | |
| 169 if (vTableTail == null) { | |
| 170 _prepare(2, _currentVTable.numOfUint16); | |
| 171 vTableTail = _tail; | |
| 172 _currentVTable.tail = vTableTail; | |
| 173 _currentVTable.output(_buf, _buf.lengthInBytes - _tail); | |
| 174 _vTables.add(_currentVTable); | |
| 175 } | |
| 176 } | |
| 158 // Set the VTable offset. | 177 // Set the VTable offset. |
| 159 _setInt32AtTail(_buf, tableTail, _tail - tableTail); | 178 _setInt32AtTail(_buf, tableTail, vTableTail - tableTail); |
| 160 // Done with this table. | 179 // Done with this table. |
| 161 _currentVTableBuilder = null; | 180 _currentVTable = null; |
| 162 return new Offset(tableTail); | 181 return new Offset(tableTail); |
| 163 } | 182 } |
| 164 | 183 |
| 165 /** | 184 /** |
| 166 * Finish off the creation of the buffer. The given [offset] is used as the | 185 * 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 | 186 * root object offset, and usually references directly or indirectly every |
| 168 * written object. | 187 * written object. |
| 169 */ | 188 */ |
| 170 Uint8List finish(Offset offset) { | 189 Uint8List finish(Offset offset) { |
| 171 _prepare(4, 1); | 190 _prepare(4, 1); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 207 _buf.setUint8(_buf.lengthInBytes - _tail, value); | 226 _buf.setUint8(_buf.lengthInBytes - _tail, value); |
| 208 } | 227 } |
| 209 | 228 |
| 210 /** | 229 /** |
| 211 * Reset the builder and make it ready for filling a new buffer. | 230 * Reset the builder and make it ready for filling a new buffer. |
| 212 */ | 231 */ |
| 213 void reset() { | 232 void reset() { |
| 214 _buf = new ByteData(initialSize); | 233 _buf = new ByteData(initialSize); |
| 215 _maxAlign = 1; | 234 _maxAlign = 1; |
| 216 _tail = 0; | 235 _tail = 0; |
| 217 _currentVTableBuilder = null; | 236 _currentVTable = null; |
| 218 } | 237 } |
| 219 | 238 |
| 220 /** | 239 /** |
| 221 * Start a new table. Must be finished with [endTable] invocation. | 240 * Start a new table. Must be finished with [endTable] invocation. |
| 222 */ | 241 */ |
| 223 void startTable() { | 242 void startTable() { |
| 224 if (_currentVTableBuilder != null) { | 243 if (_currentVTable != null) { |
| 225 throw new StateError('Inline tables are not supported.'); | 244 throw new StateError('Inline tables are not supported.'); |
| 226 } | 245 } |
| 227 _currentVTableBuilder = new _VTableBuilder(); | 246 _currentVTable = new _VTable(); |
| 228 _currentTableEndTail = _tail; | 247 _currentTableEndTail = _tail; |
| 229 } | 248 } |
| 230 | 249 |
| 231 /** | 250 /** |
| 232 * Write the given list of [values]. | 251 * Write the given list of [values]. |
| 233 */ | 252 */ |
| 234 Offset writeList(List<Offset> values) { | 253 Offset writeList(List<Offset> values) { |
| 235 if (_currentVTableBuilder != null) { | 254 if (_currentVTable != null) { |
| 236 throw new StateError( | 255 throw new StateError( |
| 237 'Cannot write a non-scalar value while writing a table.'); | 256 'Cannot write a non-scalar value while writing a table.'); |
| 238 } | 257 } |
| 239 _prepare(4, 1 + values.length); | 258 _prepare(4, 1 + values.length); |
| 240 Offset result = new Offset(_tail); | 259 Offset result = new Offset(_tail); |
| 241 int tail = _tail; | 260 int tail = _tail; |
| 242 _setUint32AtTail(_buf, tail, values.length); | 261 _setUint32AtTail(_buf, tail, values.length); |
| 243 tail -= 4; | 262 tail -= 4; |
| 244 for (Offset value in values) { | 263 for (Offset value in values) { |
| 245 _setUint32AtTail(_buf, tail, tail - value._tail); | 264 _setUint32AtTail(_buf, tail, tail - value._tail); |
| 246 tail -= 4; | 265 tail -= 4; |
| 247 } | 266 } |
| 248 return result; | 267 return result; |
| 249 } | 268 } |
| 250 | 269 |
| 251 /** | 270 /** |
| 252 * Write the given string [value] and return its [Offset], or `null` if | 271 * Write the given string [value] and return its [Offset], or `null` if |
| 253 * the [value] is equal to [def]. | 272 * the [value] is equal to [def]. |
| 254 */ | 273 */ |
| 255 Offset<String> writeString(String value, [String def]) { | 274 Offset<String> writeString(String value, [String def]) { |
| 256 if (_currentVTableBuilder != null) { | 275 if (_currentVTable != null) { |
| 257 throw new StateError( | 276 throw new StateError( |
| 258 'Cannot write a non-scalar value while writing a table.'); | 277 'Cannot write a non-scalar value while writing a table.'); |
| 259 } | 278 } |
| 260 if (value != def) { | 279 if (value != def) { |
| 261 // TODO(scheglov) optimize for ASCII strings | 280 // TODO(scheglov) optimize for ASCII strings |
| 262 List<int> bytes = UTF8.encode(value); | 281 List<int> bytes = UTF8.encode(value); |
| 263 int length = bytes.length; | 282 int length = bytes.length; |
| 264 _prepare(4, 1, additionalBytes: length); | 283 _prepare(4, 1, additionalBytes: length); |
| 265 Offset<String> result = new Offset(_tail); | 284 Offset<String> result = new Offset(_tail); |
| 266 _setUint32AtTail(_buf, _tail, length); | 285 _setUint32AtTail(_buf, _tail, length); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 303 } | 322 } |
| 304 } | 323 } |
| 305 // Update the tail pointer. | 324 // Update the tail pointer. |
| 306 _tail += bufSize; | 325 _tail += bufSize; |
| 307 } | 326 } |
| 308 | 327 |
| 309 /** | 328 /** |
| 310 * Record the offset of the given [field]. | 329 * Record the offset of the given [field]. |
| 311 */ | 330 */ |
| 312 void _trackField(int field) { | 331 void _trackField(int field) { |
| 313 _currentVTableBuilder.addField(field, _tail); | 332 _currentVTable.addField(field, _tail); |
| 314 } | 333 } |
| 315 | 334 |
| 316 static void _setInt32AtTail(ByteData _buf, int tail, int x) { | 335 static void _setInt32AtTail(ByteData _buf, int tail, int x) { |
| 317 _buf.setInt32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); | 336 _buf.setInt32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); |
| 318 } | 337 } |
| 319 | 338 |
| 320 static void _setUint32AtTail(ByteData _buf, int tail, int x) { | 339 static void _setUint32AtTail(ByteData _buf, int tail, int x) { |
| 321 _buf.setUint32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); | 340 _buf.setUint32(_buf.lengthInBytes - tail, x, Endianness.LITTLE_ENDIAN); |
| 322 } | 341 } |
| 323 } | 342 } |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 467 BufferPointer ref = bp._advance(4 + elementReader.size * i); | 486 BufferPointer ref = bp._advance(4 + elementReader.size * i); |
| 468 return elementReader.read(ref); | 487 return elementReader.read(ref); |
| 469 } | 488 } |
| 470 | 489 |
| 471 @override | 490 @override |
| 472 void operator []=(int i, E e) => | 491 void operator []=(int i, E e) => |
| 473 throw new StateError('Attempt to modify immutable list'); | 492 throw new StateError('Attempt to modify immutable list'); |
| 474 } | 493 } |
| 475 | 494 |
| 476 /** | 495 /** |
| 477 * Class for building VTable(s). | 496 * Class that describes the structure of a table. |
| 478 */ | 497 */ |
| 479 class _VTableBuilder { | 498 class _VTable { |
| 480 final List<int> fieldTails = <int>[]; | 499 final List<int> fieldTails = <int>[]; |
| 500 final List<int> fieldOffsets = <int>[]; | |
| 501 | |
| 502 /** | |
| 503 * The size of the table that uses this VTable. | |
| 504 */ | |
| 505 int tableSize; | |
| 506 | |
| 507 /** | |
| 508 * The tail of this VTable. It is used to share the same VTable between | |
| 509 * multiple tables of identical structure. | |
| 510 */ | |
| 511 int tail; | |
| 481 | 512 |
| 482 int get numOfUint16 => 1 + 1 + fieldTails.length; | 513 int get numOfUint16 => 1 + 1 + fieldTails.length; |
| 483 | 514 |
| 484 void addField(int field, int offset) { | 515 void addField(int field, int offset) { |
| 485 while (fieldTails.length <= field) { | 516 while (fieldTails.length <= field) { |
| 486 fieldTails.add(null); | 517 fieldTails.add(null); |
| 487 } | 518 } |
| 488 fieldTails[field] = offset; | 519 fieldTails[field] = offset; |
| 489 } | 520 } |
| 490 | 521 |
| 491 /** | 522 /** |
| 523 * Return `true` if the [existing] VTable can be used instead of this. | |
| 524 */ | |
| 525 bool canUseExistingVTable(_VTable existing) { | |
| 526 assert(tail == null); | |
| 527 assert(existing.tail != null); | |
| 528 if (tableSize == existing.tableSize && | |
| 529 fieldOffsets.length == existing.fieldOffsets.length) { | |
| 530 for (int i = 0; i < fieldOffsets.length; i++) { | |
| 531 if (fieldOffsets[i] != existing.fieldOffsets[i]) { | |
| 532 return false; | |
| 533 } | |
| 534 } | |
| 535 return true; | |
| 536 } | |
| 537 return false; | |
| 538 } | |
| 539 | |
| 540 /** | |
| 541 * Fill the [fieldOffsets] field. | |
| 542 */ | |
| 543 void computeFieldOffsets(int tableTail) { | |
| 544 assert(fieldOffsets.isEmpty); | |
| 545 for (int fieldTail in fieldTails) { | |
| 546 int fieldOffset = fieldTail == null ? 0 : tableTail - fieldTail; | |
| 547 fieldOffsets.add(fieldOffset); | |
| 548 } | |
| 549 } | |
| 550 | |
| 551 /** | |
| 492 * Outputs this VTable to [buf], which is is expected to be aligned to 16-bit | 552 * 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. | 553 * and have at least [numOfUint16] 16-bit words available. |
| 494 */ | 554 */ |
| 495 void output(ByteData buf, int bufOffset, int tableTail, int tableSize) { | 555 void output(ByteData buf, int bufOffset) { |
| 496 // VTable size. | 556 // VTable size. |
| 497 buf.setUint16(bufOffset, numOfUint16 * 2, Endianness.LITTLE_ENDIAN); | 557 buf.setUint16(bufOffset, numOfUint16 * 2, Endianness.LITTLE_ENDIAN); |
| 498 bufOffset += 2; | 558 bufOffset += 2; |
| 499 // Table size. | 559 // Table size. |
| 500 buf.setUint16(bufOffset, tableSize, Endianness.LITTLE_ENDIAN); | 560 buf.setUint16(bufOffset, tableSize, Endianness.LITTLE_ENDIAN); |
| 501 bufOffset += 2; | 561 bufOffset += 2; |
| 502 // Field offsets. | 562 // Field offsets. |
| 503 for (int fieldTail in fieldTails) { | 563 for (int fieldOffset in fieldOffsets) { |
| 504 int fieldOffset = fieldTail == null ? 0 : tableTail - fieldTail; | |
| 505 buf.setUint16(bufOffset, fieldOffset, Endianness.LITTLE_ENDIAN); | 564 buf.setUint16(bufOffset, fieldOffset, Endianness.LITTLE_ENDIAN); |
| 506 bufOffset += 2; | 565 bufOffset += 2; |
| 507 } | 566 } |
| 508 } | 567 } |
| 509 } | 568 } |
| OLD | NEW |