cbor-x 0.9.1 → 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 +67 -21
- package/decode.d.ts +4 -0
- package/decode.js +323 -117
- package/dist/index.js +563 -270
- package/dist/index.min.js +101 -69
- package/dist/node.cjs +560 -265
- package/dist/test.js +62 -8
- package/encode.d.ts +1 -1
- package/encode.js +236 -89
- package/index.d.ts +1 -1
- package/index.js +2 -4
- package/node-index.js +2 -4
- 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 -586
package/encode.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { write } from 'fs'
|
|
2
1
|
import { Decoder, mult10, Tag, typedArrays, addExtension as decodeAddExtension } from './decode.js'
|
|
3
2
|
let textEncoder
|
|
4
3
|
try {
|
|
@@ -8,7 +7,7 @@ let extensions, extensionClasses
|
|
|
8
7
|
const hasNodeBuffer = typeof Buffer !== 'undefined'
|
|
9
8
|
const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array
|
|
10
9
|
const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
|
|
11
|
-
const
|
|
10
|
+
const RECORD_INLINE_ID = 0xdfff // temporary first-come first-serve tag // proposed tag: 0x7265 // 're'
|
|
12
11
|
const MAX_STRUCTURES = 0x100
|
|
13
12
|
const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000
|
|
14
13
|
let serializationId = 1
|
|
@@ -16,6 +15,9 @@ let target
|
|
|
16
15
|
let targetView
|
|
17
16
|
let position = 0
|
|
18
17
|
let safeEnd
|
|
18
|
+
let bundledStrings = null
|
|
19
|
+
const MAX_BUNDLE_SIZE = 0xf000
|
|
20
|
+
const hasNonLatin = /[\u0080-\uFFFF]/
|
|
19
21
|
const RECORD_SYMBOL = Symbol('record-id')
|
|
20
22
|
export class Encoder extends Decoder {
|
|
21
23
|
constructor(options) {
|
|
@@ -28,7 +30,6 @@ export class Encoder extends Decoder {
|
|
|
28
30
|
let structures
|
|
29
31
|
let referenceMap
|
|
30
32
|
options = options || {}
|
|
31
|
-
let lastSharedStructuresLength = 0
|
|
32
33
|
let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) {
|
|
33
34
|
return target.utf8Write(string, position, maxBytes)
|
|
34
35
|
} : (textEncoder && textEncoder.encodeInto) ?
|
|
@@ -37,12 +38,14 @@ export class Encoder extends Decoder {
|
|
|
37
38
|
} : false
|
|
38
39
|
|
|
39
40
|
let encoder = this
|
|
40
|
-
let maxSharedStructures =
|
|
41
|
+
let maxSharedStructures = options.maxSharedStructures || 128
|
|
41
42
|
let isSequential = options.sequential
|
|
42
43
|
if (isSequential) {
|
|
43
44
|
maxSharedStructures = 0
|
|
44
45
|
this.structures = []
|
|
45
46
|
}
|
|
47
|
+
if (this.saveStructures)
|
|
48
|
+
this.saveShared = this.saveStructures
|
|
46
49
|
let samplingPackedValues, packedObjectMap, sharedValues = options.sharedValues
|
|
47
50
|
let sharedPackedObjectMap
|
|
48
51
|
if (sharedValues) {
|
|
@@ -50,13 +53,29 @@ export class Encoder extends Decoder {
|
|
|
50
53
|
for (let i = 0, l = sharedValues.length; i < l; i++) {
|
|
51
54
|
sharedPackedObjectMap[sharedValues[i]] = i
|
|
52
55
|
}
|
|
53
|
-
|
|
54
56
|
}
|
|
55
57
|
let recordIdsToRemove = []
|
|
56
58
|
let transitionsCount = 0
|
|
57
59
|
let serializationsSinceTransitionRebuild = 0
|
|
58
|
-
|
|
59
|
-
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) {
|
|
60
79
|
if (!target) {
|
|
61
80
|
target = new ByteArrayAllocate(8192)
|
|
62
81
|
targetView = new DataView(target.buffer, 0, 8192)
|
|
@@ -73,10 +92,25 @@ export class Encoder extends Decoder {
|
|
|
73
92
|
position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
|
|
74
93
|
start = position
|
|
75
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
|
+
|
|
76
101
|
sharedStructures = encoder.structures
|
|
77
102
|
if (sharedStructures) {
|
|
78
|
-
if (sharedStructures.uninitialized)
|
|
79
|
-
|
|
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
|
+
}
|
|
80
114
|
let sharedStructuresLength = sharedStructures.length
|
|
81
115
|
if (sharedStructuresLength > maxSharedStructures && !isSequential)
|
|
82
116
|
sharedStructuresLength = maxSharedStructures
|
|
@@ -85,6 +119,7 @@ export class Encoder extends Decoder {
|
|
|
85
119
|
sharedStructures.transitions = Object.create(null)
|
|
86
120
|
for (let i = 0; i < sharedStructuresLength; i++) {
|
|
87
121
|
let keys = sharedStructures[i]
|
|
122
|
+
//console.log('shared struct keys:', keys)
|
|
88
123
|
if (!keys)
|
|
89
124
|
continue
|
|
90
125
|
let nextTransition, transition = sharedStructures.transitions
|
|
@@ -98,7 +133,6 @@ export class Encoder extends Decoder {
|
|
|
98
133
|
}
|
|
99
134
|
transition[RECORD_SYMBOL] = i
|
|
100
135
|
}
|
|
101
|
-
lastSharedStructuresLength = sharedStructures.length
|
|
102
136
|
}
|
|
103
137
|
if (!isSequential)
|
|
104
138
|
sharedStructures.nextId = sharedStructuresLength
|
|
@@ -131,9 +165,12 @@ export class Encoder extends Decoder {
|
|
|
131
165
|
}
|
|
132
166
|
try {
|
|
133
167
|
encode(value)
|
|
168
|
+
if (bundledStrings) {
|
|
169
|
+
writeBundles(start, encode)
|
|
170
|
+
}
|
|
134
171
|
encoder.offset = position // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
|
|
135
172
|
if (referenceMap && referenceMap.idsToInsert) {
|
|
136
|
-
position += referenceMap.idsToInsert.length *
|
|
173
|
+
position += referenceMap.idsToInsert.length * 2
|
|
137
174
|
if (position > safeEnd)
|
|
138
175
|
makeRoom(position)
|
|
139
176
|
encoder.offset = position
|
|
@@ -141,12 +178,12 @@ export class Encoder extends Decoder {
|
|
|
141
178
|
referenceMap = null
|
|
142
179
|
return serialized
|
|
143
180
|
}
|
|
144
|
-
if (encodeOptions
|
|
181
|
+
if (encodeOptions & REUSE_BUFFER_MODE) {
|
|
145
182
|
target.start = start
|
|
146
183
|
target.end = position
|
|
147
184
|
return target
|
|
148
185
|
}
|
|
149
|
-
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
|
|
150
187
|
} finally {
|
|
151
188
|
if (sharedStructures) {
|
|
152
189
|
if (serializationsSinceTransitionRebuild < 10)
|
|
@@ -164,34 +201,27 @@ export class Encoder extends Decoder {
|
|
|
164
201
|
}
|
|
165
202
|
recordIdsToRemove = []
|
|
166
203
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
// we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
|
|
172
|
-
let returnBuffer = target.subarray(start, position)
|
|
173
|
-
let shared = encoder.structures || []
|
|
174
|
-
if (sharedValues) {
|
|
175
|
-
shared = shared.concat(sharedValues)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (encoder.saveStructures(encoder.structures, lastSharedStructuresLength) === false) {
|
|
179
|
-
// get updated structures and try again if the update failed
|
|
180
|
-
encoder.structures = encoder.getStructures() || []
|
|
181
|
-
return encoder.encode(value)
|
|
182
|
-
}
|
|
183
|
-
lastSharedStructuresLength = shared.length
|
|
184
|
-
return returnBuffer
|
|
204
|
+
}
|
|
205
|
+
if (hasSharedUpdate && encoder.saveShared) {
|
|
206
|
+
if (encoder.structures.length > maxSharedStructures) {
|
|
207
|
+
encoder.structures = encoder.structures.slice(0, maxSharedStructures)
|
|
185
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
|
|
186
214
|
}
|
|
215
|
+
if (encodeOptions & RESET_BUFFER_MODE)
|
|
216
|
+
position = start
|
|
187
217
|
}
|
|
188
218
|
}
|
|
189
219
|
this.findCommonStringsToPack = () => {
|
|
190
220
|
samplingPackedValues = new Map()
|
|
191
221
|
if (!sharedPackedObjectMap)
|
|
192
222
|
sharedPackedObjectMap = Object.create(null)
|
|
193
|
-
return (
|
|
194
|
-
threshold = threshold || 4
|
|
223
|
+
return (options) => {
|
|
224
|
+
let threshold = options && options.threshold || 4
|
|
195
225
|
let position = this.pack ? options.maxPrivatePackedValues || 16 : 0
|
|
196
226
|
if (!sharedValues)
|
|
197
227
|
sharedValues = this.sharedValues = []
|
|
@@ -202,6 +232,7 @@ export class Encoder extends Decoder {
|
|
|
202
232
|
hasSharedUpdate = true
|
|
203
233
|
}
|
|
204
234
|
}
|
|
235
|
+
while (this.saveShared && this.updateSharedData() === false) {}
|
|
205
236
|
samplingPackedValues = null
|
|
206
237
|
}
|
|
207
238
|
}
|
|
@@ -250,6 +281,33 @@ export class Encoder extends Decoder {
|
|
|
250
281
|
}
|
|
251
282
|
}
|
|
252
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
|
+
}
|
|
253
311
|
let headerSize
|
|
254
312
|
// first we estimate the header size, so we can write to the correct location
|
|
255
313
|
if (strLength < 0x20) {
|
|
@@ -376,16 +434,16 @@ export class Encoder extends Decoder {
|
|
|
376
434
|
if (referenceMap) {
|
|
377
435
|
let referee = referenceMap.get(value)
|
|
378
436
|
if (referee) {
|
|
379
|
-
|
|
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) {
|
|
380
441
|
let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = [])
|
|
381
|
-
referee.
|
|
442
|
+
referee.references = []
|
|
443
|
+
idsToInsert.push(referee)
|
|
382
444
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
target[position++] = 40010 & 0xff
|
|
386
|
-
target[position++] = 0x1a // uint32
|
|
387
|
-
targetView.setUint32(position, referee.id)
|
|
388
|
-
position += 4
|
|
445
|
+
referee.references.push(position - start)
|
|
446
|
+
position += 2 // TODO: also support 32-bit
|
|
389
447
|
return
|
|
390
448
|
} else
|
|
391
449
|
referenceMap.set(value, { offset: position - start })
|
|
@@ -425,16 +483,23 @@ export class Encoder extends Decoder {
|
|
|
425
483
|
targetView.setUint32(position, length)
|
|
426
484
|
position += 4
|
|
427
485
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
+
}
|
|
431
496
|
}
|
|
432
|
-
} else {
|
|
497
|
+
} else {
|
|
433
498
|
for (let i = 0, l = extensions.length; i < l; i++) {
|
|
434
499
|
let extensionClass = extensionClasses[i]
|
|
435
500
|
if (value instanceof extensionClass) {
|
|
436
501
|
let extension = extensions[i]
|
|
437
|
-
let tag = extension.tag
|
|
502
|
+
let tag = extension.tag || extension.getTag && extension.getTag(value)
|
|
438
503
|
if (tag < 0x18) {
|
|
439
504
|
target[position++] = 0xc0 | tag
|
|
440
505
|
} else if (tag < 0x100) {
|
|
@@ -496,6 +561,7 @@ export class Encoder extends Decoder {
|
|
|
496
561
|
const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => {
|
|
497
562
|
// this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
|
|
498
563
|
let keys = Object.keys(object)
|
|
564
|
+
let vals = Object.values(object)
|
|
499
565
|
let length = keys.length
|
|
500
566
|
if (length < 0x18) {
|
|
501
567
|
target[position++] = 0xa0 | length
|
|
@@ -512,9 +578,16 @@ export class Encoder extends Decoder {
|
|
|
512
578
|
position += 4
|
|
513
579
|
}
|
|
514
580
|
let key
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
+
}
|
|
518
591
|
}
|
|
519
592
|
} :
|
|
520
593
|
(object, safePrototype) => {
|
|
@@ -522,12 +595,18 @@ export class Encoder extends Decoder {
|
|
|
522
595
|
let objectOffset = position - start
|
|
523
596
|
position += 2
|
|
524
597
|
let size = 0
|
|
525
|
-
|
|
526
|
-
if (safePrototype || object.hasOwnProperty(key)) {
|
|
527
|
-
encode(key)
|
|
598
|
+
if (encoder.keyMap) {
|
|
599
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
600
|
+
encode(encoder.encodeKey(key))
|
|
528
601
|
encode(object[key])
|
|
529
602
|
size++
|
|
530
603
|
}
|
|
604
|
+
} else {
|
|
605
|
+
for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
|
|
606
|
+
encode(key)
|
|
607
|
+
encode(object[key])
|
|
608
|
+
size++
|
|
609
|
+
}
|
|
531
610
|
}
|
|
532
611
|
target[objectOffset++ + start] = size >> 8
|
|
533
612
|
target[objectOffset + start] = size & 0xff
|
|
@@ -575,11 +654,16 @@ export class Encoder extends Decoder {
|
|
|
575
654
|
}*/
|
|
576
655
|
(object) => {
|
|
577
656
|
let keys = Object.keys(object)
|
|
657
|
+
let vals = Object.values(object)
|
|
658
|
+
if (this.keyMap) keys = keys.map(k => this.encodeKey(k))
|
|
578
659
|
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
|
|
579
660
|
let newTransitions = 0
|
|
580
661
|
let length = keys.length
|
|
662
|
+
//let parentRecordId
|
|
581
663
|
for (let i = 0; i < length; i++) {
|
|
664
|
+
//if (!parentRecordId)
|
|
582
665
|
let key = keys[i]
|
|
666
|
+
// parentRecordId = transition[RECORD_SYMBOL]
|
|
583
667
|
nextTransition = transition[key]
|
|
584
668
|
if (!nextTransition) {
|
|
585
669
|
nextTransition = transition[key] = Object.create(null)
|
|
@@ -589,9 +673,9 @@ export class Encoder extends Decoder {
|
|
|
589
673
|
}
|
|
590
674
|
let recordId = transition[RECORD_SYMBOL]
|
|
591
675
|
if (recordId !== undefined) {
|
|
592
|
-
target[position++] = 0xd9
|
|
593
|
-
target[position++] =
|
|
594
|
-
target[position++] = recordId
|
|
676
|
+
target[position++] = 0xd9
|
|
677
|
+
target[position++] = (recordId >> 8) | 0xe0
|
|
678
|
+
target[position++] = recordId & 0xff
|
|
595
679
|
} else {
|
|
596
680
|
recordId = structures.nextId++
|
|
597
681
|
if (!recordId) {
|
|
@@ -604,13 +688,13 @@ export class Encoder extends Decoder {
|
|
|
604
688
|
transition[RECORD_SYMBOL] = recordId
|
|
605
689
|
structures[recordId] = keys
|
|
606
690
|
if (sharedStructures && sharedStructures.length <= maxSharedStructures) {
|
|
607
|
-
target[position++] = 0xd9
|
|
608
|
-
target[position++] =
|
|
609
|
-
target[position++] = recordId
|
|
691
|
+
target[position++] = 0xd9
|
|
692
|
+
target[position++] = (recordId >> 8) | 0xe0
|
|
693
|
+
target[position++] = recordId & 0xff
|
|
610
694
|
hasSharedUpdate = true
|
|
611
695
|
} else {
|
|
612
|
-
|
|
613
|
-
|
|
696
|
+
targetView.setUint32(position, 0xd9dfff00) // tag two byte, then record definition id
|
|
697
|
+
position += 3
|
|
614
698
|
if (newTransitions)
|
|
615
699
|
transitionsCount += serializationsSinceTransitionRebuild * newTransitions
|
|
616
700
|
// record the removal of the id, we can maintain our shared structure
|
|
@@ -618,13 +702,9 @@ export class Encoder extends Decoder {
|
|
|
618
702
|
recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined // we are cycling back through, and have to remove old ones
|
|
619
703
|
recordIdsToRemove.push(transition)
|
|
620
704
|
writeArrayHeader(length + 2)
|
|
705
|
+
encode(0xe000 + recordId)
|
|
621
706
|
encode(keys)
|
|
622
|
-
|
|
623
|
-
target[position++] = RECORD_STARTING_ID_PREFIX
|
|
624
|
-
target[position++] = recordId
|
|
625
|
-
// now write the values
|
|
626
|
-
for (let i =0; i < length; i++)
|
|
627
|
-
encode(object[keys[i]])
|
|
707
|
+
for (let v of Object.values(object)) encode(v)
|
|
628
708
|
return
|
|
629
709
|
}
|
|
630
710
|
}
|
|
@@ -633,8 +713,7 @@ export class Encoder extends Decoder {
|
|
|
633
713
|
} else {
|
|
634
714
|
writeArrayHeader(length)
|
|
635
715
|
}
|
|
636
|
-
for (let i =0; i < length; i++)
|
|
637
|
-
encode(object[keys[i]])
|
|
716
|
+
for (let i =0; i < length; i++) encode(vals[i])
|
|
638
717
|
}
|
|
639
718
|
const makeRoom = (end) => {
|
|
640
719
|
let newSize
|
|
@@ -643,7 +722,7 @@ export class Encoder extends Decoder {
|
|
|
643
722
|
if ((end - start) > MAX_BUFFER_SIZE)
|
|
644
723
|
throw new Error('Encoded buffer would be larger than maximum buffer size')
|
|
645
724
|
newSize = Math.min(MAX_BUFFER_SIZE,
|
|
646
|
-
Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2),
|
|
725
|
+
Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000)
|
|
647
726
|
} else // faster handling for smaller buffers
|
|
648
727
|
newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12
|
|
649
728
|
let newBuffer = new ByteArrayAllocate(newSize)
|
|
@@ -664,11 +743,32 @@ export class Encoder extends Decoder {
|
|
|
664
743
|
targetView = new DataView(target.buffer, target.byteOffset, target.byteLength)
|
|
665
744
|
position = 0
|
|
666
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
|
+
}
|
|
667
766
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
767
|
+
class SharedData {
|
|
768
|
+
constructor(structures, values, version) {
|
|
769
|
+
this.structures = structures
|
|
770
|
+
this.packedValues = values
|
|
771
|
+
this.version = version
|
|
672
772
|
}
|
|
673
773
|
}
|
|
674
774
|
|
|
@@ -739,9 +839,11 @@ function findRepetitiveStrings(value, packedValues) {
|
|
|
739
839
|
}
|
|
740
840
|
}
|
|
741
841
|
|
|
742
|
-
extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray,
|
|
743
|
-
Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
|
|
744
|
-
|
|
842
|
+
extensionClasses = [ Date, Set, Error, RegExp, Tag, ArrayBuffer, ByteArray,
|
|
843
|
+
Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,
|
|
844
|
+
typeof BigUint64Array == 'undefined' ? function() {} : BigUint64Array, Int8Array, Int16Array, Int32Array,
|
|
845
|
+
typeof BigInt64Array == 'undefined' ? function() {} : BigInt64Array,
|
|
846
|
+
Float32Array, Float64Array, SharedData]
|
|
745
847
|
|
|
746
848
|
//Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/
|
|
747
849
|
extensions = [{
|
|
@@ -776,6 +878,13 @@ extensions = [{
|
|
|
776
878
|
encode(regex, encode) {
|
|
777
879
|
encode([ 'RegExp', regex.source, regex.flags ])
|
|
778
880
|
}
|
|
881
|
+
}, {
|
|
882
|
+
getTag(tag) {
|
|
883
|
+
return tag.tag
|
|
884
|
+
},
|
|
885
|
+
encode(tag, encode) {
|
|
886
|
+
encode(tag.value)
|
|
887
|
+
}
|
|
779
888
|
}, {
|
|
780
889
|
encode(arrayBuffer, encode, makeRoom) {
|
|
781
890
|
writeBuffer(arrayBuffer, makeRoom)
|
|
@@ -794,7 +903,35 @@ extensions = [{
|
|
|
794
903
|
typedArrayEncoder(78),
|
|
795
904
|
typedArrayEncoder(79),
|
|
796
905
|
typedArrayEncoder(81),
|
|
797
|
-
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
|
+
}]
|
|
798
935
|
|
|
799
936
|
function typedArrayEncoder(tag) {
|
|
800
937
|
return {
|
|
@@ -834,27 +971,35 @@ function writeBuffer(buffer, makeRoom) {
|
|
|
834
971
|
function insertIds(serialized, idsToInsert) {
|
|
835
972
|
// insert the ids that need to be referenced for structured clones
|
|
836
973
|
let nextId
|
|
837
|
-
let distanceToMove = idsToInsert.length *
|
|
974
|
+
let distanceToMove = idsToInsert.length * 2
|
|
838
975
|
let lastEnd = serialized.length - distanceToMove
|
|
839
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
|
+
}
|
|
840
985
|
while (nextId = idsToInsert.pop()) {
|
|
841
986
|
let offset = nextId.offset
|
|
842
|
-
let id = nextId.id
|
|
843
987
|
serialized.copyWithin(offset + distanceToMove, offset, lastEnd)
|
|
844
|
-
distanceToMove -=
|
|
988
|
+
distanceToMove -= 2
|
|
845
989
|
let position = offset + distanceToMove
|
|
846
|
-
serialized[position++] =
|
|
847
|
-
serialized[position++] =
|
|
848
|
-
serialized[position++] = 40009 & 0xff
|
|
849
|
-
serialized[position++] = 0x1a // uint32
|
|
850
|
-
serialized[position++] = id >> 24
|
|
851
|
-
serialized[position++] = (id >> 16) & 0xff
|
|
852
|
-
serialized[position++] = (id >> 8) & 0xff
|
|
853
|
-
serialized[position++] = id & 0xff
|
|
990
|
+
serialized[position++] = 0xd8
|
|
991
|
+
serialized[position++] = 28 // http://cbor.schmorp.de/value-sharing
|
|
854
992
|
lastEnd = offset
|
|
855
993
|
}
|
|
856
994
|
return serialized
|
|
857
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
|
+
}
|
|
858
1003
|
|
|
859
1004
|
export function addExtension(extension) {
|
|
860
1005
|
if (extension.Class) {
|
|
@@ -870,4 +1015,6 @@ export const encode = defaultEncoder.encode
|
|
|
870
1015
|
export { FLOAT32_OPTIONS } from './decode.js'
|
|
871
1016
|
import { FLOAT32_OPTIONS } from './decode.js'
|
|
872
1017
|
export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
|
|
873
|
-
export const REUSE_BUFFER_MODE =
|
|
1018
|
+
export const REUSE_BUFFER_MODE = 512
|
|
1019
|
+
export const RESET_BUFFER_MODE = 1024
|
|
1020
|
+
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { Decoder, decode, addExtension, FLOAT32_OPTIONS, clearSource, roundFloat32 } from './decode.js'
|
|
1
|
+
export { Decoder, decode, addExtension, FLOAT32_OPTIONS, clearSource, roundFloat32, isNativeAccelerationEnabled } from './decode.js'
|
|
2
2
|
export { Encoder, encode } from './encode.js'
|
|
3
3
|
import { Transform, Readable } from 'stream'
|
|
4
4
|
|
package/index.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { Encoder, addExtension, encode, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } from './encode.js'
|
|
2
|
-
export { Tag, Decoder, decodeMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32 } 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, isNativeAccelerationEnabled } from './decode.js'
|
|
3
3
|
export { decodeIter, encodeIter } from './iterators.js'
|
|
4
|
-
export const useRecords = false
|
|
5
|
-
export const mapsAsObjects = true
|
package/node-index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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'
|
|
2
|
+
export { Tag, Decoder, decodeMultiple, decode, FLOAT32_OPTIONS, clearSource, roundFloat32, isNativeAccelerationEnabled } from './decode.js'
|
|
3
3
|
export { EncoderStream, DecoderStream } from './stream.js'
|
|
4
4
|
export { decodeIter, encodeIter } from './iterators.js'
|
|
5
5
|
export const useRecords = false
|
|
@@ -16,9 +16,7 @@ function tryRequire(moduleId) {
|
|
|
16
16
|
let require = createRequire(import.meta.url)
|
|
17
17
|
return require(moduleId)
|
|
18
18
|
} catch (error) {
|
|
19
|
-
if (typeof window
|
|
20
|
-
console.warn('Native extraction module not loaded, cbor-x will still run, but with decreased performance. ' + error.message.split('\n')[0])
|
|
21
|
-
else
|
|
19
|
+
if (typeof window != 'undefined')
|
|
22
20
|
console.warn('For browser usage, directly use cbor-x/decode or cbor-x/encode modules. ' + error.message.split('\n')[0])
|
|
23
21
|
}
|
|
24
22
|
}
|
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
|
-
}
|