Skip to content

Commit 3cf5d2d

Browse files
committed
fix: Support encoding map-as-arrays
1 parent cda77e3 commit 3cf5d2d

File tree

1 file changed

+46
-13
lines changed

1 file changed

+46
-13
lines changed

src/Encoder.ts

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,19 @@ export class Encoder<ContextType = undefined> {
297297
if (ext != null) {
298298
this.encodeExtension(ext);
299299
} else if (Array.isArray(object)) {
300-
this.encodeArray(object, depth);
300+
// We need to peek the first element to determine if it's an array or a map.
301+
const firstElement = object[0];
302+
303+
if (
304+
firstElement != null &&
305+
typeof firstElement === "object" &&
306+
"key" in firstElement &&
307+
"value" in firstElement
308+
) {
309+
this.encodeMapArray(object as [{ key: string | number; value: unknown }], depth);
310+
} else {
311+
this.encodeArray(object, depth);
312+
}
301313
} else if (ArrayBuffer.isView(object)) {
302314
this.encodeBinary(object);
303315
} else if (typeof object === "object") {
@@ -350,18 +362,6 @@ export class Encoder<ContextType = undefined> {
350362
}
351363
}
352364

353-
private countWithoutUndefined(object: Record<string, unknown>, keys: ReadonlyArray<string>): number {
354-
let count = 0;
355-
356-
for (const key of keys) {
357-
if (object[key] !== undefined) {
358-
count++;
359-
}
360-
}
361-
362-
return count;
363-
}
364-
365365
private encodeMap(object: Record<string, unknown>, depth: number) {
366366
const keys = Object.keys(object);
367367
if (this.sortKeys) {
@@ -395,6 +395,39 @@ export class Encoder<ContextType = undefined> {
395395
}
396396
}
397397

398+
private encodeMapArray(object: [{ key: string | number; value: unknown }], depth: number) {
399+
const keys = object.map((pair) => pair.key);
400+
if (this.sortKeys) {
401+
keys.sort();
402+
}
403+
404+
const size = keys.length;
405+
406+
if (size < 16) {
407+
// fixmap
408+
this.writeU8(0x80 + size);
409+
} else if (size < 0x10000) {
410+
// map 16
411+
this.writeU8(0xde);
412+
this.writeU16(size);
413+
} else if (size < 0x100000000) {
414+
// map 32
415+
this.writeU8(0xdf);
416+
this.writeU32(size);
417+
} else {
418+
throw new Error(`Too large map object: ${size}`);
419+
}
420+
421+
for (const pair of object) {
422+
const value = pair.value;
423+
424+
if (!(this.ignoreUndefined && value === undefined)) {
425+
this.encodeString(pair.key.toString());
426+
this.doEncode(value, depth + 1);
427+
}
428+
}
429+
}
430+
398431
private encodeExtension(ext: ExtData) {
399432
const size = ext.data.length;
400433
if (size === 1) {

0 commit comments

Comments
 (0)