blob: 32434171febddffdcae5915197de3ae1ff622ab5 [file] [log] [blame]
Austin Schuh7c75e582020-11-14 16:41:18 -08001"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Builder = void 0;
4var bit_width_1 = require("./bit-width");
5var bit_width_util_1 = require("./bit-width-util");
6var flexbuffers_util_1 = require("./flexbuffers-util");
7var value_type_1 = require("./value-type");
8var value_type_util_1 = require("./value-type-util");
9var stack_value_1 = require("./stack-value");
10var Builder = /** @class */ (function () {
11 function Builder(size, dedupStrings, dedupKeys, dedupKeyVectors) {
12 if (size === void 0) { size = 2048; }
13 if (dedupStrings === void 0) { dedupStrings = true; }
14 if (dedupKeys === void 0) { dedupKeys = true; }
15 if (dedupKeyVectors === void 0) { dedupKeyVectors = true; }
16 this.dedupStrings = dedupStrings;
17 this.dedupKeys = dedupKeys;
18 this.dedupKeyVectors = dedupKeyVectors;
19 this.stack = [];
20 this.stackPointers = [];
21 this.offset = 0;
22 this.finished = false;
23 this.stringLookup = {};
24 this.keyLookup = {};
25 this.keyVectorLookup = {};
26 this.indirectIntLookup = {};
27 this.indirectUIntLookup = {};
28 this.indirectFloatLookup = {};
29 this.buffer = new ArrayBuffer(size > 0 ? size : 2048);
30 this.view = new DataView(this.buffer);
31 }
32 Builder.prototype.align = function (width) {
33 var byteWidth = bit_width_util_1.toByteWidth(width);
34 this.offset += bit_width_util_1.paddingSize(this.offset, byteWidth);
35 return byteWidth;
36 };
37 Builder.prototype.computeOffset = function (newValueSize) {
38 var targetOffset = this.offset + newValueSize;
39 var size = this.buffer.byteLength;
40 var prevSize = size;
41 while (size < targetOffset) {
42 size <<= 1;
43 }
44 if (prevSize < size) {
45 var prevBuffer = this.buffer;
46 this.buffer = new ArrayBuffer(size);
47 this.view = new DataView(this.buffer);
48 new Uint8Array(this.buffer).set(new Uint8Array(prevBuffer), 0);
49 }
50 return targetOffset;
51 };
52 Builder.prototype.pushInt = function (value, width) {
53 if (width === bit_width_1.BitWidth.WIDTH8) {
54 this.view.setInt8(this.offset, value);
55 }
56 else if (width === bit_width_1.BitWidth.WIDTH16) {
57 this.view.setInt16(this.offset, value, true);
58 }
59 else if (width === bit_width_1.BitWidth.WIDTH32) {
60 this.view.setInt32(this.offset, value, true);
61 }
62 else if (width === bit_width_1.BitWidth.WIDTH64) {
63 this.view.setBigInt64(this.offset, BigInt(value), true);
64 }
65 else {
66 throw "Unexpected width: " + width + " for value: " + value;
67 }
68 };
69 Builder.prototype.pushUInt = function (value, width) {
70 if (width === bit_width_1.BitWidth.WIDTH8) {
71 this.view.setUint8(this.offset, value);
72 }
73 else if (width === bit_width_1.BitWidth.WIDTH16) {
74 this.view.setUint16(this.offset, value, true);
75 }
76 else if (width === bit_width_1.BitWidth.WIDTH32) {
77 this.view.setUint32(this.offset, value, true);
78 }
79 else if (width === bit_width_1.BitWidth.WIDTH64) {
80 this.view.setBigUint64(this.offset, BigInt(value), true);
81 }
82 else {
83 throw "Unexpected width: " + width + " for value: " + value;
84 }
85 };
86 Builder.prototype.writeInt = function (value, byteWidth) {
87 var newOffset = this.computeOffset(byteWidth);
88 this.pushInt(value, bit_width_util_1.fromByteWidth(byteWidth));
89 this.offset = newOffset;
90 };
91 Builder.prototype.writeUInt = function (value, byteWidth) {
92 var newOffset = this.computeOffset(byteWidth);
93 this.pushUInt(value, bit_width_util_1.fromByteWidth(byteWidth));
94 this.offset = newOffset;
95 };
96 Builder.prototype.writeBlob = function (arrayBuffer) {
97 var length = arrayBuffer.byteLength;
98 var bitWidth = bit_width_util_1.uwidth(length);
99 var byteWidth = this.align(bitWidth);
100 this.writeUInt(length, byteWidth);
101 var blobOffset = this.offset;
102 var newOffset = this.computeOffset(length);
103 new Uint8Array(this.buffer).set(new Uint8Array(arrayBuffer), blobOffset);
104 this.stack.push(this.offsetStackValue(blobOffset, value_type_1.ValueType.BLOB, bitWidth));
105 this.offset = newOffset;
106 };
107 Builder.prototype.writeString = function (str) {
108 if (this.dedupStrings && Object.prototype.hasOwnProperty.call(this.stringLookup, str)) {
109 this.stack.push(this.stringLookup[str]);
110 return;
111 }
112 var utf8 = flexbuffers_util_1.toUTF8Array(str);
113 var length = utf8.length;
114 var bitWidth = bit_width_util_1.uwidth(length);
115 var byteWidth = this.align(bitWidth);
116 this.writeUInt(length, byteWidth);
117 var stringOffset = this.offset;
118 var newOffset = this.computeOffset(length + 1);
119 new Uint8Array(this.buffer).set(utf8, stringOffset);
120 var stackValue = this.offsetStackValue(stringOffset, value_type_1.ValueType.STRING, bitWidth);
121 this.stack.push(stackValue);
122 if (this.dedupStrings) {
123 this.stringLookup[str] = stackValue;
124 }
125 this.offset = newOffset;
126 };
127 Builder.prototype.writeKey = function (str) {
128 if (this.dedupKeys && Object.prototype.hasOwnProperty.call(this.keyLookup, str)) {
129 this.stack.push(this.keyLookup[str]);
130 return;
131 }
132 var utf8 = flexbuffers_util_1.toUTF8Array(str);
133 var length = utf8.length;
134 var newOffset = this.computeOffset(length + 1);
135 new Uint8Array(this.buffer).set(utf8, this.offset);
136 var stackValue = this.offsetStackValue(this.offset, value_type_1.ValueType.KEY, bit_width_1.BitWidth.WIDTH8);
137 this.stack.push(stackValue);
138 if (this.dedupKeys) {
139 this.keyLookup[str] = stackValue;
140 }
141 this.offset = newOffset;
142 };
143 Builder.prototype.writeStackValue = function (value, byteWidth) {
144 var newOffset = this.computeOffset(byteWidth);
145 if (value.isOffset()) {
146 var relativeOffset = this.offset - value.offset;
147 if (byteWidth === 8 || BigInt(relativeOffset) < (BigInt(1) << BigInt(byteWidth * 8))) {
148 this.writeUInt(relativeOffset, byteWidth);
149 }
150 else {
151 throw "Unexpected size " + byteWidth + ". This might be a bug. Please create an issue https://github.com/google/flatbuffers/issues/new";
152 }
153 }
154 else {
155 value.writeToBuffer(byteWidth);
156 }
157 this.offset = newOffset;
158 };
159 Builder.prototype.integrityCheckOnValueAddition = function () {
160 if (this.finished) {
161 throw "Adding values after finish is prohibited";
162 }
163 if (this.stackPointers.length !== 0 && this.stackPointers[this.stackPointers.length - 1].isVector === false) {
164 if (this.stack[this.stack.length - 1].type !== value_type_1.ValueType.KEY) {
165 throw "Adding value to a map before adding a key is prohibited";
166 }
167 }
168 };
169 Builder.prototype.integrityCheckOnKeyAddition = function () {
170 if (this.finished) {
171 throw "Adding values after finish is prohibited";
172 }
173 if (this.stackPointers.length === 0 || this.stackPointers[this.stackPointers.length - 1].isVector) {
174 throw "Adding key before starting a map is prohibited";
175 }
176 };
177 Builder.prototype.startVector = function () {
178 this.stackPointers.push({ stackPosition: this.stack.length, isVector: true });
179 };
180 Builder.prototype.startMap = function (presorted) {
181 if (presorted === void 0) { presorted = false; }
182 this.stackPointers.push({ stackPosition: this.stack.length, isVector: false, presorted: presorted });
183 };
184 Builder.prototype.endVector = function (stackPointer) {
185 var vecLength = this.stack.length - stackPointer.stackPosition;
186 var vec = this.createVector(stackPointer.stackPosition, vecLength, 1);
187 this.stack.splice(stackPointer.stackPosition, vecLength);
188 this.stack.push(vec);
189 };
190 Builder.prototype.endMap = function (stackPointer) {
191 if (!stackPointer.presorted) {
192 this.sort(stackPointer);
193 }
194 var keyVectorHash = "";
195 for (var i = stackPointer.stackPosition; i < this.stack.length; i += 2) {
196 keyVectorHash += "," + this.stack[i].offset;
197 }
198 var vecLength = (this.stack.length - stackPointer.stackPosition) >> 1;
199 if (this.dedupKeyVectors && !Object.prototype.hasOwnProperty.call(this.keyVectorLookup, keyVectorHash)) {
200 this.keyVectorLookup[keyVectorHash] = this.createVector(stackPointer.stackPosition, vecLength, 2);
201 }
202 var keysStackValue = this.dedupKeyVectors ? this.keyVectorLookup[keyVectorHash] : this.createVector(stackPointer.stackPosition, vecLength, 2);
203 var valuesStackValue = this.createVector(stackPointer.stackPosition + 1, vecLength, 2, keysStackValue);
204 this.stack.splice(stackPointer.stackPosition, vecLength << 1);
205 this.stack.push(valuesStackValue);
206 };
207 Builder.prototype.sort = function (stackPointer) {
208 var view = this.view;
209 var stack = this.stack;
210 function shouldFlip(v1, v2) {
211 if (v1.type !== value_type_1.ValueType.KEY || v2.type !== value_type_1.ValueType.KEY) {
212 throw "Stack values are not keys " + v1 + " | " + v2 + ". Check if you combined [addKey] with add... method calls properly.";
213 }
214 var c1, c2;
215 var index = 0;
216 do {
217 c1 = view.getUint8(v1.offset + index);
218 c2 = view.getUint8(v2.offset + index);
219 if (c2 < c1)
220 return true;
221 if (c1 < c2)
222 return false;
223 index += 1;
224 } while (c1 !== 0 && c2 !== 0);
225 return false;
226 }
227 function swap(stack, flipIndex, i) {
228 if (flipIndex === i)
229 return;
230 var k = stack[flipIndex];
231 var v = stack[flipIndex + 1];
232 stack[flipIndex] = stack[i];
233 stack[flipIndex + 1] = stack[i + 1];
234 stack[i] = k;
235 stack[i + 1] = v;
236 }
237 function selectionSort() {
238 for (var i = stackPointer.stackPosition; i < stack.length; i += 2) {
239 var flipIndex = i;
240 for (var j = i + 2; j < stack.length; j += 2) {
241 if (shouldFlip(stack[flipIndex], stack[j])) {
242 flipIndex = j;
243 }
244 }
245 if (flipIndex !== i) {
246 swap(stack, flipIndex, i);
247 }
248 }
249 }
250 function smaller(v1, v2) {
251 if (v1.type !== value_type_1.ValueType.KEY || v2.type !== value_type_1.ValueType.KEY) {
252 throw "Stack values are not keys " + v1 + " | " + v2 + ". Check if you combined [addKey] with add... method calls properly.";
253 }
254 if (v1.offset === v2.offset) {
255 return false;
256 }
257 var c1, c2;
258 var index = 0;
259 do {
260 c1 = view.getUint8(v1.offset + index);
261 c2 = view.getUint8(v2.offset + index);
262 if (c1 < c2)
263 return true;
264 if (c2 < c1)
265 return false;
266 index += 1;
267 } while (c1 !== 0 && c2 !== 0);
268 return false;
269 }
270 function quickSort(left, right) {
271 if (left < right) {
272 var mid = left + (((right - left) >> 2)) * 2;
273 var pivot = stack[mid];
274 var left_new = left;
275 var right_new = right;
276 do {
277 while (smaller(stack[left_new], pivot)) {
278 left_new += 2;
279 }
280 while (smaller(pivot, stack[right_new])) {
281 right_new -= 2;
282 }
283 if (left_new <= right_new) {
284 swap(stack, left_new, right_new);
285 left_new += 2;
286 right_new -= 2;
287 }
288 } while (left_new <= right_new);
289 quickSort(left, right_new);
290 quickSort(left_new, right);
291 }
292 }
293 var sorted = true;
294 for (var i = stackPointer.stackPosition; i < this.stack.length - 2; i += 2) {
295 if (shouldFlip(this.stack[i], this.stack[i + 2])) {
296 sorted = false;
297 break;
298 }
299 }
300 if (!sorted) {
301 if (this.stack.length - stackPointer.stackPosition > 40) {
302 quickSort(stackPointer.stackPosition, this.stack.length - 2);
303 }
304 else {
305 selectionSort();
306 }
307 }
308 };
309 Builder.prototype.end = function () {
310 if (this.stackPointers.length < 1)
311 return;
312 var pointer = this.stackPointers.pop();
313 if (pointer.isVector) {
314 this.endVector(pointer);
315 }
316 else {
317 this.endMap(pointer);
318 }
319 };
320 Builder.prototype.createVector = function (start, vecLength, step, keys) {
321 if (keys === void 0) { keys = null; }
322 var bitWidth = bit_width_util_1.uwidth(vecLength);
323 var prefixElements = 1;
324 if (keys !== null) {
325 var elementWidth = keys.elementWidth(this.offset, 0);
326 if (elementWidth > bitWidth) {
327 bitWidth = elementWidth;
328 }
329 prefixElements += 2;
330 }
331 var vectorType = value_type_1.ValueType.KEY;
332 var typed = keys === null;
333 for (var i = start; i < this.stack.length; i += step) {
334 var elementWidth = this.stack[i].elementWidth(this.offset, i + prefixElements);
335 if (elementWidth > bitWidth) {
336 bitWidth = elementWidth;
337 }
338 if (i === start) {
339 vectorType = this.stack[i].type;
340 typed = typed && value_type_util_1.isTypedVectorElement(vectorType);
341 }
342 else {
343 if (vectorType !== this.stack[i].type) {
344 typed = false;
345 }
346 }
347 }
348 var byteWidth = this.align(bitWidth);
349 var fix = typed && value_type_util_1.isNumber(vectorType) && vecLength >= 2 && vecLength <= 4;
350 if (keys !== null) {
351 this.writeStackValue(keys, byteWidth);
352 this.writeUInt(1 << keys.width, byteWidth);
353 }
354 if (!fix) {
355 this.writeUInt(vecLength, byteWidth);
356 }
357 var vecOffset = this.offset;
358 for (var i = start; i < this.stack.length; i += step) {
359 this.writeStackValue(this.stack[i], byteWidth);
360 }
361 if (!typed) {
362 for (var i = start; i < this.stack.length; i += step) {
363 this.writeUInt(this.stack[i].storedPackedType(), 1);
364 }
365 }
366 if (keys !== null) {
367 return this.offsetStackValue(vecOffset, value_type_1.ValueType.MAP, bitWidth);
368 }
369 if (typed) {
370 var vType = value_type_util_1.toTypedVector(vectorType, fix ? vecLength : 0);
371 return this.offsetStackValue(vecOffset, vType, bitWidth);
372 }
373 return this.offsetStackValue(vecOffset, value_type_1.ValueType.VECTOR, bitWidth);
374 };
375 Builder.prototype.nullStackValue = function () {
376 return new stack_value_1.StackValue(this, value_type_1.ValueType.NULL, bit_width_1.BitWidth.WIDTH8);
377 };
378 Builder.prototype.boolStackValue = function (value) {
379 return new stack_value_1.StackValue(this, value_type_1.ValueType.BOOL, bit_width_1.BitWidth.WIDTH8, value);
380 };
381 Builder.prototype.intStackValue = function (value) {
382 return new stack_value_1.StackValue(this, value_type_1.ValueType.INT, bit_width_util_1.iwidth(value), value);
383 };
384 Builder.prototype.uintStackValue = function (value) {
385 return new stack_value_1.StackValue(this, value_type_1.ValueType.UINT, bit_width_util_1.uwidth(value), value);
386 };
387 Builder.prototype.floatStackValue = function (value) {
388 return new stack_value_1.StackValue(this, value_type_1.ValueType.FLOAT, bit_width_util_1.fwidth(value), value);
389 };
390 Builder.prototype.offsetStackValue = function (offset, valueType, bitWidth) {
391 return new stack_value_1.StackValue(this, valueType, bitWidth, null, offset);
392 };
393 Builder.prototype.finishBuffer = function () {
394 if (this.stack.length !== 1) {
395 throw "Stack has to be exactly 1, but it is " + this.stack.length + ". You have to end all started vectors and maps before calling [finish]";
396 }
397 var value = this.stack[0];
398 var byteWidth = this.align(value.elementWidth(this.offset, 0));
399 this.writeStackValue(value, byteWidth);
400 this.writeUInt(value.storedPackedType(), 1);
401 this.writeUInt(byteWidth, 1);
402 this.finished = true;
403 };
404 Builder.prototype.add = function (value) {
405 this.integrityCheckOnValueAddition();
406 if (typeof value === 'undefined') {
407 throw "You need to provide a value";
408 }
409 if (value === null) {
410 this.stack.push(this.nullStackValue());
411 }
412 else if (typeof value === "boolean") {
413 this.stack.push(this.boolStackValue(value));
414 }
415 else if (typeof value === "bigint") {
416 this.stack.push(this.intStackValue(value));
417 }
418 else if (typeof value == 'number') {
419 if (Number.isInteger(value)) {
420 this.stack.push(this.intStackValue(value));
421 }
422 else {
423 this.stack.push(this.floatStackValue(value));
424 }
425 }
426 else if (ArrayBuffer.isView(value)) {
427 this.writeBlob(value.buffer);
428 }
429 else if (typeof value === 'string' || value instanceof String) {
430 this.writeString(value);
431 }
432 else if (Array.isArray(value)) {
433 this.startVector();
434 for (var i = 0; i < value.length; i++) {
435 this.add(value[i]);
436 }
437 this.end();
438 }
439 else if (typeof value === 'object') {
440 var properties = Object.getOwnPropertyNames(value).sort();
441 this.startMap(true);
442 for (var i = 0; i < properties.length; i++) {
443 var key = properties[i];
444 this.addKey(key);
445 this.add(value[key]);
446 }
447 this.end();
448 }
449 else {
450 throw "Unexpected value input " + value;
451 }
452 };
453 Builder.prototype.finish = function () {
454 if (!this.finished) {
455 this.finishBuffer();
456 }
457 var result = this.buffer.slice(0, this.offset);
458 return new Uint8Array(result);
459 };
460 Builder.prototype.isFinished = function () {
461 return this.finished;
462 };
463 Builder.prototype.addKey = function (key) {
464 this.integrityCheckOnKeyAddition();
465 this.writeKey(key);
466 };
467 Builder.prototype.addInt = function (value, indirect, deduplicate) {
468 if (indirect === void 0) { indirect = false; }
469 if (deduplicate === void 0) { deduplicate = false; }
470 this.integrityCheckOnValueAddition();
471 if (!indirect) {
472 this.stack.push(this.intStackValue(value));
473 return;
474 }
475 if (deduplicate && Object.prototype.hasOwnProperty.call(this.indirectIntLookup, value)) {
476 this.stack.push(this.indirectIntLookup[value]);
477 return;
478 }
479 var stackValue = this.intStackValue(value);
480 var byteWidth = this.align(stackValue.width);
481 var newOffset = this.computeOffset(byteWidth);
482 var valueOffset = this.offset;
483 stackValue.writeToBuffer(byteWidth);
484 var stackOffset = this.offsetStackValue(valueOffset, value_type_1.ValueType.INDIRECT_INT, stackValue.width);
485 this.stack.push(stackOffset);
486 this.offset = newOffset;
487 if (deduplicate) {
488 this.indirectIntLookup[value] = stackOffset;
489 }
490 };
491 Builder.prototype.addUInt = function (value, indirect, deduplicate) {
492 if (indirect === void 0) { indirect = false; }
493 if (deduplicate === void 0) { deduplicate = false; }
494 this.integrityCheckOnValueAddition();
495 if (!indirect) {
496 this.stack.push(this.uintStackValue(value));
497 return;
498 }
499 if (deduplicate && Object.prototype.hasOwnProperty.call(this.indirectUIntLookup, value)) {
500 this.stack.push(this.indirectUIntLookup[value]);
501 return;
502 }
503 var stackValue = this.uintStackValue(value);
504 var byteWidth = this.align(stackValue.width);
505 var newOffset = this.computeOffset(byteWidth);
506 var valueOffset = this.offset;
507 stackValue.writeToBuffer(byteWidth);
508 var stackOffset = this.offsetStackValue(valueOffset, value_type_1.ValueType.INDIRECT_UINT, stackValue.width);
509 this.stack.push(stackOffset);
510 this.offset = newOffset;
511 if (deduplicate) {
512 this.indirectUIntLookup[value] = stackOffset;
513 }
514 };
515 Builder.prototype.addFloat = function (value, indirect, deduplicate) {
516 if (indirect === void 0) { indirect = false; }
517 if (deduplicate === void 0) { deduplicate = false; }
518 this.integrityCheckOnValueAddition();
519 if (!indirect) {
520 this.stack.push(this.floatStackValue(value));
521 return;
522 }
523 if (deduplicate && Object.prototype.hasOwnProperty.call(this.indirectFloatLookup, value)) {
524 this.stack.push(this.indirectFloatLookup[value]);
525 return;
526 }
527 var stackValue = this.floatStackValue(value);
528 var byteWidth = this.align(stackValue.width);
529 var newOffset = this.computeOffset(byteWidth);
530 var valueOffset = this.offset;
531 stackValue.writeToBuffer(byteWidth);
532 var stackOffset = this.offsetStackValue(valueOffset, value_type_1.ValueType.INDIRECT_FLOAT, stackValue.width);
533 this.stack.push(stackOffset);
534 this.offset = newOffset;
535 if (deduplicate) {
536 this.indirectFloatLookup[value] = stackOffset;
537 }
538 };
539 return Builder;
540}());
541exports.Builder = Builder;