Skip to content

Commit e4fe80d

Browse files
committed
feat: compatible mode for molecule decode
1 parent 8e1fcf9 commit e4fe80d

File tree

1 file changed

+39
-26
lines changed

1 file changed

+39
-26
lines changed

packages/core/src/molecule/codec.ts

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,19 @@ import {
1818

1919
export type CodecLike<Encodable, Decoded = Encodable> = {
2020
readonly encode: (encodable: Encodable) => Bytes;
21-
readonly decode: (decodable: BytesLike) => Decoded;
21+
readonly decode: (
22+
decodable: BytesLike,
23+
config?: { compatible?: boolean },
24+
) => Decoded;
2225
readonly byteLength?: number;
2326
};
2427
export class Codec<Encodable, Decoded = Encodable> {
2528
constructor(
2629
public readonly encode: (encodable: Encodable) => Bytes,
27-
public readonly decode: (decodable: BytesLike) => Decoded,
30+
public readonly decode: (
31+
decodable: BytesLike,
32+
config?: { compatible?: boolean },
33+
) => Decoded,
2834
public readonly byteLength?: number, // if provided, treat codec as fixed length
2935
) {}
3036

@@ -43,7 +49,7 @@ export class Codec<Encodable, Decoded = Encodable> {
4349
}
4450
return encoded;
4551
},
46-
(decodable) => {
52+
(decodable, config = { compatible: false }) => {
4753
const decodableBytes = bytesFrom(decodable);
4854
if (
4955
byteLength !== undefined &&
@@ -53,7 +59,7 @@ export class Codec<Encodable, Decoded = Encodable> {
5359
`Codec.decode: expected byte length ${byteLength}, got ${decodableBytes.byteLength}`,
5460
);
5561
}
56-
return decode(decodable);
62+
return decode(decodable, config);
5763
},
5864
byteLength,
5965
);
@@ -69,10 +75,10 @@ export class Codec<Encodable, Decoded = Encodable> {
6975
return new Codec(
7076
(encodable) =>
7177
this.encode((inMap ? inMap(encodable) : encodable) as Encodable),
72-
(buffer) =>
78+
(buffer, config = { compatible: false }) =>
7379
(outMap
74-
? outMap(this.decode(buffer))
75-
: this.decode(buffer)) as NewDecoded,
80+
? outMap(this.decode(buffer, config))
81+
: this.decode(buffer, config)) as NewDecoded,
7682
this.byteLength,
7783
);
7884
}
@@ -128,7 +134,7 @@ export function fixedItemVec<Encodable, Decoded>(
128134
throw new Error(`fixedItemVec(${e?.toString()})`);
129135
}
130136
},
131-
decode(buffer) {
137+
decode(buffer, config) {
132138
const value = bytesFrom(buffer);
133139
if (value.byteLength < 4) {
134140
throw new Error(
@@ -147,7 +153,10 @@ export function fixedItemVec<Encodable, Decoded>(
147153
const decodedArray: Array<Decoded> = [];
148154
for (let offset = 4; offset < byteLength; offset += itemByteLength) {
149155
decodedArray.push(
150-
itemCodec.decode(value.slice(offset, offset + itemByteLength)),
156+
itemCodec.decode(
157+
value.slice(offset, offset + itemByteLength),
158+
config,
159+
),
151160
);
152161
}
153162
return decodedArray;
@@ -185,7 +194,7 @@ export function dynItemVec<Encodable, Decoded>(
185194
throw new Error(`dynItemVec(${e?.toString()})`);
186195
}
187196
},
188-
decode(buffer) {
197+
decode(buffer, config) {
189198
const value = bytesFrom(buffer);
190199
if (value.byteLength < 4) {
191200
throw new Error(
@@ -215,7 +224,7 @@ export function dynItemVec<Encodable, Decoded>(
215224
const start = offsets[index];
216225
const end = offsets[index + 1];
217226
const itemBuffer = value.slice(start, end);
218-
decodedArray.push(itemCodec.decode(itemBuffer));
227+
decodedArray.push(itemCodec.decode(itemBuffer, config));
219228
}
220229
return decodedArray;
221230
} catch (e) {
@@ -259,13 +268,13 @@ export function option<Encodable, Decoded>(
259268
throw new Error(`option(${e?.toString()})`);
260269
}
261270
},
262-
decode(buffer) {
271+
decode(buffer, config) {
263272
const value = bytesFrom(buffer);
264273
if (value.byteLength === 0) {
265274
return undefined;
266275
}
267276
try {
268-
return innerCodec.decode(buffer);
277+
return innerCodec.decode(buffer, config);
269278
} catch (e) {
270279
throw new Error(`option(${e?.toString()})`);
271280
}
@@ -290,7 +299,7 @@ export function byteVec<Encodable, Decoded>(
290299
throw new Error(`byteVec(${e?.toString()})`);
291300
}
292301
},
293-
decode(buffer) {
302+
decode(buffer, config) {
294303
const value = bytesFrom(buffer);
295304
if (value.byteLength < 4) {
296305
throw new Error(
@@ -304,7 +313,7 @@ export function byteVec<Encodable, Decoded>(
304313
);
305314
}
306315
try {
307-
return codec.decode(value.slice(4));
316+
return codec.decode(value.slice(4), config);
308317
} catch (e: unknown) {
309318
throw new Error(`byteVec(${e?.toString()})`);
310319
}
@@ -371,15 +380,15 @@ export function table<
371380
const packedTotalSize = uint32To(header.length + body.length + 4);
372381
return bytesConcat(packedTotalSize, header, body);
373382
},
374-
decode(buffer) {
383+
decode(buffer, config) {
375384
const value = bytesFrom(buffer);
376385
if (value.byteLength < 4) {
377386
throw new Error(
378387
`table: too short buffer, expected at least 4 bytes, but got ${value.byteLength}`,
379388
);
380389
}
381390
const byteLength = uint32From(value.slice(0, 4));
382-
if (byteLength !== value.byteLength) {
391+
if (byteLength !== value.byteLength && !config?.compatible) {
383392
throw new Error(
384393
`table: invalid buffer size, expected ${byteLength}, but got ${value.byteLength}`,
385394
);
@@ -397,9 +406,13 @@ export function table<
397406
const payload = value.slice(start, end);
398407
try {
399408
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
400-
Object.assign(object, { [field]: codec.decode(payload) });
401-
} catch (e: unknown) {
402-
throw new Error(`table.${field}(${e?.toString()})`);
409+
Object.assign(object, { [field]: codec.decode(payload, config) });
410+
} catch (_e: unknown) {
411+
if (config?.compatible) {
412+
Object.assign(object, { [field]: null });
413+
} else {
414+
throw new Error(`table.${field}(${_e?.toString()})`);
415+
}
403416
}
404417
}
405418
return object as Decoded;
@@ -466,7 +479,7 @@ export function union<T extends Record<string, CodecLike<any, any>>>(
466479
throw new Error(`union.(${typeStr})(${e?.toString()})`);
467480
}
468481
},
469-
decode(buffer) {
482+
decode(buffer, config) {
470483
const value = bytesFrom(buffer);
471484
const fieldIndex = uint32From(value.slice(0, 4));
472485
const keys = Object.keys(codecLayout);
@@ -496,7 +509,7 @@ export function union<T extends Record<string, CodecLike<any, any>>>(
496509
return {
497510
type: field,
498511
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
499-
value: codecLayout[field].decode(value.slice(4)),
512+
value: codecLayout[field].decode(value.slice(4), config),
500513
} as UnionDecoded<T>;
501514
},
502515
});
@@ -535,15 +548,15 @@ export function struct<
535548

536549
return bytesFrom(bytes);
537550
},
538-
decode(buffer) {
551+
decode(buffer, config) {
539552
const value = bytesFrom(buffer);
540553
const object = {};
541554
let offset = 0;
542555
Object.entries(codecLayout).forEach(([key, codec]) => {
543556
const payload = value.slice(offset, offset + codec.byteLength!);
544557
try {
545558
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
546-
Object.assign(object, { [key]: codec.decode(payload) });
559+
Object.assign(object, { [key]: codec.decode(payload, config) });
547560
} catch (e: unknown) {
548561
throw new Error(`struct.${key}(${(e as Error).toString()})`);
549562
}
@@ -583,7 +596,7 @@ export function array<Encodable, Decoded>(
583596
throw new Error(`array(${e?.toString()})`);
584597
}
585598
},
586-
decode(buffer) {
599+
decode(buffer, config) {
587600
const value = bytesFrom(buffer);
588601
if (value.byteLength != byteLength) {
589602
throw new Error(
@@ -594,7 +607,7 @@ export function array<Encodable, Decoded>(
594607
const result: Array<Decoded> = [];
595608
for (let i = 0; i < value.byteLength; i += itemCodec.byteLength!) {
596609
result.push(
597-
itemCodec.decode(value.slice(i, i + itemCodec.byteLength!)),
610+
itemCodec.decode(value.slice(i, i + itemCodec.byteLength!), config),
598611
);
599612
}
600613
return result;

0 commit comments

Comments
 (0)