cbor-x 0.9.4 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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;
@@ -185,7 +250,11 @@
185
250
  position += 8;
186
251
  return value
187
252
  }
188
- if (currentDecoder.int64AsNumber) {
253
+ if (majorType > 1) {
254
+ if (dataView.getUint32(position) > 0)
255
+ throw new Error('JavaScript does not support arrays, maps, or strings with length over 4294967295')
256
+ token = dataView.getUint32(position + 4);
257
+ } else if (currentDecoder.int64AsNumber) {
189
258
  token = dataView.getUint32(position) * 0x100000000;
190
259
  token += dataView.getUint32(position + 4);
191
260
  } else
@@ -197,6 +266,7 @@
197
266
  switch(majorType) {
198
267
  case 2: // byte string
199
268
  case 3: // text string
269
+ throw new Error('Indefinite length not supported for byte or text strings')
200
270
  case 4: // array
201
271
  let array = [];
202
272
  let value, i = 0;
@@ -208,8 +278,8 @@
208
278
  let key;
209
279
  if (currentDecoder.mapsAsObjects) {
210
280
  let object = {};
211
- while ((key = readKey()) != STOP_CODE)
212
- object[key] = read();
281
+ if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[currentDecoder.decodeKey(key)] = read();
282
+ else while ((key = read()) != STOP_CODE) object[key] = read();
213
283
  return object
214
284
  } else {
215
285
  if (restoreMapsAsObject) {
@@ -217,8 +287,8 @@
217
287
  restoreMapsAsObject = false;
218
288
  }
219
289
  let map = new Map();
220
- while ((key = read()) != STOP_CODE)
221
- map.set(key, read());
290
+ if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) map.set(currentDecoder.decodeKey(key), read());
291
+ else while ((key = read()) != STOP_CODE) map.set(key, read());
222
292
  return map
223
293
  }
224
294
  case 7:
@@ -250,16 +320,15 @@
250
320
  return readFixedString(token)
251
321
  case 4: // array
252
322
  let array = new Array(token);
253
- for (let i = 0; i < token; i++) {
254
- array[i] = read();
255
- }
323
+ //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
324
+ //else
325
+ for (let i = 0; i < token; i++) array[i] = read();
256
326
  return array
257
327
  case 5: // map
258
328
  if (currentDecoder.mapsAsObjects) {
259
329
  let object = {};
260
- for (let i = 0; i < token; i++) {
261
- object[readKey()] = read();
262
- }
330
+ if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[currentDecoder.decodeKey(read())] = read();
331
+ else for (let i = 0; i < token; i++) object[read()] = read();
263
332
  return object
264
333
  } else {
265
334
  if (restoreMapsAsObject) {
@@ -267,63 +336,66 @@
267
336
  restoreMapsAsObject = false;
268
337
  }
269
338
  let map = new Map();
270
- for (let i = 0; i < token; i++) {
271
- map.set(read(), read());
272
- }
339
+ if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
340
+ else for (let i = 0; i < token; i++) map.set(read(), read());
273
341
  return map
274
342
  }
275
343
  case 6: // extension
276
- if ((token >> 8) == RECORD_TAG_ID) { // record structures
277
- let structure = currentStructures[token & 0xff];
344
+ if (token >= BUNDLED_STRINGS_ID) {
345
+ let structure = currentStructures[token & 0x1fff]; // check record structures first
346
+ // 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
347
  if (structure) {
279
- if (!structure.read)
280
- structure.read = createStructureReader(structure);
348
+ if (!structure.read) structure.read = createStructureReader(structure);
281
349
  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
350
+ }
351
+ if (token < 0x10000) {
352
+ 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)
353
+ return recordDefinition(read())
354
+ else if (token == RECORD_DEFINITIONS_ID) {
355
+ let length = readJustLength();
356
+ let id = read();
357
+ for (let i = 2; i < length; i++) {
358
+ recordDefinition([id++, read()]);
359
+ }
360
+ return read()
361
+ } else if (token == BUNDLED_STRINGS_ID) {
362
+ return readBundleExt()
363
+ }
364
+ if (currentDecoder.getShared) {
365
+ loadShared();
366
+ structure = currentStructures[token & 0x1fff];
367
+ if (structure) {
368
+ if (!structure.read)
369
+ structure.read = createStructureReader(structure);
370
+ return structure.read()
371
+ }
314
372
  }
315
- return new Tag(input)
316
373
  }
317
374
  }
375
+ let extension = currentExtensions[token];
376
+ if (extension) {
377
+ if (extension.handlesRead)
378
+ return extension(read)
379
+ else
380
+ return extension(read())
381
+ } else {
382
+ let input = read();
383
+ for (let i = 0; i < currentExtensionRanges.length; i++) {
384
+ let value = currentExtensionRanges[i](token, input);
385
+ if (value !== undefined)
386
+ return value
387
+ }
388
+ return new Tag(input, token)
389
+ }
318
390
  case 7: // fixed value
319
391
  switch (token) {
320
392
  case 0x14: return false
321
393
  case 0x15: return true
322
394
  case 0x16: return null
323
395
  case 0x17: return; // undefined
324
- case 0x1f:
396
+ case 0x1f:
325
397
  default:
326
- let packedValue = packedValues[token];
398
+ let packedValue = (packedValues || getPackedValues())[token];
327
399
  if (packedValue !== undefined)
328
400
  return packedValue
329
401
  throw new Error('Unknown token ' + token)
@@ -339,41 +411,56 @@
339
411
  }
340
412
  const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
341
413
  function createStructureReader(structure) {
342
- let l = structure.length;
343
414
  function readObject() {
415
+ // get the array size from the header
416
+ let length = src[position++];
417
+ //let majorType = token >> 5
418
+ length = length & 0x1f;
419
+ if (length > 0x17) {
420
+ switch (length) {
421
+ case 0x18:
422
+ length = src[position++];
423
+ break
424
+ case 0x19:
425
+ length = dataView.getUint16(position);
426
+ position += 2;
427
+ break
428
+ case 0x1a:
429
+ length = dataView.getUint32(position);
430
+ position += 4;
431
+ break
432
+ default:
433
+ throw new Error('Expected array header, but got ' + src[position - 1])
434
+ }
435
+ }
344
436
  // 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()
437
+ let compiledReader = this.compiledReader; // first look to see if we have the fast compiled function
438
+ while(compiledReader) {
439
+ // we have a fast compiled object literal reader
440
+ if (compiledReader.propertyCount === length)
441
+ return compiledReader(read) // with the right length, so we use it
442
+ compiledReader = compiledReader.next; // see if there is another reader with the right length
348
443
  }
349
- readArrayHeader();
350
- let object = {};
351
- for (let i = 0; i < l; i++) {
352
- let key = structure[i];
353
- object[key] = read();
444
+ if (this.slowReads++ >= 3) { // create a fast compiled reader
445
+ let array = this.length == length ? this : this.slice(0, length);
446
+ compiledReader = currentDecoder.keyMap
447
+ ? new Function('r', 'return {' + array.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
448
+ : new Function('r', 'return {' + array.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}');
449
+ if (this.compiledReader)
450
+ compiledReader.next = this.compiledReader; // if there is an existing one, we store multiple readers as a linked list because it is usually pretty rare to have multiple readers (of different length) for the same structure
451
+ compiledReader.propertyCount = length;
452
+ this.compiledReader = compiledReader;
453
+ return compiledReader(read)
354
454
  }
455
+ let object = {};
456
+ if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read();
457
+ else for (let i = 0; i < length; i++) object[this[i]] = read();
355
458
  return object
356
459
  }
357
- readObject.count = 0;
460
+ structure.slowReads = 0;
358
461
  return readObject
359
462
  }
360
463
 
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
464
  let readFixedString = readStringJS;
378
465
 
379
466
  let isNativeAccelerationEnabled = false;
@@ -605,76 +692,15 @@
605
692
  }
606
693
 
607
694
  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
695
 
671
696
  class Tag {
672
- constructor(value) {
697
+ constructor(value, tag) {
673
698
  this.value = value;
699
+ this.tag = tag;
674
700
  }
675
701
  }
676
702
 
677
- let glbl = typeof window == 'object' ? window : global;
703
+ let glbl = typeof self == 'object' ? self : global;
678
704
 
679
705
  currentExtensions[0] = (dateString) => {
680
706
  // string date extension
@@ -688,38 +714,67 @@
688
714
 
689
715
  currentExtensions[2] = (buffer) => {
690
716
  // bigint extension
691
- return new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0)
717
+ let value = BigInt(0);
718
+ for (let i = 0, l = buffer.byteLength; i < l; i++) {
719
+ value = BigInt(buffer[i]) + value << BigInt(8);
720
+ }
721
+ return value
692
722
  };
693
723
 
694
724
  currentExtensions[3] = (buffer) => {
695
725
  // negative bigint extension
696
- return BigInt(-1) - (new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0))
726
+ return BigInt(-1) - currentExtensions[2](buffer)
727
+ };
728
+ currentExtensions[4] = (fraction) => {
729
+ // best to reparse to maintain accuracy
730
+ return +(fraction[1] + 'e' + fraction[0])
697
731
  };
698
732
 
699
- // 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;
733
+ currentExtensions[5] = (fraction) => {
734
+ // probably not sufficiently accurate
735
+ return fraction[1] * Math.exp(fraction[0] * Math.log(2))
736
+ };
737
+
738
+ // the registration of the record definition extension
739
+ const recordDefinition = (definition) => {
740
+ let id = definition[0] - 0xe000;
741
+ let structure = definition[1];
742
+ let existingStructure = currentStructures[id];
743
+ if (existingStructure && existingStructure.isShared) {
744
+ (currentStructures.restoreStructures || (currentStructures.restoreStructures = []))[id] = existingStructure;
745
+ }
746
+ currentStructures[id] = structure;
747
+
705
748
  structure.read = createStructureReader(structure);
706
749
  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
- }
750
+ if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
751
+ let key = currentDecoder.decodeKey(structure[i - 2]);
752
+ object[key] = definition[i];
753
+ }
754
+ else for (let i = 2,l = definition.length; i < l; i++) {
755
+ let key = structure[i - 2];
756
+ object[key] = definition[i];
757
+ }
711
758
  return object
712
759
  };
