cbor-x 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.cjs CHANGED
@@ -256,7 +256,11 @@ function read() {
256
256
  position += 8;
257
257
  return value
258
258
  }
259
- if (currentDecoder.int64AsNumber) {
259
+ if (majorType > 1) {
260
+ if (dataView.getUint32(position) > 0)
261
+ throw new Error('JavaScript does not support arrays, maps, or strings with length over 4294967295')
262
+ token = dataView.getUint32(position + 4);
263
+ } else if (currentDecoder.int64AsNumber) {
260
264
  token = dataView.getUint32(position) * 0x100000000;
261
265
  token += dataView.getUint32(position + 4);
262
266
  } else
@@ -436,21 +440,30 @@ function createStructureReader(structure) {
436
440
  }
437
441
  }
438
442
  // This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
439
- if (this.objectLiteralSize === length) // we have a fast object literal reader, use it (assuming it is the right length)
440
- return this.objectLiteral(read)
441
- if (this.count++ == 3) { // create a fast reader
442
- this.objectLiteralSize = length;
443
- this.objectLiteral = currentDecoder.keyMap
444
- ? new Function('r', 'return {' + this.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
445
- : new Function('r', 'return {' + this.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}');
446
- return this.objectLiteral(read)
443
+ let compiledReader = this.compiledReader; // first look to see if we have the fast compiled function
444
+ while(compiledReader) {
445
+ // we have a fast compiled object literal reader
446
+ if (compiledReader.propertyCount === length)
447
+ return compiledReader(read) // with the right length, so we use it
448
+ compiledReader = compiledReader.next; // see if there is another reader with the right length
449
+ }
450
+ if (this.slowReads++ >= 3) { // create a fast compiled reader
451
+ let array = this.length == length ? this : this.slice(0, length);
452
+ compiledReader = currentDecoder.keyMap
453
+ ? new Function('r', 'return {' + array.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
454
+ : new Function('r', 'return {' + array.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}');
455
+ if (this.compiledReader)
456
+ compiledReader.next = this.compiledReader; // if there is an existing one, we store multiple readers as a linked list because it is usually pretty rare to have multiple readers (of different length) for the same structure
457
+ compiledReader.propertyCount = length;
458
+ this.compiledReader = compiledReader;
459
+ return compiledReader(read)
447
460
  }
448
461
  let object = {};
449
462
  if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read();
450
463
  else for (let i = 0; i < length; i++) object[this[i]] = read();
451
464
  return object
452
465
  }
453
- structure.count = 0;
466
+ structure.slowReads = 0;
454
467
  return readObject
455
468
  }
456
469
 
@@ -763,11 +776,16 @@ currentExtensions[5] = (fraction) => {
763
776
  return fraction[1] * Math.exp(fraction[0] * Math.log(2))
764
777
  };
765
778
 
766
- // the registration of the record definition extension (tag 105)
779
+ // the registration of the record definition extension
767
780
  const recordDefinition = (definition) => {
768
781
  let id = definition[0] - 0xe000;
769
782
  let structure = definition[1];
783
+ let existingStructure = currentStructures[id];
784
+ if (existingStructure && existingStructure.isShared) {
785
+ (currentStructures.restoreStructures || (currentStructures.restoreStructures = []))[id] = existingStructure;
786
+ }
770
787
  currentStructures[id] = structure;
788
+
771
789
  structure.read = createStructureReader(structure);
772
790
  let object = {};
773
791
  if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
@@ -911,6 +929,8 @@ currentExtensionRanges.push((tag, input) => {
911
929
  version: input,
912
930
  }
913
931
  }
932
+ if (tag == 55799) // self-descriptive CBOR tag, just return input value
933
+ return input
914
934
  });
915
935
 
916
936
  const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array');
@@ -1082,12 +1102,18 @@ class Encoder extends Decoder {
1082
1102
  } : false;
1083
1103
 
1084
1104
  let encoder = this;
1085
- let maxSharedStructures = options.maxSharedStructures || 128;
1105
+ let hasSharedStructures = options.structures || options.saveStructures;
1106
+ let maxSharedStructures = options.maxSharedStructures;
1107
+ if (maxSharedStructures == null)
1108
+ maxSharedStructures = hasSharedStructures ? 128 : 0;
1109
+ if (maxSharedStructures > 8190)
1110
+ throw new Error('Maximum maxSharedStructure is 8190')
1086
1111
  let isSequential = options.sequential;
