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/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,12 +114,21 @@
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
59
121
  // new ones
60
- dataView = source.dataView || (source.dataView = new DataView(source.buffer, source.byteOffset, source.byteLength));
61
- if (this) {
122
+ try {
123
+ dataView = source.dataView || (source.dataView = new DataView(source.buffer, source.byteOffset, source.byteLength));
124
+ } catch(error) {
125
+ // if it doesn't have a buffer, maybe it is the wrong type of object
126
+ src = null;
127
+ if (source instanceof Uint8Array)
128
+ throw error
129
+ throw new Error('Source must be a Uint8Array or Buffer but was a ' + ((source && typeof source == 'object') ? source.constructor.name : typeof source))
130
+ }
131
+ if (this instanceof Decoder) {
62
132
  currentDecoder = this;
63
133
  packedValues = this.sharedValues &&
64
134
  (this.pack ? new Array(this.maxPrivatePackedValues || 16).concat(this.sharedValues) :
@@ -115,6 +185,9 @@
115
185
  function checkedRead() {
116
186
  try {
117
187
  let result = read();
188
+ if (bundledStrings) // bundled strings to skip past
189
+ position = bundledStrings.postBundlePosition;
190
+
118
191
  if (position == srcEnd) {
119
192
  // finished reading this source, cleanup references
120
193
  currentStructures = null;
@@ -177,10 +250,15 @@
177
250
  position += 8;
178
251
  return value
179
252
  }
180
- if (currentDecoder.uint64AsNumber)
181
- return src[position++] * 0x100000000000000 + src[position++] * 0x1000000000000 + src[position++] * 0x10000000000 + src[position++] * 0x100000000 +
182
- src[position++] * 0x1000000 + (src[position++] << 16) + (src[position++] << 8) + src[position++]
183
- token = dataView.getBigUint64(position);
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) {
258
+ token = dataView.getUint32(position) * 0x100000000;
259
+ token += dataView.getUint32(position + 4);
260
+ } else
261
+ token = dataView.getBigUint64(position);
184
262
  position += 8;
185
263
  break
186
264
  case 0x1f:
@@ -188,6 +266,7 @@
188
266
  switch(majorType) {
189
267
  case 2: // byte string
190
268
  case 3: // text string
269
+ throw new Error('Indefinite length not supported for byte or text strings')
191
270
  case 4: // array
192
271
  let array = [];
193
272
  let value, i = 0;
@@ -199,8 +278,8 @@
199
278
  let key;
200
279
  if (currentDecoder.mapsAsObjects) {
201
280
  let object = {};
202
- while ((key = readKey()) != STOP_CODE)
203
- 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();
204
283
  return object
205
284
  } else {
206
285
  if (restoreMapsAsObject) {
@@ -208,8 +287,8 @@
208
287
  restoreMapsAsObject = false;
209
288
  }
210
289
  let map = new Map();
211
- while ((key = read()) != STOP_CODE)
212
- 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());
213
292
  return map
214
293
  }
215
294
  case 7:
@@ -241,16 +320,15 @@
241
320
  return readFixedString(token)
242
321
  case 4: // array
243
322
  let array = new Array(token);
244
- for (let i = 0; i < token; i++) {
245
- array[i] = read();
246
- }
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();
247
326
  return array
248
327
  case 5: // map
249
328
  if (currentDecoder.mapsAsObjects) {
250
329
  let object = {};
251
- for (let i = 0; i < token; i++) {
252
- object[readKey()] = read();
253
- }
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();
254
332
  return object
255
333
  } else {
256
334
  if (restoreMapsAsObject) {
@@ -258,63 +336,66 @@
258
336
  restoreMapsAsObject = false;
259
337
  }
260
338
  let map = new Map();
261
- for (let i = 0; i < token; i++) {
262
- map.set(read(), read());
263
- }
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());
264
341
  return map
265
342
  }
266
343
  case 6: // extension
267
- if ((token >> 8) == RECORD_TAG_ID) { // record structures
268
- 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))
269
347
  if (structure) {
270
- if (!structure.read)
271
- structure.read = createStructureReader(structure);
348
+ if (!structure.read) structure.read = createStructureReader(structure);
272
349
  return structure.read()
273
- } else if (currentDecoder.getStructures) {
274
- let updatedStructures = saveState(() => {
275
- // save the state in case getStructures modifies our buffer
276
- src = null;
277
- return currentDecoder.getStructures()
278
- });
279
- if (currentStructures === true)
280
- currentDecoder.structures = currentStructures = updatedStructures;
281
- else
282
- currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
283
- structure = currentStructures[token & 0xff];
284
- if (structure) {
285
- if (!structure.read)
286
- structure.read = createStructureReader(structure);
287
- return structure.read()
288
- } else
289
- return token
290
- } else
291
- return token
292
- } else {
293
- let extension = currentExtensions[token];
294
- if (extension) {
295
- if (extension.handlesRead)
296
- return extension(read)
297
- else
298
- return extension(read())
299
- } else {
300
- let input = read();
301
- for (let i = 0; i < currentExtensionRanges.length; i++) {
302
- let value = currentExtensionRanges[i](token, input);
303
- if (value !== undefined)
304
- 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
+ }
305
372
  }
306
- return new Tag(input)
307
373
  }
308
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
+ }
309
390
  case 7: // fixed value
310
391
  switch (token) {
311
392
  case 0x14: return false
312
393
  case 0x15: return true
313
394
  case 0x16: return null
314
395
  case 0x17: return; // undefined
315
- case 0x1f:
396
+ case 0x1f:
316
397
  default:
317
- let packedValue = packedValues[token];
398
+ let packedValue = (packedValues || getPackedValues())[token];
318
399
  if (packedValue !== undefined)
319
400
  return packedValue
320
401
  throw new Error('Unknown token ' + token)
@@ -330,42 +411,59 @@
330
411
  }
331
412
  const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
332
413
  function createStructureReader(structure) {
333
- let l = structure.length;
334
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
+ }
335
436
  // This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
336
- if (readObject.count++ > 2) {
337
- 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);
338
- 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
339
443
  }
340
- readArrayHeader();
341
- let object = {};
342
- for (let i = 0; i < l; i++) {
343
- let key = structure[i];
344
- 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)
345
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();
346
458
  return object
347
459
  }
