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/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
|
|
@@ -190,7 +254,11 @@ export function read() {
|
|
|
190
254
|
position += 8
|
|
191
255
|
return value
|
|
192
256
|
}
|
|
193
|
-
if (
|
|
257
|
+
if (majorType > 1) {
|
|
258
|
+
if (dataView.getUint32(position) > 0)
|
|
259
|
+
throw new Error('JavaScript does not support arrays, maps, or strings with length over 4294967295')
|
|
260
|
+
token = dataView.getUint32(position + 4)
|
|
261
|
+
} else if (currentDecoder.int64AsNumber) {
|
|
194
262
|
token = dataView.getUint32(position) * 0x100000000
|
|
195
263
|
token += dataView.getUint32(position + 4)
|
|
196
264
|
} else
|
|
@@ -202,6 +270,7 @@ export function read() {
|
|
|
202
270
|
switch(majorType) {
|
|
203
271
|
case 2: // byte string
|
|
204
272
|
case 3: // text string
|
|
273
|
+
throw new Error('Indefinite length not supported for byte or text strings')
|
|
205
274
|
case 4: // array
|
|
206
275
|
let array = []
|
|
207
276
|
let value, i = 0
|
|
@@ -213,8 +282,8 @@ export function read() {
|
|
|
213
282
|
let key
|
|
214
283
|
if (currentDecoder.mapsAsObjects) {
|
|
215
284
|
let object = {}
|
|
216
|
-
|
|
217
|
-
|
|
285
|
+
if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[currentDecoder.decodeKey(key)] = read()
|
|
286
|
+
else while ((key = read()) != STOP_CODE) object[key] = read()
|
|
218
287
|
return object
|
|
219
288
|
} else {
|
|
220
289
|
if (restoreMapsAsObject) {
|
|
@@ -222,8 +291,8 @@ export function read() {
|
|
|
222
291
|
restoreMapsAsObject = false
|
|
223
292
|
}
|
|
224
293
|
let map = new Map()
|
|
225
|
-
|
|
226
|
-
|
|
294
|
+
if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) map.set(currentDecoder.decodeKey(key), read())
|
|
295
|
+
else while ((key = read()) != STOP_CODE) map.set(key, read())
|
|
227
296
|
return map
|
|
228
297
|
}
|
|
229
298
|
case 7:
|
|
@@ -255,16 +324,15 @@ export function read() {
|
|
|
255
324
|
return readFixedString(token)
|
|
256
325
|
case 4: // array
|
|
257
326
|
let array = new Array(token)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
327
|
+
//if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
|
|
328
|
+
//else
|
|
329
|
+
for (let i = 0; i < token; i++) array[i] = read()
|
|
261
330
|
return array
|
|
262
331
|
case 5: // map
|
|
263
332
|
if (currentDecoder.mapsAsObjects) {
|
|
264
333
|
let object = {}
|
|
265
|
-
for (let i = 0; i < token; i++)
|
|
266
|
-
|
|
267
|
-
}
|
|
334
|
+
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[currentDecoder.decodeKey(read())] = read()
|
|
335
|
+
else for (let i = 0; i < token; i++) object[read()] = read()
|
|
268
336
|
return object
|
|
269
337
|
} else {
|
|
270
338
|
if (restoreMapsAsObject) {
|
|
@@ -272,53 +340,56 @@ export function read() {
|
|
|
272
340
|
restoreMapsAsObject = false
|
|
273
341
|
}
|
|
274
342
|
let map = new Map()
|
|
275
|
-
for (let i = 0; i < token; i++)
|
|
276
|
-
|
|
277
|
-
}
|
|
343
|
+
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read())
|
|
344
|
+
else for (let i = 0; i < token; i++) map.set(read(), read())
|
|
278
345
|
return map
|
|
279
346
|
}
|
|
280
347
|
case 6: // extension
|
|
281
|
-
if (
|
|
282
|
-
let structure = currentStructures[token &
|
|
348
|
+
if (token >= BUNDLED_STRINGS_ID) {
|
|
349
|
+
let structure = currentStructures[token & 0x1fff] // check record structures first
|
|
350
|
+
// 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
351
|
if (structure) {
|
|
284
|
-
if (!structure.read)
|
|
285
|
-
structure.read = createStructureReader(structure)
|
|
352
|
+
if (!structure.read) structure.read = createStructureReader(structure)
|
|
286
353
|
return structure.read()
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if (
|
|
299
|
-
|
|
300
|
-
structure.read = createStructureReader(structure)
|
|
301
|
-
return structure.read()
|
|
302
|
-
} else
|
|
303
|
-
return token
|
|
304
|
-
} else
|
|
305
|
-
return token
|
|
306
|
-
} else {
|
|
307
|
-
let extension = currentExtensions[token]
|
|
308
|
-
if (extension) {
|
|
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
|
|
354
|
+
}
|
|
355
|
+
if (token < 0x10000) {
|
|
356
|
+
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)
|
|
357
|
+
return recordDefinition(read())
|
|
358
|
+
else if (token == RECORD_DEFINITIONS_ID) {
|
|
359
|
+
let length = readJustLength()
|
|
360
|
+
let id = read()
|
|
361
|
+
for (let i = 2; i < length; i++) {
|
|
362
|
+
recordDefinition([id++, read()])
|
|
363
|
+
}
|
|
364
|
+
return read()
|
|
365
|
+
} else if (token == BUNDLED_STRINGS_ID) {
|
|
366
|
+
return readBundleExt()
|
|
319
367
|
}
|
|
320
|
-
|
|
368
|
+
if (currentDecoder.getShared) {
|
|
369
|
+
loadShared()
|
|
370
|
+
structure = currentStructures[token & 0x1fff]
|
|
371
|
+
if (structure) {
|
|
372
|
+
if (!structure.read)
|
|
373
|
+
structure.read = createStructureReader(structure)
|
|
374
|
+
return structure.read()
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
let extension = currentExtensions[token]
|
|
380
|
+
if (extension) {
|
|
381
|
+
if (extension.handlesRead)
|
|
382
|
+
return extension(read)
|
|
383
|
+
else
|
|
384
|
+
return extension(read())
|
|
385
|
+
} else {
|
|
386
|
+
let input = read()
|
|
387
|
+
for (let i = 0; i < currentExtensionRanges.length; i++) {
|
|
388
|
+
let value = currentExtensionRanges[i](token, input)
|
|
389
|
+
if (value !== undefined)
|
|
390
|
+
return value
|
|
321
391
|
}
|
|
392
|
+
return new Tag(input, token)
|
|
322
393
|
}
|
|
323
394
|
case 7: // fixed value
|
|
324
395
|
switch (token) {
|
|
@@ -326,9 +397,9 @@ export function read() {
|
|
|
326
397
|
case 0x15: return true
|
|
327
398
|
case 0x16: return null
|
|
328
399
|
case 0x17: return; // undefined
|
|
329
|
-
case 0x1f:
|
|
400
|
+
case 0x1f:
|
|
330
401
|
default:
|
|
331
|
-
let packedValue = packedValues[token]
|
|
402
|
+
let packedValue = (packedValues || getPackedValues())[token]
|
|
332
403
|
if (packedValue !== undefined)
|
|
333
404
|
return packedValue
|
|
334
405
|
throw new Error('Unknown token ' + token)
|
|
@@ -344,41 +415,56 @@ export function read() {
|
|
|
344
415
|
}
|
|
345
416
|
const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/
|
|
346
417
|
function createStructureReader(structure) {
|
|
347
|
-
let l = structure.length
|
|
348
418
|
function readObject() {
|
|
419
|
+
// get the array size from the header
|
|
420
|
+
let length = src[position++]
|
|
421
|
+
//let majorType = token >> 5
|
|
422
|
+
length = length & 0x1f
|
|
423
|
+
if (length > 0x17) {
|
|
424
|
+
switch (length) {
|
|
425
|
+
case 0x18:
|
|
426
|
+
length = src[position++]
|
|
427
|
+
break
|
|
428
|
+
case 0x19:
|
|
429
|
+
length = dataView.getUint16(position)
|
|
430
|
+
position += 2
|
|
431
|
+
break
|
|
432
|
+
case 0x1a:
|
|
433
|
+
length = dataView.getUint32(position)
|
|
434
|
+
position += 4
|
|
435
|
+
break
|
|
436
|
+
default:
|
|
437
|
+
throw new Error('Expected array header, but got ' + src[position - 1])
|
|
438
|
+
}
|
|
439
|
+
}
|
|
349
440
|
// This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
441
|
+
let compiledReader = this.compiledReader // first look to see if we have the fast compiled function
|
|
442
|
+
while(compiledReader) {
|
|
443
|
+
// we have a fast compiled object literal reader
|
|
444
|
+
if (compiledReader.propertyCount === length)
|
|
445
|
+
return compiledReader(read) // with the right length, so we use it
|
|
446
|
+
compiledReader = compiledReader.next // see if there is another reader with the right length
|
|
353
447
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
448
|
+
if (this.slowReads++ >= 3) { // create a fast compiled reader
|
|
449
|
+
let array = this.length == length ? this : this.slice(0, length)
|
|
450
|
+
compiledReader = currentDecoder.keyMap
|
|
451
|
+
? new Function('r', 'return {' + array.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
|
|
452
|
+
: new Function('r', 'return {' + array.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}')
|
|
453
|
+
if (this.compiledReader)
|
|
454
|
+
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
|
|
455
|
+
compiledReader.propertyCount = length
|
|
456
|
+
this.compiledReader = compiledReader
|
|
457
|
+
return compiledReader(read)
|
|
359
458
|
}
|
|
459
|
+
let object = {}
|
|
460
|
+
if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read()
|
|
461
|
+
else for (let i = 0; i < length; i++) object[this[i]] = read()
|
|
360
462
|
return object
|
|
361
463
|
}
|
|
362
|
-
|
|
464
|
+
structure.slowReads = 0
|
|
363
465
|
return readObject
|
|
364
466
|
}
|
|
365
467
|
|
|
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
468
|
let readFixedString = readStringJS
|
|
383
469
|
let readString8 = readStringJS
|
|
384
470
|
let readString16 = readStringJS
|
|
@@ -395,6 +481,8 @@ export function setExtractor(extractStrings) {
|
|
|
395
481
|
return function readString(length) {
|
|
396
482
|
let string = strings[stringPosition++]
|
|
397
483
|
if (string == null) {
|
|
484
|
+
if (bundledStrings)
|
|
485
|
+
return readStringJS(length)
|
|
398
486
|
let extraction = extractStrings(position, srcEnd, length, src)
|
|
399
487
|
if (typeof extraction == 'string') {
|
|
400
488
|
string = extraction
|
|
@@ -721,12 +809,13 @@ function readKey() {
|
|
|
721
809
|
}
|
|
722
810
|
|
|
723
811
|
export class Tag {
|
|
724
|
-
constructor(value) {
|
|
812
|
+
constructor(value, tag) {
|
|
725
813
|
this.value = value
|
|
814
|
+
this.tag = tag
|
|
726
815
|
}
|
|
727
816
|
}
|
|
728
817
|
|
|
729
|
-
let glbl = typeof
|
|
818
|
+
let glbl = typeof self == 'object' ? self : global
|
|
730
819
|
|
|
731
820
|
currentExtensions[0] = (dateString) => {
|
|
732
821
|
// string date extension
|
|
@@ -740,38 +829,67 @@ currentExtensions[1] = (epochSec) => {
|
|
|
740
829
|
|
|
741
830
|
currentExtensions[2] = (buffer) => {
|
|
742
831
|
// bigint extension
|
|
743
|
-
|
|
832
|
+
let value = BigInt(0)
|
|
833
|
+
for (let i = 0, l = buffer.byteLength; i < l; i++) {
|
|
834
|
+
value = BigInt(buffer[i]) + value << BigInt(8)
|
|
835
|
+
}
|
|
836
|
+
return value
|
|
744
837
|
}
|
|
745
838
|
|
|
746
839
|
currentExtensions[3] = (buffer) => {
|
|
747
840
|
// negative bigint extension
|
|
748
|
-
return BigInt(-1) - (
|
|
841
|
+
return BigInt(-1) - currentExtensions[2](buffer)
|
|
842
|
+
}
|
|
843
|
+
currentExtensions[4] = (fraction) => {
|
|
844
|
+
// best to reparse to maintain accuracy
|
|
845
|
+
return +(fraction[1] + 'e' + fraction[0])
|
|
749
846
|
}
|
|
750
847
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
848
|
+
currentExtensions[5] = (fraction) => {
|
|
849
|
+
// probably not sufficiently accurate
|
|
850
|
+
return fraction[1] * Math.exp(fraction[0] * Math.log(2))
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// the registration of the record definition extension
|
|
854
|
+
const recordDefinition = (definition) => {
|
|
855
|
+
let id = definition[0] - 0xe000
|
|
856
|
+
let structure = definition[1]
|
|
857
|
+
let existingStructure = currentStructures[id]
|
|
858
|
+
if (existingStructure && existingStructure.isShared) {
|
|
859
|
+
(currentStructures.restoreStructures || (currentStructures.restoreStructures = []))[id] = existingStructure
|
|
860
|
+
}
|
|
861
|
+
currentStructures[id] = structure
|
|
862
|
+
|
|
757
863
|
structure.read = createStructureReader(structure)
|
|
758
864
|
let object = {}
|
|
759
|
-
for (let i = 2,l = definition.length; i < l; i++) {
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
865
|
+
if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
|
|
866
|
+
let key = currentDecoder.decodeKey(structure[i - 2])
|
|
867
|
+
object[key] = definition[i]
|
|
868
|
+
}
|
|
869
|
+
else for (let i = 2,l = definition.length; i < l; i++) {
|
|
870
|
+
let key = structure[i - 2]
|
|
871
|
+
object[key] = definition[i]
|
|
872
|
+
}
|
|
763
873
|
return object
|
|
764
874
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
875
|
+
currentExtensions[LEGACY_RECORD_INLINE_ID] = recordDefinition
|
|
876
|
+
currentExtensions[14] = (value) => {
|
|
877
|
+
if (bundledStrings)
|
|
878
|
+
return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
|
|
879
|
+
return new Tag(value, 14)
|
|
880
|
+
}
|
|
881
|
+
currentExtensions[15] = (value) => {
|
|
882
|
+
if (bundledStrings)
|
|
883
|
+
return bundledStrings[1].slice(bundledStrings.position1, bundledStrings.position1 += value)
|
|
884
|
+
return new Tag(value, 15)
|
|
885
|
+
}
|
|
768
886
|
|
|
769
887
|
currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
|
|
770
888
|
return (glbl[data[0]] || Error)(data[1], data[2])
|
|
771
889
|
}
|
|
772
890
|
const packedTable = (read) => {
|
|
773
891
|
if (src[position++] != 0x84)
|
|
774
|
-
throw new Error('Packed values structure must be followed by 4 element array')
|
|
892
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
775
893
|
let newPackedValues = read() // packed values
|
|
776
894
|
packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues
|
|
777
895
|
packedValues.prefixes = read()
|
|
@@ -782,15 +900,36 @@ packedTable.handlesRead = true
|
|
|
782
900
|
currentExtensions[51] = packedTable
|
|
783
901
|
|
|
784
902
|
currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
|
|
903
|
+
if (!packedValues) {
|
|
904
|
+
if (currentDecoder.getShared)
|
|
905
|
+
loadShared()
|
|
906
|
+
else
|
|
907
|
+
return new Tag(data, PACKED_REFERENCE_TAG_ID)
|
|
908
|
+
}
|
|
785
909
|
if (typeof data == 'number')
|
|
786
910
|
return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
|
|
787
911
|
throw new Error('No support for non-integer packed references yet')
|
|
788
912
|
}
|
|
913
|
+
currentExtensions[25] = (id) => {
|
|
914
|
+
return stringRefs[id]
|
|
915
|
+
}
|
|
916
|
+
currentExtensions[256] = (read) => {
|
|
917
|
+
stringRefs = []
|
|
918
|
+
try {
|
|
919
|
+
return read()
|
|
920
|
+
} finally {
|
|
921
|
+
stringRefs = null
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
currentExtensions[256].handlesRead = true
|
|
789
925
|
|
|
790
|
-
currentExtensions[
|
|
791
|
-
//
|
|
792
|
-
if (!referenceMap)
|
|
926
|
+
currentExtensions[28] = (read) => {
|
|
927
|
+
// shareable http://cbor.schmorp.de/value-sharing (for structured clones)
|
|
928
|
+
if (!referenceMap) {
|
|
793
929
|
referenceMap = new Map()
|
|
930
|
+
referenceMap.id = 0
|
|
931
|
+
}
|
|
932
|
+
let id = referenceMap.id++
|
|
794
933
|
let token = src[position]
|
|
795
934
|
let target
|
|
796
935
|
// TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
|
|
@@ -808,9 +947,10 @@ currentExtensions[40009] = (id) => {
|
|
|
808
947
|
refEntry.target = targetProperties // the placeholder wasn't used, replace with the deserialized one
|
|
809
948
|
return targetProperties // no cycle, can just use the returned read object
|
|
810
949
|
}
|
|
950
|
+
currentExtensions[28].handlesRead = true
|
|
811
951
|
|
|
812
|
-
currentExtensions[
|
|
813
|
-
//
|
|
952
|
+
currentExtensions[29] = (id) => {
|
|
953
|
+
// sharedref http://cbor.schmorp.de/value-sharing (for structured clones)
|
|
814
954
|
let refEntry = referenceMap.get(id)
|
|
815
955
|
refEntry.used = true
|
|
816
956
|
return refEntry.target
|
|
@@ -833,32 +973,125 @@ function combine(a, b) {
|
|
|
833
973
|
return a.concat(b)
|
|
834
974
|
return Object.assign({}, a, b)
|
|
835
975
|
}
|
|
976
|
+
function getPackedValues() {
|
|
977
|
+
if (!packedValues) {
|
|
978
|
+
if (currentDecoder.getShared)
|
|
979
|
+
loadShared()
|
|
980
|
+
else
|
|
981
|
+
throw new Error('No packed values available')
|
|
982
|
+
}
|
|
983
|
+
return packedValues
|
|
984
|
+
}
|
|
985
|
+
const SHARED_DATA_TAG_ID = 0x53687264 // ascii 'Shrd'
|
|
836
986
|
currentExtensionRanges.push((tag, input) => {
|
|
837
987
|
if (tag >= 225 && tag <= 255)
|
|
838
|
-
return combine(
|
|
988
|
+
return combine(getPackedValues().prefixes[tag - 224], input)
|
|
839
989
|
if (tag >= 28704 && tag <= 32767)
|
|
840
|
-
return combine(
|
|
990
|
+
return combine(getPackedValues().prefixes[tag - 28672], input)
|
|
841
991
|
if (tag >= 1879052288 && tag <= 2147483647)
|
|
842
|
-
return combine(
|
|
992
|
+
return combine(getPackedValues().prefixes[tag - 1879048192], input)
|
|
843
993
|
if (tag >= 216 && tag <= 223)
|
|
844
|
-
return combine(input,
|
|
994
|
+
return combine(input, getPackedValues().suffixes[tag - 216])
|
|
845
995
|
if (tag >= 27647 && tag <= 28671)
|
|
846
|
-
return combine(input,
|
|
996
|
+
return combine(input, getPackedValues().suffixes[tag - 27639])
|
|
847
997
|
if (tag >= 1811940352 && tag <= 1879048191)
|
|
848
|
-
return combine(input,
|
|
998
|
+
return combine(input, getPackedValues().suffixes[tag - 1811939328])
|
|
999
|
+
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)
|
|
1000
|
+
return {
|
|
1001
|
+
packedValues: packedValues,
|
|
1002
|
+
structures: currentStructures.slice(0),
|
|
1003
|
+
version: input,
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
if (tag == 55799) // self-descriptive CBOR tag, just return input value
|
|
1007
|
+
return input
|
|
849
1008
|
})
|
|
850
1009
|
|
|
851
|
-
|
|
852
|
-
const
|
|
1010
|
+
const isLittleEndianMachine = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1
|
|
1011
|
+
export const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64']
|
|
1012
|
+
const typedArrayTags = [64, 68, 69, 70, 71, 72, 77, 78, 79, 85, 86]
|
|
853
1013
|
for (let i = 0; i < typedArrays.length; i++) {
|
|
854
1014
|
registerTypedArray(typedArrays[i], typedArrayTags[i])
|
|
855
1015
|
}
|
|
856
|
-
function registerTypedArray(
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
1016
|
+
function registerTypedArray(typedArrayId, tag, littleEndian) {
|
|
1017
|
+
let TypedArray = glbl[typedArrayId + 'Array']
|
|
1018
|
+
let bytesPerElement = TypedArray.BYTES_PER_ELEMENT
|
|
1019
|
+
for (let littleEndian = 0; littleEndian < 2; littleEndian++) {
|
|
1020
|
+
if (!littleEndian && bytesPerElement == 1)
|
|
1021
|
+
continue
|
|
1022
|
+
let sizeShift = bytesPerElement == 2 ? 1 : bytesPerElement == 4 ? 2 : 3
|
|
1023
|
+
currentExtensions[littleEndian ? tag : (tag - 4)] = (bytesPerElement == 1 || littleEndian == isLittleEndianMachine) ? (buffer) => {
|
|
1024
|
+
if (!TypedArray)
|
|
1025
|
+
throw new Error('Could not find typed array for code ' + tag)
|
|
1026
|
+
// we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
|
|
1027
|
+
return new TypedArray(Uint8Array.prototype.slice.call(buffer, 0).buffer)
|
|
1028
|
+
} : buffer => {
|
|
1029
|
+
if (!TypedArray)
|
|
1030
|
+
throw new Error('Could not find typed array for code ' + tag)
|
|
1031
|
+
let dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength)
|
|
1032
|
+
let elements = buffer.length >> sizeShift
|
|
1033
|
+
let ta = new TypedArray(elements)
|
|
1034
|
+
let method = dv['get' + typedArrayId]
|
|
1035
|
+
for (let i = 0; i < elements; i++) {
|
|
1036
|
+
ta[i] = method.call(dv, i << sizeShift, littleEndian)
|
|
1037
|
+
}
|
|
1038
|
+
return ta
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
function readBundleExt() {
|
|
1044
|
+
let length = readJustLength()
|
|
1045
|
+
let bundlePosition = position + read()
|
|
1046
|
+
for (let i = 2; i < length; i++) {
|
|
1047
|
+
// skip past bundles that were already read
|
|
1048
|
+
let bundleLength = readJustLength() // this will increment position, so must add to position afterwards
|
|
1049
|
+
position += bundleLength
|
|
1050
|
+
}
|
|
1051
|
+
let dataPosition = position
|
|
1052
|
+
position = bundlePosition
|
|
1053
|
+
bundledStrings = [readStringJS(readJustLength()), readStringJS(readJustLength())]
|
|
1054
|
+
bundledStrings.position0 = 0
|
|
1055
|
+
bundledStrings.position1 = 0
|
|
1056
|
+
bundledStrings.postBundlePosition = position
|
|
1057
|
+
position = dataPosition
|
|
1058
|
+
return read()
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
function readJustLength() {
|
|
1062
|
+
let token = src[position++] & 0x1f
|
|
1063
|
+
if (token > 0x17) {
|
|
1064
|
+
switch (token) {
|
|
1065
|
+
case 0x18:
|
|
1066
|
+
token = src[position++]
|
|
1067
|
+
break
|
|
1068
|
+
case 0x19:
|
|
1069
|
+
token = dataView.getUint16(position)
|
|
1070
|
+
position += 2
|
|
1071
|
+
break
|
|
1072
|
+
case 0x1a:
|
|
1073
|
+
token = dataView.getUint32(position)
|
|
1074
|
+
position += 4
|
|
1075
|
+
break
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
return token
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function loadShared() {
|
|
1082
|
+
if (currentDecoder.getShared) {
|
|
1083
|
+
let sharedData = saveState(() => {
|
|
1084
|
+
// save the state in case getShared modifies our buffer
|
|
1085
|
+
src = null
|
|
1086
|
+
return currentDecoder.getShared()
|
|
1087
|
+
}) || {}
|
|
1088
|
+
let updatedStructures = sharedData.structures || []
|
|
1089
|
+
currentDecoder.sharedVersion = sharedData.version
|
|
1090
|
+
packedValues = currentDecoder.sharedValues = sharedData.packedValues
|
|
1091
|
+
if (currentStructures === true)
|
|
1092
|
+
currentDecoder.structures = currentStructures = updatedStructures
|
|
1093
|
+
else
|
|
1094
|
+
currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures))
|
|
862
1095
|
}
|
|
863
1096
|
}
|
|
864
1097
|
|
|
@@ -871,6 +1104,8 @@ function saveState(callback) {
|
|
|
871
1104
|
let savedSrcString = srcString
|
|
872
1105
|
let savedStrings = strings
|
|
873
1106
|
let savedReferenceMap = referenceMap
|
|
1107
|
+
let savedBundledStrings = bundledStrings
|
|
1108
|
+
|
|
874
1109
|
// TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
|
|
875
1110
|
let savedSrc = new Uint8Array(src.slice(0, srcEnd)) // we copy the data in case it changes while external data is processed
|
|
876
1111
|
let savedStructures = currentStructures
|
|
@@ -885,6 +1120,7 @@ function saveState(callback) {
|
|
|
885
1120
|
srcString = savedSrcString
|
|
886
1121
|
strings = savedStrings
|
|
887
1122
|
referenceMap = savedReferenceMap
|
|
1123
|
+
bundledStrings = savedBundledStrings
|
|
888
1124
|
src = savedSrc
|
|
889
1125
|
sequentialMode = savedSequentialMode
|
|
890
1126
|
currentStructures = savedStructures
|