Skip to content

Commit bf9c15a

Browse files
authored
Merge pull request #266 from PejmanNik/adding-support-for-nonstandard-map-key-in-the-decoder
adding support for nonstandard map key in the decoder
2 parents 588354f + 59057ee commit bf9c15a

File tree

3 files changed

+44
-17
lines changed

3 files changed

+44
-17
lines changed

README.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -145,17 +145,20 @@ NodeJS `Buffer` is also acceptable because it is a subclass of `Uint8Array`.
145145

146146
#### `DecoderOptions`
147147

148-
| Name | Type | Default |
149-
| -------------- | -------------- | ----------------------------- |
150-
| extensionCodec | ExtensionCodec | `ExtensionCodec.defaultCodec` |
151-
| context | user-defined | - |
152-
| useBigInt64 | boolean | false |
153-
| rawStrings | boolean | false |
154-
| maxStrLength | number | `4_294_967_295` (UINT32_MAX) |
155-
| maxBinLength | number | `4_294_967_295` (UINT32_MAX) |
156-
| maxArrayLength | number | `4_294_967_295` (UINT32_MAX) |
157-
| maxMapLength | number | `4_294_967_295` (UINT32_MAX) |
158-
| maxExtLength | number | `4_294_967_295` (UINT32_MAX) |
148+
| Name | Type | Default |
149+
| --------------- | ------------------- | ---------------------------------------------- |
150+
| extensionCodec | ExtensionCodec | `ExtensionCodec.defaultCodec` |
151+
| context | user-defined | - |
152+
| useBigInt64 | boolean | false |
153+
| rawStrings | boolean | false |
154+
| maxStrLength | number | `4_294_967_295` (UINT32_MAX) |
155+
| maxBinLength | number | `4_294_967_295` (UINT32_MAX) |
156+
| maxArrayLength | number | `4_294_967_295` (UINT32_MAX) |
157+
| maxMapLength | number | `4_294_967_295` (UINT32_MAX) |
158+
| maxExtLength | number | `4_294_967_295` (UINT32_MAX) |
159+
| mapKeyConverter | MapKeyConverterType | throw exception if key is not string or number |
160+
161+
`MapKeyConverterType` is defined as `(key: unknown) => string | number`.
159162

160163
To skip UTF-8 decoding of strings, `rawStrings` can be set to `true`. In this case, strings are decoded into `Uint8Array`.
161164

src/Decoder.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ export type DecoderOptions<ContextType = undefined> = Readonly<
6868
* `null` is a special value to disable the use of the key decoder at all.
6969
*/
7070
keyDecoder: KeyDecoder | null;
71+
72+
/**
73+
* A function to convert decoded map key to a valid JS key type.
74+
*
75+
* Defaults to a function that throws an error if the key is not a string or a number.
76+
*/
77+
mapKeyConverter: (key: unknown) => MapKeyType;
7178
}>
7279
> &
7380
ContextOf<ContextType>;
@@ -78,8 +85,11 @@ const STATE_MAP_VALUE = "map_value";
7885

7986
type MapKeyType = string | number;
8087

81-
const isValidMapKeyType = (key: unknown): key is MapKeyType => {
82-
return typeof key === "string" || typeof key === "number";
88+
const mapKeyConverter = (key: unknown): MapKeyType => {
89+
if (typeof key === "string" || typeof key === "number") {
90+
return key;
91+
}
92+
throw new DecodeError("The type of key must be string or number but " + typeof key);
8393
};
8494

8595
type StackMapState = {
@@ -213,6 +223,7 @@ export class Decoder<ContextType = undefined> {
213223
private readonly maxMapLength: number;
214224
private readonly maxExtLength: number;
215225
private readonly keyDecoder: KeyDecoder | null;
226+
private readonly mapKeyConverter: (key: unknown) => MapKeyType;
216227

217228
private totalPos = 0;
218229
private pos = 0;
@@ -236,6 +247,7 @@ export class Decoder<ContextType = undefined> {
236247
this.maxMapLength = options?.maxMapLength ?? UINT32_MAX;
237248
this.maxExtLength = options?.maxExtLength ?? UINT32_MAX;
238249
this.keyDecoder = options?.keyDecoder !== undefined ? options.keyDecoder : sharedCachedKeyDecoder;
250+
this.mapKeyConverter = options?.mapKeyConverter ?? mapKeyConverter;
239251
}
240252

241253
private clone(): Decoder<ContextType> {
@@ -631,14 +643,11 @@ export class Decoder<ContextType = undefined> {
631643
continue DECODE;
632644
}
633645
} else if (state.type === STATE_MAP_KEY) {
634-
if (!isValidMapKeyType(object)) {
635-
throw new DecodeError("The type of key must be string or number but " + typeof object);
636-
}
637646
if (object === "__proto__") {
638647
throw new DecodeError("The key __proto__ is not allowed");
639648
}
640649

641-
state.key = object;
650+
state.key = this.mapKeyConverter(object);
642651
state.type = STATE_MAP_VALUE;
643652
continue DECODE;
644653
} else {

test/decodeAsync.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ describe("decodeAsync", () => {
3636
assert.deepStrictEqual(object, { "foo": "bar" });
3737
});
3838

39+
it("decodes fixmap {'[1, 2]': 'baz'} with custom map key converter", async () => {
40+
const createStream = async function* () {
41+
yield [0x81]; // fixmap size=1
42+
yield encode([1, 2]);
43+
yield encode("baz");
44+
};
45+
46+
const object = await decodeAsync(createStream(), {
47+
mapKeyConverter: (key) => JSON.stringify(key),
48+
});
49+
50+
const key = JSON.stringify([1, 2]);
51+
assert.deepStrictEqual(object, { [key]: "baz" });
52+
});
53+
3954
it("decodes multi-byte integer byte-by-byte", async () => {
4055
const createStream = async function* () {
4156
yield [0xcd]; // uint 16

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