348
- readObject.count = 0;
460
+ structure.slowReads = 0;
349
461
  return readObject
350
462
  }
351
463
 
352
- function readArrayHeader(expectedLength) {
353
- // consume the array header, TODO: check expected length
354
- let token = src[position++];
355
- //let majorType = token >> 5
356
- token = token & 0x1f;
357
- if (token > 0x17) {
358
- switch (token) {
359
- case 0x18: position++;
360
- break
361
- case 0x19: position += 2;
362
- break
363
- case 0x1a: position += 4;
364
- }
365
- }
366
- }
367
-
368
464
  let readFixedString = readStringJS;
465
+
466
+ let isNativeAccelerationEnabled = false;
369
467
  function readStringJS(length) {
370
468
  let result;
371
469
  if (length < 16) {
@@ -594,76 +692,15 @@
594
692
  }
595
693
 
596
694
  let keyCache = new Array(4096);
597
- function readKey() {
598
- let length = src[position++];
599
- if (length >= 0x60 && length < 0x78) {
600
- // fixstr, potentially use key cache
601
- length = length - 0x60;
602
- if (srcStringEnd >= position) // if it has been extracted, must use it (and faster anyway)
603
- return srcString.slice(position - srcStringStart, (position += length) - srcStringStart)
604
- else if (!(srcStringEnd == 0 && srcEnd < 180))
605
- return readFixedString(length)
606
- } else { // not cacheable, go back and do a standard read
607
- position--;
608
- return read()
609
- }
610
- let key = ((length << 5) ^ (length > 1 ? dataView.getUint16(position) : length > 0 ? src[position] : 0)) & 0xfff;
611
- let entry = keyCache[key];
612
- let checkPosition = position;
613
- let end = position + length - 3;
614
- let chunk;
615
- let i = 0;
616
- if (entry && entry.bytes == length) {
617
- while (checkPosition < end) {
618
- chunk = dataView.getUint32(checkPosition);
619
- if (chunk != entry[i++]) {
620
- checkPosition = 0x70000000;
621
- break
622
- }
623
- checkPosition += 4;
624
- }
625
- end += 3;
626
- while (checkPosition < end) {
627
- chunk = src[checkPosition++];
628
- if (chunk != entry[i++]) {
629
- checkPosition = 0x70000000;
630
- break
631
- }
632
- }
633
- if (checkPosition === end) {
634
- position = checkPosition;
635
- return entry.string
636
- }
637
- end -= 3;
638
- checkPosition = position;
639
- }
640
- entry = [];
641
- keyCache[key] = entry;
642
- entry.bytes = length;
643
- while (checkPosition < end) {
644
- chunk = dataView.getUint32(checkPosition);
645
- entry.push(chunk);
646
- checkPosition += 4;
647
- }
648
- end += 3;
649
- while (checkPosition < end) {
650
- chunk = src[checkPosition++];
651
- entry.push(chunk);
652
- }
653
- // for small blocks, avoiding the overhead of the extract call is helpful
654
- let string = length < 16 ? shortStringInJS(length) : longStringInJS(length);
655
- if (string != null)
656
- return entry.string = string
657
- return entry.string = readFixedString(length)
658
- }
659
695
 
660
696
  class Tag {
661
- constructor(value) {
697
+ constructor(value, tag) {
662
698
  this.value = value;
699
+ this.tag = tag;
663
700
  }
664
701
  }
665
702
 
666
- let glbl = typeof window == 'object' ? window : global;
703
+ let glbl = typeof self == 'object' ? self : global;
667
704
 
668
705
  currentExtensions[0] = (dateString) => {
669
706
  // string date extension
@@ -677,38 +714,67 @@
677
714
 
678
715
  currentExtensions[2] = (buffer) => {
679
716
  // bigint extension
680
- 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
681
722
  };
682
723
 
683
724
  currentExtensions[3] = (buffer) => {
684
725
  // negative bigint extension
685
- 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])
686
731
  };
687
732
 
688
- // the registration of the record definition extension (tag 105)
689
- const recordDefinition = () => {
690
- let definition = read();
691
- let structure = definition[0];
692
- let id = definition[1];
693
- 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
+
694
748
  structure.read = createStructureReader(structure);
695
749
  let object = {};
696
- for (let i = 2,l = definition.length; i < l; i++) {
697
- let key = structure[i - 2];
698
- object[key] = definition[i];
699
- }
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
+ }
700
758
  return object
701
759
  };
702
-
703
- recordDefinition.handlesRead = true;
704
- 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
+ };
705
771
 
706
772
  currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
707
773
  return (glbl[data[0]] || Error)(data[1], data[2])
708
774
  };
709
775
  const packedTable = (read) => {
710
776
  if (src[position++] != 0x84)
711
- 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')
712
778
  let newPackedValues = read(); // packed values
713
779
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
714
780
  packedValues.prefixes = read();
@@ -719,15 +785,36 @@
719
785
  currentExtensions[51] = packedTable;
720
786
 
721
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
+ }
722
794
  if (typeof data == 'number')
723
795
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
724
796
  throw new Error('No support for non-integer packed references yet')
725
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;
726
810
 
