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/index.js CHANGED
@@ -11,7 +11,10 @@
11
11
  let src;
12
12
  let srcEnd;
13
13
  let position = 0;
14
- const RECORD_TAG_ID = 0x69;
14
+ const LEGACY_RECORD_INLINE_ID = 105;
15
+ const RECORD_DEFINITIONS_ID = 0xdffe;
16
+ const RECORD_INLINE_ID = 0xdfff; // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
17
+ const BUNDLED_STRINGS_ID = 0xdff9;
15
18
  const PACKED_REFERENCE_TAG_ID = 6;
16
19
  const STOP_CODE = {};
17
20
  let currentDecoder = {};
@@ -19,6 +22,7 @@
19
22
  let srcString;
20
23
  let srcStringStart = 0;
21
24
  let srcStringEnd = 0;
25
+ let bundledStrings;
22
26
  let referenceMap;
23
27
  let currentExtensions = [];
24
28
  let currentExtensionRanges = [];
@@ -34,13 +38,70 @@
34
38
  class Decoder {
35
39
  constructor(options) {
36
40
  if (options) {
41
+ if ((options.keyMap || options._keyMap) && !options.useRecords) {
42
+ options.useRecords = false;
43
+ options.mapsAsObjects = true;
44
+ }
37
45
  if (options.useRecords === false && options.mapsAsObjects === undefined)
38
46
  options.mapsAsObjects = true;
39
- if (options.getStructures && !options.structures)
47
+ if (options.getStructures)
48
+ options.getShared = options.getStructures;
49
+ if (options.getShared && !options.structures)
40
50
  (options.structures = []).uninitialized = true; // this is what we use to denote an uninitialized structures
51
+ if (options.keyMap) {
52
+ this.mapKey = new Map();
53
+ for (let [k,v] of Object.entries(options.keyMap)) this.mapKey.set(v,k);
54
+ }
41
55
  }
42
56
  Object.assign(this, options);
43
57
  }
58
+ /*
59
+ decodeKey(key) {
60
+ return this.keyMap
61
+ ? Object.keys(this.keyMap)[Object.values(this.keyMap).indexOf(key)] || key
62
+ : key
63
+ }
64
+ */
65
+ decodeKey(key) {
66
+ return this.keyMap ? this.mapKey.get(key) || key : key
67
+ }
68
+
69
+ encodeKey(key) {
70
+ return this.keyMap && this.keyMap.hasOwnProperty(key) ? this.keyMap[key] : key
71
+ }
72
+
73
+ encodeKeys(rec) {
74
+ if (!this._keyMap) return rec
75
+ let map = new Map();
76
+ for (let [k,v] of Object.entries(rec)) map.set((this._keyMap.hasOwnProperty(k) ? this._keyMap[k] : k), v);
77
+ return map
78
+ }
79
+
80
+ decodeKeys(map) {
81
+ if (!this._keyMap || map.constructor.name != 'Map') return map
82
+ if (!this._mapKey) {
83
+ this._mapKey = new Map();
84
+ for (let [k,v] of Object.entries(this._keyMap)) this._mapKey.set(v,k);
85
+ }
86
+ let res = {};
87
+ //map.forEach((v,k) => res[Object.keys(this._keyMap)[Object.values(this._keyMap).indexOf(k)] || k] = v)
88
+ map.forEach((v,k) => res[this._mapKey.has(k) ? this._mapKey.get(k) : k] = v);
89
+ return res
90
+ }
91
+
92
+ mapDecode(source, end) {
93
+
94
+ let res = this.decode(source);
95
+ if (this._keyMap) {
96
+ //Experiemntal support for Optimised KeyMap decoding
97
+ switch (res.constructor.name) {
98
+ case 'Array': return res.map(r => this.decodeKeys(r))
99
+ //case 'Map': return this.decodeKeys(res)
100
+ }
101
+ }
102
+ return res
103
+ }
104
+
44
105
  decode(source, end) {
45
106
  if (src) {
46
107
  // re-entrant execution, save the state and restore it after we do this decode
@@ -53,6 +114,7 @@
53
114
  position = 0;
54
115
  srcStringEnd = 0;
55
116
  srcString = null;
117
+ bundledStrings = null;
56
118
  src = source;
57
119
  // this provides cached access to the data view for a buffer if it is getting reused, which is a recommend
58
120
  // technique for getting data from a database where it can be copied into an existing buffer instead of creating
@@ -123,6 +185,9 @@
123
185
  function checkedRead() {
124
186
  try {
125
187
  let result = read();
188
+ if (bundledStrings) // bundled strings to skip past
189
+ position = bundledStrings.postBundlePosition;
190
+
126
191
  if (position == srcEnd) {
127
192
  // finished reading this source, cleanup references
128
193
  currentStructures = null;
@@ -197,6 +262,7 @@
197
262
  switch(majorType) {
198
263
  case 2: // byte string
199
264
  case 3: // text string
265
+ throw new Error('Indefinite length not supported for byte or text strings')
200
266
  case 4: // array
201
267
  let array = [];
202
268
  let value, i = 0;
@@ -208,8 +274,8 @@
208
274
  let key;
209
275
  if (currentDecoder.mapsAsObjects) {
210
276
  let object = {};
211
- while ((key = readKey()) != STOP_CODE)
212
- object[key] = read();
277
+ if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[currentDecoder.decodeKey(key)] = read();
278
+ else while ((key = read()) != STOP_CODE) object[key] = read();
213
279
  return object
214
280
  } else {
215
281
  if (restoreMapsAsObject) {
@@ -217,8 +283,8 @@
217
283
  restoreMapsAsObject = false;
218
284
  }
219
285
  let map = new Map();
220
- while ((key = read()) != STOP_CODE)
221
- map.set(key, read());
286
+ if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) map.set(currentDecoder.decodeKey(key), read());
287
+ else while ((key = read()) != STOP_CODE) map.set(key, read());
222
288
  return map
223
289
  }
224
290
  case 7:
@@ -250,16 +316,15 @@
250
316
  return readFixedString(token)
251
317
  case 4: // array
252
318
  let array = new Array(token);
253
- for (let i = 0; i < token; i++) {
254
- array[i] = read();
255
- }
319
+ //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
320
+ //else
321
+ for (let i = 0; i < token; i++) array[i] = read();
256
322
  return array
257
323
  case 5: // map
258
324
  if (currentDecoder.mapsAsObjects) {
259
325
  let object = {};
260
- for (let i = 0; i < token; i++) {
261
- object[readKey()] = read();
262
- }
326
+ if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[currentDecoder.decodeKey(read())] = read();
327
+ else for (let i = 0; i < token; i++) object[read()] = read();
263
328
  return object
264
329
  } else {
265
330
  if (restoreMapsAsObject) {
@@ -267,63 +332,66 @@
267
332
  restoreMapsAsObject = false;
268
333
  }
269
334
  let map = new Map();
270
- for (let i = 0; i < token; i++) {
271
- map.set(read(), read());
272
- }
335
+ if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
336
+ else for (let i = 0; i < token; i++) map.set(read(), read());
273
337
  return map
274
338
  }
275
339
  case 6: // extension
276
- if ((token >> 8) == RECORD_TAG_ID) { // record structures
277
- let structure = currentStructures[token & 0xff];
340
+ if (token >= BUNDLED_STRINGS_ID) {
341
+ let structure = currentStructures[token & 0x1fff]; // check record structures first
342
+ // 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))
278
343
  if (structure) {
279
- if (!structure.read)
280
- structure.read = createStructureReader(structure);
344
+ if (!structure.read) structure.read = createStructureReader(structure);
281
345
  return structure.read()
282
- } else if (currentDecoder.getStructures) {
283
- let updatedStructures = saveState(() => {
284
- // save the state in case getStructures modifies our buffer
285
- src = null;
286
- return currentDecoder.getStructures()
287
- });
288
- if (currentStructures === true)
289
- currentDecoder.structures = currentStructures = updatedStructures;
290
- else
291
- currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
292
- structure = currentStructures[token & 0xff];
293
- if (structure) {
294
- if (!structure.read)
295
- structure.read = createStructureReader(structure);
296
- return structure.read()
297
- } else
298
- return token
299
- } else
300
- return token
301
- } else {
302
- let extension = currentExtensions[token];
303
- if (extension) {
304
- if (extension.handlesRead)
305
- return extension(read)
306
- else
307
- return extension(read())
308
- } else {
309
- let input = read();
310
- for (let i = 0; i < currentExtensionRanges.length; i++) {
311
- let value = currentExtensionRanges[i](token, input);
312
- if (value !== undefined)
313
- return value
346
+ }
347
+ if (token < 0x10000) {
348
+ 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)
349
+ return recordDefinition(read())
350
+ else if (token == RECORD_DEFINITIONS_ID) {
351
+ let length = readJustLength();
352
+ let id = read();
353
+ for (let i = 2; i < length; i++) {
354
+ recordDefinition([id++, read()]);
355
+ }
356
+ return read()
357
+ } else if (token == BUNDLED_STRINGS_ID) {
358
+ return readBundleExt()
359
+ }
360
+ if (currentDecoder.getShared) {
361
+ loadShared();
362
+ structure = currentStructures[token & 0x1fff];
363
+ if (structure) {
364
+ if (!structure.read)
365
+ structure.read = createStructureReader(structure);
366
+ return structure.read()
367
+ }
314
368
  }
315
- return new Tag(input)
316
369
  }
317
370
  }
371
+ let extension = currentExtensions[token];
372
+ if (extension) {
373
+ if (extension.handlesRead)
374
+ return extension(read)
375
+ else
376
+ return extension(read())
377
+ } else {
378
+ let input = read();
379
+ for (let i = 0; i < currentExtensionRanges.length; i++) {
380
+ let value = currentExtensionRanges[i](token, input);
381
+ if (value !== undefined)
382
+ return value
383
+ }
384
+ return new Tag(input, token)
385
+ }
318
386
  case 7: // fixed value
319
387
  switch (token) {
320
388
  case 0x14: return false
321
389
  case 0x15: return true
322
390
  case 0x16: return null
323
391
  case 0x17: return; // undefined
324
- case 0x1f:
392
+ case 0x1f:
325
393
  default:
326
- let packedValue = packedValues[token];
394
+ let packedValue = (packedValues || getPackedValues())[token];
327
395
  if (packedValue !== undefined)
328
396
  return packedValue
329
397
  throw new Error('Unknown token ' + token)
@@ -339,41 +407,47 @@
339
407
  }
340
408
  const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
341
409
  function createStructureReader(structure) {
342
- let l = structure.length;
343
410
  function readObject() {
411
+ // get the array size from the header
412
+ let length = src[position++];
413
+ //let majorType = token >> 5
414
+ length = length & 0x1f;
415
+ if (length > 0x17) {
416
+ switch (length) {
417
+ case 0x18:
418
+ length = src[position++];
419
+ break
420
+ case 0x19:
421
+ length = dataView.getUint16(position);
422
+ position += 2;
423
+ break
424
+ case 0x1a:
425
+ length = dataView.getUint32(position);
426
+ position += 4;
427
+ break
428
+ default:
429
+ throw new Error('Expected array header, but got ' + src[position - 1])
430
+ }
431
+ }
344
432
  // This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
345
- if (readObject.count++ > 2) {
346
- 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);
347
- return this.read()
433
+ if (this.objectLiteralSize === length) // we have a fast object literal reader, use it (assuming it is the right length)
434
+ return this.objectLiteral(read)
435
+ if (this.count++ == 3) { // create a fast reader
436
+ this.objectLiteralSize = length;
437
+ this.objectLiteral = currentDecoder.keyMap
438
+ ? new Function('r', 'return {' + this.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
439
+ : new Function('r', 'return {' + this.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}');
440
+ return this.objectLiteral(read)
348
441
  }
349
- readArrayHeader();
350
442
  let object = {};
351
- for (let i = 0; i < l; i++) {
352
- let key = structure[i];
353
- object[key] = read();
354
- }
443
+ if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read();
444
+ else for (let i = 0; i < length; i++) object[this[i]] = read();
355
445
  return object
356
446
  }
357
- readObject.count = 0;
447
+ structure.count = 0;
358
448
  return readObject
359
449
  }
360
450
 
361
- function readArrayHeader(expectedLength) {
362
- // consume the array header, TODO: check expected length
363
- let token = src[position++];
364
- //let majorType = token >> 5
365
- token = token & 0x1f;
366
- if (token > 0x17) {
367
- switch (token) {
368
- case 0x18: position++;
369
- break
370
- case 0x19: position += 2;
371
- break
372
- case 0x1a: position += 4;
373
- }
374
- }
375
- }
376
-
377
451
  let readFixedString = readStringJS;
378
452
 
379
453
  let isNativeAccelerationEnabled = false;
@@ -605,76 +679,15 @@
605
679
  }
606
680
 
607
681
  let keyCache = new Array(4096);
608
- function readKey() {
609
- let length = src[position++];
610
- if (length >= 0x60 && length < 0x78) {
611
- // fixstr, potentially use key cache
612
- length = length - 0x60;
613
- if (srcStringEnd >= position) // if it has been extracted, must use it (and faster anyway)
614
- return srcString.slice(position - srcStringStart, (position += length) - srcStringStart)
615
- else if (!(srcStringEnd == 0 && srcEnd < 180))
616
- return readFixedString(length)
617
- } else { // not cacheable, go back and do a standard read
618
- position--;
619
- return read()
620
- }
621
- let key = ((length << 5) ^ (length > 1 ? dataView.getUint16(position) : length > 0 ? src[position] : 0)) & 0xfff;
622
- let entry = keyCache[key];
623
- let checkPosition = position;
624
- let end = position + length - 3;
625
- let chunk;
626
- let i = 0;
627
- if (entry && entry.bytes == length) {
628
- while (checkPosition < end) {
629
- chunk = dataView.getUint32(checkPosition);
630
- if (chunk != entry[i++]) {
631
- checkPosition = 0x70000000;
632
- break
633
- }
634
- checkPosition += 4;
635
- }
636
- end += 3;
637
- while (checkPosition < end) {
638
- chunk = src[checkPosition++];
639
- if (chunk != entry[i++]) {
640
- checkPosition = 0x70000000;
641
- break
642
- }
643
- }
644
- if (checkPosition === end) {
645
- position = checkPosition;
646
- return entry.string
647
- }
648
- end -= 3;
649
- checkPosition = position;
650
- }
651
- entry = [];
652
- keyCache[key] = entry;
653
- entry.bytes = length;
654
- while (checkPosition < end) {
655
- chunk = dataView.getUint32(checkPosition);
656
- entry.push(chunk);
657
- checkPosition += 4;
658
- }
659
- end += 3;
660
- while (checkPosition < end) {
661
- chunk = src[checkPosition++];
662
- entry.push(chunk);
663
- }
664
- // for small blocks, avoiding the overhead of the extract call is helpful
665
- let string = length < 16 ? shortStringInJS(length) : longStringInJS(length);
666
- if (string != null)
667
- return entry.string = string
668
- return entry.string = readFixedString(length)
669
- }
670
682
 
671
683
  class Tag {
672
- constructor(value) {
684
+ constructor(value, tag) {
673
685
  this.value = value;
686
+ this.tag = tag;
674
687
  }
675
688
  }
676
689
 
677
- let glbl = typeof window == 'object' ? window : global;
690
+ let glbl = typeof self == 'object' ? self : global;
678
691
 
679
692
  currentExtensions[0] = (dateString) => {
680
693
  // string date extension
@@ -688,38 +701,62 @@
688
701
 
689
702
  currentExtensions[2] = (buffer) => {
690
703
  // bigint extension
691
- return new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0)
704
+ let value = BigInt(0);
705
+ for (let i = 0, l = buffer.byteLength; i < l; i++) {
706
+ value = BigInt(buffer[i]) + value << BigInt(8);
707
+ }
708
+ return value
692
709
  };
693
710
 
694
711
  currentExtensions[3] = (buffer) => {
695
712
  // negative bigint extension
696
- return BigInt(-1) - (new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0))
713
+ return BigInt(-1) - currentExtensions[2](buffer)
714
+ };
715
+ currentExtensions[4] = (fraction) => {
716
+ // best to reparse to maintain accuracy
717
+ return +(fraction[1] + 'e' + fraction[0])
718
+ };
719
+
720
+ currentExtensions[5] = (fraction) => {
721
+ // probably not sufficiently accurate
722
+ return fraction[1] * Math.exp(fraction[0] * Math.log(2))
697
723
  };
698
724
 
699
725
  // the registration of the record definition extension (tag 105)
700
- const recordDefinition = () => {
701
- let definition = read();
702
- let structure = definition[0];
703
- let id = definition[1];
704
- currentStructures[id & 0xff] = structure;
726
+ const recordDefinition = (definition) => {
727
+ let id = definition[0] - 0xe000;
728
+ let structure = definition[1];
729
+ currentStructures[id] = structure;
705
730
  structure.read = createStructureReader(structure);
706
731
  let object = {};
707
- for (let i = 2,l = definition.length; i < l; i++) {
708
- let key = structure[i - 2];
709
- object[key] = definition[i];
710
- }
732
+ if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
733
+ let key = currentDecoder.decodeKey(structure[i - 2]);
734
+ object[key] = definition[i];
735
+ }
736
+ else for (let i = 2,l = definition.length; i < l; i++) {
737
+ let key = structure[i - 2];
738
+ object[key] = definition[i];
739
+ }
711
740
  return object
712
741
  };
713
-
714
- recordDefinition.handlesRead = true;
715
- currentExtensions[RECORD_TAG_ID] = recordDefinition;
742
+ currentExtensions[LEGACY_RECORD_INLINE_ID] = recordDefinition;
743
+ currentExtensions[14] = (value) => {
744
+ if (bundledStrings)
745
+ return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
746
+ return new Tag(value, 14)
747
+ };
748
+ currentExtensions[15] = (value) => {
749
+ if (bundledStrings)
750
+ return bundledStrings[1].slice(bundledStrings.position1, bundledStrings.position1 += value)
751
+ return new Tag(value, 15)
752
+ };
716
753
 
717
754
  currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
718
755
  return (glbl[data[0]] || Error)(data[1], data[2])
719
756
  };
720
757
  const packedTable = (read) => {
721
758
  if (src[position++] != 0x84)
722
- throw new Error('Packed values structure must be followed by 4 element array')
759
+ throw new Error('Packed values structure must be followed by a 4 element array')
723
760
  let newPackedValues = read(); // packed values
724
761
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
725
762
  packedValues.prefixes = read();
@@ -730,15 +767,36 @@
730
767
  currentExtensions[51] = packedTable;
731
768
 
732
769
  currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
770
+ if (!packedValues) {
771
+ if (currentDecoder.getShared)
772
+ loadShared();
773
+ else
774
+ return new Tag(data, PACKED_REFERENCE_TAG_ID)
775
+ }
733
776
  if (typeof data == 'number')
734
777
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
735
778
  throw new Error('No support for non-integer packed references yet')
736
779
  };
780
+ currentExtensions[25] = (id) => {
781
+ return stringRefs[id]
782
+ };
783
+ currentExtensions[256] = (read) => {
784
+ stringRefs = [];
785
+ try {
786
+ return read()
787
+ } finally {
788
+ stringRefs = null;
789
+ }
790
+ };
791
+ currentExtensions[256].handlesRead = true;
737
792
 
738
- currentExtensions[40009] = (id) => {
739
- // id extension (for structured clones)
740
- if (!referenceMap)
793
+ currentExtensions[28] = (read) => {
794
+ // shareable http://cbor.schmorp.de/value-sharing (for structured clones)
795
+ if (!referenceMap) {
741
796
  referenceMap = new Map();
797
+ referenceMap.id = 0;
798
+ }
799
+ let id = referenceMap.id++;
742
800
  let token = src[position];
743
801
  let target;
744
802
  // TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
@@ -756,9 +814,10 @@
756
814
  refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
757
815
  return targetProperties // no cycle, can just use the returned read object
758
816
  };
817
+ currentExtensions[28].handlesRead = true;
759
818
 
760
- currentExtensions[40010] = (id) => {
761
- // pointer extension (for structured clones)
819
+ currentExtensions[29] = (id) => {
820
+ // sharedref http://cbor.schmorp.de/value-sharing (for structured clones)
762
821
  let refEntry = referenceMap.get(id);
763
822
  refEntry.used = true;
764
823
  return refEntry.target
@@ -781,19 +840,36 @@
781
840
  return a.concat(b)
782
841
  return Object.assign({}, a, b)
783
842
  }
843
+ function getPackedValues() {
844
+ if (!packedValues) {
845
+ if (currentDecoder.getShared)
846
+ loadShared();
847
+ else
848
+ throw new Error('No packed values available')
849
+ }
850
+ return packedValues
851
+ }
852
+ const SHARED_DATA_TAG_ID = 0x53687264; // ascii 'Shrd'
784
853
  currentExtensionRanges.push((tag, input) => {
785
854
  if (tag >= 225 && tag <= 255)
786
- return combine(packedTable.prefixes[tag - 224], input)
855
+ return combine(getPackedValues().prefixes[tag - 224], input)
787
856
  if (tag >= 28704 && tag <= 32767)
788
- return combine(packedTable.prefixes[tag - 28672], input)
857
+ return combine(getPackedValues().prefixes[tag - 28672], input)
789
858
  if (tag >= 1879052288 && tag <= 2147483647)
790
- return combine(packedTable.prefixes[tag - 1879048192], input)
859
+ return combine(getPackedValues().prefixes[tag - 1879048192], input)
791
860
  if (tag >= 216 && tag <= 223)
792
- return combine(input, packedTable.suffixes[tag - 216])
861
+ return combine(input, getPackedValues().suffixes[tag - 216])
793
862
  if (tag >= 27647 && tag <= 28671)
794
- return combine(input, packedTable.suffixes[tag - 27639])
863
+ return combine(input, getPackedValues().suffixes[tag - 27639])
795
864
  if (tag >= 1811940352 && tag <= 1879048191)
796
- return combine(input, packedTable.suffixes[tag - 1811939328])
865
+ return combine(input, getPackedValues().suffixes[tag - 1811939328])
866
+ 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)
867
+ return {
868
+ packedValues: packedValues,
869
+ structures: currentStructures.slice(0),
870
+ version: input,
871
+ }
872
+ }
797
873
  });
798
874
 
799
875
  const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array');
@@ -810,6 +886,61 @@
810
886
  };
811
887
  }
812
888
 
889
+ function readBundleExt() {
890
+ let length = readJustLength();
891
+ let bundlePosition = position + read();
892
+ for (let i = 2; i < length; i++) {
893
+ // skip past bundles that were already read
894
+ let bundleLength = readJustLength(); // this will increment position, so must add to position afterwards
895
+ position += bundleLength;
896
+ }
897
+ let dataPosition = position;
898
+ position = bundlePosition;
899
+ bundledStrings = [readStringJS(readJustLength()), readStringJS(readJustLength())];
900
+ bundledStrings.position0 = 0;
901
+ bundledStrings.position1 = 0;
902
+ bundledStrings.postBundlePosition = position;
903
+ position = dataPosition;
904
+ return read()
905
+ }
906
+
907
+ function readJustLength() {
908
+ let token = src[position++] & 0x1f;
909
+ if (token > 0x17) {
910
+ switch (token) {
911
+ case 0x18:
912
+ token = src[position++];
913
+ break
914
+ case 0x19:
915
+ token = dataView.getUint16(position);
916
+ position += 2;
917
+ break
918
+ case 0x1a:
919
+ token = dataView.getUint32(position);
920
+ position += 4;
921
+ break
922
+ }
923
+ }
924
+ return token
925
+ }
926
+
927
+ function loadShared() {
928
+ if (currentDecoder.getShared) {
929
+ let sharedData = saveState(() => {
930
+ // save the state in case getShared modifies our buffer
931
+ src = null;
932
+ return currentDecoder.getShared()
933
+ }) || {};
934
+ let updatedStructures = sharedData.structures || [];
935
+ currentDecoder.sharedVersion = sharedData.version;
936
+ packedValues = currentDecoder.sharedValues = sharedData.packedValues;
937
+ if (currentStructures === true)
938
+ currentDecoder.structures = currentStructures = updatedStructures;
939
+ else
940
+ currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
941
+ }
942
+ }
943
+
813
944
  function saveState(callback) {
814
945
  let savedSrcEnd = srcEnd;
815
946
  let savedPosition = position;
@@ -817,6 +948,8 @@
817
948
  let savedSrcStringEnd = srcStringEnd;
818
949
  let savedSrcString = srcString;
819
950
  let savedReferenceMap = referenceMap;
951
+ let savedBundledStrings = bundledStrings;
952
+
820
953
  // TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
821
954
  let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
822
955
  let savedStructures = currentStructures;
@@ -829,6 +962,7 @@
829
962
  srcStringEnd = savedSrcStringEnd;
830
963
  srcString = savedSrcString;
831
964
  referenceMap = savedReferenceMap;
965
+ bundledStrings = savedBundledStrings;
832
966
  src = savedSrc;
833
967
  sequentialMode = savedSequentialMode;
834
968
  currentStructures = savedStructures;
@@ -875,13 +1009,15 @@
875
1009
  const hasNodeBuffer = typeof Buffer !== 'undefined';
876
1010
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
877
1011
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
878
- const RECORD_STARTING_ID_PREFIX = 0x69; // tag 105/0x69
879
1012
  const MAX_STRUCTURES = 0x100;
880
1013
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
881
1014
  let target;
882
1015
  let targetView;
883
1016
  let position$1 = 0;
884
1017
  let safeEnd;
1018
+ let bundledStrings$1 = null;
1019
+ const MAX_BUNDLE_SIZE = 0xf000;
1020
+ const hasNonLatin = /[\u0080-\uFFFF]/;
885
1021
  const RECORD_SYMBOL = Symbol('record-id');
886
1022
  class Encoder extends Decoder {
887
1023
  constructor(options) {
@@ -893,7 +1029,6 @@
893
1029
  let structures;
894
1030
  let referenceMap;
895
1031
  options = options || {};
896
- let lastSharedStructuresLength = 0;
897
1032
  let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
898
1033
  return target.utf8Write(string, position, maxBytes)
899
1034
  } : (textEncoder && textEncoder.encodeInto) ?
@@ -902,12 +1037,14 @@
902
1037
  } : false;
903
1038
 
904
1039
  let encoder = this;
905
- let maxSharedStructures = 64;
1040
+ let maxSharedStructures = options.maxSharedStructures || 128;
906
1041
  let isSequential = options.sequential;
907
1042
  if (isSequential) {
908
1043
  maxSharedStructures = 0;
909
1044
  this.structures = [];
910
1045
  }
1046
+ if (this.saveStructures)
1047
+ this.saveShared = this.saveStructures;
911
1048
  let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues;
912
1049
  let sharedPackedObjectMap;
913
1050
  if (sharedValues) {
@@ -915,13 +1052,29 @@
915
1052
  for (let i = 0, l = sharedValues.length; i < l; i++) {
916
1053
  sharedPackedObjectMap[sharedValues[i]] = i;
917
1054
  }
918
-
919
1055
  }
920
1056
  let recordIdsToRemove = [];
921
1057
  let transitionsCount = 0;
922
1058
  let serializationsSinceTransitionRebuild = 0;
923
-
924
- this.encode = function(value, encodeOptions) {
1059
+
1060
+ this.mapEncode = function(value, encodeOptions) {
1061
+ // Experimental support for premapping keys using _keyMap instad of keyMap - not optiimised yet)
1062
+ if (this._keyMap && !this._mapped) {
1063
+ //console.log('encoding ', value)
1064
+ switch (value.constructor.name) {
1065
+ case 'Array':
1066
+ value = value.map(r => this.encodeKeys(r));
1067
+ break
1068
+ //case 'Map':
1069
+ // value = this.encodeKeys(value)
1070
+ // break
1071
+ }
1072
+ //this._mapped = true
1073
+ }
1074
+ return this.encode(value, encodeOptions)
1075
+ };
1076
+
1077
+ this.encode = function(value, encodeOptions) {
925
1078
  if (!target) {
926
1079
  target = new ByteArrayAllocate(8192);
927
1080
  targetView = new DataView(target.buffer, 0, 8192);
@@ -938,10 +1091,25 @@
938
1091
  position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
939
1092
  start = position$1;
940
1093
  referenceMap = encoder.structuredClone ? new Map() : null;
1094
+ if (encoder.bundleStrings && typeof value !== 'string') {
1095
+ bundledStrings$1 = [];
1096
+ bundledStrings$1.size = Infinity; // force a new bundle start on first string
1097
+ } else
1098
+ bundledStrings$1 = null;
1099
+
941
1100
  sharedStructures = encoder.structures;
942
1101
  if (sharedStructures) {
943
- if (sharedStructures.uninitialized)
944
- encoder.structures = sharedStructures = encoder.getStructures();
1102
+ if (sharedStructures.uninitialized) {
1103
+ let sharedData = encoder.getShared() || {};
1104
+ encoder.structures = sharedStructures = sharedData.structures || [];
1105
+ encoder.sharedVersion = sharedData.version;
1106
+ let sharedValues = encoder.sharedValues = sharedData.packedValues;
1107
+ if (sharedValues) {
1108
+ sharedPackedObjectMap = {};
1109
+ for (let i = 0, l = sharedValues.length; i < l; i++)
1110
+ sharedPackedObjectMap[sharedValues[i]] = i;
1111
+ }
1112
+ }
945
1113
  let sharedStructuresLength = sharedStructures.length;
946
1114
  if (sharedStructuresLength > maxSharedStructures && !isSequential)
947
1115
  sharedStructuresLength = maxSharedStructures;
@@ -950,6 +1118,7 @@
950
1118
  sharedStructures.transitions = Object.create(null);
951
1119
  for (let i = 0; i < sharedStructuresLength; i++) {
952
1120
  let keys = sharedStructures[i];
1121
+ //console.log('shared struct keys:', keys)
953
1122
  if (!keys)
954
1123
  continue
955
1124
  let nextTransition, transition = sharedStructures.transitions;
@@ -963,7 +1132,6 @@
963
1132
  }
964
1133
  transition[RECORD_SYMBOL] = i;
965
1134
  }
966
- lastSharedStructuresLength = sharedStructures.length;
967
1135
  }
968
1136
  if (!isSequential)
969
1137
  sharedStructures.nextId = sharedStructuresLength;
@@ -996,9 +1164,12 @@
996
1164
  }
997
1165
  try {
998
1166
  encode(value);
1167
+ if (bundledStrings$1) {
1168
+ writeBundles(start, encode);
1169
+ }
999
1170
  encoder.offset = position$1; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
1000
1171
  if (referenceMap && referenceMap.idsToInsert) {
1001
- position$1 += referenceMap.idsToInsert.length * 8;
1172
+ position$1 += referenceMap.idsToInsert.length * 2;
1002
1173
  if (position$1 > safeEnd)
1003
1174
  makeRoom(position$1);
1004
1175
  encoder.offset = position$1;
@@ -1006,12 +1177,12 @@
1006
1177
  referenceMap = null;
1007
1178
  return serialized
1008
1179
  }
1009
- if (encodeOptions === REUSE_BUFFER_MODE) {
1180
+ if (encodeOptions & REUSE_BUFFER_MODE) {
1010
1181
  target.start = start;
1011
1182
  target.end = position$1;
1012
1183
  return target
1013
1184
  }
1014
- return target.subarray(start, position$1) // position can change if we call pack again in saveStructures, so we get the buffer now
1185
+ return target.subarray(start, position$1) // position can change if we call pack again in saveShared, so we get the buffer now
1015
1186
  } finally {
1016
1187
  if (sharedStructures) {
1017
1188
  if (serializationsSinceTransitionRebuild < 10)
@@ -1029,34 +1200,27 @@
1029
1200
  }
1030
1201
  recordIdsToRemove = [];
1031
1202
  }
1032
- if (hasSharedUpdate && encoder.saveStructures) {
1033
- if (encoder.structures.length > maxSharedStructures) {
1034
- encoder.structures = encoder.structures.slice(0, maxSharedStructures);
1035
- }
1036
- // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1037
- let returnBuffer = target.subarray(start, position$1);
1038
- let shared = encoder.structures || [];
1039
- if (sharedValues) {
1040
- shared = shared.concat(sharedValues);
1041
- }
1042
-
1043
- if (encoder.saveStructures(encoder.structures, lastSharedStructuresLength) === false) {
1044
- // get updated structures and try again if the update failed
1045
- encoder.structures = encoder.getStructures() || [];
1046
- return encoder.encode(value)
1047
- }
1048
- lastSharedStructuresLength = shared.length;
1049
- return returnBuffer
1203
+ }
1204
+ if (hasSharedUpdate && encoder.saveShared) {
1205
+ if (encoder.structures.length > maxSharedStructures) {
1206
+ encoder.structures = encoder.structures.slice(0, maxSharedStructures);
1050
1207
  }
1208
+ // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1209
+ let returnBuffer = target.subarray(start, position$1);
1210
+ if (encoder.updateSharedData() === false)
1211
+ return encoder.encode(value) // re-encode if it fails
1212
+ return returnBuffer
1051
1213
  }
1214
+ if (encodeOptions & RESET_BUFFER_MODE)
1215
+ position$1 = start;
1052
1216
  }
1053
1217
  };
1054
1218
  this.findCommonStringsToPack = () => {
1055
1219
  samplingPackedValues = new Map();
1056
1220
  if (!sharedPackedObjectMap)
1057
1221
  sharedPackedObjectMap = Object.create(null);
1058
- return ({ threshold }) => {
1059
- threshold = threshold || 4;
1222
+ return (options) => {
1223
+ let threshold = options && options.threshold || 4;
1060
1224
  let position = this.pack ? options.maxPrivatePackedValues || 16 : 0;
1061
1225
  if (!sharedValues)
1062
1226
  sharedValues = this.sharedValues = [];
@@ -1067,6 +1231,7 @@
1067
1231
  hasSharedUpdate = true;
1068
1232
  }
1069
1233
  }
1234
+ while (this.saveShared && this.updateSharedData() === false) {}
1070
1235
  samplingPackedValues = null;
1071
1236
  }
1072
1237
  };
@@ -1115,6 +1280,33 @@
1115
1280
  }
1116
1281
  }
1117
1282
  let strLength = value.length;
1283
+ if (bundledStrings$1 && strLength >= 4 && strLength < 0x400) {
1284
+ if ((bundledStrings$1.size += strLength) > MAX_BUNDLE_SIZE) {
1285
+ let extStart;
1286
+ let maxBytes = (bundledStrings$1[0] ? bundledStrings$1[0].length * 3 + bundledStrings$1[1].length : 0) + 10;
1287
+ if (position$1 + maxBytes > safeEnd)
1288
+ target = makeRoom(position$1 + maxBytes);
1289
+ target[position$1++] = 0xd9; // tag 16-bit
1290
+ target[position$1++] = 0xdf; // tag 0xdff9
1291
+ target[position$1++] = 0xf9;
1292
+ // TODO: If we only have one bundle with any string data, only write one string bundle
1293
+ target[position$1++] = bundledStrings$1.position ? 0x84 : 0x82; // array of 4 or 2 elements depending on if we write bundles
1294
+ target[position$1++] = 0x1a; // 32-bit unsigned int
1295
+ extStart = position$1 - start;
1296
+ position$1 += 4; // reserve for writing bundle reference
1297
+ if (bundledStrings$1.position) {
1298
+ writeBundles(start, encode); // write the last bundles
1299
+ }
1300
+ bundledStrings$1 = ['', '']; // create new ones
1301
+ bundledStrings$1.size = 0;
1302
+ bundledStrings$1.position = extStart;
1303
+ }
1304
+ let twoByte = hasNonLatin.test(value);
1305
+ bundledStrings$1[twoByte ? 0 : 1] += value;
1306
+ target[position$1++] = twoByte ? 0xce : 0xcf;
1307
+ encode(strLength);
1308
+ return
1309
+ }
1118
1310
  let headerSize;
1119
1311
  // first we estimate the header size, so we can write to the correct location
1120
1312
  if (strLength < 0x20) {
@@ -1241,16 +1433,16 @@
1241
1433
  if (referenceMap) {
1242
1434
  let referee = referenceMap.get(value);
1243
1435
  if (referee) {
1244
- if (!referee.id) {
1436
+ target[position$1++] = 0xd8;
1437
+ target[position$1++] = 29; // http://cbor.schmorp.de/value-sharing
1438
+ target[position$1++] = 0x19; // 16-bit uint
1439
+ if (!referee.references) {
1245
1440
  let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = []);
1246
- referee.id = idsToInsert.push(referee);
1441
+ referee.references = [];
1442
+ idsToInsert.push(referee);
1247
1443
  }
1248
- target[position$1++] = 0xd9;
1249
- target[position$1++] = 40010 >> 8;
1250
- target[position$1++] = 40010 & 0xff;
1251
- target[position$1++] = 0x1a; // uint32
1252
- targetView.setUint32(position$1, referee.id);
1253
- position$1 += 4;
1444
+ referee.references.push(position$1 - start);
1445
+ position$1 += 2; // TODO: also support 32-bit
1254
1446
  return
1255
1447
  } else
1256
1448
  referenceMap.set(value, { offset: position$1 - start });
@@ -1290,16 +1482,23 @@
1290
1482
  targetView.setUint32(position$1, length);
1291
1483
  position$1 += 4;
1292
1484
  }
1293
- for (let [ key, entryValue ] of value) {
1294
- encode(key);
1295
- encode(entryValue);
1485
+ if (encoder.keyMap) {
1486
+ for (let [ key, entryValue ] of value) {
1487
+ encode(encoder.encodeKey(key));
1488
+ encode(entryValue);
1489
+ }
1490
+ } else {
1491
+ for (let [ key, entryValue ] of value) {
1492
+ encode(key);
1493
+ encode(entryValue);
1494
+ }
1296
1495
  }
1297
- } else {
1496
+ } else {
1298
1497
  for (let i = 0, l = extensions.length; i < l; i++) {
1299
1498
  let extensionClass = extensionClasses[i];
1300
1499
  if (value instanceof extensionClass) {
1301
1500
  let extension = extensions[i];
1302
- let tag = extension.tag;
1501
+ let tag = extension.tag || extension.getTag && extension.getTag(value);
1303
1502
  if (tag < 0x18) {
1304
1503
  target[position$1++] = 0xc0 | tag;
1305
1504
  } else if (tag < 0x100) {
@@ -1361,6 +1560,7 @@
1361
1560
  const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
1362
1561
  // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
1363
1562
  let keys = Object.keys(object);
1563
+ let vals = Object.values(object);
1364
1564
  let length = keys.length;
1365
1565
  if (length < 0x18) {
1366
1566
  target[position$1++] = 0xa0 | length;
@@ -1376,10 +1576,16 @@
1376
1576
  targetView.setUint32(position$1, length);
1377
1577
  position$1 += 4;
1378
1578
  }
1379
- let key;
1380
- for (let i = 0; i < length; i++) {
1381
- encode(key = keys[i]);
1382
- encode(object[key]);
1579
+ if (encoder.keyMap) {
1580
+ for (let i = 0; i < length; i++) {
1581
+ encode(encodeKey(keys[i]));
1582
+ encode(vals[i]);
1583
+ }
1584
+ } else {
1585
+ for (let i = 0; i < length; i++) {
1586
+ encode(keys[i]);
1587
+ encode(vals[i]);
1588
+ }
1383
1589
  }
1384
1590
  } :
1385
1591
  (object, safePrototype) => {
@@ -1387,12 +1593,18 @@
1387
1593
  let objectOffset = position$1 - start;
1388
1594
  position$1 += 2;
1389
1595
  let size = 0;
1390
- for (let key in object) {
1391
- if (safePrototype || object.hasOwnProperty(key)) {
1392
- encode(key);
1596
+ if (encoder.keyMap) {
1597
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1598
+ encode(encoder.encodeKey(key));
1393
1599
  encode(object[key]);
1394
1600
  size++;
1395
1601
  }
1602
+ } else {
1603
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1604
+ encode(key);
1605
+ encode(object[key]);
1606
+ size++;
1607
+ }
1396
1608
  }
1397
1609
  target[objectOffset++ + start] = size >> 8;
1398
1610
  target[objectOffset + start] = size & 0xff;
@@ -1440,11 +1652,16 @@
1440
1652
  }*/
1441
1653
  (object) => {
1442
1654
  let keys = Object.keys(object);
1655
+ let vals = Object.values(object);
1656
+ if (this.keyMap) keys = keys.map(k => this.encodeKey(k));
1443
1657
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1444
1658
  let newTransitions = 0;
1445
1659
  let length = keys.length;
1660
+ //let parentRecordId
1446
1661
  for (let i = 0; i < length; i++) {
1662
+ //if (!parentRecordId)
1447
1663
  let key = keys[i];
1664
+ // parentRecordId = transition[RECORD_SYMBOL]
1448
1665
  nextTransition = transition[key];
1449
1666
  if (!nextTransition) {
1450
1667
  nextTransition = transition[key] = Object.create(null);
@@ -1454,9 +1671,9 @@
1454
1671
  }
1455
1672
  let recordId = transition[RECORD_SYMBOL];
1456
1673
  if (recordId !== undefined) {
1457
- target[position$1++] = 0xd9; // tag two byte
1458
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1459
- target[position$1++] = recordId;
1674
+ target[position$1++] = 0xd9;
1675
+ target[position$1++] = (recordId >> 8) | 0xe0;
1676
+ target[position$1++] = recordId & 0xff;
1460
1677
  } else {
1461
1678
  recordId = structures.nextId++;
1462
1679
  if (!recordId) {
@@ -1469,13 +1686,13 @@
1469
1686
  transition[RECORD_SYMBOL] = recordId;
1470
1687
  structures[recordId] = keys;
1471
1688
  if (sharedStructures && sharedStructures.length <= maxSharedStructures) {
1472
- target[position$1++] = 0xd9; // tag two byte
1473
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1474
- target[position$1++] = recordId; // tag number
1689
+ target[position$1++] = 0xd9;
1690
+ target[position$1++] = (recordId >> 8) | 0xe0;
1691
+ target[position$1++] = recordId & 0xff;
1475
1692
  hasSharedUpdate = true;
1476
1693
  } else {
1477
- target[position$1++] = 0xd8;
1478
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1694
+ targetView.setUint32(position$1, 0xd9dfff00); // tag two byte, then record definition id
1695
+ position$1 += 3;
1479
1696
  if (newTransitions)
1480
1697
  transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
1481
1698
  // record the removal of the id, we can maintain our shared structure
@@ -1483,13 +1700,9 @@
1483
1700
  recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined; // we are cycling back through, and have to remove old ones
1484
1701
  recordIdsToRemove.push(transition);
1485
1702
  writeArrayHeader(length + 2);
1703
+ encode(0xe000 + recordId);
1486
1704
  encode(keys);
1487
- target[position$1++] = 0x19; // uint16
1488
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1489
- target[position$1++] = recordId;
1490
- // now write the values
1491
- for (let i =0; i < length; i++)
1492
- encode(object[keys[i]]);
1705
+ for (let v of Object.values(object)) encode(v);
1493
1706
  return
1494
1707
  }
1495
1708
  }
@@ -1498,8 +1711,7 @@
1498
1711
  } else {
1499
1712
  writeArrayHeader(length);
1500
1713
  }
1501
- for (let i =0; i < length; i++)
1502
- encode(object[keys[i]]);
1714
+ for (let i =0; i < length; i++) encode(vals[i]);
1503
1715
  };
1504
1716
  const makeRoom = (end) => {
1505
1717
  let newSize;
@@ -1529,6 +1741,33 @@
1529
1741
  targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
1530
1742
  position$1 = 0;
1531
1743
  }
1744
+ clearSharedData() {
1745
+ if (this.structures)
1746
+ this.structures = [];
1747
+ if (this.sharedValues)
1748
+ this.sharedValues = undefined;
1749
+ }
1750
+ updateSharedData() {
1751
+ let lastVersion = this.sharedVersion || 0;
1752
+ this.sharedVersion = lastVersion + 1;
1753
+ let saveResults = this.saveShared(new SharedData(this.structures, this.sharedValues, this.sharedVersion),
1754
+ existingShared => (existingShared && existingShared.version || 0) == lastVersion);
1755
+ if (saveResults === false) {
1756
+ // get updated structures and try again if the update failed
1757
+ let sharedData = this.getShared() || {};
1758
+ this.structures = sharedData.structures || [];
1759
+ this.sharedValues = sharedData.packedValues;
1760
+ this.sharedVersion = sharedData.version;
1761
+ }
1762
+ return saveResults
1763
+ }
1764
+ }
1765
+ class SharedData {
1766
+ constructor(structures, values, version) {
1767
+ this.structures = structures;
1768
+ this.packedValues = values;
1769
+ this.version = version;
1770
+ }
1532
1771
  }
1533
1772
 
1534
1773
  function writeArrayHeader(length) {
@@ -1598,11 +1837,11 @@
1598
1837
  }
1599
1838
  }
1600
1839
 
1601
- extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
1840
+ extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
1602
1841
  Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
1603
1842
  typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
1604
1843
  typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
1605
- Float32Array, Float64Array];
1844
+ Float32Array, Float64Array, SharedData];
1606
1845
 
1607
1846
  //Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
1608
1847
  extensions = [{
@@ -1637,6 +1876,13 @@
1637
1876
  encode(regex, encode) {
1638
1877
  encode([ 'RegExp', regex.source, regex.flags ]);
1639
1878
  }
1879
+ }, {
1880
+ getTag(tag) {
1881
+ return tag.tag
1882
+ },
1883
+ encode(tag, encode) {
1884
+ encode(tag.value);
1885
+ }
1640
1886
  }, {
1641
1887
  encode(arrayBuffer, encode, makeRoom) {
1642
1888
  writeBuffer(arrayBuffer, makeRoom);
@@ -1655,7 +1901,35 @@
1655
1901
  typedArrayEncoder(78),
1656
1902
  typedArrayEncoder(79),
1657
1903
  typedArrayEncoder(81),
1658
- typedArrayEncoder(82)];
1904
+ typedArrayEncoder(82),
1905
+ {
1906
+ encode(sharedData, encode) { // write SharedData
1907
+ let packedValues = sharedData.packedValues || [];
1908
+ let sharedStructures = sharedData.structures || [];
1909
+ if (packedValues.values.length > 0) {
1910
+ target[position$1++] = 0xd8; // one-byte tag
1911
+ target[position$1++] = 51; // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
1912
+ writeArrayHeader(4);
1913
+ let valuesArray = packedValues.values;
1914
+ encode(valuesArray);
1915
+ writeArrayHeader(0); // prefixes
1916
+ writeArrayHeader(0); // suffixes
1917
+ packedObjectMap = Object.create(sharedPackedObjectMap || null);
1918
+ for (let i = 0, l = valuesArray.length; i < l; i++) {
1919
+ packedObjectMap[valuesArray[i]] = i;
1920
+ }
1921
+ }
1922
+ if (sharedStructures) {
1923
+ targetView.setUint32(position$1, 0xd9dffe00);
1924
+ position$1 += 3;
1925
+ let definitions = sharedStructures.slice(0);
1926
+ definitions.unshift(0xe000);
1927
+ definitions.push(new Tag(sharedData.version, 0x53687264));
1928
+ encode(definitions);
1929
+ } else
1930
+ encode(new Tag(sharedData.version, 0x53687264));
1931
+ }
1932
+ }];
1659
1933
 
1660
1934
  function typedArrayEncoder(tag) {
1661
1935
  return {
@@ -1695,27 +1969,35 @@
1695
1969
  function insertIds(serialized, idsToInsert) {
1696
1970
  // insert the ids that need to be referenced for structured clones
1697
1971
  let nextId;
1698
- let distanceToMove = idsToInsert.length * 8;
1972
+ let distanceToMove = idsToInsert.length * 2;
1699
1973
  let lastEnd = serialized.length - distanceToMove;
1700
1974
  idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1);
1975
+ for (let id = 0; id < idsToInsert.length; id++) {
1976
+ let referee = idsToInsert[id];
1977
+ referee.id = id;
1978
+ for (let position of referee.references) {
1979
+ serialized[position++] = id >> 8;
1980
+ serialized[position] = id & 0xff;
1981
+ }
1982
+ }
1701
1983
  while (nextId = idsToInsert.pop()) {
1702
1984
  let offset = nextId.offset;
1703
- let id = nextId.id;
1704
1985
  serialized.copyWithin(offset + distanceToMove, offset, lastEnd);
1705
- distanceToMove -= 8;
1986
+ distanceToMove -= 2;
1706
1987
  let position = offset + distanceToMove;
1707
- serialized[position++] = 0xd9;
1708
- serialized[position++] = 40009 >> 8;
1709
- serialized[position++] = 40009 & 0xff;
1710
- serialized[position++] = 0x1a; // uint32
1711
- serialized[position++] = id >> 24;
1712
- serialized[position++] = (id >> 16) & 0xff;
1713
- serialized[position++] = (id >> 8) & 0xff;
1714
- serialized[position++] = id & 0xff;
1988
+ serialized[position++] = 0xd8;
1989
+ serialized[position++] = 28; // http://cbor.schmorp.de/value-sharing
1715
1990
  lastEnd = offset;
1716
1991
  }
1717
1992
  return serialized
1718
1993
  }
1994
+ function writeBundles(start, encode) {
1995
+ targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start + 1); // the offset to bundle
1996
+ let writeStrings = bundledStrings$1;
1997
+ bundledStrings$1 = null;
1998
+ encode(writeStrings[0]);
1999
+ encode(writeStrings[1]);
2000
+ }
1719
2001
 
1720
2002
  function addExtension$1(extension) {
1721
2003
  if (extension.Class) {
@@ -1729,7 +2011,8 @@
1729
2011
  let defaultEncoder = new Encoder({ useRecords: false });
1730
2012
  const encode = defaultEncoder.encode;
1731
2013
  const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
1732
- const REUSE_BUFFER_MODE = 1000;
2014
+ const REUSE_BUFFER_MODE = 512;
2015
+ const RESET_BUFFER_MODE = 1024;
1733
2016
 
1734
2017
  /**
1735
2018
  * Given an Iterable first argument, returns an Iterable where each value is encoded as a Buffer
@@ -1814,9 +2097,6 @@
1814
2097
  }
1815
2098
  }
1816
2099
 
1817
- const useRecords = false;
1818
- const mapsAsObjects = true;
1819
-
1820
2100
  exports.ALWAYS = ALWAYS;
1821
2101
  exports.DECIMAL_FIT = DECIMAL_FIT;
1822
2102
  exports.DECIMAL_ROUND = DECIMAL_ROUND;
@@ -1834,9 +2114,7 @@
1834
2114
  exports.encode = encode;
1835
2115
  exports.encodeIter = encodeIter;
1836
2116
  exports.isNativeAccelerationEnabled = isNativeAccelerationEnabled;
1837
- exports.mapsAsObjects = mapsAsObjects;
1838
2117
  exports.roundFloat32 = roundFloat32;
1839
- exports.useRecords = useRecords;
1840
2118
 
1841
2119
  Object.defineProperty(exports, '__esModule', { value: true });
1842
2120