Skip to content

Commit 7ad7809

Browse files
authored
Merge pull request #224 from msgpack/gfx/decoder-and-encoder-take-object-params
let Encoder and Decoder accept named params as encode() and decode() do
2 parents c4d65d6 + 842dc45 commit 7ad7809

9 files changed

+242
-250
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ https://github.com/msgpack/msgpack-javascript/compare/v2.8.0...v3.0.0-beta1
77
* Add an option `useBigInt64` to map JavaScript's BigInt to MessagePack's int64 and uint64 ([#223](https://github.com/msgpack/msgpack-javascript/pull/223))
88
* Drop IE11 support ([#221](https://github.com/msgpack/msgpack-javascript/pull/221))
99
* It also fixes [feature request: option to disable TEXT_ENCODING env check #219](https://github.com/msgpack/msgpack-javascript/issues/219)
10-
*
10+
* Change the interfaces of `Encoder` and `Decoder`, and describe the interfaces in README.md ([#224](https://github.com/msgpack/msgpack-javascript/pull/224)):
11+
* `new Encoder(options: EncoderOptions)`: it takes the same named-options as `encode()`
12+
* `new Decoder(options: DecoderOptions)`: it takes the same named-options as `decode()`
1113

1214
## 2.8.0 2022-09-02
1315

README.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ deepStrictEqual(decode(encoded), object);
3838
- [Table of Contents](#table-of-contents)
3939
- [Install](#install)
4040
- [API](#api)
41-
- [`encode(data: unknown, options?: EncodeOptions): Uint8Array`](#encodedata-unknown-options-encodeoptions-uint8array)
42-
- [`EncodeOptions`](#encodeoptions)
43-
- [`decode(buffer: ArrayLike<number> | BufferSource, options?: DecodeOptions): unknown`](#decodebuffer-arraylikenumber--buffersource-options-decodeoptions-unknown)
44-
- [`DecodeOptions`](#decodeoptions)
45-
- [`decodeMulti(buffer: ArrayLike<number> | BufferSource, options?: DecodeOptions): Generator<unknown, void, unknown>`](#decodemultibuffer-arraylikenumber--buffersource-options-decodeoptions-generatorunknown-void-unknown)
46-
- [`decodeAsync(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): Promise<unknown>`](#decodeasyncstream-readablestreamlikearraylikenumber--buffersource-options-decodeasyncoptions-promiseunknown)
47-
- [`decodeArrayStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): AsyncIterable<unknown>`](#decodearraystreamstream-readablestreamlikearraylikenumber--buffersource-options-decodeasyncoptions-asynciterableunknown)
48-
- [`decodeMultiStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): AsyncIterable<unknown>`](#decodemultistreamstream-readablestreamlikearraylikenumber--buffersource-options-decodeasyncoptions-asynciterableunknown)
41+
- [`encode(data: unknown, options?: EncoderOptions): Uint8Array`](#encodedata-unknown-options-encoderoptions-uint8array)
42+
- [`EncoderOptions`](#encoderoptions)
43+
- [`decode(buffer: ArrayLike<number> | BufferSource, options?: DecoderOptions): unknown`](#decodebuffer-arraylikenumber--buffersource-options-decoderoptions-unknown)
44+
- [`DecoderOptions`](#decoderoptions)
45+
- [`decodeMulti(buffer: ArrayLike<number> | BufferSource, options?: DecoderOptions): Generator<unknown, void, unknown>`](#decodemultibuffer-arraylikenumber--buffersource-options-decoderoptions-generatorunknown-void-unknown)
46+
- [`decodeAsync(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecoderOptions): Promise<unknown>`](#decodeasyncstream-readablestreamlikearraylikenumber--buffersource-options-decoderoptions-promiseunknown)
47+
- [`decodeArrayStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecoderOptions): AsyncIterable<unknown>`](#decodearraystreamstream-readablestreamlikearraylikenumber--buffersource-options-decoderoptions-asynciterableunknown)
48+
- [`decodeMultiStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecoderOptions): AsyncIterable<unknown>`](#decodemultistreamstream-readablestreamlikearraylikenumber--buffersource-options-decoderoptions-asynciterableunknown)
4949
- [Reusing Encoder and Decoder instances](#reusing-encoder-and-decoder-instances)
5050
- [Extension Types](#extension-types)
5151
- [ExtensionCodec context](#extensioncodec-context)
@@ -80,7 +80,7 @@ npm install @msgpack/msgpack
8080

8181
## API
8282

83-
### `encode(data: unknown, options?: EncodeOptions): Uint8Array`
83+
### `encode(data: unknown, options?: EncoderOptions): Uint8Array`
8484

8585
It encodes `data` into a single MessagePack-encoded object, and returns a byte array as `Uint8Array`. It throws errors if `data` is, or includes, a non-serializable object such as a `function` or a `symbol`.
8686

@@ -105,7 +105,7 @@ const buffer: Buffer = Buffer.from(encoded.buffer, encoded.byteOffset, encoded.b
105105
console.log(buffer);
106106
```
107107

108-
#### `EncodeOptions`
108+
#### `EncoderOptions`
109109

110110
Name|Type|Default
111111
----|----|----
@@ -118,7 +118,7 @@ forceIntegerToFloat | boolean | false
118118
ignoreUndefined | boolean | false
119119
context | user-defined | -
120120

121-
### `decode(buffer: ArrayLike<number> | BufferSource, options?: DecodeOptions): unknown`
121+
### `decode(buffer: ArrayLike<number> | BufferSource, options?: DecoderOptions): unknown`
122122

123123
It decodes `buffer` that includes a MessagePack-encoded object, and returns the decoded object typed `unknown`.
124124

@@ -138,7 +138,7 @@ console.log(object);
138138

139139
NodeJS `Buffer` is also acceptable because it is a subclass of `Uint8Array`.
140140

141-
#### `DecodeOptions`
141+
#### `DecoderOptions`
142142

143143
Name|Type|Default
144144
----|----|----
@@ -152,7 +152,7 @@ context | user-defined | -
152152

153153
You can use `max${Type}Length` to limit the length of each type decoded.
154154

155-
### `decodeMulti(buffer: ArrayLike<number> | BufferSource, options?: DecodeOptions): Generator<unknown, void, unknown>`
155+
### `decodeMulti(buffer: ArrayLike<number> | BufferSource, options?: DecoderOptions): Generator<unknown, void, unknown>`
156156

157157
It decodes `buffer` that includes multiple MessagePack-encoded objects, and returns decoded objects as a generator. See also `decodeMultiStream()`, which is an asynchronous variant of this function.
158158

@@ -170,14 +170,12 @@ for (const object of decodeMulti(encoded)) {
170170
}
171171
```
172172

173-
### `decodeAsync(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): Promise<unknown>`
173+
### `decodeAsync(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecoderOptions): Promise<unknown>`
174174

175175
It decodes `stream`, where `ReadableStreamLike<T>` is defined as `ReadableStream<T> | AsyncIterable<T>`, in an async iterable of byte arrays, and returns decoded object as `unknown` type, wrapped in `Promise`.
176176

177177
This function works asynchronously, and might CPU resources more efficiently compared with synchronous `decode()`, because it doesn't wait for the completion of downloading.
178178

179-
`DecodeAsyncOptions` is the same as `DecodeOptions` for `decode()`.
180-
181179
This function is designed to work with whatwg `fetch()` like this:
182180

183181
```typescript
@@ -193,7 +191,7 @@ if (contentType && contentType.startsWith(MSGPACK_TYPE) && response.body != null
193191
} else { /* handle errors */ }
194192
```
195193

196-
### `decodeArrayStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): AsyncIterable<unknown>`
194+
### `decodeArrayStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecoderOptions): AsyncIterable<unknown>`
197195

198196
It is alike to `decodeAsync()`, but only accepts a `stream` that includes an array of items, and emits a decoded item one by one.
199197

@@ -210,7 +208,7 @@ for await (const item of decodeArrayStream(stream)) {
210208
}
211209
```
212210

213-
### `decodeMultiStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecodeAsyncOptions): AsyncIterable<unknown>`
211+
### `decodeMultiStream(stream: ReadableStreamLike<ArrayLike<number> | BufferSource>, options?: DecoderOptions): AsyncIterable<unknown>`
214212

215213
It is alike to `decodeAsync()` and `decodeArrayStream()`, but the input `stream` must consist of multiple MessagePack-encoded items. This is an asynchronous variant for `decodeMulti()`.
216214

@@ -233,7 +231,7 @@ This function is available since v2.4.0; previously it was called as `decodeStre
233231

234232
### Reusing Encoder and Decoder instances
235233

236-
`Encoder` and `Decoder` classes is provided to have better performance by reusing instances:
234+
`Encoder` and `Decoder` classes are provided to have better performance by reusing instances:
237235

238236
```typescript
239237
import { deepStrictEqual } from "assert";
@@ -251,6 +249,8 @@ than `encode()` function, and reusing `Decoder` instance is about 2% faster
251249
than `decode()` function. Note that the result should vary in environments
252250
and data structure.
253251

252+
`Encoder` and `Decoder` take the same options as `encode()` and `decode()` respectively.
253+
254254
## Extension Types
255255

256256
To handle [MessagePack Extension Types](https://github.com/msgpack/msgpack/blob/master/spec.md#extension-types), this library provides `ExtensionCodec` class.
@@ -304,7 +304,7 @@ Not that extension types for custom objects must be `[0, 127]`, while `[-1, -128
304304

305305
#### ExtensionCodec context
306306

307-
When you use an extension codec, it might be necessary to have encoding/decoding state to keep track of which objects got encoded/re-created. To do this, pass a `context` to the `EncodeOptions` and `DecodeOptions`:
307+
When you use an extension codec, it might be necessary to have encoding/decoding state to keep track of which objects got encoded/re-created. To do this, pass a `context` to the `EncoderOptions` and `DecoderOptions`:
308308

309309
```typescript
310310
import { encode, decode, ExtensionCodec } from "@msgpack/msgpack";

src/Decoder.ts

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,61 @@ import { utf8Decode } from "./utils/utf8";
55
import { createDataView, ensureUint8Array } from "./utils/typedArrays";
66
import { CachedKeyDecoder, KeyDecoder } from "./CachedKeyDecoder";
77
import { DecodeError } from "./DecodeError";
8+
import type { ContextOf } from "./context";
9+
10+
export type DecoderOptions<ContextType = undefined> = Readonly<
11+
Partial<{
12+
extensionCodec: ExtensionCodecType<ContextType>;
13+
14+
/**
15+
* Decodes Int64 and Uint64 as bigint if it's set to true.
16+
* Depends on ES2020's {@link DataView#getBigInt64} and
17+
* {@link DataView#getBigUint64}.
18+
*
19+
* Defaults to false.
20+
*/
21+
useBigInt64: boolean;
22+
23+
/**
24+
* Maximum string length.
25+
*
26+
* Defaults to 4_294_967_295 (UINT32_MAX).
27+
*/
28+
maxStrLength: number;
29+
/**
30+
* Maximum binary length.
31+
*
32+
* Defaults to 4_294_967_295 (UINT32_MAX).
33+
*/
34+
maxBinLength: number;
35+
/**
36+
* Maximum array length.
37+
*
38+
* Defaults to 4_294_967_295 (UINT32_MAX).
39+
*/
40+
maxArrayLength: number;
41+
/**
42+
* Maximum map length.
43+
*
44+
* Defaults to 4_294_967_295 (UINT32_MAX).
45+
*/
46+
maxMapLength: number;
47+
/**
48+
* Maximum extension length.
49+
*
50+
* Defaults to 4_294_967_295 (UINT32_MAX).
51+
*/
52+
maxExtLength: number;
53+
54+
/**
55+
* An object key decoder. Defaults to the shared instance of {@link CachedKeyDecoder}.
56+
* `null` is a special value to disable the use of the key decoder at all.
57+
*/
58+
keyDecoder: KeyDecoder | null;
59+
}>
60+
> &
61+
ContextOf<ContextType>;
62+
863

964
const STATE_ARRAY = "array";
1065
const STATE_MAP_KEY = "map_key";
@@ -54,6 +109,16 @@ const MORE_DATA = new DataViewIndexOutOfBoundsError("Insufficient data");
54109
const sharedCachedKeyDecoder = new CachedKeyDecoder();
55110

56111
export class Decoder<ContextType = undefined> {
112+
private readonly extensionCodec: ExtensionCodecType<ContextType>;
113+
private readonly context: ContextType;
114+
private readonly useBigInt64: boolean;
115+
private readonly maxStrLength: number;
116+
private readonly maxBinLength: number;
117+
private readonly maxArrayLength: number;
118+
private readonly maxMapLength: number;
119+
private readonly maxExtLength: number;
120+
private readonly keyDecoder: KeyDecoder | null;
121+
57122
private totalPos = 0;
58123
private pos = 0;
59124

@@ -62,17 +127,18 @@ export class Decoder<ContextType = undefined> {
62127
private headByte = HEAD_BYTE_REQUIRED;
63128
private readonly stack: Array<StackState> = [];
64129

65-
public constructor(
66-
private readonly extensionCodec: ExtensionCodecType<ContextType> = ExtensionCodec.defaultCodec as any,
67-
private readonly context: ContextType = undefined as any,
68-
private readonly useBigInt64 = false,
69-
private readonly maxStrLength = UINT32_MAX,
70-
private readonly maxBinLength = UINT32_MAX,
71-
private readonly maxArrayLength = UINT32_MAX,
72-
private readonly maxMapLength = UINT32_MAX,
73-
private readonly maxExtLength = UINT32_MAX,
74-
private readonly keyDecoder: KeyDecoder | null = sharedCachedKeyDecoder,
75-
) {}
130+
public constructor(options?: DecoderOptions<ContextType>) {
131+
this.extensionCodec = options?.extensionCodec ?? (ExtensionCodec.defaultCodec as ExtensionCodecType<ContextType>);
132+
this.context = (options as { context: ContextType } | undefined)?.context as ContextType; // needs a type assertion because EncoderOptions has no context property when ContextType is undefined
133+
134+
this.useBigInt64 = options?.useBigInt64 ?? false;
135+
this.maxStrLength = options?.maxStrLength ?? UINT32_MAX;
136+
this.maxBinLength = options?.maxBinLength ?? UINT32_MAX;
137+
this.maxArrayLength = options?.maxArrayLength ?? UINT32_MAX;
138+
this.maxMapLength = options?.maxMapLength ?? UINT32_MAX;
139+
this.maxExtLength = options?.maxExtLength ?? UINT32_MAX;
140+
this.keyDecoder = (options?.keyDecoder !== undefined) ? options.keyDecoder : sharedCachedKeyDecoder;
141+
}
76142

77143
private reinitializeState() {
78144
this.totalPos = 0;

src/Encoder.ts

Lines changed: 94 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,105 @@ import { ExtensionCodec, ExtensionCodecType } from "./ExtensionCodec";
33
import { setInt64, setUint64 } from "./utils/int";
44
import { ensureUint8Array } from "./utils/typedArrays";
55
import type { ExtData } from "./ExtData";
6+
import type { ContextOf, SplitUndefined } from "./context";
7+
68

79
export const DEFAULT_MAX_DEPTH = 100;
810
export const DEFAULT_INITIAL_BUFFER_SIZE = 2048;
911

12+
export type EncoderOptions<ContextType = undefined> = Partial<
13+
Readonly<{
14+
extensionCodec: ExtensionCodecType<ContextType>;
15+
16+
/**
17+
* Encodes bigint as Int64 or Uint64 if it's set to true.
18+
* {@link forceIntegerToFloat} does not affect bigint.
19+
* Depends on ES2020's {@link DataView#setBigInt64} and
20+
* {@link DataView#setBigUint64}.
21+
*
22+
* Defaults to false.
23+
*/
24+
useBigInt64: boolean;
25+
26+
/**
27+
* The maximum depth in nested objects and arrays.
28+
*
29+
* Defaults to 100.
30+
*/
31+
maxDepth: number;
32+
33+
/**
34+
* The initial size of the internal buffer.
35+
*
36+
* Defaults to 2048.
37+
*/
38+
initialBufferSize: number;
39+
40+
/**
41+
* If `true`, the keys of an object is sorted. In other words, the encoded
42+
* binary is canonical and thus comparable to another encoded binary.
43+
*
44+
* Defaults to `false`. If enabled, it spends more time in encoding objects.
45+
*/
46+
sortKeys: boolean;
47+
/**
48+
* If `true`, non-integer numbers are encoded in float32, not in float64 (the default).
49+
*
50+
* Only use it if precisions don't matter.
51+
*
52+
* Defaults to `false`.
53+
*/
54+
forceFloat32: boolean;
55+
56+
/**
57+
* If `true`, an object property with `undefined` value are ignored.
58+
* e.g. `{ foo: undefined }` will be encoded as `{}`, as `JSON.stringify()` does.
59+
*
60+
* Defaults to `false`. If enabled, it spends more time in encoding objects.
61+
*/
62+
ignoreUndefined: boolean;
63+
64+
/**
65+
* If `true`, integer numbers are encoded as floating point numbers,
66+
* with the `forceFloat32` option taken into account.
67+
*
68+
* Defaults to `false`.
69+
*/
70+
forceIntegerToFloat: boolean;
71+
}>
72+
> & ContextOf<ContextType>;
73+
1074
export class Encoder<ContextType = undefined> {
11-
private pos = 0;
12-
private view = new DataView(new ArrayBuffer(this.initialBufferSize));
13-
private bytes = new Uint8Array(this.view.buffer);
14-
15-
public constructor(
16-
private readonly extensionCodec: ExtensionCodecType<ContextType> = ExtensionCodec.defaultCodec as any,
17-
private readonly context: ContextType = undefined as any,
18-
private readonly useBigInt64 = false,
19-
private readonly maxDepth = DEFAULT_MAX_DEPTH,
20-
private readonly initialBufferSize = DEFAULT_INITIAL_BUFFER_SIZE,
21-
private readonly sortKeys = false,
22-
private readonly forceFloat32 = false,
23-
private readonly ignoreUndefined = false,
24-
private readonly forceIntegerToFloat = false,
25-
) {}
75+
private readonly extensionCodec: ExtensionCodecType<ContextType>;
76+
private readonly context: ContextType;
77+
private readonly useBigInt64: boolean;
78+
private readonly maxDepth: number;
79+
private readonly initialBufferSize: number;
80+
private readonly sortKeys: boolean;
81+
private readonly forceFloat32: boolean;
82+
private readonly ignoreUndefined: boolean;
83+
private readonly forceIntegerToFloat: boolean;
84+
85+
private pos: number;
86+
private view: DataView;
87+
private bytes: Uint8Array;
88+
89+
public constructor(options?: EncoderOptions<ContextType>) {
90+
this.extensionCodec = options?.extensionCodec ?? (ExtensionCodec.defaultCodec as ExtensionCodecType<ContextType>);
91+
this.context = (options as { context: ContextType } | undefined)?.context as ContextType; // needs a type assertion because EncoderOptions has no context property when ContextType is undefined
92+
93+
this.useBigInt64 = options?.useBigInt64 ?? false;
94+
this.maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
95+
this.initialBufferSize = options?.initialBufferSize ?? DEFAULT_INITIAL_BUFFER_SIZE;
96+
this.sortKeys = options?.sortKeys ?? false;
97+
this.forceFloat32 = options?.forceFloat32 ?? false;
98+
this.ignoreUndefined = options?.ignoreUndefined ?? false;
99+
this.forceIntegerToFloat = options?.forceIntegerToFloat ?? false;
100+
101+
this.pos = 0;
102+
this.view = new DataView(new ArrayBuffer(this.initialBufferSize));
103+
this.bytes = new Uint8Array(this.view.buffer);
104+
}
26105

27106
private reinitializeState() {
28107
this.pos = 0;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy