cbor-x 0.9.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -21
- package/decode.d.ts +2 -0
- package/decode.js +306 -109
- package/dist/index.js +536 -258
- package/dist/index.min.js +99 -70
- package/dist/node.cjs +538 -253
- package/dist/test.js +51 -7
- package/encode.d.ts +1 -1
- package/encode.js +232 -86
- package/index.js +0 -2
- package/package.json +13 -4
- package/.vscode/launch.json +0 -22
- package/dist/index.js.map +0 -1
- package/tests/benchmark-stream.cjs +0 -282
- package/tests/benchmark.cjs +0 -245
- package/tests/example.json +0 -52
- package/tests/example2.json +0 -26
- package/tests/example3.json +0 -22
- package/tests/example4.json +0 -1
- package/tests/example5.json +0 -12
- package/tests/floats.json +0 -1
- package/tests/index.html +0 -28
- package/tests/sample-large.json +0 -231
- package/tests/strings2.json +0 -1
- package/tests/test-compatibility.cjs +0 -36
- package/tests/test-incomplete.js +0 -41
- package/tests/test-node-iterators.js +0 -72
- package/tests/test-node-stream.js +0 -60
- package/tests/test.js +0 -596
package/encode.js
CHANGED
|
@@ -7,7 +7,7 @@ let extensions, extensionClasses
|
|
|
7
7
|
const hasNodeBuffer = typeof Buffer !== 'undefined'
|
|
8
8
|
const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array
|
|
9
9
|
const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
|
|
10
|
-
const
|
|
10
|
+
const RECORD_INLINE_ID = 0xdfff // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
|
|
11
11
|
const MAX_STRUCTURES = 0x100
|
|
12
12
|
const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000
|
|
13
13
|
let serializationId = 1
|
|
@@ -15,6 +15,9 @@ let target
|
|
|
15
15
|
let targetView
|
|
16
16
|
let position = 0
|
|
17
17
|
let safeEnd
|
|
18
|
+
let bundledStrings = null
|
|
19
|
+
const MAX_BUNDLE_SIZE = 0xf000
|
|
20
|
+
const hasNonLatin = /[\u0080-\uFFFF]/
|
|
18
21
|
const RECORD_SYMBOL = Symbol('record-id')
|
|
19
22
|
export class Encoder extends Decoder {
|
|
20
23
|
constructor(options) {
|
|
@@ -27,7 +30,6 @@ export class Encoder extends Decoder {
|
|
|
27
30
|
let structures
|
|
28
31
|
let referenceMap
|
|
29
32
|
options = options || {}
|
|
30
|
-
let lastSharedStructuresLength = 0
|
|
31
33
|
let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
|
|
32
34
|
return target.utf8Write(string, position, maxBytes)
|
|
33
35
|
} : (textEncoder && textEncoder.encodeInto) ?
|
|
@@ -36,12 +38,14 @@ export class Encoder extends Decoder {
|
|
|
36
38
|
} : false
|
|
37
39
|
|
|
38
40
|
let encoder = this
|
|
39
|
-
let maxSharedStructures =
|
|
41
|
+
let maxSharedStructures = options.maxSharedStructures || 128
|
|
40
42
|
let isSequential = options.sequential
|
|
41
43
|
if (isSequential) {
|
|
42
44
|
maxSharedStructures = 0
|
|
43
45
|
this.structures = []
|
|
44
46
|
}
|
|
47
|
+
if (this.saveStructures)
|
|
48
|
+
this.saveShared = this.saveStructures
|
|
45
49
|
let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues
|
|
46
50
|
let sharedPackedObjectMap
|
|
47
51
|
if (sharedValues) {
|
|
@@ -49,13 +53,29 @@ export class Encoder extends Decoder {
|
|
|
49
53
|
for (let i = 0, l = sharedValues.length; i < l; i++) {
|
|
50
54
|
sharedPackedObjectMap[sharedValues[i]] = i
|
|
51
55
|
}
|
|
52
|
-
|
|
53
56
|
}
|
|
54
57
|
let recordIdsToRemove = []
|
|
55
58
|
let transitionsCount = 0
|
|
56
59
|
let serializationsSinceTransitionRebuild = 0
|
|
57
|
-
|
|
58
|
-
this.
|
|
60
|
+
|
|
61
|
+
this.mapEncode = function(value, encodeOptions) {
|
|
62
|
+
// Experimental support for premapping keys using _keyMap instad of keyMap - not optiimised yet)
|
|
63
|
+
if (this._keyMap && !this._mapped) {
|
|
64
|
+
//console.log('encoding ', value)
|
|
65
|
+
switch (value.constructor.name) {
|
|
66
|
+
case 'Array':
|
|
67
|
+
value = value.map(r => this.encodeKeys(r))
|
|
68
|
+
break
|
|
69
|
+
//case 'Map':
|
|
70
|
+
// value = this.encodeKeys(value)
|
|
71
|
+
// break
|
|
72
|
+
}
|
|
73
|
+
//this._mapped = true
|
|
74
|
+
}
|
|
75
|
+
return this.encode(value, encodeOptions)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.encode = function(value, encodeOptions) {
|
|
59
79
|
if (!target) {
|
|
60
80
|
target = new ByteArrayAllocate(8192)
|
|
61
81
|
targetView = new DataView(target.buffer, 0, 8192)
|
|
@@ -72,10 +92,25 @@ export class Encoder extends Decoder {
|
|
|
72
92
|
position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
|
|
73
93
|
start = position
|
|
74
94
|
referenceMap = encoder.structuredClone ? new Map() : null
|
|
95
|
+
if (encoder.bundleStrings && typeof value !== 'string') {
|
|
96
|
+
bundledStrings = []
|
|
97
|
+
bundledStrings.size = Infinity // force a new bundle start on first string
|
|
98
|
+
} else
|
|
99
|
+
bundledStrings = null
|
|
100
|
+
|
|
75
101
|
sharedStructures = encoder.structures
|
|
76
102
|
if (sharedStructures) {
|
|
77
|
-
if (sharedStructures.uninitialized)
|
|
78
|
-
|
|
103
|
+
if (sharedStructures.uninitialized) {
|
|
104
|
+
let sharedData = encoder.getShared() || {}
|
|
105
|
+
encoder.structures = sharedStructures = sharedData.structures || []
|
|
106
|
+
encoder.sharedVersion = sharedData.version
|
|
107
|
+
let sharedValues = encoder.sharedValues = sharedData.packedValues
|
|
108
|
+
if (sharedValues) {
|
|
109
|
+
sharedPackedObjectMap = {}
|
|
110
|
+
for (let i = 0, l = sharedValues.length; i < l; i++)
|
|
111
|
+
sharedPackedObjectMap[sharedValues[i]] = i
|
|
112
|
+
}
|
|
113
|
+
}
|
|
79
114
|
let sharedStructuresLength = sharedStructures.length
|
|
80
115
|
if (sharedStructuresLength > maxSharedStructures && !isSequential)
|
|
81
116
|
sharedStructuresLength = maxSharedStructures
|
|
@@ -84,6 +119,7 @@ export class Encoder extends Decoder {
|
|
|
84
119
|
sharedStructures.transitions = Object.create(null)
|
|
85
120
|
for (let i = 0; i < sharedStructuresLength; i++) {
|
|
86
121
|
let keys = sharedStructures[i]
|
|
122
|
+
//console.log('shared struct keys:', keys)
|
|
87
123
|
if (!keys)
|
|
88
124
|
continue
|
|
89
125
|
let nextTransition, transition = sharedStructures.transitions
|
|
@@ -97,7 +133,6 @@ export class Encoder extends Decoder {
|
|
|
97
133
|
}
|
|
98
134
|
transition[RECORD_SYMBOL] = i
|
|
99
135
|
}
|
|
100
|
-
lastSharedStructuresLength = sharedStructures.length
|
|
101
136
|
}
|
|
102
137
|
if (!isSequential)
|
|
103
138
|
sharedStructures.nextId = sharedStructuresLength
|
|
@@ -130,9 +165,12 @@ export class Encoder extends Decoder {
|
|
|
130
165
|
}
|
|
131
166
|
try {
|
|
132
167
|
encode(value)
|
|
168
|
+
if (bundledStrings) {
|
|
169
|
+
writeBundles(start, encode)
|
|
170
|
+
}
|
|
133
171
|
encoder.offset = position // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
|
|
134
172
|
if (referenceMap && referenceMap.idsToInsert) {
|
|
135
|
-
position += referenceMap.idsToInsert.length *
|
|
173
|
+
position += referenceMap.idsToInsert.length * 2
|
|
136
174
|
if (position > safeEnd)
|
|
137
175
|
makeRoom(position)
|
|
138
176
|
encoder.offset = position
|
|
@@ -140,12 +178,12 @@ export class Encoder extends Decoder {
|
|
|
140
178
|
referenceMap = null
|
|
141
179
|
return serialized
|
|
142
180
|
}
|
|
143
|
-
if (encodeOptions
|
|
181
|
+
if (encodeOptions & REUSE_BUFFER_MODE) {
|
|
144
182
|
target.start = start
|
|
145
183
|
target.end = position
|
|
146
184
|
return target
|
|
147
185
|
}
|
|
148
|
-
return target.subarray(start, position) // position can change if we call pack again in
|
|
186
|
+
return target.subarray(start, position) // position can change if we call pack again in saveShared, so we get the buffer now
|
|
149
187
|
} finally {
|
|
150
188
|
if (sharedStructures) {
|
|
151
189
|
if (serializationsSinceTransitionRebuild < 10)
|
|
@@ -163,34 +201,27 @@ export class Encoder extends Decoder {
|
|
|
163
201
|
}
|
|
164
202
|
recordIdsToRemove = []
|
|
165
203
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
}
|
|
176
|
-
|
|
177
|
-
if (encoder.saveStructures(encoder.structures, lastSharedStructuresLength) === false) {
|
|
178
|
-
// get updated structures and try again if the update failed
|
|
179
|
-
encoder.structures = encoder.getStructures() || []
|
|
180
|
-
return encoder.encode(value)
|
|
181
|
-
}
|
|
182
|
-
lastSharedStructuresLength = shared.length
|
|
183
|
-
return returnBuffer
|
|
204
|
+
}
|
|
205
|
+
if (hasSharedUpdate && encoder.saveShared) {
|
|
206
|
+
if (encoder.structures.length > maxSharedStructures) {
|
|
207
|
+
encoder.structures = encoder.structures.slice(0, maxSharedStructures)
|
|
184
208
|
}
|
|
209
|
+
// we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
|
|
210
|
+
let returnBuffer = target.subarray(start, position)
|
|
211
|
+
if (encoder.updateSharedData() === false)
|
|
212
|
+
return encoder.encode(value) // re-encode if it fails
|
|
213
|
+
return returnBuffer
|
|
185
214
|
}
|
|
215
|
+
if (encodeOptions & RESET_BUFFER_MODE)
|
|
216
|
+
position = start
|
|
186
217
|
}
|
|
187
218
|
}
|
|
188
219
|
this.findCommonStringsToPack = () => {
|
|
189
220
|
samplingPackedValues = new Map()
|
|
190
221
|
if (!sharedPackedObjectMap)
|
|
191
222
|
sharedPackedObjectMap = Object.create(null)
|
|
192
|
-
return (
|
|
193
|
-
threshold = threshold || 4
|
|
223
|
+
return (options) => {
|
|
224
|
+
let threshold = options && options.threshold || 4
|
|
194
225
|
let position = this.pack ? options.maxPrivatePackedValues || 16 : 0
|
|
195
226
|
if (!sharedValues)
|
|
196
227
|
sharedValues = this.sharedValues = []
|
|
@@ -201,6 +232,7 @@ export class Encoder extends Decoder {
|
|
|
201
232
|
hasSharedUpdate = true
|
|
202
233
|
}
|
|
203
234
|
}
|
|
235
|
+
while (this.saveShared && this.updateSharedData() === false) {}
|
|
204
236
|
samplingPackedValues = null
|
|
205
237
|
}
|
|
206
238
|
}
|
|
@@ -249,6 +281,33 @@ export class Encoder extends Decoder {
|
|
|
249
281
|
}
|
|
250
282
|
}
|
|
251
283
|
let strLength = value.length
|
|
284
|
+
if (bundledStrings && strLength >= 4 && strLength < 0x400) {
|
|
285
|
+
if ((bundledStrings.size += strLength) > MAX_BUNDLE_SIZE) {
|
|
286
|
+
let extStart
|
|
287
|
+
let maxBytes = (bundledStrings[0] ? bundledStrings[0].length * 3 + bundledStrings[1].length : 0) + 10
|
|
288
|
+
if (position + maxBytes > safeEnd)
|
|
289
|
+
target = makeRoom(position + maxBytes)
|
|
290
|
+
target[position++] = 0xd9 // tag 16-bit
|
|
291
|
+
target[position++] = 0xdf // tag 0xdff9
|
|
292
|
+
target[position++] = 0xf9
|
|
293
|
+
// TODO: If we only have one bundle with any string data, only write one string bundle
|
|
294
|
+
target[position++] = bundledStrings.position ? 0x84 : 0x82 // array of 4 or 2 elements depending on if we write bundles
|
|
295
|
+
target[position++] = 0x1a // 32-bit unsigned int
|
|
296
|
+
extStart = position - start
|
|
297
|
+
position += 4 // reserve for writing bundle reference
|
|
298
|
+
if (bundledStrings.position) {
|
|
299
|
+
writeBundles(start, encode) // write the last bundles
|
|
300
|
+
}
|
|
301
|
+
bundledStrings = ['', ''] // create new ones
|
|
302
|
+
bundledStrings.size = 0
|
|
303
|
+
bundledStrings.position = extStart
|
|
304
|
+
}
|
|
305
|
+
let twoByte = hasNonLatin.test(value)
|
|
306
|
+
bundledStrings[twoByte ? 0 : 1] += value
|
|
307
|
+
target[position++] = twoByte ? 0xce : 0xcf
|
|
308
|
+
encode(strLength);
|
|
309
|
+
return
|
|
310
|
+
}
|
|
252
311
|
let headerSize
|
|
253
312
|
// first we estimate the header size, so we can write to the correct location
|
|
254
313
|
if (strLength < 0x20) {
|
|
@@ -375,16 +434,16 @@ export class Encoder extends Decoder {
|
|
|
375
434
|
if (referenceMap) {
|
|
376
435
|
let referee = referenceMap.get(value)
|
|
377
436
|
if (referee) {
|
|
378
|
-
|
|
437
|
+
target[position++] = 0xd8
|
|
438
|
+
target[position++] = 29 // http://cbor.schmorp.de/value-sharing
|
|
439
|
+
target[position++] = 0x19 // 16-bit uint
|
|
440
|
+
if (!referee.references) {
|
|
379
441
|
let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = [])
|
|
380
|
-
referee.
|
|
442
|
+
referee.references = []
|
|
443
|
+
idsToInsert.push(referee)
|
|
381
444
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
target[position++] = 40010 & 0xff
|
|
385
|
-
target[position++] = 0x1a // uint32
|
|
386
|
-
targetView.setUint32(position, referee.id)
|
|
387
|
-
position += 4
|
|
445
|
+
referee.references.push(position - start)
|
|
446
|
+
position += 2 // TODO: also support 32-bit
|
|
388
447
|
return
|
|
389
448
|
} else
|
|
390
449
|
referenceMap.set(value, { offset: position - start })
|
|
@@ -424,16 +483,23 @@ export class Encoder extends Decoder {
|
|
|
424
483
|
targetView.setUint32(position, length)
|
|
425
484
|
position += 4
|
|
426
485
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
486
|
+
if (encoder.keyMap) {
|
|
487
|
+
for (let [ key, entryValue ] of value) {
|
|
488
|
+
encode(encoder.encodeKey(key))
|
|
489
|
+
encode(entryValue)
|
|
490
|
+
}
|
|
491
|
+
} else {
|
|
492
|
+
for (let [ key, entryValue ] of value) {
|
|
493
|
+
encode(key)
|
|
494
|
+
encode(entryValue)
|
|
495
|
+
}
|
|
430
496
|
}
|
|
431
|
-
} else {
|
|
497
|
+
} else {
|
|
432
498
|
for (let i = 0, l = extensions.length; i < l; i++) {
|
|
433
499
|
let extensionClass = extensionClasses[i]
|
|
434
500
|
if (value instanceof extensionClass) {
|
|
435
501
|
let extension = extensions[i]
|
|
436
|
-
let tag = extension.tag
|
|
502
|
+
let tag = extension.tag || extension.getTag && extension.getTag(value)
|
|
437
503
|
if (tag < 0x18) {
|
|
438
504
|
target[position++] = 0xc0 | tag
|
|
439
505
|
} else if (tag < 0x100) {
|
|
@@ -495,6 +561,7 @@ export class Encoder extends Decoder {
|
|
|
495
561
|
const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
|
|
496
562
|
// this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
|
|
497
563
|
let keys = Object.keys(object)
|
|
564
|
+
let vals = Object.values(object)
|
|
498
565
|
let length = keys.length
|
|
499
566
|
if (length < 0x18) {
|
|
500
567
|
target[position++] = 0xa0 | length
|
|
@@ -511,9 +578,16 @@ export class Encoder extends Decoder {
|
|
|
511
578
|
position += 4
|
|
512
579
|
}
|
|
513
580
|
let key
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
581
|
+
if (encoder.keyMap) {
|
|
582
|
+
for (let i = 0; i < length; i++) {
|
|
583
|
+
encode(encodeKey(keys[i]))
|
|
584
|
+
encode(vals[i])
|
|
585
|
+
}
|
|
586
|
+
} else {
|
|
587
|
+
for (let i = 0; i < length; i++) {
|
|
588
|
+
encode(keys[i])
|
|
589
|
+
encode(vals[i])
|
|
590
|
+
}
|
|
517
591
|
}
|
|
518
592
|
} :
|
|
519
593
|
(object, safePrototype) => {
|
|
@@ -521,12 +595,18 @@ export class Encoder extends Decoder {
|
|
|
521
595
|
let objectOffset = position - start
|
|
522
596
|
position += 2
|
|
523
597
|
let size = 0
|
|
524
|
-
|
|
525
|
-
if (safePrototype || object.hasOwnProperty(key)) {
|
|
526
|
-
encode(key)
|
|
598
|
+
if (encoder.keyMap) {
|
|
599
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
600
|
+
encode(encoder.encodeKey(key))
|
|
527
601
|
encode(object[key])
|
|
528
602
|
size++
|
|
529
603
|
}
|
|
604
|
+
} else {
|
|
605
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
606
|
+
encode(key)
|
|
607
|
+
encode(object[key])
|
|
608
|
+
size++
|
|
609
|
+
}
|
|
530
610
|
}
|
|
531
611
|
target[objectOffset++ + start] = size >> 8
|
|
532
612
|
target[objectOffset + start] = size & 0xff
|
|
@@ -574,11 +654,16 @@ export class Encoder extends Decoder {
|
|
|
574
654
|
}*/
|
|
575
655
|
(object) => {
|
|
576
656
|
let keys = Object.keys(object)
|
|
657
|
+
let vals = Object.values(object)
|
|
658
|
+
if (this.keyMap) keys = keys.map(k => this.encodeKey(k))
|
|
577
659
|
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
|
|
578
660
|
let newTransitions = 0
|
|
579
661
|
let length = keys.length
|
|
662
|
+
//let parentRecordId
|
|
580
663
|
for (let i = 0; i < length; i++) {
|
|
664
|
+
//if (!parentRecordId)
|
|
581
665
|
let key = keys[i]
|
|
666
|
+
// parentRecordId = transition[RECORD_SYMBOL]
|
|
582
667
|
nextTransition = transition[key]
|
|
583
668
|
if (!nextTransition) {
|
|
584
669
|
nextTransition = transition[key] = Object.create(null)
|
|
@@ -588,9 +673,9 @@ export class Encoder extends Decoder {
|
|
|
588
673
|
}
|
|
589
674
|
let recordId = transition[RECORD_SYMBOL]
|
|
590
675
|
if (recordId !== undefined) {
|
|
591
|
-
target[position++] = 0xd9
|
|
592
|
-
target[position++] =
|
|
593
|
-
target[position++] = recordId
|
|
676
|
+
target[position++] = 0xd9
|
|
677
|
+
target[position++] = (recordId >> 8) | 0xe0
|
|
678
|
+
target[position++] = recordId & 0xff
|
|
594
679
|
} else {
|
|
595
680
|
recordId = structures.nextId++
|
|
596
681
|
if (!recordId) {
|
|
@@ -603,13 +688,13 @@ export class Encoder extends Decoder {
|
|
|
603
688
|
transition[RECORD_SYMBOL] = recordId
|
|
604
689
|
structures[recordId] = keys
|
|
605
690
|
if (sharedStructures && sharedStructures.length <= maxSharedStructures) {
|
|
606
|
-
target[position++] = 0xd9
|
|
607
|
-
target[position++] =
|
|
608
|
-
target[position++] = recordId
|
|
691
|
+
target[position++] = 0xd9
|
|
692
|
+
target[position++] = (recordId >> 8) | 0xe0
|
|
693
|
+
target[position++] = recordId & 0xff
|
|
609
694
|
hasSharedUpdate = true
|
|
610
695
|
} else {
|
|
611
|
-
|
|
612
|
-
|
|
696
|
+
targetView.setUint32(position, 0xd9dfff00) // tag two byte, then record definition id
|
|
697
|
+
position += 3
|
|
613
698
|
if (newTransitions)
|
|
614
699
|
transitionsCount += serializationsSinceTransitionRebuild * newTransitions
|
|
615
700
|
// record the removal of the id, we can maintain our shared structure
|
|
@@ -617,13 +702,9 @@ export class Encoder extends Decoder {
|
|
|
617
702
|
recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined // we are cycling back through, and have to remove old ones
|
|
618
703
|
recordIdsToRemove.push(transition)
|
|
619
704
|
writeArrayHeader(length + 2)
|
|
705
|
+
encode(0xe000 + recordId)
|
|
620
706
|
encode(keys)
|
|
621
|
-
|
|
622
|
-
target[position++] = RECORD_STARTING_ID_PREFIX
|
|
623
|
-
target[position++] = recordId
|
|
624
|
-
// now write the values
|
|
625
|
-
for (let i =0; i < length; i++)
|
|
626
|
-
encode(object[keys[i]])
|
|
707
|
+
for (let v of Object.values(object)) encode(v)
|
|
627
708
|
return
|
|
628
709
|
}
|
|
629
710
|
}
|
|
@@ -632,8 +713,7 @@ export class Encoder extends Decoder {
|
|
|
632
713
|
} else {
|
|
633
714
|
writeArrayHeader(length)
|
|
634
715
|
}
|
|
635
|
-
for (let i =0; i < length; i++)
|
|
636
|
-
encode(object[keys[i]])
|
|
716
|
+
for (let i =0; i < length; i++) encode(vals[i])
|
|
637
717
|
}
|
|
638
718
|
const makeRoom = (end) => {
|
|
639
719
|
let newSize
|
|
@@ -663,11 +743,32 @@ export class Encoder extends Decoder {
|
|
|
663
743
|
targetView = new DataView(target.buffer, target.byteOffset, target.byteLength)
|
|
664
744
|
position = 0
|
|
665
745
|
}
|
|
746
|
+
clearSharedData() {
|
|
747
|
+
if (this.structures)
|
|
748
|
+
this.structures = []
|
|
749
|
+
if (this.sharedValues)
|
|
750
|
+
this.sharedValues = undefined
|
|
751
|
+
}
|
|
752
|
+
updateSharedData() {
|
|
753
|
+
let lastVersion = this.sharedVersion || 0
|
|
754
|
+
this.sharedVersion = lastVersion + 1
|
|
755
|
+
let saveResults = this.saveShared(new SharedData(this.structures, this.sharedValues, this.sharedVersion),
|
|
756
|
+
existingShared => (existingShared && existingShared.version || 0) == lastVersion)
|
|
757
|
+
if (saveResults === false) {
|
|
758
|
+
// get updated structures and try again if the update failed
|
|
759
|
+
let sharedData = this.getShared() || {}
|
|
760
|
+
this.structures = sharedData.structures || []
|
|
761
|
+
this.sharedValues = sharedData.packedValues
|
|
762
|
+
this.sharedVersion = sharedData.version
|
|
763
|
+
}
|
|
764
|
+
return saveResults
|
|
765
|
+
}
|
|
666
766
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
767
|
+
class SharedData {
|
|
768
|
+
constructor(structures, values, version) {
|
|
769
|
+
this.structures = structures
|
|
770
|
+
this.packedValues = values
|
|
771
|
+
this.version = version
|
|
671
772
|
}
|
|
672
773
|
}
|
|
673
774
|
|
|
@@ -738,11 +839,11 @@ function findRepetitiveStrings(value, packedValues) {
|
|
|
738
839
|
}
|
|
739
840
|
}
|
|
740
841
|
|
|
741
|
-
extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
|
|
842
|
+
extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
|
|
742
843
|
Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
|
|
743
844
|
typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
|
|
744
845
|
typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
|
|
745
|
-
Float32Array, Float64Array]
|
|
846
|
+
Float32Array, Float64Array, SharedData]
|
|
746
847
|
|
|
747
848
|
//Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
|
|
748
849
|
extensions = [{
|
|
@@ -777,6 +878,13 @@ extensions = [{
|
|
|
777
878
|
encode(regex, encode) {
|
|
778
879
|
encode([ 'RegExp', regex.source, regex.flags ])
|
|
779
880
|
}
|
|
881
|
+
}, {
|
|
882
|
+
getTag(tag) {
|
|
883
|
+
return tag.tag
|
|
884
|
+
},
|
|
885
|
+
encode(tag, encode) {
|
|
886
|
+
encode(tag.value)
|
|
887
|
+
}
|
|
780
888
|
}, {
|
|
781
889
|
encode(arrayBuffer, encode, makeRoom) {
|
|
782
890
|
writeBuffer(arrayBuffer, makeRoom)
|
|
@@ -795,7 +903,35 @@ extensions = [{
|
|
|
795
903
|
typedArrayEncoder(78),
|
|
796
904
|
typedArrayEncoder(79),
|
|
797
905
|
typedArrayEncoder(81),
|
|
798
|
-
typedArrayEncoder(82)
|
|
906
|
+
typedArrayEncoder(82),
|
|
907
|
+
{
|
|
908
|
+
encode(sharedData, encode) { // write SharedData
|
|
909
|
+
let packedValues = sharedData.packedValues || []
|
|
910
|
+
let sharedStructures = sharedData.structures || []
|
|
911
|
+
if (packedValues.values.length > 0) {
|
|
912
|
+
target[position++] = 0xd8 // one-byte tag
|
|
913
|
+
target[position++] = 51 // tag 51 for packed shared structures https://www.potaroo.net/ietf/ids/draft-ietf-cbor-packed-03.txt
|
|
914
|
+
writeArrayHeader(4)
|
|
915
|
+
let valuesArray = packedValues.values
|
|
916
|
+
encode(valuesArray)
|
|
917
|
+
writeArrayHeader(0) // prefixes
|
|
918
|
+
writeArrayHeader(0) // suffixes
|
|
919
|
+
packedObjectMap = Object.create(sharedPackedObjectMap || null)
|
|
920
|
+
for (let i = 0, l = valuesArray.length; i < l; i++) {
|
|
921
|
+
packedObjectMap[valuesArray[i]] = i
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (sharedStructures) {
|
|
925
|
+
targetView.setUint32(position, 0xd9dffe00)
|
|
926
|
+
position += 3
|
|
927
|
+
let definitions = sharedStructures.slice(0)
|
|
928
|
+
definitions.unshift(0xe000)
|
|
929
|
+
definitions.push(new Tag(sharedData.version, 0x53687264))
|
|
930
|
+
encode(definitions)
|
|
931
|
+
} else
|
|
932
|
+
encode(new Tag(sharedData.version, 0x53687264))
|
|
933
|
+
}
|
|
934
|
+
}]
|
|
799
935
|
|
|
800
936
|
function typedArrayEncoder(tag) {
|
|
801
937
|
return {
|
|
@@ -835,27 +971,35 @@ function writeBuffer(buffer, makeRoom) {
|
|
|
835
971
|
function insertIds(serialized, idsToInsert) {
|
|
836
972
|
// insert the ids that need to be referenced for structured clones
|
|
837
973
|
let nextId
|
|
838
|
-
let distanceToMove = idsToInsert.length *
|
|
974
|
+
let distanceToMove = idsToInsert.length * 2
|
|
839
975
|
let lastEnd = serialized.length - distanceToMove
|
|
840
976
|
idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1)
|
|
977
|
+
for (let id = 0; id < idsToInsert.length; id++) {
|
|
978
|
+
let referee = idsToInsert[id]
|
|
979
|
+
referee.id = id
|
|
980
|
+
for (let position of referee.references) {
|
|
981
|
+
serialized[position++] = id >> 8
|
|
982
|
+
serialized[position] = id & 0xff
|
|
983
|
+
}
|
|
984
|
+
}
|
|
841
985
|
while (nextId = idsToInsert.pop()) {
|
|
842
986
|
let offset = nextId.offset
|
|
843
|
-
let id = nextId.id
|
|
844
987
|
serialized.copyWithin(offset + distanceToMove, offset, lastEnd)
|
|
845
|
-
distanceToMove -=
|
|
988
|
+
distanceToMove -= 2
|
|
846
989
|
let position = offset + distanceToMove
|
|
847
|
-
serialized[position++] =
|
|
848
|
-
serialized[position++] =
|
|
849
|
-
serialized[position++] = 40009 & 0xff
|
|
850
|
-
serialized[position++] = 0x1a // uint32
|
|
851
|
-
serialized[position++] = id >> 24
|
|
852
|
-
serialized[position++] = (id >> 16) & 0xff
|
|
853
|
-
serialized[position++] = (id >> 8) & 0xff
|
|
854
|
-
serialized[position++] = id & 0xff
|
|
990
|
+
serialized[position++] = 0xd8
|
|
991
|
+
serialized[position++] = 28 // http://cbor.schmorp.de/value-sharing
|
|
855
992
|
lastEnd = offset
|
|
856
993
|
}
|
|
857
994
|
return serialized
|
|
858
995
|
}
|
|
996
|
+
function writeBundles(start, encode) {
|
|
997
|
+
targetView.setUint32(bundledStrings.position + start, position - bundledStrings.position - start + 1) // the offset to bundle
|
|
998
|
+
let writeStrings = bundledStrings
|
|
999
|
+
bundledStrings = null
|
|
1000
|
+
encode(writeStrings[0])
|
|
1001
|
+
encode(writeStrings[1])
|
|
1002
|
+
}
|
|
859
1003
|
|
|
860
1004
|
export function addExtension(extension) {
|
|
861
1005
|
if (extension.Class) {
|
|
@@ -871,4 +1015,6 @@ export const encode = defaultEncoder.encode
|
|
|
871
1015
|
export { FLOAT32_OPTIONS } from './decode.js'
|
|
872
1016
|
import { FLOAT32_OPTIONS } from './decode.js'
|
|
873
1017
|
export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
|
|
874
|
-
export const REUSE_BUFFER_MODE =
|
|
1018
|
+
export const REUSE_BUFFER_MODE = 512
|
|
1019
|
+
export const RESET_BUFFER_MODE = 1024
|
|
1020
|
+
|
package/index.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
export { Encoder, addExtension, encode, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT, REUSE_BUFFER_MODE } from './encode.js'
|
|
2
2
|
export { Tag, Decoder, decodeMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32, isNativeAccelerationEnabled } from './decode.js'
|
|
3
3
|
export { decodeIter, encodeIter } from './iterators.js'
|
|
4
|
-
export const useRecords = false
|
|
5
|
-
export const mapsAsObjects = true
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cbor-x",
|
|
3
3
|
"author": "Kris Zyp",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"description": "Ultra-fast CBOR implementation with tag extensions
|
|
4
|
+
"version": "1.0.0",
|
|
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",
|
|
8
8
|
"main": "./dist/node.cjs",
|
|
@@ -52,10 +52,17 @@
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
|
+
"files": [
|
|
56
|
+
"/dist",
|
|
57
|
+
"*.md",
|
|
58
|
+
"/*.js",
|
|
59
|
+
"/*.ts"
|
|
60
|
+
],
|
|
55
61
|
"optionalDependencies": {
|
|
56
|
-
"cbor-extract": "^0.
|
|
62
|
+
"cbor-extract": "^1.0.0"
|
|
57
63
|
},
|
|
58
64
|
"devDependencies": {
|
|
65
|
+
"@rollup/plugin-json": "^4.1.0",
|
|
59
66
|
"@types/node": "latest",
|
|
60
67
|
"async": "^3",
|
|
61
68
|
"cbor": "^5",
|
|
@@ -64,7 +71,9 @@
|
|
|
64
71
|
"esm": "^3.2.25",
|
|
65
72
|
"mocha": "^8.1.3",
|
|
66
73
|
"rollup": "^1.20.3",
|
|
67
|
-
"@rollup/plugin-json": "^4.1.0",
|
|
68
74
|
"rollup-plugin-babel-minify": "^9.0.0"
|
|
75
|
+
},
|
|
76
|
+
"dependencies": {
|
|
77
|
+
"esbuild": "^0.13.15"
|
|
69
78
|
}
|
|
70
79
|
}
|
package/.vscode/launch.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
// Use IntelliSense to learn about possible attributes.
|
|
3
|
-
// Hover to view descriptions of existing attributes.
|
|
4
|
-
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
-
"version": "0.2.0",
|
|
6
|
-
"configurations": [
|
|
7
|
-
{
|
|
8
|
-
"type": "pwa-node",
|
|
9
|
-
"request": "launch",
|
|
10
|
-
"name": "Run cbor-x tests",
|
|
11
|
-
"runtimeArgs": [
|
|
12
|
-
"test"
|
|
13
|
-
],
|
|
14
|
-
"runtimeExecutable": "npm",
|
|
15
|
-
"skipFiles": [
|
|
16
|
-
"<node_internals>/**"
|
|
17
|
-
],
|
|
18
|
-
"console": "internalConsole",
|
|
19
|
-
"outputCapture": "std"
|
|
20
|
-
}
|
|
21
|
-
]
|
|
22
|
-
}
|