cbor-x 1.0.0 → 1.2.1
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 +59 -20
- package/dist/index.js +158 -106
- package/dist/index.js.map +1 -0
- package/dist/index.min.js +71 -109
- package/dist/node-index.js +93 -0
- package/dist/node.cjs +158 -106
- package/dist/test.js +56 -3
- package/encode.js +94 -81
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,10 +7,9 @@
|
|
|
7
7
|
[](README.md)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
The cbor-x package is an extremely fast and conformant CBOR NodeJS/JavaScript implementation. Currently, it is over 3-10x faster than any other CBOR JS implementation (including cbor-js and cborg) and faster than most MessagePack encoders, Avro, and generally faster than native V8 JSON.stringify/parse, on NodeJS. It implements the CBOR format as specificed in [RFC-8949](https://www.rfc-editor.org/rfc/rfc8949.html), [RFC-8746](https://tools.ietf.org/html/rfc8746), [RFC-8742](https://datatracker.ietf.org/doc/html/rfc8742), [Packed CBOR](https://www.ietf.org/id/draft-ietf-cbor-packed-03.html), numerous [registered IANA tag extensions](https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml) (the `x` in cbor-x), and proposed optional [record extension](https://github.com/kriszyp/cbor-records), for defining record structures that makes CBOR even faster and more compact, often over twice as fast as even native JSON functions, and 15-50% more compact. See the performance section for more details. Structured cloning (with support for cyclical references) is supported through these tag extensions.
|
|
12
10
|
<img align="right" src="./assets/performance.png" width="380"/>
|
|
13
11
|
|
|
12
|
+
The cbor-x package is an extremely fast and conformant CBOR NodeJS/JavaScript implementation. Currently, it is over 3-10x faster than any other CBOR JS implementation (including cbor-js and cborg) and faster than most MessagePack encoders, Avro, and generally faster than native V8 JSON.stringify/parse, on NodeJS. It implements the CBOR format as specificed in [RFC-8949](https://www.rfc-editor.org/rfc/rfc8949.html), [RFC-8746](https://tools.ietf.org/html/rfc8746), [RFC-8742](https://datatracker.ietf.org/doc/html/rfc8742), [Packed CBOR](https://www.ietf.org/id/draft-ietf-cbor-packed-03.html), numerous [registered IANA tag extensions](https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml) (the `x` in cbor-x), and proposed optional [record extension](https://github.com/kriszyp/cbor-records), for defining record structures that makes CBOR even faster and more compact, often over twice as fast as even native JSON functions, and 15-50% more compact. See the performance section for more details. Structured cloning (with support for cyclical references) is supported through these tag extensions.
|
|
14
13
|
|
|
15
14
|
## Basic Usage
|
|
16
15
|
Install on NodeJS with:
|
package/decode.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface Options {
|
|
|
19
19
|
encodeUndefinedAsNil?: boolean
|
|
20
20
|
maxSharedStructures?: number
|
|
21
21
|
maxOwnStructures?: number
|
|
22
|
+
useSelfDescribedHeader?: boolean
|
|
22
23
|
shouldShareStructure?: (keys: string[]) => boolean
|
|
23
24
|
getStructures?(): {}[]
|
|
24
25
|
saveStructures?(structures: {}[]): boolean | void
|
package/decode.js
CHANGED
|
@@ -254,7 +254,11 @@ export function read() {
|
|
|
254
254
|
position += 8
|
|
255
255
|
return value
|
|
256
256
|
}
|
|
257
|
-
if (
|
|
257
|
+
if (majorType > 1) {
|
|
258
|
+
if (dataView.getUint32(position) > 0)
|
|
259
|
+
throw new Error('JavaScript does not support arrays, maps, or strings with length over 4294967295')
|
|
260
|
+
token = dataView.getUint32(position + 4)
|
|
261
|
+
} else if (currentDecoder.int64AsNumber) {
|
|
258
262
|
token = dataView.getUint32(position) * 0x100000000
|
|
259
263
|
token += dataView.getUint32(position + 4)
|
|
260
264
|
} else
|
|
@@ -434,21 +438,30 @@ function createStructureReader(structure) {
|
|
|
434
438
|
}
|
|
435
439
|
}
|
|
436
440
|
// This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
441
|
+
let compiledReader = this.compiledReader // first look to see if we have the fast compiled function
|
|
442
|
+
while(compiledReader) {
|
|
443
|
+
// we have a fast compiled object literal reader
|
|
444
|
+
if (compiledReader.propertyCount === length)
|
|
445
|
+
return compiledReader(read) // with the right length, so we use it
|
|
446
|
+
compiledReader = compiledReader.next // see if there is another reader with the right length
|
|
447
|
+
}
|
|
448
|
+
if (this.slowReads++ >= 3) { // create a fast compiled reader
|
|
449
|
+
let array = this.length == length ? this : this.slice(0, length)
|
|
450
|
+
compiledReader = currentDecoder.keyMap
|
|
451
|
+
? new Function('r', 'return {' + array.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
|
|
452
|
+
: new Function('r', 'return {' + array.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}')
|
|
453
|
+
if (this.compiledReader)
|
|
454
|
+
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
|
|
455
|
+
compiledReader.propertyCount = length
|
|
456
|
+
this.compiledReader = compiledReader
|
|
457
|
+
return compiledReader(read)
|
|
445
458
|
}
|
|
446
459
|
let object = {}
|
|
447
460
|
if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read()
|
|
448
461
|
else for (let i = 0; i < length; i++) object[this[i]] = read()
|
|
449
462
|
return object
|
|
450
463
|
}
|
|
451
|
-
structure.
|
|
464
|
+
structure.slowReads = 0
|
|
452
465
|
return readObject
|
|
453
466
|
}
|
|
454
467
|
|
|
@@ -811,7 +824,7 @@ currentExtensions[0] = (dateString) => {
|
|
|
811
824
|
|
|
812
825
|
currentExtensions[1] = (epochSec) => {
|
|
813
826
|
// numeric date extension
|
|
814
|
-
return new Date(epochSec * 1000)
|
|
827
|
+
return new Date(Math.round(epochSec * 1000))
|
|
815
828
|
}
|
|
816
829
|
|
|
817
830
|
currentExtensions[2] = (buffer) => {
|
|
@@ -837,11 +850,16 @@ currentExtensions[5] = (fraction) => {
|
|
|
837
850
|
return fraction[1] * Math.exp(fraction[0] * Math.log(2))
|
|
838
851
|
}
|
|
839
852
|
|
|
840
|
-
// the registration of the record definition extension
|
|
853
|
+
// the registration of the record definition extension
|
|
841
854
|
const recordDefinition = (definition) => {
|
|
842
855
|
let id = definition[0] - 0xe000
|
|
843
856
|
let structure = definition[1]
|
|
857
|
+
let existingStructure = currentStructures[id]
|
|
858
|
+
if (existingStructure && existingStructure.isShared) {
|
|
859
|
+
(currentStructures.restoreStructures || (currentStructures.restoreStructures = []))[id] = existingStructure
|
|
860
|
+
}
|
|
844
861
|
currentStructures[id] = structure
|
|
862
|
+
|
|
845
863
|
structure.read = createStructureReader(structure)
|
|
846
864
|
let object = {}
|
|
847
865
|
if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
|
|
@@ -985,19 +1003,40 @@ currentExtensionRanges.push((tag, input) => {
|
|
|
985
1003
|
version: input,
|
|
986
1004
|
}
|
|
987
1005
|
}
|
|
1006
|
+
if (tag == 55799) // self-descriptive CBOR tag, just return input value
|
|
1007
|
+
return input
|
|
988
1008
|
})
|
|
989
1009
|
|
|
990
|
-
|
|
991
|
-
const
|
|
1010
|
+
const isLittleEndianMachine = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1
|
|
1011
|
+
export const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64']
|
|
1012
|
+
const typedArrayTags = [64, 68, 69, 70, 71, 72, 77, 78, 79, 85, 86]
|
|
992
1013
|
for (let i = 0; i < typedArrays.length; i++) {
|
|
993
1014
|
registerTypedArray(typedArrays[i], typedArrayTags[i])
|
|
994
1015
|
}
|
|
995
|
-
function registerTypedArray(
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1016
|
+
function registerTypedArray(typedArrayId, tag, littleEndian) {
|
|
1017
|
+
let TypedArray = glbl[typedArrayId + 'Array']
|
|
1018
|
+
let bytesPerElement = TypedArray.BYTES_PER_ELEMENT
|
|
1019
|
+
for (let littleEndian = 0; littleEndian < 2; littleEndian++) {
|
|
1020
|
+
if (!littleEndian && bytesPerElement == 1)
|
|
1021
|
+
continue
|
|
1022
|
+
let sizeShift = bytesPerElement == 2 ? 1 : bytesPerElement == 4 ? 2 : 3
|
|
1023
|
+
currentExtensions[littleEndian ? tag : (tag - 4)] = (bytesPerElement == 1 || littleEndian == isLittleEndianMachine) ? (buffer) => {
|
|
1024
|
+
if (!TypedArray)
|
|
1025
|
+
throw new Error('Could not find typed array for code ' + tag)
|
|
1026
|
+
// we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
|
|
1027
|
+
return new TypedArray(Uint8Array.prototype.slice.call(buffer, 0).buffer)
|
|
1028
|
+
} : buffer => {
|
|
1029
|
+
if (!TypedArray)
|
|
1030
|
+
throw new Error('Could not find typed array for code ' + tag)
|
|
1031
|
+
let dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength)
|
|
1032
|
+
let elements = buffer.length >> sizeShift
|
|
1033
|
+
let ta = new TypedArray(elements)
|
|
1034
|
+
let method = dv['get' + typedArrayId]
|
|
1035
|
+
for (let i = 0; i < elements; i++) {
|
|
1036
|
+
ta[i] = method.call(dv, i << sizeShift, littleEndian)
|
|
1037
|
+
}
|
|
1038
|
+
return ta
|
|
1039
|
+
}
|
|
1001
1040
|
}
|
|
1002
1041
|
}
|
|
1003
1042
|
|
package/dist/index.js
CHANGED
|
@@ -250,7 +250,11 @@
|
|
|
250
250
|
position += 8;
|
|
251
251
|
return value
|
|
252
252
|
}
|
|
253
|
-
if (
|
|
253
|
+
if (majorType > 1) {
|
|
254
|
+
if (dataView.getUint32(position) > 0)
|
|
255
|
+
throw new Error('JavaScript does not support arrays, maps, or strings with length over 4294967295')
|
|
256
|
+
token = dataView.getUint32(position + 4);
|
|
257
|
+
} else if (currentDecoder.int64AsNumber) {
|
|
254
258
|
token = dataView.getUint32(position) * 0x100000000;
|
|
255
259
|
token += dataView.getUint32(position + 4);
|
|
256
260
|
} else
|
|
@@ -430,21 +434,30 @@
|
|
|
430
434
|
}
|
|
431
435
|
}
|
|
432
436
|
// This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
437
|
+
let compiledReader = this.compiledReader; // first look to see if we have the fast compiled function
|
|
438
|
+
while(compiledReader) {
|
|
439
|
+
// we have a fast compiled object literal reader
|
|
440
|
+
if (compiledReader.propertyCount === length)
|
|
441
|
+
return compiledReader(read) // with the right length, so we use it
|
|
442
|
+
compiledReader = compiledReader.next; // see if there is another reader with the right length
|
|
443
|
+
}
|
|
444
|
+
if (this.slowReads++ >= 3) { // create a fast compiled reader
|
|
445
|
+
let array = this.length == length ? this : this.slice(0, length);
|
|
446
|
+
compiledReader = currentDecoder.keyMap
|
|
447
|
+
? new Function('r', 'return {' + array.map(k => currentDecoder.decodeKey(k)).map(k => validName.test(k) ? k + ':r()' : ('[' + JSON.stringify(k) + ']:r()')).join(',') + '}')
|
|
448
|
+
: new Function('r', 'return {' + array.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}');
|
|
449
|
+
if (this.compiledReader)
|
|
450
|
+
compiledReader.next = this.compiledReader; // if there is an existing one, we store multiple readers as a linked list because it is usually pretty rare to have multiple readers (of different length) for the same structure
|
|
451
|
+
compiledReader.propertyCount = length;
|
|
452
|
+
this.compiledReader = compiledReader;
|
|
453
|
+
return compiledReader(read)
|
|
441
454
|
}
|
|
442
455
|
let object = {};
|
|
443
456
|
if (currentDecoder.keyMap) for (let i = 0; i < length; i++) object[currentDecoder.decodeKey(this[i])] = read();
|
|
444
457
|
else for (let i = 0; i < length; i++) object[this[i]] = read();
|
|
445
458
|
return object
|
|
446
459
|
}
|
|
447
|
-
structure.
|
|
460
|
+
structure.slowReads = 0;
|
|
448
461
|
return readObject
|
|
449
462
|
}
|
|
450
463
|
|
|
@@ -696,7 +709,7 @@
|
|
|
696
709
|
|
|
697
710
|
currentExtensions[1] = (epochSec) => {
|
|
698
711
|
// numeric date extension
|
|
699
|
-
return new Date(epochSec * 1000)
|
|
712
|
+
return new Date(Math.round(epochSec * 1000))
|
|
700
713
|
};
|
|
701
714
|
|
|
702
715
|
currentExtensions[2] = (buffer) => {
|
|
@@ -722,11 +735,16 @@
|
|
|
722
735
|
return fraction[1] * Math.exp(fraction[0] * Math.log(2))
|
|
723
736
|
};
|
|
724
737
|
|
|
725
|
-
// the registration of the record definition extension
|
|
738
|
+
// the registration of the record definition extension
|
|
726
739
|
const recordDefinition = (definition) => {
|
|
727
740
|
let id = definition[0] - 0xe000;
|
|
728
741
|
let structure = definition[1];
|
|
742
|
+
let existingStructure = currentStructures[id];
|
|
743
|
+
if (existingStructure && existingStructure.isShared) {
|
|
744
|
+
(currentStructures.restoreStructures || (currentStructures.restoreStructures = []))[id] = existingStructure;
|
|
745
|
+
}
|
|
729
746
|
currentStructures[id] = structure;
|
|
747
|
+
|
|
730
748
|
structure.read = createStructureReader(structure);
|
|
731
749
|
let object = {};
|
|
732
750
|
if (currentDecoder.keyMap) for (let i = 2,l = definition.length; i < l; i++) {
|
|
@@ -870,20 +888,41 @@
|
|
|
870
888
|
version: input,
|
|
871
889
|
}
|
|
872
890
|
}
|
|
891
|
+
if (tag == 55799) // self-descriptive CBOR tag, just return input value
|
|
892
|
+
return input
|
|
873
893
|
});
|
|
874
894
|
|
|
875
|
-
const
|
|
876
|
-
const
|
|
895
|
+
const isLittleEndianMachine = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1;
|
|
896
|
+
const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'];
|
|
897
|
+
const typedArrayTags = [64, 68, 69, 70, 71, 72, 77, 78, 79, 85, 86];
|
|
877
898
|
for (let i = 0; i < typedArrays.length; i++) {
|
|
878
899
|
registerTypedArray(typedArrays[i], typedArrayTags[i]);
|
|
879
900
|
}
|
|
880
|
-
function registerTypedArray(
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
901
|
+
function registerTypedArray(typedArrayId, tag, littleEndian) {
|
|
902
|
+
let TypedArray = glbl[typedArrayId + 'Array'];
|
|
903
|
+
let bytesPerElement = TypedArray.BYTES_PER_ELEMENT;
|
|
904
|
+
for (let littleEndian = 0; littleEndian < 2; littleEndian++) {
|
|
905
|
+
if (!littleEndian && bytesPerElement == 1)
|
|
906
|
+
continue
|
|
907
|
+
let sizeShift = bytesPerElement == 2 ? 1 : bytesPerElement == 4 ? 2 : 3;
|
|
908
|
+
currentExtensions[littleEndian ? tag : (tag - 4)] = (bytesPerElement == 1 || littleEndian == isLittleEndianMachine) ? (buffer) => {
|
|
909
|
+
if (!TypedArray)
|
|
910
|
+
throw new Error('Could not find typed array for code ' + tag)
|
|
911
|
+
// we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
|
|
912
|
+
return new TypedArray(Uint8Array.prototype.slice.call(buffer, 0).buffer)
|
|
913
|
+
} : buffer => {
|
|
914
|
+
if (!TypedArray)
|
|
915
|
+
throw new Error('Could not find typed array for code ' + tag)
|
|
916
|
+
let dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
917
|
+
let elements = buffer.length >> sizeShift;
|
|
918
|
+
let ta = new TypedArray(elements);
|
|
919
|
+
let method = dv['get' + typedArrayId];
|
|
920
|
+
for (let i = 0; i < elements; i++) {
|
|
921
|
+
ta[i] = method.call(dv, i << sizeShift, littleEndian);
|
|
922
|
+
}
|
|
923
|
+
return ta
|
|
924
|
+
};
|
|
925
|
+
}
|
|
887
926
|
}
|
|
888
927
|
|
|
889
928
|
function readBundleExt() {
|
|
@@ -1037,12 +1076,18 @@
|
|
|
1037
1076
|
} : false;
|
|
1038
1077
|
|
|
1039
1078
|
let encoder = this;
|
|
1040
|
-
let
|
|
1079
|
+
let hasSharedStructures = options.structures || options.saveStructures;
|
|
1080
|
+
let maxSharedStructures = options.maxSharedStructures;
|
|
1081
|
+
if (maxSharedStructures == null)
|
|
1082
|
+
maxSharedStructures = hasSharedStructures ? 128 : 0;
|
|
1083
|
+
if (maxSharedStructures > 8190)
|
|
1084
|
+
throw new Error('Maximum maxSharedStructure is 8190')
|
|
1041
1085
|
let isSequential = options.sequential;
|
|
1042
1086
|
if (isSequential) {
|
|
1043
1087
|
maxSharedStructures = 0;
|
|
1044
|
-
this.structures = [];
|
|
1045
1088
|
}
|
|
1089
|
+
if (!this.structures)
|
|
1090
|
+
this.structures = [];
|
|
1046
1091
|
if (this.saveStructures)
|
|
1047
1092
|
this.saveShared = this.saveStructures;
|
|
1048
1093
|
let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues;
|
|
@@ -1090,6 +1135,10 @@
|
|
|
1090
1135
|
} else if (encodeOptions === REUSE_BUFFER_MODE)
|
|
1091
1136
|
position$1 = (position$1 + 7) & 0x7ffffff8; // Word align to make any future copying of this buffer faster
|
|
1092
1137
|
start = position$1;
|
|
1138
|
+
if (encoder.useSelfDescribedHeader) {
|
|
1139
|
+
targetView.setUint32(position$1, 0xd9d9f700); // tag two byte, then self-descriptive tag
|
|
1140
|
+
position$1 += 3;
|
|
1141
|
+
}
|
|
1093
1142
|
referenceMap = encoder.structuredClone ? new Map() : null;
|
|
1094
1143
|
if (encoder.bundleStrings && typeof value !== 'string') {
|
|
1095
1144
|
bundledStrings$1 = [];
|
|
@@ -1123,6 +1172,8 @@
|
|
|
1123
1172
|
continue
|
|
1124
1173
|
let nextTransition, transition = sharedStructures.transitions;
|
|
1125
1174
|
for (let j = 0, l = keys.length; j < l; j++) {
|
|
1175
|
+
if (transition[RECORD_SYMBOL] === undefined)
|
|
1176
|
+
transition[RECORD_SYMBOL] = i;
|
|
1126
1177
|
let key = keys[j];
|
|
1127
1178
|
nextTransition = transition[key];
|
|
1128
1179
|
if (!nextTransition) {
|
|
@@ -1130,7 +1181,7 @@
|
|
|
1130
1181
|
}
|
|
1131
1182
|
transition = nextTransition;
|
|
1132
1183
|
}
|
|
1133
|
-
transition[RECORD_SYMBOL] = i;
|
|
1184
|
+
transition[RECORD_SYMBOL] = i | 0x100000;
|
|
1134
1185
|
}
|
|
1135
1186
|
}
|
|
1136
1187
|
if (!isSequential)
|
|
@@ -1182,11 +1233,13 @@
|
|
|
1182
1233
|
target.end = position$1;
|
|
1183
1234
|
return target
|
|
1184
1235
|
}
|
|
1185
|
-
return target.subarray(start, position$1) // position can change if we call
|
|
1236
|
+
return target.subarray(start, position$1) // position can change if we call encode again in saveShared, so we get the buffer now
|
|
1186
1237
|
} finally {
|
|
1187
1238
|
if (sharedStructures) {
|
|
1188
1239
|
if (serializationsSinceTransitionRebuild < 10)
|
|
1189
1240
|
serializationsSinceTransitionRebuild++;
|
|
1241
|
+
if (sharedStructures.length > maxSharedStructures)
|
|
1242
|
+
sharedStructures.length = maxSharedStructures;
|
|
1190
1243
|
if (transitionsCount > 10000) {
|
|
1191
1244
|
// force a rebuild occasionally after a lot of transitions so it can get cleaned up
|
|
1192
1245
|
sharedStructures.transitions = null;
|
|
@@ -1199,6 +1252,7 @@
|
|
|
1199
1252
|
recordIdsToRemove[i][RECORD_SYMBOL] = undefined;
|
|
1200
1253
|
}
|
|
1201
1254
|
recordIdsToRemove = [];
|
|
1255
|
+
//sharedStructures.nextId = maxSharedStructures
|
|
1202
1256
|
}
|
|
1203
1257
|
}
|
|
1204
1258
|
if (hasSharedUpdate && encoder.saveShared) {
|
|
@@ -1498,7 +1552,9 @@
|
|
|
1498
1552
|
let extensionClass = extensionClasses[i];
|
|
1499
1553
|
if (value instanceof extensionClass) {
|
|
1500
1554
|
let extension = extensions[i];
|
|
1501
|
-
let tag = extension.tag
|
|
1555
|
+
let tag = extension.tag;
|
|
1556
|
+
if (tag == undefined)
|
|
1557
|
+
tag = extension.getTag && extension.getTag(value);
|
|
1502
1558
|
if (tag < 0x18) {
|
|
1503
1559
|
target[position$1++] = 0xc0 | tag;
|
|
1504
1560
|
} else if (tag < 0x100) {
|
|
@@ -1609,88 +1665,74 @@
|
|
|
1609
1665
|
target[objectOffset++ + start] = size >> 8;
|
|
1610
1666
|
target[objectOffset + start] = size & 0xff;
|
|
1611
1667
|
} :
|
|
1612
|
-
|
|
1613
|
-
/* sharedStructures ? // For highly stable structures, using for-in can a little bit faster
|
|
1614
1668
|
(object, safePrototype) => {
|
|
1615
|
-
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
|
|
1616
|
-
let
|
|
1617
|
-
let
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1669
|
+
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
|
|
1670
|
+
let newTransitions = 0;
|
|
1671
|
+
let length = 0;
|
|
1672
|
+
let parentRecordId;
|
|
1673
|
+
let keys;
|
|
1674
|
+
if (this.keyMap) {
|
|
1675
|
+
keys = Object.keys(object).map(k => this.encodeKey(k));
|
|
1676
|
+
length = keys.length;
|
|
1677
|
+
for (let i = 0; i < length; i++) {
|
|
1678
|
+
let key = keys[i];
|
|
1679
|
+
nextTransition = transition[key];
|
|
1621
1680
|
if (!nextTransition) {
|
|
1622
|
-
nextTransition = transition[key] = Object.create(null)
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
nextTransition.__keys__ = keys.slice(0, i + 1)
|
|
1634
|
-
}
|
|
1681
|
+
nextTransition = transition[key] = Object.create(null);
|
|
1682
|
+
newTransitions++;
|
|
1683
|
+
}
|
|
1684
|
+
transition = nextTransition;
|
|
1685
|
+
}
|
|
1686
|
+
} else {
|
|
1687
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
1688
|
+
nextTransition = transition[key];
|
|
1689
|
+
if (!nextTransition) {
|
|
1690
|
+
if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
|
|
1691
|
+
parentRecordId = transition[RECORD_SYMBOL] & 0xffff;
|
|
1635
1692
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
target.copy(target, )
|
|
1639
|
-
objectOffset
|
|
1693
|
+
nextTransition = transition[key] = Object.create(null);
|
|
1694
|
+
newTransitions++;
|
|
1640
1695
|
}
|
|
1641
|
-
transition = nextTransition
|
|
1642
|
-
|
|
1696
|
+
transition = nextTransition;
|
|
1697
|
+
length++;
|
|
1643
1698
|
}
|
|
1644
1699
|
}
|
|
1645
|
-
let id = transition.id
|
|
1646
|
-
if (!id) {
|
|
1647
|
-
id = transition.id = structures.push(transition.__keys__) + 63
|
|
1648
|
-
if (sharedStructures.onUpdate)
|
|
1649
|
-
sharedStructures.onUpdate(id, transition.__keys__)
|
|
1650
|
-
}
|
|
1651
|
-
target[objectOffset + start] = id
|
|
1652
|
-
}*/
|
|
1653
|
-
(object) => {
|
|
1654
|
-
let keys = Object.keys(object);
|
|
1655
|
-
let vals = Object.values(object);
|
|
1656
|
-
if (this.keyMap) keys = keys.map(k => this.encodeKey(k));
|
|
1657
|
-
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
|
|
1658
|
-
let newTransitions = 0;
|
|
1659
|
-
let length = keys.length;
|
|
1660
|
-
//let parentRecordId
|
|
1661
|
-
for (let i = 0; i < length; i++) {
|
|
1662
|
-
//if (!parentRecordId)
|
|
1663
|
-
let key = keys[i];
|
|
1664
|
-
// parentRecordId = transition[RECORD_SYMBOL]
|
|
1665
|
-
nextTransition = transition[key];
|
|
1666
|
-
if (!nextTransition) {
|
|
1667
|
-
nextTransition = transition[key] = Object.create(null);
|
|
1668
|
-
newTransitions++;
|
|
1669
|
-
}
|
|
1670
|
-
transition = nextTransition;
|
|
1671
|
-
}
|
|
1672
1700
|
let recordId = transition[RECORD_SYMBOL];
|
|
1673
1701
|
if (recordId !== undefined) {
|
|
1702
|
+
recordId &= 0xffff;
|
|
1674
1703
|
target[position$1++] = 0xd9;
|
|
1675
1704
|
target[position$1++] = (recordId >> 8) | 0xe0;
|
|
1676
1705
|
target[position$1++] = recordId & 0xff;
|
|
1677
1706
|
} else {
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
structures.nextId
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1707
|
+
if (!keys)
|
|
1708
|
+
keys = transition.__keys__ || (transition.__keys__ = Object.keys(object));
|
|
1709
|
+
if (parentRecordId === undefined) {
|
|
1710
|
+
recordId = structures.nextId++;
|
|
1711
|
+
if (!recordId) {
|
|
1712
|
+
recordId = 0;
|
|
1713
|
+
structures.nextId = 1;
|
|
1714
|
+
}
|
|
1715
|
+
if (recordId >= MAX_STRUCTURES) {// cycle back around
|
|
1716
|
+
structures.nextId = (recordId = maxSharedStructures) + 1;
|
|
1717
|
+
}
|
|
1718
|
+
} else {
|
|
1719
|
+
recordId = parentRecordId;
|
|
1685
1720
|
}
|
|
1686
|
-
transition[RECORD_SYMBOL] = recordId;
|
|
1687
1721
|
structures[recordId] = keys;
|
|
1688
|
-
if (
|
|
1722
|
+
if (recordId < maxSharedStructures) {
|
|
1689
1723
|
target[position$1++] = 0xd9;
|
|
1690
1724
|
target[position$1++] = (recordId >> 8) | 0xe0;
|
|
1691
1725
|
target[position$1++] = recordId & 0xff;
|
|
1726
|
+
transition = structures.transitions;
|
|
1727
|
+
for (let i = 0; i < length; i++) {
|
|
1728
|
+
if (transition[RECORD_SYMBOL] === undefined || (transition[RECORD_SYMBOL] & 0x100000))
|
|
1729
|
+
transition[RECORD_SYMBOL] = recordId;
|
|
1730
|
+
transition = transition[keys[i]];
|
|
1731
|
+
}
|
|
1732
|
+
transition[RECORD_SYMBOL] = recordId | 0x100000; // indicates it is a extendable terminal
|
|
1692
1733
|
hasSharedUpdate = true;
|
|
1693
1734
|
} else {
|
|
1735
|
+
transition[RECORD_SYMBOL] = recordId;
|
|
1694
1736
|
targetView.setUint32(position$1, 0xd9dfff00); // tag two byte, then record definition id
|
|
1695
1737
|
position$1 += 3;
|
|
1696
1738
|
if (newTransitions)
|
|
@@ -1711,7 +1753,9 @@
|
|
|
1711
1753
|
} else {
|
|
1712
1754
|
writeArrayHeader(length);
|
|
1713
1755
|
}
|
|
1714
|
-
for (let
|
|
1756
|
+
for (let key in object)
|
|
1757
|
+
if (safePrototype || object.hasOwnProperty(key))
|
|
1758
|
+
encode(object[key]);
|
|
1715
1759
|
};
|
|
1716
1760
|
const makeRoom = (end) => {
|
|
1717
1761
|
let newSize;
|
|
@@ -1750,15 +1794,22 @@
|
|
|
1750
1794
|
updateSharedData() {
|
|
1751
1795
|
let lastVersion = this.sharedVersion || 0;
|
|
1752
1796
|
this.sharedVersion = lastVersion + 1;
|
|
1753
|
-
let
|
|
1797
|
+
let structuresCopy = this.structures.slice(0);
|
|
1798
|
+
let sharedData = new SharedData(structuresCopy, this.sharedValues, this.sharedVersion);
|
|
1799
|
+
let saveResults = this.saveShared(sharedData,
|
|
1754
1800
|
existingShared => (existingShared && existingShared.version || 0) == lastVersion);
|
|
1755
1801
|
if (saveResults === false) {
|
|
1756
1802
|
// get updated structures and try again if the update failed
|
|
1757
|
-
|
|
1803
|
+
sharedData = this.getShared() || {};
|
|
1758
1804
|
this.structures = sharedData.structures || [];
|
|
1759
1805
|
this.sharedValues = sharedData.packedValues;
|
|
1760
1806
|
this.sharedVersion = sharedData.version;
|
|
1807
|
+
this.structures.nextId = this.structures.length;
|
|
1808
|
+
} else {
|
|
1809
|
+
// restore structures
|
|
1810
|
+
structuresCopy.forEach((structure, i) => this.structures[i] = structure);
|
|
1761
1811
|
}
|
|
1812
|
+
// 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
|
|
1762
1813
|
return saveResults
|
|
1763
1814
|
}
|
|
1764
1815
|
}
|
|
@@ -1836,7 +1887,7 @@
|
|
|
1836
1887
|
case 'function': console.log(value);
|
|
1837
1888
|
}
|
|
1838
1889
|
}
|
|
1839
|
-
|
|
1890
|
+
const isLittleEndianMachine$1 = new Uint8Array(new Uint16Array([1]).buffer)[0] == 1;
|
|
1840
1891
|
extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
|
|
1841
1892
|
Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
|
|
1842
1893
|
typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
|
|
@@ -1891,17 +1942,17 @@
|
|
|
1891
1942
|
encode(arrayBuffer, encode, makeRoom) {
|
|
1892
1943
|
writeBuffer(arrayBuffer, makeRoom);
|
|
1893
1944
|
}
|
|
1894
|
-
}, typedArrayEncoder(64),
|
|
1895
|
-
typedArrayEncoder(68),
|
|
1896
|
-
typedArrayEncoder(69),
|
|
1897
|
-
typedArrayEncoder(70),
|
|
1898
|
-
typedArrayEncoder(71),
|
|
1899
|
-
typedArrayEncoder(72),
|
|
1900
|
-
typedArrayEncoder(77),
|
|
1901
|
-
typedArrayEncoder(78),
|
|
1902
|
-
typedArrayEncoder(79),
|
|
1903
|
-
typedArrayEncoder(
|
|
1904
|
-
typedArrayEncoder(
|
|
1945
|
+
}, typedArrayEncoder(64, 1),
|
|
1946
|
+
typedArrayEncoder(68, 1),
|
|
1947
|
+
typedArrayEncoder(69, 2),
|
|
1948
|
+
typedArrayEncoder(70, 4),
|
|
1949
|
+
typedArrayEncoder(71, 8),
|
|
1950
|
+
typedArrayEncoder(72, 1),
|
|
1951
|
+
typedArrayEncoder(77, 2),
|
|
1952
|
+
typedArrayEncoder(78, 4),
|
|
1953
|
+
typedArrayEncoder(79, 8),
|
|
1954
|
+
typedArrayEncoder(85, 4),
|
|
1955
|
+
typedArrayEncoder(86, 8),
|
|
1905
1956
|
{
|
|
1906
1957
|
encode(sharedData, encode) { // write SharedData
|
|
1907
1958
|
let packedValues = sharedData.packedValues || [];
|
|
@@ -1930,8 +1981,9 @@
|
|
|
1930
1981
|
encode(new Tag(sharedData.version, 0x53687264));
|
|
1931
1982
|
}
|
|
1932
1983
|
}];
|
|
1933
|
-
|
|
1934
|
-
|
|
1984
|
+
function typedArrayEncoder(tag, size) {
|
|
1985
|
+
if (!isLittleEndianMachine$1 && size > 1)
|
|
1986
|
+
tag -= 4; // the big endian equivalents are 4 less
|
|
1935
1987
|
return {
|
|
1936
1988
|
tag: tag,
|
|
1937
1989
|
encode: function writeExtBuffer(typedArray, encode) {
|