713
-
714
- recordDefinition.handlesRead = true;
715
- currentExtensions[RECORD_TAG_ID] = recordDefinition;
760
+ currentExtensions[LEGACY_RECORD_INLINE_ID] = recordDefinition;
761
+ currentExtensions[14] = (value) => {
762
+ if (bundledStrings)
763
+ return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
764
+ return new Tag(value, 14)
765
+ };
766
+ currentExtensions[15] = (value) => {
767
+ if (bundledStrings)
768
+ return bundledStrings[1].slice(bundledStrings.position1, bundledStrings.position1 += value)
769
+ return new Tag(value, 15)
770
+ };
716
771
 
717
772
  currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
718
773
  return (glbl[data[0]] || Error)(data[1], data[2])
719
774
  };
720
775
  const packedTable = (read) => {
721
776
  if (src[position++] != 0x84)
722
- throw new Error('Packed values structure must be followed by 4 element array')
777
+ throw new Error('Packed values structure must be followed by a 4 element array')
723
778
  let newPackedValues = read(); // packed values
724
779
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
725
780
  packedValues.prefixes = read();
@@ -730,15 +785,36 @@
730
785
  currentExtensions[51] = packedTable;
731
786
 
732
787
  currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
788
+ if (!packedValues) {
789
+ if (currentDecoder.getShared)
790
+ loadShared();
791
+ else
792
+ return new Tag(data, PACKED_REFERENCE_TAG_ID)
793
+ }
733
794
  if (typeof data == 'number')
734
795
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
735
796
  throw new Error('No support for non-integer packed references yet')
736
797
  };
798
+ currentExtensions[25] = (id) => {
799
+ return stringRefs[id]
800
+ };
801
+ currentExtensions[256] = (read) => {
802
+ stringRefs = [];
803
+ try {
804
+ return read()
805
+ } finally {
806
+ stringRefs = null;
807
+ }
808
+ };
809
+ currentExtensions[256].handlesRead = true;
737
810
 
