cbor-x 0.9.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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;
@@ -203,6 +268,7 @@ function read() {
203
268
  switch(majorType) {
204
269
  case 2: // byte string
205
270
  case 3: // text string
271
+ throw new Error('Indefinite length not supported for byte or text strings')
206
272
  case 4: // array
207
273
  let array = [];
208
274
  let value, i = 0;
@@ -214,8 +280,8 @@ function read() {
214
280
  let key;
215
281
  if (currentDecoder.mapsAsObjects) {
216
282
  let object = {};
217
- while ((key = readKey()) != STOP_CODE)
218
- object[key] = read();
283
+ if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[currentDecoder.decodeKey(key)] = read();
284
+ else while ((key = read()) != STOP_CODE) object[key] = read();
219
285
  return object
220
286
  } else {
221
287
  if (restoreMapsAsObject) {
@@ -223,8 +289,8 @@ function read() {
223
289
  restoreMapsAsObject = false;
224
290
  }
225
291
  let map = new Map();
226
- while ((key = read()) != STOP_CODE)
227
- map.set(key, read());
292
+ if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) map.set(currentDecoder.decodeKey(key), read());
293
+ else while ((key = read()) != STOP_CODE) map.set(key, read());
228
294
  return map
229
295
  }
230
296
  case 7:
@@ -256,16 +322,15 @@ function read() {
256
322
  return readFixedString(token)
257
323
  case 4: // array
258
324
  let array = new Array(token);
259
- for (let i = 0; i < token; i++) {
260
- array[i] = read();
261
- }
325
+ //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
326
+ //else
327
+ for (let i = 0; i < token; i++) array[i] = read();
262
328
  return array
263
329
  case 5: // map
264
330
  if (currentDecoder.mapsAsObjects) {
265
331
  let object = {};
266
- for (let i = 0; i < token; i++) {
267
- object[readKey()] = read();
268
- }
332
+ if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[currentDecoder.decodeKey(read())] = read();
333
+ else for (let i = 0; i < token; i++) object[read()] = read();
269
334
  return object
270
335
  } else {
271
336
  if (restoreMapsAsObject) {
@@ -273,63 +338,66 @@ function read() {
273
338
  restoreMapsAsObject = false;
274
339
  }
275
340
  let map = new Map();
276
- for (let i = 0; i < token; i++) {
277
- map.set(read(), read());
278
- }
341
+ if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
342
+ else for (let i = 0; i < token; i++) map.set(read(), read());
279
343
  return map
280
344
  }
281
345
  case 6: // extension
282
- if ((token >> 8) == RECORD_TAG_ID) { // record structures
283
- let structure = currentStructures[token & 0xff];
346
+ if (token >= BUNDLED_STRINGS_ID) {
347
+ let structure = currentStructures[token & 0x1fff]; // check record structures first
348
+ // 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
349
  if (structure) {
285
- if (!structure.read)
286
- structure.read = createStructureReader(structure);
350
+ if (!structure.read) structure.read = createStructureReader(structure);
287
351
  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
352
+ }
353
+ if (token < 0x10000) {
354
+ 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)
355
+ return recordDefinition(read())
356
+ else if (token == RECORD_DEFINITIONS_ID) {
357
+ let length = readJustLength();
358
+ let id = read();
359
+ for (let i = 2; i < length; i++) {
360
+ recordDefinition([id++, read()]);
361
+ }
362
+ return read()
363
+ } else if (token == BUNDLED_STRINGS_ID) {
364
+ return readBundleExt()
365
+ }
366
+ if (currentDecoder.getShared) {
367
+ loadShared();
368
+ structure = currentStructures[token & 0x1fff];
369
+ if (structure) {
370
+ if (!structure.read)
371
+ structure.read = createStructureReader(structure);
372
+ return structure.read()
373
+ }
320
374
  }
321
- return new Tag(input)
322
375
  }
323
376
  }
377
+ let extension = currentExtensions[token];
378
+ if (extension) {
379
+ if (extension.handlesRead)
380
+ return extension(read)
381
+ else
382
+ return extension(read())
383
+ } else {
384
+ let input = read();
385
+ for (let i = 0; i < currentExtensionRanges.length; i++) {
386
+ let value = currentExtensionRanges[i](token, input);
387
+ if (value !== undefined)
388
+ return value
389
+ }
390
+ return new Tag(input, token)
391
+ }
324
392
  case 7: // fixed value
325
393
  switch (token) {
326
394
  case 0x14: return false
327
395
  case 0x15: return true
328
396
  case 0x16: return null
329
397
  case 0x17: return; // undefined
330
- case 0x1f:
398
+ case 0x1f:
331
399
  default:
332
- let packedValue = packedValues[token];
400
+ let packedValue = (packedValues || getPackedValues())[token];
333
401
  if (packedValue !== undefined)
334
402
  return packedValue
335
403
  throw new Error('Unknown token ' + token)
@@ -345,41 +413,47 @@ function read() {
345
413
  }
346
414
  const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
347
415
  function createStructureReader(structure) {
348
- let l = structure.length;
349
416
  function readObject() {
417
+ // get the array size from the header
418
+ let length = src[position++];
419
+ //let majorType = token >> 5
420
+ length = length & 0x1f;
421
+ if (length > 0x17) {
422
+ switch (length) {
423
+ case 0x18:
424
+ length = src[position++];
425
+ break
426
+ case 0x19:
427
+ length = dataView.getUint16(position);
428
+ position += 2;
429
+ break
430
+ case 0x1a:
431
+ length = dataView.getUint32(position);
432
+ position += 4;
433
+ break
434
+ default:
435
+ throw new Error('Expected array header, but got ' + src[position - 1])
436
+ }
437
+ }
350
438
  // 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()
439
+ if (this.objectLiteralSize === length) // we have a fast object literal reader, use it (assuming it is the right length)
440
+ return this.objectLiteral(read)
441
+ if (this.count++ == 3) { // create a fast reader
442
+ this.objectLiteralSize = length;
443
+ this.objectLiteral = currentDecoder.keyMap
444
+ ? new Function('r', 'return {' + this.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
445
+ : new Function('r', 'return {' + this.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}');
446
+ return this.objectLiteral(read)
354
447
  }
355
- readArrayHeader();
356
448
  let object = {};
357
- for (let i = 0; i < l; i++) {
358
- let key = structure[i];
359
- object[key] = read();
360
- }
449
+ if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read();
450
+ else for (let i = 0; i < length; i++) object[this[i]] = read();
361
451
  return object
362
452
  }
363
- readObject.count = 0;
453
+ structure.count = 0;
364
454
  return readObject
365
455
  }
366
456
 
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
457
  let readFixedString = readStringJS;
384
458
 
385
459
  exports.isNativeAccelerationEnabled = false;
@@ -390,6 +464,8 @@ function setExtractor(extractStrings) {
390
464
  return function readString(length) {
391
465
  let string = strings[stringPosition++];
392
466
  if (string == null) {
467
+ if (bundledStrings)
468
+ return readStringJS(length)
393
469
  let extraction = extractStrings(position, srcEnd, length, src);
394
470
  if (typeof extraction == 'string') {
395
471
  string = extraction;
@@ -644,76 +720,15 @@ function getFloat16() {
644
720
  }
645
721
 
646
722
  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
723
 
710
724
  class Tag {
711
- constructor(value) {
725
+ constructor(value, tag) {
712
726
  this.value = value;
727
+ this.tag = tag;
713
728
  }
714
729
  }
715
730
 
716
- let glbl = typeof window == 'object' ? window : global;
731
+ let glbl = typeof self == 'object' ? self : global;
717
732
 
718
733
  currentExtensions[0] = (dateString) => {
719
734
  // string date extension
@@ -727,38 +742,62 @@ currentExtensions[1] = (epochSec) => {
727
742
 
728
743
  currentExtensions[2] = (buffer) => {
729
744
  // bigint extension
730
- return new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0)
745
+ let value = BigInt(0);
746
+ for (let i = 0, l = buffer.byteLength; i < l; i++) {
747
+ value = BigInt(buffer[i]) + value << BigInt(8);
748
+ }
749
+ return value
731
750
  };
732
751
 
733
752
  currentExtensions[3] = (buffer) => {
734
753
  // negative bigint extension
735
- return BigInt(-1) - (new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0))
754
+ return BigInt(-1) - currentExtensions[2](buffer)
755
+ };
756
+ currentExtensions[4] = (fraction) => {
757
+ // best to reparse to maintain accuracy
758
+ return +(fraction[1] + 'e' + fraction[0])
759
+ };
760
+
761
+ currentExtensions[5] = (fraction) => {
762
+ // probably not sufficiently accurate
763
+ return fraction[1] * Math.exp(fraction[0] * Math.log(2))
736
764
  };
737
765
 
738
766
  // 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;
767
+ const recordDefinition = (definition) => {
768
+ let id = definition[0] - 0xe000;
769
+ let structure = definition[1];
770
+ currentStructures[id] = structure;
744
771
  structure.read = createStructureReader(structure);
745
772
  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
- }
773
+ if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
774
+ let key = currentDecoder.decodeKey(structure[i - 2]);
775
+ object[key] = definition[i];
776
+ }
777
+ else for (let i = 2,l = definition.length; i < l; i++) {
778
+ let key = structure[i - 2];
779
+ object[key] = definition[i];
780
+ }
750
781
  return object
751
782
  };
752
-
753
- recordDefinition.handlesRead = true;
754
- currentExtensions[RECORD_TAG_ID] = recordDefinition;
783
+ currentExtensions[LEGACY_RECORD_INLINE_ID] = recordDefinition;
784
+ currentExtensions[14] = (value) => {
785
+ if (bundledStrings)
786
+ return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
787
+ return new Tag(value, 14)
788
+ };
789
+ currentExtensions[15] = (value) => {
790
+ if (bundledStrings)
791
+ return bundledStrings[1].slice(bundledStrings.position1, bundledStrings.position1 += value)
792
+ return new Tag(value, 15)
793
+ };
755
794
 
756
795
  currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
757
796
  return (glbl[data[0]] || Error)(data[1], data[2])
758
797
  };
759
798
  const packedTable = (read) => {
760
799
  if (src[position++] != 0x84)
761
- throw new Error('Packed values structure must be followed by 4 element array')
800
+ throw new Error('Packed values structure must be followed by a 4 element array')
762
801
  let newPackedValues = read(); // packed values
763
802
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
764
803
  packedValues.prefixes = read();
@@ -769,15 +808,36 @@ packedTable.handlesRead = true;
769
808
  currentExtensions[51] = packedTable;
770
809
 
771
810
  currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
811
+ if (!packedValues) {
812
+ if (currentDecoder.getShared)
813
+ loadShared();
814
+ else
815
+ return new Tag(data, PACKED_REFERENCE_TAG_ID)
816
+ }
772
817
  if (typeof data == 'number')
773
818
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
774
819
  throw new Error('No support for non-integer packed references yet')
775
820
  };
821
+ currentExtensions[25] = (id) => {
822
+ return stringRefs[id]
823
+ };
824
+ currentExtensions[256] = (read) => {
825
+ stringRefs = [];
826
+ try {
827
+ return read()
828
+ } finally {
829
+ stringRefs = null;
830
+ }
831
+ };
832
+ currentExtensions[256].handlesRead = true;
776
833
 
777
- currentExtensions[40009] = (id) => {
778
- // id extension (for structured clones)
779
- if (!referenceMap)
834
+ currentExtensions[28] = (read) => {
835
+ // shareable http://cbor.schmorp.de/value-sharing (for structured clones)
836
+ if (!referenceMap) {
780
837
  referenceMap = new Map();
838
+ referenceMap.id = 0;
839
+ }
840
+ let id = referenceMap.id++;
781
841
  let token = src[position];
782
842
  let target;
783
843
  // TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
@@ -795,9 +855,10 @@ currentExtensions[40009] = (id) => {
795
855
  refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
796
856
  return targetProperties // no cycle, can just use the returned read object
797
857
  };
858
+ currentExtensions[28].handlesRead = true;
798
859
 
799
- currentExtensions[40010] = (id) => {
800
- // pointer extension (for structured clones)
860
+ currentExtensions[29] = (id) => {
861
+ // sharedref http://cbor.schmorp.de/value-sharing (for structured clones)
801
862
  let refEntry = referenceMap.get(id);
802
863
  refEntry.used = true;
803
864
  return refEntry.target
@@ -820,19 +881,36 @@ function combine(a, b) {
820
881
  return a.concat(b)
821
882
  return Object.assign({}, a, b)
822
883
  }
884
+ function getPackedValues() {
885
+ if (!packedValues) {
886
+ if (currentDecoder.getShared)
887
+ loadShared();
888
+ else
889
+ throw new Error('No packed values available')
890
+ }
891
+ return packedValues
892
+ }
893
+ const SHARED_DATA_TAG_ID = 0x53687264; // ascii 'Shrd'
823
894
  currentExtensionRanges.push((tag, input) => {
824
895
  if (tag >= 225 && tag <= 255)
825
- return combine(packedTable.prefixes[tag - 224], input)
896
+ return combine(getPackedValues().prefixes[tag - 224], input)
826
897
  if (tag >= 28704 && tag <= 32767)
827
- return combine(packedTable.prefixes[tag - 28672], input)
898
+ return combine(getPackedValues().prefixes[tag - 28672], input)
828
899
  if (tag >= 1879052288 && tag <= 2147483647)
829
- return combine(packedTable.prefixes[tag - 1879048192], input)
900
+ return combine(getPackedValues().prefixes[tag - 1879048192], input)
830
901
  if (tag >= 216 && tag <= 223)
831
- return combine(input, packedTable.suffixes[tag - 216])
902
+ return combine(input, getPackedValues().suffixes[tag - 216])
832
903
  if (tag >= 27647 && tag <= 28671)
833
- return combine(input, packedTable.suffixes[tag - 27639])
904
+ return combine(input, getPackedValues().suffixes[tag - 27639])
834
905
  if (tag >= 1811940352 && tag <= 1879048191)
835
- return combine(input, packedTable.suffixes[tag - 1811939328])
906
+ return combine(input, getPackedValues().suffixes[tag - 1811939328])
907
+ 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)
908
+ return {
909
+ packedValues: packedValues,
910
+ structures: currentStructures.slice(0),
911
+ version: input,
912
+ }
913
+ }
836
914
  });
837
915
 
838
916
  const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array');
@@ -849,6 +927,61 @@ function registerTypedArray(typedArrayName, tag) {
849
927
  };
850
928
  }
851
929
 
930
+ function readBundleExt() {
931
+ let length = readJustLength();
932
+ let bundlePosition = position + read();
933
+ for (let i = 2; i < length; i++) {
934
+ // skip past bundles that were already read
935
+ let bundleLength = readJustLength(); // this will increment position, so must add to position afterwards
936
+ position += bundleLength;
937
+ }
938
+ let dataPosition = position;
939
+ position = bundlePosition;
940
+ bundledStrings = [readStringJS(readJustLength()), readStringJS(readJustLength())];
941
+ bundledStrings.position0 = 0;
942
+ bundledStrings.position1 = 0;
943
+ bundledStrings.postBundlePosition = position;
944
+ position = dataPosition;
945
+ return read()
946
+ }
947
+
948
+ function readJustLength() {
949
+ let token = src[position++] & 0x1f;
950
+ if (token > 0x17) {
951
+ switch (token) {
952
+ case 0x18:
953
+ token = src[position++];
954
+ break
955
+ case 0x19:
956
+ token = dataView.getUint16(position);
957
+ position += 2;
958
+ break
959
+ case 0x1a:
960
+ token = dataView.getUint32(position);
961
+ position += 4;
962
+ break
963
+ }
964
+ }
965
+ return token
966
+ }
967
+
968
+ function loadShared() {
969
+ if (currentDecoder.getShared) {
970
+ let sharedData = saveState(() => {
971
+ // save the state in case getShared modifies our buffer
972
+ src = null;
973
+ return currentDecoder.getShared()
974
+ }) || {};
975
+ let updatedStructures = sharedData.structures || [];
976
+ currentDecoder.sharedVersion = sharedData.version;
977
+ packedValues = currentDecoder.sharedValues = sharedData.packedValues;
978
+ if (currentStructures === true)
979
+ currentDecoder.structures = currentStructures = updatedStructures;
980
+ else
981
+ currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
982
+ }
983
+ }
984
+
852
985
  function saveState(callback) {
853
986
  let savedSrcEnd = srcEnd;
854
987
  let savedPosition = position;
@@ -858,6 +991,8 @@ function saveState(callback) {
858
991
  let savedSrcString = srcString;
859
992
  let savedStrings = strings;
860
993
  let savedReferenceMap = referenceMap;
994
+ let savedBundledStrings = bundledStrings;
995
+
861
996
  // TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
862
997
  let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
863
998
  let savedStructures = currentStructures;
@@ -872,6 +1007,7 @@ function saveState(callback) {
872
1007
  srcString = savedSrcString;
873
1008
  strings = savedStrings;
874
1009
  referenceMap = savedReferenceMap;
1010
+ bundledStrings = savedBundledStrings;
875
1011
  src = savedSrc;
876
1012
  sequentialMode = savedSequentialMode;
877
1013
  currentStructures = savedStructures;
@@ -918,13 +1054,15 @@ let extensions, extensionClasses;
918
1054
  const hasNodeBuffer = typeof Buffer !== 'undefined';
919
1055
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
920
1056
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
921
- const RECORD_STARTING_ID_PREFIX = 0x69; // tag 105/0x69
922
1057
  const MAX_STRUCTURES = 0x100;
923
1058
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
924
1059
  let target;
925
1060
  let targetView;
926
1061
  let position$1 = 0;
927
1062
  let safeEnd;
1063
+ let bundledStrings$1 = null;
1064
+ const MAX_BUNDLE_SIZE = 0xf000;
1065
+ const hasNonLatin = /[\u0080-\uFFFF]/;
928
1066
  const RECORD_SYMBOL = Symbol('record-id');
929
1067
  class Encoder extends Decoder {
930
1068
  constructor(options) {
@@ -936,7 +1074,6 @@ class Encoder extends Decoder {
936
1074
  let structures;
937
1075
  let referenceMap;
938
1076
  options = options || {};
939
- let lastSharedStructuresLength = 0;
940
1077
  let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
941
1078
  return target.utf8Write(string, position, maxBytes)
942
1079
  } : (textEncoder && textEncoder.encodeInto) ?
@@ -945,12 +1082,14 @@ class Encoder extends Decoder {
945
1082
  } : false;
946
1083
 
947
1084
  let encoder = this;
948
- let maxSharedStructures = 64;
1085
+ let maxSharedStructures = options.maxSharedStructures || 128;
949
1086
  let isSequential = options.sequential;
950
1087
  if (isSequential) {
951
1088
  maxSharedStructures = 0;
952
1089
  this.structures = [];
953
1090
  }
1091
+ if (this.saveStructures)
1092
+ this.saveShared = this.saveStructures;
954
1093
  let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues;
955
1094
  let sharedPackedObjectMap;
956
1095
  if (sharedValues) {
@@ -958,13 +1097,29 @@ class Encoder extends Decoder {
958
1097
  for (let i = 0, l = sharedValues.length; i < l; i++) {
959
1098
  sharedPackedObjectMap[sharedValues[i]] = i;
960
1099
  }
961
-
962
1100
  }
963
1101
  let recordIdsToRemove = [];
964
1102
  let transitionsCount = 0;
965
1103
  let serializationsSinceTransitionRebuild = 0;
966
-
967
- this.encode = function(value, encodeOptions) {
1104
+
1105
+ this.mapEncode = function(value, encodeOptions) {
1106
+ // Experimental support for premapping keys using _keyMap instad of keyMap - not optiimised yet)
1107
+ if (this._keyMap && !this._mapped) {
1108
+ //console.log('encoding ', value)
1109
+ switch (value.constructor.name) {
1110
+ case 'Array':
1111
+ value = value.map(r => this.encodeKeys(r));
1112
+ break
1113
+ //case 'Map':
1114
+ // value = this.encodeKeys(value)
1115
+ // break
1116
+ }
1117
+ //this._mapped = true
1118
+ }
1119
+ return this.encode(value, encodeOptions)
1120
+ };
1121
+
1122
+ this.encode = function(value, encodeOptions) {
968
1123
  if (!target) {
969
1124
  target = new ByteArrayAllocate(8192);
970
1125
  targetView = new DataView(target.buffer, 0, 8192);
@@ -981,10 +1136,25 @@ class Encoder extends Decoder {
981
1136
  position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
982
1137
  start = position$1;
983
1138
  referenceMap = encoder.structuredClone ? new Map() : null;
1139
+ if (encoder.bundleStrings && typeof value !== 'string') {
1140
+ bundledStrings$1 = [];
1141
+ bundledStrings$1.size = Infinity; // force a new bundle start on first string
1142
+ } else
1143
+ bundledStrings$1 = null;
1144
+
984
1145
  sharedStructures = encoder.structures;
985
1146
  if (sharedStructures) {
986
- if (sharedStructures.uninitialized)
987
- encoder.structures = sharedStructures = encoder.getStructures();
1147
+ if (sharedStructures.uninitialized) {
1148
+ let sharedData = encoder.getShared() || {};
1149
+ encoder.structures = sharedStructures = sharedData.structures || [];
1150
+ encoder.sharedVersion = sharedData.version;
1151
+ let sharedValues = encoder.sharedValues = sharedData.packedValues;
1152
+ if (sharedValues) {
1153
+ sharedPackedObjectMap = {};
1154
+ for (let i = 0, l = sharedValues.length; i < l; i++)
1155
+ sharedPackedObjectMap[sharedValues[i]] = i;
1156
+ }
1157
+ }
988
1158
  let sharedStructuresLength = sharedStructures.length;
989
1159
  if (sharedStructuresLength > maxSharedStructures && !isSequential)
990
1160
  sharedStructuresLength = maxSharedStructures;
@@ -993,6 +1163,7 @@ class Encoder extends Decoder {
993
1163
  sharedStructures.transitions = Object.create(null);
994
1164
  for (let i = 0; i < sharedStructuresLength; i++) {
995
1165
  let keys = sharedStructures[i];
1166
+ //console.log('shared struct keys:', keys)
996
1167
  if (!keys)
997
1168
  continue
998
1169
  let nextTransition, transition = sharedStructures.transitions;
@@ -1006,7 +1177,6 @@ class Encoder extends Decoder {
1006
1177
  }
1007
1178
  transition[RECORD_SYMBOL] = i;
1008
1179
  }
1009
- lastSharedStructuresLength = sharedStructures.length;
1010
1180
  }
1011
1181
  if (!isSequential)
1012
1182
  sharedStructures.nextId = sharedStructuresLength;
@@ -1039,9 +1209,12 @@ class Encoder extends Decoder {
1039
1209
  }
1040
1210
  try {
1041
1211
  encode(value);
1212
+ if (bundledStrings$1) {
1213
+ writeBundles(start, encode);
1214
+ }
1042
1215
  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
1216
  if (referenceMap && referenceMap.idsToInsert) {
1044
- position$1 += referenceMap.idsToInsert.length * 8;
1217
+ position$1 += referenceMap.idsToInsert.length * 2;
1045
1218
  if (position$1 > safeEnd)
1046
1219
  makeRoom(position$1);
1047
1220
  encoder.offset = position$1;
@@ -1049,12 +1222,12 @@ class Encoder extends Decoder {
1049
1222
  referenceMap = null;
1050
1223
  return serialized
1051
1224
  }
1052
- if (encodeOptions === REUSE_BUFFER_MODE) {
1225
+ if (encodeOptions & REUSE_BUFFER_MODE) {
1053
1226
  target.start = start;
1054
1227
  target.end = position$1;
1055
1228
  return target
1056
1229
  }
1057
- return target.subarray(start, position$1) // position can change if we call pack again in saveStructures, so we get the buffer now
1230
+ return target.subarray(start, position$1) // position can change if we call pack again in saveShared, so we get the buffer now
1058
1231
  } finally {
1059
1232
  if (sharedStructures) {
1060
1233
  if (serializationsSinceTransitionRebuild < 10)
@@ -1072,34 +1245,27 @@ class Encoder extends Decoder {
1072
1245
  }
1073
1246
  recordIdsToRemove = [];
1074
1247
  }
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
1248
+ }
1249
+ if (hasSharedUpdate && encoder.saveShared) {
1250
+ if (encoder.structures.length > maxSharedStructures) {
1251
+ encoder.structures = encoder.structures.slice(0, maxSharedStructures);
1093
1252
  }
1253
+ // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1254
+ let returnBuffer = target.subarray(start, position$1);
1255
+ if (encoder.updateSharedData() === false)
1256
+ return encoder.encode(value) // re-encode if it fails
1257
+ return returnBuffer
1094
1258
  }
1259
+ if (encodeOptions & RESET_BUFFER_MODE)
1260
+ position$1 = start;
1095
1261
  }
1096
1262
  };
1097
1263
  this.findCommonStringsToPack = () => {
1098
1264
  samplingPackedValues = new Map();
1099
1265
  if (!sharedPackedObjectMap)
1100
1266
  sharedPackedObjectMap = Object.create(null);
1101
- return ({ threshold }) => {
1102
- threshold = threshold || 4;
1267
+ return (options) => {
1268
+ let threshold = options && options.threshold || 4;
1103
1269
  let position = this.pack ? options.maxPrivatePackedValues || 16 : 0;
1104
1270
  if (!sharedValues)
1105
1271
  sharedValues = this.sharedValues = [];
@@ -1110,6 +1276,7 @@ class Encoder extends Decoder {
1110
1276
  hasSharedUpdate = true;
1111
1277
  }
1112
1278
  }
1279
+ while (this.saveShared && this.updateSharedData() === false) {}
1113
1280
  samplingPackedValues = null;
1114
1281
  }
1115
1282
  };
@@ -1158,6 +1325,33 @@ class Encoder extends Decoder {
1158
1325
  }
1159
1326
  }
1160
1327
  let strLength = value.length;
1328
+ if (bundledStrings$1 && strLength >= 4 && strLength < 0x400) {
1329
+ if ((bundledStrings$1.size += strLength) > MAX_BUNDLE_SIZE) {
1330
+ let extStart;
1331
+ let maxBytes = (bundledStrings$1[0] ? bundledStrings$1[0].length * 3 + bundledStrings$1[1].length : 0) + 10;
1332
+ if (position$1 + maxBytes > safeEnd)
1333
+ target = makeRoom(position$1 + maxBytes);
1334
+ target[position$1++] = 0xd9; // tag 16-bit
1335
+ target[position$1++] = 0xdf; // tag 0xdff9
1336
+ target[position$1++] = 0xf9;
1337
+ // TODO: If we only have one bundle with any string data, only write one string bundle
1338
+ target[position$1++] = bundledStrings$1.position ? 0x84 : 0x82; // array of 4 or 2 elements depending on if we write bundles
1339
+ target[position$1++] = 0x1a; // 32-bit unsigned int
1340
+ extStart = position$1 - start;
1341
+ position$1 += 4; // reserve for writing bundle reference
1342
+ if (bundledStrings$1.position) {
1343
+ writeBundles(start, encode); // write the last bundles
1344
+ }
1345
+ bundledStrings$1 = ['', '']; // create new ones
1346
+ bundledStrings$1.size = 0;
1347
+ bundledStrings$1.position = extStart;
1348
+ }
1349
+ let twoByte = hasNonLatin.test(value);
1350
+ bundledStrings$1[twoByte ? 0 : 1] += value;
1351
+ target[position$1++] = twoByte ? 0xce : 0xcf;
1352
+ encode(strLength);
1353
+ return
1354
+ }
1161
1355
  let headerSize;
1162
1356
  // first we estimate the header size, so we can write to the correct location
1163
1357
  if (strLength < 0x20) {
@@ -1284,16 +1478,16 @@ class Encoder extends Decoder {
1284
1478
  if (referenceMap) {
1285
1479
  let referee = referenceMap.get(value);
1286
1480
  if (referee) {
1287
- if (!referee.id) {
1481
+ target[position$1++] = 0xd8;
1482
+ target[position$1++] = 29; // http://cbor.schmorp.de/value-sharing
1483
+ target[position$1++] = 0x19; // 16-bit uint
1484
+ if (!referee.references) {
1288
1485
  let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = []);
1289
- referee.id = idsToInsert.push(referee);
1486
+ referee.references = [];
1487
+ idsToInsert.push(referee);
1290
1488
  }
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;
1489
+ referee.references.push(position$1 - start);
1490
+ position$1 += 2; // TODO: also support 32-bit
1297
1491
  return
1298
1492
  } else
1299
1493
  referenceMap.set(value, { offset: position$1 - start });
@@ -1333,16 +1527,23 @@ class Encoder extends Decoder {
1333
1527
  targetView.setUint32(position$1, length);
1334
1528
  position$1 += 4;
1335
1529
  }
1336
- for (let [ key, entryValue ] of value) {
1337
- encode(key);
1338
- encode(entryValue);
1530
+ if (encoder.keyMap) {
1531
+ for (let [ key, entryValue ] of value) {
1532
+ encode(encoder.encodeKey(key));
1533
+ encode(entryValue);
1534
+ }
1535
+ } else {
1536
+ for (let [ key, entryValue ] of value) {
1537
+ encode(key);
1538
+ encode(entryValue);
1539
+ }
1339
1540
  }
1340
- } else {
1541
+ } else {
1341
1542
  for (let i = 0, l = extensions.length; i < l; i++) {
1342
1543
  let extensionClass = extensionClasses[i];
1343
1544
  if (value instanceof extensionClass) {
1344
1545
  let extension = extensions[i];
1345
- let tag = extension.tag;
1546
+ let tag = extension.tag || extension.getTag && extension.getTag(value);
1346
1547
  if (tag < 0x18) {
1347
1548
  target[position$1++] = 0xc0 | tag;
1348
1549
  } else if (tag < 0x100) {
@@ -1404,6 +1605,7 @@ class Encoder extends Decoder {
1404
1605
  const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
1405
1606
  // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
1406
1607
  let keys = Object.keys(object);
1608
+ let vals = Object.values(object);
1407
1609
  let length = keys.length;
1408
1610
  if (length < 0x18) {
1409
1611
  target[position$1++] = 0xa0 | length;
@@ -1419,10 +1621,16 @@ class Encoder extends Decoder {
1419
1621
  targetView.setUint32(position$1, length);
1420
1622
  position$1 += 4;
1421
1623
  }
1422
- let key;
1423
- for (let i = 0; i < length; i++) {
1424
- encode(key = keys[i]);
1425
- encode(object[key]);
1624
+ if (encoder.keyMap) {
1625
+ for (let i = 0; i < length; i++) {
1626
+ encode(encodeKey(keys[i]));
1627
+ encode(vals[i]);
1628
+ }
1629
+ } else {
1630
+ for (let i = 0; i < length; i++) {
1631
+ encode(keys[i]);
1632
+ encode(vals[i]);
1633
+ }
1426
1634
  }
1427
1635
  } :
1428
1636
  (object, safePrototype) => {
@@ -1430,12 +1638,18 @@ class Encoder extends Decoder {
1430
1638
  let objectOffset = position$1 - start;
1431
1639
  position$1 += 2;
1432
1640
  let size = 0;
1433
- for (let key in object) {
1434
- if (safePrototype || object.hasOwnProperty(key)) {
1435
- encode(key);
1641
+ if (encoder.keyMap) {
1642
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1643
+ encode(encoder.encodeKey(key));
1436
1644
  encode(object[key]);
1437
1645
  size++;
1438
1646
  }
1647
+ } else {
1648
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1649
+ encode(key);
1650
+ encode(object[key]);
1651
+ size++;
1652
+ }
1439
1653
  }
1440
1654
  target[objectOffset++ + start] = size >> 8;
1441
1655
  target[objectOffset + start] = size & 0xff;
@@ -1483,11 +1697,16 @@ class Encoder extends Decoder {
1483
1697
  }*/
1484
1698
  (object) => {
1485
1699
  let keys = Object.keys(object);
1700
+ let vals = Object.values(object);
1701
+ if (this.keyMap) keys = keys.map(k => this.encodeKey(k));
1486
1702
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1487
1703
  let newTransitions = 0;
1488
1704
  let length = keys.length;
1705
+ //let parentRecordId
1489
1706
  for (let i = 0; i < length; i++) {
1707
+ //if (!parentRecordId)
1490
1708
  let key = keys[i];
1709
+ // parentRecordId = transition[RECORD_SYMBOL]
1491
1710
  nextTransition = transition[key];
1492
1711
  if (!nextTransition) {
1493
1712
  nextTransition = transition[key] = Object.create(null);
@@ -1497,9 +1716,9 @@ class Encoder extends Decoder {
1497
1716
  }
1498
1717
  let recordId = transition[RECORD_SYMBOL];
1499
1718
  if (recordId !== undefined) {
1500
- target[position$1++] = 0xd9; // tag two byte
1501
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1502
- target[position$1++] = recordId;
1719
+ target[position$1++] = 0xd9;
1720
+ target[position$1++] = (recordId >> 8) | 0xe0;
1721
+ target[position$1++] = recordId & 0xff;
1503
1722
  } else {
1504
1723
  recordId = structures.nextId++;
1505
1724
  if (!recordId) {
@@ -1512,13 +1731,13 @@ class Encoder extends Decoder {
1512
1731
  transition[RECORD_SYMBOL] = recordId;
1513
1732
  structures[recordId] = keys;
1514
1733
  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
1734
+ target[position$1++] = 0xd9;
1735
+ target[position$1++] = (recordId >> 8) | 0xe0;
1736
+ target[position$1++] = recordId & 0xff;
1518
1737
  hasSharedUpdate = true;
1519
1738
  } else {
1520
- target[position$1++] = 0xd8;
1521
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1739
+ targetView.setUint32(position$1, 0xd9dfff00); // tag two byte, then record definition id
1740
+ position$1 += 3;
1522
1741
  if (newTransitions)
1523
1742
  transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
1524
1743
  // record the removal of the id, we can maintain our shared structure
@@ -1526,13 +1745,9 @@ class Encoder extends Decoder {
1526
1745
  recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined; // we are cycling back through, and have to remove old ones
1527
1746
  recordIdsToRemove.push(transition);
1528
1747
  writeArrayHeader(length + 2);
1748
+ encode(0xe000 + recordId);
1529
1749
  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]]);
1750
+ for (let v of Object.values(object)) encode(v);
1536
1751
  return
1537
1752
  }
1538
1753
  }
@@ -1541,8 +1756,7 @@ class Encoder extends Decoder {
1541
1756
  } else {
1542
1757
  writeArrayHeader(length);
1543
1758
  }
1544
- for (let i =0; i < length; i++)
1545
- encode(object[keys[i]]);
1759
+ for (let i =0; i < length; i++) encode(vals[i]);
1546
1760
  };
1547
1761
  const makeRoom = (end) => {
1548
1762
  let newSize;
@@ -1572,6 +1786,33 @@ class Encoder extends Decoder {
1572
1786
  targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
1573
1787
  position$1 = 0;
1574
1788
  }
1789
+ clearSharedData() {
1790
+ if (this.structures)
1791
+ this.structures = [];
1792
+ if (this.sharedValues)
1793
+ this.sharedValues = undefined;
1794
+ }
1795
+ updateSharedData() {
1796
+ let lastVersion = this.sharedVersion || 0;
1797
+ this.sharedVersion = lastVersion + 1;
1798
+ let saveResults = this.saveShared(new SharedData(this.structures, this.sharedValues, this.sharedVersion),
1799
+ existingShared => (existingShared && existingShared.version || 0) == lastVersion);
1800
+ if (saveResults === false) {
1801
+ // get updated structures and try again if the update failed
1802
+ let sharedData = this.getShared() || {};
1803
+ this.structures = sharedData.structures || [];
1804
+ this.sharedValues = sharedData.packedValues;
1805
+ this.sharedVersion = sharedData.version;
1806
+ }
1807
+ return saveResults
1808
+ }
1809
+ }
1810
+ class SharedData {
1811
+ constructor(structures, values, version) {
1812
+ this.structures = structures;
1813
+ this.packedValues = values;
1814
+ this.version = version;
1815
+ }
1575
1816
  }
1576
1817
 
1577
1818
  function writeArrayHeader(length) {
@@ -1641,11 +1882,11 @@ function findRepetitiveStrings(value, packedValues) {
1641
1882
  }
1642
1883
  }
1643
1884
 
1644
- extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
1885
+ extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
1645
1886
  Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
1646
1887
  typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
1647
1888
  typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
1648
- Float32Array, Float64Array];
1889
+ Float32Array, Float64Array, SharedData];
1649
1890
 
1650
1891
  //Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
1651
1892
  extensions = [{
@@ -1680,6 +1921,13 @@ extensions = [{
1680
1921
  encode(regex, encode) {
1681
1922
  encode([ 'RegExp', regex.source, regex.flags ]);
1682
1923
  }
1924
+ }, {
1925
+ getTag(tag) {
1926
+ return tag.tag
1927
+ },
1928
+ encode(tag, encode) {
1929
+ encode(tag.value);
1930
+ }
1683
1931
  }, {
1684
1932
  encode(arrayBuffer, encode, makeRoom) {
1685
1933
  writeBuffer(arrayBuffer, makeRoom);
@@ -1698,7 +1946,35 @@ extensions = [{
1698
1946
  typedArrayEncoder(78),
1699
1947
  typedArrayEncoder(79),
1700
1948
  typedArrayEncoder(81),
1701
- typedArrayEncoder(82)];
1949
+ typedArrayEncoder(82),
1950
+ {
1951
+ encode(sharedData, encode) { // write SharedData
1952
+ let packedValues = sharedData.packedValues || [];
1953
+ let sharedStructures = sharedData.structures || [];
1954
+ if (packedValues.values.length > 0) {
1955
+ target[position$1++] = 0xd8; // one-byte tag
1956
+ target[position$1++] = 51; // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
1957
+ writeArrayHeader(4);
1958
+ let valuesArray = packedValues.values;
1959
+ encode(valuesArray);
1960
+ writeArrayHeader(0); // prefixes
1961
+ writeArrayHeader(0); // suffixes
1962
+ packedObjectMap = Object.create(sharedPackedObjectMap || null);
1963
+ for (let i = 0, l = valuesArray.length; i < l; i++) {
1964
+ packedObjectMap[valuesArray[i]] = i;
1965
+ }
1966
+ }
1967
+ if (sharedStructures) {
1968
+ targetView.setUint32(position$1, 0xd9dffe00);
1969
+ position$1 += 3;
1970
+ let definitions = sharedStructures.slice(0);
1971
+ definitions.unshift(0xe000);
1972
+ definitions.push(new Tag(sharedData.version, 0x53687264));
1973
+ encode(definitions);
1974
+ } else
1975
+ encode(new Tag(sharedData.version, 0x53687264));
1976
+ }
1977
+ }];
1702
1978
 
1703
1979
  function typedArrayEncoder(tag) {
1704
1980
  return {
@@ -1738,27 +2014,35 @@ function writeBuffer(buffer, makeRoom) {
1738
2014
  function insertIds(serialized, idsToInsert) {
1739
2015
  // insert the ids that need to be referenced for structured clones
1740
2016
  let nextId;
1741
- let distanceToMove = idsToInsert.length * 8;
2017
+ let distanceToMove = idsToInsert.length * 2;
1742
2018
  let lastEnd = serialized.length - distanceToMove;
1743
2019
  idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1);
2020
+ for (let id = 0; id < idsToInsert.length; id++) {
2021
+ let referee = idsToInsert[id];
2022
+ referee.id = id;
2023
+ for (let position of referee.references) {
2024
+ serialized[position++] = id >> 8;
2025
+ serialized[position] = id & 0xff;
2026
+ }
2027
+ }
1744
2028
  while (nextId = idsToInsert.pop()) {
1745
2029
  let offset = nextId.offset;
1746
- let id = nextId.id;
1747
2030
  serialized.copyWithin(offset + distanceToMove, offset, lastEnd);
1748
- distanceToMove -= 8;
2031
+ distanceToMove -= 2;
1749
2032
  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;
2033
+ serialized[position++] = 0xd8;
2034
+ serialized[position++] = 28; // http://cbor.schmorp.de/value-sharing
1758
2035
  lastEnd = offset;
1759
2036
  }
1760
2037
  return serialized
1761
2038
  }
2039
+ function writeBundles(start, encode) {
2040
+ targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start + 1); // the offset to bundle
2041
+ let writeStrings = bundledStrings$1;
2042
+ bundledStrings$1 = null;
2043
+ encode(writeStrings[0]);
2044
+ encode(writeStrings[1]);
2045
+ }
1762
2046
 
1763
2047
  function addExtension$1(extension) {
1764
2048
  if (extension.Class) {
@@ -1772,7 +2056,8 @@ function addExtension$1(extension) {
1772
2056
  let defaultEncoder = new Encoder({ useRecords: false });
1773
2057
  const encode = defaultEncoder.encode;
1774
2058
  const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
1775
- const REUSE_BUFFER_MODE = 1000;
2059
+ const REUSE_BUFFER_MODE = 512;
2060
+ const RESET_BUFFER_MODE = 1024;
1776
2061
 
1777
2062
  class EncoderStream extends stream.Transform {
1778
2063
  constructor(options) {