cbor-x 0.9.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('fs')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'fs'], factory) :
4
- (global = global || self, factory(global.CBOR = {}, global.fs));
5
- }(this, (function (exports, fs) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = global || self, factory(global.CBOR = {}));
5
+ }(this, (function (exports) { 'use strict';
6
6
 
7
7
  let decoder;
8
8
  try {
@@ -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,11 @@
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 (currentDecoder.int64AsNumber) {
254
+ token = dataView.getUint32(position) * 0x100000000;
255
+ token += dataView.getUint32(position + 4);
256
+ } else
257
+ token = dataView.getBigUint64(position);
184
258
  position += 8;
185
259
  break
186
260
  case 0x1f:
@@ -188,6 +262,7 @@
188
262
  switch(majorType) {
189
263
  case 2: // byte string
190
264
  case 3: // text string
265
+ throw new Error('Indefinite length not supported for byte or text strings')
191
266
  case 4: // array
192
267
  let array = [];
193
268
  let value, i = 0;
@@ -199,8 +274,8 @@
199
274
  let key;
200
275
  if (currentDecoder.mapsAsObjects) {
201
276
  let object = {};
202
- while ((key = readKey()) != STOP_CODE)
203
- object[key] = read();
277
+ if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) object[currentDecoder.decodeKey(key)] = read();
278
+ else while ((key = read()) != STOP_CODE) object[key] = read();
204
279
  return object
205
280
  } else {
206
281
  if (restoreMapsAsObject) {
@@ -208,8 +283,8 @@
208
283
  restoreMapsAsObject = false;
209
284
  }
210
285
  let map = new Map();
211
- while ((key = read()) != STOP_CODE)
212
- map.set(key, read());
286
+ if (currentDecoder.keyMap) while((key = read()) != STOP_CODE) map.set(currentDecoder.decodeKey(key), read());
287
+ else while ((key = read()) != STOP_CODE) map.set(key, read());
213
288
  return map
214
289
  }
215
290
  case 7:
@@ -241,16 +316,15 @@
241
316
  return readFixedString(token)
242
317
  case 4: // array
243
318
  let array = new Array(token);
244
- for (let i = 0; i < token; i++) {
245
- array[i] = read();
246
- }
319
+ //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
320
+ //else
321
+ for (let i = 0; i < token; i++) array[i] = read();
247
322
  return array
248
323
  case 5: // map
249
324
  if (currentDecoder.mapsAsObjects) {
250
325
  let object = {};
251
- for (let i = 0; i < token; i++) {
252
- object[readKey()] = read();
253
- }
326
+ if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[currentDecoder.decodeKey(read())] = read();
327
+ else for (let i = 0; i < token; i++) object[read()] = read();
254
328
  return object
255
329
  } else {
256
330
  if (restoreMapsAsObject) {
@@ -258,63 +332,66 @@
258
332
  restoreMapsAsObject = false;
259
333
  }
260
334
  let map = new Map();
261
- for (let i = 0; i < token; i++) {
262
- map.set(read(), read());
263
- }
335
+ if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
336
+ else for (let i = 0; i < token; i++) map.set(read(), read());
264
337
  return map
265
338
  }
266
339
  case 6: // extension
267
- if ((token >> 8) == RECORD_TAG_ID) { // record structures
268
- let structure = currentStructures[token & 0xff];
340
+ if (token >= BUNDLED_STRINGS_ID) {
341
+ let structure = currentStructures[token & 0x1fff]; // check record structures first
342
+ // At some point we may provide an option for dynamic tag assignment with a range like token >= 8 && (token < 16 || (token > 0x80 && token < 0xc0) || (token > 0x130 && token < 0x4000))
269
343
  if (structure) {
270
- if (!structure.read)
271
- structure.read = createStructureReader(structure);
344
+ if (!structure.read) structure.read = createStructureReader(structure);
272
345
  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
346
+ }
347
+ if (token < 0x10000) {
348
+ if (token == RECORD_INLINE_ID) // we do a special check for this so that we can keep the currentExtensions as densely stored array (v8 stores arrays densely under about 3000 elements)
349
+ return recordDefinition(read())
350
+ else if (token == RECORD_DEFINITIONS_ID) {
351
+ let length = readJustLength();
352
+ let id = read();
353
+ for (let i = 2; i < length; i++) {
354
+ recordDefinition([id++, read()]);
355
+ }
356
+ return read()
357
+ } else if (token == BUNDLED_STRINGS_ID) {
358
+ return readBundleExt()
359
+ }
360
+ if (currentDecoder.getShared) {
361
+ loadShared();
362
+ structure = currentStructures[token & 0x1fff];
363
+ if (structure) {
364
+ if (!structure.read)
365
+ structure.read = createStructureReader(structure);
366
+ return structure.read()
367
+ }
305
368
  }
306
- return new Tag(input)
307
369
  }
308
370
  }
371
+ let extension = currentExtensions[token];
372
+ if (extension) {
373
+ if (extension.handlesRead)
374
+ return extension(read)
375
+ else
376
+ return extension(read())
377
+ } else {
378
+ let input = read();
379
+ for (let i = 0; i < currentExtensionRanges.length; i++) {
380
+ let value = currentExtensionRanges[i](token, input);
381
+ if (value !== undefined)
382
+ return value
383
+ }
384
+ return new Tag(input, token)
385
+ }
309
386
  case 7: // fixed value
310
387
  switch (token) {
311
388
  case 0x14: return false
312
389
  case 0x15: return true
313
390
  case 0x16: return null
314
391
  case 0x17: return; // undefined
315
- case 0x1f:
392
+ case 0x1f:
316
393
  default:
317
- let packedValue = packedValues[token];
394
+ let packedValue = (packedValues || getPackedValues())[token];
318
395
  if (packedValue !== undefined)
319
396
  return packedValue
320
397
  throw new Error('Unknown token ' + token)
@@ -330,42 +407,50 @@
330
407
  }
331
408
  const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/;
332
409
  function createStructureReader(structure) {
333
- let l = structure.length;
334
410
  function readObject() {
411
+ // get the array size from the header
412
+ let length = src[position++];
413
+ //let majorType = token >> 5
414
+ length = length & 0x1f;
415
+ if (length > 0x17) {
416
+ switch (length) {
417
+ case 0x18:
418
+ length = src[position++];
419
+ break
420
+ case 0x19:
421
+ length = dataView.getUint16(position);
422
+ position += 2;
423
+ break
424
+ case 0x1a:
425
+ length = dataView.getUint32(position);
426
+ position += 4;
427
+ break
428
+ default:
429
+ throw new Error('Expected array header, but got ' + src[position - 1])
430
+ }
431
+ }
335
432
  // 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()
433
+ if (this.objectLiteralSize === length) // we have a fast object literal reader, use it (assuming it is the right length)
434
+ return this.objectLiteral(read)
435
+ if (this.count++ == 3) { // create a fast reader
436
+ this.objectLiteralSize = length;
437
+ this.objectLiteral = currentDecoder.keyMap
438
+ ? new Function('r', 'return {' + this.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
439
+ : new Function('r', 'return {' + this.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}');
440
+ return this.objectLiteral(read)
339
441
  }
340
- readArrayHeader();
341
442
  let object = {};
342
- for (let i = 0; i < l; i++) {
343
- let key = structure[i];
344
- object[key] = read();
345
- }
443
+ if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read();
444
+ else for (let i = 0; i < length; i++) object[this[i]] = read();
346
445
  return object
347
446
  }
348
- readObject.count = 0;
447
+ structure.count = 0;
349
448
  return readObject
350
449
  }
351
450
 
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
451
  let readFixedString = readStringJS;
452
+
453
+ let isNativeAccelerationEnabled = false;
369
454
  function readStringJS(length) {
370
455
  let result;
371
456
  if (length < 16) {
@@ -594,76 +679,15 @@
594
679
  }
595
680
 
596
681
  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
682
 
660
683
  class Tag {
661
- constructor(value) {
684
+ constructor(value, tag) {
662
685
  this.value = value;
686
+ this.tag = tag;
663
687
  }
664
688
  }
665
689
 
666
- let glbl = typeof window == 'object' ? window : global;
690
+ let glbl = typeof self == 'object' ? self : global;
667
691
 
668
692
  currentExtensions[0] = (dateString) => {
669
693
  // string date extension
@@ -677,38 +701,62 @@
677
701
 
678
702
  currentExtensions[2] = (buffer) => {
679
703
  // bigint extension
680
- return new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0)
704
+ let value = BigInt(0);
705
+ for (let i = 0, l = buffer.byteLength; i < l; i++) {
706
+ value = BigInt(buffer[i]) + value << BigInt(8);
707
+ }
708
+ return value
681
709
  };
682
710
 
683
711
  currentExtensions[3] = (buffer) => {
684
712
  // negative bigint extension
685
- return BigInt(-1) - (new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0))
713
+ return BigInt(-1) - currentExtensions[2](buffer)
714
+ };
715
+ currentExtensions[4] = (fraction) => {
716
+ // best to reparse to maintain accuracy
717
+ return +(fraction[1] + 'e' + fraction[0])
718
+ };
719
+
720
+ currentExtensions[5] = (fraction) => {
721
+ // probably not sufficiently accurate
722
+ return fraction[1] * Math.exp(fraction[0] * Math.log(2))
686
723
  };
687
724
 
688
725
  // 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;
726
+ const recordDefinition = (definition) => {
727
+ let id = definition[0] - 0xe000;
728
+ let structure = definition[1];
729
+ currentStructures[id] = structure;
694
730
  structure.read = createStructureReader(structure);
695
731
  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
- }
732
+ if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
733
+ let key = currentDecoder.decodeKey(structure[i - 2]);
734
+ object[key] = definition[i];
735
+ }
736
+ else for (let i = 2,l = definition.length; i < l; i++) {
737
+ let key = structure[i - 2];
738
+ object[key] = definition[i];
739
+ }
700
740
  return object
701
741
  };
702
-
703
- recordDefinition.handlesRead = true;
704
- currentExtensions[RECORD_TAG_ID] = recordDefinition;
742
+ currentExtensions[LEGACY_RECORD_INLINE_ID] = recordDefinition;
743
+ currentExtensions[14] = (value) => {
744
+ if (bundledStrings)
745
+ return bundledStrings[0].slice(bundledStrings.position0, bundledStrings.position0 += value)
746
+ return new Tag(value, 14)
747
+ };
748
+ currentExtensions[15] = (value) => {
749
+ if (bundledStrings)
750
+ return bundledStrings[1].slice(bundledStrings.position1, bundledStrings.position1 += value)
751
+ return new Tag(value, 15)
752
+ };
705
753
 
706
754
  currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
707
755
  return (glbl[data[0]] || Error)(data[1], data[2])
708
756
  };
709
757
  const packedTable = (read) => {
710
758
  if (src[position++] != 0x84)
711
- throw new Error('Packed values structure must be followed by 4 element array')
759
+ throw new Error('Packed values structure must be followed by a 4 element array')
712
760
  let newPackedValues = read(); // packed values
713
761
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
714
762
  packedValues.prefixes = read();
@@ -719,15 +767,36 @@
719
767
  currentExtensions[51] = packedTable;
720
768
 
721
769
  currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
770
+ if (!packedValues) {
771
+ if (currentDecoder.getShared)
772
+ loadShared();
773
+ else
774
+ return new Tag(data, PACKED_REFERENCE_TAG_ID)
775
+ }
722
776
  if (typeof data == 'number')
723
777
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
724
778
  throw new Error('No support for non-integer packed references yet')
725
779
  };
780
+ currentExtensions[25] = (id) => {
781
+ return stringRefs[id]
782
+ };
783
+ currentExtensions[256] = (read) => {
784
+ stringRefs = [];
785
+ try {
786
+ return read()
787
+ } finally {
788
+ stringRefs = null;
789
+ }
790
+ };
791
+ currentExtensions[256].handlesRead = true;
726
792
 
727
- currentExtensions[40009] = (id) => {
728
- // id extension (for structured clones)
729
- if (!referenceMap)
793
+ currentExtensions[28] = (read) => {
794
+ // shareable http://cbor.schmorp.de/value-sharing (for structured clones)
795
+ if (!referenceMap) {
730
796
  referenceMap = new Map();
797
+ referenceMap.id = 0;
798
+ }
799
+ let id = referenceMap.id++;
731
800
  let token = src[position];
732
801
  let target;
733
802
  // TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
@@ -745,9 +814,10 @@
745
814
  refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
746
815
  return targetProperties // no cycle, can just use the returned read object
747
816
  };
817
+ currentExtensions[28].handlesRead = true;
748
818
 
749
- currentExtensions[40010] = (id) => {
750
- // pointer extension (for structured clones)
819
+ currentExtensions[29] = (id) => {
820
+ // sharedref http://cbor.schmorp.de/value-sharing (for structured clones)
751
821
  let refEntry = referenceMap.get(id);
752
822
  refEntry.used = true;
753
823
  return refEntry.target
@@ -770,19 +840,36 @@
770
840
  return a.concat(b)
771
841
  return Object.assign({}, a, b)
772
842
  }
843
+ function getPackedValues() {
844
+ if (!packedValues) {
845
+ if (currentDecoder.getShared)
846
+ loadShared();
847
+ else
848
+ throw new Error('No packed values available')
849
+ }
850
+ return packedValues
851
+ }
852
+ const SHARED_DATA_TAG_ID = 0x53687264; // ascii 'Shrd'
773
853
  currentExtensionRanges.push((tag, input) => {
774
854
  if (tag >= 225 && tag <= 255)
775
- return combine(packedTable.prefixes[tag - 224], input)
855
+ return combine(getPackedValues().prefixes[tag - 224], input)
776
856
  if (tag >= 28704 && tag <= 32767)
777
- return combine(packedTable.prefixes[tag - 28672], input)
857
+ return combine(getPackedValues().prefixes[tag - 28672], input)
778
858
  if (tag >= 1879052288 && tag <= 2147483647)
779
- return combine(packedTable.prefixes[tag - 1879048192], input)
859
+ return combine(getPackedValues().prefixes[tag - 1879048192], input)
780
860
  if (tag >= 216 && tag <= 223)
781
- return combine(input, packedTable.suffixes[tag - 216])
861
+ return combine(input, getPackedValues().suffixes[tag - 216])
782
862
  if (tag >= 27647 && tag <= 28671)
783
- return combine(input, packedTable.suffixes[tag - 27639])
863
+ return combine(input, getPackedValues().suffixes[tag - 27639])
784
864
  if (tag >= 1811940352 && tag <= 1879048191)
785
- return combine(input, packedTable.suffixes[tag - 1811939328])
865
+ return combine(input, getPackedValues().suffixes[tag - 1811939328])
866
+ if (tag == SHARED_DATA_TAG_ID) {// we do a special check for this so that we can keep the currentExtensions as densely stored array (v8 stores arrays densely under about 3000 elements)
867
+ return {
868
+ packedValues: packedValues,
869
+ structures: currentStructures.slice(0),
870
+ version: input,
871
+ }
872
+ }
786
873
  });
787
874
 
788
875
  const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array');
@@ -799,6 +886,61 @@
799
886
  };
800
887
  }
801
888
 
889
+ function readBundleExt() {
890
+ let length = readJustLength();
891
+ let bundlePosition = position + read();
892
+ for (let i = 2; i < length; i++) {
893
+ // skip past bundles that were already read
894
+ let bundleLength = readJustLength(); // this will increment position, so must add to position afterwards
895
+ position += bundleLength;
896
+ }
897
+ let dataPosition = position;
898
+ position = bundlePosition;
899
+ bundledStrings = [readStringJS(readJustLength()), readStringJS(readJustLength())];
900
+ bundledStrings.position0 = 0;
901
+ bundledStrings.position1 = 0;
902
+ bundledStrings.postBundlePosition = position;
903
+ position = dataPosition;
904
+ return read()
905
+ }
906
+
907
+ function readJustLength() {
908
+ let token = src[position++] & 0x1f;
909
+ if (token > 0x17) {
910
+ switch (token) {
911
+ case 0x18:
912
+ token = src[position++];
913
+ break
914
+ case 0x19:
915
+ token = dataView.getUint16(position);
916
+ position += 2;
917
+ break
918
+ case 0x1a:
919
+ token = dataView.getUint32(position);
920
+ position += 4;
921
+ break
922
+ }
923
+ }
924
+ return token
925
+ }
926
+
927
+ function loadShared() {
928
+ if (currentDecoder.getShared) {
929
+ let sharedData = saveState(() => {
930
+ // save the state in case getShared modifies our buffer
931
+ src = null;
932
+ return currentDecoder.getShared()
933
+ }) || {};
934
+ let updatedStructures = sharedData.structures || [];
935
+ currentDecoder.sharedVersion = sharedData.version;
936
+ packedValues = currentDecoder.sharedValues = sharedData.packedValues;
937
+ if (currentStructures === true)
938
+ currentDecoder.structures = currentStructures = updatedStructures;
939
+ else
940
+ currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures));
941
+ }
942
+ }
943
+
802
944
  function saveState(callback) {
803
945
  let savedSrcEnd = srcEnd;
804
946
  let savedPosition = position;
@@ -806,6 +948,8 @@
806
948
  let savedSrcStringEnd = srcStringEnd;
807
949
  let savedSrcString = srcString;
808
950
  let savedReferenceMap = referenceMap;
951
+ let savedBundledStrings = bundledStrings;
952
+
809
953
  // TODO: We may need to revisit this if we do more external calls to user code (since it could be slow)
810
954
  let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed
811
955
  let savedStructures = currentStructures;
@@ -818,6 +962,7 @@
818
962
  srcStringEnd = savedSrcStringEnd;
819
963
  srcString = savedSrcString;
820
964
  referenceMap = savedReferenceMap;
965
+ bundledStrings = savedBundledStrings;
821
966
  src = savedSrc;
822
967
  sequentialMode = savedSequentialMode;
823
968
  currentStructures = savedStructures;
@@ -864,13 +1009,15 @@
864
1009
  const hasNodeBuffer = typeof Buffer !== 'undefined';
865
1010
  const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array;
866
1011
  const ByteArray = hasNodeBuffer ? Buffer : Uint8Array;
867
- const RECORD_STARTING_ID_PREFIX = 0x69; // tag 105/0x69
868
1012
  const MAX_STRUCTURES = 0x100;
869
1013
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
870
1014
  let target;
871
1015
  let targetView;
872
1016
  let position$1 = 0;
873
1017
  let safeEnd;
1018
+ let bundledStrings$1 = null;
1019
+ const MAX_BUNDLE_SIZE = 0xf000;
1020
+ const hasNonLatin = /[\u0080-\uFFFF]/;
874
1021
  const RECORD_SYMBOL = Symbol('record-id');
875
1022
  class Encoder extends Decoder {
876
1023
  constructor(options) {
@@ -882,7 +1029,6 @@
882
1029
  let structures;
883
1030
  let referenceMap;
884
1031
  options = options || {};
885
- let lastSharedStructuresLength = 0;
886
1032
  let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
887
1033
  return target.utf8Write(string, position, maxBytes)
888
1034
  } : (textEncoder && textEncoder.encodeInto) ?
@@ -891,12 +1037,14 @@
891
1037
  } : false;
892
1038
 
893
1039
  let encoder = this;
894
- let maxSharedStructures = 64;
1040
+ let maxSharedStructures = options.maxSharedStructures || 128;
895
1041
  let isSequential = options.sequential;
896
1042
  if (isSequential) {
897
1043
  maxSharedStructures = 0;
898
1044
  this.structures = [];
899
1045
  }
1046
+ if (this.saveStructures)
1047
+ this.saveShared = this.saveStructures;
900
1048
  let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues;
901
1049
  let sharedPackedObjectMap;
902
1050
  if (sharedValues) {
@@ -904,13 +1052,29 @@
904
1052
  for (let i = 0, l = sharedValues.length; i < l; i++) {
905
1053
  sharedPackedObjectMap[sharedValues[i]] = i;
906
1054
  }
907
-
908
1055
  }
909
1056
  let recordIdsToRemove = [];
910
1057
  let transitionsCount = 0;
911
1058
  let serializationsSinceTransitionRebuild = 0;
912
-
913
- this.encode = function(value, encodeOptions) {
1059
+
1060
+ this.mapEncode = function(value, encodeOptions) {
1061
+ // Experimental support for premapping keys using _keyMap instad of keyMap - not optiimised yet)
1062
+ if (this._keyMap && !this._mapped) {
1063
+ //console.log('encoding ', value)
1064
+ switch (value.constructor.name) {
1065
+ case 'Array':
1066
+ value = value.map(r => this.encodeKeys(r));
1067
+ break
1068
+ //case 'Map':
1069
+ // value = this.encodeKeys(value)
1070
+ // break
1071
+ }
1072
+ //this._mapped = true
1073
+ }
1074
+ return this.encode(value, encodeOptions)
1075
+ };
1076
+
1077
+ this.encode = function(value, encodeOptions) {
914
1078
  if (!target) {
915
1079
  target = new ByteArrayAllocate(8192);
916
1080
  targetView = new DataView(target.buffer, 0, 8192);
@@ -927,10 +1091,25 @@
927
1091
  position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
928
1092
  start = position$1;
929
1093
  referenceMap = encoder.structuredClone ? new Map() : null;
1094
+ if (encoder.bundleStrings && typeof value !== 'string') {
1095
+ bundledStrings$1 = [];
1096
+ bundledStrings$1.size = Infinity; // force a new bundle start on first string
1097
+ } else
1098
+ bundledStrings$1 = null;
1099
+
930
1100
  sharedStructures = encoder.structures;
931
1101
  if (sharedStructures) {
932
- if (sharedStructures.uninitialized)
933
- encoder.structures = sharedStructures = encoder.getStructures();
1102
+ if (sharedStructures.uninitialized) {
1103
+ let sharedData = encoder.getShared() || {};
1104
+ encoder.structures = sharedStructures = sharedData.structures || [];
1105
+ encoder.sharedVersion = sharedData.version;
1106
+ let sharedValues = encoder.sharedValues = sharedData.packedValues;
1107
+ if (sharedValues) {
1108
+ sharedPackedObjectMap = {};
1109
+ for (let i = 0, l = sharedValues.length; i < l; i++)
1110
+ sharedPackedObjectMap[sharedValues[i]] = i;
1111
+ }
1112
+ }
934
1113
  let sharedStructuresLength = sharedStructures.length;
935
1114
  if (sharedStructuresLength > maxSharedStructures && !isSequential)
936
1115
  sharedStructuresLength = maxSharedStructures;
@@ -939,6 +1118,7 @@
939
1118
  sharedStructures.transitions = Object.create(null);
940
1119
  for (let i = 0; i < sharedStructuresLength; i++) {
941
1120
  let keys = sharedStructures[i];
1121
+ //console.log('shared struct keys:', keys)
942
1122
  if (!keys)
943
1123
  continue
944
1124
  let nextTransition, transition = sharedStructures.transitions;
@@ -952,7 +1132,6 @@
952
1132
  }
953
1133
  transition[RECORD_SYMBOL] = i;
954
1134
  }
955
- lastSharedStructuresLength = sharedStructures.length;
956
1135
  }
957
1136
  if (!isSequential)
958
1137
  sharedStructures.nextId = sharedStructuresLength;
@@ -985,9 +1164,12 @@
985
1164
  }
986
1165
  try {
987
1166
  encode(value);
1167
+ if (bundledStrings$1) {
1168
+ writeBundles(start, encode);
1169
+ }
988
1170
  encoder.offset = position$1; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
989
1171
  if (referenceMap && referenceMap.idsToInsert) {
990
- position$1 += referenceMap.idsToInsert.length * 8;
1172
+ position$1 += referenceMap.idsToInsert.length * 2;
991
1173
  if (position$1 > safeEnd)
992
1174
  makeRoom(position$1);
993
1175
  encoder.offset = position$1;
@@ -995,12 +1177,12 @@
995
1177
  referenceMap = null;
996
1178
  return serialized
997
1179
  }
998
- if (encodeOptions === REUSE_BUFFER_MODE) {
1180
+ if (encodeOptions & REUSE_BUFFER_MODE) {
999
1181
  target.start = start;
1000
1182
  target.end = position$1;
1001
1183
  return target
1002
1184
  }
1003
- return target.subarray(start, position$1) // position can change if we call pack again in saveStructures, so we get the buffer now
1185
+ return target.subarray(start, position$1) // position can change if we call pack again in saveShared, so we get the buffer now
1004
1186
  } finally {
1005
1187
  if (sharedStructures) {
1006
1188
  if (serializationsSinceTransitionRebuild < 10)
@@ -1018,34 +1200,27 @@
1018
1200
  }
1019
1201
  recordIdsToRemove = [];
1020
1202
  }
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
1203
+ }
1204
+ if (hasSharedUpdate && encoder.saveShared) {
1205
+ if (encoder.structures.length > maxSharedStructures) {
1206
+ encoder.structures = encoder.structures.slice(0, maxSharedStructures);
1039
1207
  }
1208
+ // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
1209
+ let returnBuffer = target.subarray(start, position$1);
1210
+ if (encoder.updateSharedData() === false)
1211
+ return encoder.encode(value) // re-encode if it fails
1212
+ return returnBuffer
1040
1213
  }
1214
+ if (encodeOptions & RESET_BUFFER_MODE)
1215
+ position$1 = start;
1041
1216
  }
1042
1217
  };
1043
1218
  this.findCommonStringsToPack = () => {
1044
1219
  samplingPackedValues = new Map();
1045
1220
  if (!sharedPackedObjectMap)
1046
1221
  sharedPackedObjectMap = Object.create(null);
1047
- return ({ threshold }) => {
1048
- threshold = threshold || 4;
1222
+ return (options) => {
1223
+ let threshold = options && options.threshold || 4;
1049
1224
  let position = this.pack ? options.maxPrivatePackedValues || 16 : 0;
1050
1225
  if (!sharedValues)
1051
1226
  sharedValues = this.sharedValues = [];
@@ -1056,6 +1231,7 @@
1056
1231
  hasSharedUpdate = true;
1057
1232
  }
1058
1233
  }
1234
+ while (this.saveShared && this.updateSharedData() === false) {}
1059
1235
  samplingPackedValues = null;
1060
1236
  }
1061
1237
  };
@@ -1104,6 +1280,33 @@
1104
1280
  }