727
- currentExtensions[40009] = (id) => {
728
- // id extension (for structured clones)
729
- if (!referenceMap)
811
+ currentExtensions[28] = (read) => {
812
+ // shareable http://cbor.schmorp.de/value-sharing (for structured clones)
813
+ if (!referenceMap) {
730
814
  referenceMap = new Map();
815
+ referenceMap.id = 0;
816
+ }
817
+ let id = referenceMap.id++;
731
818
  let token = src[position];
732
819
  let target;
733
820
  // TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
@@ -745,9 +832,10 @@
745
832
  refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
746
833
  return targetProperties // no cycle, can just use the returned read object
747
834
  };
835
+ currentExtensions[28].handlesRead = true;
748
836
 
749
- currentExtensions[40010] = (id) => {
750
- // pointer extension (for structured clones)
837
+ currentExtensions[29] = (id) => {
838
+ // sharedref http://cbor.schmorp.de/value-sharing (for structured clones)
751
839
  let refEntry = referenceMap.get(id);
752
840
  refEntry.used = true;
753
841
  return refEntry.target
@@ -770,19 +858,38 @@
770
858
  return a.concat(b)
771
859
  return Object.assign({}, a, b)
772
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'
773
871
  currentExtensionRanges.push((tag, input) => {
774
872
  if (tag >= 225 && tag <= 255)
775
- return combine(packedTable.prefixes[tag - 224], input)
873
+ return combine(getPackedValues().prefixes[tag - 224], input)
776
874
  if (tag >= 28704 && tag <= 32767)
777
- return combine(packedTable.prefixes[tag - 28672], input)
875
+ return combine(getPackedValues().prefixes[tag - 28672], input)
778
876
  if (tag >= 1879052288 && tag <= 2147483647)
779
- return combine(packedTable.prefixes[tag - 1879048192], input)
877
+ return combine(getPackedValues().prefixes[tag - 1879048192], input)
780
878
  if (tag >= 216 && tag <= 223)
781
- return combine(input, packedTable.suffixes[tag - 216])
879
+ return combine(input, getPackedValues().suffixes[tag - 216])
782
880
  if (tag >= 27647 && tag <= 28671)
783
- return combine(input, packedTable.suffixes[tag - 27639])
881
+ return combine(input, getPackedValues().suffixes[tag - 27639])
784
882
  if (tag >= 1811940352 && tag <= 1879048191)
785
- 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
786
893
  });
787
894
 
788
895
  const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array');
@@ -799,6 +906,61 @@
799
906
  };
800
907
  }
801
908
 
909
+ function readBundleExt() {
910
+ let length = readJustLength();
911
+ let bundlePosition = position + read();
912
+ for (let i = 2; i < length; i++) {
913
+ // skip past bundles that were already read
914
+ let bundleLength = readJustLength(); // this will increment position, so must add to position afterwards
915
+ position += bundleLength;
916
+ }
917
+ let dataPosition = position;
918
+ position = bundlePosition;
919
+ bundledStrings = [readStringJS(readJustLength()), readStringJS(readJustLength())];
920
+ bundledStrings.position0 = 0;
921
+ bundledStrings.position1 = 0;
922
+ bundledStrings.postBundlePosition = position;
923
+ position = dataPosition;
924
+ return read()
925
+ }
926
+
927
+ function readJustLength() {
928
+ let token = src[position++] & 0x1f;
929
+ if (token > 0x17) {
930
+ switch (token) {
931
+ case 0x18:
932
+ token = src[position++];
933
+ break
934
+ case 0x19:
935
+ token = dataView.getUint16(position);
936
+ position += 2;
937
+ break
938
+ case 0x1a:
939
+ token = dataView.getUint32(position);
940
+ position += 4;
941
+ break
942
+ }
943
+ }
944
+ return token
945
+ }
946
+
947
+ function loadShared() {
948
+ if (currentDecoder.getShared) {
949
+ let sharedData = saveState(() => {
950
+ // save the state in case getShared modifies our buffer
951
+ src = null;
952
+ return currentDecoder.getShared()
953
+ }) || {};
954
+ let updatedStructures = sharedData.structures || [];
955
+ currentDecoder.sharedVersion = sharedData.version;
956
+ packedValues = currentDecoder.sharedValues = sharedData.packedValues;
957
+ if (currentStructures === true)
958
+ currentDecoder.structures = currentStructures = updatedStructures;
959
+ else
960
+ currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
961
+ }
962
+ }
963
+
802
964
  function saveState(callback) {
803
965
  let savedSrcEnd = srcEnd;
804
966
  let savedPosition = position;
@@ -806,6 +968,8 @@
806
968
  let savedSrcStringEnd = srcStringEnd;
807
969
  let savedSrcString = srcString;
808
970
  let savedReferenceMap = referenceMap;
971
+ let savedBundledStrings = bundledStrings;
972
+
809
973
  // TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
810
974
  let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
811
975
  let savedStructures = currentStructures;
@@ -818,6 +982,7 @@
818
982
  srcStringEnd = savedSrcStringEnd;
819
983
  srcString = savedSrcString;
820
984
  referenceMap = savedReferenceMap;
985
+ bundledStrings = savedBundledStrings;
821
986
  src = savedSrc;
822
987
  sequentialMode = savedSequentialMode;
823
988
  currentStructures = savedStructures;
@@ -864,13 +1029,15 @@
864
1029
  const hasNodeBuffer = typeof Buffer !== 'undefined';
865
1030
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
866
1031
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
867
- const RECORD_STARTING_ID_PREFIX = 0x69; // tag 105/0x69
868
1032
  const MAX_STRUCTURES = 0x100;
869
1033
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
870
1034
  let target;
871
1035
  let targetView;
872
1036
  let position$1 = 0;
873
1037
  let safeEnd;
1038
+ let bundledStrings$1 = null;
1039
+ const MAX_BUNDLE_SIZE = 0xf000;
1040
+ const hasNonLatin = /[\u0080-\uFFFF]/;
874
1041
  const RECORD_SYMBOL = Symbol('record-id');
875
1042
  class Encoder extends Decoder {
876
1043
  constructor(options) {
@@ -882,7 +1049,6 @@
882
1049
  let structures;
883
1050
  let referenceMap;
884
1051
  options = options || {};
885
- let lastSharedStructuresLength = 0;
886
1052
  let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
887
1053
  return target.utf8Write(string, position, maxBytes)
888
1054
  } : (textEncoder && textEncoder.encodeInto) ?
@@ -891,12 +1057,20 @@
891
1057
  } : false;
892
1058
 
893
1059
  let encoder = this;
894
- let maxSharedStructures = 64;
1060
+ let hasSharedStructures = options.structures || options.saveStructures;
1061
+ let maxSharedStructures = options.maxSharedStructures;
1062
+ if (maxSharedStructures == null)
1063
+ maxSharedStructures = hasSharedStructures ? 128 : 0;
1064
+ if (maxSharedStructures > 8190)
1065
+ throw new Error('Maximum maxSharedStructure is 8190')
895
1066
  let isSequential = options.sequential;
896
1067
  if (isSequential) {
897
1068
  maxSharedStructures = 0;
898
- this.structures = [];
899
1069
  }
1070
+ if (!this.structures)
1071
+ this.structures = [];
1072
+ if (this.saveStructures)
1073
+ this.saveShared = this.saveStructures;
900
1074
  let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues;
901
1075
  let sharedPackedObjectMap;
902
1076
  if (sharedValues) {
@@ -904,13 +1078,29 @@
904
1078
  for (let i = 0, l = sharedValues.length; i < l; i++) {
905
1079
  sharedPackedObjectMap[sharedValues[i]] = i;
906
1080
  }
907
-
908
1081
  }
909
1082
  let recordIdsToRemove = [];
910
1083
  let transitionsCount = 0;
911
1084
  let serializationsSinceTransitionRebuild = 0;
912
-
913
- this.encode = function(value, encodeOptions) {
1085
+
1086
+ this.mapEncode = function(value, encodeOptions) {
1087
+ // Experimental support for premapping keys using _keyMap instad of keyMap - not optiimised yet)
1088
+ if (this._keyMap && !this._mapped) {
1089
+ //console.log('encoding ', value)
1090
+ switch (value.constructor.name) {
1091
+ case 'Array':
1092
+ value = value.map(r => this.encodeKeys(r));
1093
+ break
1094
+ //case 'Map':
1095
+ // value = this.encodeKeys(value)
1096
+ // break
1097
+ }
1098
+ //this._mapped = true
1099
+ }
1100
+ return this.encode(value, encodeOptions)
1101
+ };
1102
+
1103
+ this.encode = function(value, encodeOptions) {
914
1104
  if (!target) {
915
1105
  target = new ByteArrayAllocate(8192);
916
1106
  targetView = new DataView(target.buffer, 0, 8192);
@@ -926,11 +1116,30 @@
926
1116
  } else if (encodeOptions === REUSE_BUFFER_MODE)
927
1117
  position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
928
1118
  start = position$1;
1119
+ if (encoder.useSelfDescribedHeader) {
1120
+ targetView.setUint32(position$1, 0xd9d9f700); // tag two byte, then self-descriptive tag
1121
+ position$1 += 3;
1122
+ }
929
1123
  referenceMap = encoder.structuredClone ? new Map() : null;
1124
+ if (encoder.bundleStrings && typeof value !== 'string') {
1125
+ bundledStrings$1 = [];
1126
+ bundledStrings$1.size = Infinity; // force a new bundle start on first string
1127
+ } else
1128
+ bundledStrings$1 = null;
1129
+
930
1130
  sharedStructures = encoder.structures;
931
1131
  if (sharedStructures) {
932
- if (sharedStructures.uninitialized)
933
- encoder.structures = sharedStructures = encoder.getStructures();
1132
+ if (sharedStructures.uninitialized) {
1133
+ let sharedData = encoder.getShared() || {};
1134
+ encoder.structures = sharedStructures = sharedData.structures || [];
1135
+ encoder.sharedVersion = sharedData.version;
1136
+ let sharedValues = encoder.sharedValues = sharedData.packedValues;
1137
+ if (sharedValues) {
1138
+ sharedPackedObjectMap = {};
1139
+ for (let i = 0, l = sharedValues.length; i < l; i++)
1140
+ sharedPackedObjectMap[sharedValues[i]] = i;
1141
+ }
1142
+ }
934
1143
  let sharedStructuresLength = sharedStructures.length;
935
1144
  if (sharedStructuresLength > maxSharedStructures && !isSequential)
936
1145
  sharedStructuresLength = maxSharedStructures;
@@ -939,10 +1148,13 @@
939
1148
  sharedStructures.transitions = Object.create(null);
940
1149
  for (let i = 0; i < sharedStructuresLength; i++) {
941
1150
  let keys = sharedStructures[i];
1151
+ //console.log('shared struct keys:', keys)
942
1152
  if (!keys)
943
1153
  continue
944
1154
  let nextTransition, transition = sharedStructures.transitions;
945
1155
  for (let j = 0, l = keys.length; j < l; j++) {
1156
+ if (transition[RECORD_SYMBOL] === undefined)
1157
+ transition[RECORD_SYMBOL] = i;
946
1158
  let key = keys[j];
947
1159
  nextTransition = transition[key];
948
1160
  if (!nextTransition) {
@@ -950,9 +1162,8 @@
950
1162
  }
951
1163
  transition = nextTransition;
952
1164
  }
953
- transition[RECORD_SYMBOL] = i;
1165
+ transition[RECORD_SYMBOL] = i | 0x100000;
954
1166
  }
955
- lastSharedStructuresLength = sharedStructures.length;
956
1167
  }
957
1168
  if (!isSequential)
958
1169
  sharedStructures.nextId = sharedStructuresLength;
@@ -985,9 +1196,12 @@
985
1196
  }
986
1197
  try {
987
1198
  encode(value);
1199
+ if (bundledStrings$1) {
1200
+ writeBundles(start, encode);
1201
+ }
988
1202
  encoder.offset = position$1; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
989
1203
  if (referenceMap && referenceMap.idsToInsert) {
990
- position$1 += referenceMap.idsToInsert.length * 8;
1204
+ position$1 += referenceMap.idsToInsert.length * 2;
991
1205
  if (position$1 > safeEnd)
992
1206
  makeRoom(position$1);
993
1207
  encoder.offset = position$1;
@@ -995,16 +1209,18 @@
995
1209
  referenceMap = null;
996
1210
  return serialized
997
1211
  }
998
- if (encodeOptions === REUSE_BUFFER_MODE) {
1212
+ if (encodeOptions & REUSE_BUFFER_MODE) {
999
1213
  target.start = start;
1000
1214
  target.end = position$1;
1001
1215
  return target
1002
1216
  }
1003
- return target.subarray(start, position$1) // position can change if we call pack again in saveStructures, so we get the buffer now
1217
+ return target.subarray(start, position$1) // position can change if we call encode again in saveShared, so we get the buffer now
1004
1218
  } finally {
1005
1219
  if (sharedStructures) {
1006
1220
  if (serializationsSinceTransitionRebuild < 10)
1007
1221
  serializationsSinceTransitionRebuild++;
1222
+ if (sharedStructures.length > maxSharedStructures)
1223
+ sharedStructures.length = maxSharedStructures;
1008
1224
  if (transitionsCount > 10000) {
1009
1225
  // force a rebuild occasionally after a lot of transitions so it can get cleaned up
1010
1226
  sharedStructures.transitions = null;
@@ -1017,35 +1233,29 @@
1017
1233
  recordIdsToRemove[i][RECORD_SYMBOL] = undefined;
1018
1234
  }
1019
1235
  recordIdsToRemove = [];
1236
+ //sharedStructures.nextId = maxSharedStructures
1020
1237
  }
1021
- if (hasSharedUpdate && encoder.saveStructures) {
1022
- if (encoder.structures.length > maxSharedStructures) {
1023
- encoder.structures = encoder.structures.slice(0, maxSharedStructures);
1024
- }
1025
- // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1026
- let returnBuffer = target.subarray(start, position$1);
1027
- let shared = encoder.structures || [];
1028
- if (sharedValues) {
1029
- shared = shared.concat(sharedValues);
1030
- }
1031
-
1032
- if (encoder.saveStructures(encoder.structures, lastSharedStructuresLength) === false) {
1033
- // get updated structures and try again if the update failed
1034
- encoder.structures = encoder.getStructures() || [];
1035
- return encoder.encode(value)
1036
- }
1037
- lastSharedStructuresLength = shared.length;
1038
- return returnBuffer
1238
+ }
1239
+ if (hasSharedUpdate && encoder.saveShared) {
1240
+ if (encoder.structures.length > maxSharedStructures) {
1241
+ encoder.structures = encoder.structures.slice(0, maxSharedStructures);
1039
1242
  }
1243
+ // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1244
+ let returnBuffer = target.subarray(start, position$1);
1245
+ if (encoder.updateSharedData() === false)
1246
+ return encoder.encode(value) // re-encode if it fails
1247
+ return returnBuffer
1040
1248
  }
1249
+ if (encodeOptions & RESET_BUFFER_MODE)
1250
+ position$1 = start;
1041
1251
  }
1042
1252
  };
1043
1253
  this.findCommonStringsToPack = () => {
1044
1254
  samplingPackedValues = new Map();
1045
1255
  if (!sharedPackedObjectMap)
1046
1256
  sharedPackedObjectMap = Object.create(null);
1047
- return ({ threshold }) => {
1048
- threshold = threshold || 4;
1257
+ return (options) => {
1258
+ let threshold = options && options.threshold || 4;
1049
1259
  let position = this.pack ? options.maxPrivatePackedValues || 16 : 0;
1050
1260
  if (!sharedValues)
1051
1261
  sharedValues = this.sharedValues = [];
@@ -1056,6 +1266,7 @@
1056
1266
  hasSharedUpdate = true;
1057
1267
  }
1058
1268
  }
1269
+ while (this.saveShared && this.updateSharedData() === false) {}
1059
1270
  samplingPackedValues = null;
1060
1271
  }
1061
1272
  };
@@ -1104,6 +1315,33 @@
1104
1315
  }
1105
1316
  }