738
- currentExtensions[40009] = (id) => {
739
- // id extension (for structured clones)
740
- if (!referenceMap)
811
+ currentExtensions[28] = (read) => {
812
+ // shareable http://cbor.schmorp.de/value-sharing (for structured clones)
813
+ if (!referenceMap) {
741
814
  referenceMap = new Map();
815
+ referenceMap.id = 0;
816
+ }
817
+ let id = referenceMap.id++;
742
818
  let token = src[position];
743
819
  let target;
744
820
  // TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
@@ -756,9 +832,10 @@
756
832
  refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
757
833
  return targetProperties // no cycle, can just use the returned read object
758
834
  };
835
+ currentExtensions[28].handlesRead = true;
759
836
 
760
- currentExtensions[40010] = (id) => {
761
- // pointer extension (for structured clones)
837
+ currentExtensions[29] = (id) => {
838
+ // sharedref http://cbor.schmorp.de/value-sharing (for structured clones)
762
839
  let refEntry = referenceMap.get(id);
763
840
  refEntry.used = true;
764
841
  return refEntry.target
@@ -781,33 +858,126 @@
781
858
  return a.concat(b)
782
859
  return Object.assign({}, a, b)
783
860
  }
861
+ function getPackedValues() {
862
+ if (!packedValues) {
863
+ if (currentDecoder.getShared)
864
+ loadShared();
865
+ else
866
+ throw new Error('No packed values available')
867
+ }
868
+ return packedValues
869
+ }
870
+ const SHARED_DATA_TAG_ID = 0x53687264; // ascii 'Shrd'
784
871
  currentExtensionRanges.push((tag, input) => {
785
872
  if (tag >= 225 && tag <= 255)
786
- return combine(packedTable.prefixes[tag - 224], input)
873
+ return combine(getPackedValues().prefixes[tag - 224], input)
787
874
  if (tag >= 28704 && tag <= 32767)
788
- return combine(packedTable.prefixes[tag - 28672], input)
875
+ return combine(getPackedValues().prefixes[tag - 28672], input)
789
876
  if (tag >= 1879052288 && tag <= 2147483647)
790
- return combine(packedTable.prefixes[tag - 1879048192], input)
877
+ return combine(getPackedValues().prefixes[tag - 1879048192], input)
791
878
  if (tag >= 216 && tag <= 223)
792
- return combine(input, packedTable.suffixes[tag - 216])
879
+ return combine(input, getPackedValues().suffixes[tag - 216])
793
880
  if (tag >= 27647 && tag <= 28671)
794
- return combine(input, packedTable.suffixes[tag - 27639])
881
+ return combine(input, getPackedValues().suffixes[tag - 27639])
795
882
  if (tag >= 1811940352 && tag <= 1879048191)
796
- return combine(input, packedTable.suffixes[tag - 1811939328])
883
+ return combine(input, getPackedValues().suffixes[tag - 1811939328])
884
+ 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)
885
+ return {
886
+ packedValues: packedValues,
887
+ structures: currentStructures.slice(0),
888
+ version: input,
889
+ }
890
+ }
891
+ if (tag == 55799) // self-descriptive CBOR tag, just return input value
892
+ return input
797
893
  });
798
894
 
799
- const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array');
800
- const typedArrayTags = [64, 68, 69, 70, 71, 72, 77, 78, 79, 81, 82];
895
+ const isLittleEndianMachine = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1;
896
+ const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'];
897
+ const typedArrayTags = [64, 68, 69, 70, 71, 72, 77, 78, 79, 85, 86];
801
898
  for (let i = 0; i < typedArrays.length; i++) {
802
899
  registerTypedArray(typedArrays[i], typedArrayTags[i]);
803
900
  }