1105
1281
  }
1106
1282
  let strLength = value.length;
1283
+ if (bundledStrings$1 && strLength >= 4 && strLength < 0x400) {
1284
+ if ((bundledStrings$1.size += strLength) > MAX_BUNDLE_SIZE) {
1285
+ let extStart;
1286
+ let maxBytes = (bundledStrings$1[0] ? bundledStrings$1[0].length * 3 + bundledStrings$1[1].length : 0) + 10;
1287
+ if (position$1 + maxBytes > safeEnd)
1288
+ target = makeRoom(position$1 + maxBytes);
1289
+ target[position$1++] = 0xd9; // tag 16-bit
1290
+ target[position$1++] = 0xdf; // tag 0xdff9
1291
+ target[position$1++] = 0xf9;
1292
+ // TODO: If we only have one bundle with any string data, only write one string bundle
1293
+ target[position$1++] = bundledStrings$1.position ? 0x84 : 0x82; // array of 4 or 2 elements depending on if we write bundles
1294
+ target[position$1++] = 0x1a; // 32-bit unsigned int
1295
+ extStart = position$1 - start;
1296
+ position$1 += 4; // reserve for writing bundle reference
1297
+ if (bundledStrings$1.position) {
1298
+ writeBundles(start, encode); // write the last bundles
1299
+ }
1300
+ bundledStrings$1 = ['', '']; // create new ones
1301
+ bundledStrings$1.size = 0;
1302
+ bundledStrings$1.position = extStart;
1303
+ }
1304
+ let twoByte = hasNonLatin.test(value);
1305
+ bundledStrings$1[twoByte ? 0 : 1] += value;
1306
+ target[position$1++] = twoByte ? 0xce : 0xcf;
1307
+ encode(strLength);
1308
+ return
1309
+ }
1107
1310
  let headerSize;