1106
1317
  let strLength = value.length;
1318
+ if (bundledStrings$1 && strLength >= 4 && strLength < 0x400) {
1319
+ if ((bundledStrings$1.size += strLength) > MAX_BUNDLE_SIZE) {
1320
+ let extStart;
1321
+ let maxBytes = (bundledStrings$1[0] ? bundledStrings$1[0].length * 3 + bundledStrings$1[1].length : 0) + 10;
1322
+ if (position$1 + maxBytes > safeEnd)
1323
+ target = makeRoom(position$1 + maxBytes);
1324
+ target[position$1++] = 0xd9; // tag 16-bit
1325
+ target[position$1++] = 0xdf; // tag 0xdff9
1326
+ target[position$1++] = 0xf9;
1327
+ // TODO: If we only have one bundle with any string data, only write one string bundle
1328
+ target[position$1++] = bundledStrings$1.position ? 0x84 : 0x82; // array of 4 or 2 elements depending on if we write bundles
1329
+ target[position$1++] = 0x1a; // 32-bit unsigned int
1330
+ extStart = position$1 - start;
1331
+ position$1 += 4; // reserve for writing bundle reference
1332
+ if (bundledStrings$1.position) {
1333
+ writeBundles(start, encode); // write the last bundles
1334
+ }
1335
+ bundledStrings$1 = ['', '']; // create new ones
1336
+ bundledStrings$1.size = 0;
1337
+ bundledStrings$1.position = extStart;
1338
+ }
1339
+ let twoByte = hasNonLatin.test(value);
1340
+ bundledStrings$1[twoByte ? 0 : 1] += value;
1341
+ target[position$1++] = twoByte ? 0xce : 0xcf;
1342
+ encode(strLength);
1343
+ return
1344
+ }
1107
1345
  let headerSize;
