cbor-x 0.9.4 → 1.2.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 +55 -21
- package/decode.d.ts +3 -0
- package/decode.js +355 -119
- package/dist/index.js +670 -342
- package/dist/index.min.js +99 -108
- package/dist/node-index.js +93 -0
- package/dist/node.cjs +672 -337
- package/dist/test.js +100 -8
- package/encode.d.ts +1 -1
- package/encode.js +312 -155
- package/index.js +0 -2
- package/package.json +13 -4
- package/.vscode/launch.json +0 -22
- 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;
|
|
@@ -191,7 +256,11 @@ function read() {
|
|
|
191
256
|
position += 8;
|
|
192
257
|
return value
|
|
193
258
|
}
|
|
194
|
-
if (
|
|
259
|
+
if (majorType > 1) {
|
|
260
|
+
if (dataView.getUint32(position) > 0)
|
|
261
|
+
throw new Error('JavaScript does not support arrays, maps, or strings with length over 4294967295')
|
|
262
|
+
token = dataView.getUint32(position + 4);
|
|
263
|
+
} else if (currentDecoder.int64AsNumber) {
|
|
195
264
|
token = dataView.getUint32(position) * 0x100000000;
|
|
196
265
|
token += dataView.getUint32(position + 4);
|
|
197
266
|
} else
|
|
@@ -203,6 +272,7 @@ function read() {
|
|
|
203
272
|
switch(majorType) {
|
|
204
273
|
case 2: // byte string
|
|
205
274
|
case 3: // text string
|
|
275
|
+
throw new Error('Indefinite length not supported for byte or text strings')
|
|
206
276
|
case 4: // array
|
|
207
277
|
let array = [];
|
|
208
278
|
let value, i = 0;
|
|
@@ -214,8 +284,8 @@ function read() {
|
|
|
214
284
|
let key;
|
|
215
285
|
if (currentDecoder.mapsAsObjects) {
|
|
216
286
|
let object = {};
|
|
217
|
-
|
|
218
|
-
|
|
287
|
+
if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[currentDecoder.decodeKey(key)] = read();
|
|
288
|
+
else while ((key = read()) != STOP_CODE) object[key] = read();
|
|
219
289
|
return object
|
|
220
290
|
} else {
|
|
221
291
|
if (restoreMapsAsObject) {
|
|
@@ -223,8 +293,8 @@ function read() {
|
|
|
223
293
|
restoreMapsAsObject = false;
|
|
224
294
|
}
|
|
225
295
|
let map = new Map();
|
|
226
|
-
|
|
227
|
-
|
|
296
|
+
if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) map.set(currentDecoder.decodeKey(key), read());
|
|
297
|
+
else while ((key = read()) != STOP_CODE) map.set(key, read());
|
|
228
298
|
return map
|
|
229
299
|
}
|
|
230
300
|
case 7:
|
|
@@ -256,16 +326,15 @@ function read() {
|
|
|
256
326
|
return readFixedString(token)
|
|
257
327
|
case 4: // array
|
|
258
328
|
let array = new Array(token);
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
329
|
+
//if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
|
|
330
|
+
//else
|
|
331
|
+
for (let i = 0; i < token; i++) array[i] = read();
|
|
262
332
|
return array
|
|
263
333
|
case 5: // map
|
|
264
334
|
if (currentDecoder.mapsAsObjects) {
|
|
265
335
|
let object = {};
|
|
266
|
-
for (let i = 0; i < token; i++)
|
|
267
|
-
|
|
268
|
-
}
|
|
336
|
+
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[currentDecoder.decodeKey(read())] = read();
|
|
337
|
+
else for (let i = 0; i < token; i++) object[read()] = read();
|
|
269
338
|
return object
|
|
270
339
|
} else {
|
|
271
340
|
if (restoreMapsAsObject) {
|
|
@@ -273,63 +342,66 @@ function read() {
|
|
|
273
342
|
restoreMapsAsObject = false;
|
|
274
343
|
}
|
|
275
344
|
let map = new Map();
|
|
276
|
-
for (let i = 0; i < token; i++)
|
|
277
|
-
|
|
278
|
-
}
|
|
345
|
+
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
|
|
346
|
+
else for (let i = 0; i < token; i++) map.set(read(), read());
|
|
279
347
|
return map
|
|
280
348
|
}
|
|
281
349
|
case 6: // extension
|
|
282
|
-
if (
|
|
283
|
-
let structure = currentStructures[token &
|
|
350
|
+
if (token >= BUNDLED_STRINGS_ID) {
|
|
351
|
+
let structure = currentStructures[token & 0x1fff]; // check record structures first
|
|
352
|
+
// 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
353
|
if (structure) {
|
|
285
|
-
if (!structure.read)
|
|
286
|
-
structure.read = createStructureReader(structure);
|
|
354
|
+
if (!structure.read) structure.read = createStructureReader(structure);
|
|
287
355
|
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
|
|
356
|
+
}
|
|
357
|
+
if (token < 0x10000) {
|
|
358
|
+
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)
|
|
359
|
+
return recordDefinition(read())
|
|
360
|
+
else if (token == RECORD_DEFINITIONS_ID) {
|
|
361
|
+
let length = readJustLength();
|
|
362
|
+
let id = read();
|
|
363
|
+
for (let i = 2; i < length; i++) {
|
|
364
|
+
recordDefinition([id++, read()]);
|
|
365
|
+
}
|
|
366
|
+
return read()
|
|
367
|
+
} else if (token == BUNDLED_STRINGS_ID) {
|
|
368
|
+
return readBundleExt()
|
|
369
|
+
}
|
|
370
|
+
if (currentDecoder.getShared) {
|
|
371
|
+
loadShared();
|
|
372
|
+
structure = currentStructures[token & 0x1fff];
|
|
373
|
+
if (structure) {
|
|
374
|
+
if (!structure.read)
|
|
375
|
+
structure.read = createStructureReader(structure);
|
|
376
|
+
return structure.read()
|
|
377
|
+
}
|
|
320
378
|
}
|
|
321
|
-
return new Tag(input)
|
|
322
379
|
}
|
|
323
380
|
}
|
|
381
|
+
let extension = currentExtensions[token];
|
|
382
|
+
if (extension) {
|
|
383
|
+
if (extension.handlesRead)
|
|
384
|
+
return extension(read)
|
|
385
|
+
else
|
|
386
|
+
return extension(read())
|
|
387
|
+
} else {
|
|
388
|
+
let input = read();
|
|
389
|
+
for (let i = 0; i < currentExtensionRanges.length; i++) {
|
|
390
|
+
let value = currentExtensionRanges[i](token, input);
|
|
391
|
+
if (value !== undefined)
|
|
392
|
+
return value
|
|
393
|
+
}
|
|
394
|
+
return new Tag(input, token)
|
|
395
|
+
}
|
|
324
396
|
case 7: // fixed value
|
|
325
397
|
switch (token) {
|
|
326
398
|
case 0x14: return false
|
|
327
399
|
case 0x15: return true
|
|
328
400
|
case 0x16: return null
|
|
329
401
|
case 0x17: return; // undefined
|
|
330
|
-
case 0x1f:
|
|
402
|
+
case 0x1f:
|
|
331
403
|
default:
|
|
332
|
-
let packedValue = packedValues[token];
|
|
404
|
+
let packedValue = (packedValues || getPackedValues())[token];
|
|
333
405
|
if (packedValue !== undefined)
|
|
334
406
|
return packedValue
|
|
335
407
|
throw new Error('Unknown token ' + token)
|
|
@@ -345,41 +417,56 @@ function read() {
|
|
|
345
417
|
}
|
|
346
418
|
const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
|
|
347
419
|
function createStructureReader(structure) {
|
|
348
|
-
let l = structure.length;
|
|
349
420
|
function readObject() {
|
|
421
|
+
// get the array size from the header
|
|
422
|
+
let length = src[position++];
|
|
423
|
+
//let majorType = token >> 5
|
|
424
|
+
length = length & 0x1f;
|
|
425
|
+
if (length > 0x17) {
|
|
426
|
+
switch (length) {
|
|
427
|
+
case 0x18:
|
|
428
|
+
length = src[position++];
|
|
429
|
+
break
|
|
430
|
+
case 0x19:
|
|
431
|
+
length = dataView.getUint16(position);
|
|
432
|
+
position += 2;
|
|
433
|
+
break
|
|
434
|
+
case 0x1a:
|
|
435
|
+
length = dataView.getUint32(position);
|
|
436
|
+
position += 4;
|
|
437
|
+
break
|
|
438
|
+
default:
|
|
439
|
+
throw new Error('Expected array header, but got ' + src[position - 1])
|
|
440
|
+
}
|
|
441
|
+
}
|
|
350
442
|
// This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
443
|
+
let compiledReader = this.compiledReader; // first look to see if we have the fast compiled function
|
|
444
|
+
while(compiledReader) {
|
|
445
|
+
// we have a fast compiled object literal reader
|
|
446
|
+
if (compiledReader.propertyCount === length)
|
|
447
|
+
return compiledReader(read) // with the right length, so we use it
|
|
448
|
+
compiledReader = compiledReader.next; // see if there is another reader with the right length
|
|
354
449
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
450
|
+
if (this.slowReads++ >= 3) { // create a fast compiled reader
|
|
451
|
+
let array = this.length == length ? this : this.slice(0, length);
|
|
452
|
+
compiledReader = currentDecoder.keyMap
|
|
453
|
+
? new Function('r', 'return {' + array.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
|
|
454
|
+
: new Function('r', 'return {' + array.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}');
|
|
455
|
+
if (this.compiledReader)
|
|
456
|
+
compiledReader.next = this.compiledReader; // if there is an existing one, we store multiple readers as a linked list because it is usually pretty rare to have multiple readers (of different length) for the same structure
|
|
457
|
+
compiledReader.propertyCount = length;
|
|
458
|
+
this.compiledReader = compiledReader;
|
|
459
|
+
return compiledReader(read)
|
|
360
460
|
}
|
|
461
|
+
let object = {};
|
|
462
|
+
if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read();
|
|
463
|
+
else for (let i = 0; i < length; i++) object[this[i]] = read();
|
|
361
464
|
return object
|
|
362
465
|
}
|
|
363
|
-
|
|
466
|
+
structure.slowReads = 0;
|
|
364
467
|
return readObject
|
|
365
468
|
}
|
|
366
469
|
|
|
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
470
|
let readFixedString = readStringJS;
|
|
384
471
|
|
|
385
472
|
exports.isNativeAccelerationEnabled = false;
|
|
@@ -390,6 +477,8 @@ function setExtractor(extractStrings) {
|
|
|
390
477
|
return function readString(length) {
|
|
391
478
|
let string = strings[stringPosition++];
|
|
392
479
|
if (string == null) {
|
|
480
|
+
if (bundledStrings)
|
|
481
|
+
return readStringJS(length)
|
|
393
482
|
let extraction = extractStrings(position, srcEnd, length, src);
|
|
394
483
|
if (typeof extraction == 'string') {
|
|
395
484
|
string = extraction;
|
|
@@ -644,76 +733,15 @@ function getFloat16() {
|
|
|
644
733
|
}
|
|
645
734
|
|
|
646
735
|
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
736
|
|
|
710
737
|
class Tag {
|
|
711
|
-
constructor(value) {
|
|
738
|
+
constructor(value, tag) {
|
|
712
739
|
this.value = value;
|
|
740
|
+
this.tag = tag;
|
|
713
741
|
}
|
|
714
742
|
}
|
|
715
743
|
|
|
716
|
-
let glbl = typeof
|
|
744
|
+
let glbl = typeof self == 'object' ? self : global;
|
|
717
745
|
|
|
718
746
|
currentExtensions[0] = (dateString) => {
|
|
719
747
|
// string date extension
|
|
@@ -727,38 +755,67 @@ currentExtensions[1] = (epochSec) => {
|
|
|
727
755
|
|
|
728
756
|
currentExtensions[2] = (buffer) => {
|
|
729
757
|
// bigint extension
|
|
730
|
-
|
|
758
|
+
let value = BigInt(0);
|
|
759
|
+
for (let i = 0, l = buffer.byteLength; i < l; i++) {
|
|
760
|
+
value = BigInt(buffer[i]) + value << BigInt(8);
|
|
761
|
+
}
|
|
762
|
+
return value
|
|
731
763
|
};
|
|
732
764
|
|
|
733
765
|
currentExtensions[3] = (buffer) => {
|
|
734
766
|
// negative bigint extension
|
|
735
|
-
return BigInt(-1) - (
|
|
767
|
+
return BigInt(-1) - currentExtensions[2](buffer)
|
|
768
|
+
};
|
|
769
|
+
currentExtensions[4] = (fraction) => {
|
|
770
|
+
// best to reparse to maintain accuracy
|
|
771
|
+
return +(fraction[1] + 'e' + fraction[0])
|
|
736
772
|
};
|
|
737
773
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
774
|
+
currentExtensions[5] = (fraction) => {
|
|
775
|
+
// probably not sufficiently accurate
|
|
776
|
+
return fraction[1] * Math.exp(fraction[0] * Math.log(2))
|
|
777
|
+
};
|
|
778
|
+
|
|
779
|
+
// the registration of the record definition extension
|
|
780
|
+
const recordDefinition = (definition) => {
|
|
781
|
+
let id = definition[0] - 0xe000;
|
|
782
|
+
let structure = definition[1];
|
|
783
|
+
let existingStructure = currentStructures[id];
|
|
784
|
+
if (existingStructure && existingStructure.isShared) {
|
|
785
|
+
(currentStructures.restoreStructures || (currentStructures.restoreStructures = []))[id] = existingStructure;
|
|
786
|
+
}
|
|
787
|
+
currentStructures[id] = structure;
|
|
788
|
+
|
|
744
789
|
structure.read = createStructureReader(structure);
|
|
745
790
|
let object = {};
|
|
746
|
-
for (let i = 2,l = definition.length; i < l; i++) {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
791
|
+
if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
|
|
792
|
+
let key = currentDecoder.decodeKey(structure[i - 2]);
|
|
793
|
+
object[key] = definition[i];
|
|
794
|
+
}
|
|
795
|
+
else for (let i = 2,l = definition.length; i < l; i++) {
|
|
796
|
+
let key = structure[i - 2];
|
|
797
|
+
object[key] = definition[i];
|
|
798
|
+
}
|
|
750
799
|
return object
|
|
751
800
|
};
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
801
|
+
currentExtensions[LEGACY_RECORD_INLINE_ID] = recordDefinition;
|
|
802
|
+
currentExtensions[14] = (value) => {
|
|
803
|
+
if (bundledStrings)
|
|
804
|
+
return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
|
|
805
|
+
return new Tag(value, 14)
|
|
806
|
+
};
|
|
807
|
+
currentExtensions[15] = (value) => {
|
|
808
|
+
if (bundledStrings)
|
|
809
|
+
return bundledStrings[1].slice(bundledStrings.position1, bundledStrings.position1 += value)
|
|
810
|
+
return new Tag(value, 15)
|
|
811
|
+
};
|
|
755
812
|
|
|
756
813
|
currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
|
|
757
814
|
return (glbl[data[0]] || Error)(data[1], data[2])
|
|
758
815
|
};
|
|
759
816
|
const packedTable = (read) => {
|
|
760
817
|
if (src[position++] != 0x84)
|
|
761
|
-
throw new Error('Packed values structure must be followed by 4 element array')
|
|
818
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
762
819
|
let newPackedValues = read(); // packed values
|
|
763
820
|
packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
|
|
764
821
|
packedValues.prefixes = read();
|
|
@@ -769,15 +826,36 @@ packedTable.handlesRead = true;
|
|
|
769
826
|
currentExtensions[51] = packedTable;
|
|
770
827
|
|
|
771
828
|
currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
|
|
829
|
+
if (!packedValues) {
|
|
830
|
+
if (currentDecoder.getShared)
|
|
831
|
+
loadShared();
|
|
832
|
+
else
|
|
833
|
+
return new Tag(data, PACKED_REFERENCE_TAG_ID)
|
|
834
|
+
}
|
|
772
835
|
if (typeof data == 'number')
|
|
773
836
|
return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
|
|
774
837
|
throw new Error('No support for non-integer packed references yet')
|
|
775
838
|
};
|
|
839
|
+
currentExtensions[25] = (id) => {
|
|
840
|
+
return stringRefs[id]
|
|
841
|
+
};
|
|
842
|
+
currentExtensions[256] = (read) => {
|
|
843
|
+
stringRefs = [];
|
|
844
|
+
try {
|
|
845
|
+
return read()
|
|
846
|
+
} finally {
|
|
847
|
+
stringRefs = null;
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
currentExtensions[256].handlesRead = true;
|
|
776
851
|
|
|
777
|
-
currentExtensions[
|
|
778
|
-
//
|
|
779
|
-
if (!referenceMap)
|
|
852
|
+
currentExtensions[28] = (read) => {
|
|
853
|
+
// shareable http://cbor.schmorp.de/value-sharing (for structured clones)
|
|
854
|
+
if (!referenceMap) {
|
|
780
855
|
referenceMap = new Map();
|
|
856
|
+
referenceMap.id = 0;
|
|
857
|
+
}
|
|
858
|
+
let id = referenceMap.id++;
|
|
781
859
|
let token = src[position];
|
|
782
860
|
let target;
|
|
783
861
|
// TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
|
|
@@ -795,9 +873,10 @@ currentExtensions[40009] = (id) => {
|
|
|
795
873
|
refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
|
|
796
874
|
return targetProperties // no cycle, can just use the returned read object
|
|
797
875
|
};
|
|
876
|
+
currentExtensions[28].handlesRead = true;
|
|
798
877
|
|
|
799
|
-
currentExtensions[
|
|
800
|
-
//
|
|
878
|
+
currentExtensions[29] = (id) => {
|
|
879
|
+
// sharedref http://cbor.schmorp.de/value-sharing (for structured clones)
|
|
801
880
|
let refEntry = referenceMap.get(id);
|
|
802
881
|
refEntry.used = true;
|
|
803
882
|
return refEntry.target
|
|
@@ -820,33 +899,126 @@ function combine(a, b) {
|
|
|
820
899
|
return a.concat(b)
|
|
821
900
|
return Object.assign({}, a, b)
|
|
822
901
|
}
|
|
902
|
+
function getPackedValues() {
|
|
903
|
+
if (!packedValues) {
|
|
904
|
+
if (currentDecoder.getShared)
|
|
905
|
+
loadShared();
|
|
906
|
+
else
|
|
907
|
+
throw new Error('No packed values available')
|
|
908
|
+
}
|
|
909
|
+
return packedValues
|
|
910
|
+
}
|
|
911
|
+
const SHARED_DATA_TAG_ID = 0x53687264; // ascii 'Shrd'
|
|
823
912
|
currentExtensionRanges.push((tag, input) => {
|
|
824
913
|
if (tag >= 225 && tag <= 255)
|
|
825
|
-
return combine(
|
|
914
|
+
return combine(getPackedValues().prefixes[tag - 224], input)
|
|
826
915
|
if (tag >= 28704 && tag <= 32767)
|
|
827
|
-
return combine(
|
|
916
|
+
return combine(getPackedValues().prefixes[tag - 28672], input)
|
|
828
917
|
if (tag >= 1879052288 && tag <= 2147483647)
|
|
829
|
-
return combine(
|
|
918
|
+
return combine(getPackedValues().prefixes[tag - 1879048192], input)
|
|
830
919
|
if (tag >= 216 && tag <= 223)
|
|
831
|
-
return combine(input,
|
|
920
|
+
return combine(input, getPackedValues().suffixes[tag - 216])
|
|
832
921
|
if (tag >= 27647 && tag <= 28671)
|
|
833
|
-
return combine(input,
|
|
922
|
+
return combine(input, getPackedValues().suffixes[tag - 27639])
|
|
834
923
|
if (tag >= 1811940352 && tag <= 1879048191)
|
|
835
|
-
return combine(input,
|
|
924
|
+
return combine(input, getPackedValues().suffixes[tag - 1811939328])
|
|
925
|
+
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)
|
|
926
|
+
return {
|
|
927
|
+
packedValues: packedValues,
|
|
928
|
+
structures: currentStructures.slice(0),
|
|
929
|
+
version: input,
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
if (tag == 55799) // self-descriptive CBOR tag, just return input value
|
|
933
|
+
return input
|
|
836
934
|
});
|
|
837
935
|
|
|
838
|
-
const
|
|
839
|
-
const
|
|
936
|
+
const isLittleEndianMachine = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1;
|
|
937
|
+
const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'];
|
|
938
|
+
const typedArrayTags = [64, 68, 69, 70, 71, 72, 77, 78, 79, 85, 86];
|
|
840
939
|
for (let i = 0; i < typedArrays.length; i++) {
|
|
841
940
|
registerTypedArray(typedArrays[i], typedArrayTags[i]);
|
|
842
941
|
}
|
|
843
|
-
function registerTypedArray(
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
942
|
+
function registerTypedArray(typedArrayId, tag, littleEndian) {
|
|
943
|
+
let TypedArray = glbl[typedArrayId + 'Array'];
|
|
944
|
+
let bytesPerElement = TypedArray.BYTES_PER_ELEMENT;
|
|
945
|
+
for (let littleEndian = 0; littleEndian < 2; littleEndian++) {
|
|
946
|
+
if (!littleEndian && bytesPerElement == 1)
|
|
947
|
+
continue
|
|
948
|
+
let sizeShift = bytesPerElement == 2 ? 1 : bytesPerElement == 4 ? 2 : 3;
|
|
949
|
+
currentExtensions[littleEndian ? tag : (tag - 4)] = (bytesPerElement == 1 || littleEndian == isLittleEndianMachine) ? (buffer) => {
|
|
950
|
+
if (!TypedArray)
|
|
951
|
+
throw new Error('Could not find typed array for code ' + tag)
|
|
952
|
+
// we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
|
|
953
|
+
return new TypedArray(Uint8Array.prototype.slice.call(buffer, 0).buffer)
|
|
954
|
+
} : buffer => {
|
|
955
|
+
if (!TypedArray)
|
|
956
|
+
throw new Error('Could not find typed array for code ' + tag)
|
|
957
|
+
let dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
958
|
+
let elements = buffer.length >> sizeShift;
|
|
959
|
+
let ta = new TypedArray(elements);
|
|
960
|
+
let method = dv['get' + typedArrayId];
|
|
961
|
+
for (let i = 0; i < elements; i++) {
|
|
962
|
+
ta[i] = method.call(dv, i << sizeShift, littleEndian);
|
|
963
|
+
}
|
|
964
|
+
return ta
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
function readBundleExt() {
|
|
970
|
+
let length = readJustLength();
|
|
971
|
+
let bundlePosition = position + read();
|
|
972
|
+
for (let i = 2; i < length; i++) {
|
|
973
|
+
// skip past bundles that were already read
|
|
974
|
+
let bundleLength = readJustLength(); // this will increment position, so must add to position afterwards
|
|
975
|
+
position += bundleLength;
|
|
976
|
+
}
|
|
977
|
+
let dataPosition = position;
|
|
978
|
+
position = bundlePosition;
|
|
979
|
+
bundledStrings = [readStringJS(readJustLength()), readStringJS(readJustLength())];
|
|
980
|
+
bundledStrings.position0 = 0;
|
|
981
|
+
bundledStrings.position1 = 0;
|
|
982
|
+
bundledStrings.postBundlePosition = position;
|
|
983
|
+
position = dataPosition;
|
|
984
|
+
return read()
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
function readJustLength() {
|
|
988
|
+
let token = src[position++] & 0x1f;
|
|
989
|
+
if (token > 0x17) {
|
|
990
|
+
switch (token) {
|
|
991
|
+
case 0x18:
|
|
992
|
+
token = src[position++];
|
|
993
|
+
break
|
|
994
|
+
case 0x19:
|
|
995
|
+
token = dataView.getUint16(position);
|
|
996
|
+
position += 2;
|
|
997
|
+
break
|
|
998
|
+
case 0x1a:
|
|
999
|
+
token = dataView.getUint32(position);
|
|
1000
|
+
position += 4;
|
|
1001
|
+
break
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
return token
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function loadShared() {
|
|
1008
|
+
if (currentDecoder.getShared) {
|
|
1009
|
+
let sharedData = saveState(() => {
|
|
1010
|
+
// save the state in case getShared modifies our buffer
|
|
1011
|
+
src = null;
|
|
1012
|
+
return currentDecoder.getShared()
|
|
1013
|
+
}) || {};
|
|
1014
|
+
let updatedStructures = sharedData.structures || [];
|
|
1015
|
+
currentDecoder.sharedVersion = sharedData.version;
|
|
1016
|
+
packedValues = currentDecoder.sharedValues = sharedData.packedValues;
|
|
1017
|
+
if (currentStructures === true)
|
|
1018
|
+
currentDecoder.structures = currentStructures = updatedStructures;
|
|
1019
|
+
else
|
|
1020
|
+
currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
|
|
1021
|
+
}
|
|
850
1022
|
}
|
|
851
1023
|
|
|
852
1024
|
function saveState(callback) {
|
|
@@ -858,6 +1030,8 @@ function saveState(callback) {
|
|
|
858
1030
|
let savedSrcString = srcString;
|
|
859
1031
|
let savedStrings = strings;
|
|
860
1032
|
let savedReferenceMap = referenceMap;
|
|
1033
|
+
let savedBundledStrings = bundledStrings;
|
|
1034
|
+
|
|
861
1035
|
// TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
|
|
862
1036
|
let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
|
|
863
1037
|
let savedStructures = currentStructures;
|
|
@@ -872,6 +1046,7 @@ function saveState(callback) {
|
|
|
872
1046
|
srcString = savedSrcString;
|
|
873
1047
|
strings = savedStrings;
|
|
874
1048
|
referenceMap = savedReferenceMap;
|
|
1049
|
+
bundledStrings = savedBundledStrings;
|
|
875
1050
|
src = savedSrc;
|
|
876
1051
|
sequentialMode = savedSequentialMode;
|
|
877
1052
|
currentStructures = savedStructures;
|
|
@@ -918,13 +1093,15 @@ let extensions, extensionClasses;
|
|
|
918
1093
|
const hasNodeBuffer = typeof Buffer !== 'undefined';
|
|
919
1094
|
const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
|
|
920
1095
|
const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
|
|
921
|
-
const RECORD_STARTING_ID_PREFIX = 0x69; // tag 105/0x69
|
|
922
1096
|
const MAX_STRUCTURES = 0x100;
|
|
923
1097
|
const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
|
|
924
1098
|
let target;
|
|
925
1099
|
let targetView;
|
|
926
1100
|
let position$1 = 0;
|
|
927
1101
|
let safeEnd;
|
|
1102
|
+
let bundledStrings$1 = null;
|
|
1103
|
+
const MAX_BUNDLE_SIZE = 0xf000;
|
|
1104
|
+
const hasNonLatin = /[\u0080-\uFFFF]/;
|
|
928
1105
|
const RECORD_SYMBOL = Symbol('record-id');
|
|
929
1106
|
class Encoder extends Decoder {
|
|
930
1107
|
constructor(options) {
|
|
@@ -936,7 +1113,6 @@ class Encoder extends Decoder {
|
|
|
936
1113
|
let structures;
|
|
937
1114
|
let referenceMap;
|
|
938
1115
|
options = options || {};
|
|
939
|
-
let lastSharedStructuresLength = 0;
|
|
940
1116
|
let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
|
|
941
1117
|
return target.utf8Write(string, position, maxBytes)
|
|
942
1118
|
} : (textEncoder && textEncoder.encodeInto) ?
|
|
@@ -945,12 +1121,20 @@ class Encoder extends Decoder {
|
|
|
945
1121
|
} : false;
|
|
946
1122
|
|
|
947
1123
|
let encoder = this;
|
|
948
|
-
let
|
|
1124
|
+
let hasSharedStructures = options.structures || options.saveStructures;
|
|
1125
|
+
let maxSharedStructures = options.maxSharedStructures;
|
|
1126
|
+
if (maxSharedStructures == null)
|
|
1127
|
+
maxSharedStructures = hasSharedStructures ? 128 : 0;
|
|
1128
|
+
if (maxSharedStructures > 8190)
|
|
1129
|
+
throw new Error('Maximum maxSharedStructure is 8190')
|
|
949
1130
|
let isSequential = options.sequential;
|
|
950
1131
|
if (isSequential) {
|
|
951
1132
|
maxSharedStructures = 0;
|
|
952
|
-
this.structures = [];
|
|
953
1133
|
}
|
|
1134
|
+
if (!this.structures)
|
|
1135
|
+
this.structures = [];
|
|
1136
|
+
if (this.saveStructures)
|
|
1137
|
+
this.saveShared = this.saveStructures;
|
|
954
1138
|
let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues;
|
|
955
1139
|
let sharedPackedObjectMap;
|
|
956
1140
|
if (sharedValues) {
|
|
@@ -958,13 +1142,29 @@ class Encoder extends Decoder {
|
|
|
958
1142
|
for (let i = 0, l = sharedValues.length; i < l; i++) {
|
|
959
1143
|
sharedPackedObjectMap[sharedValues[i]] = i;
|
|
960
1144
|
}
|
|
961
|
-
|
|
962
1145
|
}
|
|
963
1146
|
let recordIdsToRemove = [];
|
|
964
1147
|
let transitionsCount = 0;
|
|
965
1148
|
let serializationsSinceTransitionRebuild = 0;
|
|
966
|
-
|
|
967
|
-
this.
|
|
1149
|
+
|
|
1150
|
+
this.mapEncode = function(value, encodeOptions) {
|
|
1151
|
+
// Experimental support for premapping keys using _keyMap instad of keyMap - not optiimised yet)
|
|
1152
|
+
if (this._keyMap && !this._mapped) {
|
|
1153
|
+
//console.log('encoding ', value)
|
|
1154
|
+
switch (value.constructor.name) {
|
|
1155
|
+
case 'Array':
|
|
1156
|
+
value = value.map(r => this.encodeKeys(r));
|
|
1157
|
+
break
|
|
1158
|
+
//case 'Map':
|
|
1159
|
+
// value = this.encodeKeys(value)
|
|
1160
|
+
// break
|
|
1161
|
+
}
|
|
1162
|
+
//this._mapped = true
|
|
1163
|
+
}
|
|
1164
|
+
return this.encode(value, encodeOptions)
|
|
1165
|
+
};
|
|
1166
|
+
|
|
1167
|
+
this.encode = function(value, encodeOptions) {
|
|
968
1168
|
if (!target) {
|
|
969
1169
|
target = new ByteArrayAllocate(8192);
|
|
970
1170
|
targetView = new DataView(target.buffer, 0, 8192);
|
|
@@ -980,11 +1180,30 @@ class Encoder extends Decoder {
|
|
|
980
1180
|
} else if (encodeOptions === REUSE_BUFFER_MODE)
|
|
981
1181
|
position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
|
|
982
1182
|
start = position$1;
|
|
1183
|
+
if (encoder.useSelfDescribedHeader) {
|
|
1184
|
+
targetView.setUint32(position$1, 0xd9d9f700); // tag two byte, then self-descriptive tag
|
|
1185
|
+
position$1 += 3;
|
|
1186
|
+
}
|
|
983
1187
|
referenceMap = encoder.structuredClone ? new Map() : null;
|
|
1188
|
+
if (encoder.bundleStrings && typeof value !== 'string') {
|
|
1189
|
+
bundledStrings$1 = [];
|
|
1190
|
+
bundledStrings$1.size = Infinity; // force a new bundle start on first string
|
|
1191
|
+
} else
|
|
1192
|
+
bundledStrings$1 = null;
|
|
1193
|
+
|
|
984
1194
|
sharedStructures = encoder.structures;
|
|
985
1195
|
if (sharedStructures) {
|
|
986
|
-
if (sharedStructures.uninitialized)
|
|
987
|
-
|
|
1196
|
+
if (sharedStructures.uninitialized) {
|
|
1197
|
+
let sharedData = encoder.getShared() || {};
|
|
1198
|
+
encoder.structures = sharedStructures = sharedData.structures || [];
|
|
1199
|
+
encoder.sharedVersion = sharedData.version;
|
|
1200
|
+
let sharedValues = encoder.sharedValues = sharedData.packedValues;
|
|
1201
|
+
if (sharedValues) {
|
|
1202
|
+
sharedPackedObjectMap = {};
|
|
1203
|
+
for (let i = 0, l = sharedValues.length; i < l; i++)
|
|
1204
|
+
sharedPackedObjectMap[sharedValues[i]] = i;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
988
1207
|
let sharedStructuresLength = sharedStructures.length;
|
|
989
1208
|
if (sharedStructuresLength > maxSharedStructures && !isSequential)
|
|
990
1209
|
sharedStructuresLength = maxSharedStructures;
|
|
@@ -993,10 +1212,13 @@ class Encoder extends Decoder {
|
|
|
993
1212
|
sharedStructures.transitions = Object.create(null);
|
|
994
1213
|
for (let i = 0; i < sharedStructuresLength; i++) {
|
|
995
1214
|
let keys = sharedStructures[i];
|
|
1215
|
+
//console.log('shared struct keys:', keys)
|
|
996
1216
|
if (!keys)
|
|
997
1217
|
continue
|
|
998
1218
|
let nextTransition, transition = sharedStructures.transitions;
|
|
999
1219
|
for (let j = 0, l = keys.length; j < l; j++) {
|
|
1220
|
+
if (transition[RECORD_SYMBOL] === undefined)
|
|
1221
|
+
transition[RECORD_SYMBOL] = i;
|
|
1000
1222
|
let key = keys[j];
|
|
1001
1223
|
nextTransition = transition[key];
|
|
1002
1224
|
if (!nextTransition) {
|
|
@@ -1004,9 +1226,8 @@ class Encoder extends Decoder {
|
|
|
1004
1226
|
}
|
|
1005
1227
|
transition = nextTransition;
|
|
1006
1228
|
}
|
|
1007
|
-
transition[RECORD_SYMBOL] = i;
|
|
1229
|
+
transition[RECORD_SYMBOL] = i | 0x100000;
|
|
1008
1230
|
}
|
|
1009
|
-
lastSharedStructuresLength = sharedStructures.length;
|
|
1010
1231
|
}
|
|
1011
1232
|
if (!isSequential)
|
|
1012
1233
|
sharedStructures.nextId = sharedStructuresLength;
|
|
@@ -1039,9 +1260,12 @@ class Encoder extends Decoder {
|
|
|
1039
1260
|
}
|
|
1040
1261
|
try {
|
|
1041
1262
|
encode(value);
|
|
1263
|
+
if (bundledStrings$1) {
|
|
1264
|
+
writeBundles(start, encode);
|
|
1265
|
+
}
|
|
1042
1266
|
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
1267
|
if (referenceMap && referenceMap.idsToInsert) {
|
|
1044
|
-
position$1 += referenceMap.idsToInsert.length *
|
|
1268
|
+
position$1 += referenceMap.idsToInsert.length * 2;
|
|
1045
1269
|
if (position$1 > safeEnd)
|
|
1046
1270
|
makeRoom(position$1);
|
|
1047
1271
|
encoder.offset = position$1;
|
|
@@ -1049,16 +1273,18 @@ class Encoder extends Decoder {
|
|
|
1049
1273
|
referenceMap = null;
|
|
1050
1274
|
return serialized
|
|
1051
1275
|
}
|
|
1052
|
-
if (encodeOptions
|
|
1276
|
+
if (encodeOptions & REUSE_BUFFER_MODE) {
|
|
1053
1277
|
target.start = start;
|
|
1054
1278
|
target.end = position$1;
|
|
1055
1279
|
return target
|
|
1056
1280
|
}
|
|
1057
|
-
return target.subarray(start, position$1) // position can change if we call
|
|
1281
|
+
return target.subarray(start, position$1) // position can change if we call encode again in saveShared, so we get the buffer now
|
|
1058
1282
|
} finally {
|
|
1059
1283
|
if (sharedStructures) {
|
|
1060
1284
|
if (serializationsSinceTransitionRebuild < 10)
|
|
1061
1285
|
serializationsSinceTransitionRebuild++;
|
|
1286
|
+
if (sharedStructures.length > maxSharedStructures)
|
|
1287
|
+
sharedStructures.length = maxSharedStructures;
|
|
1062
1288
|
if (transitionsCount > 10000) {
|
|
1063
1289
|
// force a rebuild occasionally after a lot of transitions so it can get cleaned up
|
|
1064
1290
|
sharedStructures.transitions = null;
|
|
@@ -1071,35 +1297,29 @@ class Encoder extends Decoder {
|
|
|
1071
1297
|
recordIdsToRemove[i][RECORD_SYMBOL] = undefined;
|
|
1072
1298
|
}
|
|
1073
1299
|
recordIdsToRemove = [];
|
|
1300
|
+
//sharedStructures.nextId = maxSharedStructures
|
|
1074
1301
|
}
|
|
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
|
|
1302
|
+
}
|
|
1303
|
+
if (hasSharedUpdate && encoder.saveShared) {
|
|
1304
|
+
if (encoder.structures.length > maxSharedStructures) {
|
|
1305
|
+
encoder.structures = encoder.structures.slice(0, maxSharedStructures);
|
|
1093
1306
|
}
|
|
1307
|
+
// we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
|
|
1308
|
+
let returnBuffer = target.subarray(start, position$1);
|
|
1309
|
+
if (encoder.updateSharedData() === false)
|
|
1310
|
+
return encoder.encode(value) // re-encode if it fails
|
|
1311
|
+
return returnBuffer
|
|
1094
1312
|
}
|
|
1313
|
+
if (encodeOptions & RESET_BUFFER_MODE)
|
|
1314
|
+
position$1 = start;
|
|
1095
1315
|
}
|
|
1096
1316
|
};
|
|
1097
1317
|
this.findCommonStringsToPack = () => {
|
|
1098
1318
|
samplingPackedValues = new Map();
|
|
1099
1319
|
if (!sharedPackedObjectMap)
|
|
1100
1320
|
sharedPackedObjectMap = Object.create(null);
|
|
1101
|
-
return (
|
|
1102
|
-
threshold = threshold || 4;
|
|
1321
|
+
return (options) => {
|
|
1322
|
+
let threshold = options && options.threshold || 4;
|
|
1103
1323
|
let position = this.pack ? options.maxPrivatePackedValues || 16 : 0;
|
|
1104
1324
|
if (!sharedValues)
|
|
1105
1325
|
sharedValues = this.sharedValues = [];
|
|
@@ -1110,6 +1330,7 @@ class Encoder extends Decoder {
|
|
|
1110
1330
|
hasSharedUpdate = true;
|
|
1111
1331
|
}
|
|
1112
1332
|
}
|
|
1333
|
+
while (this.saveShared && this.updateSharedData() === false) {}
|
|
1113
1334
|
samplingPackedValues = null;
|
|
1114
1335
|
}
|
|
1115
1336
|
};
|
|
@@ -1158,6 +1379,33 @@ class Encoder extends Decoder {
|
|
|
1158
1379
|
}
|
|
1159
1380
|
}
|
|
1160
1381
|
let strLength = value.length;
|
|
1382
|
+
if (bundledStrings$1 && strLength >= 4 && strLength < 0x400) {
|
|
1383
|
+
if ((bundledStrings$1.size += strLength) > MAX_BUNDLE_SIZE) {
|
|
1384
|
+
let extStart;
|
|
1385
|
+
let maxBytes = (bundledStrings$1[0] ? bundledStrings$1[0].length * 3 + bundledStrings$1[1].length : 0) + 10;
|
|
1386
|
+
if (position$1 + maxBytes > safeEnd)
|
|
1387
|
+
target = makeRoom(position$1 + maxBytes);
|
|
1388
|
+
target[position$1++] = 0xd9; // tag 16-bit
|
|
1389
|
+
target[position$1++] = 0xdf; // tag 0xdff9
|
|
1390
|
+
target[position$1++] = 0xf9;
|
|
1391
|
+
// TODO: If we only have one bundle with any string data, only write one string bundle
|
|
1392
|
+
target[position$1++] = bundledStrings$1.position ? 0x84 : 0x82; // array of 4 or 2 elements depending on if we write bundles
|
|
1393
|
+
target[position$1++] = 0x1a; // 32-bit unsigned int
|
|
1394
|
+
extStart = position$1 - start;
|
|
1395
|
+
position$1 += 4; // reserve for writing bundle reference
|
|
1396
|
+
if (bundledStrings$1.position) {
|
|
1397
|
+
writeBundles(start, encode); // write the last bundles
|
|
1398
|
+
}
|
|
1399
|
+
bundledStrings$1 = ['', '']; // create new ones
|
|
1400
|
+
bundledStrings$1.size = 0;
|
|
1401
|
+
bundledStrings$1.position = extStart;
|
|
1402
|
+
}
|
|
1403
|
+
let twoByte = hasNonLatin.test(value);
|
|
1404
|
+
bundledStrings$1[twoByte ? 0 : 1] += value;
|
|
1405
|
+
target[position$1++] = twoByte ? 0xce : 0xcf;
|
|
1406
|
+
encode(strLength);
|
|
1407
|
+
return
|
|
1408
|
+
}
|
|
1161
1409
|
let headerSize;
|
|
1162
1410
|
// first we estimate the header size, so we can write to the correct location
|
|
1163
1411
|
if (strLength < 0x20) {
|
|
@@ -1284,16 +1532,16 @@ class Encoder extends Decoder {
|
|
|
1284
1532
|
if (referenceMap) {
|
|
1285
1533
|
let referee = referenceMap.get(value);
|
|
1286
1534
|
if (referee) {
|
|
1287
|
-
|
|
1535
|
+
target[position$1++] = 0xd8;
|
|
1536
|
+
target[position$1++] = 29; // http://cbor.schmorp.de/value-sharing
|
|
1537
|
+
target[position$1++] = 0x19; // 16-bit uint
|
|
1538
|
+
if (!referee.references) {
|
|
1288
1539
|
let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = []);
|
|
1289
|
-
referee.
|
|
1540
|
+
referee.references = [];
|
|
1541
|
+
idsToInsert.push(referee);
|
|
1290
1542
|
}
|
|
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;
|
|
1543
|
+
referee.references.push(position$1 - start);
|
|
1544
|
+
position$1 += 2; // TODO: also support 32-bit
|
|
1297
1545
|
return
|
|
1298
1546
|
} else
|
|
1299
1547
|
referenceMap.set(value, { offset: position$1 - start });
|
|
@@ -1333,16 +1581,23 @@ class Encoder extends Decoder {
|
|
|
1333
1581
|
targetView.setUint32(position$1, length);
|
|
1334
1582
|
position$1 += 4;
|
|
1335
1583
|
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1584
|
+
if (encoder.keyMap) {
|
|
1585
|
+
for (let [ key, entryValue ] of value) {
|
|
1586
|
+
encode(encoder.encodeKey(key));
|
|
1587
|
+
encode(entryValue);
|
|
1588
|
+
}
|
|
1589
|
+
} else {
|
|
1590
|
+
for (let [ key, entryValue ] of value) {
|
|
1591
|
+
encode(key);
|
|
1592
|
+
encode(entryValue);
|
|
1593
|
+
}
|
|
1339
1594
|
}
|
|
1340
|
-
} else {
|
|
1595
|
+
} else {
|
|
1341
1596
|
for (let i = 0, l = extensions.length; i < l; i++) {
|
|
1342
1597
|
let extensionClass = extensionClasses[i];
|
|
1343
1598
|
if (value instanceof extensionClass) {
|
|
1344
1599
|
let extension = extensions[i];
|
|
1345
|
-
let tag = extension.tag;
|
|
1600
|
+
let tag = extension.tag || extension.getTag && extension.getTag(value);
|
|
1346
1601
|
if (tag < 0x18) {
|
|
1347
1602
|
target[position$1++] = 0xc0 | tag;
|
|
1348
1603
|
} else if (tag < 0x100) {
|
|
@@ -1404,6 +1659,7 @@ class Encoder extends Decoder {
|
|
|
1404
1659
|
const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
|
|
1405
1660
|
// this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
|
|
1406
1661
|
let keys = Object.keys(object);
|
|
1662
|
+
let vals = Object.values(object);
|
|
1407
1663
|
let length = keys.length;
|
|
1408
1664
|
if (length < 0x18) {
|
|
1409
1665
|
target[position$1++] = 0xa0 | length;
|
|
@@ -1419,10 +1675,16 @@ class Encoder extends Decoder {
|
|
|
1419
1675
|
targetView.setUint32(position$1, length);
|
|
1420
1676
|
position$1 += 4;
|
|
1421
1677
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1678
|
+
if (encoder.keyMap) {
|
|
1679
|
+
for (let i = 0; i < length; i++) {
|
|
1680
|
+
encode(encodeKey(keys[i]));
|
|
1681
|
+
encode(vals[i]);
|
|
1682
|
+
}
|
|
1683
|
+
} else {
|
|
1684
|
+
for (let i = 0; i < length; i++) {
|
|
1685
|
+
encode(keys[i]);
|
|
1686
|
+
encode(vals[i]);
|
|
1687
|
+
}
|
|
1426
1688
|
}
|
|
1427
1689
|
} :
|
|
1428
1690
|
(object, safePrototype) => {
|
|
@@ -1430,95 +1692,92 @@ class Encoder extends Decoder {
|
|
|
1430
1692
|
let objectOffset = position$1 - start;
|
|
1431
1693
|
position$1 += 2;
|
|
1432
1694
|
let size = 0;
|
|
1433
|
-
|
|
1434
|
-
if (safePrototype || object.hasOwnProperty(key)) {
|
|
1435
|
-
encode(key);
|
|
1695
|
+
if (encoder.keyMap) {
|
|
1696
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
1697
|
+
encode(encoder.encodeKey(key));
|
|
1436
1698
|
encode(object[key]);
|
|
1437
1699
|
size++;
|
|
1438
1700
|
}
|
|
1701
|
+
} else {
|
|
1702
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
1703
|
+
encode(key);
|
|
1704
|
+
encode(object[key]);
|
|
1705
|
+
size++;
|
|
1706
|
+
}
|
|
1439
1707
|
}
|
|
1440
1708
|
target[objectOffset++ + start] = size >> 8;
|
|
1441
1709
|
target[objectOffset + start] = size & 0xff;
|
|
1442
1710
|
} :
|
|
1443
|
-
|
|
1444
|
-
/* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
|
|
1445
1711
|
(object, safePrototype) => {
|
|
1446
|
-
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
|
|
1447
|
-
let
|
|
1448
|
-
let
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1712
|
+
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
|
|
1713
|
+
let newTransitions = 0;
|
|
1714
|
+
let length = 0;
|
|
1715
|
+
let parentRecordId;
|
|
1716
|
+
let keys;
|
|
1717
|
+
if (this.keyMap) {
|
|
1718
|
+
keys = Object.keys(object).map(k => this.encodeKey(k));
|
|
1719
|
+
length = keys.length;
|
|
1720
|
+
for (let i = 0; i < length; i++) {
|
|
1721
|
+
let key = keys[i];
|
|
1722
|
+
nextTransition = transition[key];
|
|
1452
1723
|
if (!nextTransition) {
|
|
1453
|
-
nextTransition = transition[key] = Object.create(null)
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
nextTransition.__keys__ = keys.slice(0, i + 1)
|
|
1465
|
-
}
|
|
1724
|
+
nextTransition = transition[key] = Object.create(null);
|
|
1725
|
+
newTransitions++;
|
|
1726
|
+
}
|
|
1727
|
+
transition = nextTransition;
|
|
1728
|
+
}
|
|
1729
|
+
} else {
|
|
1730
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
1731
|
+
nextTransition = transition[key];
|
|
1732
|
+
if (!nextTransition) {
|
|
1733
|
+
if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
|
|
1734
|
+
parentRecordId = transition[RECORD_SYMBOL] & 0xffff;
|
|
1466
1735
|
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
target.copy(target, )
|
|
1470
|
-
objectOffset
|
|
1736
|
+
nextTransition = transition[key] = Object.create(null);
|
|
1737
|
+
newTransitions++;
|
|
1471
1738
|
}
|
|
1472
|
-
transition = nextTransition
|
|
1473
|
-
|
|
1739
|
+
transition = nextTransition;
|
|
1740
|
+
length++;
|
|
1474
1741
|
}
|
|
1475
1742
|
}
|
|
1476
|
-
let id = transition.id
|
|
1477
|
-
if (!id) {
|
|
1478
|
-
id = transition.id = structures.push(transition.__keys__) + 63
|
|
1479
|
-
if (sharedStructures.onUpdate)
|
|
1480
|
-
sharedStructures.onUpdate(id, transition.__keys__)
|
|
1481
|
-
}
|
|
1482
|
-
target[objectOffset + start] = id
|
|
1483
|
-
}*/
|
|
1484
|
-
(object) => {
|
|
1485
|
-
let keys = Object.keys(object);
|
|
1486
|
-
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
|
|
1487
|
-
let newTransitions = 0;
|
|
1488
|
-
let length = keys.length;
|
|
1489
|
-
for (let i = 0; i < length; i++) {
|
|
1490
|
-
let key = keys[i];
|
|
1491
|
-
nextTransition = transition[key];
|
|
1492
|
-
if (!nextTransition) {
|
|
1493
|
-
nextTransition = transition[key] = Object.create(null);
|
|
1494
|
-
newTransitions++;
|
|
1495
|
-
}
|
|
1496
|
-
transition = nextTransition;
|
|
1497
|
-
}
|
|
1498
1743
|
let recordId = transition[RECORD_SYMBOL];
|
|
1499
1744
|
if (recordId !== undefined) {
|
|
1500
|
-
|
|
1501
|
-
target[position$1++] =
|
|
1502
|
-
target[position$1++] = recordId;
|
|
1745
|
+
recordId &= 0xffff;
|
|
1746
|
+
target[position$1++] = 0xd9;
|
|
1747
|
+
target[position$1++] = (recordId >> 8) | 0xe0;
|
|
1748
|
+
target[position$1++] = recordId & 0xff;
|
|
1503
1749
|
} else {
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
structures.nextId
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1750
|
+
if (!keys)
|
|
1751
|
+
keys = transition.__keys__ || (transition.__keys__ = Object.keys(object));
|
|
1752
|
+
if (parentRecordId === undefined) {
|
|
1753
|
+
recordId = structures.nextId++;
|
|
1754
|
+
if (!recordId) {
|
|
1755
|
+
recordId = 0;
|
|
1756
|
+
structures.nextId = 1;
|
|
1757
|
+
}
|
|
1758
|
+
if (recordId >= MAX_STRUCTURES) {// cycle back around
|
|
1759
|
+
structures.nextId = (recordId = maxSharedStructures) + 1;
|
|
1760
|
+
}
|
|
1761
|
+
} else {
|
|
1762
|
+
recordId = parentRecordId;
|
|
1511
1763
|
}
|
|
1512
|
-
transition[RECORD_SYMBOL] = recordId;
|
|
1513
1764
|
structures[recordId] = keys;
|
|
1514
|
-
if (
|
|
1515
|
-
target[position$1++] = 0xd9;
|
|
1516
|
-
target[position$1++] =
|
|
1517
|
-
target[position$1++] = recordId
|
|
1765
|
+
if (recordId < maxSharedStructures) {
|
|
1766
|
+
target[position$1++] = 0xd9;
|
|
1767
|
+
target[position$1++] = (recordId >> 8) | 0xe0;
|
|
1768
|
+
target[position$1++] = recordId & 0xff;
|
|
1769
|
+
transition = structures.transitions;
|
|
1770
|
+
for (let i = 0; i < length; i++) {
|
|
1771
|
+
if (transition[RECORD_SYMBOL] === undefined || (transition[RECORD_SYMBOL] & 0x100000))
|
|
1772
|
+
transition[RECORD_SYMBOL] = recordId;
|
|
1773
|
+
transition = transition[keys[i]];
|
|
1774
|
+
}
|
|
1775
|
+
transition[RECORD_SYMBOL] = recordId | 0x100000; // indicates it is a extendable terminal
|
|
1518
1776
|
hasSharedUpdate = true;
|
|
1519
1777
|
} else {
|
|
1520
|
-
|
|
1521
|
-
|
|
1778
|
+
transition[RECORD_SYMBOL] = recordId;
|
|
1779
|
+
targetView.setUint32(position$1, 0xd9dfff00); // tag two byte, then record definition id
|
|
1780
|
+
position$1 += 3;
|
|
1522
1781
|
if (newTransitions)
|
|
1523
1782
|
transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
|
|
1524
1783
|
// record the removal of the id, we can maintain our shared structure
|
|
@@ -1526,13 +1785,9 @@ class Encoder extends Decoder {
|
|
|
1526
1785
|
recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined; // we are cycling back through, and have to remove old ones
|
|
1527
1786
|
recordIdsToRemove.push(transition);
|
|
1528
1787
|
writeArrayHeader(length + 2);
|
|
1788
|
+
encode(0xe000 + recordId);
|
|
1529
1789
|
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]]);
|
|
1790
|
+
for (let v of Object.values(object)) encode(v);
|
|
1536
1791
|
return
|
|
1537
1792
|
}
|
|
1538
1793
|
}
|
|
@@ -1541,8 +1796,9 @@ class Encoder extends Decoder {
|
|
|
1541
1796
|
} else {
|
|
1542
1797
|
writeArrayHeader(length);
|
|
1543
1798
|
}
|
|
1544
|
-
for (let
|
|
1545
|
-
|
|
1799
|
+
for (let key in object)
|
|
1800
|
+
if (safePrototype || object.hasOwnProperty(key))
|
|
1801
|
+
encode(object[key]);
|
|
1546
1802
|
};
|
|
1547
1803
|
const makeRoom = (end) => {
|
|
1548
1804
|
let newSize;
|
|
@@ -1572,6 +1828,40 @@ class Encoder extends Decoder {
|
|
|
1572
1828
|
targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
|
|
1573
1829
|
position$1 = 0;
|
|
1574
1830
|
}
|
|
1831
|
+
clearSharedData() {
|
|
1832
|
+
if (this.structures)
|
|
1833
|
+
this.structures = [];
|
|
1834
|
+
if (this.sharedValues)
|
|
1835
|
+
this.sharedValues = undefined;
|
|
1836
|
+
}
|
|
1837
|
+
updateSharedData() {
|
|
1838
|
+
let lastVersion = this.sharedVersion || 0;
|
|
1839
|
+
this.sharedVersion = lastVersion + 1;
|
|
1840
|
+
let structuresCopy = this.structures.slice(0);
|
|
1841
|
+
let sharedData = new SharedData(structuresCopy, this.sharedValues, this.sharedVersion);
|
|
1842
|
+
let saveResults = this.saveShared(sharedData,
|
|
1843
|
+
existingShared => (existingShared && existingShared.version || 0) == lastVersion);
|
|
1844
|
+
if (saveResults === false) {
|
|
1845
|
+
// get updated structures and try again if the update failed
|
|
1846
|
+
sharedData = this.getShared() || {};
|
|
1847
|
+
this.structures = sharedData.structures || [];
|
|
1848
|
+
this.sharedValues = sharedData.packedValues;
|
|
1849
|
+
this.sharedVersion = sharedData.version;
|
|
1850
|
+
this.structures.nextId = this.structures.length;
|
|
1851
|
+
} else {
|
|
1852
|
+
// restore structures
|
|
1853
|
+
structuresCopy.forEach((structure, i) => this.structures[i] = structure);
|
|
1854
|
+
}
|
|
1855
|
+
// saveShared may fail to write and reload, or may have reloaded to check compatibility and overwrite saved data, either way load the correct shared data
|
|
1856
|
+
return saveResults
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
class SharedData {
|
|
1860
|
+
constructor(structures, values, version) {
|
|
1861
|
+
this.structures = structures;
|
|
1862
|
+
this.packedValues = values;
|
|
1863
|
+
this.version = version;
|
|
1864
|
+
}
|
|
1575
1865
|
}
|
|
1576
1866
|
|
|
1577
1867
|
function writeArrayHeader(length) {
|
|
@@ -1640,12 +1930,12 @@ function findRepetitiveStrings(value, packedValues) {
|
|
|
1640
1930
|
case 'function': console.log(value);
|
|
1641
1931
|
}
|
|
1642
1932
|
}
|
|
1643
|
-
|
|
1644
|
-
extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
|
|
1933
|
+
const isLittleEndianMachine$1 = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1;
|
|
1934
|
+
extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
|
|
1645
1935
|
Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
|
|
1646
1936
|
typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
|
|
1647
1937
|
typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
|
|
1648
|
-
Float32Array, Float64Array];
|
|
1938
|
+
Float32Array, Float64Array, SharedData];
|
|
1649
1939
|
|
|
1650
1940
|
//Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
|
|
1651
1941
|
extensions = [{
|
|
@@ -1680,6 +1970,13 @@ extensions = [{
|
|
|
1680
1970
|
encode(regex, encode) {
|
|
1681
1971
|
encode([ 'RegExp', regex.source, regex.flags ]);
|
|
1682
1972
|
}
|
|
1973
|
+
}, {
|
|
1974
|
+
getTag(tag) {
|
|
1975
|
+
return tag.tag
|
|
1976
|
+
},
|
|
1977
|
+
encode(tag, encode) {
|
|
1978
|
+
encode(tag.value);
|
|
1979
|
+
}
|
|
1683
1980
|
}, {
|
|
1684
1981
|
encode(arrayBuffer, encode, makeRoom) {
|
|
1685
1982
|
writeBuffer(arrayBuffer, makeRoom);
|
|
@@ -1688,19 +1985,48 @@ extensions = [{
|
|
|
1688
1985
|
encode(arrayBuffer, encode, makeRoom) {
|
|
1689
1986
|
writeBuffer(arrayBuffer, makeRoom);
|
|
1690
1987
|
}
|
|
1691
|
-
}, typedArrayEncoder(64),
|
|
1692
|
-
typedArrayEncoder(68),
|
|
1693
|
-
typedArrayEncoder(69),
|
|
1694
|
-
typedArrayEncoder(70),
|
|
1695
|
-
typedArrayEncoder(71),
|
|
1696
|
-
typedArrayEncoder(72),
|
|
1697
|
-
typedArrayEncoder(77),
|
|
1698
|
-
typedArrayEncoder(78),
|
|
1699
|
-
typedArrayEncoder(79),
|
|
1700
|
-
typedArrayEncoder(
|
|
1701
|
-
typedArrayEncoder(
|
|
1702
|
-
|
|
1703
|
-
|
|
1988
|
+
}, typedArrayEncoder(64, 1),
|
|
1989
|
+
typedArrayEncoder(68, 1),
|
|
1990
|
+
typedArrayEncoder(69, 2),
|
|
1991
|
+
typedArrayEncoder(70, 4),
|
|
1992
|
+
typedArrayEncoder(71, 8),
|
|
1993
|
+
typedArrayEncoder(72, 1),
|
|
1994
|
+
typedArrayEncoder(77, 2),
|
|
1995
|
+
typedArrayEncoder(78, 4),
|
|
1996
|
+
typedArrayEncoder(79, 8),
|
|
1997
|
+
typedArrayEncoder(85, 4),
|
|
1998
|
+
typedArrayEncoder(86, 8),
|
|
1999
|
+
{
|
|
2000
|
+
encode(sharedData, encode) { // write SharedData
|
|
2001
|
+
let packedValues = sharedData.packedValues || [];
|
|
2002
|
+
let sharedStructures = sharedData.structures || [];
|
|
2003
|
+
if (packedValues.values.length > 0) {
|
|
2004
|
+
target[position$1++] = 0xd8; // one-byte tag
|
|
2005
|
+
target[position$1++] = 51; // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
|
|
2006
|
+
writeArrayHeader(4);
|
|
2007
|
+
let valuesArray = packedValues.values;
|
|
2008
|
+
encode(valuesArray);
|
|
2009
|
+
writeArrayHeader(0); // prefixes
|
|
2010
|
+
writeArrayHeader(0); // suffixes
|
|
2011
|
+
packedObjectMap = Object.create(sharedPackedObjectMap || null);
|
|
2012
|
+
for (let i = 0, l = valuesArray.length; i < l; i++) {
|
|
2013
|
+
packedObjectMap[valuesArray[i]] = i;
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
if (sharedStructures) {
|
|
2017
|
+
targetView.setUint32(position$1, 0xd9dffe00);
|
|
2018
|
+
position$1 += 3;
|
|
2019
|
+
let definitions = sharedStructures.slice(0);
|
|
2020
|
+
definitions.unshift(0xe000);
|
|
2021
|
+
definitions.push(new Tag(sharedData.version, 0x53687264));
|
|
2022
|
+
encode(definitions);
|
|
2023
|
+
} else
|
|
2024
|
+
encode(new Tag(sharedData.version, 0x53687264));
|
|
2025
|
+
}
|
|
2026
|
+
}];
|
|
2027
|
+
function typedArrayEncoder(tag, size) {
|
|
2028
|
+
if (!isLittleEndianMachine$1 && size > 1)
|
|
2029
|
+
tag -= 4; // the big endian equivalents are 4 less
|
|
1704
2030
|
return {
|
|
1705
2031
|
tag: tag,
|
|
1706
2032
|
encode: function writeExtBuffer(typedArray, encode) {
|
|
@@ -1738,27 +2064,35 @@ function writeBuffer(buffer, makeRoom) {
|
|
|
1738
2064
|
function insertIds(serialized, idsToInsert) {
|
|
1739
2065
|
// insert the ids that need to be referenced for structured clones
|
|
1740
2066
|
let nextId;
|
|
1741
|
-
let distanceToMove = idsToInsert.length *
|
|
2067
|
+
let distanceToMove = idsToInsert.length * 2;
|
|
1742
2068
|
let lastEnd = serialized.length - distanceToMove;
|
|
1743
2069
|
idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1);
|
|
2070
|
+
for (let id = 0; id < idsToInsert.length; id++) {
|
|
2071
|
+
let referee = idsToInsert[id];
|
|
2072
|
+
referee.id = id;
|
|
2073
|
+
for (let position of referee.references) {
|
|
2074
|
+
serialized[position++] = id >> 8;
|
|
2075
|
+
serialized[position] = id & 0xff;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
1744
2078
|
while (nextId = idsToInsert.pop()) {
|
|
1745
2079
|
let offset = nextId.offset;
|
|
1746
|
-
let id = nextId.id;
|
|
1747
2080
|
serialized.copyWithin(offset + distanceToMove, offset, lastEnd);
|
|
1748
|
-
distanceToMove -=
|
|
2081
|
+
distanceToMove -= 2;
|
|
1749
2082
|
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;
|
|
2083
|
+
serialized[position++] = 0xd8;
|
|
2084
|
+
serialized[position++] = 28; // http://cbor.schmorp.de/value-sharing
|
|
1758
2085
|
lastEnd = offset;
|
|
1759
2086
|
}
|
|
1760
2087
|
return serialized
|
|
1761
2088
|
}
|
|
2089
|
+
function writeBundles(start, encode) {
|
|
2090
|
+
targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start + 1); // the offset to bundle
|
|
2091
|
+
let writeStrings = bundledStrings$1;
|
|
2092
|
+
bundledStrings$1 = null;
|
|
2093
|
+
encode(writeStrings[0]);
|
|
2094
|
+
encode(writeStrings[1]);
|
|
2095
|
+
}
|
|
1762
2096
|
|
|
1763
2097
|
function addExtension$1(extension) {
|
|
1764
2098
|
if (extension.Class) {
|
|
@@ -1772,7 +2106,8 @@ function addExtension$1(extension) {
|
|
|
1772
2106
|
let defaultEncoder = new Encoder({ useRecords: false });
|
|
1773
2107
|
const encode = defaultEncoder.encode;
|
|
1774
2108
|
const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
|
|
1775
|
-
const REUSE_BUFFER_MODE =
|
|
2109
|
+
const REUSE_BUFFER_MODE = 512;
|
|
2110
|
+
const RESET_BUFFER_MODE = 1024;
|
|
1776
2111
|
|
|
1777
2112
|
class EncoderStream extends stream.Transform {
|
|
1778
2113
|
constructor(options) {
|