Skip to content

Commit 533207e

Browse files
committed
let Encoder and Decoder accept named params as encode() and decode() do
1 parent c4d65d6 commit 533207e

File tree

7 files changed

+219
-229
lines changed

7 files changed

+219
-229
lines changed

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;

src/decode.ts

Lines changed: 14 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,16 @@
11
import { Decoder } from "./Decoder";
2-
import type { ExtensionCodecType } from "./ExtensionCodec";
3-
import type { ContextOf, SplitUndefined } from "./context";
2+
import type { DecoderOptions } from "./Decoder";
3+
import type { SplitUndefined } from "./context";
44

5-
export type DecodeOptions<ContextType = undefined> = Readonly<
6-
Partial<{
7-
extensionCodec: ExtensionCodecType<ContextType>;
8-
9-
/**
10-
* Decodes Int64 and Uint64 as bigint if it's set to true.
11-
* Depends on ES2020's {@link DataView#getBigInt64} and
12-
* {@link DataView#getBigUint64}.
13-
*
14-
* Defaults to false.
15-
*/
16-
useBigInt64: boolean;
17-
18-
/**
19-
* Maximum string length.
20-
*
21-
* Defaults to 4_294_967_295 (UINT32_MAX).
22-
*/
23-
maxStrLength: number;
24-
/**
25-
* Maximum binary length.
26-
*
27-
* Defaults to 4_294_967_295 (UINT32_MAX).
28-
*/
29-
maxBinLength: number;
30-
/**
31-
* Maximum array length.
32-
*
33-
* Defaults to 4_294_967_295 (UINT32_MAX).
34-
*/
35-
maxArrayLength: number;
36-
/**
37-
* Maximum map length.
38-
*
39-
* Defaults to 4_294_967_295 (UINT32_MAX).
40-
*/
41-
maxMapLength: number;
42-
/**
43-
* Maximum extension length.
44-
*
45-
* Defaults to 4_294_967_295 (UINT32_MAX).
46-
*/
47-
maxExtLength: number;
48-
}>
49-
> &
50-
ContextOf<ContextType>;
5+
/**
6+
* @deprecated Use {@link DecoderOptions} instead.
7+
*/
8+
export type DecodeOptions = never;
519

52-
export const defaultDecodeOptions: DecodeOptions = {};
10+
/**
11+
* @deprecated No longer supported.
12+
*/
13+
export const defaultDecodeOptions: never = undefined as never;
5314

5415
/**
5516
* It decodes a single MessagePack object in a buffer.
@@ -62,18 +23,9 @@ export const defaultDecodeOptions: DecodeOptions = {};
6223
*/
6324
export function decode<ContextType = undefined>(
6425
buffer: ArrayLike<number> | BufferSource,
65-
options: DecodeOptions<SplitUndefined<ContextType>> = defaultDecodeOptions as any,
26+
options?: DecoderOptions<SplitUndefined<ContextType>>,
6627
): unknown {
67-
const decoder = new Decoder(
68-
options.extensionCodec,
69-
(options as typeof options & { context: any }).context,
70-
options.useBigInt64,
71-
options.maxStrLength,
72-
options.maxBinLength,
73-
options.maxArrayLength,
74-
options.maxMapLength,
75-
options.maxExtLength,
76-
);
28+
const decoder = new Decoder(options);
7729
return decoder.decode(buffer);
7830
}
7931

@@ -86,17 +38,8 @@ export function decode<ContextType = undefined>(
8638
*/
8739
export function decodeMulti<ContextType = undefined>(
8840
buffer: ArrayLike<number> | BufferSource,
89-
options: DecodeOptions<SplitUndefined<ContextType>> = defaultDecodeOptions as any,
41+
options?: DecoderOptions<SplitUndefined<ContextType>>,
9042
): Generator<unknown, void, unknown> {
91-
const decoder = new Decoder(
92-
options.extensionCodec,
93-
(options as typeof options & { context: any }).context,
94-
options.useBigInt64,
95-
options.maxStrLength,
96-
options.maxBinLength,
97-
options.maxArrayLength,
98-
options.maxMapLength,
99-
options.maxExtLength,
100-
);
43+
const decoder = new Decoder(options);
10144
return decoder.decodeMulti(buffer);
10245
}

0 commit comments

Comments
 (0)