1108
1346
  // first we estimate the header size, so we can write to the correct location
1109
1347
  if (strLength < 0x20) {
@@ -1230,16 +1468,16 @@
1230
1468
  if (referenceMap) {
1231
1469
  let referee = referenceMap.get(value);
1232
1470
  if (referee) {
1233
- if (!referee.id) {
1471
+ target[position$1++] = 0xd8;
1472
+ target[position$1++] = 29; // http://cbor.schmorp.de/value-sharing
1473
+ target[position$1++] = 0x19; // 16-bit uint
1474
+ if (!referee.references) {
1234
1475
  let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = []);
1235
- referee.id = idsToInsert.push(referee);
1476
+ referee.references = [];
1477
+ idsToInsert.push(referee);
1236
1478
  }
1237
- target[position$1++] = 0xd9;
1238
- target[position$1++] = 40010 >> 8;
1239
- target[position$1++] = 40010 & 0xff;
1240
- target[position$1++] = 0x1a; // uint32
1241
- targetView.setUint32(position$1, referee.id);
1242
- position$1 += 4;
1479
+ referee.references.push(position$1 - start);
1480
+ position$1 += 2; // TODO: also support 32-bit
1243
1481
  return
1244
1482
  } else
1245
1483
  referenceMap.set(value, { offset: position$1 - start });
@@ -1279,16 +1517,23 @@
1279
1517
  targetView.setUint32(position$1, length);
1280
1518
  position$1 += 4;
1281
1519
  }
1282
- for (let [ key, entryValue ] of value) {
1283
- encode(key);
1284
- encode(entryValue);
1520
+ if (encoder.keyMap) {
1521
+ for (let [ key, entryValue ] of value) {
1522
+ encode(encoder.encodeKey(key));
1523
+ encode(entryValue);
1524
+ }
1525
+ } else {
1526
+ for (let [ key, entryValue ] of value) {
1527
+ encode(key);
1528
+ encode(entryValue);
1529
+ }
1285
1530
  }
1286
- } else {
1531
+ } else {
1287
1532
  for (let i = 0, l = extensions.length; i < l; i++) {
1288
1533
  let extensionClass = extensionClasses[i];
1289
1534
  if (value instanceof extensionClass) {
1290
1535
  let extension = extensions[i];
1291
- let tag = extension.tag;
1536
+ let tag = extension.tag || extension.getTag && extension.getTag(value);
1292
1537
  if (tag < 0x18) {
1293
1538
  target[position$1++] = 0xc0 | tag;
1294
1539
  } else if (tag < 0x100) {
@@ -1350,6 +1595,7 @@
1350
1595
  const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
1351
1596
  // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
1352
1597
  let keys = Object.keys(object);
1598
+ let vals = Object.values(object);
1353
1599
  let length = keys.length;
1354
1600
  if (length < 0x18) {
1355
1601
  target[position$1++] = 0xa0 | length;
@@ -1365,10 +1611,16 @@
1365
1611
  targetView.setUint32(position$1, length);
1366
1612
  position$1 += 4;
1367
1613
  }
1368
- let key;
1369
- for (let i = 0; i < length; i++) {
1370
- encode(key = keys[i]);
1371
- encode(object[key]);
1614
+ if (encoder.keyMap) {
1615
+ for (let i = 0; i < length; i++) {
1616
+ encode(encodeKey(keys[i]));
1617
+ encode(vals[i]);
1618
+ }
1619
+ } else {
1620
+ for (let i = 0; i < length; i++) {
1621
+ encode(keys[i]);
1622
+ encode(vals[i]);
1623
+ }
1372
1624
  }
1373
1625
  } :
1374
1626
  (object, safePrototype) => {
@@ -1376,95 +1628,92 @@
1376
1628
  let objectOffset = position$1 - start;
1377
1629
  position$1 += 2;
1378
1630
  let size = 0;
1379
- for (let key in object) {
1380
- if (safePrototype || object.hasOwnProperty(key)) {
1381
- encode(key);
1631
+ if (encoder.keyMap) {
1632
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1633
+ encode(encoder.encodeKey(key));
1382
1634
  encode(object[key]);
1383
1635
  size++;
1384
1636
  }
1637
+ } else {
1638
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1639
+ encode(key);
1640
+ encode(object[key]);
1641
+ size++;
1642
+ }
1385
1643
  }
1386
1644
  target[objectOffset++ + start] = size >> 8;
1387
1645
  target[objectOffset + start] = size & 0xff;
1388
1646
  } :
1389
-
1390
- /* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
1391
1647
  (object, safePrototype) => {
1392
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
1393
- let objectOffset = position++ - start
1394
- let wroteKeys
1395
- for (let key in object) {
1396
- if (safePrototype || object.hasOwnProperty(key)) {
1397
- nextTransition = transition[key]
1648
+ let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1649
+ let newTransitions = 0;
1650
+ let length = 0;
1651
+ let parentRecordId;
1652
+ let keys;
1653
+ if (this.keyMap) {
1654
+ keys = Object.keys(object).map(k => this.encodeKey(k));
1655
+ length = keys.length;
1656
+ for (let i = 0; i < length; i++) {
1657
+ let key = keys[i];
1658
+ nextTransition = transition[key];
1398
1659
  if (!nextTransition) {
1399
- nextTransition = transition[key] = Object.create(null)
1400
- nextTransition.__keys__ = (transition.__keys__ || []).concat([key])
1401
- /*let keys = Object.keys(object)
1402
- if
1403
- let size = 0
1404
- let startBranch = transition.__keys__ ? transition.__keys__.length : 0
1405
- for (let i = 0, l = keys.length; i++) {
1406
- let key = keys[i]
1407
- size += key.length << 2
1408
- if (i >= startBranch) {
1409
- nextTransition = nextTransition[key] = Object.create(null)
1410
- nextTransition.__keys__ = keys.slice(0, i + 1)
1411
- }
1660
+ nextTransition = transition[key] = Object.create(null);
1661
+ newTransitions++;
1662
+ }
1663
+ transition = nextTransition;
1664
+ }
1665
+ } else {
1666
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1667
+ nextTransition = transition[key];
1668
+ if (!nextTransition) {
1669
+ if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
1670
+ parentRecordId = transition[RECORD_SYMBOL] & 0xffff;
1412
1671
  }
1413
- makeRoom(position + size)
1414
- nextTransition = transition[key]
1415
- target.copy(target, )
1416
- objectOffset
1672
+ nextTransition = transition[key] = Object.create(null);
1673
+ newTransitions++;
1417
1674
  }
1418
- transition = nextTransition
1419
- encode(object[key])
1420
- }
1421
- }
1422
- let id = transition.id
1423
- if (!id) {
1424
- id = transition.id = structures.push(transition.__keys__) + 63
1425
- if (sharedStructures.onUpdate)
1426
- sharedStructures.onUpdate(id, transition.__keys__)
1427
- }
1428
- target[objectOffset + start] = id
1429
- }*/
1430
- (object) => {
1431
- let keys = Object.keys(object);
1432
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1433
- let newTransitions = 0;
1434
- let length = keys.length;
1435
- for (let i = 0; i < length; i++) {
1436
- let key = keys[i];
1437
- nextTransition = transition[key];
1438
- if (!nextTransition) {
1439
- nextTransition = transition[key] = Object.create(null);
1440
- newTransitions++;
1675
+ transition = nextTransition;
1676
+ length++;
1441
1677
  }
1442
- transition = nextTransition;
1443
1678
  }
1444
1679
  let recordId = transition[RECORD_SYMBOL];
1445
1680
  if (recordId !== undefined) {
1446
- target[position$1++] = 0xd9; // tag two byte
1447
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1448
- target[position$1++] = recordId;
1681
+ recordId &= 0xffff;
1682
+ target[position$1++] = 0xd9;
1683
+ target[position$1++] = (recordId >> 8) | 0xe0;
1684
+ target[position$1++] = recordId & 0xff;
1449
1685
  } else {
1450
- recordId = structures.nextId++;
1451
- if (!recordId) {
1452
- recordId = 0;
1453
- structures.nextId = 1;
1454
- }
1455
- if (recordId >= MAX_STRUCTURES) {// cycle back around
1456
- structures.nextId = (recordId = maxSharedStructures) + 1;
1686
+ if (!keys)
1687
+ keys = transition.__keys__ || (transition.__keys__ = Object.keys(object));
1688
+ if (parentRecordId === undefined) {
1689
+ recordId = structures.nextId++;
1690
+ if (!recordId) {
1691
+ recordId = 0;
1692
+ structures.nextId = 1;
1693
+ }
1694
+ if (recordId >= MAX_STRUCTURES) {// cycle back around
1695
+ structures.nextId = (recordId = maxSharedStructures) + 1;
1696
+ }
1697
+ } else {
1698
+ recordId = parentRecordId;
1457
1699
  }
1458
- transition[RECORD_SYMBOL] = recordId;
1459
1700
  structures[recordId] = keys;
1460
- if (sharedStructures && sharedStructures.length <= maxSharedStructures) {
1461
- target[position$1++] = 0xd9; // tag two byte
1462
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1463
- target[position$1++] = recordId; // tag number
1701
+ if (recordId < maxSharedStructures) {
1702
+ target[position$1++] = 0xd9;
1703
+ target[position$1++] = (recordId >> 8) | 0xe0;
1704
+ target[position$1++] = recordId & 0xff;
1705
+ transition = structures.transitions;
1706
+ for (let i = 0; i < length; i++) {
1707
+ if (transition[RECORD_SYMBOL] === undefined || (transition[RECORD_SYMBOL] & 0x100000))
1708
+ transition[RECORD_SYMBOL] = recordId;
1709
+ transition = transition[keys[i]];
1710
+ }
1711
+ transition[RECORD_SYMBOL] = recordId | 0x100000; // indicates it is a extendable terminal
1464
1712
  hasSharedUpdate = true;
1465
1713
  } else {
1466
- target[position$1++] = 0xd8;
1467
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1714
+ transition[RECORD_SYMBOL] = recordId;
1715
+ targetView.setUint32(position$1, 0xd9dfff00); // tag two byte, then record definition id
1716
+ position$1 += 3;
1468
1717
  if (newTransitions)
1469
1718
  transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
1470
1719
  // record the removal of the id, we can maintain our shared structure
@@ -1472,13 +1721,9 @@
1472
1721
  recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined; // we are cycling back through, and have to remove old ones
1473
1722
  recordIdsToRemove.push(transition);
1474
1723
  writeArrayHeader(length + 2);
1724
+ encode(0xe000 + recordId);
1475
1725
  encode(keys);
1476
- target[position$1++] = 0x19; // uint16
1477
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1478
- target[position$1++] = recordId;
1479
- // now write the values
1480
- for (let i =0; i < length; i++)
1481
- encode(object[keys[i]]);
1726
+ for (let v of Object.values(object)) encode(v);
1482
1727
  return
1483
1728
  }
1484
1729
  }
@@ -1487,8 +1732,9 @@
1487
1732
  } else {
1488
1733
  writeArrayHeader(length);
1489
1734
  }
1490
- for (let i =0; i < length; i++)
1491
- encode(object[keys[i]]);
1735
+ for (let key in object)
1736
+ if (safePrototype || object.hasOwnProperty(key))
1737
+ encode(object[key]);
1492
1738
  };
1493
1739
  const makeRoom = (end) => {
1494
1740
  let newSize;
@@ -1497,7 +1743,7 @@
1497
1743
  if ((end - start) > MAX_BUFFER_SIZE)
1498
1744
  throw new Error('Encoded buffer would be larger than maximum buffer size')
1499
1745
  newSize = Math.min(MAX_BUFFER_SIZE,
1500
- Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x1000000) / 0x1000) * 0x1000);
1746
+ Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000);
1501
1747
  } else // faster handling for smaller buffers
1502
1748
  newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12;
1503
1749
  let newBuffer = new ByteArrayAllocate(newSize);
@@ -1518,6 +1764,40 @@
1518
1764
  targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
1519
1765
  position$1 = 0;
1520
1766
  }
1767
+ clearSharedData() {
1768
+ if (this.structures)
1769
+ this.structures = [];
1770
+ if (this.sharedValues)
1771
+ this.sharedValues = undefined;
1772
+ }
1773
+ updateSharedData() {
1774
+ let lastVersion = this.sharedVersion || 0;
1775
+ this.sharedVersion = lastVersion + 1;
1776
+ let structuresCopy = this.structures.slice(0);
1777
+ let sharedData = new SharedData(structuresCopy, this.sharedValues, this.sharedVersion);
1778
+ let saveResults = this.saveShared(sharedData,
1779
+ existingShared => (existingShared && existingShared.version || 0) == lastVersion);
1780
+ if (saveResults === false) {
1781
+ // get updated structures and try again if the update failed
1782
+ sharedData = this.getShared() || {};
1783
+ this.structures = sharedData.structures || [];
1784
+ this.sharedValues = sharedData.packedValues;
1785
+ this.sharedVersion = sharedData.version;
1786
+ this.structures.nextId = this.structures.length;
1787
+ } else {
1788
+ // restore structures
1789
+ structuresCopy.forEach((structure, i) => this.structures[i] = structure);
1790
+ }
1791
+ // 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
1792
+ return saveResults
1793
+ }
1794
+ }
1795
+ class SharedData {
1796
+ constructor(structures, values, version) {
1797
+ this.structures = structures;
1798
+ this.packedValues = values;
1799
+ this.version = version;
1800
+ }
1521
1801
  }
1522
1802
 
1523
1803
  function writeArrayHeader(length) {
@@ -1587,9 +1867,11 @@
1587
1867
  }
1588
1868
  }