804
- function registerTypedArray(typedArrayName, tag) {
805
- currentExtensions[tag] = (buffer) => {
806
- if (!typedArrayName)
807
- throw new Error('Could not find typed array for code ' + typeCode)
808
- // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
809
- return new glbl[typedArrayName](Uint8Array.prototype.slice.call(buffer, 0).buffer)
810
- };
901
+ function registerTypedArray(typedArrayId, tag, littleEndian) {
902
+ let TypedArray = glbl[typedArrayId + 'Array'];
903
+ let bytesPerElement = TypedArray.BYTES_PER_ELEMENT;
904
+ for (let littleEndian = 0; littleEndian < 2; littleEndian++) {
905
+ if (!littleEndian && bytesPerElement == 1)
906
+ continue
907
+ let sizeShift = bytesPerElement == 2 ? 1 : bytesPerElement == 4 ? 2 : 3;
908
+ currentExtensions[littleEndian ? tag : (tag - 4)] = (bytesPerElement == 1 || littleEndian == isLittleEndianMachine) ? (buffer) => {
909
+ if (!TypedArray)
910
+ throw new Error('Could not find typed array for code ' + tag)
911
+ // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
912
+ return new TypedArray(Uint8Array.prototype.slice.call(buffer, 0).buffer)
913
+ } : buffer => {
914
+ if (!TypedArray)
915
+ throw new Error('Could not find typed array for code ' + tag)
916
+ let dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
917
+ let elements = buffer.length >> sizeShift;
918
+ let ta = new TypedArray(elements);
919
+ let method = dv['get' + typedArrayId];
920
+ for (let i = 0; i < elements; i++) {
921
+ ta[i] = method.call(dv, i << sizeShift, littleEndian);
922
+ }
923
+ return ta
924
+ };
925
+ }
926
+ }
927
+
928
+ function readBundleExt() {
929
+ let length = readJustLength();
930
+ let bundlePosition = position + read();
931
+ for (let i = 2; i < length; i++) {
932
+ // skip past bundles that were already read
933
+ let bundleLength = readJustLength(); // this will increment position, so must add to position afterwards
934
+ position += bundleLength;
935
+ }
936
+ let dataPosition = position;
937
+ position = bundlePosition;
938
+ bundledStrings = [readStringJS(readJustLength()), readStringJS(readJustLength())];
939
+ bundledStrings.position0 = 0;
940
+ bundledStrings.position1 = 0;
941
+ bundledStrings.postBundlePosition = position;
942
+ position = dataPosition;
943
+ return read()
944
+ }
945
+
946
+ function readJustLength() {
947
+ let token = src[position++] & 0x1f;
948
+ if (token > 0x17) {
949
+ switch (token) {
950
+ case 0x18:
951
+ token = src[position++];
952
+ break
953
+ case 0x19:
954
+ token = dataView.getUint16(position);
955
+ position += 2;
956
+ break
957
+ case 0x1a:
958
+ token = dataView.getUint32(position);
959
+ position += 4;
960
+ break
961
+ }
962
+ }
963
+ return token
964
+ }
965
+
966
+ function loadShared() {
967
+ if (currentDecoder.getShared) {
968
+ let sharedData = saveState(() => {
969
+ // save the state in case getShared modifies our buffer
970
+ src = null;
971
+ return currentDecoder.getShared()
972
+ }) || {};
973
+ let updatedStructures = sharedData.structures || [];
974
+ currentDecoder.sharedVersion = sharedData.version;
975
+ packedValues = currentDecoder.sharedValues = sharedData.packedValues;
976
+ if (currentStructures === true)
977
+ currentDecoder.structures = currentStructures = updatedStructures;
978
+ else
979
+ currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
980
+ }
811
981
  }
812
982
 
813
983
  function saveState(callback) {
@@ -817,6 +987,8 @@
817
987
  let savedSrcStringEnd = srcStringEnd;
818
988
  let savedSrcString = srcString;
819
989
  let savedReferenceMap = referenceMap;
990
+ let savedBundledStrings = bundledStrings;
991
+
820
992
  // TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
821
993
  let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
822
994
  let savedStructures = currentStructures;
@@ -829,6 +1001,7 @@
829
1001
  srcStringEnd = savedSrcStringEnd;
830
1002
  srcString = savedSrcString;
831
1003
  referenceMap = savedReferenceMap;
1004
+ bundledStrings = savedBundledStrings;
832
1005
  src = savedSrc;
833
1006
  sequentialMode = savedSequentialMode;
834
1007
  currentStructures = savedStructures;
@@ -875,13 +1048,15 @@
875
1048
  const hasNodeBuffer = typeof Buffer !== 'undefined';
876
1049
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
877
1050
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
878
- const RECORD_STARTING_ID_PREFIX = 0x69; // tag 105/0x69
879
1051
  const MAX_STRUCTURES = 0x100;
880
1052
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
881
1053
  let target;
882
1054
  let targetView;
883
1055
  let position$1 = 0;
884
1056
  let safeEnd;
1057
+ let bundledStrings$1 = null;
1058
+ const MAX_BUNDLE_SIZE = 0xf000;
1059
+ const hasNonLatin = /[\u0080-\uFFFF]/;
885
1060
  const RECORD_SYMBOL = Symbol('record-id');
886
1061
  class Encoder extends Decoder {
887
1062
  constructor(options) {
@@ -893,7 +1068,6 @@
893
1068
  let structures;
894
1069
  let referenceMap;
895
1070
  options = options || {};
896
- let lastSharedStructuresLength = 0;
897
1071
  let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
898
1072
  return target.utf8Write(string, position, maxBytes)
899
1073
  } : (textEncoder && textEncoder.encodeInto) ?
@@ -902,12 +1076,20 @@
902
1076
  } : false;
903
1077
 
904
1078
  let encoder = this;
905
- let maxSharedStructures = 64;
1079
+ let hasSharedStructures = options.structures || options.saveStructures;
1080
+ let maxSharedStructures = options.maxSharedStructures;
1081
+ if (maxSharedStructures == null)
1082
+ maxSharedStructures = hasSharedStructures ? 128 : 0;
1083
+ if (maxSharedStructures > 8190)
1084
+ throw new Error('Maximum maxSharedStructure is 8190')
906
1085
  let isSequential = options.sequential;
907
1086
  if (isSequential) {
908
1087
  maxSharedStructures = 0;
909
- this.structures = [];
910
1088
  }
1089
+ if (!this.structures)
1090
+ this.structures = [];
1091
+ if (this.saveStructures)
1092
+ this.saveShared = this.saveStructures;
911
1093
  let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues;
912
1094
  let sharedPackedObjectMap;
913
1095
  if (sharedValues) {
@@ -915,13 +1097,29 @@
915
1097
  for (let i = 0, l = sharedValues.length; i < l; i++) {
916
1098
  sharedPackedObjectMap[sharedValues[i]] = i;
917
1099
  }
918
-
919
1100
  }
920
1101
  let recordIdsToRemove = [];
921
1102
  let transitionsCount = 0;
922
1103
  let serializationsSinceTransitionRebuild = 0;
923
-
924
- 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) {
925
1123
  if (!target) {
926
1124
  target = new ByteArrayAllocate(8192);
927
1125
  targetView = new DataView(target.buffer, 0, 8192);
@@ -937,11 +1135,30 @@
937
1135
  } else if (encodeOptions === REUSE_BUFFER_MODE)
938
1136
  position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
939
1137
  start = position$1;
1138
+ if (encoder.useSelfDescribedHeader) {
1139
+ targetView.setUint32(position$1, 0xd9d9f700); // tag two byte, then self-descriptive tag
1140
+ position$1 += 3;
1141
+ }
940
1142
  referenceMap = encoder.structuredClone ? new Map() : null;
1143
+ if (encoder.bundleStrings && typeof value !== 'string') {
1144
+ bundledStrings$1 = [];
1145
+ bundledStrings$1.size = Infinity; // force a new bundle start on first string
1146
+ } else
1147
+ bundledStrings$1 = null;
1148
+
941
1149
  sharedStructures = encoder.structures;
942
1150
  if (sharedStructures) {
943
- if (sharedStructures.uninitialized)
944
- encoder.structures = sharedStructures = encoder.getStructures();
1151
+ if (sharedStructures.uninitialized) {
1152
+ let sharedData = encoder.getShared() || {};
1153
+ encoder.structures = sharedStructures = sharedData.structures || [];
1154
+ encoder.sharedVersion = sharedData.version;
1155
+ let sharedValues = encoder.sharedValues = sharedData.packedValues;
1156
+ if (sharedValues) {
1157
+ sharedPackedObjectMap = {};
1158
+ for (let i = 0, l = sharedValues.length; i < l; i++)
1159
+ sharedPackedObjectMap[sharedValues[i]] = i;
1160
+ }
1161
+ }
945
1162
  let sharedStructuresLength = sharedStructures.length;
946
1163
  if (sharedStructuresLength > maxSharedStructures && !isSequential)
947
1164
  sharedStructuresLength = maxSharedStructures;
@@ -950,10 +1167,13 @@
950
1167
  sharedStructures.transitions = Object.create(null);
951
1168
  for (let i = 0; i < sharedStructuresLength; i++) {
952
1169
  let keys = sharedStructures[i];
1170
+ //console.log('shared struct keys:', keys)
953
1171
  if (!keys)
954
1172
  continue
955
1173
  let nextTransition, transition = sharedStructures.transitions;
956
1174
  for (let j = 0, l = keys.length; j < l; j++) {
1175
+ if (transition[RECORD_SYMBOL] === undefined)
1176
+ transition[RECORD_SYMBOL] = i;
957
1177
  let key = keys[j];
958
1178
  nextTransition = transition[key];
959
1179
  if (!nextTransition) {
@@ -961,9 +1181,8 @@
961
1181
  }
962
1182
  transition = nextTransition;
963
1183
  }
964
- transition[RECORD_SYMBOL] = i;
1184
+ transition[RECORD_SYMBOL] = i | 0x100000;
965
1185
  }
966
- lastSharedStructuresLength = sharedStructures.length;
967
1186
  }
968
1187
  if (!isSequential)
969
1188
  sharedStructures.nextId = sharedStructuresLength;
@@ -996,9 +1215,12 @@
996
1215
  }
997
1216
  try {
998
1217
  encode(value);
1218
+ if (bundledStrings$1) {
1219
+ writeBundles(start, encode);
1220
+ }
999
1221
  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
1222
  if (referenceMap && referenceMap.idsToInsert) {
1001
- position$1 += referenceMap.idsToInsert.length * 8;
1223
+ position$1 += referenceMap.idsToInsert.length * 2;
1002
1224
  if (position$1 > safeEnd)
1003
1225
  makeRoom(position$1);
1004
1226
  encoder.offset = position$1;
@@ -1006,16 +1228,18 @@
1006
1228
  referenceMap = null;
1007
1229
  return serialized
1008
1230
  }
1009
- if (encodeOptions === REUSE_BUFFER_MODE) {
1231
+ if (encodeOptions & REUSE_BUFFER_MODE) {
1010
1232
  target.start = start;
1011
1233
  target.end = position$1;
1012
1234
  return target
1013
1235
  }
1014
- return target.subarray(start, position$1) // position can change if we call pack again in saveStructures, so we get the buffer now
1236
+ return target.subarray(start, position$1) // position can change if we call encode again in saveShared, so we get the buffer now
1015
1237
  } finally {
1016
1238
  if (sharedStructures) {
1017
1239
  if (serializationsSinceTransitionRebuild < 10)
1018
1240
  serializationsSinceTransitionRebuild++;
1241
+ if (sharedStructures.length > maxSharedStructures)
1242
+ sharedStructures.length = maxSharedStructures;
1019
1243
  if (transitionsCount > 10000) {
1020
1244
  // force a rebuild occasionally after a lot of transitions so it can get cleaned up
1021
1245
  sharedStructures.transitions = null;
@@ -1028,35 +1252,29 @@
1028
1252
  recordIdsToRemove[i][RECORD_SYMBOL] = undefined;
1029
1253
  }
1030
1254
  recordIdsToRemove = [];
1255
+ //sharedStructures.nextId = maxSharedStructures
1031
1256
  }
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
1257
+ }
1258
+ if (hasSharedUpdate && encoder.saveShared) {
1259
+ if (encoder.structures.length > maxSharedStructures) {
1260
+ encoder.structures = encoder.structures.slice(0, maxSharedStructures);
1050
1261
  }
1262
+ // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1263
+ let returnBuffer = target.subarray(start, position$1);
1264
+ if (encoder.updateSharedData() === false)
1265
+ return encoder.encode(value) // re-encode if it fails
1266
+ return returnBuffer
1051
1267
  }
1268
+ if (encodeOptions & RESET_BUFFER_MODE)
1269
+ position$1 = start;
1052
1270
  }
1053
1271
  };
