microcbor 0.3.0 → 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.
@@ -1,4 +1,9 @@
1
- import { getFloat16Precision, getFloat32Precision, Precision } from "fp16";
1
+ import { Precision, getFloat16Precision, getFloat32Precision } from "fp16";
2
+ import { getByteLength } from "./utils.js";
3
+ /**
4
+ * Calculate the byte length that a value will encode into
5
+ * without actually allocating anything.
6
+ */
2
7
  export function encodingLength(value) {
3
8
  if (value === false) {
4
9
  return 1;
@@ -70,9 +75,7 @@ function numberEncodingLength(value) {
70
75
  else if (Object.is(value, -0)) {
71
76
  return floatEncodingLength(value);
72
77
  }
73
- else if (Math.floor(value) === value &&
74
- Number.MIN_SAFE_INTEGER <= value &&
75
- value <= Number.MAX_SAFE_INTEGER) {
78
+ else if (Math.floor(value) === value && Number.MIN_SAFE_INTEGER <= value && value <= Number.MAX_SAFE_INTEGER) {
76
79
  return integerEncodingLength(value);
77
80
  }
78
81
  else {
@@ -99,71 +102,10 @@ function floatEncodingLength(value) {
99
102
  }
100
103
  }
101
104
  function stringEncodingLength(value) {
102
- const length = byteLength(value);
105
+ const length = getByteLength(value);
103
106
  return argumentEncodingLength(length) + length;
104
107
  }
105
108
  function bytesEncodingLength(value) {
106
109
  const length = value.byteLength;
107
110
  return argumentEncodingLength(length) + length;
108
111
  }
109
- // https://github.com/feross/buffer/blob/57caad4450d241207066ca3832fb8e9095ad402f/index.js#L434
110
- function byteLength(string) {
111
- let codePoint;
112
- const length = string.length;
113
- let leadSurrogate = null;
114
- let bytes = 0;
115
- for (let i = 0; i < length; ++i) {
116
- codePoint = string.charCodeAt(i);
117
- // is surrogate component
118
- if (codePoint > 0xd7ff && codePoint < 0xe000) {
119
- // last char was a lead
120
- if (!leadSurrogate) {
121
- // no lead yet
122
- if (codePoint > 0xdbff) {
123
- // unexpected trail
124
- bytes += 3;
125
- continue;
126
- }
127
- else if (i + 1 === length) {
128
- // unpaired lead
129
- bytes += 3;
130
- continue;
131
- }
132
- // valid lead
133
- leadSurrogate = codePoint;
134
- continue;
135
- }
136
- // 2 leads in a row
137
- if (codePoint < 0xdc00) {
138
- bytes += 3;
139
- leadSurrogate = codePoint;
140
- continue;
141
- }
142
- // valid surrogate pair
143
- codePoint =
144
- (((leadSurrogate - 0xd800) << 10) | (codePoint - 0xdc00)) + 0x10000;
145
- }
146
- else if (leadSurrogate) {
147
- // valid bmp char, but last char was a lead
148
- bytes += 3;
149
- }
150
- leadSurrogate = null;
151
- // encode utf8
152
- if (codePoint < 0x80) {
153
- bytes += 1;
154
- }
155
- else if (codePoint < 0x800) {
156
- bytes += 2;
157
- }
158
- else if (codePoint < 0x10000) {
159
- bytes += 3;
160
- }
161
- else if (codePoint < 0x110000) {
162
- bytes += 4;
163
- }
164
- else {
165
- throw new Error("Invalid code point");
166
- }
167
- }
168
- return bytes;
169
- }
package/lib/index.d.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  export type { CBORValue, CBORMap, CBORArray } from "./types.js";
2
- export { encode, Encoder } from "./encode.js";
3
- export { decode, Decoder } from "./decode.js";
4
- export { encodeStream } from "./encodeStream.js";
5
- export { decodeStream } from "./decodeStream.js";
6
2
  export { encodingLength } from "./encodingLength.js";
3
+ export { Encoder, encode } from "./Encoder.js";
4
+ export { Decoder, decode } from "./Decoder.js";
5
+ export { encodeIterable } from "./encodeIterable.js";
6
+ export { decodeIterable } from "./decodeIterable.js";
7
+ export { encodeAsyncIterable } from "./encodeAsyncIterable.js";
8
+ export { decodeAsyncIterable } from "./decodeAsyncIterable.js";
9
+ export { CBORDecoderStream } from "./CBORDecoderStream.js";
10
+ export { CBOREncoderStream } from "./CBOREncoderStream.js";
7
11
  export { UnsafeIntegerError } from "./utils.js";
package/lib/index.js CHANGED
@@ -1,6 +1,10 @@
1
- export { encode, Encoder } from "./encode.js";
2
- export { decode, Decoder } from "./decode.js";
3
- export { encodeStream } from "./encodeStream.js";
4
- export { decodeStream } from "./decodeStream.js";
5
1
  export { encodingLength } from "./encodingLength.js";
2
+ export { Encoder, encode } from "./Encoder.js";
3
+ export { Decoder, decode } from "./Decoder.js";
4
+ export { encodeIterable } from "./encodeIterable.js";
5
+ export { decodeIterable } from "./decodeIterable.js";
6
+ export { encodeAsyncIterable } from "./encodeAsyncIterable.js";
7
+ export { decodeAsyncIterable } from "./decodeAsyncIterable.js";
8
+ export { CBORDecoderStream } from "./CBORDecoderStream.js";
9
+ export { CBOREncoderStream } from "./CBOREncoderStream.js";
6
10
  export { UnsafeIntegerError } from "./utils.js";
package/lib/utils.d.ts CHANGED
@@ -4,3 +4,10 @@ export declare class UnsafeIntegerError extends RangeError {
4
4
  readonly value: bigint;
5
5
  constructor(message: string, value: bigint);
6
6
  }
7
+ export declare class AssertError extends Error {
8
+ readonly message: string;
9
+ readonly props?: any | undefined;
10
+ constructor(message: string, props?: any | undefined);
11
+ }
12
+ export declare function assert(condition: unknown, message?: string, props?: any): asserts condition;
13
+ export declare function getByteLength(string: string): number;
package/lib/utils.js CHANGED
@@ -6,3 +6,75 @@ export class UnsafeIntegerError extends RangeError {
6
6
  this.value = value;
7
7
  }
8
8
  }
9
+ export class AssertError extends Error {
10
+ constructor(message, props) {
11
+ super(message);
12
+ this.message = message;
13
+ this.props = props;
14
+ }
15
+ }
16
+ export function assert(condition, message = "assertion failed", props) {
17
+ if (!condition) {
18
+ throw new AssertError(message, props);
19
+ }
20
+ }
21
+ // https://github.com/feross/buffer/blob/57caad4450d241207066ca3832fb8e9095ad402f/index.js#L434
22
+ export function getByteLength(string) {
23
+ let codePoint;
24
+ const length = string.length;
25
+ let leadSurrogate = null;
26
+ let bytes = 0;
27
+ for (let i = 0; i < length; ++i) {
28
+ codePoint = string.charCodeAt(i);
29
+ // is surrogate component
30
+ if (codePoint > 0xd7ff && codePoint < 0xe000) {
31
+ // last char was a lead
32
+ if (!leadSurrogate) {
33
+ // no lead yet
34
+ if (codePoint > 0xdbff) {
35
+ // unexpected trail
36
+ bytes += 3;
37
+ continue;
38
+ }
39
+ else if (i + 1 === length) {
40
+ // unpaired lead
41
+ bytes += 3;
42
+ continue;
43
+ }
44
+ // valid lead
45
+ leadSurrogate = codePoint;
46
+ continue;
47
+ }
48
+ // 2 leads in a row
49
+ if (codePoint < 0xdc00) {
50
+ bytes += 3;
51
+ leadSurrogate = codePoint;
52
+ continue;
53
+ }
54
+ // valid surrogate pair
55
+ codePoint = (((leadSurrogate - 0xd800) << 10) | (codePoint - 0xdc00)) + 0x10000;
56
+ }
57
+ else if (leadSurrogate) {
58
+ // valid bmp char, but last char was a lead
59
+ bytes += 3;
60
+ }
61
+ leadSurrogate = null;
62
+ // encode utf8
63
+ if (codePoint < 0x80) {
64
+ bytes += 1;
65
+ }
66
+ else if (codePoint < 0x800) {
67
+ bytes += 2;
68
+ }
69
+ else if (codePoint < 0x10000) {
70
+ bytes += 3;
71
+ }
72
+ else if (codePoint < 0x110000) {
73
+ bytes += 4;
74
+ }
75
+ else {
76
+ throw new Error("Invalid code point");
77
+ }
78
+ }
79
+ return bytes;
80
+ }
package/package.json CHANGED
@@ -1,15 +1,20 @@
1
1
  {
2
2
  "name": "microcbor",
3
- "version": "0.3.0",
4
- "description": "Encode JavaScript values as canonical CBOR",
3
+ "version": "1.0.0",
5
4
  "type": "module",
6
- "main": "lib/index.js",
7
- "types": "lib/index.d.ts",
8
5
  "files": [
9
6
  "lib"
10
7
  ],
8
+ "description": "Encode JavaScript values as canonical CBOR",
9
+ "main": "lib/index.js",
10
+ "types": "./lib/index.d.ts",
11
+ "exports": {
12
+ ".": "./lib/index.js"
13
+ },
11
14
  "scripts": {
12
- "build": "tsc",
15
+ "dev": "tsc --build tsconfig.json test/tsconfig.json --watch",
16
+ "build": "tsc --build tsconfig.json test/tsconfig.json",
17
+ "clean": "tsc --build tsconfig.json test/tsconfig.json --clean",
13
18
  "test": "ava"
14
19
  },
15
20
  "repository": {
@@ -27,11 +32,13 @@
27
32
  },
28
33
  "homepage": "https://github.com/joeltg/microcbor#readme",
29
34
  "devDependencies": {
30
- "ava": "^5.3.1",
31
- "cbor": "^8.1.0",
32
- "typescript": "^5.1.6"
35
+ "@ava/typescript": "^5.0.0",
36
+ "@types/node": "^22.13.9",
37
+ "ava": "^6.2.0",
38
+ "cbor": "^10.0.3",
39
+ "typescript": "^5.6.0"
33
40
  },
34
41
  "dependencies": {
35
- "fp16": "^0.2.0"
42
+ "fp16": "^1.0.0"
36
43
  }
37
44
  }
@@ -1,2 +0,0 @@
1
- import type { CBORValue } from "./types.js";
2
- export declare function decodeStream(source: AsyncIterable<Uint8Array>): AsyncIterable<CBORValue>;
package/lib/encode.d.ts DELETED
@@ -1,35 +0,0 @@
1
- import type { CBORValue } from "./types.js";
2
- export declare class Encoder {
3
- static defaultChunkSize: number;
4
- closed: boolean;
5
- private buffer;
6
- private view;
7
- private offset;
8
- private readonly encoder;
9
- private readonly chunkSize;
10
- constructor(options?: {
11
- chunkSize?: number;
12
- noCopy?: boolean;
13
- });
14
- private allocate;
15
- private float16;
16
- private float32;
17
- private float64;
18
- private uint8;
19
- private uint16;
20
- private uint32;
21
- private uint64;
22
- private encodeTypeAndArgument;
23
- private encodeNumber;
24
- private encodeInteger;
25
- private encodeFloat;
26
- private encodeString;
27
- private encodeBytes;
28
- encodeValue(value: CBORValue): Iterable<Uint8Array>;
29
- flush(): Iterable<Uint8Array>;
30
- private static compareKeys;
31
- private static getAdditionalInformation;
32
- }
33
- export declare function encode(value: CBORValue, options?: {
34
- chunkSize?: number;
35
- }): Uint8Array;
package/lib/encode.js DELETED
@@ -1,228 +0,0 @@
1
- import { getFloat16Precision, getFloat32Precision, setFloat16, Precision, } from "fp16";
2
- export class Encoder {
3
- constructor(options = {}) {
4
- this.encoder = new TextEncoder();
5
- this.chunkSize = options.chunkSize || Encoder.defaultChunkSize;
6
- this.buffer = new ArrayBuffer(this.chunkSize);
7
- this.view = new DataView(this.buffer);
8
- this.offset = 0;
9
- this.closed = false;
10
- }
11
- *allocate(size) {
12
- if (this.buffer.byteLength < this.offset + size) {
13
- yield new Uint8Array(this.buffer, 0, this.offset);
14
- const byteLength = Math.max(size, this.chunkSize);
15
- this.buffer = new ArrayBuffer(byteLength);
16
- this.view = new DataView(this.buffer);
17
- this.offset = 0;
18
- }
19
- }
20
- *float16(value) {
21
- yield* this.allocate(2);
22
- setFloat16(this.view, this.offset, value);
23
- this.offset += 2;
24
- }
25
- *float32(value) {
26
- yield* this.allocate(4);
27
- this.view.setFloat32(this.offset, value);
28
- this.offset += 4;
29
- }
30
- *float64(value) {
31
- yield* this.allocate(8);
32
- this.view.setFloat64(this.offset, value);
33
- this.offset += 8;
34
- }
35
- *uint8(value) {
36
- yield* this.allocate(1);
37
- this.view.setUint8(this.offset, value);
38
- this.offset += 1;
39
- }
40
- *uint16(value) {
41
- yield* this.allocate(2);
42
- this.view.setUint16(this.offset, value);
43
- this.offset += 2;
44
- }
45
- *uint32(value) {
46
- yield* this.allocate(4);
47
- this.view.setUint32(this.offset, value);
48
- this.offset += 4;
49
- }
50
- *uint64(value) {
51
- yield* this.allocate(8);
52
- this.view.setBigUint64(this.offset, BigInt(value));
53
- this.offset += 8;
54
- }
55
- *encodeTypeAndArgument(type, argument) {
56
- const additionalInformation = Encoder.getAdditionalInformation(argument);
57
- yield* this.uint8((type << 5) | additionalInformation);
58
- switch (additionalInformation) {
59
- case 24:
60
- return yield* this.uint8(argument);
61
- case 25:
62
- return yield* this.uint16(argument);
63
- case 26:
64
- return yield* this.uint32(argument);
65
- case 27:
66
- return yield* this.uint64(argument);
67
- }
68
- }
69
- *encodeNumber(value) {
70
- if (Object.is(value, 0)) {
71
- yield* this.encodeInteger(value);
72
- }
73
- else if (Object.is(value, -0)) {
74
- yield* this.encodeFloat(value);
75
- }
76
- else if (Math.floor(value) === value &&
77
- Number.MIN_SAFE_INTEGER <= value &&
78
- value <= Number.MAX_SAFE_INTEGER) {
79
- yield* this.encodeInteger(value);
80
- }
81
- else {
82
- yield* this.encodeFloat(value);
83
- }
84
- }
85
- *encodeInteger(value) {
86
- if (value < 0) {
87
- yield* this.encodeTypeAndArgument(1, -value - 1);
88
- }
89
- else {
90
- yield* this.encodeTypeAndArgument(0, value);
91
- }
92
- }
93
- *encodeFloat(value) {
94
- if (getFloat16Precision(value) === Precision.Exact) {
95
- yield* this.uint8(0xe0 | 25);
96
- yield* this.float16(value);
97
- }
98
- else if (getFloat32Precision(value) === Precision.Exact) {
99
- yield* this.uint8(0xe0 | 26);
100
- yield* this.float32(value);
101
- }
102
- else {
103
- yield* this.uint8(0xe0 | 27);
104
- yield* this.float64(value);
105
- }
106
- }
107
- *encodeString(value) {
108
- const data = this.encoder.encode(value);
109
- yield* this.encodeTypeAndArgument(3, data.byteLength);
110
- yield* this.allocate(data.byteLength);
111
- new Uint8Array(this.buffer, this.offset).set(data);
112
- this.offset += data.byteLength;
113
- }
114
- *encodeBytes(value) {
115
- yield* this.encodeTypeAndArgument(2, value.byteLength);
116
- yield* this.allocate(value.byteLength);
117
- new Uint8Array(this.buffer, this.offset, value.byteLength).set(value);
118
- this.offset += value.byteLength;
119
- }
120
- *encodeValue(value) {
121
- if (this.closed) {
122
- return;
123
- }
124
- if (value === false) {
125
- yield* this.uint8(0xf4);
126
- }
127
- else if (value === true) {
128
- yield* this.uint8(0xf5);
129
- }
130
- else if (value === null) {
131
- yield* this.uint8(0xf6);
132
- }
133
- else if (value === undefined) {
134
- yield* this.uint8(0xf7);
135
- }
136
- else if (typeof value === "number") {
137
- yield* this.encodeNumber(value);
138
- }
139
- else if (typeof value === "string") {
140
- yield* this.encodeString(value);
141
- }
142
- else if (value instanceof Uint8Array) {
143
- yield* this.encodeBytes(value);
144
- }
145
- else if (Array.isArray(value)) {
146
- yield* this.encodeTypeAndArgument(4, value.length);
147
- for (const element of value) {
148
- yield* this.encodeValue(element);
149
- }
150
- }
151
- else {
152
- const keys = Object.keys(value).sort(Encoder.compareKeys);
153
- yield* this.encodeTypeAndArgument(5, keys.length);
154
- for (const key of keys) {
155
- yield* this.encodeString(key);
156
- yield* this.encodeValue(value[key]);
157
- }
158
- }
159
- }
160
- *flush() {
161
- if (this.closed) {
162
- return;
163
- }
164
- this.closed = true;
165
- if (this.offset > 0) {
166
- yield new Uint8Array(this.buffer, 0, this.offset);
167
- }
168
- }
169
- // Per the deterministic CBOR spec, we're supposed to sort keys
170
- // the byte-wise lexicographic order of the key's CBOR encoding
171
- // - ie lower major types come before higher major types!
172
- // However, microcbor only supports string keys, which means we
173
- // can get away with sorting them without actually encoding them
174
- // first. One thing we know for sure about strings is that a
175
- // string with a smaller length will sort byte-wise before a string
176
- // with a longer length, since strings are encoded with a length
177
- // prefix (either in the additionalInformation bits, if < 24, or
178
- // in the next serveral bytes, but in all cases the order holds).
179
- static compareKeys(a, b) {
180
- if (a.length < b.length) {
181
- return -1;
182
- }
183
- else if (b.length < a.length) {
184
- return 1;
185
- }
186
- else {
187
- return a < b ? -1 : 1;
188
- }
189
- }
190
- static getAdditionalInformation(argument) {
191
- if (argument < 24) {
192
- return argument;
193
- }
194
- else if (argument < 0x100) {
195
- return 24;
196
- }
197
- else if (argument < 0x10000) {
198
- return 25;
199
- }
200
- else if (argument < 0x100000000) {
201
- return 26;
202
- }
203
- else {
204
- return 27;
205
- }
206
- }
207
- }
208
- Encoder.defaultChunkSize = 512;
209
- export function encode(value, options = {}) {
210
- const encoder = new Encoder(options);
211
- let byteLength = 0;
212
- const chunks = [];
213
- for (const chunk of encoder.encodeValue(value)) {
214
- chunks.push(chunk);
215
- byteLength += chunk.byteLength;
216
- }
217
- for (const chunk of encoder.flush()) {
218
- chunks.push(chunk);
219
- byteLength += chunk.byteLength;
220
- }
221
- const data = new Uint8Array(byteLength);
222
- let offset = 0;
223
- for (const chunk of chunks) {
224
- data.set(chunk, offset);
225
- offset += chunk.length;
226
- }
227
- return data;
228
- }
@@ -1,4 +0,0 @@
1
- import type { CBORValue } from "./types.js";
2
- export declare function encodeStream(source: AsyncIterable<CBORValue>, options?: {
3
- chunkSize?: number;
4
- }): AsyncIterable<Uint8Array>;
@@ -1,8 +0,0 @@
1
- import { Encoder } from "./encode.js";
2
- export async function* encodeStream(source, options = {}) {
3
- const encoder = new Encoder(options);
4
- for await (const value of source) {
5
- yield* encoder.encodeValue(value);
6
- }
7
- yield* encoder.flush();
8
- }