1589
1869
 
1590
- extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
1591
- Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array, BigUint64Array, Int8Array, Int16Array, Int32Array, BigInt64Array,
1592
- Float32Array, Float64Array];
1870
+ extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
1871
+ Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
1872
+ typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
1873
+ typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
1874
+ Float32Array, Float64Array, SharedData];
1593
1875
 
1594
1876
  //Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
1595
1877
  extensions = [{
@@ -1624,6 +1906,13 @@
1624
1906
  encode(regex, encode) {
1625
1907
  encode([ 'RegExp', regex.source, regex.flags ]);
1626
1908
  }
1909
+ }, {
1910
+ getTag(tag) {
1911
+ return tag.tag
1912
+ },
1913
+ encode(tag, encode) {
1914
+ encode(tag.value);
1915
+ }
1627
1916
  }, {
1628
1917
  encode(arrayBuffer, encode, makeRoom) {
1629
1918
  writeBuffer(arrayBuffer, makeRoom);
@@ -1642,7 +1931,35 @@
1642
1931
  typedArrayEncoder(78),
1643
1932
  typedArrayEncoder(79),
1644
1933
  typedArrayEncoder(81),
1645
- typedArrayEncoder(82)];
1934
+ typedArrayEncoder(82),
1935
+ {
1936
+ encode(sharedData, encode) { // write SharedData
1937
+ let packedValues = sharedData.packedValues || [];
1938
+ let sharedStructures = sharedData.structures || [];
1939
+ if (packedValues.values.length > 0) {
1940
+ target[position$1++] = 0xd8; // one-byte tag
1941
+ target[position$1++] = 51; // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
1942
+ writeArrayHeader(4);
1943
+ let valuesArray = packedValues.values;
1944
+ encode(valuesArray);
1945
+ writeArrayHeader(0); // prefixes
1946
+ writeArrayHeader(0); // suffixes
1947
+ packedObjectMap = Object.create(sharedPackedObjectMap || null);
1948
+ for (let i = 0, l = valuesArray.length; i < l; i++) {
1949
+ packedObjectMap[valuesArray[i]] = i;
1950
+ }
1951
+ }
1952
+ if (sharedStructures) {
1953
+ targetView.setUint32(position$1, 0xd9dffe00);
1954
+ position$1 += 3;
1955
+ let definitions = sharedStructures.slice(0);
1956
+ definitions.unshift(0xe000);
1957
+ definitions.push(new Tag(sharedData.version, 0x53687264));
1958
+ encode(definitions);
1959
+ } else
1960
+ encode(new Tag(sharedData.version, 0x53687264));
1961
+ }
1962
+ }];
1646
1963
 
1647
1964
  function typedArrayEncoder(tag) {
1648
1965
  return {
@@ -1682,27 +1999,35 @@
1682
1999
  function insertIds(serialized, idsToInsert) {
1683
2000
  // insert the ids that need to be referenced for structured clones
1684
2001
  let nextId;
1685
- let distanceToMove = idsToInsert.length * 8;
2002
+ let distanceToMove = idsToInsert.length * 2;
1686
2003
  let lastEnd = serialized.length - distanceToMove;
1687
2004
  idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1);
2005
+ for (let id = 0; id < idsToInsert.length; id++) {
2006
+ let referee = idsToInsert[id];
2007
+ referee.id = id;
2008
+ for (let position of referee.references) {
2009
+ serialized[position++] = id >> 8;
2010
+ serialized[position] = id & 0xff;
2011
+ }
2012
+ }
1688
2013
  while (nextId = idsToInsert.pop()) {
1689
2014
  let offset = nextId.offset;
1690
- let id = nextId.id;
1691
2015
  serialized.copyWithin(offset + distanceToMove, offset, lastEnd);
1692
- distanceToMove -= 8;
2016
+ distanceToMove -= 2;
1693
2017
  let position = offset + distanceToMove;
1694
- serialized[position++] = 0xd9;
1695
- serialized[position++] = 40009 >> 8;
1696
- serialized[position++] = 40009 & 0xff;
1697
- serialized[position++] = 0x1a; // uint32
1698
- serialized[position++] = id >> 24;
1699
- serialized[position++] = (id >> 16) & 0xff;
1700
- serialized[position++] = (id >> 8) & 0xff;
1701
- serialized[position++] = id & 0xff;
2018
+ serialized[position++] = 0xd8;
2019
+ serialized[position++] = 28; // http://cbor.schmorp.de/value-sharing
1702
2020
  lastEnd = offset;
1703
2021
  }
1704
2022
  return serialized
1705
2023
  }
2024
+ function writeBundles(start, encode) {
2025
+ targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start + 1); // the offset to bundle
2026
+ let writeStrings = bundledStrings$1;
2027
+ bundledStrings$1 = null;
2028
+ encode(writeStrings[0]);
2029
+ encode(writeStrings[1]);
2030
+ }
1706
2031
 
1707
2032
  function addExtension$1(extension) {
1708
2033
  if (extension.Class) {
@@ -1716,7 +2041,8 @@
1716
2041
  let defaultEncoder = new Encoder({ useRecords: false });
1717
2042
  const encode = defaultEncoder.encode;
1718
2043
  const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
1719
- const REUSE_BUFFER_MODE = 1000;
2044
+ const REUSE_BUFFER_MODE = 512;
2045
+ const RESET_BUFFER_MODE = 1024;
1720
2046
 
1721
2047
  /**
1722
2048
  * Given an Iterable first argument, returns an Iterable where each value is encoded as a Buffer
@@ -1801,9 +2127,6 @@
1801
2127
  }
1802
2128
  }
1803
2129
 
1804
- const useRecords = false;
1805
- const mapsAsObjects = true;
1806
-
1807
2130
  exports.ALWAYS = ALWAYS;
1808
2131
  exports.DECIMAL_FIT = DECIMAL_FIT;
1809
2132
  exports.DECIMAL_ROUND = DECIMAL_ROUND;
@@ -1811,6 +2134,7 @@
1811
2134
  exports.Encoder = Encoder;
1812
2135
  exports.FLOAT32_OPTIONS = FLOAT32_OPTIONS;
1813
2136
  exports.NEVER = NEVER;
2137
+ exports.REUSE_BUFFER_MODE = REUSE_BUFFER_MODE;
1814
2138
  exports.Tag = Tag;
1815
2139
  exports.addExtension = addExtension$1;
1816
2140
  exports.clearSource = clearSource;
@@ -1819,9 +2143,8 @@
1819
2143
  exports.decodeMultiple = decodeMultiple;
1820
2144
  exports.encode = encode;
1821
2145
  exports.encodeIter = encodeIter;
1822
- exports.mapsAsObjects = mapsAsObjects;
2146
+ exports.isNativeAccelerationEnabled = isNativeAccelerationEnabled;
1823
2147
  exports.roundFloat32 = roundFloat32;
1824
- exports.useRecords = useRecords;
1825
2148
 
1826
2149
  Object.defineProperty(exports, '__esModule', { value: true });
1827
2150