cbor-x 0.8.2 → 0.9.2
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/.vscode/launch.json +22 -0
- package/README.md +54 -18
- package/SECURITY.md +11 -0
- package/{unpack.d.ts → decode.d.ts} +12 -11
- package/decode.js +134 -33
- package/dist/index.js +400 -57
- package/dist/index.min.js +83 -53
- package/dist/node.cjs +419 -80
- package/dist/test.js +82 -1
- package/encode.d.ts +6 -0
- package/encode.js +181 -24
- package/index.d.ts +7 -6
- package/index.js +2 -2
- package/iterators.js +85 -0
- package/{node.js → node-index.js} +4 -3
- package/package.json +17 -17
- package/rollup.config.js +2 -2
- package/stream.js +20 -22
- package/tests/benchmark.cjs +48 -8
- package/tests/test-compatibility.cjs +1 -0
- package/tests/test-incomplete.js +41 -0
- package/tests/test-node-iterators.js +72 -0
- package/tests/test-node-stream.js +11 -1
- package/tests/test.js +82 -2
- package/pack.d.ts +0 -9
package/dist/test.js
CHANGED
|
@@ -581,7 +581,7 @@
|
|
|
581
581
|
outcomesScore: outcomesScore
|
|
582
582
|
};
|
|
583
583
|
|
|
584
|
-
//import inspector from 'inspector'; inspector.open(
|
|
584
|
+
//import inspector from 'inspector'; inspector.open(9229, null, true); debugger
|
|
585
585
|
function tryRequire(module) {
|
|
586
586
|
try {
|
|
587
587
|
return require(module)
|
|
@@ -659,6 +659,13 @@
|
|
|
659
659
|
assert.deepEqual(deserialized, data);
|
|
660
660
|
});
|
|
661
661
|
|
|
662
|
+
test('255 chars', function() {
|
|
663
|
+
const data = 'RRZG9A6I7xupPeOZhxcOcioFsuhszGOdyDUcbRf4Zef2kdPIfC9RaLO4jTM5JhuZvTsF09fbRHMGtqk7YAgu3vespeTe9l61ziZ6VrMnYu2CamK96wCkmz0VUXyqaiUoTPgzk414LS9yYrd5uh7w18ksJF5SlC2e91rukWvNqAZJjYN3jpkqHNOFchCwFrhbxq2Lrv1kSJPYCx9blRg2hGmYqTbElLTZHv20iNqwZeQbRMgSBPT6vnbCBPnOh1W';
|
|
664
|
+
var serialized = CBOR.encode(data);
|
|
665
|
+
var deserialized = CBOR.decode(serialized);
|
|
666
|
+
assert.equal(deserialized, data);
|
|
667
|
+
});
|
|
668
|
+
|
|
662
669
|
test('encode/decode sample data', function(){
|
|
663
670
|
var data = sampleData;
|
|
664
671
|
var serialized = CBOR.encode(data);
|
|
@@ -676,6 +683,55 @@
|
|
|
676
683
|
var deserialized = encoder.decode(serialized);
|
|
677
684
|
assert.deepEqual(deserialized, data);
|
|
678
685
|
});
|
|
686
|
+
test('encode/decode sample data with packing', function(){
|
|
687
|
+
var data = sampleData;
|
|
688
|
+
let encoder = new Encoder({ pack: true, useRecords: false });
|
|
689
|
+
var serialized = encoder.encode(data);
|
|
690
|
+
var deserialized = encoder.decode(serialized);
|
|
691
|
+
assert.deepEqual(deserialized, data);
|
|
692
|
+
});
|
|
693
|
+
test('encode/decode sample data with packing and records', function(){
|
|
694
|
+
var data = sampleData;
|
|
695
|
+
let structures = [];
|
|
696
|
+
let encoder = new Encoder({ structures, pack: true, useRecords: true });
|
|
697
|
+
var serialized = encoder.encode(data);
|
|
698
|
+
var deserialized = encoder.decode(serialized);
|
|
699
|
+
assert.deepEqual(deserialized, data);
|
|
700
|
+
});
|
|
701
|
+
test('encode/decode sample data with shared packing and records', function(){
|
|
702
|
+
let encoder = new Encoder({ useRecords: true });
|
|
703
|
+
let finishPack = encoder.findCommonStringsToPack();
|
|
704
|
+
for (let i = 0; i < 20; i++) {
|
|
705
|
+
let data = {
|
|
706
|
+
shouldShare: 'same each time',
|
|
707
|
+
shouldShare2: 'same each time 2',
|
|
708
|
+
shouldntShare: 'different each time ' + i
|
|
709
|
+
};
|
|
710
|
+
if (i == 10)
|
|
711
|
+
finishPack({});
|
|
712
|
+
var serialized = encoder.encode(data);
|
|
713
|
+
var deserialized = encoder.decode(serialized);
|
|
714
|
+
assert.deepEqual(deserialized, data);
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
test('encode/decode sample data with individual packing, shared packing and records', function(){
|
|
718
|
+
let encoder = new Encoder({ pack: true, useRecords: true });
|
|
719
|
+
let finishPack = encoder.findCommonStringsToPack();
|
|
720
|
+
for (let i = 0; i < 20; i++) {
|
|
721
|
+
let data = {
|
|
722
|
+
shouldShare: 'same each time',
|
|
723
|
+
shouldShare2: 'same each time',
|
|
724
|
+
shouldntShare: 'different each time ' + i,
|
|
725
|
+
shouldntShare2: 'different each time ' + i,
|
|
726
|
+
noPack: 'no packing ' + i,
|
|
727
|
+
};
|
|
728
|
+
if (i == 10)
|
|
729
|
+
finishPack({ threshold: 5 });
|
|
730
|
+
var serialized = encoder.encode(data);
|
|
731
|
+
var deserialized = encoder.decode(serialized);
|
|
732
|
+
assert.deepEqual(deserialized, data);
|
|
733
|
+
}
|
|
734
|
+
});
|
|
679
735
|
if (typeof Buffer != 'undefined')
|
|
680
736
|
test('replace data', function(){
|
|
681
737
|
var data1 = {
|
|
@@ -787,10 +843,13 @@
|
|
|
787
843
|
let fa = new Float32Array(b.buffer, 8, 2);
|
|
788
844
|
fa[0] = 2.25;
|
|
789
845
|
fa[1] = 6;
|
|
846
|
+
let map = new Map();
|
|
847
|
+
map.set('key', 'value');
|
|
790
848
|
let object = {
|
|
791
849
|
error: new Error('test'),
|
|
792
850
|
set: new Set(['a', 'b']),
|
|
793
851
|
regexp: /test/gi,
|
|
852
|
+
map,
|
|
794
853
|
float32Array: fa,
|
|
795
854
|
uint16Array: new Uint16Array([3,4])
|
|
796
855
|
};
|
|
@@ -800,6 +859,7 @@
|
|
|
800
859
|
var serialized = encoder.encode(object);
|
|
801
860
|
var deserialized = encoder.decode(serialized);
|
|
802
861
|
assert.deepEqual(Array.from(deserialized.set), Array.from(object.set));
|
|
862
|
+
assert.equal(deserialized.map.get('key'), 'value');
|
|
803
863
|
assert.equal(deserialized.error.message, object.error.message);
|
|
804
864
|
assert.equal(deserialized.regexp.test('TEST'), true);
|
|
805
865
|
assert.equal(deserialized.float32Array.constructor.name, 'Float32Array');
|
|
@@ -810,6 +870,19 @@
|
|
|
810
870
|
assert.equal(deserialized.uint16Array[1], 4);
|
|
811
871
|
});
|
|
812
872
|
|
|
873
|
+
test('explicit maps and sets', function () {
|
|
874
|
+
let map = new Map();
|
|
875
|
+
map.set('key', { inside: 'value'});
|
|
876
|
+
let object = {
|
|
877
|
+
set: new Set(['a', 'b']),
|
|
878
|
+
map,
|
|
879
|
+
};
|
|
880
|
+
var serialized = encode(object); // default encoder
|
|
881
|
+
var deserialized = decode(serialized);
|
|
882
|
+
assert.deepEqual(Array.from(deserialized.set), Array.from(object.set));
|
|
883
|
+
assert.equal(deserialized.map.get('key').inside, 'value');
|
|
884
|
+
});
|
|
885
|
+
|
|
813
886
|
test('object without prototype', function(){
|
|
814
887
|
var data = Object.create(null);
|
|
815
888
|
data.test = 3;
|
|
@@ -825,6 +898,13 @@
|
|
|
825
898
|
var decoded = decode(encoded);
|
|
826
899
|
assert.equal(decoded.length, size);
|
|
827
900
|
});
|
|
901
|
+
test('little buffer', function() {
|
|
902
|
+
var data = typeof Buffer == 'undefined' ? new Uint8Array(0) : Buffer.alloc(0);
|
|
903
|
+
var encoded = encode(data);
|
|
904
|
+
assert.equal(encoded.length, 1); // make sure to use canonical form
|
|
905
|
+
var decoded = decode(encoded);
|
|
906
|
+
assert.equal(decoded.length, 0);
|
|
907
|
+
});
|
|
828
908
|
|
|
829
909
|
test('random strings', function(){
|
|
830
910
|
var data = [];
|
|
@@ -872,6 +952,7 @@
|
|
|
872
952
|
let encoder = new Encoder({
|
|
873
953
|
mapsAsObjects: true,
|
|
874
954
|
useTimestamp32: true,
|
|
955
|
+
useTag259ForMaps: false,
|
|
875
956
|
});
|
|
876
957
|
var serialized = encoder.encode(data);
|
|
877
958
|
var deserialized = encoder.decode(serialized);
|
package/encode.d.ts
ADDED
package/encode.js
CHANGED
|
@@ -10,6 +10,7 @@ const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
|
|
|
10
10
|
const RECORD_STARTING_ID_PREFIX = 0x69 // tag 105/0x69
|
|
11
11
|
const MAX_STRUCTURES = 0x100
|
|
12
12
|
const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000
|
|
13
|
+
let serializationId = 1
|
|
13
14
|
let target
|
|
14
15
|
let targetView
|
|
15
16
|
let position = 0
|
|
@@ -25,6 +26,7 @@ export class Encoder extends Decoder {
|
|
|
25
26
|
let hasSharedUpdate
|
|
26
27
|
let structures
|
|
27
28
|
let referenceMap
|
|
29
|
+
options = options || {}
|
|
28
30
|
let lastSharedStructuresLength = 0
|
|
29
31
|
let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
|
|
30
32
|
return target.utf8Write(string, position, maxBytes)
|
|
@@ -35,19 +37,25 @@ export class Encoder extends Decoder {
|
|
|
35
37
|
|
|
36
38
|
let encoder = this
|
|
37
39
|
let maxSharedStructures = 64
|
|
38
|
-
let isSequential = options
|
|
40
|
+
let isSequential = options.sequential
|
|
39
41
|
if (isSequential) {
|
|
40
42
|
maxSharedStructures = 0
|
|
41
43
|
this.structures = []
|
|
42
44
|
}
|
|
45
|
+
let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues
|
|
46
|
+
let sharedPackedObjectMap
|
|
47
|
+
if (sharedValues) {
|
|
48
|
+
sharedPackedObjectMap = Object.create(null)
|
|
49
|
+
for (let i = 0, l = sharedValues.length; i < l; i++) {
|
|
50
|
+
sharedPackedObjectMap[sharedValues[i]] = i
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
}
|
|
43
54
|
let recordIdsToRemove = []
|
|
44
55
|
let transitionsCount = 0
|
|
45
56
|
let serializationsSinceTransitionRebuild = 0
|
|
46
|
-
if (this.structures && this.structures.length > maxSharedStructures) {
|
|
47
|
-
throw new Error('Too many shared structures')
|
|
48
|
-
}
|
|
49
57
|
|
|
50
|
-
this.encode = function(value) {
|
|
58
|
+
this.encode = function(value, encodeOptions) {
|
|
51
59
|
if (!target) {
|
|
52
60
|
target = new ByteArrayAllocate(8192)
|
|
53
61
|
targetView = new DataView(target.buffer, 0, 8192)
|
|
@@ -60,7 +68,8 @@ export class Encoder extends Decoder {
|
|
|
60
68
|
targetView = new DataView(target.buffer, 0, target.length)
|
|
61
69
|
safeEnd = target.length - 10
|
|
62
70
|
position = 0
|
|
63
|
-
}
|
|
71
|
+
} else if (encodeOptions === REUSE_BUFFER_MODE)
|
|
72
|
+
position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
|
|
64
73
|
start = position
|
|
65
74
|
referenceMap = encoder.structuredClone ? new Map() : null
|
|
66
75
|
sharedStructures = encoder.structures
|
|
@@ -68,7 +77,7 @@ export class Encoder extends Decoder {
|
|
|
68
77
|
if (sharedStructures.uninitialized)
|
|
69
78
|
encoder.structures = sharedStructures = encoder.getStructures()
|
|
70
79
|
let sharedStructuresLength = sharedStructures.length
|
|
71
|
-
if (sharedStructuresLength >
|
|
80
|
+
if (sharedStructuresLength > maxSharedStructures && !isSequential)
|
|
72
81
|
sharedStructuresLength = maxSharedStructures
|
|
73
82
|
if (!sharedStructures.transitions) {
|
|
74
83
|
// rebuild our structure transitions
|
|
@@ -78,8 +87,8 @@ export class Encoder extends Decoder {
|
|
|
78
87
|
if (!keys)
|
|
79
88
|
continue
|
|
80
89
|
let nextTransition, transition = sharedStructures.transitions
|
|
81
|
-
for (let
|
|
82
|
-
let key = keys[
|
|
90
|
+
for (let j = 0, l = keys.length; j < l; j++) {
|
|
91
|
+
let key = keys[j]
|
|
83
92
|
nextTransition = transition[key]
|
|
84
93
|
if (!nextTransition) {
|
|
85
94
|
nextTransition = transition[key] = Object.create(null)
|
|
@@ -96,6 +105,29 @@ export class Encoder extends Decoder {
|
|
|
96
105
|
if (hasSharedUpdate)
|
|
97
106
|
hasSharedUpdate = false
|
|
98
107
|
structures = sharedStructures || []
|
|
108
|
+
packedObjectMap = sharedPackedObjectMap
|
|
109
|
+
if (options.pack) {
|
|
110
|
+
let packedValues = new Map()
|
|
111
|
+
packedValues.values = []
|
|
112
|
+
packedValues.encoder = encoder
|
|
113
|
+
packedValues.maxValues = options.maxPrivatePackedValues || (sharedPackedObjectMap ? 16 : Infinity)
|
|
114
|
+
packedValues.objectMap = sharedPackedObjectMap || false
|
|
115
|
+
packedValues.samplingPackedValues = samplingPackedValues
|
|
116
|
+
findRepetitiveStrings(value, packedValues)
|
|
117
|
+
if (packedValues.values.length > 0) {
|
|
118
|
+
target[position++] = 0xd8 // one-byte tag
|
|
119
|
+
target[position++] = 51 // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
|
|
120
|
+
writeArrayHeader(4)
|
|
121
|
+
let valuesArray = packedValues.values
|
|
122
|
+
encode(valuesArray)
|
|
123
|
+
writeArrayHeader(0) // prefixes
|
|
124
|
+
writeArrayHeader(0) // suffixes
|
|
125
|
+
packedObjectMap = Object.create(sharedPackedObjectMap || null)
|
|
126
|
+
for (let i = 0, l = valuesArray.length; i < l; i++) {
|
|
127
|
+
packedObjectMap[valuesArray[i]] = i
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
99
131
|
try {
|
|
100
132
|
encode(value)
|
|
101
133
|
encoder.offset = position // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
|
|
@@ -108,7 +140,12 @@ export class Encoder extends Decoder {
|
|
|
108
140
|
referenceMap = null
|
|
109
141
|
return serialized
|
|
110
142
|
}
|
|
111
|
-
|
|
143
|
+
if (encodeOptions === REUSE_BUFFER_MODE) {
|
|
144
|
+
target.start = start
|
|
145
|
+
target.end = position
|
|
146
|
+
return target
|
|
147
|
+
}
|
|
148
|
+
return target.subarray(start, position) // position can change if we call pack again in saveStructures, so we get the buffer now
|
|
112
149
|
} finally {
|
|
113
150
|
if (sharedStructures) {
|
|
114
151
|
if (serializationsSinceTransitionRebuild < 10)
|
|
@@ -122,7 +159,7 @@ export class Encoder extends Decoder {
|
|
|
122
159
|
recordIdsToRemove = []
|
|
123
160
|
} else if (recordIdsToRemove.length > 0 && !isSequential) {
|
|
124
161
|
for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
|
|
125
|
-
recordIdsToRemove[i][RECORD_SYMBOL] =
|
|
162
|
+
recordIdsToRemove[i][RECORD_SYMBOL] = undefined
|
|
126
163
|
}
|
|
127
164
|
recordIdsToRemove = []
|
|
128
165
|
}
|
|
@@ -130,15 +167,41 @@ export class Encoder extends Decoder {
|
|
|
130
167
|
if (encoder.structures.length > maxSharedStructures) {
|
|
131
168
|
encoder.structures = encoder.structures.slice(0, maxSharedStructures)
|
|
132
169
|
}
|
|
170
|
+
// we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
|
|
171
|
+
let returnBuffer = target.subarray(start, position)
|
|
172
|
+
let shared = encoder.structures || []
|
|
173
|
+
if (sharedValues) {
|
|
174
|
+
shared = shared.concat(sharedValues)
|
|
175
|
+
}
|
|
133
176
|
|
|
134
177
|
if (encoder.saveStructures(encoder.structures, lastSharedStructuresLength) === false) {
|
|
135
178
|
// get updated structures and try again if the update failed
|
|
136
179
|
encoder.structures = encoder.getStructures() || []
|
|
137
180
|
return encoder.encode(value)
|
|
138
181
|
}
|
|
139
|
-
lastSharedStructuresLength =
|
|
182
|
+
lastSharedStructuresLength = shared.length
|
|
183
|
+
return returnBuffer
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
this.findCommonStringsToPack = () => {
|
|
189
|
+
samplingPackedValues = new Map()
|
|
190
|
+
if (!sharedPackedObjectMap)
|
|
191
|
+
sharedPackedObjectMap = Object.create(null)
|
|
192
|
+
return ({ threshold }) => {
|
|
193
|
+
threshold = threshold || 4
|
|
194
|
+
let position = this.pack ? options.maxPrivatePackedValues || 16 : 0
|
|
195
|
+
if (!sharedValues)
|
|
196
|
+
sharedValues = this.sharedValues = []
|
|
197
|
+
for (let [ key, status ] of samplingPackedValues) {
|
|
198
|
+
if (status.count > threshold) {
|
|
199
|
+
sharedPackedObjectMap[key] = position++
|
|
200
|
+
sharedValues.push(key)
|
|
201
|
+
hasSharedUpdate = true
|
|
140
202
|
}
|
|
141
203
|
}
|
|
204
|
+
samplingPackedValues = null
|
|
142
205
|
}
|
|
143
206
|
}
|
|
144
207
|
const encode = (value) => {
|
|
@@ -148,6 +211,43 @@ export class Encoder extends Decoder {
|
|
|
148
211
|
var type = typeof value
|
|
149
212
|
var length
|
|
150
213
|
if (type === 'string') {
|
|
214
|
+
if (packedObjectMap) {
|
|
215
|
+
let packedPosition = packedObjectMap[value]
|
|
216
|
+
if (packedPosition >= 0) {
|
|
217
|
+
if (packedPosition < 16)
|
|
218
|
+
target[position++] = packedPosition + 0xe0 // simple values, defined in https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
|
|
219
|
+
else {
|
|
220
|
+
target[position++] = 0xc6 // tag 6 defined in https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
|
|
221
|
+
if (packedPosition & 1)
|
|
222
|
+
encode((15 - packedPosition) >> 1)
|
|
223
|
+
else
|
|
224
|
+
encode((packedPosition - 16) >> 1)
|
|
225
|
+
}
|
|
226
|
+
return
|
|
227
|
+
/* } else if (packedStatus.serializationId != serializationId) {
|
|
228
|
+
packedStatus.serializationId = serializationId
|
|
229
|
+
packedStatus.count = 1
|
|
230
|
+
if (options.sharedPack) {
|
|
231
|
+
let sharedCount = packedStatus.sharedCount = (packedStatus.sharedCount || 0) + 1
|
|
232
|
+
if (shareCount > (options.sharedPack.threshold || 5)) {
|
|
233
|
+
let sharedPosition = packedStatus.position = packedStatus.nextSharedPosition
|
|
234
|
+
hasSharedUpdate = true
|
|
235
|
+
if (sharedPosition < 16)
|
|
236
|
+
target[position++] = sharedPosition + 0xc0
|
|
237
|
+
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
} // else any in-doc incrementation?*/
|
|
241
|
+
} else if (samplingPackedValues && !options.pack) {
|
|
242
|
+
let status = samplingPackedValues.get(value)
|
|
243
|
+
if (status)
|
|
244
|
+
status.count++
|
|
245
|
+
else
|
|
246
|
+
samplingPackedValues.set(value, {
|
|
247
|
+
count: 1,
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
}
|
|
151
251
|
let strLength = value.length
|
|
152
252
|
let headerSize
|
|
153
253
|
// first we estimate the header size, so we can write to the correct location
|
|
@@ -257,7 +357,7 @@ export class Encoder extends Decoder {
|
|
|
257
357
|
targetView.setFloat32(position, value)
|
|
258
358
|
let xShifted
|
|
259
359
|
if (useFloat32 < 4 ||
|
|
260
|
-
|
|
360
|
+
// this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
|
|
261
361
|
((xShifted = value * mult10[((target[position] & 0x7f) << 1) | (target[position + 1] >> 7)]) >> 0) === xShifted) {
|
|
262
362
|
position += 4
|
|
263
363
|
return
|
|
@@ -303,6 +403,12 @@ export class Encoder extends Decoder {
|
|
|
303
403
|
encode(value[i])
|
|
304
404
|
}
|
|
305
405
|
} else if (constructor === Map) {
|
|
406
|
+
if (this.mapsAsObjects ? this.useTag259ForMaps !== false : this.useTag259ForMaps) {
|
|
407
|
+
// use Tag 259 (https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec--explicit-maps.md) for maps if the user wants it that way
|
|
408
|
+
target[position++] = 0xd9
|
|
409
|
+
target[position++] = 1
|
|
410
|
+
target[position++] = 3
|
|
411
|
+
}
|
|
306
412
|
length = value.size
|
|
307
413
|
if (length < 0x18) {
|
|
308
414
|
target[position++] = 0xa0 | length
|
|
@@ -380,10 +486,9 @@ export class Encoder extends Decoder {
|
|
|
380
486
|
}
|
|
381
487
|
position += 8
|
|
382
488
|
} else if (type === 'undefined') {
|
|
383
|
-
//target[position++] = 0xc1 // this is the "never-used" byte
|
|
384
489
|
target[position++] = 0xf7
|
|
385
490
|
} else {
|
|
386
|
-
throw new Error('Unknown type ' + type)
|
|
491
|
+
throw new Error('Unknown type: ' + type)
|
|
387
492
|
}
|
|
388
493
|
}
|
|
389
494
|
|
|
@@ -472,7 +577,7 @@ export class Encoder extends Decoder {
|
|
|
472
577
|
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
|
|
473
578
|
let newTransitions = 0
|
|
474
579
|
let length = keys.length
|
|
475
|
-
for (let i =0; i < length; i++) {
|
|
580
|
+
for (let i = 0; i < length; i++) {
|
|
476
581
|
let key = keys[i]
|
|
477
582
|
nextTransition = transition[key]
|
|
478
583
|
if (!nextTransition) {
|
|
@@ -482,7 +587,7 @@ export class Encoder extends Decoder {
|
|
|
482
587
|
transition = nextTransition
|
|
483
588
|
}
|
|
484
589
|
let recordId = transition[RECORD_SYMBOL]
|
|
485
|
-
if (recordId) {
|
|
590
|
+
if (recordId !== undefined) {
|
|
486
591
|
target[position++] = 0xd9 // tag two byte
|
|
487
592
|
target[position++] = RECORD_STARTING_ID_PREFIX
|
|
488
593
|
target[position++] = recordId
|
|
@@ -509,12 +614,9 @@ export class Encoder extends Decoder {
|
|
|
509
614
|
transitionsCount += serializationsSinceTransitionRebuild * newTransitions
|
|
510
615
|
// record the removal of the id, we can maintain our shared structure
|
|
511
616
|
if (recordIdsToRemove.length >= MAX_STRUCTURES - maxSharedStructures)
|
|
512
|
-
recordIdsToRemove.shift()[RECORD_SYMBOL] =
|
|
617
|
+
recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined // we are cycling back through, and have to remove old ones
|
|
513
618
|
recordIdsToRemove.push(transition)
|
|
514
|
-
|
|
515
|
-
target[position++] = 0x82 + length // array header, length of values + 2
|
|
516
|
-
else
|
|
517
|
-
writeArrayHeader(length + 2)
|
|
619
|
+
writeArrayHeader(length + 2)
|
|
518
620
|
encode(keys)
|
|
519
621
|
target[position++] = 0x19 // uint16
|
|
520
622
|
target[position++] = RECORD_STARTING_ID_PREFIX
|
|
@@ -570,7 +672,9 @@ function copyBinary(source, target, targetOffset, offset, endOffset) {
|
|
|
570
672
|
}
|
|
571
673
|
|
|
572
674
|
function writeArrayHeader(length) {
|
|
573
|
-
if (length <
|
|
675
|
+
if (length < 0x18)
|
|
676
|
+
target[position++] = 0x80 | length
|
|
677
|
+
else if (length < 0x100) {
|
|
574
678
|
target[position++] = 0x98
|
|
575
679
|
target[position++] = length
|
|
576
680
|
} else if (length < 0x10000) {
|
|
@@ -584,6 +688,56 @@ function writeArrayHeader(length) {
|
|
|
584
688
|
}
|
|
585
689
|
}
|
|
586
690
|
|
|
691
|
+
function findRepetitiveStrings(value, packedValues) {
|
|
692
|
+
switch(typeof value) {
|
|
693
|
+
case 'string':
|
|
694
|
+
if (value.length > 3) {
|
|
695
|
+
if (packedValues.objectMap[value] > -1 || packedValues.values.length >= packedValues.maxValues)
|
|
696
|
+
return
|
|
697
|
+
let packedStatus = packedValues.get(value)
|
|
698
|
+
if (packedStatus) {
|
|
699
|
+
if (++packedStatus.count == 2) {
|
|
700
|
+
packedValues.values.push(value)
|
|
701
|
+
}
|
|
702
|
+
} else {
|
|
703
|
+
packedValues.set(value, {
|
|
704
|
+
count: 1,
|
|
705
|
+
})
|
|
706
|
+
if (packedValues.samplingPackedValues) {
|
|
707
|
+
let status = packedValues.samplingPackedValues.get(value)
|
|
708
|
+
if (status)
|
|
709
|
+
status.count++
|
|
710
|
+
else
|
|
711
|
+
packedValues.samplingPackedValues.set(value, {
|
|
712
|
+
count: 1,
|
|
713
|
+
})
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
break
|
|
718
|
+
case 'object':
|
|
719
|
+
if (value) {
|
|
720
|
+
if (value instanceof Array) {
|
|
721
|
+
for (let i = 0, l = value.length; i < l; i++) {
|
|
722
|
+
findRepetitiveStrings(value[i], packedValues)
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
} else {
|
|
726
|
+
let includeKeys = !packedValues.encoder.useRecords
|
|
727
|
+
for (var key in value) {
|
|
728
|
+
if (value.hasOwnProperty(key)) {
|
|
729
|
+
if (includeKeys)
|
|
730
|
+
findRepetitiveStrings(key, packedValues)
|
|
731
|
+
findRepetitiveStrings(value[key], packedValues)
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
break
|
|
737
|
+
case 'function': console.log(value)
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
587
741
|
extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
|
|
588
742
|
Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array, BigUint64Array, Int8Array, Int16Array, Int32Array, BigInt64Array,
|
|
589
743
|
Float32Array, Float64Array]
|
|
@@ -655,7 +809,9 @@ function typedArrayEncoder(tag) {
|
|
|
655
809
|
}
|
|
656
810
|
function writeBuffer(buffer, makeRoom) {
|
|
657
811
|
let length = buffer.byteLength
|
|
658
|
-
if (length <
|
|
812
|
+
if (length < 0x18) {
|
|
813
|
+
target[position++] = 0x40 + length
|
|
814
|
+
} else if (length < 0x100) {
|
|
659
815
|
target[position++] = 0x58
|
|
660
816
|
target[position++] = length
|
|
661
817
|
} else if (length < 0x10000) {
|
|
@@ -713,3 +869,4 @@ export const encode = defaultEncoder.encode
|
|
|
713
869
|
export { FLOAT32_OPTIONS } from './decode.js'
|
|
714
870
|
import { FLOAT32_OPTIONS } from './decode.js'
|
|
715
871
|
export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
|
|
872
|
+
export const REUSE_BUFFER_MODE = 1000
|
package/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export { Decoder, decode, addExtension, FLOAT32_OPTIONS } from './decode.js'
|
|
1
|
+
export { Decoder, decode, addExtension, FLOAT32_OPTIONS, clearSource, roundFloat32 } from './decode.js'
|
|
2
2
|
export { Encoder, encode } from './encode.js'
|
|
3
|
+
import { Transform, Readable } from 'stream'
|
|
3
4
|
|
|
4
|
-
export as namespace
|
|
5
|
-
export class DecoderStream {
|
|
5
|
+
export as namespace CBOR;
|
|
6
|
+
export class DecoderStream extends Transform {
|
|
7
|
+
constructor(options?: Options | { highWaterMark: number, emitClose: boolean, allowHalfOpen: boolean })
|
|
6
8
|
}
|
|
7
|
-
export class EncoderStream {
|
|
8
|
-
|
|
9
|
-
end(value?: any): void
|
|
9
|
+
export class EncoderStream extends Transform {
|
|
10
|
+
constructor(options?: Options | { highWaterMark: number, emitClose: boolean, allowHalfOpen: boolean })
|
|
10
11
|
}
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { Encoder, addExtension, encode, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } from './encode.js'
|
|
2
|
-
export { Tag, Decoder, decodeMultiple, decode, FLOAT32_OPTIONS } from './decode.js'
|
|
2
|
+
export { Tag, Decoder, decodeMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32 } from './decode.js'
|
|
3
|
+
export { decodeIter, encodeIter } from './iterators.js'
|
|
3
4
|
export const useRecords = false
|
|
4
5
|
export const mapsAsObjects = true
|
|
5
|
-
|
package/iterators.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Encoder } from './encode.js'
|
|
2
|
+
import { Decoder } from './decode.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Given an Iterable first argument, returns an Iterable where each value is encoded as a Buffer
|
|
6
|
+
* If the argument is only Async Iterable, the return value will be an Async Iterable.
|
|
7
|
+
* @param {Iterable|Iterator|AsyncIterable|AsyncIterator} objectIterator - iterable source, like a Readable object stream, an array, Set, or custom object
|
|
8
|
+
* @param {options} [options] - cbor-x Encoder options
|
|
9
|
+
* @returns {IterableIterator|Promise.<AsyncIterableIterator>}
|
|
10
|
+
*/
|
|
11
|
+
export function encodeIter (objectIterator, options = {}) {
|
|
12
|
+
if (!objectIterator || typeof objectIterator !== 'object') {
|
|
13
|
+
throw new Error('first argument must be an Iterable, Async Iterable, or a Promise for an Async Iterable')
|
|
14
|
+
} else if (typeof objectIterator[Symbol.iterator] === 'function') {
|
|
15
|
+
return encodeIterSync(objectIterator, options)
|
|
16
|
+
} else if (typeof objectIterator.then === 'function' || typeof objectIterator[Symbol.asyncIterator] === 'function') {
|
|
17
|
+
return encodeIterAsync(objectIterator, options)
|
|
18
|
+
} else {
|
|
19
|
+
throw new Error('first argument must be an Iterable, Async Iterable, Iterator, Async Iterator, or a Promise')
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function * encodeIterSync (objectIterator, options) {
|
|
24
|
+
const encoder = new Encoder(options)
|
|
25
|
+
for (const value of objectIterator) {
|
|
26
|
+
yield encoder.encode(value)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function * encodeIterAsync (objectIterator, options) {
|
|
31
|
+
const encoder = new Encoder(options)
|
|
32
|
+
for await (const value of objectIterator) {
|
|
33
|
+
yield encoder.encode(value)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Given an Iterable/Iterator input which yields buffers, returns an IterableIterator which yields sync decoded objects
|
|
39
|
+
* Or, given an Async Iterable/Iterator which yields promises resolving in buffers, returns an AsyncIterableIterator.
|
|
40
|
+
* @param {Iterable|Iterator|AsyncIterable|AsyncIterableIterator} bufferIterator
|
|
41
|
+
* @param {object} [options] - Decoder options
|
|
42
|
+
* @returns {IterableIterator|Promise.<AsyncIterableIterator}
|
|
43
|
+
*/
|
|
44
|
+
export function decodeIter (bufferIterator, options = {}) {
|
|
45
|
+
if (!bufferIterator || typeof bufferIterator !== 'object') {
|
|
46
|
+
throw new Error('first argument must be an Iterable, Async Iterable, Iterator, Async Iterator, or a promise')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const decoder = new Decoder(options)
|
|
50
|
+
let incomplete
|
|
51
|
+
const parser = (chunk) => {
|
|
52
|
+
let yields
|
|
53
|
+
// if there's incomplete data from previous chunk, concatinate and try again
|
|
54
|
+
if (incomplete) {
|
|
55
|
+
chunk = Buffer.concat([incomplete, chunk])
|
|
56
|
+
incomplete = undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
yields = decoder.decodeMultiple(chunk)
|
|
61
|
+
} catch (err) {
|
|
62
|
+
if (err.incomplete) {
|
|
63
|
+
incomplete = chunk.slice(err.lastPosition)
|
|
64
|
+
yields = err.values
|
|
65
|
+
} else {
|
|
66
|
+
throw err
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return yields
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (typeof bufferIterator[Symbol.iterator] === 'function') {
|
|
73
|
+
return (function * iter () {
|
|
74
|
+
for (const value of bufferIterator) {
|
|
75
|
+
yield * parser(value)
|
|
76
|
+
}
|
|
77
|
+
})()
|
|
78
|
+
} else if (typeof bufferIterator[Symbol.asyncIterator] === 'function') {
|
|
79
|
+
return (async function * iter () {
|
|
80
|
+
for await (const value of bufferIterator) {
|
|
81
|
+
yield * parser(value)
|
|
82
|
+
}
|
|
83
|
+
})()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export { Encoder, addExtension, encode, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } from './encode.js'
|
|
2
|
-
export { Tag, Decoder, decodeMultiple, decode, FLOAT32_OPTIONS } from './decode.js'
|
|
1
|
+
export { Encoder, addExtension, encode, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT, REUSE_BUFFER_MODE } from './encode.js'
|
|
2
|
+
export { Tag, Decoder, decodeMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32 } from './decode.js'
|
|
3
3
|
export { EncoderStream, DecoderStream } from './stream.js'
|
|
4
|
+
export { decodeIter, encodeIter } from './iterators.js'
|
|
4
5
|
export const useRecords = false
|
|
5
6
|
export const mapsAsObjects = true
|
|
6
7
|
import { setExtractor } from './decode.js'
|
|
@@ -20,4 +21,4 @@ function tryRequire(moduleId) {
|
|
|
20
21
|
else
|
|
21
22
|
console.warn('For browser usage, directly use cbor-x/decode or cbor-x/encode modules. ' + error.message.split('\n')[0])
|
|
22
23
|
}
|
|
23
|
-
}
|
|
24
|
+
}
|