1087
1112
  if (isSequential) {
1088
1113
  maxSharedStructures = 0;
1089
- this.structures = [];
1090
1114
  }
1115
+ if (!this.structures)
1116
+ this.structures = [];
1091
1117
  if (this.saveStructures)
1092
1118
  this.saveShared = this.saveStructures;
1093
1119
  let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues;
@@ -1135,6 +1161,10 @@ class Encoder extends Decoder {
1135
1161
  } else if (encodeOptions === REUSE_BUFFER_MODE)
1136
1162
  position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
1137
1163
  start = position$1;
1164
+ if (encoder.useSelfDescribedHeader) {
1165
+ targetView.setUint32(position$1, 0xd9d9f700); // tag two byte, then self-descriptive tag
1166
+ position$1 += 3;
1167
+ }
1138
1168
  referenceMap = encoder.structuredClone ? new Map() : null;
1139
1169
  if (encoder.bundleStrings && typeof value !== 'string') {
1140
1170
  bundledStrings$1 = [];
@@ -1168,6 +1198,8 @@ class Encoder extends Decoder {
1168
1198
  continue
1169
1199
  let nextTransition, transition = sharedStructures.transitions;
1170
1200
  for (let j = 0, l = keys.length; j < l; j++) {
1201
+ if (transition[RECORD_SYMBOL] === undefined)
1202
+ transition[RECORD_SYMBOL] = i;
1171
1203
  let key = keys[j];
1172
1204
  nextTransition = transition[key];
1173
1205
  if (!nextTransition) {
@@ -1175,7 +1207,7 @@ class Encoder extends Decoder {
1175
1207
  }
1176
1208
  transition = nextTransition;
1177
1209
  }
1178
- transition[RECORD_SYMBOL] = i;
1210
+ transition[RECORD_SYMBOL] = i | 0x100000;
1179
1211
  }
1180
1212
  }
1181
1213
  if (!isSequential)
@@ -1227,11 +1259,13 @@ class Encoder extends Decoder {
1227
1259
  target.end = position$1;
1228
1260
  return target
1229
1261
  }
1230
- return target.subarray(start, position$1) // position can change if we call pack again in saveShared, so we get the buffer now
1262
+ return target.subarray(start, position$1) // position can change if we call encode again in saveShared, so we get the buffer now
1231
1263
  } finally {
1232
1264
  if (sharedStructures) {
1233
1265
  if (serializationsSinceTransitionRebuild < 10)
1234
1266
  serializationsSinceTransitionRebuild++;
1267
+ if (sharedStructures.length > maxSharedStructures)
1268
+ sharedStructures.length = maxSharedStructures;
1235
1269
  if (transitionsCount > 10000) {
1236
1270
  // force a rebuild occasionally after a lot of transitions so it can get cleaned up
1237
1271
  sharedStructures.transitions = null;
@@ -1244,6 +1278,7 @@ class Encoder extends Decoder {
1244
1278
  recordIdsToRemove[i][RECORD_SYMBOL] = undefined;
1245
1279
  }
1246
1280
  recordIdsToRemove = [];
1281
+ //sharedStructures.nextId = maxSharedStructures
1247
1282
  }
1248
1283
  }
1249
1284
  if (hasSharedUpdate && encoder.saveShared) {
@@ -1654,88 +1689,74 @@ class Encoder extends Decoder {
1654
1689
  target[objectOffset++ + start] = size >> 8;
1655
1690
  target[objectOffset + start] = size & 0xff;
1656
1691
  } :
1657
-
1658
- /* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
1659
1692
  (object, safePrototype) => {
1660
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
1661
- let objectOffset = position++ - start
1662
- let wroteKeys
1663
- for (let key in object) {
1664
- if (safePrototype || object.hasOwnProperty(key)) {
1665
- nextTransition = transition[key]
1693
+ let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1694
+ let newTransitions = 0;
1695
+ let length = 0;
1696
+ let parentRecordId;
1697
+ let keys;
1698
+ if (this.keyMap) {
1699
+ keys = Object.keys(object).map(k => this.encodeKey(k));
1700
+ length = keys.length;
1701
+ for (let i = 0; i < length; i++) {
1702
+ let key = keys[i];
1703
+ nextTransition = transition[key];
1666
1704
  if (!nextTransition) {
1667
- nextTransition = transition[key] = Object.create(null)
1668
- nextTransition.__keys__ = (transition.__keys__ || []).concat([key])
1669
- /*let keys = Object.keys(object)
1670
- if
1671
- let size = 0
1672
- let startBranch = transition.__keys__ ? transition.__keys__.length : 0
1673
- for (let i = 0, l = keys.length; i++) {
1674
- let key = keys[i]
1675
- size += key.length << 2
1676
- if (i >= startBranch) {
1677
- nextTransition = nextTransition[key] = Object.create(null)
1678
- nextTransition.__keys__ = keys.slice(0, i + 1)
1679
- }
1705
+ nextTransition = transition[key] = Object.create(null);
1706
+ newTransitions++;
1707
+ }
1708
+ transition = nextTransition;
1709
+ }
1710
+ } else {
1711
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1712
+ nextTransition = transition[key];
1713
+ if (!nextTransition) {
1714
+ if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
1715
+ parentRecordId = transition[RECORD_SYMBOL] & 0xffff;
1680
1716
  }
1681
- makeRoom(position + size)
1682
- nextTransition = transition[key]
1683
- target.copy(target, )
1684
- objectOffset
1717
+ nextTransition = transition[key] = Object.create(null);
1718
+ newTransitions++;
1685
1719
  }
1686
- transition = nextTransition
1687
- encode(object[key])
1720
+ transition = nextTransition;
1721
+ length++;
1688
1722
  }
1689
1723
  }
1690
- let id = transition.id
1691
- if (!id) {
1692
- id = transition.id = structures.push(transition.__keys__) + 63
1693
- if (sharedStructures.onUpdate)
1694
- sharedStructures.onUpdate(id, transition.__keys__)
1695
- }
1696
- target[objectOffset + start] = id
1697
- }*/
1698
- (object) => {
1699
- let keys = Object.keys(object);
1700
- let vals = Object.values(object);
1701
- if (this.keyMap) keys = keys.map(k => this.encodeKey(k));
1702
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1703
- let newTransitions = 0;
1704
- let length = keys.length;
1705
- //let parentRecordId
1706
- for (let i = 0; i < length; i++) {
1707
- //if (!parentRecordId)
1708
- let key = keys[i];
1709
- // parentRecordId = transition[RECORD_SYMBOL]
1710
- nextTransition = transition[key];
1711
- if (!nextTransition) {
1712
- nextTransition = transition[key] = Object.create(null);
1713
- newTransitions++;
1714
- }
1715
- transition = nextTransition;
1716
- }
1717
1724
  let recordId = transition[RECORD_SYMBOL];
1718
1725
  if (recordId !== undefined) {
1726
+ recordId &= 0xffff;
1719
1727
  target[position$1++] = 0xd9;
1720
1728
  target[position$1++] = (recordId >> 8) | 0xe0;
1721
1729
  target[position$1++] = recordId & 0xff;
1722
1730
  } else {
1723
- recordId = structures.nextId++;
1724
- if (!recordId) {
1725
- recordId = 0;
1726
- structures.nextId = 1;
1727
- }
1728
- if (recordId >= MAX_STRUCTURES) {// cycle back around
1729
- structures.nextId = (recordId = maxSharedStructures) + 1;
1731
+ if (!keys)
1732
+ keys = transition.__keys__ || (transition.__keys__ = Object.keys(object));
1733
+ if (parentRecordId === undefined) {
1734
+ recordId = structures.nextId++;
1735
+ if (!recordId) {
1736
+ recordId = 0;
1737
+ structures.nextId = 1;
1738
+ }
1739
+ if (recordId >= MAX_STRUCTURES) {// cycle back around
1740
+ structures.nextId = (recordId = maxSharedStructures) + 1;
1741
+ }
1742
+ } else {
1743
+ recordId = parentRecordId;
1730
1744
  }
1731
- transition[RECORD_SYMBOL] = recordId;
1732
1745
  structures[recordId] = keys;
1733
- if (sharedStructures && sharedStructures.length <= maxSharedStructures) {
1746
+ if (recordId < maxSharedStructures) {
1734
1747
  target[position$1++] = 0xd9;
1735
1748
  target[position$1++] = (recordId >> 8) | 0xe0;
1736
1749
  target[position$1++] = recordId & 0xff;
1750
+ transition = structures.transitions;
1751
+ for (let i = 0; i < length; i++) {
1752
+ if (transition[RECORD_SYMBOL] === undefined || (transition[RECORD_SYMBOL] & 0x100000))
1753
+ transition[RECORD_SYMBOL] = recordId;
1754
+ transition = transition[keys[i]];
1755
+ }
1756
+ transition[RECORD_SYMBOL] = recordId | 0x100000; // indicates it is a extendable terminal
1737
1757
  hasSharedUpdate = true;
1738
1758
  } else {
1759
+ transition[RECORD_SYMBOL] = recordId;
1739
1760
  targetView.setUint32(position$1, 0xd9dfff00); // tag two byte, then record definition id
1740
1761
  position$1 += 3;
1741
1762
  if (newTransitions)
@@ -1756,7 +1777,9 @@ class Encoder extends Decoder {
1756
1777
  } else {
1757
1778
  writeArrayHeader(length);
1758
1779
  }
1759
- for (let i =0; i < length; i++) encode(vals[i]);
1780
+ for (let key in object)
1781
+ if (safePrototype || object.hasOwnProperty(key))
1782
+ encode(object[key]);
1760
1783
  };
1761
1784
  const makeRoom = (end) => {
1762
1785
  let newSize;
@@ -1795,15 +1818,22 @@ class Encoder extends Decoder {
1795
1818
  updateSharedData() {
1796
1819
  let lastVersion = this.sharedVersion || 0;
1797
1820
  this.sharedVersion = lastVersion + 1;
1798
- let saveResults = this.saveShared(new SharedData(this.structures, this.sharedValues, this.sharedVersion),
1821
+ let structuresCopy = this.structures.slice(0);
1822
+ let sharedData = new SharedData(structuresCopy, this.sharedValues, this.sharedVersion);
1823
+ let saveResults = this.saveShared(sharedData,
1799
1824
  existingShared => (existingShared && existingShared.version || 0) == lastVersion);
1800
1825
  if (saveResults === false) {
1801
1826
  // get updated structures and try again if the update failed
1802
- let sharedData = this.getShared() || {};
1827
+ sharedData = this.getShared() || {};
1803
1828
  this.structures = sharedData.structures || [];
1804
1829
  this.sharedValues = sharedData.packedValues;
1805
1830
  this.sharedVersion = sharedData.version;
1831
+ this.structures.nextId = this.structures.length;
1832
+ } else {
1833
+ // restore structures
1834
+ structuresCopy.forEach((structure, i) => this.structures[i] = structure);
1806
1835
  }
1836
+ // saveShared may fail to write and reload, or may have reloaded to check compatibility and overwrite saved data, either way load the correct shared data
1807
1837
  return saveResults
1808
1838
  }
1809
1839
  }
package/dist/test.js CHANGED
@@ -592,7 +592,6 @@
592
592
 
593
593
  const senmlKeys = { bs: -6, bv: -5, bu: -4, bt: -3, bn: -2, n: 0, u: 1, v: 2, vs: 3, t: 6, ut: 7, vd: 8 };
594
594
 
595
-
596
595
  //import inspector from 'inspector'; inspector.open(9229, null, true); debugger
597
596
  function tryRequire(module) {
598
597
  try {
@@ -666,8 +665,40 @@
666
665
  let structures = [];
667
666
  let encoder = new Encoder({ structures });
668
667
  var serialized = encoder.encode(data);
668
+ serialized = encoder.encode(data);
669
+ var deserialized = encoder.decode(serialized);
670
+ assert.deepEqual(deserialized, data);
671
+ });
672
+
673
+ test('mixed structures, shared', function(){
674
+ let data1 = { a: 1, b: 2, c: 3 };
675
+ let data2 = { a: 1, b: 2, d: 4 };
676
+ let data3 = { a: 1, b: 2, e: 5 };
677
+ let structures = [];
678
+ let encoder = new Encoder({ structures });
679
+ var serialized = encoder.encode(data1);
680
+ var deserialized = encoder.decode(serialized);
681
+ assert.deepEqual(deserialized, data1);
682
+ var serialized = encoder.encode(data2);
683
+ var deserialized = encoder.decode(serialized);
684
+ assert.deepEqual(deserialized, data2);
685
+ var serialized = encoder.encode(data3);
686
+ var deserialized = encoder.decode(serialized);
687
+ assert.deepEqual(deserialized, data3);
688
+ });
689
+
690
+ test('mixed structures, unshared', function(){
691
+ let data = [];
692
+ let encoder = new Encoder({ });
693
+ for (let i = 0; i< 1000; i++) {
694
+ data.push({a: 1, ['test' + i]: i});
695
+ }
696
+ var serialized = encoder.encode(data);
669
697
  var deserialized = encoder.decode(serialized);
670
698
  assert.deepEqual(deserialized, data);
699
+ serialized = encoder.encode(data);
700
+ deserialized = encoder.decode(serialized);
701
+ assert.deepEqual(deserialized, data);
671
702
  });
672
703
 
673
704
  test('mixed array', function(){
@@ -772,6 +803,16 @@
772
803
  var deserialized = encoder.decode(serialized);
773
804
  assert.deepEqual(deserialized, data);
774
805
  });
806
+ test('pack/unpack sample data with self-descriptive header', function(){
807
+ var data = sampleData;
808
+ let encoder = new Encoder({ useSelfDescribedHeader: true });
809
+ var serialized = encoder.encode(data);
810
+ var deserialized = encoder.decode(serialized);
811
+ assert.deepEqual(deserialized, data);
812
+ assert.equal(serialized[0], 0xd9);
813
+ assert.equal(serialized[1], 0xd9);
814
+ assert.equal(serialized[2], 0xf7);
815
+ });
775
816
  if (typeof Buffer != 'undefined')
776
817
  test('replace data', function(){
777
818
  var data1 = {
@@ -1162,7 +1203,10 @@
1162
1203
  CBOR.decodeMultiple(new Uint8Array([1, 2, 3, 4]), value => values.push(value));
1163
1204
  assert.deepEqual(values, [1, 2, 3, 4]);
1164
1205
  });
1165
-
1206
+ test('bad input', function() {
1207
+ let badInput = Buffer.from('7b2273657269616c6e6f223a2265343a30222c226970223a223139322e3136382e312e3335222c226b6579223a226770735f736563726574227d', 'hex');
1208
+ assert.throws(function(){ decode(badInput); }); // should throw, not crash
1209
+ });
1166
1210
  });
1167
1211
  suite('CBOR performance tests', function(){
1168
1212
  test('performance JSON.parse', function() {
package/encode.js CHANGED
@@ -38,12 +38,18 @@ export class Encoder extends Decoder {
38
38
  } : false
39
39
 
40
40
  let encoder = this
41
- let maxSharedStructures = options.maxSharedStructures || 128
41
+ let hasSharedStructures = options.structures || options.saveStructures
42
+ let maxSharedStructures = options.maxSharedStructures
43
+ if (maxSharedStructures == null)
44
+ maxSharedStructures = hasSharedStructures ? 128 : 0
45
+ if (maxSharedStructures > 8190)
46
+ throw new Error('Maximum maxSharedStructure is 8190')
42
47
  let isSequential = options.sequential
43
48
  if (isSequential) {
44
49
  maxSharedStructures = 0
45
- this.structures = []
46
50
  }
51
+ if (!this.structures)
52
+ this.structures = []
47
53
  if (this.saveStructures)
48
54
  this.saveShared = this.saveStructures
49
55
  let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues
@@ -91,6 +97,10 @@ export class Encoder extends Decoder {
91
97
  } else if (encodeOptions === REUSE_BUFFER_MODE)
92
98
  position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
93
99
  start = position
100
+ if (encoder.useSelfDescribedHeader) {
101
+ targetView.setUint32(position, 0xd9d9f700) // tag two byte, then self-descriptive tag
102
+ position += 3
103
+ }
94
104
  referenceMap = encoder.structuredClone ? new Map() : null
95
105
  if (encoder.bundleStrings && typeof value !== 'string') {
96
106
  bundledStrings = []
@@ -124,6 +134,8 @@ export class Encoder extends Decoder {
124
134
  continue
125
135
  let nextTransition, transition = sharedStructures.transitions
126
136
  for (let j = 0, l = keys.length; j < l; j++) {
137
+ if (transition[RECORD_SYMBOL] === undefined)
138
+ transition[RECORD_SYMBOL] = i
127
139
  let key = keys[j]
128
140
  nextTransition = transition[key]
129
141
  if (!nextTransition) {
@@ -131,7 +143,7 @@ export class Encoder extends Decoder {
131
143
  }
132
144
  transition = nextTransition
133
145
  }
134
- transition[RECORD_SYMBOL] = i
146
+ transition[RECORD_SYMBOL] = i | 0x100000
135
147
  }
136
148
  }
137
149
  if (!isSequential)
@@ -183,11 +195,13 @@ export class Encoder extends Decoder {
183
195
  target.end = position
184
196
  return target
185
197
  }
186
- return target.subarray(start, position) // position can change if we call pack again in saveShared, so we get the buffer now
198
+ return target.subarray(start, position) // position can change if we call encode again in saveShared, so we get the buffer now
187
199
  } finally {
188
200
  if (sharedStructures) {
189
201
  if (serializationsSinceTransitionRebuild < 10)
190
202
  serializationsSinceTransitionRebuild++
203
+ if (sharedStructures.length > maxSharedStructures)
204
+ sharedStructures.length = maxSharedStructures
191
205
  if (transitionsCount > 10000) {
192
206
  // force a rebuild occasionally after a lot of transitions so it can get cleaned up
193
207
  sharedStructures.transitions = null
@@ -200,6 +214,7 @@ export class Encoder extends Decoder {
200
214
  recordIdsToRemove[i][RECORD_SYMBOL] = undefined
201
215
  }
202
216
  recordIdsToRemove = []
217
+ //sharedStructures.nextId = maxSharedStructures
203
218
  }
204
219
  }
205
220
  if (hasSharedUpdate && encoder.saveShared) {
@@ -611,88 +626,74 @@ export class Encoder extends Decoder {
611
626
  target[objectOffset++ + start] = size >> 8
612
627
  target[objectOffset + start] = size & 0xff
613
628
  } :
614
-
615
- /* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
616
629
  (object, safePrototype) => {
617
630
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
618
- let objectOffset = position++ - start
619
- let wroteKeys
620
- for (let key in object) {
621
- if (safePrototype || object.hasOwnProperty(key)) {
631
+ let newTransitions = 0
632
+ let length = 0
633
+ let parentRecordId
634
+ let keys
635
+ if (this.keyMap) {
636
+ keys = Object.keys(object).map(k => this.encodeKey(k))
637
+ length = keys.length
638
+ for (let i = 0; i < length; i++) {
639
+ let key = keys[i]
622
640
  nextTransition = transition[key]
623
641
  if (!nextTransition) {
624
642
  nextTransition = transition[key] = Object.create(null)
625
- nextTransition.__keys__ = (transition.__keys__ || []).concat([key])
626
- /*let keys = Object.keys(object)
627
- if
628
- let size = 0
629
- let startBranch = transition.__keys__ ? transition.__keys__.length : 0
630
- for (let i = 0, l = keys.length; i++) {
631
- let key = keys[i]
632
- size += key.length << 2
633
- if (i >= startBranch) {
634
- nextTransition = nextTransition[key] = Object.create(null)
635
- nextTransition.__keys__ = keys.slice(0, i + 1)
636
- }
643
+ newTransitions++
644
+ }
645
+ transition = nextTransition
646
+ }
647
+ } else {
648
+ for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
649
+ nextTransition = transition[key]
650
+ if (!nextTransition) {
651
+ if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
652
+ parentRecordId = transition[RECORD_SYMBOL] & 0xffff
637
653
  }
638
- makeRoom(position + size)
639
- nextTransition = transition[key]
640
- target.copy(target, )
641
- objectOffset
654
+ nextTransition = transition[key] = Object.create(null)
655
+ newTransitions++
642
656
  }
643
657
  transition = nextTransition
644
- encode(object[key])
658
+ length++
645
659
  }
646
660
  }
647
- let id = transition.id
648
- if (!id) {
649
- id = transition.id = structures.push(transition.__keys__) + 63
650
- if (sharedStructures.onUpdate)
651
- sharedStructures.onUpdate(id, transition.__keys__)
652
- }
653
- target[objectOffset + start] = id
654
- }*/
655
- (object) => {
656
- let keys = Object.keys(object)
657
- let vals = Object.values(object)
658
- if (this.keyMap) keys = keys.map(k => this.encodeKey(k))
659
- let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
660
- let newTransitions = 0
661
- let length = keys.length
662
- //let parentRecordId
663
- for (let i = 0; i < length; i++) {
664
- //if (!parentRecordId)
665
- let key = keys[i]
666
- // parentRecordId = transition[RECORD_SYMBOL]
667
- nextTransition = transition[key]
668
- if (!nextTransition) {
669
- nextTransition = transition[key] = Object.create(null)
670
- newTransitions++
671
- }
672
- transition = nextTransition
673
- }
674
661
  let recordId = transition[RECORD_SYMBOL]
675
662
  if (recordId !== undefined) {
663
+ recordId &= 0xffff
676
664
  target[position++] = 0xd9
677
665
  target[position++] = (recordId >> 8) | 0xe0
678
666
  target[position++] = recordId & 0xff
679
667
  } else {
680
- recordId = structures.nextId++
681
- if (!recordId) {
682
- recordId = 0
683
- structures.nextId = 1
684
- }
685
- if (recordId >= MAX_STRUCTURES) {// cycle back around
686
- structures.nextId = (recordId = maxSharedStructures) + 1
668
+ if (!keys)
669
+ keys = transition.__keys__ || (transition.__keys__ = Object.keys(object))
670
+ if (parentRecordId === undefined) {
671
+ recordId = structures.nextId++
672
+ if (!recordId) {
673
+ recordId = 0
674
+ structures.nextId = 1
675
+ }
676
+ if (recordId >= MAX_STRUCTURES) {// cycle back around
677
+ structures.nextId = (recordId = maxSharedStructures) + 1
678
+ }
679
+ } else {
680
+ recordId = parentRecordId
687
681
  }
688
- transition[RECORD_SYMBOL] = recordId
689
682
  structures[recordId] = keys
690
- if (sharedStructures && sharedStructures.length <= maxSharedStructures) {
683
+ if (recordId < maxSharedStructures) {
691
684
  target[position++] = 0xd9
692
685
  target[position++] = (recordId >> 8) | 0xe0
693
686
  target[position++] = recordId & 0xff
687
+ transition = structures.transitions
688
+ for (let i = 0; i < length; i++) {
689
+ if (transition[RECORD_SYMBOL] === undefined || (transition[RECORD_SYMBOL] & 0x100000))
690
+ transition[RECORD_SYMBOL] = recordId
691
+ transition = transition[keys[i]]
692
+ }
693
+ transition[RECORD_SYMBOL] = recordId | 0x100000 // indicates it is a extendable terminal
694
694
  hasSharedUpdate = true
695
695
  } else {
696
+ transition[RECORD_SYMBOL] = recordId
696
697
  targetView.setUint32(position, 0xd9dfff00) // tag two byte, then record definition id
697
698
  position += 3
698
699
  if (newTransitions)
@@ -713,7 +714,9 @@ export class Encoder extends Decoder {
713
714
  } else {
714
715
  writeArrayHeader(length)
715
716
  }
716
- for (let i =0; i < length; i++) encode(vals[i])
717
+ for (let key in object)
718
+ if (safePrototype || object.hasOwnProperty(key))
719
+ encode(object[key])
717
720
  }
718
721
  const makeRoom = (end) => {
719
722
  let newSize
@@ -752,15 +755,22 @@ export class Encoder extends Decoder {
752
755
  updateSharedData() {
753
756
  let lastVersion = this.sharedVersion || 0
754
757
  this.sharedVersion = lastVersion + 1
755
- let saveResults = this.saveShared(new SharedData(this.structures, this.sharedValues, this.sharedVersion),
758
+ let structuresCopy = this.structures.slice(0)
759
+ let sharedData = new SharedData(structuresCopy, this.sharedValues, this.sharedVersion)
760
+ let saveResults = this.saveShared(sharedData,
756
761
  existingShared => (existingShared && existingShared.version || 0) == lastVersion)
757
762
  if (saveResults === false) {
758
763
  // get updated structures and try again if the update failed
759
- let sharedData = this.getShared() || {}
764
+ sharedData = this.getShared() || {}
760
765
  this.structures = sharedData.structures || []
761
766
  this.sharedValues = sharedData.packedValues
762
767
  this.sharedVersion = sharedData.version
768
+ this.structures.nextId = this.structures.length
769
+ } else {
770
+ // restore structures
771
+ structuresCopy.forEach((structure, i) => this.structures[i] = structure)
763
772
  }
773
+ // 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
764
774
  return saveResults
765
775
  }
766
776
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cbor-x",
3
3
  "author": "Kris Zyp",
4
- "version": "1.0.0",
4
+ "version": "1.1.0",
5
5
  "description": "Ultra-fast and conformant CBOR (RFC 8949) implementation with support for numerous tag extensions including records and structured cloning",
6
6
  "license": "MIT",
7
7
  "types": "./index.d.ts",