cborg 1.7.0 → 1.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.
Files changed (73) hide show
  1. package/README.md +16 -0
  2. package/cjs/browser-test/common.js +24 -0
  3. package/cjs/browser-test/node-test-bin.js +15 -0
  4. package/cjs/browser-test/test-6tag.js +5 -19
  5. package/cjs/browser-test/test-length.js +63 -0
  6. package/cjs/lib/3string.js +12 -8
  7. package/cjs/lib/4array.js +3 -0
  8. package/cjs/lib/5map.js +3 -0
  9. package/cjs/lib/6tag.js +3 -0
  10. package/cjs/lib/7float.js +1 -2
  11. package/cjs/lib/bl.js +3 -1
  12. package/cjs/lib/diagnostic.js +11 -4
  13. package/cjs/lib/encode.js +15 -9
  14. package/cjs/lib/length.js +36 -0
  15. package/cjs/lib/token.js +1 -0
  16. package/cjs/node-test/common.js +24 -0
  17. package/cjs/node-test/node-test-bin.js +15 -0
  18. package/cjs/node-test/test-6tag.js +5 -19
  19. package/cjs/node-test/test-length.js +63 -0
  20. package/esm/browser-test/common.js +19 -0
  21. package/esm/browser-test/node-test-bin.js +15 -0
  22. package/esm/browser-test/test-6tag.js +4 -15
  23. package/esm/browser-test/test-length.js +55 -0
  24. package/esm/lib/3string.js +16 -9
  25. package/esm/lib/4array.js +4 -1
  26. package/esm/lib/5map.js +4 -1
  27. package/esm/lib/6tag.js +4 -1
  28. package/esm/lib/7float.js +1 -2
  29. package/esm/lib/bl.js +3 -1
  30. package/esm/lib/diagnostic.js +11 -4
  31. package/esm/lib/encode.js +14 -9
  32. package/esm/lib/length.js +31 -0
  33. package/esm/lib/token.js +1 -0
  34. package/esm/node-test/common.js +19 -0
  35. package/esm/node-test/node-test-bin.js +15 -0
  36. package/esm/node-test/test-6tag.js +4 -15
  37. package/esm/node-test/test-length.js +55 -0
  38. package/interface.ts +2 -0
  39. package/length +1 -0
  40. package/lib/3string.js +15 -10
  41. package/lib/4array.js +8 -0
  42. package/lib/5map.js +8 -0
  43. package/lib/6tag.js +8 -0
  44. package/lib/7float.js +1 -2
  45. package/lib/bl.js +3 -1
  46. package/lib/diagnostic.js +13 -6
  47. package/lib/encode.js +16 -10
  48. package/lib/length.js +61 -0
  49. package/lib/token.js +2 -0
  50. package/package.json +11 -2
  51. package/test/common.js +18 -0
  52. package/test/node-test-bin.js +19 -0
  53. package/test/test-6tag.js +1 -17
  54. package/test/test-length.js +65 -0
  55. package/types/interface.d.ts +1 -0
  56. package/types/interface.d.ts.map +1 -1
  57. package/types/lib/3string.d.ts +2 -2
  58. package/types/lib/3string.d.ts.map +1 -1
  59. package/types/lib/4array.d.ts +5 -0
  60. package/types/lib/4array.d.ts.map +1 -1
  61. package/types/lib/5map.d.ts +5 -0
  62. package/types/lib/5map.d.ts.map +1 -1
  63. package/types/lib/6tag.d.ts +5 -0
  64. package/types/lib/6tag.d.ts.map +1 -1
  65. package/types/lib/7float.d.ts.map +1 -1
  66. package/types/lib/bl.d.ts.map +1 -1
  67. package/types/lib/diagnostic.d.ts.map +1 -1
  68. package/types/lib/encode.d.ts +2 -0
  69. package/types/lib/encode.d.ts.map +1 -1
  70. package/types/lib/length.d.ts +27 -0
  71. package/types/lib/length.d.ts.map +1 -0
  72. package/types/lib/token.d.ts +2 -0
  73. package/types/lib/token.d.ts.map +1 -1