1054
1272
  this.findCommonStringsToPack = () => {
1055
1273
  samplingPackedValues = new Map();
1056
1274
  if (!sharedPackedObjectMap)
1057
1275
  sharedPackedObjectMap = Object.create(null);
1058
- return ({ threshold }) => {
1059
- threshold = threshold || 4;
1276
+ return (options) => {
1277
+ let threshold = options && options.threshold || 4;
1060
1278
  let position = this.pack ? options.maxPrivatePackedValues || 16 : 0;
1061
1279
  if (!sharedValues)
1062
1280
  sharedValues = this.sharedValues = [];
@@ -1067,6 +1285,7 @@
1067
1285
  hasSharedUpdate = true;
1068
1286
  }
1069
1287
  }
1288
+ while (this.saveShared && this.updateSharedData() === false) {}
1070
1289
  samplingPackedValues = null;
1071
1290
  }
1072
1291
  };
@@ -1115,6 +1334,33 @@
1115
1334
  }
1116
1335
  }
1117
1336
  let strLength = value.length;
1337
+ if (bundledStrings$1 && strLength >= 4 && strLength < 0x400) {
1338
+ if ((bundledStrings$1.size += strLength) > MAX_BUNDLE_SIZE) {
1339
+ let extStart;
1340
+ let maxBytes = (bundledStrings$1[0] ? bundledStrings$1[0].length * 3 + bundledStrings$1[1].length : 0) + 10;
1341
+ if (position$1 + maxBytes > safeEnd)
1342
+ target = makeRoom(position$1 + maxBytes);
1343
+ target[position$1++] = 0xd9; // tag 16-bit
1344
+ target[position$1++] = 0xdf; // tag 0xdff9
1345
+ target[position$1++] = 0xf9;
1346
+ // TODO: If we only have one bundle with any string data, only write one string bundle
1347
+ target[position$1++] = bundledStrings$1.position ? 0x84 : 0x82; // array of 4 or 2 elements depending on if we write bundles
1348
+ target[position$1++] = 0x1a; // 32-bit unsigned int
1349
+ extStart = position$1 - start;
1350
+ position$1 += 4; // reserve for writing bundle reference
1351
+ if (bundledStrings$1.position) {
1352
+ writeBundles(start, encode); // write the last bundles
1353
+ }
1354
+ bundledStrings$1 = ['', '']; // create new ones
1355
+ bundledStrings$1.size = 0;
1356
+ bundledStrings$1.position = extStart;
1357
+ }
1358
+ let twoByte = hasNonLatin.test(value);
1359
+ bundledStrings$1[twoByte ? 0 : 1] += value;
1360
+ target[position$1++] = twoByte ? 0xce : 0xcf;
1361
+ encode(strLength);
1362
+ return
1363
+ }
1118
1364
  let headerSize;
1119
1365
  // first we estimate the header size, so we can write to the correct location
