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

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: 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 library analyzer.src.summary.flat_buffers;
2
3 import 'dart:collection';
4 import 'dart:convert';
5 import 'dart:typed_data';
6
7 /**
8 * A pointer to some data.
9 */
10 class BufferPointer {
11 final ByteData _buffer;
12 final int _offset;
13
14 factory BufferPointer.fromBytes(List<int> byteList, [int offset = 0]) {
15 ByteBuffer byteBuffer = new Int8List.fromList(byteList).buffer;
Paul Berry 2016/01/07 15:55:03 It seems unfortunate that this constructor always
scheglov 2016/01/07 22:07:40 Done. If Uint8List is given, we create a view on i
16 ByteData buf = new ByteData.view(byteBuffer);
17 return new BufferPointer._(buf, offset);
18 }
19
20 BufferPointer._(this._buffer, this._offset);
21
22 BufferPointer derefObject() => _derefSOffset();
Paul Berry 2016/01/07 15:55:03 This is incorrect. Offsets to objects should be u
scheglov 2016/01/07 22:07:40 OK, rewritten to use backward building.
23
24 @override
25 String toString() => _offset.toString();
26
27 BufferPointer _advance(int delta) {
28 return new BufferPointer._(_buffer, _offset + delta);
29 }
30
31 BufferPointer _derefSOffset() {
32 int sOffset = _getInt32();
33 return _advance(sOffset);
Paul Berry 2016/01/07 15:55:03 This should be _advance(-sOffset). Signed offsets
34 }
35
36 int _getInt32([int delta = 0]) =>
37 _buffer.getInt32(_offset + delta, Endianness.LITTLE_ENDIAN);
38
39 int _getInt8([int delta = 0]) => _buffer.getInt8(_offset + delta);
40
41 int _getUint16([int delta = 0]) =>
42 _buffer.getUint16(_offset + delta, Endianness.LITTLE_ENDIAN);
43
44 int _getUint32([int delta = 0]) =>
45 _buffer.getUint32(_offset + delta, Endianness.LITTLE_ENDIAN);
46 }
47
48 /**
49 * Class that helps building flat buffers.
50 */
51 class Builder {
52 final int initialSize;
53
54 ByteData _buf;
55 int _offset;
56
57 int _currentTableOffset;
58 _VTableBuilder _currentVTableBuilder;
59
60 Builder({this.initialSize: 1024}) {
61 reset();
62 }
63
64 /**
65 * Add the [field] with the given 32-bit signed integer [value]. The field is
66 * not added if the [value] is equal to [def].
67 */
68 void addInt32(int field, int value, [int def]) {
69 if (_currentVTableBuilder == null) {
70 throw new StateError('Start a table before adding values.');
71 }
72 if (value != def) {
73 int size = 4;
74 _align(size);
75 _trackField(field);
76 _ensure(size);
77 _buf.setInt32(_offset, value, Endianness.LITTLE_ENDIAN);
78 _offset += size;
79 }
80 }
81
82 /**
83 * Add the [field] with the given 8-bit signed integer [value]. The field is
84 * not added if the [value] is equal to [def].
85 */
86 void addInt8(int field, int value, [int def]) {
87 if (_currentVTableBuilder == null) {
88 throw new StateError('Start a table before adding values.');
89 }
90 if (value != def) {
91 int size = 1;
92 _align(size);
93 _trackField(field);
94 _ensure(size);
95 _buf.setInt8(_offset, value);
96 _offset += size;
97 }
98 }
99
100 /**
101 * Add the [field] referencing an object with the given [offset].
102 */
103 void addOffset(int field, Offset offset) {
104 if (_currentVTableBuilder == null) {
105 throw new StateError('Start a table before adding values.');
106 }
107 if (offset != null) {
108 int size = 4;
109 _align(size);
110 _trackField(field);
111 _ensure(size);
112 _buf.setInt32(
Paul Berry 2016/01/07 15:55:03 This isn't right. Offsets are unsigned and always
113 _offset, offset._offset - _offset, Endianness.LITTLE_ENDIAN);
114 _offset += size;
115 }
116 }
117
118 /**
119 * End the current table and return its offset.
120 */
121 Offset endTable() {
122 if (_currentVTableBuilder == null) {
123 throw new StateError('Start a table before ending it.');
124 }
125 // Prepare the size of the current table.
126 int tableSize = _offset - _currentTableOffset;
127 // Prepare for writing the VTable.
Paul Berry 2016/01/07 15:55:03 Add a TODO comment to note that we shouldn't outpu
scheglov 2016/01/07 22:07:40 Done.
128 _align(2);
129 int vTableOffset = _offset;
130 // Write the VTable.
131 int vTableSize = _currentVTableBuilder.size;
132 _ensure(vTableSize);
133 _offset = _currentVTableBuilder.output(_buf, _offset, tableSize);
134 // Set the VTable offset.
135 _buf.setInt32(_currentTableOffset, vTableOffset - _currentTableOffset,
Paul Berry 2016/01/07 15:55:03 This should be _currentTableOffset - vTableOffset.
136 Endianness.LITTLE_ENDIAN);
137 // Done with this table.
138 _currentVTableBuilder = null;
139 return new Offset(_currentTableOffset);
140 }
141
142 /**
143 * Finish off the creation of the buffer. The given [offset] is used as the
144 * root object offset, and usually references directly of indirectly every
Paul Berry 2016/01/07 15:55:03 s/of/or/
scheglov 2016/01/07 22:07:39 Done.
145 * written object.
146 */
147 Uint8List finish(Offset offset) {
148 _buf.setUint32(0, offset._offset, Endianness.LITTLE_ENDIAN);
149 return _buf.buffer.asUint8List(0, _offset);
150 }
151
152 /**
153 * Reset the builder and make it ready for filling a new buffer.
154 */
155 void reset() {
156 _offset = 0;
157 _buf = new ByteData(initialSize);
158 _currentVTableBuilder = null;
159 // Skip the root offset.
160 _align(4);
161 _ensure(4);
162 _buf.setUint32(_offset, -1, Endianness.LITTLE_ENDIAN);
Paul Berry 2016/01/07 15:55:03 It seems weird to pass a literal -1 to setUint32,
163 _offset += 4;
164 }
165
166 /**
167 * Start a new table. Must be finished with [endTable] invocation.
168 */
169 void startTable() {
170 if (_currentVTableBuilder != null) {
171 throw new StateError('Inline tables are not supported.');
172 }
173 _currentVTableBuilder = new _VTableBuilder();
174 // Skip the VTable offset.
175 _align(4);
176 _currentTableOffset = _offset;
177 _buf.setUint32(_offset, -1, Endianness.LITTLE_ENDIAN);
Paul Berry 2016/01/07 15:55:03 Similar comment here.
178 _offset += 4;
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 _align(4);
190 Offset<String> result = new Offset(_offset);
191 _ensure(4 + values.length);
192 _buf.setUint32(_offset, values.length, Endianness.LITTLE_ENDIAN);
193 _offset += 4;
194 for (Offset value in values) {
195 _buf.setInt32(_offset, value._offset - _offset, Endianness.LITTLE_ENDIAN);
Paul Berry 2016/01/07 15:55:03 As with line 112 this disagrees with the flatbuffe
196 _offset += 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 _align(4);
213 Offset<String> result = new Offset(_offset);
214 _ensure(4 + bytes.length);
215 _buf.setUint32(_offset, bytes.length, Endianness.LITTLE_ENDIAN);
216 _offset += 4;
217 for (int b in bytes) {
Paul Berry 2016/01/07 15:55:03 In practice UTF8.encode() will always return a Uin
scheglov 2016/01/07 22:07:40 I measured times to write using this cycle vs. usi
Paul Berry 2016/01/07 22:44:53 Fair enough. Thanks for checking.
218 _buf.setInt8(_offset++, b);
219 }
220 return result;
221 }
222 return null;
223 }
224
225 /**
226 * Prepare to write an element with the given [size]. The current offset is
227 * aligned to the given [size].
228 */
229 void _align(int size) {
230 int alignDelta = (-_offset) % size;
231 _ensure(alignDelta + size);
232 _offset += alignDelta;
233 }
234
235 /**
236 * Ensure that there is at least the [size] bytes before the buffer end.
237 */
238 void _ensure(int size) {
239 int newSize = _offset + size;
240 int oldCapacity = _buf.lengthInBytes;
241 if (newSize > oldCapacity) {
242 int newCapacity = newSize * 2;
243 ByteData newBuf = new ByteData(newCapacity);
244 newBuf.buffer.asUint8List().setAll(0, _buf.buffer.asUint8List());
245 _buf = newBuf;
246 }
247 }
248
249 /**
250 * Record the offset of the given [field].
251 */
252 void _trackField(int field) {
253 _currentVTableBuilder.addField(field, _offset - _currentTableOffset);
254 }
255 }
256
257 /**
258 * The reader of 32-bit signed integers.
259 */
260 class Int32Reader extends Reader<int> {
261 const Int32Reader() : super();
262
263 @override
264 int get size => 2;
265
266 @override
267 int read(BufferPointer bp) => bp._getInt32();
268 }
269
270 /**
271 * The reader of 8-bit signed integers.
272 */
273 class Int8Reader extends Reader<int> {
274 const Int8Reader() : super();
275
276 @override
277 int get size => 1;
278
279 @override
280 int read(BufferPointer bp) => bp._getInt8();
281 }
282
283 /**
284 * The reader of object.
285 *
286 * The returned unmodifiable lists lazily read objects on access.
287 */
288 class ListReader<E> extends Reader<List<E>> {
289 final Reader<E> _elementReader;
290
291 const ListReader(this._elementReader);
292
293 @override
294 int get size => 4;
295
296 @override
297 List<E> read(BufferPointer bp) =>
298 new _FbList<E>(_elementReader, bp.derefObject());
299 }
300
301 /**
302 * The absolute offset to a serialized object of the type [T].
303 */
304 class Offset<T> {
305 final int _offset;
306
307 Offset(this._offset);
308 }
309
310 /**
311 * Object that can read a value at a [BufferPointer].
312 */
313 abstract class Reader<T> {
314 const Reader();
315
316 /**
317 * The size of the value in bytes.
318 */
319 int get size;
320
321 /**
322 * Read the value at the given pointer.
323 */
324 T read(BufferPointer bp);
325
326 /**
327 * Read the value of the given [field] in the given [object].
328 */
329 T vTableGet(BufferPointer object, int field, [T defaultValue]) {
330 BufferPointer vTable = object._derefSOffset();
331 int vTableSize = vTable._getUint16();
332 int vTableFieldOffset = (1 + 1 + field) * 2;
333 if (vTableFieldOffset < vTableSize) {
334 int fieldOffsetInObject = vTable._getUint16(vTableFieldOffset);
335 if (fieldOffsetInObject != 0) {
336 BufferPointer fieldPointer = object._advance(fieldOffsetInObject);
337 return read(fieldPointer);
338 }
339 }
340 return defaultValue;
341 }
342 }
343
344 /**
345 * The reader of string values.
346 */
347 class StringReader extends Reader<String> {
348 const StringReader() : super();
349
350 @override
351 int get size => 4;
352
353 @override
354 String read(BufferPointer ref) {
355 BufferPointer object = ref.derefObject();
356 int length = object._getUint32();
357 return UTF8
358 .decode(ref._buffer.buffer.asUint8List(object._offset + 4, length));
359 }
360 }
361
362 /**
363 * An abstract reader for tables.
364 */
365 abstract class TableReader<T extends TableReader<T>> extends Reader<T> {
366 const TableReader();
367
368 @override
369 int get size => 4;
370
371 /**
372 * Return the [Reader] for reading fields of the object at [bp].
373 */
374 T createReader(BufferPointer bp);
375
376 @override
377 T read(BufferPointer bp) {
378 bp = bp.derefObject();
379 return createReader(bp);
380 }
381 }
382
383 class _FbList<E> extends Object with ListMixin<E> implements List<E> {
384 final Reader<E> elementReader;
385 final BufferPointer bp;
386
387 _FbList(this.elementReader, this.bp);
388
389 @override
390 int get length => bp._getUint32();
391
392 @override
393 void set length(int i) =>
394 throw new StateError('Attempt to modify immutable list');
395
396 @override
397 E operator [](int i) {
398 BufferPointer ref = bp._advance(4 + elementReader.size * i);
399 return elementReader.read(ref);
400 }
401
402 @override
403 void operator []=(int i, E e) =>
404 throw new StateError('Attempt to modify immutable list');
405 }
406
407 /**
408 * Class for building VTable(s).
409 */
410 class _VTableBuilder {
411 final List<int> fieldOffsets = <int>[];
412
413 int get size => (1 + 1 + fieldOffsets.length) * 2;
414
415 void addField(int field, int offset) {
416 while (fieldOffsets.length <= field) {
417 fieldOffsets.add(null);
418 }
419 fieldOffsets[field] = offset;
420 }
421
422 /**
423 * Outputs this VTable to [buf], which is is expected to be aligned and
424 * have at least [size] bytes available. Return the offset directly after
425 * the VTable in [buf].
426 */
427 int output(ByteData buf, int bufOffset, int tableSize) {
428 buf.setUint16(bufOffset, size, Endianness.LITTLE_ENDIAN);
429 bufOffset += 2;
430 buf.setUint16(bufOffset, tableSize, Endianness.LITTLE_ENDIAN);
431 bufOffset += 2;
432 for (int fieldOffset in fieldOffsets) {
433 buf.setUint16(bufOffset, fieldOffset ?? 0, Endianness.LITTLE_ENDIAN);
434 bufOffset += 2;
435 }
436 return bufOffset;
437 }
438 }
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