1108
1311
  // first we estimate the header size, so we can write to the correct location
1109
1312
  if (strLength < 0x20) {
@@ -1230,16 +1433,16 @@
1230
1433
  if (referenceMap) {
1231
1434
  let referee = referenceMap.get(value);
1232
1435
  if (referee) {
1233
- if (!referee.id) {
1436
+ target[position$1++] = 0xd8;
1437
+ target[position$1++] = 29; // http://cbor.schmorp.de/value-sharing
1438
+ target[position$1++] = 0x19; // 16-bit uint
1439
+ if (!referee.references) {
1234
1440
  let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = []);
1235
- referee.id = idsToInsert.push(referee);
1441
+ referee.references = [];
1442
+ idsToInsert.push(referee);
1236
1443
  }
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;
1444
+ referee.references.push(position$1 - start);
1445
+ position$1 += 2; // TODO: also support 32-bit
1243
1446
  return
1244
1447
  } else
1245
1448
  referenceMap.set(value, { offset: position$1 - start });
@@ -1279,16 +1482,23 @@
1279
1482
  targetView.setUint32(position$1, length);
1280
1483
  position$1 += 4;
1281
1484
  }
1282
- for (let [ key, entryValue ] of value) {
1283
- encode(key);
1284
- encode(entryValue);
1485
+ if (encoder.keyMap) {
1486
+ for (let [ key, entryValue ] of value) {
1487
+ encode(encoder.encodeKey(key));
1488
+ encode(entryValue);
1489
+ }
1490
+ } else {
1491
+ for (let [ key, entryValue ] of value) {
1492
+ encode(key);
1493
+ encode(entryValue);
1494
+ }
1285
1495
  }
1286
- } else {
1496
+ } else {
1287
1497
  for (let i = 0, l = extensions.length; i < l; i++) {
1288
1498
  let extensionClass = extensionClasses[i];
1289
1499
  if (value instanceof extensionClass) {
1290
1500
  let extension = extensions[i];
1291
- let tag = extension.tag;
1501
+ let tag = extension.tag || extension.getTag && extension.getTag(value);
1292
1502
  if (tag < 0x18) {
1293
1503
  target[position$1++] = 0xc0 | tag;
1294
1504
  } else if (tag < 0x100) {
@@ -1350,6 +1560,7 @@
1350
1560
  const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
1351
1561
  // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
1352
1562
  let keys = Object.keys(object);
1563
+ let vals = Object.values(object);
1353
1564
  let length = keys.length;
1354
1565
  if (length < 0x18) {
1355
1566
  target[position$1++] = 0xa0 | length;
@@ -1365,10 +1576,16 @@
1365
1576
  targetView.setUint32(position$1, length);
1366
1577
  position$1 += 4;
1367
1578
  }
1368
- let key;
1369
- for (let i = 0; i < length; i++) {
1370
- encode(key = keys[i]);
1371
- encode(object[key]);
1579
+ if (encoder.keyMap) {
1580
+ for (let i = 0; i < length; i++) {
1581
+ encode(encodeKey(keys[i]));
1582
+ encode(vals[i]);
1583
+ }
1584
+ } else {
1585
+ for (let i = 0; i < length; i++) {
1586
+ encode(keys[i]);
1587
+ encode(vals[i]);
1588
+ }
1372
1589
  }
1373
1590
  } :
1374
1591
  (object, safePrototype) => {
@@ -1376,12 +1593,18 @@
1376
1593
  let objectOffset = position$1 - start;
1377
1594
  position$1 += 2;
1378
1595
  let size = 0;
1379
- for (let key in object) {
1380
- if (safePrototype || object.hasOwnProperty(key)) {
1381
- encode(key);
1596
+ if (encoder.keyMap) {
1597
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1598
+ encode(encoder.encodeKey(key));
1382
1599
  encode(object[key]);
1383
1600
  size++;
1384
1601
  }
1602
+ } else {
1603
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1604
+ encode(key);
1605
+ encode(object[key]);
1606
+ size++;
1607
+ }
1385
1608
  }
1386
1609
  target[objectOffset++ + start] = size >> 8;
1387
1610
  target[objectOffset + start] = size & 0xff;
@@ -1429,11 +1652,16 @@
1429
1652
  }*/
1430
1653
  (object) => {
1431
1654
  let keys = Object.keys(object);
1655
+ let vals = Object.values(object);
1656
+ if (this.keyMap) keys = keys.map(k => this.encodeKey(k));
1432
1657
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1433
1658
  let newTransitions = 0;
1434
1659
  let length = keys.length;
1660
+ //let parentRecordId
1435
1661
  for (let i = 0; i < length; i++) {
1662
+ //if (!parentRecordId)
1436
1663
  let key = keys[i];
1664
+ // parentRecordId = transition[RECORD_SYMBOL]
1437
1665
  nextTransition = transition[key];
1438
1666
  if (!nextTransition) {
1439
1667
  nextTransition = transition[key] = Object.create(null);
@@ -1443,9 +1671,9 @@
1443
1671
  }
1444
1672
  let recordId = transition[RECORD_SYMBOL];
1445
1673
  if (recordId !== undefined) {
1446
- target[position$1++] = 0xd9; // tag two byte
1447
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1448
- target[position$1++] = recordId;
1674
+ target[position$1++] = 0xd9;
1675
+ target[position$1++] = (recordId >> 8) | 0xe0;
1676
+ target[position$1++] = recordId & 0xff;
1449
1677
  } else {
1450
1678
  recordId = structures.nextId++;
1451
1679
  if (!recordId) {
@@ -1458,13 +1686,13 @@
1458
1686
  transition[RECORD_SYMBOL] = recordId;
1459
1687
  structures[recordId] = keys;
1460
1688
  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
1689
+ target[position$1++] = 0xd9;
1690
+ target[position$1++] = (recordId >> 8) | 0xe0;
1691
+ target[position$1++] = recordId & 0xff;
1464
1692
  hasSharedUpdate = true;
1465
1693
  } else {
1466
- target[position$1++] = 0xd8;
1467
- target[position$1++] = RECORD_STARTING_ID_PREFIX;
1694
+ targetView.setUint32(position$1, 0xd9dfff00); // tag two byte, then record definition id
1695
+ position$1 += 3;
1468
1696
  if (newTransitions)
1469
1697
  transitionsCount += serializationsSinceTransitionRebuild * newTransitions;
1470
1698
  // record the removal of the id, we can maintain our shared structure
@@ -1472,13 +1700,9 @@
1472
1700
  recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined; // we are cycling back through, and have to remove old ones
1473
1701
  recordIdsToRemove.push(transition);
1474
1702
  writeArrayHeader(length + 2);
1703
+ encode(0xe000 + recordId);
1475
1704
  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]]);
1705
+ for (let v of Object.values(object)) encode(v);
1482
1706
  return
1483
1707
  }
1484
1708
  }
@@ -1487,8 +1711,7 @@
1487
1711
  } else {
1488
1712
  writeArrayHeader(length);
1489
1713
  }
1490
- for (let i =0; i < length; i++)
1491
- encode(object[keys[i]]);
1714
+ for (let i =0; i < length; i++) encode(vals[i]);
1492
1715
  };
1493
1716
  const makeRoom = (end) => {
1494
1717
  let newSize;
@@ -1497,7 +1720,7 @@
1497
1720
  if ((end - start) > MAX_BUFFER_SIZE)
1498
1721
  throw new Error('Encoded buffer would be larger than maximum buffer size')
1499
1722
  newSize = Math.min(MAX_BUFFER_SIZE,
1500
- Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x1000000) / 0x1000) * 0x1000);
1723
+ Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000);
1501
1724
  } else // faster handling for smaller buffers
