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/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 RECORD_TAG_ID = 0x69;
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 && !options.structures)
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 (currentDecoder.int64AsNumber) {
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
- while ((key = readKey()) != STOP_CODE)
218
- object[key] = read();
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
- while ((key = read()) != STOP_CODE)
227
- map.set(key, read());
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
- for (let i = 0; i < token; i++) {
260
- array[i] = read();
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
- object[readKey()] = read();
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
- map.set(read(), read());
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 ((token >> 8) == RECORD_TAG_ID) { // record structures
283
- let structure = currentStructures[token & 0xff];
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
- } else if (currentDecoder.getStructures) {
289
- let updatedStructures = saveState(() => {
290
- // save the state in case getStructures modifies our buffer
291
- src = null;
292
- return currentDecoder.getStructures()
293
- });
294
- if (currentStructures === true)
295
- currentDecoder.structures = currentStructures = updatedStructures;
296
- else
297
- currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
298
- structure = currentStructures[token & 0xff];
299
- if (structure) {
300
- if (!structure.read)
301
- structure.read = createStructureReader(structure);
302
- return structure.read()
303
- } else
304
- return token
305
- } else
306
- return token
307
- } else {
308
- let extension = currentExtensions[token];
309
- if (extension) {
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
- if (readObject.count++ > 2) {
352
- 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);
353
- return this.read()
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
- readArrayHeader();
356
- let object = {};
357
- for (let i = 0; i < l; i++) {
358
- let key = structure[i];
359
- object[key] = read();
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
- readObject.count = 0;
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 window == 'object' ? window : global;
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
- return new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0)
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) - (new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0))
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
- // the registration of the record definition extension (tag 105)
739
- const recordDefinition = () => {
740
- let definition = read();
741
- let structure = definition[0];
742
- let id = definition[1];
743
- currentStructures[id & 0xff] = structure;
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
- let key = structure[i - 2];
748
- object[key] = definition[i];
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
- recordDefinition.handlesRead = true;
754
- currentExtensions[RECORD_TAG_ID] = recordDefinition;
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[40009] = (id) => {
778
- // id extension (for structured clones)
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[40010] = (id) => {
800
- // pointer extension (for structured clones)
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(packedTable.prefixes[tag - 224], input)
914
+ return combine(getPackedValues().prefixes[tag - 224], input)
826
915
  if (tag >= 28704 && tag <= 32767)
827
- return combine(packedTable.prefixes[tag - 28672], input)
916
+ return combine(getPackedValues().prefixes[tag - 28672], input)
828
917
  if (tag >= 1879052288 && tag <= 2147483647)
829
- return combine(packedTable.prefixes[tag - 1879048192], input)
918
+ return combine(getPackedValues().prefixes[tag - 1879048192], input)
830
919
  if (tag >= 216 && tag <= 223)
831
- return combine(input, packedTable.suffixes[tag - 216])
920
+ return combine(input, getPackedValues().suffixes[tag - 216])
832
921
  if (tag >= 27647 && tag <= 28671)
833
- return combine(input, packedTable.suffixes[tag - 27639])
922
+ return combine(input, getPackedValues().suffixes[tag - 27639])
834
923
  if (tag >= 1811940352 && tag <= 1879048191)
835
- return combine(input, packedTable.suffixes[tag - 1811939328])
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 typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array');
839
- const typedArrayTags = [64, 68, 69, 70, 71, 72, 77, 78, 79, 81, 82];
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(typedArrayName, tag) {
844
- currentExtensions[tag] = (buffer) => {
845
- if (!typedArrayName)
846
- throw new Error('Could not find typed array for code ' + typeCode)
847
- // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
848
- return new glbl[typedArrayName](Uint8Array.prototype.slice.call(buffer, 0).buffer)
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 maxSharedStructures = 64;
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.encode = function(value, encodeOptions) {
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
- encoder.structures = sharedStructures = encoder.getStructures();
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 * 8;
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 === REUSE_BUFFER_MODE) {
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 pack again in saveStructures, so we get the buffer now
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
- if (hasSharedUpdate && encoder.saveStructures) {
1076
- if (encoder.structures.length > maxSharedStructures) {
1077
- encoder.structures = encoder.structures.slice(0, maxSharedStructures);
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 ({ threshold }) => {
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
- if (!referee.id) {
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.id = idsToInsert.push(referee);
1540
+ referee.references = [];
1541
+ idsToInsert.push(referee);
1290
1542
  }
1291
- target[position$1++] = 0xd9;
1292
- target[position$1++] = 40010 >> 8;
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
- for (let [ key, entryValue ] of value) {
1337
- encode(key);
1338
- encode(entryValue);
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
- let key;
1423
- for (let i = 0; i < length; i++) {
1424
- encode(key = keys[i]);
1425
- encode(object[key]);
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
- for (let key in object) {
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 objectOffset = position++ - start
1448
- let wroteKeys
1449
- for (let key in object) {
1450
- if (safePrototype || object.hasOwnProperty(key)) {
1451
- nextTransition = transition[key]
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
- nextTransition.__keys__ = (transition.__keys__ || []).concat([key])
1455
- /*let keys = Object.keys(object)
1456
- if
1457
- let size = 0
1458
- let startBranch = transition.__keys__ ? transition.__keys__.length : 0
1459
- for (let i = 0, l = keys.length; i++) {
1460
- let key = keys[i]
1461
- size += key.length << 2
1462
- if (i >= startBranch) {
1463
- nextTransition = nextTransition[key] = Object.create(null)
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
- makeRoom(position + size)
1468
- nextTransition = transition[key]
1469
- target.copy(target, )
1470
- objectOffset
1736
+ nextTransition = transition[key] = Object.create(null);
1737
+ newTransitions++;
1471
1738
  }
1472
- transition = nextTransition
1473
- encode(object[key])
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
- target[position$1++] = 0xd9; // tag two byte
1501
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
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
- recordId = structures.nextId++;
1505
- if (!recordId) {
1506
- recordId = 0;
1507
- structures.nextId = 1;
1508
- }
1509
- if (recordId >= MAX_STRUCTURES) {// cycle back around
1510
- structures.nextId = (recordId = maxSharedStructures) + 1;
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 (sharedStructures && sharedStructures.length <= maxSharedStructures) {
1515
- target[position$1++] = 0xd9; // tag two byte
1516
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1517
- target[position$1++] = recordId; // tag number
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
- target[position$1++] = 0xd8;
1521
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
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
- target[position$1++] = 0x19; // uint16
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 i =0; i < length; i++)
1545
- encode(object[keys[i]]);
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(81),
1701
- typedArrayEncoder(82)];
1702
-
1703
- function typedArrayEncoder(tag) {
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 * 8;
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 -= 8;
2081
+ distanceToMove -= 2;
1749
2082
  let position = offset + distanceToMove;
1750
- serialized[position++] = 0xd9;
1751
- serialized[position++] = 40009 >> 8;
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 = 1000;
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) {