cbor-x 0.9.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.cjs CHANGED
@@ -13,7 +13,10 @@ let src;
13
13
  let srcEnd;
14
14
  let position = 0;
15
15
  const EMPTY_ARRAY = [];
16
- const RECORD_TAG_ID = 0x69;
16
+ const LEGACY_RECORD_INLINE_ID = 105;
17
+ const RECORD_DEFINITIONS_ID = 0xdffe;
18
+ const RECORD_INLINE_ID = 0xdfff; // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
19
+ const BUNDLED_STRINGS_ID = 0xdff9;
17
20
  const PACKED_REFERENCE_TAG_ID = 6;
18
21
  const STOP_CODE = {};
19
22
  let strings = EMPTY_ARRAY;
@@ -23,6 +26,7 @@ let currentStructures;
23
26
  let srcString;
24
27
  let srcStringStart = 0;
25
28
  let srcStringEnd = 0;
29
+ let bundledStrings;
26
30
  let referenceMap;
27
31
  let currentExtensions = [];
28
32
  let currentExtensionRanges = [];
@@ -38,13 +42,70 @@ let sequentialMode = false;
38
42
  class Decoder {
39
43
  constructor(options) {
40
44
  if (options) {
45
+ if ((options.keyMap || options._keyMap) && !options.useRecords) {
46
+ options.useRecords = false;
47
+ options.mapsAsObjects = true;
48
+ }
41
49
  if (options.useRecords === false && options.mapsAsObjects === undefined)
42
50
  options.mapsAsObjects = true;
43
- if (options.getStructures && !options.structures)
51
+ if (options.getStructures)
52
+ options.getShared = options.getStructures;
53
+ if (options.getShared && !options.structures)
44
54
  (options.structures = []).uninitialized = true; // this is what we use to denote an uninitialized structures
55
+ if (options.keyMap) {
56
+ this.mapKey = new Map();
57
+ for (let [k,v] of Object.entries(options.keyMap)) this.mapKey.set(v,k);
58
+ }
45
59
  }
46
60
  Object.assign(this, options);
47
61
  }
62
+ /*
63
+ decodeKey(key) {
64
+ return this.keyMap
65
+ ? Object.keys(this.keyMap)[Object.values(this.keyMap).indexOf(key)] || key
66
+ : key
67
+ }
68
+ */
69
+ decodeKey(key) {
70
+ return this.keyMap ? this.mapKey.get(key) || key : key
71
+ }
72
+
73
+ encodeKey(key) {
74
+ return this.keyMap && this.keyMap.hasOwnProperty(key) ? this.keyMap[key] : key
75
+ }
76
+
77
+ encodeKeys(rec) {
78
+ if (!this._keyMap) return rec
79
+ let map = new Map();
80
+ for (let [k,v] of Object.entries(rec)) map.set((this._keyMap.hasOwnProperty(k) ? this._keyMap[k] : k), v);
81
+ return map
82
+ }
83
+
84
+ decodeKeys(map) {
85
+ if (!this._keyMap || map.constructor.name != 'Map') return map
86
+ if (!this._mapKey) {
87
+ this._mapKey = new Map();
88
+ for (let [k,v] of Object.entries(this._keyMap)) this._mapKey.set(v,k);
89
+ }
90
+ let res = {};
91
+ //map.forEach((v,k) => res[Object.keys(this._keyMap)[Object.values(this._keyMap).indexOf(k)] || k] = v)
92
+ map.forEach((v,k) => res[this._mapKey.has(k) ? this._mapKey.get(k) : k] = v);
93
+ return res
94
+ }
95
+
96
+ mapDecode(source, end) {
97
+
98
+ let res = this.decode(source);
99
+ if (this._keyMap) {
100
+ //Experiemntal support for Optimised KeyMap decoding
101
+ switch (res.constructor.name) {
102
+ case 'Array': return res.map(r => this.decodeKeys(r))
103
+ //case 'Map': return this.decodeKeys(res)
104
+ }
105
+ }
106
+ return res
107
+ }
108
+
48
109
  decode(source, end) {
49
110
  if (src) {
50
111
  // re-entrant execution, save the state and restore it after we do this decode
@@ -59,12 +120,21 @@ class Decoder {
59
120
  srcStringEnd = 0;
60
121
  srcString = null;
61
122
  strings = EMPTY_ARRAY;
123
+ bundledStrings = null;
62
124
  src = source;
63
125
  // this provides cached access to the data view for a buffer if it is getting reused, which is a recommend
64
126
  // technique for getting data from a database where it can be copied into an existing buffer instead of creating
65
127
  // new ones
66
- dataView = source.dataView || (source.dataView = new DataView(source.buffer, source.byteOffset, source.byteLength));
67
- if (this) {
128
+ try {
129
+ dataView = source.dataView || (source.dataView = new DataView(source.buffer, source.byteOffset, source.byteLength));
130
+ } catch(error) {
131
+ // if it doesn't have a buffer, maybe it is the wrong type of object
132
+ src = null;
133
+ if (source instanceof Uint8Array)
134
+ throw error
135
+ throw new Error('Source must be a Uint8Array or Buffer but was a ' + ((source && typeof source == 'object') ? source.constructor.name : typeof source))
136
+ }
137
+ if (this instanceof Decoder) {
68
138
  currentDecoder = this;
69
139
  packedValues = this.sharedValues &&
70
140
  (this.pack ? new Array(this.maxPrivatePackedValues || 16).concat(this.sharedValues) :
@@ -121,6 +191,9 @@ class Decoder {
121
191
  function checkedRead() {
122
192
  try {
123
193
  let result = read();
194
+ if (bundledStrings) // bundled strings to skip past
195
+ position = bundledStrings.postBundlePosition;
196
+
124
197
  if (position == srcEnd) {
125
198
  // finished reading this source, cleanup references
126
199
  currentStructures = null;
@@ -183,10 +256,15 @@ function read() {
183
256
  position += 8;
184
257
  return value
185
258
  }
186
- if (currentDecoder.uint64AsNumber)
187
- return src[position++] * 0x100000000000000 + src[position++] * 0x1000000000000 + src[position++] * 0x10000000000 + src[position++] * 0x100000000 +
188
- src[position++] * 0x1000000 + (src[position++] << 16) + (src[position++] << 8) + src[position++]
189
- token = dataView.getBigUint64(position);
259
+ if (majorType > 1) {
260
+ if (dataView.getUint32(position) > 0)
261
+ throw new Error('JavaScript does not support arrays, maps, or strings with length over 4294967295')
262
+ token = dataView.getUint32(position + 4);
263
+ } else if (currentDecoder.int64AsNumber) {
264
+ token = dataView.getUint32(position) * 0x100000000;
265
+ token += dataView.getUint32(position + 4);
266
+ } else
267
+ token = dataView.getBigUint64(position);
190
268
  position += 8;
191
269
  break
192
270
  case 0x1f:
@@ -194,6 +272,7 @@ function read() {
194
272
  switch(majorType) {
195
273
  case 2: // byte string
196
274
  case 3: // text string
275
+ throw new Error('Indefinite length not supported for byte or text strings')
197
276
  case 4: // array
198
277
  let array = [];
199
278
  let value, i = 0;
@@ -205,8 +284,8 @@ function read() {
205
284
  let key;
206
285
  if (currentDecoder.mapsAsObjects) {
207
286
  let object = {};
208
- while ((key = readKey()) != STOP_CODE)
209
- object[key] = read();
287
+ if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[currentDecoder.decodeKey(key)] = read();
288
+ else while ((key = read()) != STOP_CODE) object[key] = read();
210
289
  return object
211
290
  } else {
212
291
  if (restoreMapsAsObject) {
@@ -214,8 +293,8 @@ function read() {
214
293
  restoreMapsAsObject = false;
215
294
  }
216
295
  let map = new Map();
217
- while ((key = read()) != STOP_CODE)
218
- map.set(key, read());
296
+ if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) map.set(currentDecoder.decodeKey(key), read());
297
+ else while ((key = read()) != STOP_CODE) map.set(key, read());
219
298
  return map
220
299
  }
221
300
  case 7:
@@ -247,16 +326,15 @@ function read() {
247
326
  return readFixedString(token)
248
327
  case 4: // array
249
328
  let array = new Array(token);
250
- for (let i = 0; i < token; i++) {
251
- array[i] = read();
252
- }
329
+ //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
330
+ //else
331
+ for (let i = 0; i < token; i++) array[i] = read();
253
332
  return array
254
333
  case 5: // map
255
334
  if (currentDecoder.mapsAsObjects) {
256
335
  let object = {};
257
- for (let i = 0; i < token; i++) {
258
- object[readKey()] = read();
259
- }
336
+ if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[currentDecoder.decodeKey(read())] = read();
337
+ else for (let i = 0; i < token; i++) object[read()] = read();
260
338
  return object
261
339
  } else {
262
340
  if (restoreMapsAsObject) {
@@ -264,63 +342,66 @@ function read() {
264
342
  restoreMapsAsObject = false;
265
343
  }
266
344
  let map = new Map();
267
- for (let i = 0; i < token; i++) {
268
- map.set(read(), read());
269
- }
345
+ if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
346
+ else for (let i = 0; i < token; i++) map.set(read(), read());
270
347
  return map
271
348
  }
272
349
  case 6: // extension
273
- if ((token >> 8) == RECORD_TAG_ID) { // record structures
274
- let structure = currentStructures[token & 0xff];
350
+ if (token >= BUNDLED_STRINGS_ID) {
351
+ let structure = currentStructures[token & 0x1fff]; // check record structures first
352
+ // At some point we may provide an option for dynamic tag assignment with a range like token >= 8 && (token < 16 || (token > 0x80 && token < 0xc0) || (token > 0x130 && token < 0x4000))
275
353
  if (structure) {
276
- if (!structure.read)
277
- structure.read = createStructureReader(structure);
354
+ if (!structure.read) structure.read = createStructureReader(structure);
278
355
  return structure.read()
279
- } else if (currentDecoder.getStructures) {
280
- let updatedStructures = saveState(() => {
281
- // save the state in case getStructures modifies our buffer
282
- src = null;
283
- return currentDecoder.getStructures()
284
- });
285
- if (currentStructures === true)
286
- currentDecoder.structures = currentStructures = updatedStructures;
287
- else
288
- currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
289
- structure = currentStructures[token & 0xff];
290
- if (structure) {
291
- if (!structure.read)
292
- structure.read = createStructureReader(structure);
293
- return structure.read()
294
- } else
295
- return token
296
- } else
297
- return token
298
- } else {
299
- let extension = currentExtensions[token];
300
- if (extension) {
301
- if (extension.handlesRead)
302
- return extension(read)
303
- else
304
- return extension(read())
305
- } else {
306
- let input = read();
307
- for (let i = 0; i < currentExtensionRanges.length; i++) {
308
- let value = currentExtensionRanges[i](token, input);
309
- if (value !== undefined)
310
- return value
356
+ }
357
+ if (token < 0x10000) {
358
+ if (token == RECORD_INLINE_ID) // we do a special check for this so that we can keep the currentExtensions as densely stored array (v8 stores arrays densely under about 3000 elements)
359
+ return recordDefinition(read())
360
+ else if (token == RECORD_DEFINITIONS_ID) {
361
+ let length = readJustLength();
362
+ let id = read();
363
+ for (let i = 2; i < length; i++) {
364
+ recordDefinition([id++, read()]);
365
+ }
366
+ return read()
367
+ } else if (token == BUNDLED_STRINGS_ID) {
368
+ return readBundleExt()
369
+ }
370
+ if (currentDecoder.getShared) {
371
+ loadShared();
372
+ structure = currentStructures[token & 0x1fff];
373
+ if (structure) {
374
+ if (!structure.read)
375
+ structure.read = createStructureReader(structure);
376
+ return structure.read()
377
+ }
311
378
  }
312
- return new Tag(input)
313
379
  }
314
380
  }
381
+ let extension = currentExtensions[token];
382
+ if (extension) {
383
+ if (extension.handlesRead)
384
+ return extension(read)
385
+ else
386
+ return extension(read())
387
+ } else {
388
+ let input = read();
389
+ for (let i = 0; i < currentExtensionRanges.length; i++) {
390
+ let value = currentExtensionRanges[i](token, input);
391
+ if (value !== undefined)
392
+ return value
393
+ }
394
+ return new Tag(input, token)
395
+ }
315
396
  case 7: // fixed value
316
397
  switch (token) {
317
398
  case 0x14: return false
318
399
  case 0x15: return true
319
400
  case 0x16: return null
320
401
  case 0x17: return; // undefined
321
- case 0x1f:
402
+ case 0x1f:
322
403
  default:
323
- let packedValue = packedValues[token];
404
+ let packedValue = (packedValues || getPackedValues())[token];
324
405
  if (packedValue !== undefined)
325
406
  return packedValue
326
407
  throw new Error('Unknown token ' + token)
@@ -336,49 +417,68 @@ function read() {
336
417
  }
337
418
  const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
338
419
  function createStructureReader(structure) {
339
- let l = structure.length;
340
420
  function readObject() {
421
+ // get the array size from the header
422
+ let length = src[position++];
423
+ //let majorType = token >> 5
424
+ length = length & 0x1f;
425
+ if (length > 0x17) {
426
+ switch (length) {
427
+ case 0x18:
428
+ length = src[position++];
429
+ break
430
+ case 0x19:
431
+ length = dataView.getUint16(position);
432
+ position += 2;
433
+ break
434
+ case 0x1a:
435
+ length = dataView.getUint32(position);
436
+ position += 4;
437
+ break
438
+ default:
439
+ throw new Error('Expected array header, but got ' + src[position - 1])
440
+ }
441
+ }
341
442
  // This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
342
- if (readObject.count++ > 2) {
343
- 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);
344
- return this.read()
443
+ let compiledReader = this.compiledReader; // first look to see if we have the fast compiled function
444
+ while(compiledReader) {
445
+ // we have a fast compiled object literal reader
446
+ if (compiledReader.propertyCount === length)
447
+ return compiledReader(read) // with the right length, so we use it
448
+ compiledReader = compiledReader.next; // see if there is another reader with the right length
345
449
  }
346
- readArrayHeader();
347
- let object = {};
348
- for (let i = 0; i < l; i++) {
349
- let key = structure[i];
350
- object[key] = read();
450
+ if (this.slowReads++ >= 3) { // create a fast compiled reader
451
+ let array = this.length == length ? this : this.slice(0, length);
452
+ compiledReader = currentDecoder.keyMap
453
+ ? new Function('r', 'return {' + array.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
454
+ : new Function('r', 'return {' + array.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}');
455
+ if (this.compiledReader)
456
+ compiledReader.next = this.compiledReader; // if there is an existing one, we store multiple readers as a linked list because it is usually pretty rare to have multiple readers (of different length) for the same structure
457
+ compiledReader.propertyCount = length;
458
+ this.compiledReader = compiledReader;
459
+ return compiledReader(read)
351
460
  }
461
+ let object = {};
462
+ if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read();
463
+ else for (let i = 0; i < length; i++) object[this[i]] = read();
352
464
  return object
353
465
  }
354
- readObject.count = 0;
466
+ structure.slowReads = 0;
355
467
  return readObject
356
468
  }
357
469
 
358
- function readArrayHeader(expectedLength) {
359
- // consume the array header, TODO: check expected length
360
- let token = src[position++];
361
- //let majorType = token >> 5
362
- token = token & 0x1f;
363
- if (token > 0x17) {
364
- switch (token) {
365
- case 0x18: position++;
366
- break
367
- case 0x19: position += 2;
368
- break
369
- case 0x1a: position += 4;
370
- }
371
- }
372
- }
373
-
374
470
  let readFixedString = readStringJS;
375
471
 
472
+ exports.isNativeAccelerationEnabled = false;
376
473
  function setExtractor(extractStrings) {
474
+ exports.isNativeAccelerationEnabled = true;
377
475
  readFixedString = readString();
378
476
  function readString(headerLength) {
379
477
  return function readString(length) {
380
478
  let string = strings[stringPosition++];
381
479
  if (string == null) {
480
+ if (bundledStrings)
481
+ return readStringJS(length)
382
482
  let extraction = extractStrings(position, srcEnd, length, src);
383
483
  if (typeof extraction == 'string') {
384
484
  string = extraction;
@@ -633,76 +733,15 @@ function getFloat16() {
633
733
  }
634
734
 
635
735
  let keyCache = new Array(4096);
636
- function readKey() {
637
- let length = src[position++];
638
- if (length >= 0x60 && length < 0x78) {
639
- // fixstr, potentially use key cache
640
- length = length - 0x60;
641
- if (srcStringEnd >= position) // if it has been extracted, must use it (and faster anyway)
642
- return srcString.slice(position - srcStringStart, (position += length) - srcStringStart)
643
- else if (!(srcStringEnd == 0 && srcEnd < 180))
644
- return readFixedString(length)
645
- } else { // not cacheable, go back and do a standard read
646
- position--;
647
- return read()
648
- }
649
- let key = ((length << 5) ^ (length > 1 ? dataView.getUint16(position) : length > 0 ? src[position] : 0)) & 0xfff;
650
- let entry = keyCache[key];
651
- let checkPosition = position;
652
- let end = position + length - 3;
653
- let chunk;
654
- let i = 0;
655
- if (entry && entry.bytes == length) {
656
- while (checkPosition < end) {
657
- chunk = dataView.getUint32(checkPosition);
658
- if (chunk != entry[i++]) {
659
- checkPosition = 0x70000000;
660
- break
661
- }
662
- checkPosition += 4;
663
- }
664
- end += 3;
665
- while (checkPosition < end) {
666
- chunk = src[checkPosition++];
667
- if (chunk != entry[i++]) {
668
- checkPosition = 0x70000000;
669
- break
670
- }
671
- }
672
- if (checkPosition === end) {
673
- position = checkPosition;
674
- return entry.string
675
- }
676
- end -= 3;
677
- checkPosition = position;
678
- }
679
- entry = [];
680
- keyCache[key] = entry;
681
- entry.bytes = length;
682
- while (checkPosition < end) {
683
- chunk = dataView.getUint32(checkPosition);
684
- entry.push(chunk);
685
- checkPosition += 4;
686
- }
687
- end += 3;
688
- while (checkPosition < end) {
689
- chunk = src[checkPosition++];
690
- entry.push(chunk);
691
- }
692
- // for small blocks, avoiding the overhead of the extract call is helpful
693
- let string = length < 16 ? shortStringInJS(length) : longStringInJS(length);
694
- if (string != null)
695
- return entry.string = string
696
- return entry.string = readFixedString(length)
697
- }
698
736
 
699
737
  class Tag {
700
- constructor(value) {
738
+ constructor(value, tag) {
701
739
  this.value = value;
740
+ this.tag = tag;
702
741
  }
703
742
  }
704
743
 
705
- let glbl = typeof window == 'object' ? window : global;
744
+ let glbl = typeof self == 'object' ? self : global;
706
745
 
707
746
  currentExtensions[0] = (dateString) => {
708
747
  // string date extension
@@ -716,38 +755,67 @@ currentExtensions[1] = (epochSec) => {
716
755
 
717
756
  currentExtensions[2] = (buffer) => {
718
757
  // bigint extension
719
- return new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0)
758
+ let value = BigInt(0);
759
+ for (let i = 0, l = buffer.byteLength; i < l; i++) {
760
+ value = BigInt(buffer[i]) + value << BigInt(8);
761
+ }
762
+ return value
720
763
  };
721
764
 
722
765
  currentExtensions[3] = (buffer) => {
723
766
  // negative bigint extension
724
- return BigInt(-1) - (new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0))
767
+ return BigInt(-1) - currentExtensions[2](buffer)
768
+ };
769
+ currentExtensions[4] = (fraction) => {
770
+ // best to reparse to maintain accuracy
771
+ return +(fraction[1] + 'e' + fraction[0])
772
+ };
773
+
774
+ currentExtensions[5] = (fraction) => {
775
+ // probably not sufficiently accurate
776
+ return fraction[1] * Math.exp(fraction[0] * Math.log(2))
725
777
  };
726
778
 
727
- // the registration of the record definition extension (tag 105)
728
- const recordDefinition = () => {
729
- let definition = read();
730
- let structure = definition[0];
731
- let id = definition[1];
732
- currentStructures[id & 0xff] = structure;
779
+ // the registration of the record definition extension
780
+ const recordDefinition = (definition) => {
781
+ let id = definition[0] - 0xe000;
782
+ let structure = definition[1];
783
+ let existingStructure = currentStructures[id];
784
+ if (existingStructure && existingStructure.isShared) {
785
+ (currentStructures.restoreStructures || (currentStructures.restoreStructures = []))[id] = existingStructure;
786
+ }
787
+ currentStructures[id] = structure;
788
+
733
789
  structure.read = createStructureReader(structure);
734
790
  let object = {};
735
- for (let i = 2,l = definition.length; i < l; i++) {
736
- let key = structure[i - 2];
737
- object[key] = definition[i];
738
- }
791
+ if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
792
+ let key = currentDecoder.decodeKey(structure[i - 2]);
793
+ object[key] = definition[i];
794
+ }
795
+ else for (let i = 2,l = definition.length; i < l; i++) {
796
+ let key = structure[i - 2];
797
+ object[key] = definition[i];
798
+ }
739
799
  return object
740
800
  };
741
-
742
- recordDefinition.handlesRead = true;
743
- currentExtensions[RECORD_TAG_ID] = recordDefinition;
801
+ currentExtensions[LEGACY_RECORD_INLINE_ID] = recordDefinition;
802
+ currentExtensions[14] = (value) => {
803
+ if (bundledStrings)
804
+ return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
805
+ return new Tag(value, 14)
806
+ };
807
+ currentExtensions[15] = (value) => {
808
+ if (bundledStrings)
809
+ return bundledStrings[1].slice(bundledStrings.position1, bundledStrings.position1 += value)
810
+ return new Tag(value, 15)
811
+ };
744
812
 
745
813
  currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
746
814
  return (glbl[data[0]] || Error)(data[1], data[2])
747
815
  };
748
816
  const packedTable = (read) => {
749
817
  if (src[position++] != 0x84)
750
- throw new Error('Packed values structure must be followed by 4 element array')
818
+ throw new Error('Packed values structure must be followed by a 4 element array')
751
819
  let newPackedValues = read(); // packed values
752
820
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
753
821
  packedValues.prefixes = read();
@@ -758,15 +826,36 @@ packedTable.handlesRead = true;
758
826
  currentExtensions[51] = packedTable;
759
827
 
760
828
  currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
829
+ if (!packedValues) {
830
+ if (currentDecoder.getShared)
831
+ loadShared();
832
+ else
833
+ return new Tag(data, PACKED_REFERENCE_TAG_ID)
834
+ }
761
835
  if (typeof data == 'number')
762
836
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
763
837
  throw new Error('No support for non-integer packed references yet')
764
838
  };
839
+ currentExtensions[25] = (id) => {
840
+ return stringRefs[id]
841
+ };
842
+ currentExtensions[256] = (read) => {
843
+ stringRefs = [];
844
+ try {
845
+ return read()
846
+ } finally {
847
+ stringRefs = null;
848
+ }
849
+ };
850
+ currentExtensions[256].handlesRead = true;
765
851
 
766
- currentExtensions[40009] = (id) => {
767
- // id extension (for structured clones)
768
- if (!referenceMap)
852
+ currentExtensions[28] = (read) => {
853
+ // shareable http://cbor.schmorp.de/value-sharing (for structured clones)
854
+ if (!referenceMap) {
769
855
  referenceMap = new Map();
856
+ referenceMap.id = 0;
857
+ }
858
+ let id = referenceMap.id++;
770
859
  let token = src[position];
771
860
  let target;
772
861
  // TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
@@ -784,9 +873,10 @@ currentExtensions[40009] = (id) => {
784
873
  refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
785
874
  return targetProperties // no cycle, can just use the returned read object
786
875
  };
876
+ currentExtensions[28].handlesRead = true;
787
877
 
788
- currentExtensions[40010] = (id) => {
789
- // pointer extension (for structured clones)
878
+ currentExtensions[29] = (id) => {
879
+ // sharedref http://cbor.schmorp.de/value-sharing (for structured clones)
790
880
  let refEntry = referenceMap.get(id);
791
881
  refEntry.used = true;
792
882
  return refEntry.target
@@ -809,19 +899,38 @@ function combine(a, b) {
809
899
  return a.concat(b)
810
900
  return Object.assign({}, a, b)
811
901
  }
902
+ function getPackedValues() {
903
+ if (!packedValues) {
904
+ if (currentDecoder.getShared)
905
+ loadShared();
906
+ else
907
+ throw new Error('No packed values available')
908
+ }
909
+ return packedValues
910
+ }
911
+ const SHARED_DATA_TAG_ID = 0x53687264; // ascii 'Shrd'
812
912
  currentExtensionRanges.push((tag, input) => {
813
913
  if (tag >= 225 && tag <= 255)
814
- return combine(packedTable.prefixes[tag - 224], input)
914
+ return combine(getPackedValues().prefixes[tag - 224], input)
815
915
  if (tag >= 28704 && tag <= 32767)
816
- return combine(packedTable.prefixes[tag - 28672], input)
916
+ return combine(getPackedValues().prefixes[tag - 28672], input)
817
917
  if (tag >= 1879052288 && tag <= 2147483647)
818
- return combine(packedTable.prefixes[tag - 1879048192], input)
918
+ return combine(getPackedValues().prefixes[tag - 1879048192], input)
819
919
  if (tag >= 216 && tag <= 223)
820
- return combine(input, packedTable.suffixes[tag - 216])
920
+ return combine(input, getPackedValues().suffixes[tag - 216])
821
921
  if (tag >= 27647 && tag <= 28671)
822
- return combine(input, packedTable.suffixes[tag - 27639])
922
+ return combine(input, getPackedValues().suffixes[tag - 27639])
823
923
  if (tag >= 1811940352 && tag <= 1879048191)
824
- return combine(input, packedTable.suffixes[tag - 1811939328])
924
+ return combine(input, getPackedValues().suffixes[tag - 1811939328])
925
+ if (tag == SHARED_DATA_TAG_ID) {// we do a special check for this so that we can keep the currentExtensions as densely stored array (v8 stores arrays densely under about 3000 elements)
926
+ return {
927
+ packedValues: packedValues,
928
+ structures: currentStructures.slice(0),
929
+ version: input,
930
+ }
931
+ }
932
+ if (tag == 55799) // self-descriptive CBOR tag, just return input value
933
+ return input
825
934
  });
826
935
 
827
936
  const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array');
@@ -838,6 +947,61 @@ function registerTypedArray(typedArrayName, tag) {
838
947
  };
839
948
  }
840
949
 
950
+ function readBundleExt() {
951
+ let length = readJustLength();
952
+ let bundlePosition = position + read();
953
+ for (let i = 2; i < length; i++) {
954
+ // skip past bundles that were already read
955
+ let bundleLength = readJustLength(); // this will increment position, so must add to position afterwards
956
+ position += bundleLength;
957
+ }
958
+ let dataPosition = position;
959
+ position = bundlePosition;
960
+ bundledStrings = [readStringJS(readJustLength()), readStringJS(readJustLength())];
961
+ bundledStrings.position0 = 0;
962
+ bundledStrings.position1 = 0;
963
+ bundledStrings.postBundlePosition = position;
964
+ position = dataPosition;
965
+ return read()
966
+ }
967
+
968
+ function readJustLength() {
969
+ let token = src[position++] & 0x1f;
970
+ if (token > 0x17) {
971
+ switch (token) {
972
+ case 0x18:
973
+ token = src[position++];
974
+ break
975
+ case 0x19:
976
+ token = dataView.getUint16(position);
977
+ position += 2;
978
+ break
979
+ case 0x1a:
980
+ token = dataView.getUint32(position);
981
+ position += 4;
982
+ break
983
+ }
984
+ }
985
+ return token
986
+ }
987
+
988
+ function loadShared() {
989
+ if (currentDecoder.getShared) {
990
+ let sharedData = saveState(() => {
991
+ // save the state in case getShared modifies our buffer
992
+ src = null;
993
+ return currentDecoder.getShared()
994
+ }) || {};
995
+ let updatedStructures = sharedData.structures || [];
996
+ currentDecoder.sharedVersion = sharedData.version;
997
+ packedValues = currentDecoder.sharedValues = sharedData.packedValues;
998
+ if (currentStructures === true)
999
+ currentDecoder.structures = currentStructures = updatedStructures;
1000
+ else
1001
+ currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
1002
+ }
1003
+ }
1004
+
841
1005
  function saveState(callback) {
842
1006
  let savedSrcEnd = srcEnd;
843
1007
  let savedPosition = position;
@@ -847,6 +1011,8 @@ function saveState(callback) {
847
1011
  let savedSrcString = srcString;
848
1012
  let savedStrings = strings;
849
1013
  let savedReferenceMap = referenceMap;
1014
+ let savedBundledStrings = bundledStrings;
1015
+
850
1016
  // TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
851
1017
  let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
852
1018
  let savedStructures = currentStructures;
@@ -861,6 +1027,7 @@ function saveState(callback) {
861
1027
  srcString = savedSrcString;
862
1028
  strings = savedStrings;
863
1029
  referenceMap = savedReferenceMap;
1030
+ bundledStrings = savedBundledStrings;
864
1031
  src = savedSrc;
865
1032
  sequentialMode = savedSequentialMode;
866
1033
  currentStructures = savedStructures;
@@ -907,13 +1074,15 @@ let extensions, extensionClasses;
907
1074
  const hasNodeBuffer = typeof Buffer !== 'undefined';
908
1075
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
909
1076
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
910
- const RECORD_STARTING_ID_PREFIX = 0x69; // tag 105/0x69
911
1077
  const MAX_STRUCTURES = 0x100;
912
1078
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
913
1079
  let target;
914
1080
  let targetView;
915
1081
  let position$1 = 0;
916
1082
  let safeEnd;
1083
+ let bundledStrings$1 = null;
1084
+ const MAX_BUNDLE_SIZE = 0xf000;
1085
+ const hasNonLatin = /[\u0080-\uFFFF]/;
917
1086
  const RECORD_SYMBOL = Symbol('record-id');
918
1087
  class Encoder extends Decoder {
919
1088
  constructor(options) {
@@ -925,7 +1094,6 @@ class Encoder extends Decoder {
925
1094
  let structures;
926
1095
  let referenceMap;
927
1096
  options = options || {};
928
- let lastSharedStructuresLength = 0;
929
1097
  let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
930
1098
  return target.utf8Write(string, position, maxBytes)
931
1099
  } : (textEncoder && textEncoder.encodeInto) ?
@@ -934,12 +1102,20 @@ class Encoder extends Decoder {
934
1102
  } : false;
935
1103
 
936
1104
  let encoder = this;
937
- let maxSharedStructures = 64;
1105
+ let hasSharedStructures = options.structures || options.saveStructures;
1106
+ let maxSharedStructures = options.maxSharedStructures;
1107
+ if (maxSharedStructures == null)
1108
+ maxSharedStructures = hasSharedStructures ? 128 : 0;
1109
+ if (maxSharedStructures > 8190)
1110
+ throw new Error('Maximum maxSharedStructure is 8190')
938
1111
  let isSequential = options.sequential;
939
1112
  if (isSequential) {
940
1113
  maxSharedStructures = 0;
941
- this.structures = [];
942
1114
  }
1115
+ if (!this.structures)
1116
+ this.structures = [];
1117
+ if (this.saveStructures)
1118
+ this.saveShared = this.saveStructures;
943
1119
  let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues;
944
1120
  let sharedPackedObjectMap;
945
1121
  if (sharedValues) {
@@ -947,13 +1123,29 @@ class Encoder extends Decoder {
947
1123
  for (let i = 0, l = sharedValues.length; i < l; i++) {
948
1124
  sharedPackedObjectMap[sharedValues[i]] = i;
949
1125
  }
950
-
951
1126
  }
952
1127
  let recordIdsToRemove = [];
953
1128
  let transitionsCount = 0;
954
1129
  let serializationsSinceTransitionRebuild = 0;
955
-
956
- this.encode = function(value, encodeOptions) {
1130
+
1131
+ this.mapEncode = function(value, encodeOptions) {
1132
+ // Experimental support for premapping keys using _keyMap instad of keyMap - not optiimised yet)
1133
+ if (this._keyMap && !this._mapped) {
1134
+ //console.log('encoding ', value)
1135
+ switch (value.constructor.name) {
1136
+ case 'Array':
1137
+ value = value.map(r => this.encodeKeys(r));
1138
+ break
1139
+ //case 'Map':
1140
+ // value = this.encodeKeys(value)
1141
+ // break
1142
+ }
1143
+ //this._mapped = true
1144
+ }
1145
+ return this.encode(value, encodeOptions)
1146
+ };
1147
+
1148
+ this.encode = function(value, encodeOptions) {
957
1149
  if (!target) {
958
1150
  target = new ByteArrayAllocate(8192);
959
1151
  targetView = new DataView(target.buffer, 0, 8192);
@@ -969,11 +1161,30 @@ class Encoder extends Decoder {
969
1161
  } else if (encodeOptions === REUSE_BUFFER_MODE)
970
1162
  position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
971
1163
  start = position$1;
1164
+ if (encoder.useSelfDescribedHeader) {
1165
+ targetView.setUint32(position$1, 0xd9d9f700); // tag two byte, then self-descriptive tag
1166
+ position$1 += 3;
1167
+ }
972
1168
  referenceMap = encoder.structuredClone ? new Map() : null;
1169
+ if (encoder.bundleStrings && typeof value !== 'string') {
1170
+ bundledStrings$1 = [];
1171
+ bundledStrings$1.size = Infinity; // force a new bundle start on first string
1172
+ } else
1173
+ bundledStrings$1 = null;
1174
+
973
1175
  sharedStructures = encoder.structures;
974
1176
  if (sharedStructures) {
975
- if (sharedStructures.uninitialized)
976
- encoder.structures = sharedStructures = encoder.getStructures();
1177
+ if (sharedStructures.uninitialized) {
1178
+ let sharedData = encoder.getShared() || {};
1179
+ encoder.structures = sharedStructures = sharedData.structures || [];
1180
+ encoder.sharedVersion = sharedData.version;
1181
+ let sharedValues = encoder.sharedValues = sharedData.packedValues;
1182
+ if (sharedValues) {
1183
+ sharedPackedObjectMap = {};
1184
+ for (let i = 0, l = sharedValues.length; i < l; i++)
1185
+ sharedPackedObjectMap[sharedValues[i]] = i;
1186
+ }
1187
+ }
977
1188
  let sharedStructuresLength = sharedStructures.length;
978
1189
  if (sharedStructuresLength > maxSharedStructures && !isSequential)
979
1190
  sharedStructuresLength = maxSharedStructures;
@@ -982,10 +1193,13 @@ class Encoder extends Decoder {
982
1193
  sharedStructures.transitions = Object.create(null);
983
1194
  for (let i = 0; i < sharedStructuresLength; i++) {
984
1195
  let keys = sharedStructures[i];
1196
+ //console.log('shared struct keys:', keys)
985
1197
  if (!keys)
986
1198
  continue
987
1199
  let nextTransition, transition = sharedStructures.transitions;
988
1200
  for (let j = 0, l = keys.length; j < l; j++) {
1201
+ if (transition[RECORD_SYMBOL] === undefined)
1202
+ transition[RECORD_SYMBOL] = i;
989
1203
  let key = keys[j];
990
1204
  nextTransition = transition[key];
991
1205
  if (!nextTransition) {
@@ -993,9 +1207,8 @@ class Encoder extends Decoder {
993
1207
  }
994
1208
  transition = nextTransition;
995
1209
  }
996
- transition[RECORD_SYMBOL] = i;
1210
+ transition[RECORD_SYMBOL] = i | 0x100000;
997
1211
  }
998
- lastSharedStructuresLength = sharedStructures.length;
999
1212
  }
1000
1213
  if (!isSequential)
1001
1214
  sharedStructures.nextId = sharedStructuresLength;
@@ -1028,9 +1241,12 @@ class Encoder extends Decoder {
1028
1241
  }
1029
1242
  try {
1030
1243
  encode(value);
1244
+ if (bundledStrings$1) {
1245
+ writeBundles(start, encode);
1246
+ }
1031
1247
  encoder.offset = position$1; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
1032
1248
  if (referenceMap && referenceMap.idsToInsert) {
1033
- position$1 += referenceMap.idsToInsert.length * 8;
1249
+ position$1 += referenceMap.idsToInsert.length * 2;
1034
1250
  if (position$1 > safeEnd)
1035
1251
  makeRoom(position$1);
1036
1252
  encoder.offset = position$1;
@@ -1038,16 +1254,18 @@ class Encoder extends Decoder {
1038
1254
  referenceMap = null;
1039
1255
  return serialized
1040
1256
  }
1041
- if (encodeOptions === REUSE_BUFFER_MODE) {
1257
+ if (encodeOptions & REUSE_BUFFER_MODE) {
1042
1258
  target.start = start;
1043
1259
  target.end = position$1;
1044
1260
  return target
1045
1261
  }
1046
- return target.subarray(start, position$1) // position can change if we call pack again in saveStructures, so we get the buffer now
1262
+ return target.subarray(start, position$1) // position can change if we call encode again in saveShared, so we get the buffer now
1047
1263
  } finally {
1048
1264
  if (sharedStructures) {
1049
1265
  if (serializationsSinceTransitionRebuild < 10)
1050
1266
  serializationsSinceTransitionRebuild++;
1267
+ if (sharedStructures.length > maxSharedStructures)
1268
+ sharedStructures.length = maxSharedStructures;
1051
1269
  if (transitionsCount > 10000) {
1052
1270
  // force a rebuild occasionally after a lot of transitions so it can get cleaned up
1053
1271
  sharedStructures.transitions = null;
@@ -1060,35 +1278,29 @@ class Encoder extends Decoder {
1060
1278
  recordIdsToRemove[i][RECORD_SYMBOL] = undefined;
1061
1279
  }
1062
1280
  recordIdsToRemove = [];
1281
+ //sharedStructures.nextId = maxSharedStructures
1063
1282
  }
1064
- if (hasSharedUpdate && encoder.saveStructures) {
1065
- if (encoder.structures.length > maxSharedStructures) {
1066
- encoder.structures = encoder.structures.slice(0, maxSharedStructures);
1067
- }
1068
- // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1069
- let returnBuffer = target.subarray(start, position$1);
1070
- let shared = encoder.structures || [];
1071
- if (sharedValues) {
1072
- shared = shared.concat(sharedValues);
1073
- }
1074
-
1075
- if (encoder.saveStructures(encoder.structures, lastSharedStructuresLength) === false) {
1076
- // get updated structures and try again if the update failed
1077
- encoder.structures = encoder.getStructures() || [];
1078
- return encoder.encode(value)
1079
- }
1080
- lastSharedStructuresLength = shared.length;
1081
- return returnBuffer
1283
+ }
1284
+ if (hasSharedUpdate && encoder.saveShared) {
1285
+ if (encoder.structures.length > maxSharedStructures) {
1286
+ encoder.structures = encoder.structures.slice(0, maxSharedStructures);
1082
1287
  }
1288
+ // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1289
+ let returnBuffer = target.subarray(start, position$1);
1290
+ if (encoder.updateSharedData() === false)
1291
+ return encoder.encode(value) // re-encode if it fails
1292
+ return returnBuffer
1083
1293
  }
1294
+ if (encodeOptions & RESET_BUFFER_MODE)
1295
+ position$1 = start;
1084
1296
  }
1085
1297
  };
1086
1298
  this.findCommonStringsToPack = () => {
1087
1299
  samplingPackedValues = new Map();
1088
1300
  if (!sharedPackedObjectMap)
1089
1301
  sharedPackedObjectMap = Object.create(null);
1090
- return ({ threshold }) => {
1091
- threshold = threshold || 4;
1302
+ return (options) => {
1303
+ let threshold = options && options.threshold || 4;
1092
1304
  let position = this.pack ? options.maxPrivatePackedValues || 16 : 0;
1093
1305
  if (!sharedValues)
1094
1306
  sharedValues = this.sharedValues = [];
@@ -1099,6 +1311,7 @@ class Encoder extends Decoder {
1099
1311
  hasSharedUpdate = true;
1100
1312
  }
1101
1313
  }
1314
+ while (this.saveShared && this.updateSharedData() === false) {}
1102
1315
  samplingPackedValues = null;
1103
1316
  }
1104
1317
  };
@@ -1147,6 +1360,33 @@ class Encoder extends Decoder {
1147
1360
  }
1148
1361
  }
1149
1362
  let strLength = value.length;
1363
+ if (bundledStrings$1 && strLength >= 4 && strLength < 0x400) {
1364
+ if ((bundledStrings$1.size += strLength) > MAX_BUNDLE_SIZE) {
1365
+ let extStart;
1366
+ let maxBytes = (bundledStrings$1[0] ? bundledStrings$1[0].length * 3 + bundledStrings$1[1].length : 0) + 10;
1367
+ if (position$1 + maxBytes > safeEnd)
1368
+ target = makeRoom(position$1 + maxBytes);
1369
+ target[position$1++] = 0xd9; // tag 16-bit
1370
+ target[position$1++] = 0xdf; // tag 0xdff9
1371
+ target[position$1++] = 0xf9;
1372
+ // TODO: If we only have one bundle with any string data, only write one string bundle
1373
+ target[position$1++] = bundledStrings$1.position ? 0x84 : 0x82; // array of 4 or 2 elements depending on if we write bundles
1374
+ target[position$1++] = 0x1a; // 32-bit unsigned int
1375
+ extStart = position$1 - start;
1376
+ position$1 += 4; // reserve for writing bundle reference
1377
+ if (bundledStrings$1.position) {
1378
+ writeBundles(start, encode); // write the last bundles
1379
+ }
1380
+ bundledStrings$1 = ['', '']; // create new ones
1381
+ bundledStrings$1.size = 0;
1382
+ bundledStrings$1.position = extStart;
1383
+ }
1384
+ let twoByte = hasNonLatin.test(value);
1385
+ bundledStrings$1[twoByte ? 0 : 1] += value;
1386
+ target[position$1++] = twoByte ? 0xce : 0xcf;
1387
+ encode(strLength);
1388
+ return
1389
+ }
1150
1390
  let headerSize;
1151
1391
  // first we estimate the header size, so we can write to the correct location
1152
1392
  if (strLength < 0x20) {
@@ -1273,16 +1513,16 @@ class Encoder extends Decoder {
1273
1513
  if (referenceMap) {
1274
1514
  let referee = referenceMap.get(value);
1275
1515
  if (referee) {
1276
- if (!referee.id) {
1516
+ target[position$1++] = 0xd8;
1517
+ target[position$1++] = 29; // http://cbor.schmorp.de/value-sharing
1518
+ target[position$1++] = 0x19; // 16-bit uint
1519
+ if (!referee.references) {
1277
1520
  let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = []);
1278
- referee.id = idsToInsert.push(referee);
1521
+ referee.references = [];
1522
+ idsToInsert.push(referee);
1279
1523
  }
1280
- target[position$1++] = 0xd9;
1281
- target[position$1++] = 40010 >> 8;
1282
- target[position$1++] = 40010 & 0xff;
1283
- target[position$1++] = 0x1a; // uint32
1284
- targetView.setUint32(position$1, referee.id);
1285
- position$1 += 4;
1524
+ referee.references.push(position$1 - start);
1525
+ position$1 += 2; // TODO: also support 32-bit
1286
1526
  return
1287
1527
  } else
1288
1528
  referenceMap.set(value, { offset: position$1 - start });
@@ -1322,16 +1562,23 @@ class Encoder extends Decoder {
1322
1562
  targetView.setUint32(position$1, length);
1323
1563
  position$1 += 4;
1324
1564
  }
1325
- for (let [ key, entryValue ] of value) {
1326
- encode(key);
1327
- encode(entryValue);
1565
+ if (encoder.keyMap) {
1566
+ for (let [ key, entryValue ] of value) {
1567
+ encode(encoder.encodeKey(key));
1568
+ encode(entryValue);
1569
+ }
1570
+ } else {
1571
+ for (let [ key, entryValue ] of value) {
1572
+ encode(key);
1573
+ encode(entryValue);
1574
+ }
1328
1575
  }
1329
- } else {
1576
+ } else {
1330
1577
  for (let i = 0, l = extensions.length; i < l; i++) {
1331
1578
  let extensionClass = extensionClasses[i];
1332
1579
  if (value instanceof extensionClass) {
1333
1580
  let extension = extensions[i];
1334
- let tag = extension.tag;
1581
+ let tag = extension.tag || extension.getTag && extension.getTag(value);
1335
1582
  if (tag < 0x18) {
1336
1583
  target[position$1++] = 0xc0 | tag;
1337
1584
  } else if (tag < 0x100) {
@@ -1393,6 +1640,7 @@ class Encoder extends Decoder {
1393
1640
  const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
1394
1641
  // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
1395
1642
  let keys = Object.keys(object);
1643
+ let vals = Object.values(object);
1396
1644
  let length = keys.length;
1397
1645
  if (length < 0x18) {
1398
1646
  target[position$1++] = 0xa0 | length;
@@ -1408,10 +1656,16 @@ class Encoder extends Decoder {
1408
1656
  targetView.setUint32(position$1, length);
1409
1657
  position$1 += 4;
1410
1658
  }
1411
- let key;
1412
- for (let i = 0; i < length; i++) {
1413
- encode(key = keys[i]);
1414
- encode(object[key]);
1659
+ if (encoder.keyMap) {
1660
+ for (let i = 0; i < length; i++) {
1661
+ encode(encodeKey(keys[i]));
1662
+ encode(vals[i]);
1663
+ }
1664
+ } else {
1665
+ for (let i = 0; i < length; i++) {
1666
+ encode(keys[i]);
1667
+ encode(vals[i]);
1668
+ }
1415
1669
  }
1416
1670
  } :
1417
1671
  (object, safePrototype) => {
@@ -1419,95 +1673,92 @@ class Encoder extends Decoder {
1419
1673
  let objectOffset = position$1 - start;
1420
1674
  position$1 += 2;
1421
1675
  let size = 0;
1422
- for (let key in object) {
1423
- if (safePrototype || object.hasOwnProperty(key)) {
1424
- encode(key);
1676
+ if (encoder.keyMap) {
1677
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1678
+ encode(encoder.encodeKey(key));
1425
1679
  encode(object[key]);
1426
1680
  size++;
1427
1681
  }
1682
+ } else {
1683
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1684
+ encode(key);
1685
+ encode(object[key]);
1686
+ size++;
1687
+ }
1428
1688
  }
1429
1689
  target[objectOffset++ + start] = size >> 8;
1430
1690
  target[objectOffset + start] = size & 0xff;
1431
1691
  } :
1432
-
1433
- /* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
1434
1692
  (object, safePrototype) => {
1435
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
1436
- let objectOffset = position++ - start
1437
- let wroteKeys
1438
- for (let key in object) {
1439
- if (safePrototype || object.hasOwnProperty(key)) {
1440
- nextTransition = transition[key]
1693
+ let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1694
+ let newTransitions = 0;
1695
+ let length = 0;
1696
+ let parentRecordId;
1697
+ let keys;
1698
+ if (this.keyMap) {
1699
+ keys = Object.keys(object).map(k => this.encodeKey(k));
1700
+ length = keys.length;
1701
+ for (let i = 0; i < length; i++) {
1702
+ let key = keys[i];
1703
+ nextTransition = transition[key];
1441
1704
  if (!nextTransition) {
1442
- nextTransition = transition[key] = Object.create(null)
1443
- nextTransition.__keys__ = (transition.__keys__ || []).concat([key])
1444
- /*let keys = Object.keys(object)
1445
- if
1446
- let size = 0
1447
- let startBranch = transition.__keys__ ? transition.__keys__.length : 0
1448
- for (let i = 0, l = keys.length; i++) {
1449
- let key = keys[i]
1450
- size += key.length << 2
1451
- if (i >= startBranch) {
1452
- nextTransition = nextTransition[key] = Object.create(null)
1453
- nextTransition.__keys__ = keys.slice(0, i + 1)
1454
- }
1705
+ nextTransition = transition[key] = Object.create(null);
1706
+ newTransitions++;
1707
+ }
1708
+ transition = nextTransition;
1709
+ }
1710
+ } else {
1711
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1712
+ nextTransition = transition[key];
1713
+ if (!nextTransition) {
1714
+ if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
1715
+ parentRecordId = transition[RECORD_SYMBOL] & 0xffff;
1455
1716
  }
1456
- makeRoom(position + size)
1457
- nextTransition = transition[key]
1458
- target.copy(target, )
1459
- objectOffset
1717
+ nextTransition = transition[key] = Object.create(null);
1718
+ newTransitions++;
1460
1719
  }
1461
- transition = nextTransition
1462
- encode(object[key])
1463
- }
1464
- }
1465
- let id = transition.id
1466
- if (!id) {
1467
- id = transition.id = structures.push(transition.__keys__) + 63
1468
- if (sharedStructures.onUpdate)
1469
- sharedStructures.onUpdate(id, transition.__keys__)
1470
- }
1471
- target[objectOffset + start] = id
1472
- }*/
1473
- (object) => {
1474
- let keys = Object.keys(object);
1475
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1476
- let newTransitions = 0;
1477
- let length = keys.length;
1478
- for (let i = 0; i < length; i++) {
1479
- let key = keys[i];
1480
- nextTransition = transition[key];
1481
- if (!nextTransition) {
1482
- nextTransition = transition[key] = Object.create(null);
1483
- newTransitions++;
1720
+ transition = nextTransition;
1721
+ length++;
1484
1722
  }
1485
- transition = nextTransition;
1486
1723
  }
1487
1724
  let recordId = transition[RECORD_SYMBOL];
1488
1725
  if (recordId !== undefined) {
1489
- target[position$1++] = 0xd9; // tag two byte
1490
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1491
- target[position$1++] = recordId;
1726
+ recordId &= 0xffff;
1727
+ target[position$1++] = 0xd9;
1728
+ target[position$1++] = (recordId >> 8) | 0xe0;
1729
+ target[position$1++] = recordId & 0xff;
1492
1730
  } else {
1493
- recordId = structures.nextId++;
1494
- if (!recordId) {
1495
- recordId = 0;
1496
- structures.nextId = 1;
1497
- }
1498
- if (recordId >= MAX_STRUCTURES) {// cycle back around
1499
- structures.nextId = (recordId = maxSharedStructures) + 1;
1731
+ if (!keys)
1732
+ keys = transition.__keys__ || (transition.__keys__ = Object.keys(object));
1733
+ if (parentRecordId === undefined) {
1734
+ recordId = structures.nextId++;
1735
+ if (!recordId) {
1736
+ recordId = 0;
1737
+ structures.nextId = 1;
1738
+ }
1739
+ if (recordId >= MAX_STRUCTURES) {// cycle back around
1740
+ structures.nextId = (recordId = maxSharedStructures) + 1;
1741
+ }
1742
+ } else {
1743
+ recordId = parentRecordId;
1500
1744
  }
1501
- transition[RECORD_SYMBOL] = recordId;
1502
1745
  structures[recordId] = keys;
1503
- if (sharedStructures && sharedStructures.length <= maxSharedStructures) {
1504
- target[position$1++] = 0xd9; // tag two byte
1505
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1506
- target[position$1++] = recordId; // tag number
1746
+ if (recordId < maxSharedStructures) {
1747
+ target[position$1++] = 0xd9;
1748
+ target[position$1++] = (recordId >> 8) | 0xe0;
1749
+ target[position$1++] = recordId & 0xff;
1750
+ transition = structures.transitions;
1751
+ for (let i = 0; i < length; i++) {
1752
+ if (transition[RECORD_SYMBOL] === undefined || (transition[RECORD_SYMBOL] & 0x100000))
1753
+ transition[RECORD_SYMBOL] = recordId;
1754
+ transition = transition[keys[i]];
1755
+ }
1756
+ transition[RECORD_SYMBOL] = recordId | 0x100000; // indicates it is a extendable terminal
1507
1757
  hasSharedUpdate = true;
1508
1758
  } else {
1509
- target[position$1++] = 0xd8;
1510
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1759
+ transition[RECORD_SYMBOL] = recordId;
1760
+ targetView.setUint32(position$1, 0xd9dfff00); // tag two byte, then record definition id
1761
+ position$1 += 3;
1511
1762
  if (newTransitions)
1512
1763
  transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
1513
1764
  // record the removal of the id, we can maintain our shared structure
@@ -1515,13 +1766,9 @@ class Encoder extends Decoder {
1515
1766
  recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined; // we are cycling back through, and have to remove old ones
1516
1767
  recordIdsToRemove.push(transition);
1517
1768
  writeArrayHeader(length + 2);
1769
+ encode(0xe000 + recordId);
1518
1770
  encode(keys);
1519
- target[position$1++] = 0x19; // uint16
1520
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1521
- target[position$1++] = recordId;
1522
- // now write the values
1523
- for (let i =0; i < length; i++)
1524
- encode(object[keys[i]]);
1771
+ for (let v of Object.values(object)) encode(v);
1525
1772
  return
1526
1773
  }
1527
1774
  }
@@ -1530,8 +1777,9 @@ class Encoder extends Decoder {
1530
1777
  } else {
1531
1778
  writeArrayHeader(length);
1532
1779
  }
1533
- for (let i =0; i < length; i++)
1534
- encode(object[keys[i]]);
1780
+ for (let key in object)
1781
+ if (safePrototype || object.hasOwnProperty(key))
1782
+ encode(object[key]);
1535
1783
  };
1536
1784
  const makeRoom = (end) => {
1537
1785
  let newSize;
@@ -1540,7 +1788,7 @@ class Encoder extends Decoder {
1540
1788
  if ((end - start) > MAX_BUFFER_SIZE)
1541
1789
  throw new Error('Encoded buffer would be larger than maximum buffer size')
1542
1790
  newSize = Math.min(MAX_BUFFER_SIZE,
1543
- Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x1000000) / 0x1000) * 0x1000);
1791
+ Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000);
1544
1792
  } else // faster handling for smaller buffers
1545
1793
  newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12;
1546
1794
  let newBuffer = new ByteArrayAllocate(newSize);
@@ -1561,6 +1809,40 @@ class Encoder extends Decoder {
1561
1809
  targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
1562
1810
  position$1 = 0;
1563
1811
  }
1812
+ clearSharedData() {
1813
+ if (this.structures)
1814
+ this.structures = [];
1815
+ if (this.sharedValues)
1816
+ this.sharedValues = undefined;
1817
+ }
1818
+ updateSharedData() {
1819
+ let lastVersion = this.sharedVersion || 0;
1820
+ this.sharedVersion = lastVersion + 1;
1821
+ let structuresCopy = this.structures.slice(0);
1822
+ let sharedData = new SharedData(structuresCopy, this.sharedValues, this.sharedVersion);
1823
+ let saveResults = this.saveShared(sharedData,
1824
+ existingShared => (existingShared && existingShared.version || 0) == lastVersion);
1825
+ if (saveResults === false) {
1826
+ // get updated structures and try again if the update failed
1827
+ sharedData = this.getShared() || {};
1828
+ this.structures = sharedData.structures || [];
1829
+ this.sharedValues = sharedData.packedValues;
1830
+ this.sharedVersion = sharedData.version;
1831
+ this.structures.nextId = this.structures.length;
1832
+ } else {
1833
+ // restore structures
1834
+ structuresCopy.forEach((structure, i) => this.structures[i] = structure);
1835
+ }
1836
+ // 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
1837
+ return saveResults
1838
+ }
1839
+ }
1840
+ class SharedData {
1841
+ constructor(structures, values, version) {
1842
+ this.structures = structures;
1843
+ this.packedValues = values;
1844
+ this.version = version;
1845
+ }
1564
1846
  }
1565
1847
 
1566
1848
  function writeArrayHeader(length) {
@@ -1630,9 +1912,11 @@ function findRepetitiveStrings(value, packedValues) {
1630
1912
  }
1631
1913
  }
1632
1914
 
1633
- extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
1634
- Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array, BigUint64Array, Int8Array, Int16Array, Int32Array, BigInt64Array,
1635
- Float32Array, Float64Array];
1915
+ extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
1916
+ Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
1917
+ typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
1918
+ typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
1919
+ Float32Array, Float64Array, SharedData];
1636
1920
 
1637
1921
  //Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
1638
1922
  extensions = [{
@@ -1667,6 +1951,13 @@ extensions = [{
1667
1951
  encode(regex, encode) {
1668
1952
  encode([ 'RegExp', regex.source, regex.flags ]);
1669
1953
  }
1954
+ }, {
1955
+ getTag(tag) {
1956
+ return tag.tag
1957
+ },
1958
+ encode(tag, encode) {
1959
+ encode(tag.value);
1960
+ }
1670
1961
  }, {
1671
1962
  encode(arrayBuffer, encode, makeRoom) {
1672
1963
  writeBuffer(arrayBuffer, makeRoom);
@@ -1685,7 +1976,35 @@ extensions = [{
1685
1976
  typedArrayEncoder(78),
1686
1977
  typedArrayEncoder(79),
1687
1978
  typedArrayEncoder(81),
1688
- typedArrayEncoder(82)];
1979
+ typedArrayEncoder(82),
1980
+ {
1981
+ encode(sharedData, encode) { // write SharedData
1982
+ let packedValues = sharedData.packedValues || [];
1983
+ let sharedStructures = sharedData.structures || [];
1984
+ if (packedValues.values.length > 0) {
1985
+ target[position$1++] = 0xd8; // one-byte tag
1986
+ target[position$1++] = 51; // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
1987
+ writeArrayHeader(4);
1988
+ let valuesArray = packedValues.values;
1989
+ encode(valuesArray);
1990
+ writeArrayHeader(0); // prefixes
1991
+ writeArrayHeader(0); // suffixes
1992
+ packedObjectMap = Object.create(sharedPackedObjectMap || null);
1993
+ for (let i = 0, l = valuesArray.length; i < l; i++) {
1994
+ packedObjectMap[valuesArray[i]] = i;
1995
+ }
1996
+ }
1997
+ if (sharedStructures) {
1998
+ targetView.setUint32(position$1, 0xd9dffe00);
1999
+ position$1 += 3;
2000
+ let definitions = sharedStructures.slice(0);
2001
+ definitions.unshift(0xe000);
2002
+ definitions.push(new Tag(sharedData.version, 0x53687264));
2003
+ encode(definitions);
2004
+ } else
2005
+ encode(new Tag(sharedData.version, 0x53687264));
2006
+ }
2007
+ }];
1689
2008
 
1690
2009
  function typedArrayEncoder(tag) {
1691
2010
  return {
@@ -1725,27 +2044,35 @@ function writeBuffer(buffer, makeRoom) {
1725
2044
  function insertIds(serialized, idsToInsert) {
1726
2045
  // insert the ids that need to be referenced for structured clones
1727
2046
  let nextId;
1728
- let distanceToMove = idsToInsert.length * 8;
2047
+ let distanceToMove = idsToInsert.length * 2;
1729
2048
  let lastEnd = serialized.length - distanceToMove;
1730
2049
  idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1);
2050
+ for (let id = 0; id < idsToInsert.length; id++) {
2051
+ let referee = idsToInsert[id];
2052
+ referee.id = id;
2053
+ for (let position of referee.references) {
2054
+ serialized[position++] = id >> 8;
2055
+ serialized[position] = id & 0xff;
2056
+ }
2057
+ }
1731
2058
  while (nextId = idsToInsert.pop()) {
1732
2059
  let offset = nextId.offset;
1733
- let id = nextId.id;
1734
2060
  serialized.copyWithin(offset + distanceToMove, offset, lastEnd);
1735
- distanceToMove -= 8;
2061
+ distanceToMove -= 2;
1736
2062
  let position = offset + distanceToMove;
1737
- serialized[position++] = 0xd9;
1738
- serialized[position++] = 40009 >> 8;
1739
- serialized[position++] = 40009 & 0xff;
1740
- serialized[position++] = 0x1a; // uint32
1741
- serialized[position++] = id >> 24;
1742
- serialized[position++] = (id >> 16) & 0xff;
1743
- serialized[position++] = (id >> 8) & 0xff;
1744
- serialized[position++] = id & 0xff;
2063
+ serialized[position++] = 0xd8;
2064
+ serialized[position++] = 28; // http://cbor.schmorp.de/value-sharing
1745
2065
  lastEnd = offset;
1746
2066
  }
1747
2067
  return serialized
1748
2068
  }
2069
+ function writeBundles(start, encode) {
2070
+ targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start + 1); // the offset to bundle
2071
+ let writeStrings = bundledStrings$1;
2072
+ bundledStrings$1 = null;
2073
+ encode(writeStrings[0]);
2074
+ encode(writeStrings[1]);
2075
+ }
1749
2076
 
1750
2077
  function addExtension$1(extension) {
1751
2078
  if (extension.Class) {
@@ -1759,7 +2086,8 @@ function addExtension$1(extension) {
1759
2086
  let defaultEncoder = new Encoder({ useRecords: false });
1760
2087
  const encode = defaultEncoder.encode;
1761
2088
  const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
1762
- const REUSE_BUFFER_MODE = 1000;
2089
+ const REUSE_BUFFER_MODE = 512;
2090
+ const RESET_BUFFER_MODE = 1024;
1763
2091
 
1764
2092
  class EncoderStream extends stream.Transform {
1765
2093
  constructor(options) {
@@ -1909,9 +2237,7 @@ function tryRequire(moduleId) {
1909
2237
  let require$1 = module$1.createRequire((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('node.cjs', document.baseURI).href)));
1910
2238
  return require$1(moduleId)
1911
2239
  } catch (error) {
1912
- if (typeof window == 'undefined')
1913
- console.warn('Native extraction module not loaded, cbor-x will still run, but with decreased performance. ' + error.message.split('\n')[0]);
1914
- else
2240
+ if (typeof window != 'undefined')
1915
2241
  console.warn('For browser usage, directly use cbor-x/decode or cbor-x/encode modules. ' + error.message.split('\n')[0]);
1916
2242
  }
1917
2243
  }