1502
1725
  newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12;
1503
1726
  let newBuffer = new ByteArrayAllocate(newSize);
@@ -1518,6 +1741,33 @@
1518
1741
  targetView = new DataView(target.buffer, target.byteOffset, target.byteLength);
1519
1742
  position$1 = 0;
1520
1743
  }
1744
+ clearSharedData() {
1745
+ if (this.structures)
1746
+ this.structures = [];
1747
+ if (this.sharedValues)
1748
+ this.sharedValues = undefined;
1749
+ }
1750
+ updateSharedData() {
1751
+ let lastVersion = this.sharedVersion || 0;
1752
+ this.sharedVersion = lastVersion + 1;
1753
+ let saveResults = this.saveShared(new SharedData(this.structures, this.sharedValues, this.sharedVersion),
1754
+ existingShared => (existingShared && existingShared.version || 0) == lastVersion);
1755
+ if (saveResults === false) {
1756
+ // get updated structures and try again if the update failed
1757
+ let sharedData = this.getShared() || {};
1758
+ this.structures = sharedData.structures || [];
1759
+ this.sharedValues = sharedData.packedValues;
1760
+ this.sharedVersion = sharedData.version;
1761
+ }
1762
+ return saveResults
1763
+ }
1764
+ }
1765
+ class SharedData {
1766
+ constructor(structures, values, version) {
1767
+ this.structures = structures;
1768
+ this.packedValues = values;
1769
+ this.version = version;
1770
+ }
1521
1771
  }