@@ -0,0 +1,55 @@
1
+ import chai from 'chai';
2
+ import { garbage } from 'ipld-garbage';
3
+ import { uintBoundaries } from '../lib/0uint.js';
4
+ import { encode } from '../cborg.js';
5
+ import { encodedLength } from '../lib/length.js';
6
+ import { dateEncoder } from './common.js';
7
+ const {assert} = chai;
8
+ function verifyLength(object, options) {
9
+ const len = encodedLength(object, options);
10
+ const encoded = encode(object, options);
11
+ const actual = encoded.length;
12
+ assert.strictEqual(actual, len, JSON.stringify(object));
13
+ }
14
+ describe('encodedLength', () => {
15
+ it('int boundaries', () => {
16
+ for (let ii = 0; ii < 4; ii++) {
17
+ verifyLength(uintBoundaries[ii]);
18
+ verifyLength(uintBoundaries[ii] - 1);
19
+ verifyLength(uintBoundaries[ii] + 1);
20
+ verifyLength(-1 * uintBoundaries[ii]);
21
+ verifyLength(-1 * uintBoundaries[ii] - 1);
22
+ verifyLength(-1 * uintBoundaries[ii] + 1);
23
+ }
24
+ });
25
+ it('tags', () => {
26
+ verifyLength({ date: new Date('2013-03-21T20:04:00Z') }, { typeEncoders: { Date: dateEncoder } });
27
+ });
28
+ it('floats', () => {
29
+ verifyLength(0.5);
30
+ verifyLength(0.5, { float64: true });
31
+ verifyLength(8.940696716308594e-8);
32
+ verifyLength(8.940696716308594e-8, { float64: true });
33
+ });
34
+ it('small garbage', function () {
35
+ this.timeout(10000);
36
+ for (let ii = 0; ii < 1000; ii++) {
37
+ const gbg = garbage(1 << 6, { weights: { CID: 0 } });
38
+ verifyLength(gbg);
39
+ }
40
+ });
41
+ it('medium garbage', function () {
42
+ this.timeout(10000);
43
+ for (let ii = 0; ii < 100; ii++) {
44
+ const gbg = garbage(1 << 16, { weights: { CID: 0 } });
45
+ verifyLength(gbg);
46
+ }
47
+ });
48
+ it('large garbage', function () {
49
+ this.timeout(10000);
50
+ for (let ii = 0; ii < 10; ii++) {
51
+ const gbg = garbage(1 << 20, { weights: { CID: 0 } });
52
+ verifyLength(gbg);
53
+ }
54
+ });
55
+ });
@@ -8,29 +8,36 @@ import {
8
8
  } from './common.js';
9
9
  import * as uint from './0uint.js';
10
10
  import { encodeBytes } from './2bytes.js';
11
- import { toString } from './byte-utils.js';
12
- function toToken(data, pos, prefix, length) {
11
+ import {
12
+ toString,
13
+ slice
14
+ } from './byte-utils.js';
15
+ function toToken(data, pos, prefix, length, options) {
13
16
  const totLength = prefix + length;
14
17
  assertEnoughData(data, pos, totLength);
15
- return new Token(Type.string, toString(data, pos + prefix, pos + totLength), totLength);
18
+ const tok = new Token(Type.string, toString(data, pos + prefix, pos + totLength), totLength);
19
+ if (options.retainStringBytes === true) {
20
+ tok.byteValue = slice(data, pos + prefix, pos + totLength);
21
+ }
22
+ return tok;
16
23
  }
17
- export function decodeStringCompact(data, pos, minor, _options) {
18
- return toToken(data, pos, 1, minor);
24
+ export function decodeStringCompact(data, pos, minor, options) {
25
+ return toToken(data, pos, 1, minor, options);
19
26
  }
20
27
  export function decodeString8(data, pos, _minor, options) {
21
- return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options));
28
+ return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options), options);
22
29
  }
23
30
  export function decodeString16(data, pos, _minor, options) {
24
- return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options));
31
+ return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options), options);
25
32
  }
26
33
  export function decodeString32(data, pos, _minor, options) {
27
- return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options));
34
+ return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options), options);
28
35
  }
29
36
  export function decodeString64(data, pos, _minor, options) {
30
37
  const l = uint.readUint64(data, pos + 1, options);
31
38
  if (typeof l === 'bigint') {
32
39
  throw new Error(`${ decodeErrPrefix } 64-bit integer string lengths not supported`);
33
40
  }
34
- return toToken(data, pos, 9, l);
41
+ return toToken(data, pos, 9, l, options);
35
42
  }
