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/index.js
CHANGED
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
let src;
|
|
12
12
|
let srcEnd;
|
|
13
13
|
let position = 0;
|
|
14
|
-
const
|
|
14
|
+
const LEGACY_RECORD_INLINE_ID = 105;
|
|
15
|
+
const RECORD_DEFINITIONS_ID = 0xdffe;
|
|
16
|
+
const RECORD_INLINE_ID = 0xdfff; // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
|
|
17
|
+
const BUNDLED_STRINGS_ID = 0xdff9;
|
|
15
18
|
const PACKED_REFERENCE_TAG_ID = 6;
|
|
16
19
|
const STOP_CODE = {};
|
|
17
20
|
let currentDecoder = {};
|
|
@@ -19,6 +22,7 @@
|
|
|
19
22
|
let srcString;
|
|
20
23
|
let srcStringStart = 0;
|
|
21
24
|
let srcStringEnd = 0;
|
|
25
|
+
let bundledStrings;
|
|
22
26
|
let referenceMap;
|
|
23
27
|
let currentExtensions = [];
|
|
24
28
|
let currentExtensionRanges = [];
|
|
@@ -34,13 +38,70 @@
|
|
|
34
38
|
class Decoder {
|
|
35
39
|
constructor(options) {
|
|
36
40
|
if (options) {
|
|
41
|
+
if ((options.keyMap || options._keyMap) && !options.useRecords) {
|
|
42
|
+
options.useRecords = false;
|
|
43
|
+
options.mapsAsObjects = true;
|
|
44
|
+
}
|
|
37
45
|
if (options.useRecords === false && options.mapsAsObjects === undefined)
|
|
38
46
|
options.mapsAsObjects = true;
|
|
39
|
-
if (options.getStructures
|
|
47
|
+
if (options.getStructures)
|
|
48
|
+
options.getShared = options.getStructures;
|
|
49
|
+
if (options.getShared && !options.structures)
|
|
40
50
|
(options.structures = []).uninitialized = true; // this is what we use to denote an uninitialized structures
|
|
51
|
+
if (options.keyMap) {
|
|
52
|
+
this.mapKey = new Map();
|
|
53
|
+
for (let [k,v] of Object.entries(options.keyMap)) this.mapKey.set(v,k);
|
|
54
|
+
}
|
|
41
55
|
}
|
|
42
56
|
Object.assign(this, options);
|
|
43
57
|
}
|
|
58
|
+
/*
|
|
59
|
+
decodeKey(key) {
|
|
60
|
+
return this.keyMap
|
|
61
|
+
? Object.keys(this.keyMap)[Object.values(this.keyMap).indexOf(key)] || key
|
|
62
|
+
: key
|
|
63
|
+
}
|
|
64
|
+
*/
|
|
65
|
+
decodeKey(key) {
|
|
66
|
+
return this.keyMap ? this.mapKey.get(key) || key : key
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
encodeKey(key) {
|
|
70
|
+
return this.keyMap && this.keyMap.hasOwnProperty(key) ? this.keyMap[key] : key
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
encodeKeys(rec) {
|
|
74
|
+
if (!this._keyMap) return rec
|
|
75
|
+
let map = new Map();
|
|
76
|
+
for (let [k,v] of Object.entries(rec)) map.set((this._keyMap.hasOwnProperty(k) ? this._keyMap[k] : k), v);
|
|
77
|
+
return map
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
decodeKeys(map) {
|
|
81
|
+
if (!this._keyMap || map.constructor.name != 'Map') return map
|
|
82
|
+
if (!this._mapKey) {
|
|
83
|
+
this._mapKey = new Map();
|
|
84
|
+
for (let [k,v] of Object.entries(this._keyMap)) this._mapKey.set(v,k);
|
|
85
|
+
}
|
|
86
|
+
let res = {};
|
|
87
|
+
//map.forEach((v,k) => res[Object.keys(this._keyMap)[Object.values(this._keyMap).indexOf(k)] || k] = v)
|
|
88
|
+
map.forEach((v,k) => res[this._mapKey.has(k) ? this._mapKey.get(k) : k] = v);
|
|
89
|
+
return res
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
mapDecode(source, end) {
|
|
93
|
+
|
|
94
|
+
let res = this.decode(source);
|
|
95
|
+
if (this._keyMap) {
|
|
96
|
+
//Experiemntal support for Optimised KeyMap decoding
|
|
97
|
+
switch (res.constructor.name) {
|
|
98
|
+
case 'Array': return res.map(r => this.decodeKeys(r))
|
|
99
|
+
//case 'Map': return this.decodeKeys(res)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return res
|
|
103
|
+
}
|
|
104
|
+
|
|
44
105
|
decode(source, end) {
|
|
45
106
|
if (src) {
|
|
46
107
|
// re-entrant execution, save the state and restore it after we do this decode
|
|
@@ -53,6 +114,7 @@
|
|
|
53
114
|
position = 0;
|
|
54
115
|
srcStringEnd = 0;
|
|
55
116
|
srcString = null;
|
|
117
|
+
bundledStrings = null;
|
|
56
118
|
src = source;
|
|
57
119
|
// this provides cached access to the data view for a buffer if it is getting reused, which is a recommend
|
|
58
120
|
// technique for getting data from a database where it can be copied into an existing buffer instead of creating
|
|
@@ -123,6 +185,9 @@
|
|
|
123
185
|
function checkedRead() {
|
|
124
186
|
try {
|
|
125
187
|
let result = read();
|
|
188
|
+
if (bundledStrings) // bundled strings to skip past
|
|
189
|
+
position = bundledStrings.postBundlePosition;
|
|
190
|
+
|
|
126
191
|
if (position == srcEnd) {
|
|
127
192
|
// finished reading this source, cleanup references
|
|
128
193
|
currentStructures = null;
|
|
@@ -197,6 +262,7 @@
|
|
|
197
262
|
switch(majorType) {
|
|
198
263
|
case 2: // byte string
|
|
199
264
|
case 3: // text string
|
|
265
|
+
throw new Error('Indefinite length not supported for byte or text strings')
|
|
200
266
|
case 4: // array
|
|
201
267
|
let array = [];
|
|
202
268
|
let value, i = 0;
|
|
@@ -208,8 +274,8 @@
|
|
|
208
274
|
let key;
|
|
209
275
|
if (currentDecoder.mapsAsObjects) {
|
|
210
276
|
let object = {};
|
|
211
|
-
|
|
212
|
-
|
|
277
|
+
if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[currentDecoder.decodeKey(key)] = read();
|
|
278
|
+
else while ((key = read()) != STOP_CODE) object[key] = read();
|
|
213
279
|
return object
|
|
214
280
|
} else {
|
|
215
281
|
if (restoreMapsAsObject) {
|
|
@@ -217,8 +283,8 @@
|
|
|
217
283
|
restoreMapsAsObject = false;
|
|
218
284
|
}
|
|
219
285
|
let map = new Map();
|
|
220
|
-
|
|
221
|
-
|
|
286
|
+
if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) map.set(currentDecoder.decodeKey(key), read());
|
|
287
|
+
else while ((key = read()) != STOP_CODE) map.set(key, read());
|
|
222
288
|
return map
|
|
223
289
|
}
|
|
224
290
|
case 7:
|
|
@@ -250,16 +316,15 @@
|
|
|
250
316
|
return readFixedString(token)
|
|
251
317
|
case 4: // array
|
|
252
318
|
let array = new Array(token);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
319
|
+
//if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
|
|
320
|
+
//else
|
|
321
|
+
for (let i = 0; i < token; i++) array[i] = read();
|
|
256
322
|
return array
|
|
257
323
|
case 5: // map
|
|
258
324
|
if (currentDecoder.mapsAsObjects) {
|
|
259
325
|
let object = {};
|
|
260
|
-
for (let i = 0; i < token; i++)
|
|
261
|
-
|
|
262
|
-
}
|
|
326
|
+
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[currentDecoder.decodeKey(read())] = read();
|
|
327
|
+
else for (let i = 0; i < token; i++) object[read()] = read();
|
|
263
328
|
return object
|
|
264
329
|
} else {
|
|
265
330
|
if (restoreMapsAsObject) {
|
|
@@ -267,63 +332,66 @@
|
|
|
267
332
|
restoreMapsAsObject = false;
|
|
268
333
|
}
|
|
269
334
|
let map = new Map();
|
|
270
|
-
for (let i = 0; i < token; i++)
|
|
271
|
-
|
|
272
|
-
}
|
|
335
|
+
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
|
|
336
|
+
else for (let i = 0; i < token; i++) map.set(read(), read());
|
|
273
337
|
return map
|
|
274
338
|
}
|
|
275
339
|
case 6: // extension
|
|
276
|
-
if (
|
|
277
|
-
let structure = currentStructures[token &
|
|
340
|
+
if (token >= BUNDLED_STRINGS_ID) {
|
|
341
|
+
let structure = currentStructures[token & 0x1fff]; // check record structures first
|
|
342
|
+
// 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))
|
|
278
343
|
if (structure) {
|
|
279
|
-
if (!structure.read)
|
|
280
|
-
structure.read = createStructureReader(structure);
|
|
344
|
+
if (!structure.read) structure.read = createStructureReader(structure);
|
|
281
345
|
return structure.read()
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (extension.handlesRead)
|
|
305
|
-
return extension(read)
|
|
306
|
-
else
|
|
307
|
-
return extension(read())
|
|
308
|
-
} else {
|
|
309
|
-
let input = read();
|
|
310
|
-
for (let i = 0; i < currentExtensionRanges.length; i++) {
|
|
311
|
-
let value = currentExtensionRanges[i](token, input);
|
|
312
|
-
if (value !== undefined)
|
|
313
|
-
return value
|
|
346
|
+
}
|
|
347
|
+
if (token < 0x10000) {
|
|
348
|
+
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)
|
|
349
|
+
return recordDefinition(read())
|
|
350
|
+
else if (token == RECORD_DEFINITIONS_ID) {
|
|
351
|
+
let length = readJustLength();
|
|
352
|
+
let id = read();
|
|
353
|
+
for (let i = 2; i < length; i++) {
|
|
354
|
+
recordDefinition([id++, read()]);
|
|
355
|
+
}
|
|
356
|
+
return read()
|
|
357
|
+
} else if (token == BUNDLED_STRINGS_ID) {
|
|
358
|
+
return readBundleExt()
|
|
359
|
+
}
|
|
360
|
+
if (currentDecoder.getShared) {
|
|
361
|
+
loadShared();
|
|
362
|
+
structure = currentStructures[token & 0x1fff];
|
|
363
|
+
if (structure) {
|
|
364
|
+
if (!structure.read)
|
|
365
|
+
structure.read = createStructureReader(structure);
|
|
366
|
+
return structure.read()
|
|
367
|
+
}
|
|
314
368
|
}
|
|
315
|
-
return new Tag(input)
|
|
316
369
|
}
|
|
317
370
|
}
|
|
371
|
+
let extension = currentExtensions[token];
|
|
372
|
+
if (extension) {
|
|
373
|
+
if (extension.handlesRead)
|
|
374
|
+
return extension(read)
|
|
375
|
+
else
|
|
376
|
+
return extension(read())
|
|
377
|
+
} else {
|
|
378
|
+
let input = read();
|
|
379
|
+
for (let i = 0; i < currentExtensionRanges.length; i++) {
|
|
380
|
+
let value = currentExtensionRanges[i](token, input);
|
|
381
|
+
if (value !== undefined)
|
|
382
|
+
return value
|
|
383
|
+
}
|
|
384
|
+
return new Tag(input, token)
|
|
385
|
+
}
|
|
318
386
|
case 7: // fixed value
|
|
319
387
|
switch (token) {
|
|
320
388
|
case 0x14: return false
|
|
321
389
|
case 0x15: return true
|
|
322
390
|
case 0x16: return null
|
|
323
391
|
case 0x17: return; // undefined
|
|
324
|
-
case 0x1f:
|
|
392
|
+
case 0x1f:
|
|
325
393
|
default:
|
|
326
|
-
let packedValue = packedValues[token];
|
|
394
|
+
let packedValue = (packedValues || getPackedValues())[token];
|
|
327
395
|
if (packedValue !== undefined)
|
|
328
396
|
return packedValue
|
|
329
397
|
throw new Error('Unknown token ' + token)
|
|
@@ -339,41 +407,47 @@
|
|
|
339
407
|
}
|
|
340
408
|
const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
|
|
341
409
|
function createStructureReader(structure) {
|
|
342
|
-
let l = structure.length;
|
|
343
410
|
function readObject() {
|
|
411
|
+
// get the array size from the header
|
|
412
|
+
let length = src[position++];
|
|
413
|
+
//let majorType = token >> 5
|
|
414
|
+
length = length & 0x1f;
|
|
415
|
+
if (length > 0x17) {
|
|
416
|
+
switch (length) {
|
|
417
|
+
case 0x18:
|
|
418
|
+
length = src[position++];
|
|
419
|
+
break
|
|
420
|
+
case 0x19:
|
|
421
|
+
length = dataView.getUint16(position);
|
|
422
|
+
position += 2;
|
|
423
|
+
break
|
|
424
|
+
case 0x1a:
|
|
425
|
+
length = dataView.getUint32(position);
|
|
426
|
+
position += 4;
|
|
427
|
+
break
|
|
428
|
+
default:
|
|
429
|
+
throw new Error('Expected array header, but got ' + src[position - 1])
|
|
430
|
+
}
|
|
431
|
+
}
|
|
344
432
|
// This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
|
|
345
|
-
if (
|
|
346
|
-
|
|
347
|
-
|
|
433
|
+
if (this.objectLiteralSize === length) // we have a fast object literal reader, use it (assuming it is the right length)
|
|
434
|
+
return this.objectLiteral(read)
|
|
435
|
+
if (this.count++ == 3) { // create a fast reader
|
|
436
|
+
this.objectLiteralSize = length;
|
|
437
|
+
this.objectLiteral = currentDecoder.keyMap
|
|
438
|
+
? new Function('r', 'return {' + this.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
|
|
439
|
+
: new Function('r', 'return {' + this.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}');
|
|
440
|
+
return this.objectLiteral(read)
|
|
348
441
|
}
|
|
349
|
-
readArrayHeader();
|
|
350
442
|
let object = {};
|
|
351
|
-
for (let i = 0; i <
|
|
352
|
-
|
|
353
|
-
object[key] = read();
|
|
354
|
-
}
|
|
443
|
+
if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read();
|
|
444
|
+
else for (let i = 0; i < length; i++) object[this[i]] = read();
|
|
355
445
|
return object
|
|
356
446
|
}
|
|
357
|
-
|
|
447
|
+
structure.count = 0;
|
|
358
448
|
return readObject
|
|
359
449
|
}
|
|
360
450
|
|
|
361
|
-
function readArrayHeader(expectedLength) {
|
|
362
|
-
// consume the array header, TODO: check expected length
|
|
363
|
-
let token = src[position++];
|
|
364
|
-
//let majorType = token >> 5
|
|
365
|
-
token = token & 0x1f;
|
|
366
|
-
if (token > 0x17) {
|
|
367
|
-
switch (token) {
|
|
368
|
-
case 0x18: position++;
|
|
369
|
-
break
|
|
370
|
-
case 0x19: position += 2;
|
|
371
|
-
break
|
|
372
|
-
case 0x1a: position += 4;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
451
|
let readFixedString = readStringJS;
|
|
378
452
|
|
|
379
453
|
let isNativeAccelerationEnabled = false;
|
|
@@ -605,76 +679,15 @@
|
|
|
605
679
|
}
|
|
606
680
|
|
|
607
681
|
let keyCache = new Array(4096);
|
|
608
|
-
function readKey() {
|
|
609
|
-
let length = src[position++];
|
|
610
|
-
if (length >= 0x60 && length < 0x78) {
|
|
611
|
-
// fixstr, potentially use key cache
|
|
612
|
-
length = length - 0x60;
|
|
613
|
-
if (srcStringEnd >= position) // if it has been extracted, must use it (and faster anyway)
|
|
614
|
-
return srcString.slice(position - srcStringStart, (position += length) - srcStringStart)
|
|
615
|
-
else if (!(srcStringEnd == 0 && srcEnd < 180))
|
|
616
|
-
return readFixedString(length)
|
|
617
|
-
} else { // not cacheable, go back and do a standard read
|
|
618
|
-
position--;
|
|
619
|
-
return read()
|
|
620
|
-
}
|
|
621
|
-
let key = ((length << 5) ^ (length > 1 ? dataView.getUint16(position) : length > 0 ? src[position] : 0)) & 0xfff;
|
|
622
|
-
let entry = keyCache[key];
|
|
623
|
-
let checkPosition = position;
|
|
624
|
-
let end = position + length - 3;
|
|
625
|
-
let chunk;
|
|
626
|
-
let i = 0;
|
|
627
|
-
if (entry && entry.bytes == length) {
|
|
628
|
-
while (checkPosition < end) {
|
|
629
|
-
chunk = dataView.getUint32(checkPosition);
|
|
630
|
-
if (chunk != entry[i++]) {
|
|
631
|
-
checkPosition = 0x70000000;
|
|
632
|
-
break
|
|
633
|
-
}
|
|
634
|
-
checkPosition += 4;
|
|
635
|
-
}
|
|
636
|
-
end += 3;
|
|
637
|
-
while (checkPosition < end) {
|
|
638
|
-
chunk = src[checkPosition++];
|
|
639
|
-
if (chunk != entry[i++]) {
|
|
640
|
-
checkPosition = 0x70000000;
|
|
641
|
-
break
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
if (checkPosition === end) {
|
|
645
|
-
position = checkPosition;
|
|
646
|
-
return entry.string
|
|
647
|
-
}
|
|
648
|
-
end -= 3;
|
|
649
|
-
checkPosition = position;
|
|
650
|
-
}
|
|
651
|
-
entry = [];
|
|
652
|
-
keyCache[key] = entry;
|
|
653
|
-
entry.bytes = length;
|
|
654
|
-
while (checkPosition < end) {
|
|
655
|
-
chunk = dataView.getUint32(checkPosition);
|
|
656
|
-
entry.push(chunk);
|
|
657
|
-
checkPosition += 4;
|
|
658
|
-
}
|
|
659
|
-
end += 3;
|
|
660
|
-
while (checkPosition < end) {
|
|
661
|
-
chunk = src[checkPosition++];
|
|
662
|
-
entry.push(chunk);
|
|
663
|
-
}
|
|
664
|
-
// for small blocks, avoiding the overhead of the extract call is helpful
|
|
665
|
-
let string = length < 16 ? shortStringInJS(length) : longStringInJS(length);
|
|
666
|
-
if (string != null)
|
|
667
|
-
return entry.string = string
|
|
668
|
-
return entry.string = readFixedString(length)
|
|
669
|
-
}
|
|
670
682
|
|
|
671
683
|
class Tag {
|
|
672
|
-
constructor(value) {
|
|
684
|
+
constructor(value, tag) {
|
|
673
685
|
this.value = value;
|
|
686
|
+
this.tag = tag;
|
|
674
687
|
}
|
|
675
688
|
}
|
|
676
689
|
|
|
677
|
-
let glbl = typeof
|
|
690
|
+
let glbl = typeof self == 'object' ? self : global;
|
|
678
691
|
|
|
679
692
|
currentExtensions[0] = (dateString) => {
|
|
680
693
|
// string date extension
|
|
@@ -688,38 +701,62 @@
|
|
|
688
701
|
|
|
689
702
|
currentExtensions[2] = (buffer) => {
|
|
690
703
|
// bigint extension
|
|
691
|
-
|
|
704
|
+
let value = BigInt(0);
|
|
705
|
+
for (let i = 0, l = buffer.byteLength; i < l; i++) {
|
|
706
|
+
value = BigInt(buffer[i]) + value << BigInt(8);
|
|
707
|
+
}
|
|
708
|
+
return value
|
|
692
709
|
};
|
|
693
710
|
|
|
694
711
|
currentExtensions[3] = (buffer) => {
|
|
695
712
|
// negative bigint extension
|
|
696
|
-
return BigInt(-1) - (
|
|
713
|
+
return BigInt(-1) - currentExtensions[2](buffer)
|
|
714
|
+
};
|
|
715
|
+
currentExtensions[4] = (fraction) => {
|
|
716
|
+
// best to reparse to maintain accuracy
|
|
717
|
+
return +(fraction[1] + 'e' + fraction[0])
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
currentExtensions[5] = (fraction) => {
|
|
721
|
+
// probably not sufficiently accurate
|
|
722
|
+
return fraction[1] * Math.exp(fraction[0] * Math.log(2))
|
|
697
723
|
};
|
|
698
724
|
|
|
699
725
|
// the registration of the record definition extension (tag 105)
|
|
700
|
-
const recordDefinition = () => {
|
|
701
|
-
let
|
|
702
|
-
let structure = definition[
|
|
703
|
-
|
|
704
|
-
currentStructures[id & 0xff] = structure;
|
|
726
|
+
const recordDefinition = (definition) => {
|
|
727
|
+
let id = definition[0] - 0xe000;
|
|
728
|
+
let structure = definition[1];
|
|
729
|
+
currentStructures[id] = structure;
|
|
705
730
|
structure.read = createStructureReader(structure);
|
|
706
731
|
let object = {};
|
|
707
|
-
for (let i = 2,l = definition.length; i < l; i++) {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
732
|
+
if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
|
|
733
|
+
let key = currentDecoder.decodeKey(structure[i - 2]);
|
|
734
|
+
object[key] = definition[i];
|
|
735
|
+
}
|
|
736
|
+
else for (let i = 2,l = definition.length; i < l; i++) {
|
|
737
|
+
let key = structure[i - 2];
|
|
738
|
+
object[key] = definition[i];
|
|
739
|
+
}
|
|
711
740
|
return object
|
|
712
741
|
};
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
742
|
+
currentExtensions[LEGACY_RECORD_INLINE_ID] = recordDefinition;
|
|
743
|
+
currentExtensions[14] = (value) => {
|
|
744
|
+
if (bundledStrings)
|
|
745
|
+
return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
|
|
746
|
+
return new Tag(value, 14)
|
|
747
|
+
};
|
|
748
|
+
currentExtensions[15] = (value) => {
|
|
749
|
+
if (bundledStrings)
|
|
750
|
+
return bundledStrings[1].slice(bundledStrings.position1, bundledStrings.position1 += value)
|
|
751
|
+
return new Tag(value, 15)
|
|
752
|
+
};
|
|
716
753
|
|
|
717
754
|
currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
|
|
718
755
|
return (glbl[data[0]] || Error)(data[1], data[2])
|
|
719
756
|
};
|
|
720
757
|
const packedTable = (read) => {
|
|
721
758
|
if (src[position++] != 0x84)
|
|
722
|
-
throw new Error('Packed values structure must be followed by 4 element array')
|
|
759
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
723
760
|
let newPackedValues = read(); // packed values
|
|
724
761
|
packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
|
|
725
762
|
packedValues.prefixes = read();
|
|
@@ -730,15 +767,36 @@
|
|
|
730
767
|
currentExtensions[51] = packedTable;
|
|
731
768
|
|
|
732
769
|
currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
|
|
770
|
+
if (!packedValues) {
|
|
771
|
+
if (currentDecoder.getShared)
|
|
772
|
+
loadShared();
|
|
773
|
+
else
|
|
774
|
+
return new Tag(data, PACKED_REFERENCE_TAG_ID)
|
|
775
|
+
}
|
|
733
776
|
if (typeof data == 'number')
|
|
734
777
|
return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
|
|
735
778
|
throw new Error('No support for non-integer packed references yet')
|
|
736
779
|
};
|
|
780
|
+
currentExtensions[25] = (id) => {
|
|
781
|
+
return stringRefs[id]
|
|
782
|
+
};
|
|
783
|
+
currentExtensions[256] = (read) => {
|
|
784
|
+
stringRefs = [];
|
|
785
|
+
try {
|
|
786
|
+
return read()
|
|
787
|
+
} finally {
|
|
788
|
+
stringRefs = null;
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
currentExtensions[256].handlesRead = true;
|
|
737
792
|
|
|
738
|
-
currentExtensions[
|
|
739
|
-
//
|
|
740
|
-
if (!referenceMap)
|
|
793
|
+
currentExtensions[28] = (read) => {
|
|
794
|
+
// shareable http://cbor.schmorp.de/value-sharing (for structured clones)
|
|
795
|
+
if (!referenceMap) {
|
|
741
796
|
referenceMap = new Map();
|
|
797
|
+
referenceMap.id = 0;
|
|
798
|
+
}
|
|
799
|
+
let id = referenceMap.id++;
|
|
742
800
|
let token = src[position];
|
|
743
801
|
let target;
|
|
744
802
|
// TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
|
|
@@ -756,9 +814,10 @@
|
|
|
756
814
|
refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
|
|
757
815
|
return targetProperties // no cycle, can just use the returned read object
|
|
758
816
|
};
|
|
817
|
+
currentExtensions[28].handlesRead = true;
|
|
759
818
|
|
|
760
|
-
currentExtensions[
|
|
761
|
-
//
|
|
819
|
+
currentExtensions[29] = (id) => {
|
|
820
|
+
// sharedref http://cbor.schmorp.de/value-sharing (for structured clones)
|
|
762
821
|
let refEntry = referenceMap.get(id);
|
|
763
822
|
refEntry.used = true;
|
|
764
823
|
return refEntry.target
|
|
@@ -781,19 +840,36 @@
|
|
|
781
840
|
return a.concat(b)
|
|
782
841
|
return Object.assign({}, a, b)
|
|
783
842
|
}
|
|
843
|
+
function getPackedValues() {
|
|
844
|
+
if (!packedValues) {
|
|
845
|
+
if (currentDecoder.getShared)
|
|
846
|
+
loadShared();
|
|
847
|
+
else
|
|
848
|
+
throw new Error('No packed values available')
|
|
849
|
+
}
|
|
850
|
+
return packedValues
|
|
851
|
+
}
|
|
852
|
+
const SHARED_DATA_TAG_ID = 0x53687264; // ascii 'Shrd'
|
|
784
853
|
currentExtensionRanges.push((tag, input) => {
|
|
785
854
|
if (tag >= 225 && tag <= 255)
|
|
786
|
-
return combine(
|
|
855
|
+
return combine(getPackedValues().prefixes[tag - 224], input)
|
|
787
856
|
if (tag >= 28704 && tag <= 32767)
|
|
788
|
-
return combine(
|
|
857
|
+
return combine(getPackedValues().prefixes[tag - 28672], input)
|
|
789
858
|
if (tag >= 1879052288 && tag <= 2147483647)
|
|
790
|
-
return combine(
|
|
859
|
+
return combine(getPackedValues().prefixes[tag - 1879048192], input)
|
|
791
860
|
if (tag >= 216 && tag <= 223)
|
|
792
|
-
return combine(input,
|
|
861
|
+
return combine(input, getPackedValues().suffixes[tag - 216])
|
|
793
862
|
if (tag >= 27647 && tag <= 28671)
|
|
794
|
-
return combine(input,
|
|
863
|
+
return combine(input, getPackedValues().suffixes[tag - 27639])
|
|
795
864
|
if (tag >= 1811940352 && tag <= 1879048191)
|
|
796
|
-
return combine(input,
|
|
865
|
+
return combine(input, getPackedValues().suffixes[tag - 1811939328])
|
|
866
|
+
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)
|
|
867
|
+
return {
|
|
868
|
+
packedValues: packedValues,
|
|
869
|
+
structures: currentStructures.slice(0),
|
|
870
|
+
version: input,
|
|
871
|
+
}
|
|
872
|
+
}
|
|
797
873
|
});
|
|
798
874
|
|
|
799
875
|
const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array');
|
|
@@ -810,6 +886,61 @@
|
|
|
810
886
|
};
|
|
811
887
|
}
|
|
812
888
|
|
|
889
|
+
function readBundleExt() {
|
|
890
|
+
let length = readJustLength();
|
|
891
|
+
let bundlePosition = position + read();
|
|
892
|
+
for (let i = 2; i < length; i++) {
|
|
893
|
+
// skip past bundles that were already read
|
|
894
|
+
let bundleLength = readJustLength(); // this will increment position, so must add to position afterwards
|
|
895
|
+
position += bundleLength;
|
|
896
|
+
}
|
|
897
|
+
let dataPosition = position;
|
|
898
|
+
position = bundlePosition;
|
|
899
|
+
bundledStrings = [readStringJS(readJustLength()), readStringJS(readJustLength())];
|
|
900
|
+
bundledStrings.position0 = 0;
|
|
901
|
+
bundledStrings.position1 = 0;
|
|
902
|
+
bundledStrings.postBundlePosition = position;
|
|
903
|
+
position = dataPosition;
|
|
904
|
+
return read()
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
function readJustLength() {
|
|
908
|
+
let token = src[position++] & 0x1f;
|
|
909
|
+
if (token > 0x17) {
|
|
910
|
+
switch (token) {
|
|
911
|
+
case 0x18:
|
|
912
|
+
token = src[position++];
|
|
913
|
+
break
|
|
914
|
+
case 0x19:
|
|
915
|
+
token = dataView.getUint16(position);
|
|
916
|
+
position += 2;
|
|
917
|
+
break
|
|
918
|
+
case 0x1a:
|
|
919
|
+
token = dataView.getUint32(position);
|
|
920
|
+
position += 4;
|
|
921
|
+
break
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return token
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
function loadShared() {
|
|
928
|
+
if (currentDecoder.getShared) {
|
|
929
|
+
let sharedData = saveState(() => {
|
|
930
|
+
// save the state in case getShared modifies our buffer
|
|
931
|
+
src = null;
|
|
932
|
+
return currentDecoder.getShared()
|
|
933
|
+
}) || {};
|
|
934
|
+
let updatedStructures = sharedData.structures || [];
|
|
935
|
+
currentDecoder.sharedVersion = sharedData.version;
|
|
936
|
+
packedValues = currentDecoder.sharedValues = sharedData.packedValues;
|
|
937
|
+
if (currentStructures === true)
|
|
938
|
+
currentDecoder.structures = currentStructures = updatedStructures;
|
|
939
|
+
else
|
|
940
|
+
currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
813
944
|
function saveState(callback) {
|
|
814
945
|
let savedSrcEnd = srcEnd;
|
|
815
946
|
let savedPosition = position;
|
|
@@ -817,6 +948,8 @@
|
|
|
817
948
|
let savedSrcStringEnd = srcStringEnd;
|
|
818
949
|
let savedSrcString = srcString;
|
|
819
950
|
let savedReferenceMap = referenceMap;
|
|
951
|
+
let savedBundledStrings = bundledStrings;
|
|
952
|
+
|
|
820
953
|
// TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
|
|
821
954
|
let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
|
|
822
955
|
let savedStructures = currentStructures;
|
|
@@ -829,6 +962,7 @@
|
|
|
829
962
|
srcStringEnd = savedSrcStringEnd;
|
|
830
963
|
srcString = savedSrcString;
|
|
831
964
|
referenceMap = savedReferenceMap;
|
|
965
|
+
bundledStrings = savedBundledStrings;
|
|
832
966
|
src = savedSrc;
|
|
833
967
|
sequentialMode = savedSequentialMode;
|
|
834
968
|
currentStructures = savedStructures;
|
|
@@ -875,13 +1009,15 @@
|
|
|
875
1009
|
const hasNodeBuffer = typeof Buffer !== 'undefined';
|
|
876
1010
|
const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
|
|
877
1011
|
const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
|
|
878
|
-
const RECORD_STARTING_ID_PREFIX = 0x69; // tag 105/0x69
|
|
879
1012
|
const MAX_STRUCTURES = 0x100;
|
|
880
1013
|
const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
|
|
881
1014
|
let target;
|
|
882
1015
|
let targetView;
|
|
883
1016
|
let position$1 = 0;
|
|
884
1017
|
let safeEnd;
|
|
1018
|
+
let bundledStrings$1 = null;
|
|
1019
|
+
const MAX_BUNDLE_SIZE = 0xf000;
|
|
1020
|
+
const hasNonLatin = /[\u0080-\uFFFF]/;
|
|
885
1021
|
const RECORD_SYMBOL = Symbol('record-id');
|
|
886
1022
|
class Encoder extends Decoder {
|
|
887
1023
|
constructor(options) {
|
|
@@ -893,7 +1029,6 @@
|
|
|
893
1029
|
let structures;
|
|
894
1030
|
let referenceMap;
|
|
895
1031
|
options = options || {};
|
|
896
|
-
let lastSharedStructuresLength = 0;
|
|
897
1032
|
let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
|
|
898
1033
|
return target.utf8Write(string, position, maxBytes)
|
|
899
1034
|
} : (textEncoder && textEncoder.encodeInto) ?
|
|
@@ -902,12 +1037,14 @@
|
|
|
902
1037
|
} : false;
|
|
903
1038
|
|
|
904
1039
|
let encoder = this;
|
|
905
|
-
let maxSharedStructures =
|
|
1040
|
+
let maxSharedStructures = options.maxSharedStructures || 128;
|
|
906
1041
|
let isSequential = options.sequential;
|
|
907
1042
|
if (isSequential) {
|
|
908
1043
|
maxSharedStructures = 0;
|
|
909
1044
|
this.structures = [];
|
|
910
1045
|
}
|
|
1046
|
+
if (this.saveStructures)
|
|
1047
|
+
this.saveShared = this.saveStructures;
|
|
911
1048
|
let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues;
|
|
912
1049
|
let sharedPackedObjectMap;
|
|
913
1050
|
if (sharedValues) {
|
|
@@ -915,13 +1052,29 @@
|
|
|
915
1052
|
for (let i = 0, l = sharedValues.length; i < l; i++) {
|
|
916
1053
|
sharedPackedObjectMap[sharedValues[i]] = i;
|
|
917
1054
|
}
|
|
918
|
-
|
|
919
1055
|
}
|
|
920
1056
|
let recordIdsToRemove = [];
|
|
921
1057
|
let transitionsCount = 0;
|
|
922
1058
|
let serializationsSinceTransitionRebuild = 0;
|
|
923
|
-
|
|
924
|
-
this.
|
|
1059
|
+
|
|
1060
|
+
this.mapEncode = function(value, encodeOptions) {
|
|
1061
|
+
// Experimental support for premapping keys using _keyMap instad of keyMap - not optiimised yet)
|
|
1062
|
+
if (this._keyMap && !this._mapped) {
|
|
1063
|
+
//console.log('encoding ', value)
|
|
1064
|
+
switch (value.constructor.name) {
|
|
1065
|
+
case 'Array':
|
|
1066
|
+
value = value.map(r => this.encodeKeys(r));
|
|
1067
|
+
break
|
|
1068
|
+
//case 'Map':
|
|
1069
|
+
// value = this.encodeKeys(value)
|
|
1070
|
+
// break
|
|
1071
|
+
}
|
|
1072
|
+
//this._mapped = true
|
|
1073
|
+
}
|
|
1074
|
+
return this.encode(value, encodeOptions)
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
this.encode = function(value, encodeOptions) {
|
|
925
1078
|
if (!target) {
|
|
926
1079
|
target = new ByteArrayAllocate(8192);
|
|
927
1080
|
targetView = new DataView(target.buffer, 0, 8192);
|
|
@@ -938,10 +1091,25 @@
|
|
|
938
1091
|
position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
|
|
939
1092
|
start = position$1;
|
|
940
1093
|
referenceMap = encoder.structuredClone ? new Map() : null;
|
|
1094
|
+
if (encoder.bundleStrings && typeof value !== 'string') {
|
|
1095
|
+
bundledStrings$1 = [];
|
|
1096
|
+
bundledStrings$1.size = Infinity; // force a new bundle start on first string
|
|
1097
|
+
} else
|
|
1098
|
+
bundledStrings$1 = null;
|
|
1099
|
+
|
|
941
1100
|
sharedStructures = encoder.structures;
|
|
942
1101
|
if (sharedStructures) {
|
|
943
|
-
if (sharedStructures.uninitialized)
|
|
944
|
-
|
|
1102
|
+
if (sharedStructures.uninitialized) {
|
|
1103
|
+
let sharedData = encoder.getShared() || {};
|
|
1104
|
+
encoder.structures = sharedStructures = sharedData.structures || [];
|
|
1105
|
+
encoder.sharedVersion = sharedData.version;
|
|
1106
|
+
let sharedValues = encoder.sharedValues = sharedData.packedValues;
|
|
1107
|
+
if (sharedValues) {
|
|
1108
|
+
sharedPackedObjectMap = {};
|
|
1109
|
+
for (let i = 0, l = sharedValues.length; i < l; i++)
|
|
1110
|
+
sharedPackedObjectMap[sharedValues[i]] = i;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
945
1113
|
let sharedStructuresLength = sharedStructures.length;
|
|
946
1114
|
if (sharedStructuresLength > maxSharedStructures && !isSequential)
|
|
947
1115
|
sharedStructuresLength = maxSharedStructures;
|
|
@@ -950,6 +1118,7 @@
|
|
|
950
1118
|
sharedStructures.transitions = Object.create(null);
|
|
951
1119
|
for (let i = 0; i < sharedStructuresLength; i++) {
|
|
952
1120
|
let keys = sharedStructures[i];
|
|
1121
|
+
//console.log('shared struct keys:', keys)
|
|
953
1122
|
if (!keys)
|
|
954
1123
|
continue
|
|
955
1124
|
let nextTransition, transition = sharedStructures.transitions;
|
|
@@ -963,7 +1132,6 @@
|
|
|
963
1132
|
}
|
|
964
1133
|
transition[RECORD_SYMBOL] = i;
|
|
965
1134
|
}
|
|
966
|
-
lastSharedStructuresLength = sharedStructures.length;
|
|
967
1135
|
}
|
|
968
1136
|
if (!isSequential)
|
|
969
1137
|
sharedStructures.nextId = sharedStructuresLength;
|
|
@@ -996,9 +1164,12 @@
|
|
|
996
1164
|
}
|
|
997
1165
|
try {
|
|
998
1166
|
encode(value);
|
|
1167
|
+
if (bundledStrings$1) {
|
|
1168
|
+
writeBundles(start, encode);
|
|
1169
|
+
}
|
|
999
1170
|
encoder.offset = position$1; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
|
|
1000
1171
|
if (referenceMap && referenceMap.idsToInsert) {
|
|
1001
|
-
position$1 += referenceMap.idsToInsert.length *
|
|
1172
|
+
position$1 += referenceMap.idsToInsert.length * 2;
|
|
1002
1173
|
if (position$1 > safeEnd)
|
|
1003
1174
|
makeRoom(position$1);
|
|
1004
1175
|
encoder.offset = position$1;
|
|
@@ -1006,12 +1177,12 @@
|
|
|
1006
1177
|
referenceMap = null;
|
|
1007
1178
|
return serialized
|
|
1008
1179
|
}
|
|
1009
|
-
if (encodeOptions
|
|
1180
|
+
if (encodeOptions & REUSE_BUFFER_MODE) {
|
|
1010
1181
|
target.start = start;
|
|
1011
1182
|
target.end = position$1;
|
|
1012
1183
|
return target
|
|
1013
1184
|
}
|
|
1014
|
-
return target.subarray(start, position$1) // position can change if we call pack again in
|
|
1185
|
+
return target.subarray(start, position$1) // position can change if we call pack again in saveShared, so we get the buffer now
|
|
1015
1186
|
} finally {
|
|
1016
1187
|
if (sharedStructures) {
|
|
1017
1188
|
if (serializationsSinceTransitionRebuild < 10)
|
|
@@ -1029,34 +1200,27 @@
|
|
|
1029
1200
|
}
|
|
1030
1201
|
recordIdsToRemove = [];
|
|
1031
1202
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
// we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
|
|
1037
|
-
let returnBuffer = target.subarray(start, position$1);
|
|
1038
|
-
let shared = encoder.structures || [];
|
|
1039
|
-
if (sharedValues) {
|
|
1040
|
-
shared = shared.concat(sharedValues);
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
if (encoder.saveStructures(encoder.structures, lastSharedStructuresLength) === false) {
|
|
1044
|
-
// get updated structures and try again if the update failed
|
|
1045
|
-
encoder.structures = encoder.getStructures() || [];
|
|
1046
|
-
return encoder.encode(value)
|
|
1047
|
-
}
|
|
1048
|
-
lastSharedStructuresLength = shared.length;
|
|
1049
|
-
return returnBuffer
|
|
1203
|
+
}
|
|
1204
|
+
if (hasSharedUpdate && encoder.saveShared) {
|
|
1205
|
+
if (encoder.structures.length > maxSharedStructures) {
|
|
1206
|
+
encoder.structures = encoder.structures.slice(0, maxSharedStructures);
|
|
1050
1207
|
}
|
|
1208
|
+
// we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
|
|
1209
|
+
let returnBuffer = target.subarray(start, position$1);
|
|
1210
|
+
if (encoder.updateSharedData() === false)
|
|
1211
|
+
return encoder.encode(value) // re-encode if it fails
|
|
1212
|
+
return returnBuffer
|
|
1051
1213
|
}
|
|
1214
|
+
if (encodeOptions & RESET_BUFFER_MODE)
|
|
1215
|
+
position$1 = start;
|
|
1052
1216
|
}
|
|
1053
1217
|
};
|
|
1054
1218
|
this.findCommonStringsToPack = () => {
|
|
1055
1219
|
samplingPackedValues = new Map();
|
|
1056
1220
|
if (!sharedPackedObjectMap)
|
|
1057
1221
|
sharedPackedObjectMap = Object.create(null);
|
|
1058
|
-
return (
|
|
1059
|
-
threshold = threshold || 4;
|
|
1222
|
+
return (options) => {
|
|
1223
|
+
let threshold = options && options.threshold || 4;
|
|
1060
1224
|
let position = this.pack ? options.maxPrivatePackedValues || 16 : 0;
|
|
1061
1225
|
if (!sharedValues)
|
|
1062
1226
|
sharedValues = this.sharedValues = [];
|
|
@@ -1067,6 +1231,7 @@
|
|
|
1067
1231
|
hasSharedUpdate = true;
|
|
1068
1232
|
}
|
|
1069
1233
|
}
|
|
1234
|
+
while (this.saveShared && this.updateSharedData() === false) {}
|
|
1070
1235
|
samplingPackedValues = null;
|
|
1071
1236
|
}
|
|
1072
1237
|
};
|
|
@@ -1115,6 +1280,33 @@
|
|
|
1115
1280
|
}
|
|
1116
1281
|
}
|
|
1117
1282
|
let strLength = value.length;
|
|
1283
|
+
if (bundledStrings$1 && strLength >= 4 && strLength < 0x400) {
|
|
1284
|
+
if ((bundledStrings$1.size += strLength) > MAX_BUNDLE_SIZE) {
|
|
1285
|
+
let extStart;
|
|
1286
|
+
let maxBytes = (bundledStrings$1[0] ? bundledStrings$1[0].length * 3 + bundledStrings$1[1].length : 0) + 10;
|
|
1287
|
+
if (position$1 + maxBytes > safeEnd)
|
|
1288
|
+
target = makeRoom(position$1 + maxBytes);
|
|
1289
|
+
target[position$1++] = 0xd9; // tag 16-bit
|
|
1290
|
+
target[position$1++] = 0xdf; // tag 0xdff9
|
|
1291
|
+
target[position$1++] = 0xf9;
|
|
1292
|
+
// TODO: If we only have one bundle with any string data, only write one string bundle
|
|
1293
|
+
target[position$1++] = bundledStrings$1.position ? 0x84 : 0x82; // array of 4 or 2 elements depending on if we write bundles
|
|
1294
|
+
target[position$1++] = 0x1a; // 32-bit unsigned int
|
|
1295
|
+
extStart = position$1 - start;
|
|
1296
|
+
position$1 += 4; // reserve for writing bundle reference
|
|
1297
|
+
if (bundledStrings$1.position) {
|
|
1298
|
+
writeBundles(start, encode); // write the last bundles
|
|
1299
|
+
}
|
|
1300
|
+
bundledStrings$1 = ['', '']; // create new ones
|
|
1301
|
+
bundledStrings$1.size = 0;
|
|
1302
|
+
bundledStrings$1.position = extStart;
|
|
1303
|
+
}
|
|
1304
|
+
let twoByte = hasNonLatin.test(value);
|
|
1305
|
+
bundledStrings$1[twoByte ? 0 : 1] += value;
|
|
1306
|
+
target[position$1++] = twoByte ? 0xce : 0xcf;
|
|
1307
|
+
encode(strLength);
|
|
1308
|
+
return
|
|
1309
|
+
}
|
|
1118
1310
|
let headerSize;
|
|
1119
1311
|
// first we estimate the header size, so we can write to the correct location
|
|
1120
1312
|
if (strLength < 0x20) {
|
|
@@ -1241,16 +1433,16 @@
|
|
|
1241
1433
|
if (referenceMap) {
|
|
1242
1434
|
let referee = referenceMap.get(value);
|
|
1243
1435
|
if (referee) {
|
|
1244
|
-
|
|
1436
|
+
target[position$1++] = 0xd8;
|
|
1437
|
+
target[position$1++] = 29; // http://cbor.schmorp.de/value-sharing
|
|
1438
|
+
target[position$1++] = 0x19; // 16-bit uint
|
|
1439
|
+
if (!referee.references) {
|
|
1245
1440
|
let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = []);
|
|
1246
|
-
referee.
|
|
1441
|
+
referee.references = [];
|
|
1442
|
+
idsToInsert.push(referee);
|
|
1247
1443
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
target[position$1++] = 40010 & 0xff;
|
|
1251
|
-
target[position$1++] = 0x1a; // uint32
|
|
1252
|
-
targetView.setUint32(position$1, referee.id);
|
|
1253
|
-
position$1 += 4;
|
|
1444
|
+
referee.references.push(position$1 - start);
|
|
1445
|
+
position$1 += 2; // TODO: also support 32-bit
|
|
1254
1446
|
return
|
|
1255
1447
|
} else
|
|
1256
1448
|
referenceMap.set(value, { offset: position$1 - start });
|
|
@@ -1290,16 +1482,23 @@
|
|
|
1290
1482
|
targetView.setUint32(position$1, length);
|
|
1291
1483
|
position$1 += 4;
|
|
1292
1484
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1485
|
+
if (encoder.keyMap) {
|
|
1486
|
+
for (let [ key, entryValue ] of value) {
|
|
1487
|
+
encode(encoder.encodeKey(key));
|
|
1488
|
+
encode(entryValue);
|
|
1489
|
+
}
|
|
1490
|
+
} else {
|
|
1491
|
+
for (let [ key, entryValue ] of value) {
|
|
1492
|
+
encode(key);
|
|
1493
|
+
encode(entryValue);
|
|
1494
|
+
}
|
|
1296
1495
|
}
|
|
1297
|
-
} else {
|
|
1496
|
+
} else {
|
|
1298
1497
|
for (let i = 0, l = extensions.length; i < l; i++) {
|
|
1299
1498
|
let extensionClass = extensionClasses[i];
|
|
1300
1499
|
if (value instanceof extensionClass) {
|
|
1301
1500
|
let extension = extensions[i];
|
|
1302
|
-
let tag = extension.tag;
|
|
1501
|
+
let tag = extension.tag || extension.getTag && extension.getTag(value);
|
|
1303
1502
|
if (tag < 0x18) {
|
|
1304
1503
|
target[position$1++] = 0xc0 | tag;
|
|
1305
1504
|
} else if (tag < 0x100) {
|
|
@@ -1361,6 +1560,7 @@
|
|
|
1361
1560
|
const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
|
|
1362
1561
|
// this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
|
|
1363
1562
|
let keys = Object.keys(object);
|
|
1563
|
+
let vals = Object.values(object);
|
|
1364
1564
|
let length = keys.length;
|
|
1365
1565
|
if (length < 0x18) {
|
|
1366
1566
|
target[position$1++] = 0xa0 | length;
|
|
@@ -1376,10 +1576,16 @@
|
|
|
1376
1576
|
targetView.setUint32(position$1, length);
|
|
1377
1577
|
position$1 += 4;
|
|
1378
1578
|
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1579
|
+
if (encoder.keyMap) {
|
|
1580
|
+
for (let i = 0; i < length; i++) {
|
|
1581
|
+
encode(encodeKey(keys[i]));
|
|
1582
|
+
encode(vals[i]);
|
|
1583
|
+
}
|
|
1584
|
+
} else {
|
|
1585
|
+
for (let i = 0; i < length; i++) {
|
|
1586
|
+
encode(keys[i]);
|
|
1587
|
+
encode(vals[i]);
|
|
1588
|
+
}
|
|
1383
1589
|
}
|
|
1384
1590
|
} :
|
|
1385
1591
|
(object, safePrototype) => {
|
|
@@ -1387,12 +1593,18 @@
|
|
|
1387
1593
|
let objectOffset = position$1 - start;
|
|
1388
1594
|
position$1 += 2;
|
|
1389
1595
|
let size = 0;
|
|
1390
|
-
|
|
1391
|
-
if (safePrototype || object.hasOwnProperty(key)) {
|
|
1392
|
-
encode(key);
|
|
1596
|
+
if (encoder.keyMap) {
|
|
1597
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
1598
|
+
encode(encoder.encodeKey(key));
|
|
1393
1599
|
encode(object[key]);
|
|
1394
1600
|
size++;
|
|
1395
1601
|
}
|
|
1602
|
+
} else {
|
|
1603
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
1604
|
+
encode(key);
|
|
1605
|
+
encode(object[key]);
|
|
1606
|
+
size++;
|
|
1607
|
+
}
|
|
1396
1608
|
}
|
|
1397
1609
|
target[objectOffset++ + start] = size >> 8;
|
|
1398
1610
|
target[objectOffset + start] = size & 0xff;
|
|
@@ -1440,11 +1652,16 @@
|
|
|
1440
1652
|
}*/
|
|
1441
1653
|
(object) => {
|
|
1442
1654
|
let keys = Object.keys(object);
|
|
1655
|
+
let vals = Object.values(object);
|
|
1656
|
+
if (this.keyMap) keys = keys.map(k => this.encodeKey(k));
|
|
1443
1657
|
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
|
|
1444
1658
|
let newTransitions = 0;
|
|
1445
1659
|
let length = keys.length;
|
|
1660
|
+
//let parentRecordId
|
|
1446
1661
|
for (let i = 0; i < length; i++) {
|
|
1662
|
+
//if (!parentRecordId)
|
|
1447
1663
|
let key = keys[i];
|
|
1664
|
+
// parentRecordId = transition[RECORD_SYMBOL]
|
|
1448
1665
|
nextTransition = transition[key];
|
|
1449
1666
|
if (!nextTransition) {
|
|
1450
1667
|
nextTransition = transition[key] = Object.create(null);
|
|
@@ -1454,9 +1671,9 @@
|
|
|
1454
1671
|
}
|
|
1455
1672
|
let recordId = transition[RECORD_SYMBOL];
|
|
1456
1673
|
if (recordId !== undefined) {
|
|
1457
|
-
target[position$1++] = 0xd9;
|
|
1458
|
-
target[position$1++] =
|
|
1459
|
-
target[position$1++] = recordId;
|
|
1674
|
+
target[position$1++] = 0xd9;
|
|
1675
|
+
target[position$1++] = (recordId >> 8) | 0xe0;
|
|
1676
|
+
target[position$1++] = recordId & 0xff;
|
|
1460
1677
|
} else {
|
|
1461
1678
|
recordId = structures.nextId++;
|
|
1462
1679
|
if (!recordId) {
|
|
@@ -1469,13 +1686,13 @@
|
|
|
1469
1686
|
transition[RECORD_SYMBOL] = recordId;
|
|
1470
1687
|
structures[recordId] = keys;
|
|
1471
1688
|
if (sharedStructures && sharedStructures.length <= maxSharedStructures) {
|
|
1472
|
-
target[position$1++] = 0xd9;
|
|
1473
|
-
target[position$1++] =
|
|
1474
|
-
target[position$1++] = recordId
|
|
1689
|
+
target[position$1++] = 0xd9;
|
|
1690
|
+
target[position$1++] = (recordId >> 8) | 0xe0;
|
|
1691
|
+
target[position$1++] = recordId & 0xff;
|
|
1475
1692
|
hasSharedUpdate = true;
|
|
1476
1693
|
} else {
|
|
1477
|
-
|
|
1478
|
-
|
|
1694
|
+
targetView.setUint32(position$1, 0xd9dfff00); // tag two byte, then record definition id
|
|
1695
|
+
position$1 += 3;
|
|
1479
1696
|
if (newTransitions)
|
|
1480
1697
|
transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
|
|
1481
1698
|
// record the removal of the id, we can maintain our shared structure
|
|
@@ -1483,13 +1700,9 @@
|
|
|
1483
1700
|
recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined; // we are cycling back through, and have to remove old ones
|
|
1484
1701
|
recordIdsToRemove.push(transition);
|
|
1485
1702
|
writeArrayHeader(length + 2);
|
|
1703
|
+
encode(0xe000 + recordId);
|
|
1486
1704
|
encode(keys);
|
|
1487
|
-
|
|
1488
|
-
target[position$1++] = RECORD_STARTING_ID_PREFIX;
|
|
1489
|
-
target[position$1++] = recordId;
|
|
1490
|
-
// now write the values
|
|
1491
|
-
for (let i =0; i < length; i++)
|
|
1492
|
-
encode(object[keys[i]]);
|
|
1705
|
+
for (let v of Object.values(object)) encode(v);
|
|
1493
1706
|
return
|
|
1494
1707
|
}
|
|
1495
1708
|
}
|
|
@@ -1498,8 +1711,7 @@
|
|
|
1498
1711
|
} else {
|
|
1499
1712
|
writeArrayHeader(length);
|
|
1500
1713
|
}
|
|
1501
|
-
for (let i =0; i < length; i++)
|
|
1502
|
-
encode(object[keys[i]]);
|
|
1714
|
+
for (let i =0; i < length; i++) encode(vals[i]);
|
|
1503
1715
|
};
|
|
1504
1716
|
const makeRoom = (end) => {
|
|
1505
1717
|
let newSize;
|
|
@@ -1529,6 +1741,33 @@
|
|
|
1529
1741
|
targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
|
|
1530
1742
|
position$1 = 0;
|
|
1531
1743
|
}
|
|
1744
|
+
clearSharedData() {
|
|
1745
|
+
if (this.structures)
|
|
1746
|
+
this.structures = [];
|
|
1747
|
+
if (this.sharedValues)
|
|
1748
|
+
this.sharedValues = undefined;
|
|
1749
|
+
}
|
|
1750
|
+
updateSharedData() {
|
|
1751
|
+
let lastVersion = this.sharedVersion || 0;
|
|
1752
|
+
this.sharedVersion = lastVersion + 1;
|
|
1753
|
+
let saveResults = this.saveShared(new SharedData(this.structures, this.sharedValues, this.sharedVersion),
|
|
1754
|
+
existingShared => (existingShared && existingShared.version || 0) == lastVersion);
|
|
1755
|
+
if (saveResults === false) {
|
|
1756
|
+
// get updated structures and try again if the update failed
|
|
1757
|
+
let sharedData = this.getShared() || {};
|
|
1758
|
+
this.structures = sharedData.structures || [];
|
|
1759
|
+
this.sharedValues = sharedData.packedValues;
|
|
1760
|
+
this.sharedVersion = sharedData.version;
|
|
1761
|
+
}
|
|
1762
|
+
return saveResults
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
class SharedData {
|
|
1766
|
+
constructor(structures, values, version) {
|
|
1767
|
+
this.structures = structures;
|
|
1768
|
+
this.packedValues = values;
|
|
1769
|
+
this.version = version;
|
|
1770
|
+
}
|
|
1532
1771
|
}
|
|
1533
1772
|
|
|
1534
1773
|
function writeArrayHeader(length) {
|
|
@@ -1598,11 +1837,11 @@
|
|
|
1598
1837
|
}
|
|
1599
1838
|
}
|
|
1600
1839
|
|
|
1601
|
-
extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
|
|
1840
|
+
extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
|
|
1602
1841
|
Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
|
|
1603
1842
|
typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
|
|
1604
1843
|
typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
|
|
1605
|
-
Float32Array, Float64Array];
|
|
1844
|
+
Float32Array, Float64Array, SharedData];
|
|
1606
1845
|
|
|
1607
1846
|
//Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
|
|
1608
1847
|
extensions = [{
|
|
@@ -1637,6 +1876,13 @@
|
|
|
1637
1876
|
encode(regex, encode) {
|
|
1638
1877
|
encode([ 'RegExp', regex.source, regex.flags ]);
|
|
1639
1878
|
}
|
|
1879
|
+
}, {
|
|
1880
|
+
getTag(tag) {
|
|
1881
|
+
return tag.tag
|
|
1882
|
+
},
|
|
1883
|
+
encode(tag, encode) {
|
|
1884
|
+
encode(tag.value);
|
|
1885
|
+
}
|
|
1640
1886
|
}, {
|
|
1641
1887
|
encode(arrayBuffer, encode, makeRoom) {
|
|
1642
1888
|
writeBuffer(arrayBuffer, makeRoom);
|
|
@@ -1655,7 +1901,35 @@
|
|
|
1655
1901
|
typedArrayEncoder(78),
|
|
1656
1902
|
typedArrayEncoder(79),
|
|
1657
1903
|
typedArrayEncoder(81),
|
|
1658
|
-
typedArrayEncoder(82)
|
|
1904
|
+
typedArrayEncoder(82),
|
|
1905
|
+
{
|
|
1906
|
+
encode(sharedData, encode) { // write SharedData
|
|
1907
|
+
let packedValues = sharedData.packedValues || [];
|
|
1908
|
+
let sharedStructures = sharedData.structures || [];
|
|
1909
|
+
if (packedValues.values.length > 0) {
|
|
1910
|
+
target[position$1++] = 0xd8; // one-byte tag
|
|
1911
|
+
target[position$1++] = 51; // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
|
|
1912
|
+
writeArrayHeader(4);
|
|
1913
|
+
let valuesArray = packedValues.values;
|
|
1914
|
+
encode(valuesArray);
|
|
1915
|
+
writeArrayHeader(0); // prefixes
|
|
1916
|
+
writeArrayHeader(0); // suffixes
|
|
1917
|
+
packedObjectMap = Object.create(sharedPackedObjectMap || null);
|
|
1918
|
+
for (let i = 0, l = valuesArray.length; i < l; i++) {
|
|
1919
|
+
packedObjectMap[valuesArray[i]] = i;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
if (sharedStructures) {
|
|
1923
|
+
targetView.setUint32(position$1, 0xd9dffe00);
|
|
1924
|
+
position$1 += 3;
|
|
1925
|
+
let definitions = sharedStructures.slice(0);
|
|
1926
|
+
definitions.unshift(0xe000);
|
|
1927
|
+
definitions.push(new Tag(sharedData.version, 0x53687264));
|
|
1928
|
+
encode(definitions);
|
|
1929
|
+
} else
|
|
1930
|
+
encode(new Tag(sharedData.version, 0x53687264));
|
|
1931
|
+
}
|
|
1932
|
+
}];
|
|
1659
1933
|
|
|
1660
1934
|
function typedArrayEncoder(tag) {
|
|
1661
1935
|
return {
|
|
@@ -1695,27 +1969,35 @@
|
|
|
1695
1969
|
function insertIds(serialized, idsToInsert) {
|
|
1696
1970
|
// insert the ids that need to be referenced for structured clones
|
|
1697
1971
|
let nextId;
|
|
1698
|
-
let distanceToMove = idsToInsert.length *
|
|
1972
|
+
let distanceToMove = idsToInsert.length * 2;
|
|
1699
1973
|
let lastEnd = serialized.length - distanceToMove;
|
|
1700
1974
|
idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1);
|
|
1975
|
+
for (let id = 0; id < idsToInsert.length; id++) {
|
|
1976
|
+
let referee = idsToInsert[id];
|
|
1977
|
+
referee.id = id;
|
|
1978
|
+
for (let position of referee.references) {
|
|
1979
|
+
serialized[position++] = id >> 8;
|
|
1980
|
+
serialized[position] = id & 0xff;
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1701
1983
|
while (nextId = idsToInsert.pop()) {
|
|
1702
1984
|
let offset = nextId.offset;
|
|
1703
|
-
let id = nextId.id;
|
|
1704
1985
|
serialized.copyWithin(offset + distanceToMove, offset, lastEnd);
|
|
1705
|
-
distanceToMove -=
|
|
1986
|
+
distanceToMove -= 2;
|
|
1706
1987
|
let position = offset + distanceToMove;
|
|
1707
|
-
serialized[position++] =
|
|
1708
|
-
serialized[position++] =
|
|
1709
|
-
serialized[position++] = 40009 & 0xff;
|
|
1710
|
-
serialized[position++] = 0x1a; // uint32
|
|
1711
|
-
serialized[position++] = id >> 24;
|
|
1712
|
-
serialized[position++] = (id >> 16) & 0xff;
|
|
1713
|
-
serialized[position++] = (id >> 8) & 0xff;
|
|
1714
|
-
serialized[position++] = id & 0xff;
|
|
1988
|
+
serialized[position++] = 0xd8;
|
|
1989
|
+
serialized[position++] = 28; // http://cbor.schmorp.de/value-sharing
|
|
1715
1990
|
lastEnd = offset;
|
|
1716
1991
|
}
|
|
1717
1992
|
return serialized
|
|
1718
1993
|
}
|
|
1994
|
+
function writeBundles(start, encode) {
|
|
1995
|
+
targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start + 1); // the offset to bundle
|
|
1996
|
+
let writeStrings = bundledStrings$1;
|
|
1997
|
+
bundledStrings$1 = null;
|
|
1998
|
+
encode(writeStrings[0]);
|
|
1999
|
+
encode(writeStrings[1]);
|
|
2000
|
+
}
|
|
1719
2001
|
|
|
1720
2002
|
function addExtension$1(extension) {
|
|
1721
2003
|
if (extension.Class) {
|
|
@@ -1729,7 +2011,8 @@
|
|
|
1729
2011
|
let defaultEncoder = new Encoder({ useRecords: false });
|
|
1730
2012
|
const encode = defaultEncoder.encode;
|
|
1731
2013
|
const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
|
|
1732
|
-
const REUSE_BUFFER_MODE =
|
|
2014
|
+
const REUSE_BUFFER_MODE = 512;
|
|
2015
|
+
const RESET_BUFFER_MODE = 1024;
|
|
1733
2016
|
|
|
1734
2017
|
/**
|
|
1735
2018
|
* Given an Iterable first argument, returns an Iterable where each value is encoded as a Buffer
|
|
@@ -1814,9 +2097,6 @@
|
|
|
1814
2097
|
}
|
|
1815
2098
|
}
|
|
1816
2099
|
|
|
1817
|
-
const useRecords = false;
|
|
1818
|
-
const mapsAsObjects = true;
|
|
1819
|
-
|
|
1820
2100
|
exports.ALWAYS = ALWAYS;
|
|
1821
2101
|
exports.DECIMAL_FIT = DECIMAL_FIT;
|
|
1822
2102
|
exports.DECIMAL_ROUND = DECIMAL_ROUND;
|
|
@@ -1834,9 +2114,7 @@
|
|
|
1834
2114
|
exports.encode = encode;
|
|
1835
2115
|
exports.encodeIter = encodeIter;
|
|
1836
2116
|
exports.isNativeAccelerationEnabled = isNativeAccelerationEnabled;
|
|
1837
|
-
exports.mapsAsObjects = mapsAsObjects;
|
|
1838
2117
|
exports.roundFloat32 = roundFloat32;
|
|
1839
|
-
exports.useRecords = useRecords;
|
|
1840
2118
|
|
|
1841
2119
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
1842
2120
|
|