1522
1772
 
1523
1773
  function writeArrayHeader(length) {
@@ -1587,9 +1837,11 @@
1587
1837
  }
1588
1838
  }
1589
1839
 
1590
- extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
1591
- Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array, BigUint64Array, Int8Array, Int16Array, Int32Array, BigInt64Array,
1592
- Float32Array, Float64Array];
1840
+ extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
1841
+ Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
1842
+ typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
1843
+ typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
1844
+ Float32Array, Float64Array, SharedData];
1593
1845
 
1594
1846
  //Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
1595
1847
  extensions = [{
@@ -1624,6 +1876,13 @@
1624
1876
  encode(regex, encode) {
1625
1877
  encode([ 'RegExp', regex.source, regex.flags ]);
1626
1878
  }
1879
+ }, {
1880
+ getTag(tag) {
1881
+ return tag.tag
1882
+ },
1883
+ encode(tag, encode) {
1884
+ encode(tag.value);
1885
+ }
1627
1886
  }, {
1628
1887
  encode(arrayBuffer, encode, makeRoom) {
1629
1888
  writeBuffer(arrayBuffer, makeRoom);
@@ -1642,7 +1901,35 @@
1642
1901
  typedArrayEncoder(78),
1643
1902
  typedArrayEncoder(79),
1644
1903
  typedArrayEncoder(81),
1645
- typedArrayEncoder(82)];
1904
+ typedArrayEncoder(82),
1905
+ {
1906
+ encode(sharedData, encode) { // write SharedData
1907
+ let packedValues = sharedData.packedValues || [];
1908
+ let sharedStructures = sharedData.structures || [];
1909
+ if (packedValues.values.length > 0) {
1910
+ target[position$1++] = 0xd8; // one-byte tag
1911
+ target[position$1++] = 51; // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
1912
+ writeArrayHeader(4);
1913
+ let valuesArray = packedValues.values;
1914
+ encode(valuesArray);
1915
+ writeArrayHeader(0); // prefixes
1916
+ writeArrayHeader(0); // suffixes
1917
+ packedObjectMap = Object.create(sharedPackedObjectMap || null);
1918
+ for (let i = 0, l = valuesArray.length; i < l; i++) {
1919
+ packedObjectMap[valuesArray[i]] = i;
1920
+ }
1921
+ }
1922
+ if (sharedStructures) {
1923
+ targetView.setUint32(position$1, 0xd9dffe00);
1924
+ position$1 += 3;
1925
+ let definitions = sharedStructures.slice(0);
1926
+ definitions.unshift(0xe000);
1927
+ definitions.push(new Tag(sharedData.version, 0x53687264));
1928
+ encode(definitions);
1929
+ } else
1930
+ encode(new Tag(sharedData.version, 0x53687264));
1931
+ }
1932
+ }];
1646
1933
 
1647
1934
  function typedArrayEncoder(tag) {
1648
1935
  return {
@@ -1682,27 +1969,35 @@
1682
1969
  function insertIds(serialized, idsToInsert) {
1683
1970
  // insert the ids that need to be referenced for structured clones
1684
1971
  let nextId;
1685
- let distanceToMove = idsToInsert.length * 8;
1972
+ let distanceToMove = idsToInsert.length * 2;
1686
1973
  let lastEnd = serialized.length - distanceToMove;
1687
1974
  idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1);
1975
+ for (let id = 0; id < idsToInsert.length; id++) {
1976
+ let referee = idsToInsert[id];
1977
+ referee.id = id;
1978
+ for (let position of referee.references) {
1979
+ serialized[position++] = id >> 8;
1980
+ serialized[position] = id & 0xff;
1981
+ }
1982
+ }
1688
1983
  while (nextId = idsToInsert.pop()) {
1689
1984
  let offset = nextId.offset;
1690
- let id = nextId.id;
1691
1985
  serialized.copyWithin(offset + distanceToMove, offset, lastEnd);
1692
- distanceToMove -= 8;
1986
+ distanceToMove -= 2;
1693
1987
  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;
1988
+ serialized[position++] = 0xd8;
1989
+ serialized[position++] = 28; // http://cbor.schmorp.de/value-sharing
1702
1990
  lastEnd = offset;
1703
1991
  }
1704
1992
  return serialized
1705
1993
  }
1994
+ function writeBundles(start, encode) {
1995
+ targetView.setUint32(bundledStrings$1.position + start, position$1 - bundledStrings$1.position - start + 1); // the offset to bundle
1996
+ let writeStrings = bundledStrings$1;
1997
+ bundledStrings$1 = null;
1998
+ encode(writeStrings[0]);
1999
+ encode(writeStrings[1]);
2000
+ }
1706
2001
 
1707
2002
  function addExtension$1(extension) {
1708
2003
  if (extension.Class) {
@@ -1716,7 +2011,8 @@
1716
2011
  let defaultEncoder = new Encoder({ useRecords: false });
1717
2012
  const encode = defaultEncoder.encode;
1718
2013
  const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS;
1719
- const REUSE_BUFFER_MODE = 1000;
2014
+ const REUSE_BUFFER_MODE = 512;
2015
+ const RESET_BUFFER_MODE = 1024;
1720
2016
 
1721
2017
  /**
1722
2018
  * Given an Iterable first argument, returns an Iterable where each value is encoded as a Buffer
@@ -1801,9 +2097,6 @@
1801
2097
  }
1802
2098
  }
1803
2099
 
1804
- const useRecords = false;
1805
- const mapsAsObjects = true;
1806
-
1807
2100
  exports.ALWAYS = ALWAYS;
1808
2101
  exports.DECIMAL_FIT = DECIMAL_FIT;
1809
2102
  exports.DECIMAL_ROUND = DECIMAL_ROUND;
@@ -1811,6 +2104,7 @@
1811
2104
  exports.Encoder = Encoder;
1812
2105
  exports.FLOAT32_OPTIONS = FLOAT32_OPTIONS;
1813
2106
  exports.NEVER = NEVER;
2107
+ exports.REUSE_BUFFER_MODE = REUSE_BUFFER_MODE;
1814
2108
  exports.Tag = Tag;
1815
2109
  exports.addExtension = addExtension$1;
1816
2110
  exports.clearSource = clearSource;
@@ -1819,9 +2113,8 @@
1819
2113
  exports.decodeMultiple = decodeMultiple;
1820
2114
  exports.encode = encode;
1821
2115
  exports.encodeIter = encodeIter;
1822
- exports.mapsAsObjects = mapsAsObjects;
2116
+ exports.isNativeAccelerationEnabled = isNativeAccelerationEnabled;
1823
2117
  exports.roundFloat32 = roundFloat32;
1824
- exports.useRecords = useRecords;
1825
2118
 
1826
2119
  Object.defineProperty(exports, '__esModule', { value: true });
1827
2120