Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(20)

Side by Side Diff: pkg/analyzer/lib/src/summary/flat_buffers.dart

Issue 1564913004: Initial flat buffers implementation. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Fixes for review comments. Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | pkg/analyzer/test/src/summary/flat_buffers_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/test/src/summary/flat_buffers_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698