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/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 RECORD_TAG_ID = 0x69
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 && !options.structures)
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 (currentDecoder.int64AsNumber) {
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
- while ((key = readKey()) != STOP_CODE)
217
- object[key] = read()
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
- while ((key = read()) != STOP_CODE)
226
- map.set(key, read())
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
- for (let i = 0; i < token; i++) {
259
- array[i] = read()
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
- object[readKey()] = read()
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
- map.set(read(), read())
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 ((token >> 8) == RECORD_TAG_ID) { // record structures
282
- let structure = currentStructures[token & 0xff]
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
- } else if (currentDecoder.getStructures) {
288
- let updatedStructures = saveState(() => {
289
- // save the state in case getStructures modifies our buffer
290
- src = null
291
- return currentDecoder.getStructures()
292
- })
293
- if (currentStructures === true)
294
- currentDecoder.structures = currentStructures = updatedStructures
295
- else
296
- currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures))
297
- structure = currentStructures[token & 0xff]
298
- if (structure) {
299
- if (!structure.read)
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
- return new Tag(input)
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
- if (readObject.count++ > 2) {
351
- this.read = (new Function('a', 'r', 'return function(){a();return {' + structure.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}}'))(readArrayHeader, read)
352
- return this.read()
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
- readArrayHeader(l)
355
- let object = {}
356
- for (let i = 0; i < l; i++) {
357
- let key = structure[i]
358
- object[key] = read()
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
- readObject.count = 0
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 window == 'object' ? window : global
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
- return new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0)
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) - (new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0))
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
- // the registration of the record definition extension (tag 105)
752
- const recordDefinition = () => {
753
- let definition = read()
754
- let structure = definition[0]
755
- let id = definition[1]
756
- currentStructures[id & 0xff] = structure
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
- let key = structure[i - 2]
761
- object[key] = definition[i]
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
- recordDefinition.handlesRead = true
767
- currentExtensions[RECORD_TAG_ID] = recordDefinition
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[40009] = (id) => {
791
- // id extension (for structured clones)
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[40010] = (id) => {
813
- // pointer extension (for structured clones)
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(packedTable.prefixes[tag - 224], input)
988
+ return combine(getPackedValues().prefixes[tag - 224], input)
839
989
  if (tag >= 28704 && tag <= 32767)
840
- return combine(packedTable.prefixes[tag - 28672], input)
990
+ return combine(getPackedValues().prefixes[tag - 28672], input)
841
991
  if (tag >= 1879052288 && tag <= 2147483647)
842
- return combine(packedTable.prefixes[tag - 1879048192], input)
992
+ return combine(getPackedValues().prefixes[tag - 1879048192], input)
843
993
  if (tag >= 216 && tag <= 223)
844
- return combine(input, packedTable.suffixes[tag - 216])
994
+ return combine(input, getPackedValues().suffixes[tag - 216])
845
995
  if (tag >= 27647 && tag <= 28671)
846
- return combine(input, packedTable.suffixes[tag - 27639])
996
+ return combine(input, getPackedValues().suffixes[tag - 27639])
847
997
  if (tag >= 1811940352 && tag <= 1879048191)
848
- return combine(input, packedTable.suffixes[tag - 1811939328])
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
- export const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array')
852
- const typedArrayTags = [64, 68, 69, 70, 71, 72, 77, 78, 79, 81, 82]
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(typedArrayName, tag) {
857
- currentExtensions[tag] = (buffer) => {
858
- if (!typedArrayName)
859
- throw new Error('Could not find typed array for code ' + typeCode)
860
- // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
861
- return new glbl[typedArrayName](Uint8Array.prototype.slice.call(buffer, 0).buffer)
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