cbor-x 0.8.0 → 0.9.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/.vscode/launch.json +22 -0
- package/README.md +49 -45
- package/SECURITY.md +11 -0
- package/{unpack.d.ts → decode.d.ts} +12 -11
- package/decode.js +86 -33
- package/dist/index.js +194 -49
- package/dist/index.min.js +65 -51
- package/dist/node.cjs +228 -81
- package/dist/test.js +33 -1
- package/encode.d.ts +6 -0
- package/encode.js +31 -18
- package/index.d.ts +7 -6
- package/index.js +2 -2
- package/iterators.js +85 -0
- package/node-index.js +24 -0
- package/package.json +16 -17
- package/rollup.config.js +5 -5
- package/stream.js +20 -22
- package/tests/benchmark.cjs +13 -7
- package/tests/{test-compatibility.js → test-compatibility.cjs} +7 -12
- package/tests/test-incomplete.js +41 -0
- package/tests/test-node-iterators.js +72 -0
- package/tests/test-node-stream.js +17 -5
- package/tests/test.js +33 -2
- package/index.mjs +0 -35
- package/node.js +0 -25
- package/pack.d.ts +0 -9
- package/tests/test.mjs +0 -31
package/encode.js
CHANGED
|
@@ -43,11 +43,8 @@ export class Encoder extends Decoder {
|
|
|
43
43
|
let recordIdsToRemove = []
|
|
44
44
|
let transitionsCount = 0
|
|
45
45
|
let serializationsSinceTransitionRebuild = 0
|
|
46
|
-
if (this.structures && this.structures.length > maxSharedStructures) {
|
|
47
|
-
throw new Error('Too many shared structures')
|
|
48
|
-
}
|
|
49
46
|
|
|
50
|
-
this.encode = function(value) {
|
|
47
|
+
this.encode = function(value, encodeOptions) {
|
|
51
48
|
if (!target) {
|
|
52
49
|
target = new ByteArrayAllocate(8192)
|
|
53
50
|
targetView = new DataView(target.buffer, 0, 8192)
|
|
@@ -60,7 +57,8 @@ export class Encoder extends Decoder {
|
|
|
60
57
|
targetView = new DataView(target.buffer, 0, target.length)
|
|
61
58
|
safeEnd = target.length - 10
|
|
62
59
|
position = 0
|
|
63
|
-
}
|
|
60
|
+
} else if (encodeOptions === REUSE_BUFFER_MODE)
|
|
61
|
+
position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
|
|
64
62
|
start = position
|
|
65
63
|
referenceMap = encoder.structuredClone ? new Map() : null
|
|
66
64
|
sharedStructures = encoder.structures
|
|
@@ -68,7 +66,7 @@ export class Encoder extends Decoder {
|
|
|
68
66
|
if (sharedStructures.uninitialized)
|
|
69
67
|
encoder.structures = sharedStructures = encoder.getStructures()
|
|
70
68
|
let sharedStructuresLength = sharedStructures.length
|
|
71
|
-
if (sharedStructuresLength >
|
|
69
|
+
if (sharedStructuresLength > maxSharedStructures && !isSequential)
|
|
72
70
|
sharedStructuresLength = maxSharedStructures
|
|
73
71
|
if (!sharedStructures.transitions) {
|
|
74
72
|
// rebuild our structure transitions
|
|
@@ -78,8 +76,8 @@ export class Encoder extends Decoder {
|
|
|
78
76
|
if (!keys)
|
|
79
77
|
continue
|
|
80
78
|
let nextTransition, transition = sharedStructures.transitions
|
|
81
|
-
for (let
|
|
82
|
-
let key = keys[
|
|
79
|
+
for (let j = 0, l = keys.length; j < l; j++) {
|
|
80
|
+
let key = keys[j]
|
|
83
81
|
nextTransition = transition[key]
|
|
84
82
|
if (!nextTransition) {
|
|
85
83
|
nextTransition = transition[key] = Object.create(null)
|
|
@@ -108,7 +106,12 @@ export class Encoder extends Decoder {
|
|
|
108
106
|
referenceMap = null
|
|
109
107
|
return serialized
|
|
110
108
|
}
|
|
111
|
-
|
|
109
|
+
if (encodeOptions === REUSE_BUFFER_MODE) {
|
|
110
|
+
target.start = start
|
|
111
|
+
target.end = position
|
|
112
|
+
return target
|
|
113
|
+
}
|
|
114
|
+
return target.subarray(start, position) // position can change if we call pack again in saveStructures, so we get the buffer now
|
|
112
115
|
} finally {
|
|
113
116
|
if (sharedStructures) {
|
|
114
117
|
if (serializationsSinceTransitionRebuild < 10)
|
|
@@ -122,7 +125,7 @@ export class Encoder extends Decoder {
|
|
|
122
125
|
recordIdsToRemove = []
|
|
123
126
|
} else if (recordIdsToRemove.length > 0 && !isSequential) {
|
|
124
127
|
for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
|
|
125
|
-
recordIdsToRemove[i][RECORD_SYMBOL] =
|
|
128
|
+
recordIdsToRemove[i][RECORD_SYMBOL] = undefined
|
|
126
129
|
}
|
|
127
130
|
recordIdsToRemove = []
|
|
128
131
|
}
|
|
@@ -130,13 +133,15 @@ export class Encoder extends Decoder {
|
|
|
130
133
|
if (encoder.structures.length > maxSharedStructures) {
|
|
131
134
|
encoder.structures = encoder.structures.slice(0, maxSharedStructures)
|
|
132
135
|
}
|
|
133
|
-
|
|
136
|
+
// we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
|
|
137
|
+
let returnBuffer = target.subarray(start, position)
|
|
134
138
|
if (encoder.saveStructures(encoder.structures, lastSharedStructuresLength) === false) {
|
|
135
139
|
// get updated structures and try again if the update failed
|
|
136
140
|
encoder.structures = encoder.getStructures() || []
|
|
137
141
|
return encoder.encode(value)
|
|
138
142
|
}
|
|
139
143
|
lastSharedStructuresLength = encoder.structures.length
|
|
144
|
+
return returnBuffer
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
147
|
}
|
|
@@ -257,7 +262,7 @@ export class Encoder extends Decoder {
|
|
|
257
262
|
targetView.setFloat32(position, value)
|
|
258
263
|
let xShifted
|
|
259
264
|
if (useFloat32 < 4 ||
|
|
260
|
-
|
|
265
|
+
// this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
|
|
261
266
|
((xShifted = value * mult10[((target[position] & 0x7f) << 1) | (target[position + 1] >> 7)]) >> 0) === xShifted) {
|
|
262
267
|
position += 4
|
|
263
268
|
return
|
|
@@ -303,6 +308,12 @@ export class Encoder extends Decoder {
|
|
|
303
308
|
encode(value[i])
|
|
304
309
|
}
|
|
305
310
|
} else if (constructor === Map) {
|
|
311
|
+
if (this.mapsAsObjects ? this.useTag259ForMaps !== false : this.useTag259ForMaps) {
|
|
312
|
+
// 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
|
|
313
|
+
target[position++] = 0xd9
|
|
314
|
+
target[position++] = 1
|
|
315
|
+
target[position++] = 3
|
|
316
|
+
}
|
|
306
317
|
length = value.size
|
|
307
318
|
if (length < 0x18) {
|
|
308
319
|
target[position++] = 0xa0 | length
|
|
@@ -380,10 +391,9 @@ export class Encoder extends Decoder {
|
|
|
380
391
|
}
|
|
381
392
|
position += 8
|
|
382
393
|
} else if (type === 'undefined') {
|
|
383
|
-
//target[position++] = 0xc1 // this is the "never-used" byte
|
|
384
394
|
target[position++] = 0xf7
|
|
385
395
|
} else {
|
|
386
|
-
throw new Error('Unknown type ' + type)
|
|
396
|
+
throw new Error('Unknown type: ' + type)
|
|
387
397
|
}
|
|
388
398
|
}
|
|
389
399
|
|
|
@@ -472,7 +482,7 @@ export class Encoder extends Decoder {
|
|
|
472
482
|
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
|
|
473
483
|
let newTransitions = 0
|
|
474
484
|
let length = keys.length
|
|
475
|
-
for (let i =0; i < length; i++) {
|
|
485
|
+
for (let i = 0; i < length; i++) {
|
|
476
486
|
let key = keys[i]
|
|
477
487
|
nextTransition = transition[key]
|
|
478
488
|
if (!nextTransition) {
|
|
@@ -482,7 +492,7 @@ export class Encoder extends Decoder {
|
|
|
482
492
|
transition = nextTransition
|
|
483
493
|
}
|
|
484
494
|
let recordId = transition[RECORD_SYMBOL]
|
|
485
|
-
if (recordId) {
|
|
495
|
+
if (recordId !== undefined) {
|
|
486
496
|
target[position++] = 0xd9 // tag two byte
|
|
487
497
|
target[position++] = RECORD_STARTING_ID_PREFIX
|
|
488
498
|
target[position++] = recordId
|
|
@@ -509,7 +519,7 @@ export class Encoder extends Decoder {
|
|
|
509
519
|
transitionsCount += serializationsSinceTransitionRebuild * newTransitions
|
|
510
520
|
// record the removal of the id, we can maintain our shared structure
|
|
511
521
|
if (recordIdsToRemove.length >= MAX_STRUCTURES - maxSharedStructures)
|
|
512
|
-
recordIdsToRemove.shift()[RECORD_SYMBOL] =
|
|
522
|
+
recordIdsToRemove.shift()[RECORD_SYMBOL] = undefined // we are cycling back through, and have to remove old ones
|
|
513
523
|
recordIdsToRemove.push(transition)
|
|
514
524
|
if (length < 0x16)
|
|
515
525
|
target[position++] = 0x82 + length // array header, length of values + 2
|
|
@@ -655,7 +665,9 @@ function typedArrayEncoder(tag) {
|
|
|
655
665
|
}
|
|
656
666
|
function writeBuffer(buffer, makeRoom) {
|
|
657
667
|
let length = buffer.byteLength
|
|
658
|
-
if (length <
|
|
668
|
+
if (length < 0x18) {
|
|
669
|
+
target[position++] = 0x40 + length
|
|
670
|
+
} else if (length < 0x100) {
|
|
659
671
|
target[position++] = 0x58
|
|
660
672
|
target[position++] = length
|
|
661
673
|
} else if (length < 0x10000) {
|
|
@@ -713,3 +725,4 @@ export const encode = defaultEncoder.encode
|
|
|
713
725
|
export { FLOAT32_OPTIONS } from './decode.js'
|
|
714
726
|
import { FLOAT32_OPTIONS } from './decode.js'
|
|
715
727
|
export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
|
|
728
|
+
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
|
+
}
|
package/node-index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
export { EncoderStream, DecoderStream } from './stream.js'
|
|
4
|
+
export { decodeIter, encodeIter } from './iterators.js'
|
|
5
|
+
export const useRecords = false
|
|
6
|
+
export const mapsAsObjects = true
|
|
7
|
+
import { setExtractor } from './decode.js'
|
|
8
|
+
import { createRequire } from 'module'
|
|
9
|
+
|
|
10
|
+
const extractor = tryRequire('cbor-extract')
|
|
11
|
+
if (extractor)
|
|
12
|
+
setExtractor(extractor.extractStrings)
|
|
13
|
+
|
|
14
|
+
function tryRequire(moduleId) {
|
|
15
|
+
try {
|
|
16
|
+
let require = createRequire(import.meta.url)
|
|
17
|
+
return require(moduleId)
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (typeof window == 'undefined')
|
|
20
|
+
console.warn('Native extraction module not loaded, cbor-x will still run, but with decreased performance. ' + error.message.split('\n')[0])
|
|
21
|
+
else
|
|
22
|
+
console.warn('For browser usage, directly use cbor-x/decode or cbor-x/encode modules. ' + error.message.split('\n')[0])
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cbor-x",
|
|
3
3
|
"author": "Kris Zyp",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.0",
|
|
5
5
|
"description": "Ultra-fast CBOR implementation with tag extensions for records and structured cloning",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"types": "./index.d.ts",
|
|
8
|
+
"main": "./dist/node.cjs",
|
|
9
|
+
"module": "./index.js",
|
|
8
10
|
"keywords": [
|
|
9
11
|
"CBOR",
|
|
10
12
|
"performance",
|
|
@@ -16,55 +18,52 @@
|
|
|
16
18
|
"url": "http://github.com/kriszyp/cbor-x"
|
|
17
19
|
},
|
|
18
20
|
"scripts": {
|
|
19
|
-
"benchmark": "node ./tests/benchmark.
|
|
21
|
+
"benchmark": "node ./tests/benchmark.cjs",
|
|
20
22
|
"build": "rollup -c",
|
|
23
|
+
"dry-run": "npm publish --dry-run",
|
|
21
24
|
"prepare": "npm run build",
|
|
22
|
-
"test": "
|
|
25
|
+
"test": "mocha tests/test**.*js -u tdd --experimental-json-modules"
|
|
23
26
|
},
|
|
24
27
|
"type": "module",
|
|
25
|
-
"main": "./dist/node.cjs",
|
|
26
28
|
"exports": {
|
|
27
29
|
".": {
|
|
28
30
|
"node": {
|
|
29
31
|
"require": "./dist/node.cjs",
|
|
30
|
-
"import": "./node.js"
|
|
32
|
+
"import": "./node-index.js"
|
|
31
33
|
},
|
|
32
|
-
"default":
|
|
33
|
-
"import": "./index.js"
|
|
34
|
-
}
|
|
34
|
+
"default": "./index.js"
|
|
35
35
|
},
|
|
36
|
-
"./
|
|
36
|
+
"./encode": {
|
|
37
37
|
"node": {
|
|
38
38
|
"import": "./index.js",
|
|
39
39
|
"require": "./dist/node.cjs"
|
|
40
40
|
},
|
|
41
41
|
"default": {
|
|
42
|
-
"import": "./
|
|
42
|
+
"import": "./encode.js"
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
|
-
"./
|
|
45
|
+
"./decode": {
|
|
46
46
|
"node": {
|
|
47
47
|
"import": "./index.js",
|
|
48
48
|
"require": "./dist/node.cjs"
|
|
49
49
|
},
|
|
50
50
|
"default": {
|
|
51
|
-
"import": "./
|
|
51
|
+
"import": "./decode.js"
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
"optionalDependencies": {
|
|
56
|
-
"cbor-extract": "^0.
|
|
56
|
+
"cbor-extract": "^0.3.2"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
+
"@types/node": "latest",
|
|
59
60
|
"async": "^3",
|
|
60
61
|
"cbor": "^5",
|
|
61
62
|
"cbor-sync": "^1.0.4",
|
|
62
|
-
"
|
|
63
|
-
"chai": "^4",
|
|
63
|
+
"chai": "^4.3.4",
|
|
64
64
|
"esm": "^3.2.25",
|
|
65
65
|
"mocha": "^8.1.3",
|
|
66
66
|
"rollup": "^1.20.3",
|
|
67
|
-
"rollup-plugin-babel-minify": "^9.0.0"
|
|
68
|
-
"@rollup/plugin-json": "^4.1.0"
|
|
67
|
+
"rollup-plugin-babel-minify": "^9.0.0"
|
|
69
68
|
}
|
|
70
69
|
}
|
package/rollup.config.js
CHANGED
|
@@ -3,7 +3,7 @@ import json from "@rollup/plugin-json";
|
|
|
3
3
|
|
|
4
4
|
export default [
|
|
5
5
|
{
|
|
6
|
-
input: "node.js",
|
|
6
|
+
input: "node-index.js",
|
|
7
7
|
output: [
|
|
8
8
|
{
|
|
9
9
|
file: "dist/node.cjs",
|
|
@@ -16,7 +16,7 @@ export default [
|
|
|
16
16
|
output: {
|
|
17
17
|
file: "dist/index.js",
|
|
18
18
|
format: "umd",
|
|
19
|
-
name: "
|
|
19
|
+
name: "CBOR"
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
{
|
|
@@ -26,7 +26,7 @@ export default [
|
|
|
26
26
|
output: {
|
|
27
27
|
file: "dist/index.min.js",
|
|
28
28
|
format: "umd",
|
|
29
|
-
name: "
|
|
29
|
+
name: "CBOR"
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
{
|
|
@@ -38,8 +38,8 @@ export default [
|
|
|
38
38
|
format: "iife",
|
|
39
39
|
globals: {
|
|
40
40
|
chai: 'chai',
|
|
41
|
-
'./index.js': '
|
|
41
|
+
'./index.js': 'CBOR',
|
|
42
42
|
},
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
];
|
|
45
|
+
];
|
package/stream.js
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
import { Transform } from 'stream'
|
|
2
2
|
import { Encoder } from './encode.js'
|
|
3
|
-
import {
|
|
3
|
+
import { checkedRead, getPosition, Decoder, clearSource } from './decode.js'
|
|
4
4
|
var DEFAULT_OPTIONS = {objectMode: true}
|
|
5
5
|
|
|
6
6
|
export class EncoderStream extends Transform {
|
|
7
7
|
constructor(options) {
|
|
8
8
|
if (!options)
|
|
9
9
|
options = {}
|
|
10
|
+
options.writableObjectMode = true
|
|
10
11
|
super(options)
|
|
11
12
|
options.sequential = true
|
|
12
|
-
this.encoder = new Encoder(options)
|
|
13
|
+
this.encoder = options.encoder || new Encoder(options)
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
+
_transform(value, encoding, callback) {
|
|
15
16
|
this.push(this.encoder.encode(value))
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
end(value) {
|
|
19
|
-
if (value != null)
|
|
20
|
-
this.push(this.encoder.encode(value))
|
|
21
|
-
this.push(null)
|
|
17
|
+
callback()
|
|
22
18
|
}
|
|
23
19
|
}
|
|
24
20
|
|
|
@@ -29,31 +25,33 @@ export class DecoderStream extends Transform {
|
|
|
29
25
|
options.objectMode = true
|
|
30
26
|
super(options)
|
|
31
27
|
options.structures = []
|
|
32
|
-
this.decoder = new Decoder(options)
|
|
28
|
+
this.decoder = options.decoder || new Decoder(options)
|
|
33
29
|
}
|
|
34
30
|
_transform(chunk, encoding, callback) {
|
|
35
31
|
if (this.incompleteBuffer) {
|
|
36
32
|
chunk = Buffer.concat([this.incompleteBuffer, chunk])
|
|
37
33
|
this.incompleteBuffer = null
|
|
38
34
|
}
|
|
39
|
-
let
|
|
40
|
-
let size = chunk.length
|
|
35
|
+
let values
|
|
41
36
|
try {
|
|
42
|
-
this.
|
|
43
|
-
position = getPosition()
|
|
44
|
-
while (position < size) {
|
|
45
|
-
let value = read()
|
|
46
|
-
this.push(value)
|
|
47
|
-
position = getPosition()
|
|
48
|
-
}
|
|
37
|
+
values = this.decoder.decodeMultiple(chunk)
|
|
49
38
|
} catch(error) {
|
|
50
|
-
if (error.incomplete)
|
|
51
|
-
this.incompleteBuffer = chunk.slice(
|
|
39
|
+
if (error.incomplete) {
|
|
40
|
+
this.incompleteBuffer = chunk.slice(error.lastPosition)
|
|
41
|
+
values = error.values
|
|
42
|
+
}
|
|
52
43
|
else
|
|
53
44
|
throw error
|
|
54
45
|
} finally {
|
|
55
|
-
|
|
46
|
+
for (let value of values || []) {
|
|
47
|
+
if (value === null)
|
|
48
|
+
value = this.getNullValue()
|
|
49
|
+
this.push(value)
|
|
50
|
+
}
|
|
56
51
|
}
|
|
57
52
|
if (callback) callback()
|
|
58
53
|
}
|
|
54
|
+
getNullValue() {
|
|
55
|
+
return Symbol.for(null)
|
|
56
|
+
}
|
|
59
57
|
}
|
package/tests/benchmark.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var cborX = tryRequire("
|
|
1
|
+
var cborX = tryRequire("../dist/node.cjs");
|
|
2
2
|
var msgpack_node = tryRequire("msgpack");
|
|
3
3
|
var msgpack_msgpack = tryRequire("@msgpack/msgpack");
|
|
4
4
|
var msgpack_lite = tryRequire("msgpack-lite");
|
|
@@ -43,12 +43,6 @@ console.log(rpad("", COL1, "-"), "|", lpad(":", COL2, "-"), "|", lpad(":", COL3,
|
|
|
43
43
|
|
|
44
44
|
var buf, obj;
|
|
45
45
|
|
|
46
|
-
if (JSON) {
|
|
47
|
-
buf = bench('buf = Buffer(JSON.stringify(obj));', JSON_stringify, data);
|
|
48
|
-
obj = bench('obj = JSON.parse(buf);', JSON.parse, buf);
|
|
49
|
-
test(obj);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
46
|
if (cborX) {
|
|
53
47
|
// let packr = new msgpackr.Packr({ objectsAsMaps: true })
|
|
54
48
|
buf = bench('require("cbor-x").encode(obj);', cborX.encode, data);
|
|
@@ -63,6 +57,18 @@ if (cborX) {
|
|
|
63
57
|
obj = bench('cbor-x w/ shared structures: encoder.decode(buf);', encoder.decode.bind(encoder), buf);
|
|
64
58
|
test(obj);
|
|
65
59
|
}
|
|
60
|
+
if (JSON) {
|
|
61
|
+
buf = bench('buf = Buffer(JSON.stringify(obj));', JSON_stringify, data);
|
|
62
|
+
obj = bench('obj = JSON.parse(buf);', JSON.parse, buf);
|
|
63
|
+
test(obj);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if (JSON) {
|
|
68
|
+
buf = bench('buf = Buffer(JSON.stringify(obj));', JSON_stringify, data);
|
|
69
|
+
obj = bench('obj = JSON.parse(buf);', JSON.parse, buf);
|
|
70
|
+
test(obj);
|
|
71
|
+
}
|
|
66
72
|
|
|
67
73
|
if (msgpack_lite) {
|
|
68
74
|
buf = bench('buf = require("msgpack-lite").encode(obj);', msgpack_lite.encode, data);
|
|
@@ -1,26 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const data = require('./example4.json');
|
|
2
|
+
const cborX = require('..');
|
|
3
|
+
const chai = require('chai');
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
function tryRequire(module) {
|
|
6
6
|
try {
|
|
7
7
|
return require(module)
|
|
8
8
|
} catch(error) {
|
|
9
|
+
console.log(error)
|
|
9
10
|
}
|
|
10
11
|
}
|
|
11
12
|
//if (typeof chai === 'undefined') { chai = require('chai') }
|
|
12
|
-
assert = chai.assert
|
|
13
|
-
if (typeof cborX === 'undefined') { cborX = require('..') }
|
|
13
|
+
const assert = chai.assert
|
|
14
14
|
var cbor_module = tryRequire('cbor');
|
|
15
15
|
var decode = cborX.decode
|
|
16
16
|
var encode = cborX.encode
|
|
17
|
-
//if (typeof msgpackr === 'undefined') { msgpackr = require('..') }
|
|
18
|
-
var msgpack_msgpack = tryRequire('@msgpack/msgpack');
|
|
19
|
-
var msgpack_lite = tryRequire('msgpack-lite');*/
|
|
20
|
-
var unpack = msgpackr.unpack
|
|
21
|
-
var pack = msgpackr.pack
|
|
22
17
|
|
|
23
|
-
addCompatibilitySuite = (data) => () => {
|
|
18
|
+
const addCompatibilitySuite = (data) => () => {
|
|
24
19
|
if (cbor_module) {
|
|
25
20
|
test('from cbor', function(){
|
|
26
21
|
var serialized = cbor_module.encode(data)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { encode } from '../index.js'
|
|
2
|
+
import { assert } from 'chai'
|
|
3
|
+
import { Encoder } from '../encode.js'
|
|
4
|
+
|
|
5
|
+
const tests = {
|
|
6
|
+
string: 'interesting string',
|
|
7
|
+
number: 12345,
|
|
8
|
+
buffer: Buffer.from('hello world'),
|
|
9
|
+
bigint: 12345678910n,
|
|
10
|
+
array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
|
11
|
+
'many-strings': [],
|
|
12
|
+
set: new Set('abcdefghijklmnopqrstuvwxyz'.split('')),
|
|
13
|
+
object: { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 }
|
|
14
|
+
}
|
|
15
|
+
for (let i = 0; i < 100; i++) {
|
|
16
|
+
tests['many-strings'].push('test-data-' + i)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
suite('encode and decode tests with partial values', function () {
|
|
20
|
+
const encoder = new Encoder({ objectMode: true, structures: [] })
|
|
21
|
+
|
|
22
|
+
for (const [label, testData] of Object.entries(tests)) {
|
|
23
|
+
test(label, () => {
|
|
24
|
+
const encoded = encoder.encode(testData)
|
|
25
|
+
assert.isTrue(Buffer.isBuffer(encoded), 'encode returns a Buffer')
|
|
26
|
+
assert.deepStrictEqual(encoder.decode(encoded, encoded.length, true), testData, 'full buffer decodes well')
|
|
27
|
+
const firstHalf = encoded.slice(0, Math.floor(encoded.length / 2))
|
|
28
|
+
let value
|
|
29
|
+
try {
|
|
30
|
+
value = encoder.decode(firstHalf, firstHalf.length, true)
|
|
31
|
+
} catch (err) {
|
|
32
|
+
if (err.incomplete !== true) {
|
|
33
|
+
assert.fail(`Should throw an error with .incomplete set to true, instead threw error <${err}>`)
|
|
34
|
+
} else {
|
|
35
|
+
return; // victory! correct outcome!
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
assert.fail(`Should throw an error with .incomplete set to true, instead returned value ${JSON.stringify(value)}`)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
})
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { encodeIter, decodeIter } from '../index.js'
|
|
2
|
+
import { decode } from '../index.js'
|
|
3
|
+
import { assert } from 'chai'
|
|
4
|
+
|
|
5
|
+
const tests = [
|
|
6
|
+
null,
|
|
7
|
+
false,
|
|
8
|
+
true,
|
|
9
|
+
'interesting string',
|
|
10
|
+
12345,
|
|
11
|
+
123456789n,
|
|
12
|
+
123.456,
|
|
13
|
+
Buffer.from('Hello World'),
|
|
14
|
+
new Set('abcdefghijklmnopqrstuvwxyz'.split(''))
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
suite('cbor-x iterators interface tests', function () {
|
|
18
|
+
test('sync encode iterator', () => {
|
|
19
|
+
const encodings = [...encodeIter(tests)]
|
|
20
|
+
const decodings = encodings.map(x => decode(x))
|
|
21
|
+
assert.deepStrictEqual(decodings, tests)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('async encode iterator', async () => {
|
|
25
|
+
async function * generate () {
|
|
26
|
+
for (const test of tests) {
|
|
27
|
+
await new Promise((resolve, reject) => setImmediate(resolve))
|
|
28
|
+
yield test
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const chunks = []
|
|
33
|
+
for await (const chunk of encodeIter(generate())) {
|
|
34
|
+
chunks.push(chunk)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const decodings = chunks.map(x => decode(x))
|
|
38
|
+
assert.deepStrictEqual(decodings, tests)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('sync encode and decode iterator', () => {
|
|
42
|
+
const encodings = [...encodeIter(tests)]
|
|
43
|
+
assert.isTrue(encodings.every(v => Buffer.isBuffer(v)))
|
|
44
|
+
const decodings = [...decodeIter(encodings)]
|
|
45
|
+
assert.deepStrictEqual(decodings, tests)
|
|
46
|
+
|
|
47
|
+
// also test decodings work with buffers multiple values in a buffer
|
|
48
|
+
const concatEncoding = Buffer.concat([...encodings])
|
|
49
|
+
const decodings2 = [...decodeIter([concatEncoding])]
|
|
50
|
+
assert.deepStrictEqual(decodings2, tests)
|
|
51
|
+
|
|
52
|
+
// also test decodings work with partial buffers that don't align to values perfectly
|
|
53
|
+
const half1 = concatEncoding.slice(0, Math.floor(concatEncoding.length / 2))
|
|
54
|
+
const half2 = concatEncoding.slice(Math.floor(concatEncoding.length / 2))
|
|
55
|
+
const decodings3 = [...decodeIter([half1, half2])]
|
|
56
|
+
assert.deepStrictEqual(decodings3, tests)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('async encode and decode iterator', async () => {
|
|
60
|
+
async function * generator () {
|
|
61
|
+
for (const obj of tests) {
|
|
62
|
+
await new Promise((resolve, reject) => setImmediate(resolve))
|
|
63
|
+
yield obj
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const yields = []
|
|
67
|
+
for await (const value of decodeIter(encodeIter(generator()))) {
|
|
68
|
+
yields.push(value)
|
|
69
|
+
}
|
|
70
|
+
assert.deepStrictEqual(yields, tests)
|
|
71
|
+
})
|
|
72
|
+
})
|