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/README.md +1 -2
- package/decode.d.ts +1 -0
- package/decode.js +31 -11
- package/dist/index.js +111 -81
- package/dist/index.js.map +1 -0
- package/dist/index.min.js +66 -105
- package/dist/node-index.js +93 -0
- package/dist/node.cjs +111 -81
- package/dist/test.js +46 -2
- package/encode.js +76 -66
- package/package.json +1 -1
package/dist/node.cjs
CHANGED
|
@@ -256,7 +256,11 @@ function read() {
|
|
|
256
256
|
position += 8;
|
|
257
257
|
return value
|
|
258
258
|
}
|
|
259
|
-
if (
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1662
|
-
let
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
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
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
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
|
-
|
|
1682
|
-
|
|
1683
|
-
target.copy(target, )
|
|
1684
|
-
objectOffset
|
|
1717
|
+
nextTransition = transition[key] = Object.create(null);
|
|
1718
|
+
newTransitions++;
|
|
1685
1719
|
}
|
|
1686
|
-
transition = nextTransition
|
|
1687
|
-
|
|
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
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
structures.nextId
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
619
|
-
let
|
|
620
|
-
|
|
621
|
-
|
|
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
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
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
|
-
|
|
639
|
-
|
|
640
|
-
target.copy(target, )
|
|
641
|
-
objectOffset
|
|
654
|
+
nextTransition = transition[key] = Object.create(null)
|
|
655
|
+
newTransitions++
|
|
642
656
|
}
|
|
643
657
|
transition = nextTransition
|
|
644
|
-
|
|
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
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
structures.nextId
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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",
|