microcbor 0.2.0 → 0.2.1

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 CHANGED
@@ -4,22 +4,22 @@
4
4
 
5
5
  Encode JavaScript values as canonical CBOR.
6
6
 
7
- microcbor is a minimal JavaScript [CBOR](https://cbor.io/) implementation. You can use microcbor to serialize JavaScript values to CBOR, and to deserialize them back into JavaScript values again. **microcbor doesn't support tags, bigints, typed arrays, non-string keys, or indefinite-length collections.**
7
+ microcbor is a minimal JavaScript [CBOR](https://cbor.io/) implementation featuring
8
8
 
9
- microcbor follows the [deterministic CBOR encoding requirements](https://www.rfc-editor.org/rfc/rfc8949.html#core-det) - all floating-point numbers are serialized in the smallest possible size without losing precision, and object entries are always sorted by key in byte-wise lexicographic order. `NaN` is always serialized as `0xf97e00`.
9
+ - a small footprint,
10
+ - fast performance, and
11
+ - an async iterable streaming API
10
12
 
11
- This library is TypeScript-native, ESM-only, and has just one dependency ([joeltg/fp16](https://github.com/joeltg/fp16) for half-precision floats). It works in Node, the browser, and Deno.
13
+ microcbor follows the [deterministic CBOR encoding requirements](https://www.rfc-editor.org/rfc/rfc8949.html#core-det) - all floating-point numbers are serialized in the smallest possible size without losing precision, and object entries are always sorted by key in byte-wise lexicographic order. `NaN` is always serialized as `0xf97e00`. **microcbor doesn't support tags, bigints, typed arrays, non-string keys, or indefinite-length collections.**
14
+
15
+ This library is TypeScript-native, ESM-only, and has just one dependency [joeltg/fp16](https://github.com/joeltg/fp16) for half-precision floats. It works in Node, the browser, and Deno.
12
16
 
13
17
  ## Table of Contents
14
18
 
15
19
  - [Install](#install)
16
20
  - [Usage](#usage)
17
21
  - [API](#api)
18
- - [Value types](#value-types)
19
- - [Encoding](#encoding)
20
- - [Decoding](#decoding)
21
- - [Encoding length](#encoding-length)
22
- - [Support](#support)
22
+ - [Value mapping](#value-mapping)
23
23
  - [Testing](#testing)
24
24
  - [Benchmarks](#benchmarks)
25
25
  - [Contributing](#contributing)
@@ -34,15 +34,7 @@ npm i microcbor
34
34
  Or in Deno:
35
35
 
36
36
  ```typescript
37
- import {
38
- encode,
39
- decode,
40
- encodeStream,
41
- decodeStream,
42
- encodingLength,
43
- CBORValue,
44
- UnsafeIntegerError,
45
- } from "https://cdn.skypack.dev/microcbor"
37
+ import { encode, decode } from "https://cdn.skypack.dev/microcbor"
46
38
  ```
47
39
 
48
40
  ## Usage
@@ -65,8 +57,6 @@ console.log(decode(data))
65
57
 
66
58
  ## API
67
59
 
68
- ### Value types
69
-
70
60
  ```ts
71
61
  declare type CBORValue =
72
62
  | undefined
@@ -82,39 +72,29 @@ interface CBORArray extends Array<CBORValue> {}
82
72
  interface CBORMap {
83
73
  [key: string]: CBORValue
84
74
  }
85
- ```
86
-
87
- ### Encoding
88
75
 
89
- ```typescript
76
+ // If not provided, chunkSize defaults to 512 bytes.
77
+ // It's only a guideline; `encodeStream` won't break up
78
+ // individual CBOR values like strings or byte arrays
79
+ // that are larger than the provided chunk size.
90
80
  declare function encode(
91
81
  value: CBORValue,
92
- options: { chunkSize?: number } = {}
82
+ options?: { chunkSize?: number }
93
83
  ): Uint8Array
94
84
 
95
85
  declare function encodeStream(
96
86
  source: AsyncIterable<CBORValue>,
97
87
  options?: { chunkSize?: number }
98
88
  ): AsyncIterable<Uint8Array>
99
- ```
100
89
 
101
- If not provided, `chunkSize` defaults to 512 bytes. It's only a guideline; `encodeStream` won't break up individual CBOR values like strings or byte arrays that are larger than the provided chunk size.
102
-
103
- ### Decoding
104
-
105
- ```typescript
106
90
  declare function decode(data: Uint8Array): CBORValue
107
91
 
108
92
  declare function decodeStream(
109
93
  source: AsyncIterable<Uint8Array>
110
94
  ): AsyncIterable<CBORValue>
111
- ```
112
95
 
113
- ### Encoding length
114
-
115
- You can measure the byte length that a given value will serialize to without actually allocating anything.
116
-
117
- ```ts
96
+ // You can measure the byte length that a given value will
97
+ // serialize to without actually allocating anything.
118
98
  declare function encodingLength(value: CBORValue): number
119
99
  ```
120
100
 
@@ -132,19 +112,19 @@ declare class UnsafeIntegerError extends RangeError {
132
112
 
133
113
  ## Value mapping
134
114
 
135
- | CBOR major type | JavaScript | notes |
136
- | ---------------------------- | -------------- | -------------------------------------------------------- |
137
- | `0` (non-negative integer) | `number` | decoding throws an `UnsafeIntegerError` on unsafe values |
138
- | `1` (negative integer) | `number` | decoding throws an `UnsafeIntegerError` on unsafe values |
139
- | `2` (byte string) | `Uint8Array` | |
140
- | `3` (UTF-8 string) | `string` | |
141
- | `4` (array) | `Array` | |
142
- | `5` (map) | `Object` | decoding throws an error on non-string keys |
143
- | `6` (tagged item) | Unsupported | decoding throws an error on non-string keys |
144
- | `7` (floating-point numbers) | `number` | |
145
- | `7` (booleans) | `boolean` | |
146
- | `7` (null) | `null` | |
147
- | `7` (undefined) | `undefined` | |
115
+ | CBOR major type | JavaScript | notes |
116
+ | ---------------------------- | --------------- | -------------------------------------------------------- |
117
+ | `0` (non-negative integer) | `number` | decoding throws an `UnsafeIntegerError` on unsafe values |
118
+ | `1` (negative integer) | `number` | decoding throws an `UnsafeIntegerError` on unsafe values |
119
+ | `2` (byte string) | `Uint8Array` | |
120
+ | `3` (UTF-8 string) | `string` | |
121
+ | `4` (array) | `Array` | |
122
+ | `5` (map) | `Object` | decoding throws an error on non-string keys |
123
+ | `6` (tagged item) | **Unsupported** | |
124
+ | `7` (floating-point numbers) | `number` | |
125
+ | `7` (booleans) | `boolean` | |
126
+ | `7` (null) | `null` | |
127
+ | `7` (undefined) | `undefined` | |
148
128
 
149
129
  ## Testing
150
130
 
@@ -154,9 +134,12 @@ Tests use [AVA](https://github.com/avajs/ava) and live in the [test](./test/) di
154
134
  npm run test
155
135
  ```
156
136
 
157
- ## Benchmarks
137
+ ## Comparison to node-cbor
158
138
 
159
- Basic testing in [src/benchmarks.test.js](src/benchmarks.test.js) indicate that microcbor is about **2x as fast** as node-cbor at encoding and about **1.5x as fast** as node-cbor at decoding.
139
+ - microcbor runs isomorphically on the web, in Node, and in Deno. node-cbor ships a separate cbor-web package.
140
+ - microcbor encodes `Uint8Array` values as CBOR byte strings (major type 2). node-cbor encodes `Uint8Array` values as tagged type arrays (major type 6 / RFC 8746), and encodes NodeJS `Buffer` values as CBOR byte strings (major type 2).
141
+ - microcbor uses async iterables for its streaming API. node-cbor uses NodeJS streams.
142
+ - microcbor is about **2x faster** than node-cbor at encoding and about **1.5x faster** than node-cbor at decoding.
160
143
 
161
144
  ```
162
145
  microcbor % npm run test -- test/benchmarks.test.js
@@ -165,12 +148,14 @@ microcbor % npm run test -- test/benchmarks.test.js
165
148
  > ava
166
149
 
167
150
 
168
- ✔ time encode() (382ms)
169
- ℹ microcbor: 63.44141721725464 (ms)
170
- ℹ node-cbor: 152.31466674804688 (ms)
171
- time decode() (164ms)
172
- microcbor: 72.13012504577637 (ms)
173
- node-cbor: 87.16287469863892 (ms)
151
+ ✔ time encode() (390ms)
152
+ ℹ microcbor: 66.47262525558472 (ms)
153
+ ℹ node-cbor: 155.0249171257019 (ms)
154
+ JSON.stringify: 5.56374979019165 (ms)
155
+ time decode() (161ms)
156
+ microcbor: 64.23729228973389 (ms)
157
+ ℹ node-cbor: 91.34658432006836 (ms)
158
+ ℹ JSON.parse: 2.7592921257019043 (ms)
174
159
 
175
160
 
176
161
  2 tests passed
package/lib/encode.d.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import type { CBORValue } from "./types.js";
2
2
  export declare class Encoder {
3
- readonly options: {
4
- chunkSize?: number;
5
- };
6
3
  static defaultChunkSize: number;
7
4
  closed: boolean;
8
5
  private buffer;
9
6
  private view;
10
7
  private offset;
8
+ private readonly encoder;
9
+ private readonly chunkSize;
11
10
  constructor(options?: {
12
11
  chunkSize?: number;
12
+ noCopy?: boolean;
13
13
  });
14
14
  private allocate;
15
15
  private float16;
@@ -19,7 +19,6 @@ export declare class Encoder {
19
19
  private uint16;
20
20
  private uint32;
21
21
  private uint64;
22
- private constant;
23
22
  private encodeTypeAndArgument;
24
23
  private encodeNumber;
25
24
  private encodeInteger;
package/lib/encode.js CHANGED
@@ -1,15 +1,9 @@
1
1
  import { getFloat16Precision, getFloat32Precision, setFloat16, Precision, } from "fp16";
2
2
  export class Encoder {
3
3
  constructor(options = {}) {
4
- this.options = options;
5
- this.float16 = this.constant(2, (value) => setFloat16(this.view, this.offset, value));
6
- this.float32 = this.constant(4, (value) => this.view.setFloat32(this.offset, value));
7
- this.float64 = this.constant(8, (value) => this.view.setFloat64(this.offset, value));
8
- this.uint8 = this.constant(1, (value) => this.view.setUint8(this.offset, value));
9
- this.uint16 = this.constant(2, (value) => this.view.setUint16(this.offset, value));
10
- this.uint32 = this.constant(4, (value) => this.view.setUint32(this.offset, value));
11
- this.uint64 = this.constant(8, (value) => this.view.setBigUint64(this.offset, BigInt(value)));
12
- this.buffer = new ArrayBuffer(options.chunkSize || Encoder.defaultChunkSize);
4
+ this.encoder = new TextEncoder();
5
+ this.chunkSize = options.chunkSize || Encoder.defaultChunkSize;
6
+ this.buffer = new ArrayBuffer(this.chunkSize);
13
7
  this.view = new DataView(this.buffer);
14
8
  this.offset = 0;
15
9
  this.closed = false;
@@ -17,19 +11,46 @@ export class Encoder {
17
11
  *allocate(size) {
18
12
  if (this.buffer.byteLength < this.offset + size) {
19
13
  yield new Uint8Array(this.buffer, 0, this.offset);
20
- const byteLength = Math.max(size, this.options.chunkSize || Encoder.defaultChunkSize);
14
+ const byteLength = Math.max(size, this.chunkSize);
21
15
  this.buffer = new ArrayBuffer(byteLength);
22
16
  this.view = new DataView(this.buffer);
23
17
  this.offset = 0;
24
18
  }
25
19
  }
26
- constant(size, f) {
27
- const g = function* (value) {
28
- yield* this.allocate(size);
29
- f(value);
30
- this.offset += size;
31
- };
32
- return g.bind(this);
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;
33
54
  }
34
55
  *encodeTypeAndArgument(type, argument) {
35
56
  const additionalInformation = Encoder.getAdditionalInformation(argument);
@@ -84,7 +105,7 @@ export class Encoder {
84
105
  }
85
106
  }
86
107
  *encodeString(value) {
87
- const data = new TextEncoder().encode(value);
108
+ const data = this.encoder.encode(value);
88
109
  yield* this.encodeTypeAndArgument(3, data.byteLength);
89
110
  yield* this.allocate(data.byteLength);
90
111
  new Uint8Array(this.buffer, this.offset).set(data);
package/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export { encode } from "./encode.js";
1
+ export type { CBORValue, CBORMap, CBORArray } from "./types.js";
2
+ export { encode, Encoder } from "./encode.js";
2
3
  export { decode } from "./decode.js";
3
4
  export { encodeStream } from "./encodeStream.js";
4
5
  export { decodeStream } from "./decodeStream.js";
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { encode } from "./encode.js";
1
+ export { encode, Encoder } from "./encode.js";
2
2
  export { decode } from "./decode.js";
3
3
  export { encodeStream } from "./encodeStream.js";
4
4
  export { decodeStream } from "./decodeStream.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "microcbor",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Encode JavaScript values as canonical CBOR",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",