cborg 4.5.4 → 4.5.6
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/CHANGELOG.md +8 -0
- package/bench/bench.js +16 -12
- package/lib/2bytes.js +2 -2
- package/lib/3string.js +32 -3
- package/lib/byte-utils.js +1 -111
- package/lib/decode.js +4 -1
- package/package.json +1 -1
- package/types/lib/3string.d.ts.map +1 -1
- package/types/lib/byte-utils.d.ts +0 -6
- package/types/lib/byte-utils.d.ts.map +1 -1
- package/types/lib/decode.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## [4.5.6](https://github.com/rvagg/cborg/compare/v4.5.5...v4.5.6) (2026-01-21)
|
|
2
|
+
|
|
3
|
+
## [4.5.5](https://github.com/rvagg/cborg/compare/v4.5.4...v4.5.5) (2026-01-20)
|
|
4
|
+
|
|
5
|
+
### Trivial Changes
|
|
6
|
+
|
|
7
|
+
* **bench:** output json to file ([ca32690](https://github.com/rvagg/cborg/commit/ca326908643ce9bc2ac56dd06b6c64502b8a4f03))
|
|
8
|
+
|
|
1
9
|
## [4.5.4](https://github.com/rvagg/cborg/compare/v4.5.3...v4.5.4) (2026-01-20)
|
|
2
10
|
|
|
3
11
|
## [4.5.3](https://github.com/rvagg/cborg/compare/v4.5.2...v4.5.3) (2026-01-20)
|
package/bench/bench.js
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
* Works in both Node.js and browser environments.
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
|
-
* node bench/bench
|
|
9
|
-
* node bench/bench
|
|
10
|
-
* node bench/bench
|
|
11
|
-
* node bench/bench
|
|
12
|
-
* node bench/bench
|
|
13
|
-
* node bench/bench
|
|
8
|
+
* node bench/bench.js # run all benchmarks (dag-cbor mode)
|
|
9
|
+
* node bench/bench.js --mode=raw # run with raw cborg (no tags)
|
|
10
|
+
* node bench/bench.js --suite=bsky # run only bluesky suite
|
|
11
|
+
* node bench/bench.js --json=output.json # write JSON results to file
|
|
12
|
+
* node bench/bench.js --compare=baseline.json # compare to baseline
|
|
13
|
+
* node bench/bench.js --encode-into # use encodeInto instead of encode
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { encode, decode, encodeInto, Token, Type } from '../cborg.js'
|
|
@@ -129,7 +129,7 @@ const FIXTURE_SEED = 12345
|
|
|
129
129
|
// Parse CLI args (Node.js only, ignored in browser)
|
|
130
130
|
const args = typeof process !== 'undefined' ? process.argv.slice(2) : []
|
|
131
131
|
const opts = {
|
|
132
|
-
json: args.
|
|
132
|
+
json: args.find(a => a.startsWith('--json='))?.split('=')[1] || null,
|
|
133
133
|
suite: args.find(a => a.startsWith('--suite='))?.split('=')[1] || null,
|
|
134
134
|
compare: args.find(a => a.startsWith('--compare='))?.split('=')[1] || null,
|
|
135
135
|
duration: parseInt(args.find(a => a.startsWith('--duration='))?.split('=')[1] || DEFAULT_DURATION_MS),
|
|
@@ -184,11 +184,11 @@ function getOptions (suiteType = 'default') {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
// Output helpers
|
|
188
|
-
const log =
|
|
187
|
+
// Output helpers - always show progress to console
|
|
188
|
+
const log = console.log.bind(console)
|
|
189
189
|
const write = typeof process !== 'undefined' && process.stdout
|
|
190
190
|
? (s) => process.stdout.write(s)
|
|
191
|
-
: (s) => log(s)
|
|
191
|
+
: (s) => console.log(s)
|
|
192
192
|
|
|
193
193
|
/**
|
|
194
194
|
* Run a benchmark function for a duration, return ops/sec
|
|
@@ -364,7 +364,7 @@ async function main () {
|
|
|
364
364
|
const avgDecode = Math.round(allDecodeRates.reduce((a, b) => a + b, 0) / allDecodeRates.length * 10) / 10
|
|
365
365
|
log(`Average throughput: encode ${avgEncode} MB/s, decode ${avgDecode} MB/s`)
|
|
366
366
|
|
|
367
|
-
// JSON output
|
|
367
|
+
// JSON output to file
|
|
368
368
|
if (opts.json) {
|
|
369
369
|
const output = {
|
|
370
370
|
timestamp: new Date().toISOString(),
|
|
@@ -375,7 +375,11 @@ async function main () {
|
|
|
375
375
|
suites: allResults,
|
|
376
376
|
summary: { avgEncodeMBps: avgEncode, avgDecodeMBps: avgDecode }
|
|
377
377
|
}
|
|
378
|
-
|
|
378
|
+
if (typeof process !== 'undefined') {
|
|
379
|
+
const fs = await import('fs')
|
|
380
|
+
fs.writeFileSync(opts.json, JSON.stringify(output, null, 2) + '\n')
|
|
381
|
+
log(`\nResults written to ${opts.json}`)
|
|
382
|
+
}
|
|
379
383
|
}
|
|
380
384
|
|
|
381
385
|
// Compare to baseline
|
package/lib/2bytes.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Token, Type } from './token.js'
|
|
2
2
|
import { assertEnoughData, decodeErrPrefix } from './common.js'
|
|
3
3
|
import * as uint from './0uint.js'
|
|
4
|
-
import { compare, fromString
|
|
4
|
+
import { compare, fromString } from './byte-utils.js'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @typedef {import('../interface').ByteWriter} ByteWriter
|
|
@@ -17,7 +17,7 @@ import { compare, fromString, slice } from './byte-utils.js'
|
|
|
17
17
|
*/
|
|
18
18
|
function toToken (data, pos, prefix, length) {
|
|
19
19
|
assertEnoughData(data, pos, prefix + length)
|
|
20
|
-
const buf = slice(
|
|
20
|
+
const buf = data.slice(pos + prefix, pos + prefix + length)
|
|
21
21
|
return new Token(Type.bytes, buf, prefix + length)
|
|
22
22
|
}
|
|
23
23
|
|
package/lib/3string.js
CHANGED
|
@@ -2,13 +2,42 @@ import { Token, Type } from './token.js'
|
|
|
2
2
|
import { assertEnoughData, decodeErrPrefix } from './common.js'
|
|
3
3
|
import * as uint from './0uint.js'
|
|
4
4
|
import { encodeBytes } from './2bytes.js'
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
const textDecoder = new TextDecoder()
|
|
7
|
+
|
|
8
|
+
// Threshold for ASCII fast-path vs TextDecoder. Short ASCII strings (common for
|
|
9
|
+
// map keys) are faster to decode with a simple loop than TextDecoder overhead.
|
|
10
|
+
const ASCII_THRESHOLD = 32
|
|
6
11
|
|
|
7
12
|
/**
|
|
8
13
|
* @typedef {import('../interface').ByteWriter} ByteWriter
|
|
9
14
|
* @typedef {import('../interface').DecodeOptions} DecodeOptions
|
|
10
15
|
*/
|
|
11
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Decode UTF-8 bytes to string. For short ASCII strings (common case for map keys),
|
|
19
|
+
* a simple loop is faster than TextDecoder.
|
|
20
|
+
* @param {Uint8Array} bytes
|
|
21
|
+
* @param {number} start
|
|
22
|
+
* @param {number} end
|
|
23
|
+
* @returns {string}
|
|
24
|
+
*/
|
|
25
|
+
function toStr (bytes, start, end) {
|
|
26
|
+
const len = end - start
|
|
27
|
+
if (len < ASCII_THRESHOLD) {
|
|
28
|
+
let str = ''
|
|
29
|
+
for (let i = start; i < end; i++) {
|
|
30
|
+
const c = bytes[i]
|
|
31
|
+
if (c & 0x80) { // non-ASCII, fall back to TextDecoder
|
|
32
|
+
return textDecoder.decode(bytes.subarray(start, end))
|
|
33
|
+
}
|
|
34
|
+
str += String.fromCharCode(c)
|
|
35
|
+
}
|
|
36
|
+
return str
|
|
37
|
+
}
|
|
38
|
+
return textDecoder.decode(bytes.subarray(start, end))
|
|
39
|
+
}
|
|
40
|
+
|
|
12
41
|
/**
|
|
13
42
|
* @param {Uint8Array} data
|
|
14
43
|
* @param {number} pos
|
|
@@ -20,9 +49,9 @@ import { toString, slice } from './byte-utils.js'
|
|
|
20
49
|
function toToken (data, pos, prefix, length, options) {
|
|
21
50
|
const totLength = prefix + length
|
|
22
51
|
assertEnoughData(data, pos, totLength)
|
|
23
|
-
const tok = new Token(Type.string,
|
|
52
|
+
const tok = new Token(Type.string, toStr(data, pos + prefix, pos + totLength), totLength)
|
|
24
53
|
if (options.retainStringBytes === true) {
|
|
25
|
-
tok.byteValue = slice(
|
|
54
|
+
tok.byteValue = data.slice(pos + prefix, pos + totLength)
|
|
26
55
|
}
|
|
27
56
|
return tok
|
|
28
57
|
}
|
package/lib/byte-utils.js
CHANGED
|
@@ -10,7 +10,6 @@ export const useBuffer = globalThis.process &&
|
|
|
10
10
|
// @ts-ignore
|
|
11
11
|
typeof globalThis.Buffer.isBuffer === 'function'
|
|
12
12
|
|
|
13
|
-
const textDecoder = new TextDecoder()
|
|
14
13
|
const textEncoder = new TextEncoder()
|
|
15
14
|
|
|
16
15
|
/**
|
|
@@ -34,37 +33,6 @@ export function asU8A (buf) {
|
|
|
34
33
|
return isBuffer(buf) ? new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength) : buf
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
// Threshold for switching between manual utf8Slice and native TextDecoder/Buffer
|
|
38
|
-
// Manual decoding has overhead from array building; native is fast for strings > 32 bytes
|
|
39
|
-
const UTF8_THRESHOLD = 32
|
|
40
|
-
|
|
41
|
-
export const toString = useBuffer
|
|
42
|
-
? // eslint-disable-line operator-linebreak
|
|
43
|
-
/**
|
|
44
|
-
* @param {Uint8Array} bytes
|
|
45
|
-
* @param {number} start
|
|
46
|
-
* @param {number} end
|
|
47
|
-
*/
|
|
48
|
-
(bytes, start, end) => {
|
|
49
|
-
return end - start > UTF8_THRESHOLD
|
|
50
|
-
? // eslint-disable-line operator-linebreak
|
|
51
|
-
// @ts-ignore
|
|
52
|
-
globalThis.Buffer.from(bytes.subarray(start, end)).toString('utf8')
|
|
53
|
-
: utf8Slice(bytes, start, end)
|
|
54
|
-
}
|
|
55
|
-
/* c8 ignore next 11 */
|
|
56
|
-
: // eslint-disable-line operator-linebreak
|
|
57
|
-
/**
|
|
58
|
-
* @param {Uint8Array} bytes
|
|
59
|
-
* @param {number} start
|
|
60
|
-
* @param {number} end
|
|
61
|
-
*/
|
|
62
|
-
(bytes, start, end) => {
|
|
63
|
-
return end - start > UTF8_THRESHOLD
|
|
64
|
-
? textDecoder.decode(bytes.subarray(start, end))
|
|
65
|
-
: utf8Slice(bytes, start, end)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
36
|
export const fromString = useBuffer
|
|
69
37
|
? // eslint-disable-line operator-linebreak
|
|
70
38
|
/**
|
|
@@ -102,6 +70,7 @@ export const slice = useBuffer
|
|
|
102
70
|
* @param {number} start
|
|
103
71
|
* @param {number} end
|
|
104
72
|
*/
|
|
73
|
+
// Buffer.slice() returns a view, not a copy, so we need special handling
|
|
105
74
|
(bytes, start, end) => {
|
|
106
75
|
if (isBuffer(bytes)) {
|
|
107
76
|
return new Uint8Array(bytes.subarray(start, end))
|
|
@@ -317,85 +286,6 @@ function utf8ToBytes (str) {
|
|
|
317
286
|
return out
|
|
318
287
|
}
|
|
319
288
|
|
|
320
|
-
// The below code is mostly taken from https://github.com/feross/buffer
|
|
321
|
-
// Licensed MIT. Copyright (c) Feross Aboukhadijeh
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* @param {Uint8Array} buf
|
|
325
|
-
* @param {number} offset
|
|
326
|
-
* @param {number} end
|
|
327
|
-
* @returns {string}
|
|
328
|
-
*/
|
|
329
|
-
function utf8Slice (buf, offset, end) {
|
|
330
|
-
const res = []
|
|
331
|
-
|
|
332
|
-
while (offset < end) {
|
|
333
|
-
const firstByte = buf[offset]
|
|
334
|
-
let codePoint = null
|
|
335
|
-
let bytesPerSequence = (firstByte > 0xef) ? 4 : (firstByte > 0xdf) ? 3 : (firstByte > 0xbf) ? 2 : 1
|
|
336
|
-
|
|
337
|
-
if (offset + bytesPerSequence <= end) {
|
|
338
|
-
let secondByte, thirdByte, fourthByte, tempCodePoint
|
|
339
|
-
|
|
340
|
-
switch (bytesPerSequence) {
|
|
341
|
-
case 1:
|
|
342
|
-
if (firstByte < 0x80) {
|
|
343
|
-
codePoint = firstByte
|
|
344
|
-
}
|
|
345
|
-
break
|
|
346
|
-
case 2:
|
|
347
|
-
secondByte = buf[offset + 1]
|
|
348
|
-
if ((secondByte & 0xc0) === 0x80) {
|
|
349
|
-
tempCodePoint = (firstByte & 0x1f) << 0x6 | (secondByte & 0x3f)
|
|
350
|
-
if (tempCodePoint > 0x7f) {
|
|
351
|
-
codePoint = tempCodePoint
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
break
|
|
355
|
-
case 3:
|
|
356
|
-
secondByte = buf[offset + 1]
|
|
357
|
-
thirdByte = buf[offset + 2]
|
|
358
|
-
if ((secondByte & 0xc0) === 0x80 && (thirdByte & 0xc0) === 0x80) {
|
|
359
|
-
tempCodePoint = (firstByte & 0xf) << 0xc | (secondByte & 0x3f) << 0x6 | (thirdByte & 0x3f)
|
|
360
|
-
/* c8 ignore next 3 */
|
|
361
|
-
if (tempCodePoint > 0x7ff && (tempCodePoint < 0xd800 || tempCodePoint > 0xdfff)) {
|
|
362
|
-
codePoint = tempCodePoint
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
break
|
|
366
|
-
case 4:
|
|
367
|
-
secondByte = buf[offset + 1]
|
|
368
|
-
thirdByte = buf[offset + 2]
|
|
369
|
-
fourthByte = buf[offset + 3]
|
|
370
|
-
if ((secondByte & 0xc0) === 0x80 && (thirdByte & 0xc0) === 0x80 && (fourthByte & 0xc0) === 0x80) {
|
|
371
|
-
tempCodePoint = (firstByte & 0xf) << 0x12 | (secondByte & 0x3f) << 0xc | (thirdByte & 0x3f) << 0x6 | (fourthByte & 0x3f)
|
|
372
|
-
if (tempCodePoint > 0xffff && tempCodePoint < 0x110000) {
|
|
373
|
-
codePoint = tempCodePoint
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/* c8 ignore next 5 */
|
|
380
|
-
if (codePoint === null) {
|
|
381
|
-
// we did not generate a valid codePoint so insert a
|
|
382
|
-
// replacement char (U+FFFD) and advance only 1 byte
|
|
383
|
-
codePoint = 0xfffd
|
|
384
|
-
bytesPerSequence = 1
|
|
385
|
-
} else if (codePoint > 0xffff) {
|
|
386
|
-
// encode to utf16 (surrogate pair dance)
|
|
387
|
-
codePoint -= 0x10000
|
|
388
|
-
res.push(codePoint >>> 10 & 0x3ff | 0xd800)
|
|
389
|
-
codePoint = 0xdc00 | codePoint & 0x3ff
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
res.push(codePoint)
|
|
393
|
-
offset += bytesPerSequence
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
return decodeCodePointsArray(res)
|
|
397
|
-
}
|
|
398
|
-
|
|
399
289
|
// Based on http://stackoverflow.com/a/22747272/680742, the browser with
|
|
400
290
|
// the lowest limit is Chrome, with 0x10000 args.
|
|
401
291
|
// We go 1 magnitude less, for safety
|
package/lib/decode.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { decodeErrPrefix } from './common.js'
|
|
2
2
|
import { Type } from './token.js'
|
|
3
3
|
import { jump, quick } from './jump.js'
|
|
4
|
+
import { asU8A } from './byte-utils.js'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {import('./token.js').Token} Token
|
|
@@ -183,7 +184,9 @@ function decodeFirst (data, options) {
|
|
|
183
184
|
throw new Error(`${decodeErrPrefix} data to decode must be a Uint8Array`)
|
|
184
185
|
}
|
|
185
186
|
options = Object.assign({}, defaultDecodeOptions, options)
|
|
186
|
-
|
|
187
|
+
// Convert Buffer to plain Uint8Array for faster slicing in decode path
|
|
188
|
+
const u8aData = asU8A(data)
|
|
189
|
+
const tokeniser = options.tokenizer || new Tokeniser(u8aData, options)
|
|
187
190
|
const decoded = tokensToObject(tokeniser, options)
|
|
188
191
|
if (decoded === DONE) {
|
|
189
192
|
throw new Error(`${decodeErrPrefix} did not find any content to decode`)
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"3string.d.ts","sourceRoot":"","sources":["../../lib/3string.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"3string.d.ts","sourceRoot":"","sources":["../../lib/3string.js"],"names":[],"mappings":"AA0DA;;;;;;GAMG;AACH,0CANW,UAAU,OACV,MAAM,SACN,MAAM,WACN,aAAa,GACX,KAAK,CAIjB;AAED;;;;;;GAMG;AACH,oCANW,UAAU,OACV,MAAM,UACN,MAAM,WACN,aAAa,GACX,KAAK,CAIjB;AAED;;;;;;GAMG;AACH,qCANW,UAAU,OACV,MAAM,UACN,MAAM,WACN,aAAa,GACX,KAAK,CAIjB;AAED;;;;;;GAMG;AACH,qCANW,UAAU,OACV,MAAM,UACN,MAAM,WACN,aAAa,GACX,KAAK,CAIjB;AAGD;;;;;;GAMG;AACH,qCANW,UAAU,OACV,MAAM,UACN,MAAM,WACN,aAAa,GACX,KAAK,CAQjB;AAED,8CAAuC;yBA1G1B,OAAO,cAAc,EAAE,UAAU;4BACjC,OAAO,cAAc,EAAE,aAAa;sBAbrB,YAAY;4BAGZ,aAAa"}
|
|
@@ -15,12 +15,6 @@ export function compare(b1: Uint8Array, b2: Uint8Array): number;
|
|
|
15
15
|
*/
|
|
16
16
|
export function decodeCodePointsArray(codePoints: number[]): string;
|
|
17
17
|
export const useBuffer: boolean;
|
|
18
|
-
/**
|
|
19
|
-
* @param {Uint8Array} bytes
|
|
20
|
-
* @param {number} start
|
|
21
|
-
* @param {number} end
|
|
22
|
-
*/
|
|
23
|
-
export function toString(bytes: Uint8Array, start: number, end: number): string;
|
|
24
18
|
export const fromString: ((string: string) => number[] | Buffer<ArrayBuffer>) | ((string: string) => number[] | Uint8Array<ArrayBuffer>);
|
|
25
19
|
export function fromArray(arr: number[]): Uint8Array;
|
|
26
20
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"byte-utils.d.ts","sourceRoot":"","sources":["../../lib/byte-utils.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"byte-utils.d.ts","sourceRoot":"","sources":["../../lib/byte-utils.js"],"names":[],"mappings":"AAuBA;;;GAGG;AACH,2BAHW,UAAU,GAAC,MAAM,EAAE,GACjB,UAAU,CAQtB;AAoMD;;;;GAIG;AACH,4BAJW,UAAU,MACV,UAAU,GACR,MAAM,CAgBlB;AA6CD;;;GAGG;AACH,kDAHW,MAAM,EAAE,GACN,MAAM,CAkBlB;AArTD,gCAMkD;AAyBlD,mCAGe,MAAM,iDAYN,MAAM,yCAIhB;AAOE,+BAHI,MAAM,EAAE,GACN,UAAU,CAItB;AAIG;;;;GAIG;AAEH,6BALW,UAAU,SACV,MAAM,OACN,MAAM,2BAQhB;AAcD;;;;GAIG;AACH,+BAJW,UAAU,EAAE,UACZ,MAAM,GACJ,UAAU,CActB;AAwBD;;;GAGG;AACH,4BAHW,MAAM,GACJ,UAAU,CAMtB;AAaD;;;GAGG;AACH,yBAHW,UAAU,GACR,MAAM,CAQlB;AAiBH;;;GAGG;AACD,6BAHS,MAAM,GAAC,UAAU,GACf,UAAU,CAQpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decode.d.ts","sourceRoot":"","sources":["../../lib/decode.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"decode.d.ts","sourceRoot":"","sources":["../../lib/decode.js"],"names":[],"mappings":"oBAMa,OAAO,YAAY,EAAE,KAAK;4BAC1B,OAAO,cAAc,EAAE,aAAa;8BACpC,OAAO,cAAc,EAAE,eAAe;AAUnD;;GAEG;AACH,kCAFgB,eAAe;IAG7B;;;OAGG;IACH,kBAHW,UAAU,YACV,aAAa,EAMvB;IAHC,aAAa;IACb,kCAAgB;IAChB,8CAAsB;IAGxB,cAEC;IAED,gBAEC;IAED,mCAgBC;CACF;AA8ED;;;;GAIG;AACH,0CAJW,eAAe,WACf,aAAa,GACX,GAAG,6BAAW,CAoC1B;AAyBD;;;;GAIG;AACH,6BAJW,UAAU,YACV,aAAa,GACX,GAAG,CAQf;AAlCD;;;;GAIG;AACH,kCAJW,UAAU,YACV,aAAa,GACX,CAAC,GAAG,EAAE,UAAU,CAAC,CAkB7B;AAzID,mCAAiC;AADjC,kCAA+B"}
|