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/decode.js
CHANGED
|
@@ -7,7 +7,10 @@ let srcEnd
|
|
|
7
7
|
let position = 0
|
|
8
8
|
let alreadySet
|
|
9
9
|
const EMPTY_ARRAY = []
|
|
10
|
-
const
|
|
10
|
+
const LEGACY_RECORD_INLINE_ID = 105
|
|
11
|
+
const RECORD_DEFINITIONS_ID = 0xdffe
|
|
12
|
+
const RECORD_INLINE_ID = 0xdfff // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
|
|
13
|
+
const BUNDLED_STRINGS_ID = 0xdff9
|
|
11
14
|
const PACKED_TABLE_TAG_ID = 51
|
|
12
15
|
const PACKED_REFERENCE_TAG_ID = 6
|
|
13
16
|
const STOP_CODE = {}
|
|
@@ -18,13 +21,13 @@ let currentStructures
|
|
|
18
21
|
let srcString
|
|
19
22
|
let srcStringStart = 0
|
|
20
23
|
let srcStringEnd = 0
|
|
24
|
+
let bundledStrings
|
|
21
25
|
let referenceMap
|
|
22
26
|
let currentExtensions = []
|
|
23
27
|
let currentExtensionRanges = []
|
|
24
28
|
let packedValues
|
|
25
29
|
let dataView
|
|
26
30
|
let restoreMapsAsObject
|
|
27
|
-
let sharedValues
|
|
28
31
|
let defaultOptions = {
|
|
29
32
|
useRecords: false,
|
|
30
33
|
mapsAsObjects: true
|
|
@@ -34,13 +37,70 @@ let sequentialMode = false
|
|
|
34
37
|
export class Decoder {
|
|
35
38
|
constructor(options) {
|
|
36
39
|
if (options) {
|
|
40
|
+
if ((options.keyMap || options._keyMap) && !options.useRecords) {
|
|
41
|
+
options.useRecords = false
|
|
42
|
+
options.mapsAsObjects = true
|
|
43
|
+
}
|
|
37
44
|
if (options.useRecords === false && options.mapsAsObjects === undefined)
|
|
38
45
|
options.mapsAsObjects = true
|
|
39
|
-
if (options.getStructures
|
|
46
|
+
if (options.getStructures)
|
|
47
|
+
options.getShared = options.getStructures
|
|
48
|
+
if (options.getShared && !options.structures)
|
|
40
49
|
(options.structures = []).uninitialized = true // this is what we use to denote an uninitialized structures
|
|
50
|
+
if (options.keyMap) {
|
|
51
|
+
this.mapKey = new Map()
|
|
52
|
+
for (let [k,v] of Object.entries(options.keyMap)) this.mapKey.set(v,k)
|
|
53
|
+
}
|
|
41
54
|
}
|
|
42
55
|
Object.assign(this, options)
|
|
43
56
|
}
|
|
57
|
+
/*
|
|
58
|
+
decodeKey(key) {
|
|
59
|
+
return this.keyMap
|
|
60
|
+
? Object.keys(this.keyMap)[Object.values(this.keyMap).indexOf(key)] || key
|
|
61
|
+
: key
|
|
62
|
+
}
|
|
63
|
+
*/
|
|
64
|
+
decodeKey(key) {
|
|
65
|
+
return this.keyMap ? this.mapKey.get(key) || key : key
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
encodeKey(key) {
|
|
69
|
+
return this.keyMap && this.keyMap.hasOwnProperty(key) ? this.keyMap[key] : key
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
encodeKeys(rec) {
|
|
73
|
+
if (!this._keyMap) return rec
|
|
74
|
+
let map = new Map()
|
|
75
|
+
for (let [k,v] of Object.entries(rec)) map.set((this._keyMap.hasOwnProperty(k) ? this._keyMap[k] : k), v)
|
|
76
|
+
return map
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
decodeKeys(map) {
|
|
80
|
+
if (!this._keyMap || map.constructor.name != 'Map') return map
|
|
81
|
+
if (!this._mapKey) {
|
|
82
|
+
this._mapKey = new Map()
|
|
83
|
+
for (let [k,v] of Object.entries(this._keyMap)) this._mapKey.set(v,k)
|
|
84
|
+
}
|
|
85
|
+
let res = {}
|
|
86
|
+
//map.forEach((v,k) => res[Object.keys(this._keyMap)[Object.values(this._keyMap).indexOf(k)] || k] = v)
|
|
87
|
+
map.forEach((v,k) => res[this._mapKey.has(k) ? this._mapKey.get(k) : k] = v)
|
|
88
|
+
return res
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
mapDecode(source, end) {
|
|
92
|
+
|
|
93
|
+
let res = this.decode(source)
|
|
94
|
+
if (this._keyMap) {
|
|
95
|
+
//Experiemntal support for Optimised KeyMap decoding
|
|
96
|
+
switch (res.constructor.name) {
|
|
97
|
+
case 'Array': return res.map(r => this.decodeKeys(r))
|
|
98
|
+
//case 'Map': return this.decodeKeys(res)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return res
|
|
102
|
+
}
|
|
103
|
+
|
|
44
104
|
decode(source, end) {
|
|
45
105
|
if (src) {
|
|
46
106
|
// re-entrant execution, save the state and restore it after we do this decode
|
|
@@ -55,6 +115,7 @@ export class Decoder {
|
|
|
55
115
|
srcStringEnd = 0
|
|
56
116
|
srcString = null
|
|
57
117
|
strings = EMPTY_ARRAY
|
|
118
|
+
bundledStrings = null
|
|
58
119
|
src = source
|
|
59
120
|
// this provides cached access to the data view for a buffer if it is getting reused, which is a recommend
|
|
60
121
|
// technique for getting data from a database where it can be copied into an existing buffer instead of creating
|
|
@@ -128,6 +189,9 @@ export function getPosition() {
|
|
|
128
189
|
export function checkedRead() {
|
|
129
190
|
try {
|
|
130
191
|
let result = read()
|
|
192
|
+
if (bundledStrings) // bundled strings to skip past
|
|
193
|
+
position = bundledStrings.postBundlePosition
|
|
194
|
+
|
|
131
195
|
if (position == srcEnd) {
|
|
132
196
|
// finished reading this source, cleanup references
|
|
133
197
|
currentStructures = null
|
|
@@ -202,6 +266,7 @@ export function read() {
|
|
|
202
266
|
switch(majorType) {
|
|
203
267
|
case 2: // byte string
|
|
204
268
|
case 3: // text string
|
|
269
|
+
throw new Error('Indefinite length not supported for byte or text strings')
|
|
205
270
|
case 4: // array
|
|
206
271
|
let array = []
|
|
207
272
|
let value, i = 0
|
|
@@ -213,8 +278,8 @@ export function read() {
|
|
|
213
278
|
let key
|
|
214
279
|
if (currentDecoder.mapsAsObjects) {
|
|
215
280
|
let object = {}
|
|
216
|
-
|
|
217
|
-
|
|
281
|
+
if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[currentDecoder.decodeKey(key)] = read()
|
|
282
|
+
else while ((key = read()) != STOP_CODE) object[key] = read()
|
|
218
283
|
return object
|
|
219
284
|
} else {
|
|
220
285
|
if (restoreMapsAsObject) {
|
|
@@ -222,8 +287,8 @@ export function read() {
|
|
|
222
287
|
restoreMapsAsObject = false
|
|
223
288
|
}
|
|
224
289
|
let map = new Map()
|
|
225
|
-
|
|
226
|
-
|
|
290
|
+
if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) map.set(currentDecoder.decodeKey(key), read())
|
|
291
|
+
else while ((key = read()) != STOP_CODE) map.set(key, read())
|
|
227
292
|
return map
|
|
228
293
|
}
|
|
229
294
|
case 7:
|
|
@@ -255,16 +320,15 @@ export function read() {
|
|
|
255
320
|
return readFixedString(token)
|
|
256
321
|
case 4: // array
|
|
257
322
|
let array = new Array(token)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
323
|
+
//if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
|
|
324
|
+
//else
|
|
325
|
+
for (let i = 0; i < token; i++) array[i] = read()
|
|
261
326
|
return array
|
|
262
327
|
case 5: // map
|
|
263
328
|
if (currentDecoder.mapsAsObjects) {
|
|
264
329
|
let object = {}
|
|
265
|
-
for (let i = 0; i < token; i++)
|
|
266
|
-
|
|
267
|
-
}
|
|
330
|
+
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[currentDecoder.decodeKey(read())] = read()
|
|
331
|
+
else for (let i = 0; i < token; i++) object[read()] = read()
|
|
268
332
|
return object
|
|
269
333
|
} else {
|
|
270
334
|
if (restoreMapsAsObject) {
|
|
@@ -272,63 +336,66 @@ export function read() {
|
|
|
272
336
|
restoreMapsAsObject = false
|
|
273
337
|
}
|
|
274
338
|
let map = new Map()
|
|
275
|
-
for (let i = 0; i < token; i++)
|
|
276
|
-
|
|
277
|
-
}
|
|
339
|
+
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read())
|
|
340
|
+
else for (let i = 0; i < token; i++) map.set(read(), read())
|
|
278
341
|
return map
|
|
279
342
|
}
|
|
280
343
|
case 6: // extension
|
|
281
|
-
if (
|
|
282
|
-
let structure = currentStructures[token &
|
|
344
|
+
if (token >= BUNDLED_STRINGS_ID) {
|
|
345
|
+
let structure = currentStructures[token & 0x1fff] // check record structures first
|
|
346
|
+
// 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))
|
|
283
347
|
if (structure) {
|
|
284
|
-
if (!structure.read)
|
|
285
|
-
structure.read = createStructureReader(structure)
|
|
348
|
+
if (!structure.read) structure.read = createStructureReader(structure)
|
|
286
349
|
return structure.read()
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if (
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (extension.handlesRead)
|
|
310
|
-
return extension(read)
|
|
311
|
-
else
|
|
312
|
-
return extension(read())
|
|
313
|
-
} else {
|
|
314
|
-
let input = read()
|
|
315
|
-
for (let i = 0; i < currentExtensionRanges.length; i++) {
|
|
316
|
-
let value = currentExtensionRanges[i](token, input)
|
|
317
|
-
if (value !== undefined)
|
|
318
|
-
return value
|
|
350
|
+
}
|
|
351
|
+
if (token < 0x10000) {
|
|
352
|
+
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)
|
|
353
|
+
return recordDefinition(read())
|
|
354
|
+
else if (token == RECORD_DEFINITIONS_ID) {
|
|
355
|
+
let length = readJustLength()
|
|
356
|
+
let id = read()
|
|
357
|
+
for (let i = 2; i < length; i++) {
|
|
358
|
+
recordDefinition([id++, read()])
|
|
359
|
+
}
|
|
360
|
+
return read()
|
|
361
|
+
} else if (token == BUNDLED_STRINGS_ID) {
|
|
362
|
+
return readBundleExt()
|
|
363
|
+
}
|
|
364
|
+
if (currentDecoder.getShared) {
|
|
365
|
+
loadShared()
|
|
366
|
+
structure = currentStructures[token & 0x1fff]
|
|
367
|
+
if (structure) {
|
|
368
|
+
if (!structure.read)
|
|
369
|
+
structure.read = createStructureReader(structure)
|
|
370
|
+
return structure.read()
|
|
371
|
+
}
|
|
319
372
|
}
|
|
320
|
-
return new Tag(input)
|
|
321
373
|
}
|
|
322
374
|
}
|
|
375
|
+
let extension = currentExtensions[token]
|
|
376
|
+
if (extension) {
|
|
377
|
+
if (extension.handlesRead)
|
|
378
|
+
return extension(read)
|
|
379
|
+
else
|
|
380
|
+
return extension(read())
|
|
381
|
+
} else {
|
|
382
|
+
let input = read()
|
|
383
|
+
for (let i = 0; i < currentExtensionRanges.length; i++) {
|
|
384
|
+
let value = currentExtensionRanges[i](token, input)
|
|
385
|
+
if (value !== undefined)
|
|
386
|
+
return value
|
|
387
|
+
}
|
|
388
|
+
return new Tag(input, token)
|
|
389
|
+
}
|
|
323
390
|
case 7: // fixed value
|
|
324
391
|
switch (token) {
|
|
325
392
|
case 0x14: return false
|
|
326
393
|
case 0x15: return true
|
|
327
394
|
case 0x16: return null
|
|
328
395
|
case 0x17: return; // undefined
|
|
329
|
-
case 0x1f:
|
|
396
|
+
case 0x1f:
|
|
330
397
|
default:
|
|
331
|
-
let packedValue = packedValues[token]
|
|
398
|
+
let packedValue = (packedValues || getPackedValues())[token]
|
|
332
399
|
if (packedValue !== undefined)
|
|
333
400
|
return packedValue
|
|
334
401
|
throw new Error('Unknown token ' + token)
|
|
@@ -344,41 +411,47 @@ export function read() {
|
|
|
344
411
|
}
|
|
345
412
|
const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/
|
|
346
413
|
function createStructureReader(structure) {
|
|
347
|
-
let l = structure.length
|
|
348
414
|
function readObject() {
|
|
415
|
+
// get the array size from the header
|
|
416
|
+
let length = src[position++]
|
|
417
|
+
//let majorType = token >> 5
|
|
418
|
+
length = length & 0x1f
|
|
419
|
+
if (length > 0x17) {
|
|
420
|
+
switch (length) {
|
|
421
|
+
case 0x18:
|
|
422
|
+
length = src[position++]
|
|
423
|
+
break
|
|
424
|
+
case 0x19:
|
|
425
|
+
length = dataView.getUint16(position)
|
|
426
|
+
position += 2
|
|
427
|
+
break
|
|
428
|
+
case 0x1a:
|
|
429
|
+
length = dataView.getUint32(position)
|
|
430
|
+
position += 4
|
|
431
|
+
break
|
|
432
|
+
default:
|
|
433
|
+
throw new Error('Expected array header, but got ' + src[position - 1])
|
|
434
|
+
}
|
|
435
|
+
}
|
|
349
436
|
// This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
|
|
350
|
-
if (
|
|
351
|
-
|
|
352
|
-
|
|
437
|
+
if (this.objectLiteralSize === length) // we have a fast object literal reader, use it (assuming it is the right length)
|
|
438
|
+
return this.objectLiteral(read)
|
|
439
|
+
if (this.count++ == 3) { // create a fast reader
|
|
440
|
+
this.objectLiteralSize = length
|
|
441
|
+
this.objectLiteral = currentDecoder.keyMap
|
|
442
|
+
? new Function('r', 'return {' + this.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
|
|
443
|
+
: new Function('r', 'return {' + this.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}')
|
|
444
|
+
return this.objectLiteral(read)
|
|
353
445
|
}
|
|
354
|
-
readArrayHeader(l)
|
|
355
446
|
let object = {}
|
|
356
|
-
for (let i = 0; i <
|
|
357
|
-
|
|
358
|
-
object[key] = read()
|
|
359
|
-
}
|
|
447
|
+
if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read()
|
|
448
|
+
else for (let i = 0; i < length; i++) object[this[i]] = read()
|
|
360
449
|
return object
|
|
361
450
|
}
|
|
362
|
-
|
|
451
|
+
structure.count = 0
|
|
363
452
|
return readObject
|
|
364
453
|
}
|
|
365
454
|
|
|
366
|
-
function readArrayHeader(expectedLength) {
|
|
367
|
-
// consume the array header, TODO: check expected length
|
|
368
|
-
let token = src[position++]
|
|
369
|
-
//let majorType = token >> 5
|
|
370
|
-
token = token & 0x1f
|
|
371
|
-
if (token > 0x17) {
|
|
372
|
-
switch (token) {
|
|
373
|
-
case 0x18: position++
|
|
374
|
-
break
|
|
375
|
-
case 0x19: position += 2
|
|
376
|
-
break
|
|
377
|
-
case 0x1a: position += 4
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
455
|
let readFixedString = readStringJS
|
|
383
456
|
let readString8 = readStringJS
|
|
384
457
|
let readString16 = readStringJS
|
|
@@ -395,6 +468,8 @@ export function setExtractor(extractStrings) {
|
|
|
395
468
|
return function readString(length) {
|
|
396
469
|
let string = strings[stringPosition++]
|
|
397
470
|
if (string == null) {
|
|
471
|
+
if (bundledStrings)
|
|
472
|
+
return readStringJS(length)
|
|
398
473
|
let extraction = extractStrings(position, srcEnd, length, src)
|
|
399
474
|
if (typeof extraction == 'string') {
|
|
400
475
|
string = extraction
|
|
@@ -721,12 +796,13 @@ function readKey() {
|
|
|
721
796
|
}
|
|
722
797
|
|
|
723
798
|
export class Tag {
|
|
724
|
-
constructor(value) {
|
|
799
|
+
constructor(value, tag) {
|
|
725
800
|
this.value = value
|
|
801
|
+
this.tag = tag
|
|
726
802
|
}
|
|
727
803
|
}
|
|
728
804
|
|
|
729
|
-
let glbl = typeof
|
|
805
|
+
let glbl = typeof self == 'object' ? self : global
|
|
730
806
|
|
|
731
807
|
currentExtensions[0] = (dateString) => {
|
|
732
808
|
// string date extension
|
|
@@ -740,38 +816,62 @@ currentExtensions[1] = (epochSec) => {
|
|
|
740
816
|
|
|
741
817
|
currentExtensions[2] = (buffer) => {
|
|
742
818
|
// bigint extension
|
|
743
|
-
|
|
819
|
+
let value = BigInt(0)
|
|
820
|
+
for (let i = 0, l = buffer.byteLength; i < l; i++) {
|
|
821
|
+
value = BigInt(buffer[i]) + value << BigInt(8)
|
|
822
|
+
}
|
|
823
|
+
return value
|
|
744
824
|
}
|
|
745
825
|
|
|
746
826
|
currentExtensions[3] = (buffer) => {
|
|
747
827
|
// negative bigint extension
|
|
748
|
-
return BigInt(-1) - (
|
|
828
|
+
return BigInt(-1) - currentExtensions[2](buffer)
|
|
829
|
+
}
|
|
830
|
+
currentExtensions[4] = (fraction) => {
|
|
831
|
+
// best to reparse to maintain accuracy
|
|
832
|
+
return +(fraction[1] + 'e' + fraction[0])
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
currentExtensions[5] = (fraction) => {
|
|
836
|
+
// probably not sufficiently accurate
|
|
837
|
+
return fraction[1] * Math.exp(fraction[0] * Math.log(2))
|
|
749
838
|
}
|
|
750
839
|
|
|
751
840
|
// the registration of the record definition extension (tag 105)
|
|
752
|
-
const recordDefinition = () => {
|
|
753
|
-
let
|
|
754
|
-
let structure = definition[
|
|
755
|
-
|
|
756
|
-
currentStructures[id & 0xff] = structure
|
|
841
|
+
const recordDefinition = (definition) => {
|
|
842
|
+
let id = definition[0] - 0xe000
|
|
843
|
+
let structure = definition[1]
|
|
844
|
+
currentStructures[id] = structure
|
|
757
845
|
structure.read = createStructureReader(structure)
|
|
758
846
|
let object = {}
|
|
759
|
-
for (let i = 2,l = definition.length; i < l; i++) {
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
847
|
+
if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
|
|
848
|
+
let key = currentDecoder.decodeKey(structure[i - 2])
|
|
849
|
+
object[key] = definition[i]
|
|
850
|
+
}
|
|
851
|
+
else for (let i = 2,l = definition.length; i < l; i++) {
|
|
852
|
+
let key = structure[i - 2]
|
|
853
|
+
object[key] = definition[i]
|
|
854
|
+
}
|
|
763
855
|
return object
|
|
764
856
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
857
|
+
currentExtensions[LEGACY_RECORD_INLINE_ID] = recordDefinition
|
|
858
|
+
currentExtensions[14] = (value) => {
|
|
859
|
+
if (bundledStrings)
|
|
860
|
+
return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
|
|
861
|
+
return new Tag(value, 14)
|
|
862
|
+
}
|
|
863
|
+
currentExtensions[15] = (value) => {
|
|
864
|
+
if (bundledStrings)
|
|
865
|
+
return bundledStrings[1].slice(bundledStrings.position1, bundledStrings.position1 += value)
|
|
866
|
+
return new Tag(value, 15)
|
|
867
|
+
}
|
|
768
868
|
|
|
769
869
|
currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
|
|
770
870
|
return (glbl[data[0]] || Error)(data[1], data[2])
|
|
771
871
|
}
|
|
772
872
|
const packedTable = (read) => {
|
|
773
873
|
if (src[position++] != 0x84)
|
|
774
|
-
throw new Error('Packed values structure must be followed by 4 element array')
|
|
874
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
775
875
|
let newPackedValues = read() // packed values
|
|
776
876
|
packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues
|
|
777
877
|
packedValues.prefixes = read()
|
|
@@ -782,15 +882,36 @@ packedTable.handlesRead = true
|
|
|
782
882
|
currentExtensions[51] = packedTable
|
|
783
883
|
|
|
784
884
|
currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
|
|
885
|
+
if (!packedValues) {
|
|
886
|
+
if (currentDecoder.getShared)
|
|
887
|
+
loadShared()
|
|
888
|
+
else
|
|
889
|
+
return new Tag(data, PACKED_REFERENCE_TAG_ID)
|
|
890
|
+
}
|
|
785
891
|
if (typeof data == 'number')
|
|
786
892
|
return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
|
|
787
893
|
throw new Error('No support for non-integer packed references yet')
|
|
788
894
|
}
|
|
895
|
+
currentExtensions[25] = (id) => {
|
|
896
|
+
return stringRefs[id]
|
|
897
|
+
}
|
|
898
|
+
currentExtensions[256] = (read) => {
|
|
899
|
+
stringRefs = []
|
|
900
|
+
try {
|
|
901
|
+
return read()
|
|
902
|
+
} finally {
|
|
903
|
+
stringRefs = null
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
currentExtensions[256].handlesRead = true
|
|
789
907
|
|
|
790
|
-
currentExtensions[
|
|
791
|
-
//
|
|
792
|
-
if (!referenceMap)
|
|
908
|
+
currentExtensions[28] = (read) => {
|
|
909
|
+
// shareable http://cbor.schmorp.de/value-sharing (for structured clones)
|
|
910
|
+
if (!referenceMap) {
|
|
793
911
|
referenceMap = new Map()
|
|
912
|
+
referenceMap.id = 0
|
|
913
|
+
}
|
|
914
|
+
let id = referenceMap.id++
|
|
794
915
|
let token = src[position]
|
|
795
916
|
let target
|
|
796
917
|
// TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
|
|
@@ -808,9 +929,10 @@ currentExtensions[40009] = (id) => {
|
|
|
808
929
|
refEntry.target = targetProperties // the placeholder wasn't used, replace with the deserialized one
|
|
809
930
|
return targetProperties // no cycle, can just use the returned read object
|
|
810
931
|
}
|
|
932
|
+
currentExtensions[28].handlesRead = true
|
|
811
933
|
|
|
812
|
-
currentExtensions[
|
|
813
|
-
//
|
|
934
|
+
currentExtensions[29] = (id) => {
|
|
935
|
+
// sharedref http://cbor.schmorp.de/value-sharing (for structured clones)
|
|
814
936
|
let refEntry = referenceMap.get(id)
|
|
815
937
|
refEntry.used = true
|
|
816
938
|
return refEntry.target
|
|
@@ -833,19 +955,36 @@ function combine(a, b) {
|
|
|
833
955
|
return a.concat(b)
|
|
834
956
|
return Object.assign({}, a, b)
|
|
835
957
|
}
|
|
958
|
+
function getPackedValues() {
|
|
959
|
+
if (!packedValues) {
|
|
960
|
+
if (currentDecoder.getShared)
|
|
961
|
+
loadShared()
|
|
962
|
+
else
|
|
963
|
+
throw new Error('No packed values available')
|
|
964
|
+
}
|
|
965
|
+
return packedValues
|
|
966
|
+
}
|
|
967
|
+
const SHARED_DATA_TAG_ID = 0x53687264 // ascii 'Shrd'
|
|
836
968
|
currentExtensionRanges.push((tag, input) => {
|
|
837
969
|
if (tag >= 225 && tag <= 255)
|
|
838
|
-
return combine(
|
|
970
|
+
return combine(getPackedValues().prefixes[tag - 224], input)
|
|
839
971
|
if (tag >= 28704 && tag <= 32767)
|
|
840
|
-
return combine(
|
|
972
|
+
return combine(getPackedValues().prefixes[tag - 28672], input)
|
|
841
973
|
if (tag >= 1879052288 && tag <= 2147483647)
|
|
842
|
-
return combine(
|
|
974
|
+
return combine(getPackedValues().prefixes[tag - 1879048192], input)
|
|
843
975
|
if (tag >= 216 && tag <= 223)
|
|
844
|
-
return combine(input,
|
|
976
|
+
return combine(input, getPackedValues().suffixes[tag - 216])
|
|
845
977
|
if (tag >= 27647 && tag <= 28671)
|
|
846
|
-
return combine(input,
|
|
978
|
+
return combine(input, getPackedValues().suffixes[tag - 27639])
|
|
847
979
|
if (tag >= 1811940352 && tag <= 1879048191)
|
|
848
|
-
return combine(input,
|
|
980
|
+
return combine(input, getPackedValues().suffixes[tag - 1811939328])
|
|
981
|
+
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)
|
|
982
|
+
return {
|
|
983
|
+
packedValues: packedValues,
|
|
984
|
+
structures: currentStructures.slice(0),
|
|
985
|
+
version: input,
|
|
986
|
+
}
|
|
987
|
+
}
|
|
849
988
|
})
|
|
850
989
|
|
|
851
990
|
export const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array')
|
|
@@ -862,6 +1001,61 @@ function registerTypedArray(typedArrayName, tag) {
|
|
|
862
1001
|
}
|
|
863
1002
|
}
|
|
864
1003
|
|
|
1004
|
+
function readBundleExt() {
|
|
1005
|
+
let length = readJustLength()
|
|
1006
|
+
let bundlePosition = position + read()
|
|
1007
|
+
for (let i = 2; i < length; i++) {
|
|
1008
|
+
// skip past bundles that were already read
|
|
1009
|
+
let bundleLength = readJustLength() // this will increment position, so must add to position afterwards
|
|
1010
|
+
position += bundleLength
|
|
1011
|
+
}
|
|
1012
|
+
let dataPosition = position
|
|
1013
|
+
position = bundlePosition
|
|
1014
|
+
bundledStrings = [readStringJS(readJustLength()), readStringJS(readJustLength())]
|
|
1015
|
+
bundledStrings.position0 = 0
|
|
1016
|
+
bundledStrings.position1 = 0
|
|
1017
|
+
bundledStrings.postBundlePosition = position
|
|
1018
|
+
position = dataPosition
|
|
1019
|
+
return read()
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
function readJustLength() {
|
|
1023
|
+
let token = src[position++] & 0x1f
|
|
1024
|
+
if (token > 0x17) {
|
|
1025
|
+
switch (token) {
|
|
1026
|
+
case 0x18:
|
|
1027
|
+
token = src[position++]
|
|
1028
|
+
break
|
|
1029
|
+
case 0x19:
|
|
1030
|
+
token = dataView.getUint16(position)
|
|
1031
|
+
position += 2
|
|
1032
|
+
break
|
|
1033
|
+
case 0x1a:
|
|
1034
|
+
token = dataView.getUint32(position)
|
|
1035
|
+
position += 4
|
|
1036
|
+
break
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
return token
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
function loadShared() {
|
|
1043
|
+
if (currentDecoder.getShared) {
|
|
1044
|
+
let sharedData = saveState(() => {
|
|
1045
|
+
// save the state in case getShared modifies our buffer
|
|
1046
|
+
src = null
|
|
1047
|
+
return currentDecoder.getShared()
|
|
1048
|
+
}) || {}
|
|
1049
|
+
let updatedStructures = sharedData.structures || []
|
|
1050
|
+
currentDecoder.sharedVersion = sharedData.version
|
|
1051
|
+
packedValues = currentDecoder.sharedValues = sharedData.packedValues
|
|
1052
|
+
if (currentStructures === true)
|
|
1053
|
+
currentDecoder.structures = currentStructures = updatedStructures
|
|
1054
|
+
else
|
|
1055
|
+
currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures))
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
865
1059
|
function saveState(callback) {
|
|
866
1060
|
let savedSrcEnd = srcEnd
|
|
867
1061
|
let savedPosition = position
|
|
@@ -871,6 +1065,8 @@ function saveState(callback) {
|
|
|
871
1065
|
let savedSrcString = srcString
|
|
872
1066
|
let savedStrings = strings
|
|
873
1067
|
let savedReferenceMap = referenceMap
|
|
1068
|
+
let savedBundledStrings = bundledStrings
|
|
1069
|
+
|
|
874
1070
|
// TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
|
|
875
1071
|
let savedSrc = new Uint8Array(src.slice(0, srcEnd)) // we copy the data in case it changes while external data is processed
|
|
876
1072
|
let savedStructures = currentStructures
|
|
@@ -885,6 +1081,7 @@ function saveState(callback) {
|
|
|
885
1081
|
srcString = savedSrcString
|
|
886
1082
|
strings = savedStrings
|
|
887
1083
|
referenceMap = savedReferenceMap
|
|
1084
|
+
bundledStrings = savedBundledStrings
|
|
888
1085
|
src = savedSrc
|
|
889
1086
|
sequentialMode = savedSequentialMode
|
|
890
1087
|
currentStructures = savedStructures
|