1120
1366
  if (strLength < 0x20) {
@@ -1241,16 +1487,16 @@
1241
1487
  if (referenceMap) {
1242
1488
  let referee = referenceMap.get(value);
1243
1489
  if (referee) {
1244
- if (!referee.id) {
1490
+ target[position$1++] = 0xd8;
1491
+ target[position$1++] = 29; // http://cbor.schmorp.de/value-sharing
1492
+ target[position$1++] = 0x19; // 16-bit uint
1493
+ if (!referee.references) {
1245
1494
  let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = []);
1246
- referee.id = idsToInsert.push(referee);
1495
+ referee.references = [];
1496
+ idsToInsert.push(referee);
1247
1497
  }
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;
1498
+ referee.references.push(position$1 - start);
1499
+ position$1 += 2; // TODO: also support 32-bit
1254
1500
  return
1255
1501
  } else
1256
1502
  referenceMap.set(value, { offset: position$1 - start });
@@ -1290,16 +1536,23 @@
1290
1536
  targetView.setUint32(position$1, length);
1291
1537
  position$1 += 4;
1292
1538
  }
1293
- for (let [ key, entryValue ] of value) {
1294
- encode(key);
1295
- encode(entryValue);
1539
+ if (encoder.keyMap) {
1540
+ for (let [ key, entryValue ] of value) {
1541
+ encode(encoder.encodeKey(key));
1542
+ encode(entryValue);
1543
+ }
1544
+ } else {
1545
+ for (let [ key, entryValue ] of value) {
1546
+ encode(key);
1547
+ encode(entryValue);
1548
+ }
1296
1549
  }
1297
- } else {
1550
+ } else {
1298
1551
  for (let i = 0, l = extensions.length; i < l; i++) {
1299
1552
  let extensionClass = extensionClasses[i];
1300
1553
  if (value instanceof extensionClass) {
1301
1554
  let extension = extensions[i];
1302
- let tag = extension.tag;
1555
+ let tag = extension.tag || extension.getTag && extension.getTag(value);
1303
1556
  if (tag < 0x18) {
1304
1557
  target[position$1++] = 0xc0 | tag;
1305
1558
  } else if (tag < 0x100) {
@@ -1361,6 +1614,7 @@
1361
1614
  const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
1362
1615
  // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
1363
1616
  let keys = Object.keys(object);
1617
+ let vals = Object.values(object);
1364
1618
  let length = keys.length;
1365
1619
  if (length < 0x18) {
1366
1620
  target[position$1++] = 0xa0 | length;
@@ -1376,10 +1630,16 @@
1376
1630
  targetView.setUint32(position$1, length);
1377
1631
  position$1 += 4;
1378
1632
  }
1379
- let key;
1380
- for (let i = 0; i < length; i++) {
1381
- encode(key = keys[i]);
1382
- encode(object[key]);
1633
+ if (encoder.keyMap) {
1634
+ for (let i = 0; i < length; i++) {
1635
+ encode(encodeKey(keys[i]));
1636
+ encode(vals[i]);
1637
+ }
1638
+ } else {
1639
+ for (let i = 0; i < length; i++) {
1640
+ encode(keys[i]);
1641
+ encode(vals[i]);
1642
+ }
1383
1643
  }
1384
1644
  } :
1385
1645
  (object, safePrototype) => {
@@ -1387,95 +1647,92 @@
1387
1647
  let objectOffset = position$1 - start;
1388
1648
  position$1 += 2;
1389
1649
  let size = 0;
1390
- for (let key in object) {
1391
- if (safePrototype || object.hasOwnProperty(key)) {
1392
- encode(key);
1650
+ if (encoder.keyMap) {
1651
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1652
+ encode(encoder.encodeKey(key));
1393
1653
  encode(object[key]);
1394
1654
  size++;
1395
1655
  }
1656
+ } else {
1657
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1658
+ encode(key);
1659
+ encode(object[key]);
1660
+ size++;
1661
+ }
1396
1662
  }
1397
1663
  target[objectOffset++ + start] = size >> 8;
1398
1664
  target[objectOffset + start] = size & 0xff;
1399
1665
  } :
1400
-
1401
- /* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
1402
1666
  (object, safePrototype) => {
1403
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
1404
- let objectOffset = position++ - start
1405
- let wroteKeys
1406
- for (let key in object) {
1407
- if (safePrototype || object.hasOwnProperty(key)) {
1408
- nextTransition = transition[key]
1667
+ let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1668
+ let newTransitions = 0;
1669
+ let length = 0;
1670
+ let parentRecordId;
1671
+ let keys;
1672
+ if (this.keyMap) {
1673
+ keys = Object.keys(object).map(k => this.encodeKey(k));
1674
+ length = keys.length;
1675
+ for (let i = 0; i < length; i++) {
1676
+ let key = keys[i];
1677
+ nextTransition = transition[key];
1409
1678
  if (!nextTransition) {
1410
- nextTransition = transition[key] = Object.create(null)
1411
- nextTransition.__keys__ = (transition.__keys__ || []).concat([key])
1412
- /*let keys = Object.keys(object)
1413
- if
1414
- let size = 0
1415
- let startBranch = transition.__keys__ ? transition.__keys__.length : 0
1416
- for (let i = 0, l = keys.length; i++) {
1417
- let key = keys[i]
1418
- size += key.length << 2
1419
- if (i >= startBranch) {
1420
- nextTransition = nextTransition[key] = Object.create(null)
1421
- nextTransition.__keys__ = keys.slice(0, i + 1)
1422
- }
1679
+ nextTransition = transition[key] = Object.create(null);
1680
+ newTransitions++;
1681
+ }
1682
+ transition = nextTransition;
1683
+ }
1684
+ } else {
1685
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1686
+ nextTransition = transition[key];
1687
+ if (!nextTransition) {
1688
+ if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
1689
+ parentRecordId = transition[RECORD_SYMBOL] & 0xffff;
1423
1690
  }
1424
- makeRoom(position + size)
1425
- nextTransition = transition[key]
1426
- target.copy(target, )
1427
- objectOffset
1691
+ nextTransition = transition[key] = Object.create(null);
1692
+ newTransitions++;
1428
1693
  }
1429
- transition = nextTransition
1430
- encode(object[key])
1694
+ transition = nextTransition;
1695
+ length++;
1431
1696
  }
1432
1697
  }
1433
- let id = transition.id
1434
- if (!id) {
1435
- id = transition.id = structures.push(transition.__keys__) + 63
1436
- if (sharedStructures.onUpdate)
1437
- sharedStructures.onUpdate(id, transition.__keys__)
1438
- }
1439
- target[objectOffset + start] = id
1440
- }*/
1441
- (object) => {
1442
- let keys = Object.keys(object);
1443
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1444
- let newTransitions = 0;
1445
- let length = keys.length;
1446
- for (let i = 0; i < length; i++) {
1447
- let key = keys[i];
1448
- nextTransition = transition[key];
1449
- if (!nextTransition) {
1450
- nextTransition = transition[key] = Object.create(null);
1451
- newTransitions++;
1452
- }
1453
- transition = nextTransition;
1454
- }
1455
1698
  let recordId = transition[RECORD_SYMBOL];
1456
1699
  if (recordId !== undefined) {
1457
- target[position$1++] = 0xd9; // tag two byte
1458
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1459
- target[position$1++] = recordId;
1700
+ recordId &= 0xffff;
1701
+ target[position$1++] = 0xd9;
1702
+ target[position$1++] = (recordId >> 8) | 0xe0;
1703
+ target[position$1++] = recordId & 0xff;
1460
1704
  } else {
1461
- recordId = structures.nextId++;
1462
- if (!recordId) {
1463
- recordId = 0;
1464
- structures.nextId = 1;
1465
- }
1466
- if (recordId >= MAX_STRUCTURES) {// cycle back around
1467
- structures.nextId = (recordId = maxSharedStructures) + 1;
1705
+ if (!keys)
1706
+ keys = transition.__keys__ || (transition.__keys__ = Object.keys(object));
1707
+ if (parentRecordId === undefined) {
1708
+ recordId = structures.nextId++;
1709
+ if (!recordId) {
1710
+ recordId = 0;
1711
+ structures.nextId = 1;
1712
+ }
1713
+ if (recordId >= MAX_STRUCTURES) {// cycle back around
1714
+ structures.nextId = (recordId = maxSharedStructures) + 1;
1715
+ }
1716
+ } else {
1717
+ recordId = parentRecordId;
1468
1718
  }
1469
- transition[RECORD_SYMBOL] = recordId;
1470
1719
  structures[recordId] = keys;
1471
- 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
1720
+ if (recordId < maxSharedStructures) {
1721
+ target[position$1++] = 0xd9;
1722
+ target[position$1++] = (recordId >> 8) | 0xe0;
1723
+ target[position$1++] = recordId & 0xff;
1724
+ transition = structures.transitions;
1725
+ for (let i = 0; i < length; i++) {
1726
+ if (transition[RECORD_SYMBOL] === undefined || (transition[RECORD_SYMBOL] & 0x100000))
1727
+ transition[RECORD_SYMBOL] = recordId;
1728
+ transition = transition[keys[i]];
1729
+ }
1730
+ transition[RECORD_SYMBOL] = recordId | 0x100000; // indicates it is a extendable terminal
1475
1731
  hasSharedUpdate = true;
1476
1732
  } else {
1477
- target[position$1++] = 0xd8;
1478
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1733
+ transition[RECORD_SYMBOL] = recordId;
1734
+ targetView.setUint32(position$1, 0xd9dfff00); // tag two byte, then record definition id
1735
+ position$1 += 3;
1479
1736
  if (newTransitions)
1480
1737
  transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
1481
1738
  // record the removal of the id, we can maintain our shared structure
@@ -1483,13 +1740,9 @@
1483
1740
  recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined; // we are cycling back through, and have to remove old ones
1484
1741
  recordIdsToRemove.push(transition);
1485
1742
  writeArrayHeader(length + 2);
1743
+ encode(0xe000 + recordId);
1486
1744
  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]]);
1745
+ for (let v of Object.values(object)) encode(v);
1493
1746
  return
1494
1747
  }
1495
1748
  }
@@ -1498,8 +1751,9 @@
1498
1751
  } else {
1499
1752
  writeArrayHeader(length);
1500
1753
  }
1501
- for (let i =0; i < length; i++)
1502
- encode(object[keys[i]]);
1754
+ for (let key in object)
1755
+ if (safePrototype || object.hasOwnProperty(key))
1756
+ encode(object[key]);
1503
1757
  };
1504
1758
  const makeRoom = (end) => {
1505
1759
  let newSize;
@@ -1529,6 +1783,40 @@
1529
1783
  targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
1530
1784
  position$1 = 0;
1531
1785
  }
1786
+ clearSharedData() {
1787
+ if (this.structures)
1788
+ this.structures = [];
1789
+ if (this.sharedValues)
1790
+ this.sharedValues = undefined;
1791
+ }
1792
+ updateSharedData() {
1793
+ let lastVersion = this.sharedVersion || 0;
1794
+ this.sharedVersion = lastVersion + 1;
1795
+ let structuresCopy = this.structures.slice(0);
1796
+ let sharedData = new SharedData(structuresCopy, this.sharedValues, this.sharedVersion);
1797
+ let saveResults = this.saveShared(sharedData,
1798
+ existingShared => (existingShared && existingShared.version || 0) == lastVersion);
1799
+ if (saveResults === false) {
1800
+ // get updated structures and try again if the update failed
1801
+ sharedData = this.getShared() || {};
1802
+ this.structures = sharedData.structures || [];
1803
+ this.sharedValues = sharedData.packedValues;
1804
+ this.sharedVersion = sharedData.version;
1805
+ this.structures.nextId = this.structures.length;
1806
+ } else {
1807
+ // restore structures
1808
+ structuresCopy.forEach((structure, i) => this.structures[i] = structure);
1809
+ }
1810
+ // saveShared may fail to write and reload, or may have reloaded to check compatibility and overwrite saved data, either way load the correct shared data
1811
+ return saveResults
1812
+ }
1813
+ }
1814
+ class SharedData {
1815
+ constructor(structures, values, version) {
1816
+ this.structures = structures;
1817
+ this.packedValues = values;
1818
+ this.version = version;
1819
+ }
1532
1820
  }
1533
1821
 
1534
1822
  function writeArrayHeader(length) {
@@ -1597,12 +1885,12 @@
1597
1885
  case 'function': console.log(value);
1598
1886
  }
1599
1887
  }