36
43
  export const encodeString = encodeBytes;
package/esm/lib/4array.js CHANGED
@@ -35,4 +35,7 @@ export function decodeArrayIndefinite(data, pos, _minor, options) {
35
35
  export function encodeArray(buf, token) {
36
36
  uint.encodeUintValue(buf, Type.array.majorEncoded, token.value);
37
37
  }
38
- encodeArray.compareTokens = uint.encodeUint.compareTokens;
38
+ encodeArray.compareTokens = uint.encodeUint.compareTokens;
39
+ encodeArray.encodedSize = function encodedSize(token) {
40
+ return uint.encodeUintValue.encodedSize(token.value);
41
+ };
package/esm/lib/5map.js CHANGED
@@ -35,4 +35,7 @@ export function decodeMapIndefinite(data, pos, _minor, options) {
35
35
  export function encodeMap(buf, token) {
36
36
  uint.encodeUintValue(buf, Type.map.majorEncoded, token.value);
37
37
  }
38
- encodeMap.compareTokens = uint.encodeUint.compareTokens;
38
+ encodeMap.compareTokens = uint.encodeUint.compareTokens;
39
+ encodeMap.encodedSize = function encodedSize(token) {
40
+ return uint.encodeUintValue.encodedSize(token.value);
41
+ };
package/esm/lib/6tag.js CHANGED
@@ -21,4 +21,7 @@ export function decodeTag64(data, pos, _minor, options) {
21
21
  export function encodeTag(buf, token) {
22
22
  uint.encodeUintValue(buf, Type.tag.majorEncoded, token.value);
23
23
  }
24
- encodeTag.compareTokens = uint.encodeUint.compareTokens;
24
+ encodeTag.compareTokens = uint.encodeUint.compareTokens;
25
+ encodeTag.encodedSize = function encodedSize(token) {
26
+ return uint.encodeUintValue.encodedSize(token.value);
27
+ };
package/esm/lib/7float.js CHANGED
@@ -85,10 +85,9 @@ encodeFloat.encodedSize = function encodedSize(token, options) {
85
85
  if (float === false || float === true || float === null || float === undefined) {
86
86
  return 1;
87
87
  }
88
- let decoded;
89
88
  if (!options || options.float64 !== true) {
90
89
  encodeFloat16(float);
91
- decoded = readFloat16(ui8a, 1);
90
+ let decoded = readFloat16(ui8a, 1);
92
91
  if (float === decoded || Number.isNaN(float)) {
93
92
  return 3;
94
93
  }
package/esm/lib/bl.js CHANGED
@@ -13,9 +13,11 @@ export class Bl {
13
13
  this._initReuseChunk = null;
14
14
  }
15
15
  reset() {
16
- this.chunks = [];
17
16
  this.cursor = 0;
18
17
  this.maxCursor = -1;
18
+ if (this.chunks.length) {
19
+ this.chunks = [];
20
+ }
19
21
  if (this._initReuseChunk !== null) {
20
22
  this.chunks.push(this._initReuseChunk);
21
23
  this.maxCursor = this._initReuseChunk.length - 1;
@@ -7,7 +7,7 @@ import { uintBoundaries } from './0uint.js';
7
7
  const utf8Encoder = new TextEncoder();
8
8
  const utf8Decoder = new TextDecoder();
9
9
  function* tokensToDiagnostic(inp, width = 100) {
10
- const tokeniser = new Tokeniser(inp);
10
+ const tokeniser = new Tokeniser(inp, { retainStringBytes: true });
11
11
  let pos = 0;
12
12
  const indent = [];
13
13
  const slc = (start, length) => {
@@ -57,15 +57,22 @@ function* tokensToDiagnostic(inp, width = 100) {
57
57
  }
58
58
  yield outp;
59
59
  if (str) {
60
+ let asString = token.type.name === 'string';
60
61
  margin += ' ';
61
- const repr = token.type.name === 'bytes' ? token.value : utf8Encoder.encode(token.value);
62
+ let repr = asString ? utf8Encoder.encode(token.value) : token.value;
63
+ if (asString && token.byteValue !== undefined) {
64
+ if (repr.length !== token.byteValue.length) {
65
+ repr = token.byteValue;
66
+ asString = false;
67
+ }
68
+ }
62
69
  const wh = (width / 2 - margin.length - 1) / 2;
63
70
  let snip = 0;
64
71
  while (repr.length - snip > 0) {
65
72
  const piece = repr.slice(snip, snip + wh);
66
73
  snip += piece.length;
67
- const st = token.type.name === 'string' ? utf8Decoder.decode(piece) : piece.reduce((p, c) => {
68
- if (c < 32 || c === 127) {
74
+ const st = asString ? utf8Decoder.decode(piece) : piece.reduce((p, c) => {
75
+ if (c < 32 || c >= 127 && c < 161 || c === 173) {
69
76
  return `${ p }\\x${ c.toString(16).padStart(2, '0') }`;
70
77
  }
71
78
  return `${ p }${ String.fromCharCode(c) }`;
package/esm/lib/encode.js CHANGED
@@ -20,15 +20,19 @@ const defaultEncodeOptions = {
20
20
  mapSorter,
21
21
  quickEncodeToken
22
22
  };
23
- const cborEncoders = [];
24
- cborEncoders[Type.uint.major] = encodeUint;
25
- cborEncoders[Type.negint.major] = encodeNegint;
26
- cborEncoders[Type.bytes.major] = encodeBytes;
27
- cborEncoders[Type.string.major] = encodeString;
28
- cborEncoders[Type.array.major] = encodeArray;
29
- cborEncoders[Type.map.major] = encodeMap;
30
- cborEncoders[Type.tag.major] = encodeTag;
31
- cborEncoders[Type.float.major] = encodeFloat;
23
+ export function makeCborEncoders() {
24
+ const encoders = [];
25
+ encoders[Type.uint.major] = encodeUint;
26
+ encoders[Type.negint.major] = encodeNegint;
27
+ encoders[Type.bytes.major] = encodeBytes;
28
+ encoders[Type.string.major] = encodeString;
29
+ encoders[Type.array.major] = encodeArray;
30
+ encoders[Type.map.major] = encodeMap;
31
+ encoders[Type.tag.major] = encodeTag;
32
+ encoders[Type.float.major] = encodeFloat;
33
+ return encoders;
34
+ }
35
+ const cborEncoders = makeCborEncoders();
32
36
  const buf = new Bl();
33
37
  class Ref {
34
38
  constructor(obj, parent) {
@@ -226,6 +230,7 @@ function encodeCustom(data, encoders, options) {
226
230
  return asU8A(buf.chunks[0]);
227
231
  }
228
232
  }
233
+ buf.reset();
229
234
  tokensToEncoded(buf, tokens, encoders, options);
230
235
  return buf.toBytes(true);
231
236
  }
@@ -0,0 +1,31 @@
1
+ import {
2
+ makeCborEncoders,
3
+ objectToTokens
4
+ } from './encode.js';
5
+ import { quickEncodeToken } from './jump.js';
6
+ const cborEncoders = makeCborEncoders();
7
+ const defaultEncodeOptions = {
8
+ float64: false,
9
+ quickEncodeToken
10
+ };
11
+ export function encodedLength(data, options) {
12
+ options = Object.assign({}, defaultEncodeOptions, options);
13
+ options.mapSorter = undefined;
14
+ const tokens = objectToTokens(data, options);
15
+ return tokensToLength(tokens, cborEncoders, options);
16
+ }
17
+ export function tokensToLength(tokens, encoders = cborEncoders, options = defaultEncodeOptions) {
18
+ if (Array.isArray(tokens)) {
19
+ let len = 0;
20
+ for (const token of tokens) {
21
+ len += tokensToLength(token, encoders, options);
22
+ }
23
+ return len;
24
+ } else {
25
+ const encoder = encoders[tokens.type.major];
26
+ if (encoder.encodedSize === undefined || typeof encoder.encodedSize !== 'function') {
27
+ throw new Error(`Encoder for ${ tokens.type.name } does not have an encodedSize()`);
28
+ }
29
+ return encoder.encodedSize(tokens, options);
30
+ }
31
+ }
package/esm/lib/token.js CHANGED
@@ -31,6 +31,7 @@ class Token {
31
31
  this.value = value;
32
32
  this.encodedLength = encodedLength;
33
33
  this.encodedBytes = undefined;
34
+ this.byteValue = undefined;
34
35
  }
35
36
  toString() {
36
37
  return `Token[${ this.type }].${ this.value }`;
@@ -0,0 +1,19 @@
1
+ import {
2
+ Token,
3
+ Type
4
+ } from '../lib/token.js';
5
+ export function dateDecoder(obj) {
6
+ if (typeof obj !== 'string') {
7
+ throw new Error('expected string for tag 1');
8
+ }
9
+ return new Date(obj);
10
+ }
11
+ export function dateEncoder(obj) {
12
+ if (!(obj instanceof Date)) {
13
+ throw new Error('expected Date for "Date" encoder');
14
+ }
15
+ return [
16
+ new Token(Type.tag, 0),
17
+ new Token(Type.string, obj.toISOString().replace(/\.000Z$/, 'Z'))
18
+ ];
19
+ }
@@ -322,4 +322,19 @@ Input may either be supplied as an argument or piped via stdin
322
322
  `);
323
323
  });
324
324
  });
325
+ it('diag non-utf8 and non-printable ascii', async () => {
326
+ const input = '7864f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c11e756338bd93865e645f1adec9b9c99ef407fbd4fc6859e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82f9f18c3d03418e35';
327
+ let {stdout, stderr} = await execBin(`hex2diag ${ input }`);
328
+ assert.strictEqual(stderr, '');
329
+ assert.strictEqual(stdout, `78 64 # string(86)
330
+ f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b # "õ_øñ%\\x08¶>ò¿ì§Uzé\\x0dö1\\x1a^Ác\\x1b"
331
+ 4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c # "J\\x1f¨C1\\x0bÙç\\x10ê¬å¡½×*пàIw\\x1c"
332
+ 11e756338bd93865e645f1adec9b9c99ef407fbd4fc685 # "\\x11çV3\\x8bÙ8eæEñ\\xadì\\x9b\\x9c\\x99ï@\\x7f½OÆ\\x85"
333
+ 9e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82 # "\\x9ey\\x04Å\\xad}ɽ\\x10¥Ì\\x16\\x97=[(ì\\x1amÔ=\\x9f\\x82"
334
+ f9f18c3d03418e35 # "ùñ\\x8c=\\x03A\\x8e5"
335
+ `);
336
+ ({stdout, stderr} = await execBin('diag2hex', stdout));
337
+ assert.strictEqual(stderr, '');
338
+ assert.strictEqual(stdout, `${ input }\n`);
339
+ });
325
340
  });
@@ -11,22 +11,11 @@ import {
11
11
  fromHex,
12
12
  toHex
13
13
  } from '../lib/byte-utils.js';
14
+ import {
15
+ dateDecoder,
16
+ dateEncoder
17
+ } from './common.js';
14
18
  const {assert} = chai;
15
- function dateDecoder(obj) {
16
- if (typeof obj !== 'string') {
17
- throw new Error('expected string for tag 1');
18
- }
19
- return new Date(obj);
20
- }
21
- function dateEncoder(obj) {
22
- if (!(obj instanceof Date)) {
23
- throw new Error('expected Date for "Date" encoder');
24
- }
25
- return [
26
- new Token(Type.tag, 0),
27
- new Token(Type.string, obj.toISOString().replace(/\.000Z$/, 'Z'))
28
- ];
29
- }
30
19
  function Uint16ArrayDecoder(obj) {
31
20
  if (typeof obj !== 'string') {
32
21
  throw new Error('expected string for tag 23');
@@ -0,0 +1,55 @@
1
+ import chai from 'chai';
2
+ import { garbage } from 'ipld-garbage';
3
+ import { uintBoundaries } from '../lib/0uint.js';
4
+ import { encode } from '../cborg.js';
5
+ import { encodedLength } from '../lib/length.js';
6
+ import { dateEncoder } from './common.js';
7
+ const {assert} = chai;
8
+ function verifyLength(object, options) {
9
+ const len = encodedLength(object, options);
10
+ const encoded = encode(object, options);
11
+ const actual = encoded.length;
12
+ assert.strictEqual(actual, len, JSON.stringify(object));
13
+ }
14
+ describe('encodedLength', () => {
15
+ it('int boundaries', () => {
16
+ for (let ii = 0; ii < 4; ii++) {
17
+ verifyLength(uintBoundaries[ii]);
18
+ verifyLength(uintBoundaries[ii] - 1);
19
+ verifyLength(uintBoundaries[ii] + 1);
20
+ verifyLength(-1 * uintBoundaries[ii]);
21
+ verifyLength(-1 * uintBoundaries[ii] - 1);
22
+ verifyLength(-1 * uintBoundaries[ii] + 1);
23
+ }
24
+ });
25
+ it('tags', () => {
26
+ verifyLength({ date: new Date('2013-03-21T20:04:00Z') }, { typeEncoders: { Date: dateEncoder } });
27
+ });
28
+ it('floats', () => {
29
+ verifyLength(0.5);
30
+ verifyLength(0.5, { float64: true });
31
+ verifyLength(8.940696716308594e-8);
32
+ verifyLength(8.940696716308594e-8, { float64: true });
33
+ });
34
+ it('small garbage', function () {
35
+ this.timeout(10000);
36
+ for (let ii = 0; ii < 1000; ii++) {
37
+ const gbg = garbage(1 << 6, { weights: { CID: 0 } });
38
+ verifyLength(gbg);
39
+ }
40
+ });
41
+ it('medium garbage', function () {
42
+ this.timeout(10000);
43
+ for (let ii = 0; ii < 100; ii++) {
44
+ const gbg = garbage(1 << 16, { weights: { CID: 0 } });
45
+ verifyLength(gbg);
46
+ }
47
+ });
48
+ it('large garbage', function () {
49
+ this.timeout(10000);
50
+ for (let ii = 0; ii < 10; ii++) {
51
+ const gbg = garbage(1 << 20, { weights: { CID: 0 } });
52
+ verifyLength(gbg);
53
+ }
54
+ });
55
+ });
package/interface.ts CHANGED
@@ -16,6 +16,7 @@ export type StrictTypeEncoder = (data: any, typ: string, options: EncodeOptions,
16
16
  export type TokenTypeEncoder = {
17
17
  (buf: Bl, token: Token, options?: EncodeOptions): void;
18
18
  compareTokens(t1: Token, t2: Token): number;
19
+ // TODO: make this non-optional as a breaking change and remove the throw in length.js
19
20
  encodedSize?(token: Token, options?: EncodeOptions): number;
20
21
  }
21
22
 
@@ -39,6 +40,7 @@ export interface DecodeOptions {
39
40
  allowBigInt?: boolean
40
41
  strict?: boolean
41
42
  useMaps?: boolean
43
+ retainStringBytes?: boolean
42
44
  tags?: TagDecoder[],
43
45
  tokenizer?: DecodeTokenizer
44
46
  }
package/length ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./cjs/lib/length.js')
package/lib/3string.js CHANGED
@@ -2,7 +2,7 @@ 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
- import { toString } from './byte-utils.js'
5
+ import { toString, slice } from './byte-utils.js'
6
6
 
7
7
  /**
8
8
  * @typedef {import('./bl.js').Bl} Bl
@@ -14,23 +14,28 @@ import { toString } from './byte-utils.js'
14
14
  * @param {number} pos
15
15
  * @param {number} prefix
16
16
  * @param {number} length
17
+ * @param {DecodeOptions} options
17
18
  * @returns {Token}
18
19
  */
19
- function toToken (data, pos, prefix, length) {
20
+ function toToken (data, pos, prefix, length, options) {
20
21
  const totLength = prefix + length
21
22
  assertEnoughData(data, pos, totLength)
22
- return new Token(Type.string, toString(data, pos + prefix, pos + totLength), totLength)
23
+ const tok = new Token(Type.string, toString(data, pos + prefix, pos + totLength), totLength)
24
+ if (options.retainStringBytes === true) {
25
+ tok.byteValue = slice(data, pos + prefix, pos + totLength)
26
+ }
27
+ return tok
23
28
  }
24
29
 
25
30
  /**
26
31
  * @param {Uint8Array} data
27
32
  * @param {number} pos
28
33
  * @param {number} minor
29
- * @param {DecodeOptions} _options
34
+ * @param {DecodeOptions} options
30
35
  * @returns {Token}
31
36
  */
32
- export function decodeStringCompact (data, pos, minor, _options) {
33
- return toToken(data, pos, 1, minor)
37
+ export function decodeStringCompact (data, pos, minor, options) {
38
+ return toToken(data, pos, 1, minor, options)
34
39
  }
35
40
 
36
41
  /**
@@ -41,7 +46,7 @@ export function decodeStringCompact (data, pos, minor, _options) {
41
46
  * @returns {Token}
42
47
  */
43
48
  export function decodeString8 (data, pos, _minor, options) {
44
- return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options))
49
+ return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options), options)
45
50
  }
46
51
 
47
52
  /**
@@ -52,7 +57,7 @@ export function decodeString8 (data, pos, _minor, options) {
52
57
  * @returns {Token}
53
58
  */
54
59
  export function decodeString16 (data, pos, _minor, options) {
55
- return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options))
60
+ return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options), options)
56
61
  }
57
62
 
58
63
  /**
@@ -63,7 +68,7 @@ export function decodeString16 (data, pos, _minor, options) {
63
68
  * @returns {Token}
64
69
  */
65
70
  export function decodeString32 (data, pos, _minor, options) {
66
- return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options))
71
+ return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options), options)
67
72
  }
68
73
 
69
74
  // TODO: maybe we shouldn't support this ..
@@ -79,7 +84,7 @@ export function decodeString64 (data, pos, _minor, options) {
79
84
  if (typeof l === 'bigint') {
80
85
  throw new Error(`${decodeErrPrefix} 64-bit integer string lengths not supported`)
81
86
  }
82
- return toToken(data, pos, 9, l)
87
+ return toToken(data, pos, 9, l, options)
83
88
  }
84
89
 
85
90
  export const encodeString = encodeBytes
package/lib/4array.js CHANGED
@@ -103,3 +103,11 @@ export function encodeArray (buf, token) {
103
103
  // using an array as a map key, are you sure about this? we can only sort
104
104
  // by map length here, it's up to the encoder to decide to look deeper
105
105
  encodeArray.compareTokens = uint.encodeUint.compareTokens
106
+
107
+ /**
108
+ * @param {Token} token
109
+ * @returns {number}
110
+ */
111
+ encodeArray.encodedSize = function encodedSize (token) {
112
+ return uint.encodeUintValue.encodedSize(token.value)
113
+ }
package/lib/5map.js CHANGED
@@ -103,3 +103,11 @@ export function encodeMap (buf, token) {
103
103
  // using a map as a map key, are you sure about this? we can only sort
104
104
  // by map length here, it's up to the encoder to decide to look deeper
105
105
  encodeMap.compareTokens = uint.encodeUint.compareTokens
106
+
107
+ /**
108
+ * @param {Token} token
109
+ * @returns {number}
110
+ */
111
+ encodeMap.encodedSize = function encodedSize (token) {
112
+ return uint.encodeUintValue.encodedSize(token.value)
113
+ }
package/lib/6tag.js CHANGED
@@ -70,3 +70,11 @@ export function encodeTag (buf, token) {
70
70
  }
71
71
 
72
72
  encodeTag.compareTokens = uint.encodeUint.compareTokens
73
+
74
+ /**
75
+ * @param {Token} token
76
+ * @returns {number}
77
+ */
78
+ encodeTag.encodedSize = function encodedSize (token) {
79
+ return uint.encodeUintValue.encodedSize(token.value)
80
+ }
package/lib/7float.js CHANGED
@@ -154,10 +154,9 @@ encodeFloat.encodedSize = function encodedSize (token, options) {
154
154
  return 1
155
155
  }
156
156
 
157
- let decoded
158
157
  if (!options || options.float64 !== true) {
159
158
  encodeFloat16(float)
160
- decoded = readFloat16(ui8a, 1)
159
+ let decoded = readFloat16(ui8a, 1)
161
160
  if (float === decoded || Number.isNaN(float)) {
162
161
  return 3
163
162
  }
package/lib/bl.js CHANGED
@@ -42,9 +42,11 @@ export class Bl {
42
42
  }
43
43
 
44
44
  reset () {
45
- this.chunks = []
46
45
  this.cursor = 0
47
46
  this.maxCursor = -1
47
+ if (this.chunks.length) {
48
+ this.chunks = []
49
+ }
48
50
  if (this._initReuseChunk !== null) {
49
51
  this.chunks.push(this._initReuseChunk)
50
52
  this.maxCursor = this._initReuseChunk.length - 1
package/lib/diagnostic.js CHANGED
@@ -10,7 +10,7 @@ const utf8Decoder = new TextDecoder()
10
10
  * @param {number} [width]
11
11
  */
12
12
  function * tokensToDiagnostic (inp, width = 100) {
13
- const tokeniser = new Tokeniser(inp)
13
+ const tokeniser = new Tokeniser(inp, { retainStringBytes: true })
14
14
  let pos = 0
15
15
  const indent = []
16
16
 
@@ -77,19 +77,26 @@ function * tokensToDiagnostic (inp, width = 100) {
77
77
  yield outp
78
78
 
79
79
  if (str) {
80
+ let asString = token.type.name === 'string'
80
81
  margin += ' '
81
- const repr = token.type.name === 'bytes' ? token.value : utf8Encoder.encode(token.value)
82
+ let repr = asString ? utf8Encoder.encode(token.value) : token.value
83
+ if (asString && token.byteValue !== undefined) {
84
+ if (repr.length !== token.byteValue.length) {
85
+ // bail on printing this as a string, it's probably not utf8, so treat it as bytes
86
+ // (you can probably blame a Go programmer for this)
87
+ repr = token.byteValue
88
+ asString = false
89
+ }
90
+ }
82
91
  const wh = ((width / 2) - margin.length - 1) / 2
83
92
  let snip = 0
84
93
  while (repr.length - snip > 0) {
85
94
  const piece = repr.slice(snip, snip + wh)
86
95
  snip += piece.length
87
- // the assumption that we can utf8 a byte-sliced version is a stretch,
88
- // we could be slicing in the middle of a multi-byte character
89
- const st = token.type.name === 'string'
96
+ const st = asString
90
97
  ? utf8Decoder.decode(piece)
91
98
  : piece.reduce((/** @type {string} */ p, /** @type {number} */ c) => {
92
- if (c < 0x20 || c === 0x7f) {
99
+ if (c < 0x20 || (c >= 0x7f && c < 0xa1) || c === 0xad) {
93
100
  return `${p}\\x${c.toString(16).padStart(2, '0')}`
94
101
  }
95
102
  return `${p}${String.fromCharCode(c)}`
package/lib/encode.js CHANGED
@@ -30,16 +30,21 @@ const defaultEncodeOptions = {
30
30
  quickEncodeToken
31
31
  }
32
32
 
33
- /** @type {TokenTypeEncoder[]} */
34
- const cborEncoders = []
35
- cborEncoders[Type.uint.major] = encodeUint
36
- cborEncoders[Type.negint.major] = encodeNegint
37
- cborEncoders[Type.bytes.major] = encodeBytes
38
- cborEncoders[Type.string.major] = encodeString
39
- cborEncoders[Type.array.major] = encodeArray
40
- cborEncoders[Type.map.major] = encodeMap
41
- cborEncoders[Type.tag.major] = encodeTag
42
- cborEncoders[Type.float.major] = encodeFloat
33
+ /** @returns {TokenTypeEncoder[]} */
34
+ export function makeCborEncoders () {
35
+ const encoders = []
36
+ encoders[Type.uint.major] = encodeUint
37
+ encoders[Type.negint.major] = encodeNegint
38
+ encoders[Type.bytes.major] = encodeBytes
39
+ encoders[Type.string.major] = encodeString
40
+ encoders[Type.array.major] = encodeArray
41
+ encoders[Type.map.major] = encodeMap
42
+ encoders[Type.tag.major] = encodeTag
43
+ encoders[Type.float.major] = encodeFloat
44
+ return encoders
45
+ }
46
+
47
+ const cborEncoders = makeCborEncoders()
43
48
 
44
49
  const buf = new Bl()
45
50
 
@@ -441,6 +446,7 @@ function encodeCustom (data, encoders, options) {
441
446
  return asU8A(buf.chunks[0])
442
447
  }
443
448
  }
449
+ buf.reset()
444
450
  tokensToEncoded(buf, tokens, encoders, options)
445
451
  return buf.toBytes(true)
446
452
  }