cbor-x 0.9.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -21
- package/decode.d.ts +2 -0
- package/decode.js +306 -109
- package/dist/index.js +536 -258
- package/dist/index.min.js +99 -70
- package/dist/node.cjs +538 -253
- package/dist/test.js +51 -7
- package/encode.d.ts +1 -1
- package/encode.js +232 -86
- package/index.js +0 -2
- package/package.json +13 -4
- package/.vscode/launch.json +0 -22
- package/dist/index.js.map +0 -1
- package/tests/benchmark-stream.cjs +0 -282
- package/tests/benchmark.cjs +0 -245
- package/tests/example.json +0 -52
- package/tests/example2.json +0 -26
- package/tests/example3.json +0 -22
- package/tests/example4.json +0 -1
- package/tests/example5.json +0 -12
- package/tests/floats.json +0 -1
- package/tests/index.html +0 -28
- package/tests/sample-large.json +0 -231
- package/tests/strings2.json +0 -1
- package/tests/test-compatibility.cjs +0 -36
- package/tests/test-incomplete.js +0 -41
- package/tests/test-node-iterators.js +0 -72
- package/tests/test-node-stream.js +0 -60
- package/tests/test.js +0 -596
package/dist/node.cjs
CHANGED
|
@@ -13,7 +13,10 @@ let src;
|
|
|
13
13
|
let srcEnd;
|
|
14
14
|
let position = 0;
|
|
15
15
|
const EMPTY_ARRAY = [];
|
|
16
|
-
const
|
|
16
|
+
const LEGACY_RECORD_INLINE_ID = 105;
|
|
17
|
+
const RECORD_DEFINITIONS_ID = 0xdffe;
|
|
18
|
+
const RECORD_INLINE_ID = 0xdfff; // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
|
|
19
|
+
const BUNDLED_STRINGS_ID = 0xdff9;
|
|
17
20
|
const PACKED_REFERENCE_TAG_ID = 6;
|
|
18
21
|
const STOP_CODE = {};
|
|
19
22
|
let strings = EMPTY_ARRAY;
|
|
@@ -23,6 +26,7 @@ let currentStructures;
|
|
|
23
26
|
let srcString;
|
|
24
27
|
let srcStringStart = 0;
|
|
25
28
|
let srcStringEnd = 0;
|
|
29
|
+
let bundledStrings;
|
|
26
30
|
let referenceMap;
|
|
27
31
|
let currentExtensions = [];
|
|
28
32
|
let currentExtensionRanges = [];
|
|
@@ -38,13 +42,70 @@ let sequentialMode = false;
|
|
|
38
42
|
class Decoder {
|
|
39
43
|
constructor(options) {
|
|
40
44
|
if (options) {
|
|
45
|
+
if ((options.keyMap || options._keyMap) && !options.useRecords) {
|
|
46
|
+
options.useRecords = false;
|
|
47
|
+
options.mapsAsObjects = true;
|
|
48
|
+
}
|
|
41
49
|
if (options.useRecords === false && options.mapsAsObjects === undefined)
|
|
42
50
|
options.mapsAsObjects = true;
|
|
43
|
-
if (options.getStructures
|
|
51
|
+
if (options.getStructures)
|
|
52
|
+
options.getShared = options.getStructures;
|
|
53
|
+
if (options.getShared && !options.structures)
|
|
44
54
|
(options.structures = []).uninitialized = true; // this is what we use to denote an uninitialized structures
|
|
55
|
+
if (options.keyMap) {
|
|
56
|
+
this.mapKey = new Map();
|
|
57
|
+
for (let [k,v] of Object.entries(options.keyMap)) this.mapKey.set(v,k);
|
|
58
|
+
}
|
|
45
59
|
}
|
|
46
60
|
Object.assign(this, options);
|
|
47
61
|
}
|
|
62
|
+
/*
|
|
63
|
+
decodeKey(key) {
|
|
64
|
+
return this.keyMap
|
|
65
|
+
? Object.keys(this.keyMap)[Object.values(this.keyMap).indexOf(key)] || key
|
|
66
|
+
: key
|
|
67
|
+
}
|
|
68
|
+
*/
|
|
69
|
+
decodeKey(key) {
|
|
70
|
+
return this.keyMap ? this.mapKey.get(key) || key : key
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
encodeKey(key) {
|
|
74
|
+
return this.keyMap && this.keyMap.hasOwnProperty(key) ? this.keyMap[key] : key
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
encodeKeys(rec) {
|
|
78
|
+
if (!this._keyMap) return rec
|
|
79
|
+
let map = new Map();
|
|
80
|
+
for (let [k,v] of Object.entries(rec)) map.set((this._keyMap.hasOwnProperty(k) ? this._keyMap[k] : k), v);
|
|
81
|
+
return map
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
decodeKeys(map) {
|
|
85
|
+
if (!this._keyMap || map.constructor.name != 'Map') return map
|
|
86
|
+
if (!this._mapKey) {
|
|
87
|
+
this._mapKey = new Map();
|
|
88
|
+
for (let [k,v] of Object.entries(this._keyMap)) this._mapKey.set(v,k);
|
|
89
|
+
}
|
|
90
|
+
let res = {};
|
|
91
|
+
//map.forEach((v,k) => res[Object.keys(this._keyMap)[Object.values(this._keyMap).indexOf(k)] || k] = v)
|
|
92
|
+
map.forEach((v,k) => res[this._mapKey.has(k) ? this._mapKey.get(k) : k] = v);
|
|
93
|
+
return res
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
mapDecode(source, end) {
|
|
97
|
+
|
|
98
|
+
let res = this.decode(source);
|
|
99
|
+
if (this._keyMap) {
|
|
100
|
+
//Experiemntal support for Optimised KeyMap decoding
|
|
101
|
+
switch (res.constructor.name) {
|
|
102
|
+
case 'Array': return res.map(r => this.decodeKeys(r))
|
|
103
|
+
//case 'Map': return this.decodeKeys(res)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return res
|
|
107
|
+
}
|
|
108
|
+
|
|
48
109
|
decode(source, end) {
|
|
49
110
|
if (src) {
|
|
50
111
|
// re-entrant execution, save the state and restore it after we do this decode
|
|
@@ -59,6 +120,7 @@ class Decoder {
|
|
|
59
120
|
srcStringEnd = 0;
|
|
60
121
|
srcString = null;
|
|
61
122
|
strings = EMPTY_ARRAY;
|
|
123
|
+
bundledStrings = null;
|
|
62
124
|
src = source;
|
|
63
125
|
// this provides cached access to the data view for a buffer if it is getting reused, which is a recommend
|
|
64
126
|
// technique for getting data from a database where it can be copied into an existing buffer instead of creating
|
|
@@ -129,6 +191,9 @@ class Decoder {
|
|
|
129
191
|
function checkedRead() {
|
|
130
192
|
try {
|
|
131
193
|
let result = read();
|
|
194
|
+
if (bundledStrings) // bundled strings to skip past
|
|
195
|
+
position = bundledStrings.postBundlePosition;
|
|
196
|
+
|
|
132
197
|
if (position == srcEnd) {
|
|
133
198
|
// finished reading this source, cleanup references
|
|
134
199
|
currentStructures = null;
|
|
@@ -203,6 +268,7 @@ function read() {
|
|
|
203
268
|
switch(majorType) {
|
|
204
269
|
case 2: // byte string
|
|
205
270
|
case 3: // text string
|
|
271
|
+
throw new Error('Indefinite length not supported for byte or text strings')
|
|
206
272
|
case 4: // array
|
|
207
273
|
let array = [];
|
|
208
274
|
let value, i = 0;
|
|
@@ -214,8 +280,8 @@ function read() {
|
|
|
214
280
|
let key;
|
|
215
281
|
if (currentDecoder.mapsAsObjects) {
|
|
216
282
|
let object = {};
|
|
217
|
-
|
|
218
|
-
|
|
283
|
+
if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[currentDecoder.decodeKey(key)] = read();
|
|
284
|
+
else while ((key = read()) != STOP_CODE) object[key] = read();
|
|
219
285
|
return object
|
|
220
286
|
} else {
|
|
221
287
|
if (restoreMapsAsObject) {
|
|
@@ -223,8 +289,8 @@ function read() {
|
|
|
223
289
|
restoreMapsAsObject = false;
|
|
224
290
|
}
|
|
225
291
|
let map = new Map();
|
|
226
|
-
|
|
227
|
-
|
|
292
|
+
if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) map.set(currentDecoder.decodeKey(key), read());
|
|
293
|
+
else while ((key = read()) != STOP_CODE) map.set(key, read());
|
|
228
294
|
return map
|
|
229
295
|
}
|
|
230
296
|
case 7:
|
|
@@ -256,16 +322,15 @@ function read() {
|
|
|
256
322
|
return readFixedString(token)
|
|
257
323
|
case 4: // array
|
|
258
324
|
let array = new Array(token);
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
325
|
+
//if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
|
|
326
|
+
//else
|
|
327
|
+
for (let i = 0; i < token; i++) array[i] = read();
|
|
262
328
|
return array
|
|
263
329
|
case 5: // map
|
|
264
330
|
if (currentDecoder.mapsAsObjects) {
|
|
265
331
|
let object = {};
|
|
266
|
-
for (let i = 0; i < token; i++)
|
|
267
|
-
|
|
268
|
-
}
|
|
332
|
+
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[currentDecoder.decodeKey(read())] = read();
|
|
333
|
+
else for (let i = 0; i < token; i++) object[read()] = read();
|
|
269
334
|
return object
|
|
270
335
|
} else {
|
|
271
336
|
if (restoreMapsAsObject) {
|
|
@@ -273,63 +338,66 @@ function read() {
|
|
|
273
338
|
restoreMapsAsObject = false;
|
|
274
339
|
}
|
|
275
340
|
let map = new Map();
|
|
276
|
-
for (let i = 0; i < token; i++)
|
|
277
|
-
|
|
278
|
-
}
|
|
341
|
+
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
|
|
342
|
+
else for (let i = 0; i < token; i++) map.set(read(), read());
|
|
279
343
|
return map
|
|
280
344
|
}
|
|
281
345
|
case 6: // extension
|
|
282
|
-
if (
|
|
283
|
-
let structure = currentStructures[token &
|
|
346
|
+
if (token >= BUNDLED_STRINGS_ID) {
|
|
347
|
+
let structure = currentStructures[token & 0x1fff]; // check record structures first
|
|
348
|
+
// At some point we may provide an option for dynamic tag assignment with a range like token >= 8 && (token < 16 || (token > 0x80 && token < 0xc0) || (token > 0x130 && token < 0x4000))
|
|
284
349
|
if (structure) {
|
|
285
|
-
if (!structure.read)
|
|
286
|
-
structure.read = createStructureReader(structure);
|
|
350
|
+
if (!structure.read) structure.read = createStructureReader(structure);
|
|
287
351
|
return structure.read()
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (extension.handlesRead)
|
|
311
|
-
return extension(read)
|
|
312
|
-
else
|
|
313
|
-
return extension(read())
|
|
314
|
-
} else {
|
|
315
|
-
let input = read();
|
|
316
|
-
for (let i = 0; i < currentExtensionRanges.length; i++) {
|
|
317
|
-
let value = currentExtensionRanges[i](token, input);
|
|
318
|
-
if (value !== undefined)
|
|
319
|
-
return value
|
|
352
|
+
}
|
|
353
|
+
if (token < 0x10000) {
|
|
354
|
+
if (token == RECORD_INLINE_ID) // we do a special check for this so that we can keep the currentExtensions as densely stored array (v8 stores arrays densely under about 3000 elements)
|
|
355
|
+
return recordDefinition(read())
|
|
356
|
+
else if (token == RECORD_DEFINITIONS_ID) {
|
|
357
|
+
let length = readJustLength();
|
|
358
|
+
let id = read();
|
|
359
|
+
for (let i = 2; i < length; i++) {
|
|
360
|
+
recordDefinition([id++, read()]);
|
|
361
|
+
}
|
|
362
|
+
return read()
|
|
363
|
+
} else if (token == BUNDLED_STRINGS_ID) {
|
|
364
|
+
return readBundleExt()
|
|
365
|
+
}
|
|
366
|
+
if (currentDecoder.getShared) {
|
|
367
|
+
loadShared();
|
|
368
|
+
structure = currentStructures[token & 0x1fff];
|
|
369
|
+
if (structure) {
|
|
370
|
+
if (!structure.read)
|
|
371
|
+
structure.read = createStructureReader(structure);
|
|
372
|
+
return structure.read()
|
|
373
|
+
}
|
|
320
374
|
}
|
|
321
|
-
return new Tag(input)
|
|
322
375
|
}
|
|
323
376
|
}
|
|
377
|
+
let extension = currentExtensions[token];
|
|
378
|
+
if (extension) {
|
|
379
|
+
if (extension.handlesRead)
|
|
380
|
+
return extension(read)
|
|
381
|
+
else
|
|
382
|
+
return extension(read())
|
|
383
|
+
} else {
|
|
384
|
+
let input = read();
|
|
385
|
+
for (let i = 0; i < currentExtensionRanges.length; i++) {
|
|
386
|
+
let value = currentExtensionRanges[i](token, input);
|
|
387
|
+
if (value !== undefined)
|
|
388
|
+
return value
|
|
389
|
+
}
|
|
390
|
+
return new Tag(input, token)
|
|
391
|
+
}
|
|
324
392
|
case 7: // fixed value
|
|
325
393
|
switch (token) {
|
|
326
394
|
case 0x14: return false
|
|
327
395
|
case 0x15: return true
|
|
328
396
|
case 0x16: return null
|
|
329
397
|
case 0x17: return; // undefined
|
|
330
|
-
case 0x1f:
|
|
398
|
+
case 0x1f:
|
|
331
399
|
default:
|
|
332
|
-
let packedValue = packedValues[token];
|
|
400
|
+
let packedValue = (packedValues || getPackedValues())[token];
|
|
333
401
|
if (packedValue !== undefined)
|
|
334
402
|
return packedValue
|
|
335
403
|
throw new Error('Unknown token ' + token)
|
|
@@ -345,41 +413,47 @@ function read() {
|
|
|
345
413
|
}
|
|
346
414
|
const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
|
|
347
415
|
function createStructureReader(structure) {
|
|
348
|
-
let l = structure.length;
|
|
349
416
|
function readObject() {
|
|
417
|
+
// get the array size from the header
|
|
418
|
+
let length = src[position++];
|
|
419
|
+
//let majorType = token >> 5
|
|
420
|
+
length = length & 0x1f;
|
|
421
|
+
if (length > 0x17) {
|
|
422
|
+
switch (length) {
|
|
423
|
+
case 0x18:
|
|
424
|
+
length = src[position++];
|
|
425
|
+
break
|
|
426
|
+
case 0x19:
|
|
427
|
+
length = dataView.getUint16(position);
|
|
428
|
+
position += 2;
|
|
429
|
+
break
|
|
430
|
+
case 0x1a:
|
|
431
|
+
length = dataView.getUint32(position);
|
|
432
|
+
position += 4;
|
|
433
|
+
break
|
|
434
|
+
default:
|
|
435
|
+
throw new Error('Expected array header, but got ' + src[position - 1])
|
|
436
|
+
}
|
|
437
|
+
}
|
|
350
438
|
// This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
|
|
351
|
-
if (
|
|
352
|
-
|
|
353
|
-
|
|
439
|
+
if (this.objectLiteralSize === length) // we have a fast object literal reader, use it (assuming it is the right length)
|
|
440
|
+
return this.objectLiteral(read)
|
|
441
|
+
if (this.count++ == 3) { // create a fast reader
|
|
442
|
+
this.objectLiteralSize = length;
|
|
443
|
+
this.objectLiteral = currentDecoder.keyMap
|
|
444
|
+
? new Function('r', 'return {' + this.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
|
|
445
|
+
: new Function('r', 'return {' + this.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}');
|
|
446
|
+
return this.objectLiteral(read)
|
|
354
447
|
}
|
|
355
|
-
readArrayHeader();
|
|
356
448
|
let object = {};
|
|
357
|
-
for (let i = 0; i <
|
|
358
|
-
|
|
359
|
-
object[key] = read();
|
|
360
|
-
}
|
|
449
|
+
if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read();
|
|
450
|
+
else for (let i = 0; i < length; i++) object[this[i]] = read();
|
|
361
451
|
return object
|
|
362
452
|
}
|
|
363
|
-
|
|
453
|
+
structure.count = 0;
|
|
364
454
|
return readObject
|
|
365
455
|
}
|
|
366
456
|
|
|
367
|
-
function readArrayHeader(expectedLength) {
|
|
368
|
-
// consume the array header, TODO: check expected length
|
|
369
|
-
let token = src[position++];
|
|
370
|
-
//let majorType = token >> 5
|
|
371
|
-
token = token & 0x1f;
|
|
372
|
-
if (token > 0x17) {
|
|
373
|
-
switch (token) {
|
|
374
|
-
case 0x18: position++;
|
|
375
|
-
break
|
|
376
|
-
case 0x19: position += 2;
|
|
377
|
-
break
|
|
378
|
-
case 0x1a: position += 4;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
457
|
let readFixedString = readStringJS;
|
|
384
458
|
|
|
385
459
|
exports.isNativeAccelerationEnabled = false;
|
|
@@ -390,6 +464,8 @@ function setExtractor(extractStrings) {
|
|
|
390
464
|
return function readString(length) {
|
|
391
465
|
let string = strings[stringPosition++];
|
|
392
466
|
if (string == null) {
|
|
467
|
+
if (bundledStrings)
|
|
468
|
+
return readStringJS(length)
|
|
393
469
|
let extraction = extractStrings(position, srcEnd, length, src);
|
|
394
470
|
if (typeof extraction == 'string') {
|
|
395
471
|
string = extraction;
|
|
@@ -644,76 +720,15 @@ function getFloat16() {
|
|
|
644
720
|
}
|
|
645
721
|
|
|
646
722
|
let keyCache = new Array(4096);
|
|
647
|
-
function readKey() {
|
|
648
|
-
let length = src[position++];
|
|
649
|
-
if (length >= 0x60 && length < 0x78) {
|
|
650
|
-
// fixstr, potentially use key cache
|
|
651
|
-
length = length - 0x60;
|
|
652
|
-
if (srcStringEnd >= position) // if it has been extracted, must use it (and faster anyway)
|
|
653
|
-
return srcString.slice(position - srcStringStart, (position += length) - srcStringStart)
|
|
654
|
-
else if (!(srcStringEnd == 0 && srcEnd < 180))
|
|
655
|
-
return readFixedString(length)
|
|
656
|
-
} else { // not cacheable, go back and do a standard read
|
|
657
|
-
position--;
|
|
658
|
-
return read()
|
|
659
|
-
}
|
|
660
|
-
let key = ((length << 5) ^ (length > 1 ? dataView.getUint16(position) : length > 0 ? src[position] : 0)) & 0xfff;
|
|
661
|
-
let entry = keyCache[key];
|
|
662
|
-
let checkPosition = position;
|
|
663
|
-
let end = position + length - 3;
|
|
664
|
-
let chunk;
|
|
665
|
-
let i = 0;
|
|
666
|
-
if (entry && entry.bytes == length) {
|
|
667
|
-
while (checkPosition < end) {
|
|
668
|
-
chunk = dataView.getUint32(checkPosition);
|
|
669
|
-
if (chunk != entry[i++]) {
|
|
670
|
-
checkPosition = 0x70000000;
|
|
671
|
-
break
|
|
672
|
-
}
|
|
673
|
-
checkPosition += 4;
|
|
674
|
-
}
|
|
675
|
-
end += 3;
|
|
676
|
-
while (checkPosition < end) {
|
|
677
|
-
chunk = src[checkPosition++];
|
|
678
|
-
if (chunk != entry[i++]) {
|
|
679
|
-
checkPosition = 0x70000000;
|
|
680
|
-
break
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
if (checkPosition === end) {
|
|
684
|
-
position = checkPosition;
|
|
685
|
-
return entry.string
|
|
686
|
-
}
|
|
687
|
-
end -= 3;
|
|
688
|
-
checkPosition = position;
|
|
689
|
-
}
|
|
690
|
-
entry = [];
|
|
691
|
-
keyCache[key] = entry;
|
|
692
|
-
entry.bytes = length;
|
|
693
|
-
while (checkPosition < end) {
|
|
694
|
-
chunk = dataView.getUint32(checkPosition);
|
|
695
|
-
entry.push(chunk);
|
|
696
|
-
checkPosition += 4;
|
|
697
|
-
}
|
|
698
|
-
end += 3;
|
|
699
|
-
while (checkPosition < end) {
|
|
700
|
-
chunk = src[checkPosition++];
|
|
701
|
-
entry.push(chunk);
|
|
702
|
-
}
|
|
703
|
-
// for small blocks, avoiding the overhead of the extract call is helpful
|
|
704
|
-
let string = length < 16 ? shortStringInJS(length) : longStringInJS(length);
|
|
705
|
-
if (string != null)
|
|
706
|
-
return entry.string = string
|
|
707
|
-
return entry.string = readFixedString(length)
|
|
708
|
-
}
|
|
709
723
|
|
|
710
724
|
class Tag {
|
|
711
|
-
constructor(value) {
|
|
725
|
+
constructor(value, tag) {
|
|
712
726
|
this.value = value;
|
|
727
|
+
this.tag = tag;
|
|
713
728
|
}
|
|
714
729
|
}
|
|
715
730
|
|
|
716
|
-
let glbl = typeof
|
|
731
|
+
let glbl = typeof self == 'object' ? self : global;
|
|
717
732
|
|
|
718
733
|
currentExtensions[0] = (dateString) => {
|
|
719
734
|
// string date extension
|
|
@@ -727,38 +742,62 @@ currentExtensions[1] = (epochSec) => {
|
|
|
727
742
|
|
|
728
743
|
currentExtensions[2] = (buffer) => {
|
|
729
744
|
// bigint extension
|
|
730
|
-
|
|
745
|
+
let value = BigInt(0);
|
|
746
|
+
for (let i = 0, l = buffer.byteLength; i < l; i++) {
|
|
747
|
+
value = BigInt(buffer[i]) + value << BigInt(8);
|
|
748
|
+
}
|
|
749
|
+
return value
|
|
731
750
|
};
|
|
732
751
|
|
|
733
752
|
currentExtensions[3] = (buffer) => {
|
|
734
753
|
// negative bigint extension
|
|
735
|
-
return BigInt(-1) - (
|
|
754
|
+
return BigInt(-1) - currentExtensions[2](buffer)
|
|
755
|
+
};
|
|
756
|
+
currentExtensions[4] = (fraction) => {
|
|
757
|
+
// best to reparse to maintain accuracy
|
|
758
|
+
return +(fraction[1] + 'e' + fraction[0])
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
currentExtensions[5] = (fraction) => {
|
|
762
|
+
// probably not sufficiently accurate
|
|
763
|
+
return fraction[1] * Math.exp(fraction[0] * Math.log(2))
|
|
736
764
|
};
|
|
737
765
|
|
|
738
766
|
// the registration of the record definition extension (tag 105)
|
|
739
|
-
const recordDefinition = () => {
|
|
740
|
-
let
|
|
741
|
-
let structure = definition[
|
|
742
|
-
|
|
743
|
-
currentStructures[id & 0xff] = structure;
|
|
767
|
+
const recordDefinition = (definition) => {
|
|
768
|
+
let id = definition[0] - 0xe000;
|
|
769
|
+
let structure = definition[1];
|
|
770
|
+
currentStructures[id] = structure;
|
|
744
771
|
structure.read = createStructureReader(structure);
|
|
745
772
|
let object = {};
|
|
746
|
-
for (let i = 2,l = definition.length; i < l; i++) {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
773
|
+
if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
|
|
774
|
+
let key = currentDecoder.decodeKey(structure[i - 2]);
|
|
775
|
+
object[key] = definition[i];
|
|
776
|
+
}
|
|
777
|
+
else for (let i = 2,l = definition.length; i < l; i++) {
|
|
778
|
+
let key = structure[i - 2];
|
|
779
|
+
object[key] = definition[i];
|
|
780
|
+
}
|
|
750
781
|
return object
|
|
751
782
|
};
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
783
|
+
currentExtensions[LEGACY_RECORD_INLINE_ID] = recordDefinition;
|
|
784
|
+
currentExtensions[14] = (value) => {
|
|
785
|
+
if (bundledStrings)
|
|
786
|
+
return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
|
|
787
|
+
return new Tag(value, 14)
|
|
788
|
+
};
|
|
789
|
+
currentExtensions[15] = (value) => {
|
|
790
|
+
if (bundledStrings)
|
|
791
|
+
return bundledStrings[1].slice(bundledStrings.position1, bundledStrings.position1 += value)
|
|
792
|
+
return new Tag(value, 15)
|
|
793
|
+
};
|
|
755
794
|
|
|
756
795
|
currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
|
|
757
796
|
return (glbl[data[0]] || Error)(data[1], data[2])
|
|
758
797
|
};
|
|
759
798
|
const packedTable = (read) => {
|
|
760
799
|
if (src[position++] != 0x84)
|
|
761
|
-
throw new Error('Packed values structure must be followed by 4 element array')
|
|
800
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
762
801
|
let newPackedValues = read(); // packed values
|
|
763
802
|
packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
|
|
764
803
|
packedValues.prefixes = read();
|
|
@@ -769,15 +808,36 @@ packedTable.handlesRead = true;
|
|
|
769
808
|
currentExtensions[51] = packedTable;
|
|
770
809
|
|
|
771
810
|
currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
|
|
811
|
+
if (!packedValues) {
|
|
812
|
+
if (currentDecoder.getShared)
|
|
813
|
+
loadShared();
|
|
814
|
+
else
|
|
815
|
+
return new Tag(data, PACKED_REFERENCE_TAG_ID)
|
|
816
|
+
}
|
|
772
817
|
if (typeof data == 'number')
|
|
773
818
|
return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
|
|
774
819
|
throw new Error('No support for non-integer packed references yet')
|
|
775
820
|
};
|
|
821
|
+
currentExtensions[25] = (id) => {
|
|
822
|
+
return stringRefs[id]
|
|
823
|
+
};
|
|
824
|
+
currentExtensions[256] = (read) => {
|
|
825
|
+
stringRefs = [];
|
|
826
|
+
try {
|
|
827
|
+
return read()
|
|
828
|
+
} finally {
|
|
829
|
+
stringRefs = null;
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
currentExtensions[256].handlesRead = true;
|
|
776
833
|
|
|
777
|
-
currentExtensions[
|
|
778
|
-
//
|
|
779
|
-
if (!referenceMap)
|
|
834
|
+
currentExtensions[28] = (read) => {
|
|
835
|
+
// shareable http://cbor.schmorp.de/value-sharing (for structured clones)
|
|
836
|
+
if (!referenceMap) {
|
|
780
837
|
referenceMap = new Map();
|
|
838
|
+
referenceMap.id = 0;
|
|
839
|
+
}
|
|
840
|
+
let id = referenceMap.id++;
|
|
781
841
|
let token = src[position];
|
|
782
842
|
let target;
|
|
783
843
|
// TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
|
|
@@ -795,9 +855,10 @@ currentExtensions[40009] = (id) => {
|
|
|
795
855
|
refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
|
|
796
856
|
return targetProperties // no cycle, can just use the returned read object
|
|
797
857
|
};
|
|
858
|
+
currentExtensions[28].handlesRead = true;
|
|
798
859
|
|
|
799
|
-
currentExtensions[
|
|
800
|
-
//
|
|
860
|
+
currentExtensions[29] = (id) => {
|
|
861
|
+
// sharedref http://cbor.schmorp.de/value-sharing (for structured clones)
|
|
801
862
|
let refEntry = referenceMap.get(id);
|
|
802
863
|
refEntry.used = true;
|
|
803
864
|
return refEntry.target
|
|
@@ -820,19 +881,36 @@ function combine(a, b) {
|
|
|
820
881
|
return a.concat(b)
|
|
821
882
|
return Object.assign({}, a, b)
|
|
822
883
|
}
|
|
884
|
+
function getPackedValues() {
|
|
885
|
+
if (!packedValues) {
|
|
886
|
+
if (currentDecoder.getShared)
|
|
887
|
+
loadShared();
|
|
888
|
+
else
|
|
889
|
+
throw new Error('No packed values available')
|
|
890
|
+
}
|
|
891
|
+
return packedValues
|
|
892
|
+
}
|
|
893
|
+
const SHARED_DATA_TAG_ID = 0x53687264; // ascii 'Shrd'
|
|
823
894
|
currentExtensionRanges.push((tag, input) => {
|
|
824
895
|
if (tag >= 225 && tag <= 255)
|
|
825
|
-
return combine(
|
|
896
|
+
return combine(getPackedValues().prefixes[tag - 224], input)
|
|
826
897
|
if (tag >= 28704 && tag <= 32767)
|
|
827
|
-
return combine(
|
|
898
|
+
return combine(getPackedValues().prefixes[tag - 28672], input)
|
|
828
899
|
if (tag >= 1879052288 && tag <= 2147483647)
|
|
829
|
-
return combine(
|
|
900
|
+
return combine(getPackedValues().prefixes[tag - 1879048192], input)
|
|
830
901
|
if (tag >= 216 && tag <= 223)
|
|
831
|
-
return combine(input,
|
|
902
|
+
return combine(input, getPackedValues().suffixes[tag - 216])
|
|
832
903
|
if (tag >= 27647 && tag <= 28671)
|
|
833
|
-
return combine(input,
|
|
904
|
+
return combine(input, getPackedValues().suffixes[tag - 27639])
|
|
834
905
|
if (tag >= 1811940352 && tag <= 1879048191)
|
|
835
|
-
return combine(input,
|
|
906
|
+
return combine(input, getPackedValues().suffixes[tag - 1811939328])
|
|
907
|
+
if (tag == SHARED_DATA_TAG_ID) {// we do a special check for this so that we can keep the currentExtensions as densely stored array (v8 stores arrays densely under about 3000 elements)
|
|
908
|
+
return {
|
|
909
|
+
packedValues: packedValues,
|
|
910
|
+
structures: currentStructures.slice(0),
|
|
911
|
+
version: input,
|
|
912
|
+
}
|
|
913
|
+
}
|
|
836
914
|
});
|
|
837
915
|
|
|
838
916
|
const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array');
|
|
@@ -849,6 +927,61 @@ function registerTypedArray(typedArrayName, tag) {
|
|
|
849
927
|
};
|
|
850
928
|
}
|
|
851
929
|
|
|
930
|
+
function readBundleExt() {
|
|
931
|
+
let length = readJustLength();
|
|
932
|
+
let bundlePosition = position + read();
|
|
933
|
+
for (let i = 2; i < length; i++) {
|
|
934
|
+
// skip past bundles that were already read
|
|
935
|
+
let bundleLength = readJustLength(); // this will increment position, so must add to position afterwards
|
|
936
|
+
position += bundleLength;
|
|
937
|
+
}
|
|
938
|
+
let dataPosition = position;
|
|
939
|
+
position = bundlePosition;
|
|
940
|
+
bundledStrings = [readStringJS(readJustLength()), readStringJS(readJustLength())];
|
|
941
|
+
bundledStrings.position0 = 0;
|
|
942
|
+
bundledStrings.position1 = 0;
|
|
943
|
+
bundledStrings.postBundlePosition = position;
|
|
944
|
+
position = dataPosition;
|
|
945
|
+
return read()
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
function readJustLength() {
|
|
949
|
+
let token = src[position++] & 0x1f;
|
|
950
|
+
if (token > 0x17) {
|
|
951
|
+
switch (token) {
|
|
952
|
+
case 0x18:
|
|
953
|
+
token = src[position++];
|
|
954
|
+
break
|
|
955
|
+
case 0x19:
|
|
956
|
+
token = dataView.getUint16(position);
|
|
957
|
+
position += 2;
|
|
958
|
+
break
|
|
959
|
+
case 0x1a:
|
|
960
|
+
token = dataView.getUint32(position);
|
|
961
|
+
position += 4;
|
|
962
|
+
break
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return token
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function loadShared() {
|
|
969
|
+
if (currentDecoder.getShared) {
|
|
970
|
+
let sharedData = saveState(() => {
|
|
971
|
+
// save the state in case getShared modifies our buffer
|
|
972
|
+
src = null;
|
|
973
|
+
return currentDecoder.getShared()
|
|
974
|
+
}) || {};
|
|
975
|
+
let updatedStructures = sharedData.structures || [];
|
|
976
|
+
currentDecoder.sharedVersion = sharedData.version;
|
|
977
|
+
packedValues = currentDecoder.sharedValues = sharedData.packedValues;
|
|
978
|
+
if (currentStructures === true)
|
|
979
|
+
currentDecoder.structures = currentStructures = updatedStructures;
|
|
980
|
+
else
|
|
981
|
+
currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
|
|
852
985
|
function saveState(callback) {
|
|
853
986
|
let savedSrcEnd = srcEnd;
|
|
854
987
|
let savedPosition = position;
|
|
@@ -858,6 +991,8 @@ function saveState(callback) {
|
|
|
858
991
|
let savedSrcString = srcString;
|
|
859
992
|
let savedStrings = strings;
|
|
860
993
|
let savedReferenceMap = referenceMap;
|
|
994
|
+
let savedBundledStrings = bundledStrings;
|
|
995
|
+
|
|
861
996
|
// TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
|
|
862
997
|
let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
|
|
863
998
|
let savedStructures = currentStructures;
|
|
@@ -872,6 +1007,7 @@ function saveState(callback) {
|
|
|
872
1007
|
srcString = savedSrcString;
|
|
873
1008
|
strings = savedStrings;
|
|
874
1009
|
referenceMap = savedReferenceMap;
|
|
1010
|
+
bundledStrings = savedBundledStrings;
|
|
875
1011
|
src = savedSrc;
|
|
876
1012
|
sequentialMode = savedSequentialMode;
|
|
877
1013
|
currentStructures = savedStructures;
|
|
@@ -918,13 +1054,15 @@ let extensions, extensionClasses;
|
|
|
918
1054
|
const hasNodeBuffer = typeof Buffer !== 'undefined';
|
|
919
1055
|
const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
|
|
920
1056
|
const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
|
|
921
|
-
const RECORD_STARTING_ID_PREFIX = 0x69; // tag 105/0x69
|
|
922
1057
|
const MAX_STRUCTURES = 0x100;
|
|
923
1058
|
const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
|
|
924
1059
|
let target;
|
|
925
1060
|
let targetView;
|
|
926
1061
|
let position$1 = 0;
|
|
927
1062
|
let safeEnd;
|
|
1063
|
+
let bundledStrings$1 = null;
|
|
1064
|
+
const MAX_BUNDLE_SIZE = 0xf000;
|
|
1065
|
+
const hasNonLatin = /[\u0080-\uFFFF]/;
|
|
928
1066
|
const RECORD_SYMBOL = Symbol('record-id');
|
|
929
1067
|
class Encoder extends Decoder {
|
|
930
1068
|
constructor(options) {
|
|
@@ -936,7 +1074,6 @@ class Encoder extends Decoder {
|
|
|
936
1074
|
let structures;
|
|
937
1075
|
let referenceMap;
|
|
938
1076
|
options = options || {};
|
|
939
|
-
let lastSharedStructuresLength = 0;
|
|
940
1077
|
let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
|
|
941
1078
|
return target.utf8Write(string, position, maxBytes)
|
|
942
1079
|
} : (textEncoder && textEncoder.encodeInto) ?
|
|
@@ -945,12 +1082,14 @@ class Encoder extends Decoder {
|
|
|
945
1082
|
} : false;
|
|
946
1083
|
|
|
947
1084
|
let encoder = this;
|
|
948
|
-
let maxSharedStructures =
|
|
1085
|
+
let maxSharedStructures = options.maxSharedStructures || 128;
|
|
949
1086
|
let isSequential = options.sequential;
|
|
950
1087
|
if (isSequential) {
|
|
951
1088
|
maxSharedStructures = 0;
|
|
952
1089
|
this.structures = [];
|
|
953
1090
|
}
|
|
1091
|
+
if (this.saveStructures)
|
|
1092
|
+
this.saveShared = this.saveStructures;
|
|
954
1093
|
let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues;
|
|
955
1094
|
let sharedPackedObjectMap;
|
|
956
1095
|
if (sharedValues) {
|
|
@@ -958,13 +1097,29 @@ class Encoder extends Decoder {
|
|
|
958
1097
|
for (let i = 0, l = sharedValues.length; i < l; i++) {
|
|
959
1098
|
sharedPackedObjectMap[sharedValues[i]] = i;
|
|
960
1099
|
}
|
|
961
|
-
|
|
962
1100
|
}
|
|
963
1101
|
let recordIdsToRemove = [];
|
|
964
1102
|
let transitionsCount = 0;
|
|
965
1103
|
let serializationsSinceTransitionRebuild = 0;
|
|
966
|
-
|
|
967
|
-
this.
|
|
1104
|
+
|
|
1105
|
+
this.mapEncode = function(value, encodeOptions) {
|
|
1106
|
+
// Experimental support for premapping keys using _keyMap instad of keyMap - not optiimised yet)
|
|
1107
|
+
if (this._keyMap && !this._mapped) {
|
|
1108
|
+
//console.log('encoding ', value)
|
|
1109
|
+
switch (value.constructor.name) {
|
|
1110
|
+
case 'Array':
|
|
1111
|
+
value = value.map(r => this.encodeKeys(r));
|
|
1112
|
+
break
|
|
1113
|
+
//case 'Map':
|
|
1114
|
+
// value = this.encodeKeys(value)
|
|
1115
|
+
// break
|
|
1116
|
+
}
|
|
1117
|
+
//this._mapped = true
|
|
1118
|
+
}
|
|
1119
|
+
return this.encode(value, encodeOptions)
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
this.encode = function(value, encodeOptions) {
|
|
968
1123
|
if (!target) {
|
|
969
1124
|
target = new ByteArrayAllocate(8192);
|
|
970
1125
|
targetView = new DataView(target.buffer, 0, 8192);
|
|
@@ -981,10 +1136,25 @@ class Encoder extends Decoder {
|
|
|
981
1136
|
position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
|
|
982
1137
|
start = position$1;
|
|
983
1138
|
referenceMap = encoder.structuredClone ? new Map() : null;
|
|
1139
|
+
if (encoder.bundleStrings && typeof value !== 'string') {
|
|
1140
|
+
bundledStrings$1 = [];
|
|
1141
|
+
bundledStrings$1.size = Infinity; // force a new bundle start on first string
|
|
1142
|
+
} else
|
|
1143
|
+
bundledStrings$1 = null;
|
|
1144
|
+
|
|
984
1145
|
sharedStructures = encoder.structures;
|
|
985
1146
|
if (sharedStructures) {
|
|
986
|
-
if (sharedStructures.uninitialized)
|
|
987
|
-
|
|
1147
|
+
if (sharedStructures.uninitialized) {
|
|
1148
|
+
let sharedData = encoder.getShared() || {};
|
|
1149
|
+
encoder.structures = sharedStructures = sharedData.structures || [];
|
|
1150
|
+
encoder.sharedVersion = sharedData.version;
|
|
1151
|
+
let sharedValues = encoder.sharedValues = sharedData.packedValues;
|
|
1152
|
+
if (sharedValues) {
|
|
1153
|
+
sharedPackedObjectMap = {};
|
|
1154
|
+
for (let i = 0, l = sharedValues.length; i < l; i++)
|
|
1155
|
+
sharedPackedObjectMap[sharedValues[i]] = i;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
988
1158
|
let sharedStructuresLength = sharedStructures.length;
|
|
989
1159
|
if (sharedStructuresLength > maxSharedStructures && !isSequential)
|
|
990
1160
|
sharedStructuresLength = maxSharedStructures;
|
|
@@ -993,6 +1163,7 @@ class Encoder extends Decoder {
|
|
|
993
1163
|
sharedStructures.transitions = Object.create(null);
|
|
994
1164
|
for (let i = 0; i < sharedStructuresLength; i++) {
|
|
995
1165
|
let keys = sharedStructures[i];
|
|
1166
|
+
//console.log('shared struct keys:', keys)
|
|
996
1167
|
if (!keys)
|
|
997
1168
|
continue
|
|
998
1169
|
let nextTransition, transition = sharedStructures.transitions;
|
|
@@ -1006,7 +1177,6 @@ class Encoder extends Decoder {
|
|
|
1006
1177
|
}
|
|
1007
1178
|
transition[RECORD_SYMBOL] = i;
|
|
1008
1179
|
}
|
|
1009
|
-
lastSharedStructuresLength = sharedStructures.length;
|
|
1010
1180
|
}
|
|
1011
1181
|
if (!isSequential)
|
|
1012
1182
|
sharedStructures.nextId = sharedStructuresLength;
|
|
@@ -1039,9 +1209,12 @@ class Encoder extends Decoder {
|
|
|
1039
1209
|
}
|
|
1040
1210
|
try {
|
|
1041
1211
|
encode(value);
|
|
1212
|
+
if (bundledStrings$1) {
|
|
1213
|
+
writeBundles(start, encode);
|
|
1214
|
+
}
|
|
1042
1215
|
encoder.offset = position$1; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
|
|
1043
1216
|
if (referenceMap && referenceMap.idsToInsert) {
|
|
1044
|
-
position$1 += referenceMap.idsToInsert.length *
|
|
1217
|
+
position$1 += referenceMap.idsToInsert.length * 2;
|
|
1045
1218
|
if (position$1 > safeEnd)
|
|
1046
1219
|
makeRoom(position$1);
|
|
1047
1220
|
encoder.offset = position$1;
|
|
@@ -1049,12 +1222,12 @@ class Encoder extends Decoder {
|
|
|
1049
1222
|
referenceMap = null;
|
|
1050
1223
|
return serialized
|
|
1051
1224
|
}
|
|
1052
|
-
if (encodeOptions
|
|
1225
|
+
if (encodeOptions & REUSE_BUFFER_MODE) {
|
|
1053
1226
|
target.start = start;
|
|
1054
1227
|
target.end = position$1;
|
|
1055
1228
|
return target
|
|
1056
1229
|
}
|
|
1057
|
-
return target.subarray(start, position$1) // position can change if we call pack again in
|
|
1230
|
+
return target.subarray(start, position$1) // position can change if we call pack again in saveShared, so we get the buffer now
|
|
1058
1231
|
} finally {
|
|
1059
1232
|
if (sharedStructures) {
|
|
1060
1233
|
if (serializationsSinceTransitionRebuild < 10)
|
|
@@ -1072,34 +1245,27 @@ class Encoder extends Decoder {
|
|
|
1072
1245
|
}
|
|
1073
1246
|
recordIdsToRemove = [];
|
|
1074
1247
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
// we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
|
|
1080
|
-
let returnBuffer = target.subarray(start, position$1);
|
|
1081
|
-
let shared = encoder.structures || [];
|
|
1082
|
-
if (sharedValues) {
|
|
1083
|
-
shared = shared.concat(sharedValues);
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
if (encoder.saveStructures(encoder.structures, lastSharedStructuresLength) === false) {
|
|
1087
|
-
// get updated structures and try again if the update failed
|
|
1088
|
-
encoder.structures = encoder.getStructures() || [];
|
|
1089
|
-
return encoder.encode(value)
|
|
1090
|
-
}
|
|
1091
|
-
lastSharedStructuresLength = shared.length;
|
|
1092
|
-
return returnBuffer
|
|
1248
|
+
}
|
|
1249
|
+
if (hasSharedUpdate && encoder.saveShared) {
|
|
1250
|
+
if (encoder.structures.length > maxSharedStructures) {
|
|
1251
|
+
encoder.structures = encoder.structures.slice(0, maxSharedStructures);
|
|
1093
1252
|
}
|
|
1253
|
+
// we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
|
|
1254
|
+
let returnBuffer = target.subarray(start, position$1);
|
|
1255
|
+
if (encoder.updateSharedData() === false)
|
|
1256
|
+
return encoder.encode(value) // re-encode if it fails
|
|
1257
|
+
return returnBuffer
|
|
1094
1258
|
}
|
|
1259
|
+
if (encodeOptions & RESET_BUFFER_MODE)
|
|
1260
|
+
position$1 = start;
|
|
1095
1261
|
}
|
|
1096
1262
|
};
|
|
1097
1263
|
this.findCommonStringsToPack = () => {
|
|
1098
1264
|
samplingPackedValues = new Map();
|
|
1099
1265
|
if (!sharedPackedObjectMap)
|
|
1100
1266
|
sharedPackedObjectMap = Object.create(null);
|
|
1101
|
-
return (
|
|
1102
|
-
threshold = threshold || 4;
|
|
1267
|
+
return (options) => {
|
|
1268
|
+
let threshold = options && options.threshold || 4;
|
|
1103
1269
|
let position = this.pack ? options.maxPrivatePackedValues || 16 : 0;
|
|
1104
1270
|
if (!sharedValues)
|
|
1105
1271
|
sharedValues = this.sharedValues = [];
|
|
@@ -1110,6 +1276,7 @@ class Encoder extends Decoder {
|
|
|
1110
1276
|
hasSharedUpdate = true;
|
|
1111
1277
|
}
|
|
1112
1278
|
}
|
|
1279
|
+
while (this.saveShared && this.updateSharedData() === false) {}
|
|
1113
1280
|
samplingPackedValues = null;
|
|
1114
1281
|
}
|
|
1115
1282
|
};
|
|
@@ -1158,6 +1325,33 @@ class Encoder extends Decoder {
|
|
|
1158
1325
|
}
|
|
1159
1326
|
}
|
|
1160
1327
|
let strLength = value.length;
|
|
1328
|
+
if (bundledStrings$1 && strLength >= 4 && strLength < 0x400) {
|
|
1329
|
+
if ((bundledStrings$1.size += strLength) > MAX_BUNDLE_SIZE) {
|
|
1330
|
+
let extStart;
|
|
1331
|
+
let maxBytes = (bundledStrings$1[0] ? bundledStrings$1[0].length * 3 + bundledStrings$1[1].length : 0) + 10;
|
|
1332
|
+
if (position$1 + maxBytes > safeEnd)
|
|
1333
|
+
target = makeRoom(position$1 + maxBytes);
|
|
1334
|
+
target[position$1++] = 0xd9; // tag 16-bit
|
|
1335
|
+
target[position$1++] = 0xdf; // tag 0xdff9
|
|
1336
|
+
target[position$1++] = 0xf9;
|
|
1337
|
+
// TODO: If we only have one bundle with any string data, only write one string bundle
|
|
1338
|
+
target[position$1++] = bundledStrings$1.position ? 0x84 : 0x82; // array of 4 or 2 elements depending on if we write bundles
|
|
1339
|
+
target[position$1++] = 0x1a; // 32-bit unsigned int
|
|
1340
|
+
extStart = position$1 - start;
|
|
1341
|
+
position$1 += 4; // reserve for writing bundle reference
|
|
1342
|
+
if (bundledStrings$1.position) {
|
|
1343
|
+
writeBundles(start, encode); // write the last bundles
|
|
1344
|
+
}
|
|
1345
|
+
bundledStrings$1 = ['', '']; // create new ones
|
|
1346
|
+
bundledStrings$1.size = 0;
|
|
1347
|
+
bundledStrings$1.position = extStart;
|
|
1348
|
+
}
|
|
1349
|
+
let twoByte = hasNonLatin.test(value);
|
|
1350
|
+
bundledStrings$1[twoByte ? 0 : 1] += value;
|
|
1351
|
+
target[position$1++] = twoByte ? 0xce : 0xcf;
|
|
1352
|
+
encode(strLength);
|
|
1353
|
+
return
|
|
1354
|
+
}
|
|
1161
1355
|
let headerSize;
|
|
1162
1356
|
// first we estimate the header size, so we can write to the correct location
|
|
1163
1357
|
if (strLength < 0x20) {
|
|
@@ -1284,16 +1478,16 @@ class Encoder extends Decoder {
|
|
|
1284
1478
|
if (referenceMap) {
|
|
1285
1479
|
let referee = referenceMap.get(value);
|
|
1286
1480
|
if (referee) {
|
|
1287
|
-
|
|
1481
|
+
target[position$1++] = 0xd8;
|
|
1482
|
+
target[position$1++] = 29; // http://cbor.schmorp.de/value-sharing
|
|
1483
|
+
target[position$1++] = 0x19; // 16-bit uint
|
|
1484
|
+
if (!referee.references) {
|
|
1288
1485
|
let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = []);
|
|
1289
|
-
referee.
|
|
1486
|
+
referee.references = [];
|
|
1487
|
+
idsToInsert.push(referee);
|
|
1290
1488
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
target[position$1++] = 40010 & 0xff;
|
|
1294
|
-
target[position$1++] = 0x1a; // uint32
|
|
1295
|
-
targetView.setUint32(position$1, referee.id);
|
|
1296
|
-
position$1 += 4;
|
|
1489
|
+
referee.references.push(position$1 - start);
|
|
1490
|
+
position$1 += 2; // TODO: also support 32-bit
|
|
1297
1491
|
return
|
|
1298
1492
|
} else
|
|
1299
1493
|
referenceMap.set(value, { offset: position$1 - start });
|
|
@@ -1333,16 +1527,23 @@ class Encoder extends Decoder {
|
|
|
1333
1527
|
targetView.setUint32(position$1, length);
|
|
1334
1528
|
position$1 += 4;
|
|
1335
1529
|
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1530
|
+
if (encoder.keyMap) {
|
|
1531
|
+
for (let [ key, entryValue ] of value) {
|
|
1532
|
+
encode(encoder.encodeKey(key));
|
|
1533
|
+
encode(entryValue);
|
|
1534
|
+
}
|
|
1535
|
+
} else {
|
|
1536
|
+
for (let [ key, entryValue ] of value) {
|
|
1537
|
+
encode(key);
|
|
1538
|
+
encode(entryValue);
|
|
1539
|
+
}
|
|
1339
1540
|
}
|
|
1340
|
-
} else {
|
|
1541
|
+
} else {
|
|
1341
1542
|
for (let i = 0, l = extensions.length; i < l; i++) {
|
|
1342
1543
|
let extensionClass = extensionClasses[i];
|
|
1343
1544
|
if (value instanceof extensionClass) {
|
|
1344
1545
|
let extension = extensions[i];
|
|
1345
|
-
let tag = extension.tag;
|
|
1546
|
+
let tag = extension.tag || extension.getTag && extension.getTag(value);
|
|
1346
1547
|
if (tag < 0x18) {
|
|
1347
1548
|
target[position$1++] = 0xc0 | tag;
|
|
1348
1549
|
} else if (tag < 0x100) {
|
|
@@ -1404,6 +1605,7 @@ class Encoder extends Decoder {
|
|
|
1404
1605
|
const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
|
|
1405
1606
|
// this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
|
|
1406
1607
|
let keys = Object.keys(object);
|
|
1608
|
+
let vals = Object.values(object);
|
|
1407
1609
|
let length = keys.length;
|
|
1408
1610
|
if (length < 0x18) {
|
|
1409
1611
|
target[position$1++] = 0xa0 | length;
|
|
@@ -1419,10 +1621,16 @@ class Encoder extends Decoder {
|
|
|
1419
1621
|
targetView.setUint32(position$1, length);
|
|
1420
1622
|
position$1 += 4;
|
|
1421
1623
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1624
|
+
if (encoder.keyMap) {
|
|
1625
|
+
for (let i = 0; i < length; i++) {
|
|
1626
|
+
encode(encodeKey(keys[i]));
|
|
1627
|
+
encode(vals[i]);
|
|
1628
|
+
}
|
|
1629
|
+
} else {
|
|
1630
|
+
for (let i = 0; i < length; i++) {
|
|
1631
|
+
encode(keys[i]);
|
|
1632
|
+
encode(vals[i]);
|
|
1633
|
+
}
|
|
1426
1634
|
}
|
|
1427
1635
|
} :
|
|
1428
1636
|
(object, safePrototype) => {
|
|
@@ -1430,12 +1638,18 @@ class Encoder extends Decoder {
|
|
|
1430
1638
|
let objectOffset = position$1 - start;
|
|
1431
1639
|
position$1 += 2;
|
|
1432
1640
|
let size = 0;
|
|
1433
|
-
|
|
1434
|
-
if (safePrototype || object.hasOwnProperty(key)) {
|
|
1435
|
-
encode(key);
|
|
1641
|
+
if (encoder.keyMap) {
|
|
1642
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
1643
|
+
encode(encoder.encodeKey(key));
|
|
1436
1644
|
encode(object[key]);
|
|
1437
1645
|
size++;
|
|
1438
1646
|
}
|
|
1647
|
+
} else {
|
|
1648
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
1649
|
+
encode(key);
|
|
1650
|
+
encode(object[key]);
|
|
1651
|
+
size++;
|
|
1652
|
+
}
|
|
1439
1653
|
}
|
|
1440
1654
|
target[objectOffset++ + start] = size >> 8;
|
|
1441
1655
|
target[objectOffset + start] = size & 0xff;
|
|
@@ -1483,11 +1697,16 @@ class Encoder extends Decoder {
|
|
|
1483
1697
|
}*/
|
|
1484
1698
|
(object) => {
|
|
1485
1699
|
let keys = Object.keys(object);
|
|
1700
|
+
let vals = Object.values(object);
|
|
1701
|
+
if (this.keyMap) keys = keys.map(k => this.encodeKey(k));
|
|
1486
1702
|
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
|
|
1487
1703
|
let newTransitions = 0;
|
|
1488
1704
|
let length = keys.length;
|
|
1705
|
+
//let parentRecordId
|
|
1489
1706
|
for (let i = 0; i < length; i++) {
|
|
1707
|
+
//if (!parentRecordId)
|
|
1490
1708
|
let key = keys[i];
|
|
1709
|
+
// parentRecordId = transition[RECORD_SYMBOL]
|
|
1491
1710
|
nextTransition = transition[key];
|
|
1492
1711
|
if (!nextTransition) {
|
|
1493
1712
|
nextTransition = transition[key] = Object.create(null);
|
|
@@ -1497,9 +1716,9 @@ class Encoder extends Decoder {
|
|
|
1497
1716
|
}
|
|
1498
1717
|
let recordId = transition[RECORD_SYMBOL];
|
|
1499
1718
|
if (recordId !== undefined) {
|
|
1500
|
-
target[position$1++] = 0xd9;
|
|
1501
|
-
target[position$1++] =
|
|
1502
|
-
target[position$1++] = recordId;
|
|
1719
|
+
target[position$1++] = 0xd9;
|
|
1720
|
+
target[position$1++] = (recordId >> 8) | 0xe0;
|
|
1721
|
+
target[position$1++] = recordId & 0xff;
|
|
1503
1722
|
} else {
|
|
1504
1723
|
recordId = structures.nextId++;
|
|
1505
1724
|
if (!recordId) {
|
|
@@ -1512,13 +1731,13 @@ class Encoder extends Decoder {
|
|
|
1512
1731
|
transition[RECORD_SYMBOL] = recordId;
|
|
1513
1732
|
structures[recordId] = keys;
|
|
1514
1733
|
if (sharedStructures && sharedStructures.length <= maxSharedStructures) {
|
|
1515
|
-
target[position$1++] = 0xd9;
|
|
1516
|
-
target[position$1++] =
|
|
1517
|
-
target[position$1++] = recordId
|
|
1734
|
+
target[position$1++] = 0xd9;
|
|
1735
|
+
target[position$1++] = (recordId >> 8) | 0xe0;
|
|
1736
|
+
target[position$1++] = recordId & 0xff;
|
|
1518
1737
|
hasSharedUpdate = true;
|
|
1519
1738
|
} else {
|
|
1520
|
-
|
|
1521
|
-
|
|
1739
|
+
targetView.setUint32(position$1, 0xd9dfff00); // tag two byte, then record definition id
|
|
1740
|
+
position$1 += 3;
|
|
1522
1741
|
if (newTransitions)
|
|
1523
1742
|
transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
|
|
1524
1743
|
// record the removal of the id, we can maintain our shared structure
|
|
@@ -1526,13 +1745,9 @@ class Encoder extends Decoder {
|
|
|
1526
1745
|
recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined; // we are cycling back through, and have to remove old ones
|
|
1527
1746
|
recordIdsToRemove.push(transition);
|
|
1528
1747
|
writeArrayHeader(length + 2);
|
|
1748
|
+
encode(0xe000 + recordId);
|
|
1529
1749
|
encode(keys);
|
|
1530
|
-
|
|
1531
|
-
target[position$1++] = RECORD_STARTING_ID_PREFIX;
|
|
1532
|
-
target[position$1++] = recordId;
|
|
1533
|
-
// now write the values
|
|
1534
|
-
for (let i =0; i < length; i++)
|
|
1535
|
-
encode(object[keys[i]]);
|
|
1750
|
+
for (let v of Object.values(object)) encode(v);
|
|
1536
1751
|
return
|
|
1537
1752
|
}
|
|
1538
1753
|
}
|
|
@@ -1541,8 +1756,7 @@ class Encoder extends Decoder {
|
|
|
1541
1756
|
} else {
|
|
1542
1757
|
writeArrayHeader(length);
|
|
1543
1758
|
}
|
|
1544
|
-
for (let i =0; i < length; i++)
|
|
1545
|
-
encode(object[keys[i]]);
|
|
1759
|
+
for (let i =0; i < length; i++) encode(vals[i]);
|
|
1546
1760
|
};
|
|
1547
1761
|
const makeRoom = (end) => {
|
|
1548
1762
|
let newSize;
|
|
@@ -1572,6 +1786,33 @@ class Encoder extends Decoder {
|
|
|
1572
1786
|
targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
|
|
1573
1787
|
position$1 = 0;
|
|
1574
1788
|
}
|
|
1789
|
+
clearSharedData() {
|
|
1790
|
+
if (this.structures)
|
|
1791
|
+
this.structures = [];
|
|
1792
|
+
if (this.sharedValues)
|
|
1793
|
+
this.sharedValues = undefined;
|
|
1794
|
+
}
|
|
1795
|
+
updateSharedData() {
|
|
1796
|
+
let lastVersion = this.sharedVersion || 0;
|
|
1797
|
+
this.sharedVersion = lastVersion + 1;
|
|
1798
|
+
let saveResults = this.saveShared(new SharedData(this.structures, this.sharedValues, this.sharedVersion),
|
|
1799
|
+
existingShared => (existingShared && existingShared.version || 0) == lastVersion);
|
|
1800
|
+
if (saveResults === false) {
|
|
1801
|
+
// get updated structures and try again if the update failed
|
|
1802
|
+
let sharedData = this.getShared() || {};
|
|
1803
|
+
this.structures = sharedData.structures || [];
|
|
1804
|
+
this.sharedValues = sharedData.packedValues;
|
|
1805
|
+
this.sharedVersion = sharedData.version;
|
|
1806
|
+
}
|
|
1807
|
+
return saveResults
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
class SharedData {
|
|
1811
|
+
constructor(structures, values, version) {
|
|
1812
|
+
this.structures = structures;
|
|
1813
|
+
this.packedValues = values;
|
|
1814
|
+
this.version = version;
|
|
1815
|
+
}
|
|
1575
1816
|
}
|
|
1576
1817
|
|
|
1577
1818
|
function writeArrayHeader(length) {
|
|
@@ -1641,11 +1882,11 @@ function findRepetitiveStrings(value, packedValues) {
|
|
|
1641
1882
|
}
|
|
1642
1883
|
}
|
|
1643
1884
|
|
|
1644
|
-
extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
|
|
1885
|
+
extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
|
|
1645
1886
|
Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
|
|
1646
1887
|
typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
|
|
1647
1888
|
typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
|
|
1648
|
-
Float32Array, Float64Array];
|
|
1889
|
+
Float32Array, Float64Array, SharedData];
|
|
1649
1890
|
|
|
1650
1891
|
//Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
|
|
1651
1892
|
extensions = [{
|
|
@@ -1680,6 +1921,13 @@ extensions = [{
|
|
|
1680
1921
|
encode(regex, encode) {
|
|
1681
1922
|
encode([ 'RegExp', regex.source, regex.flags ]);
|
|
1682
1923
|
}
|
|
1924
|
+
}, {
|
|
1925
|
+
getTag(tag) {
|
|
1926
|
+
return tag.tag
|
|
1927
|
+
},
|
|
1928
|
+
encode(tag, encode) {
|
|
1929
|
+
encode(tag.value);
|
|
1930
|
+
}
|
|
1683
1931
|
}, {
|
|
1684
1932
|
encode(arrayBuffer, encode, makeRoom) {
|
|
1685
1933
|
writeBuffer(arrayBuffer, makeRoom);
|
|
@@ -1698,7 +1946,35 @@ extensions = [{
|
|
|
1698
1946
|
typedArrayEncoder(78),
|
|
1699
1947
|
typedArrayEncoder(79),
|
|
1700
1948
|
typedArrayEncoder(81),
|
|
1701
|
-
typedArrayEncoder(82)
|
|
1949
|
+
typedArrayEncoder(82),
|
|
1950
|
+
{
|
|
1951
|
+
encode(sharedData, encode) { // write SharedData
|
|
1952
|
+
let packedValues = sharedData.packedValues || [];
|
|
1953
|
+
let sharedStructures = sharedData.structures || [];
|
|
1954
|
+
if (packedValues.values.length > 0) {
|
|
1955
|
+
target[position$1++] = 0xd8; // one-byte tag
|
|
1956
|
+
target[position$1++] = 51; // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
|
|
1957
|
+
writeArrayHeader(4);
|
|
1958
|
+
let valuesArray = packedValues.values;
|
|
1959
|
+
encode(valuesArray);
|
|
1960
|
+
writeArrayHeader(0); // prefixes
|
|
1961
|
+
writeArrayHeader(0); // suffixes
|
|
1962
|
+
packedObjectMap = Object.create(sharedPackedObjectMap || null);
|
|
1963
|
+
for (let i = 0, l = valuesArray.length; i < l; i++) {
|
|
1964
|
+
packedObjectMap[valuesArray[i]] = i;
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
if (sharedStructures) {
|
|
1968
|
+
targetView.setUint32(position$1, 0xd9dffe00);
|
|
1969
|
+
position$1 += 3;
|
|
1970
|
+
let definitions = sharedStructures.slice(0);
|
|
1971
|
+
definitions.unshift(0xe000);
|
|
1972
|
+
definitions.push(new Tag(sharedData.version, 0x53687264));
|
|
1973
|
+
encode(definitions);
|
|
1974
|
+
} else
|
|
1975
|
+
encode(new Tag(sharedData.version, 0x53687264));
|
|
1976
|
+
}
|
|
1977
|
+
}];
|
|
1702
1978
|
|
|
1703
1979
|
function typedArrayEncoder(tag) {
|
|
1704
1980
|
return {
|
|
@@ -1738,27 +2014,35 @@ function writeBuffer(buffer, makeRoom) {
|
|
|
1738
2014
|
function insertIds(serialized, idsToInsert) {
|
|
1739
2015
|
// insert the ids that need to be referenced for structured clones
|
|
1740
2016
|
let nextId;
|
|
1741
|
-
let distanceToMove = idsToInsert.length *
|
|
2017
|
+
let distanceToMove = idsToInsert.length * 2;
|
|
1742
2018
|
let lastEnd = serialized.length - distanceToMove;
|
|
1743
2019
|
idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1);
|
|
2020
|
+
for (let id = 0; id < idsToInsert.length; id++) {
|
|
2021
|
+
let referee = idsToInsert[id];
|
|
2022
|
+
referee.id = id;
|
|
2023
|
+
for (let position of referee.references) {
|
|
2024
|
+
serialized[position++] = id >> 8;
|
|
2025
|
+
serialized[position] = id & 0xff;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
1744
2028
|
while (nextId = idsToInsert.pop()) {
|
|
1745
2029
|
let offset = nextId.offset;
|
|
1746
|
-
let id = nextId.id;
|
|
1747
2030
|
serialized.copyWithin(offset + distanceToMove, offset, lastEnd);
|
|
1748
|
-
distanceToMove -=
|
|
2031
|
+
distanceToMove -= 2;
|
|
1749
2032
|
let position = offset + distanceToMove;
|
|
1750
|
-
serialized[position++] =
|
|
1751
|
-
serialized[position++] =
|
|
1752
|
-
serialized[position++] = 40009 & 0xff;
|
|
1753
|
-
serialized[position++] = 0x1a; // uint32
|
|
1754
|
-
serialized[position++] = id >> 24;
|
|
1755
|
-
serialized[position++] = (id >> 16) & 0xff;
|
|
1756
|
-
serialized[position++] = (id >> 8) & 0xff;
|
|
1757
|
-
serialized[position++] = id & 0xff;
|
|
2033
|
+
serialized[position++] = 0xd8;
|
|
2034
|
+
serialized[position++] = 28; // http://cbor.schmorp.de/value-sharing
|
|
1758
2035
|
lastEnd = offset;
|
|
1759
2036
|
}
|
|
1760
2037
|
return serialized
|
|
1761
2038
|
}
|
|
2039
|
+
function writeBundles(start, encode) {
|
|
2040
|
+
targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start + 1); // the offset to bundle
|
|
2041
|
+
let writeStrings = bundledStrings$1;
|
|
2042
|
+
bundledStrings$1 = null;
|
|
2043
|
+
encode(writeStrings[0]);
|
|
2044
|
+
encode(writeStrings[1]);
|
|
2045
|
+
}
|
|
1762
2046
|
|
|
1763
2047
|
function addExtension$1(extension) {
|
|
1764
2048
|
if (extension.Class) {
|
|
@@ -1772,7 +2056,8 @@ function addExtension$1(extension) {
|
|
|
1772
2056
|
let defaultEncoder = new Encoder({ useRecords: false });
|
|
1773
2057
|
const encode = defaultEncoder.encode;
|
|
1774
2058
|
const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
|
|
1775
|
-
const REUSE_BUFFER_MODE =
|
|
2059
|
+
const REUSE_BUFFER_MODE = 512;
|
|
2060
|
+
const RESET_BUFFER_MODE = 1024;
|
|
1776
2061
|
|
|
1777
2062
|
class EncoderStream extends stream.Transform {
|
|
1778
2063
|
constructor(options) {
|