1600
-
1601
- extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
1888
+ const isLittleEndianMachine$1 = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1;
1889
+ extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
1602
1890
  Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
1603
1891
  typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
1604
1892
  typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
1605
- Float32Array, Float64Array];
1893
+ Float32Array, Float64Array, SharedData];
1606
1894
 
1607
1895
  //Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
1608
1896
  extensions = [{
@@ -1637,6 +1925,13 @@
1637
1925
  encode(regex, encode) {
1638
1926
  encode([ 'RegExp', regex.source, regex.flags ]);
1639
1927
  }
1928
+ }, {
1929
+ getTag(tag) {
1930
+ return tag.tag
1931
+ },
1932
+ encode(tag, encode) {
1933
+ encode(tag.value);
1934
+ }
1640
1935
  }, {
1641
1936
  encode(arrayBuffer, encode, makeRoom) {
1642
1937
  writeBuffer(arrayBuffer, makeRoom);
@@ -1645,19 +1940,48 @@
1645
1940
  encode(arrayBuffer, encode, makeRoom) {
1646
1941
  writeBuffer(arrayBuffer, makeRoom);
1647
1942
  }
1648
- }, typedArrayEncoder(64),
1649
- typedArrayEncoder(68),
1650
- typedArrayEncoder(69),
1651
- typedArrayEncoder(70),
1652
- typedArrayEncoder(71),
1653
- typedArrayEncoder(72),
1654
- typedArrayEncoder(77),
1655
- typedArrayEncoder(78),
1656
- typedArrayEncoder(79),
1657
- typedArrayEncoder(81),
1658
- typedArrayEncoder(82)];
1659
-
1660
- function typedArrayEncoder(tag) {
1943
+ }, typedArrayEncoder(64, 1),
1944
+ typedArrayEncoder(68, 1),
1945
+ typedArrayEncoder(69, 2),
1946
+ typedArrayEncoder(70, 4),
1947
+ typedArrayEncoder(71, 8),
1948
+ typedArrayEncoder(72, 1),
1949
+ typedArrayEncoder(77, 2),
1950
+ typedArrayEncoder(78, 4),
1951
+ typedArrayEncoder(79, 8),
1952
+ typedArrayEncoder(85, 4),
1953
+ typedArrayEncoder(86, 8),
1954
+ {
1955
+ encode(sharedData, encode) { // write SharedData
1956
+ let packedValues = sharedData.packedValues || [];
1957
+ let sharedStructures = sharedData.structures || [];
1958
+ if (packedValues.values.length > 0) {
1959
+ target[position$1++] = 0xd8; // one-byte tag
1960
+ target[position$1++] = 51; // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
1961
+ writeArrayHeader(4);
1962
+ let valuesArray = packedValues.values;
1963
+ encode(valuesArray);
1964
+ writeArrayHeader(0); // prefixes
1965
+ writeArrayHeader(0); // suffixes
1966
+ packedObjectMap = Object.create(sharedPackedObjectMap || null);
1967
+ for (let i = 0, l = valuesArray.length; i < l; i++) {
1968
+ packedObjectMap[valuesArray[i]] = i;
1969
+ }
1970
+ }
1971
+ if (sharedStructures) {
1972
+ targetView.setUint32(position$1, 0xd9dffe00);
1973
+ position$1 += 3;
1974
+ let definitions = sharedStructures.slice(0);
1975
+ definitions.unshift(0xe000);
1976
+ definitions.push(new Tag(sharedData.version, 0x53687264));
1977
+ encode(definitions);
1978
+ } else
1979
+ encode(new Tag(sharedData.version, 0x53687264));
1980
+ }
1981
+ }];
1982
+ function typedArrayEncoder(tag, size) {
1983
+ if (!isLittleEndianMachine$1 && size > 1)
1984
+ tag -= 4; // the big endian equivalents are 4 less
1661
1985
  return {
1662
1986
  tag: tag,
1663
1987
  encode: function writeExtBuffer(typedArray, encode) {
@@ -1695,27 +2019,35 @@
1695
2019
  function insertIds(serialized, idsToInsert) {
1696
2020
  // insert the ids that need to be referenced for structured clones
1697
2021
  let nextId;
1698
- let distanceToMove = idsToInsert.length * 8;
2022
+ let distanceToMove = idsToInsert.length * 2;
1699
2023
  let lastEnd = serialized.length - distanceToMove;
1700
2024
  idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1);
2025
+ for (let id = 0; id < idsToInsert.length; id++) {
2026
+ let referee = idsToInsert[id];
2027
+ referee.id = id;
2028
+ for (let position of referee.references) {
2029
+ serialized[position++] = id >> 8;
2030
+ serialized[position] = id & 0xff;
2031
+ }
2032
+ }
1701
2033
  while (nextId = idsToInsert.pop()) {
1702
2034
  let offset = nextId.offset;
1703
- let id = nextId.id;
1704
2035
  serialized.copyWithin(offset + distanceToMove, offset, lastEnd);
1705
- distanceToMove -= 8;
2036
+ distanceToMove -= 2;
1706
2037
  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;
2038
+ serialized[position++] = 0xd8;
2039
+ serialized[position++] = 28; // http://cbor.schmorp.de/value-sharing
1715
2040
  lastEnd = offset;
1716
2041
  }
1717
2042
  return serialized
1718
2043
  }
2044
+ function writeBundles(start, encode) {
2045
+ targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start + 1); // the offset to bundle
2046
+ let writeStrings = bundledStrings$1;
2047
+ bundledStrings$1 = null;
2048
+ encode(writeStrings[0]);
2049
+ encode(writeStrings[1]);
2050
+ }
1719
2051
 
1720
2052
  function addExtension$1(extension) {
1721
2053
  if (extension.Class) {
@@ -1729,7 +2061,8 @@
1729
2061
  let defaultEncoder = new Encoder({ useRecords: false });
1730
2062
  const encode = defaultEncoder.encode;
1731
2063
  const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
1732
- const REUSE_BUFFER_MODE = 1000;
2064
+ const REUSE_BUFFER_MODE = 512;
2065
+ const RESET_BUFFER_MODE = 1024;
1733
2066
 
1734
2067
  /**
1735
2068
  * Given an Iterable first argument, returns an Iterable where each value is encoded as a Buffer
@@ -1814,9 +2147,6 @@
1814
2147
  }
1815
2148
  }
1816
2149
 
1817
- const useRecords = false;
1818
- const mapsAsObjects = true;
1819
-
1820
2150
  exports.ALWAYS = ALWAYS;
1821
2151
  exports.DECIMAL_FIT = DECIMAL_FIT;
1822
2152
  exports.DECIMAL_ROUND = DECIMAL_ROUND;
@@ -1834,9 +2164,7 @@
1834
2164
  exports.encode = encode;
1835
2165
  exports.encodeIter = encodeIter;
1836
2166
  exports.isNativeAccelerationEnabled = isNativeAccelerationEnabled;
1837
- exports.mapsAsObjects = mapsAsObjects;
1838
2167
  exports.roundFloat32 = roundFloat32;
1839
- exports.useRecords = useRecords;
1840
2168
 
1841
2169
  Object.defineProperty(exports, '__esModule', { value: true });
1842
2170