Skip to content

Commit 7543d04

Browse files
committed
fix: throw when encoding a BigInt outside int64/uint64 range
With `useBigInt64: true`, `encodeBigInt64` branched only on sign and called the native `DataView.setBigUint64`/`setBigInt64`, which truncate mod 2^64 without throwing. A BigInt outside the representable range was silently corrupted on encode: encode(2n ** 64n, { useBigInt64: true }) // decoded as 0n encode(2n ** 64n + 1n, { useBigInt64: true }) // decoded as 1n encode(-(2n ** 63n) - 1n,{ useBigInt64: true }) // sign flip: 9223372036854775807n The MessagePack int family is fixed 64-bit, so the only representable range is the union of int64 and uint64: [-2^63, 2^64 - 1]. Add a range check that throws for anything outside it, matching how the encoder already rejects other unrepresentable inputs (too-long strings, too-large arrays/maps/binaries).
1 parent dd2190b commit 7543d04

2 files changed

Lines changed: 29 additions & 0 deletions

File tree

src/Encoder.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,9 @@ export class Encoder<ContextType = undefined> {
291291
}
292292

293293
private encodeBigInt64(object: bigint): void {
294+
if (object < -(BigInt(2) ** BigInt(63)) || object > BigInt(2) ** BigInt(64) - BigInt(1)) {
295+
throw new Error(`Cannot encode BigInt as int64/uint64 because it is out of range: ${object}`);
296+
}
294297
if (object >= BigInt(0)) {
295298
// uint 64
296299
this.writeU8(0xcf);

test/bigint64.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,30 @@ describe("useBigInt64: true", () => {
3535
const encoded = encode(value, { useBigInt64: true });
3636
assert.deepStrictEqual(decode(encoded, { useBigInt64: true }), value);
3737
});
38+
39+
it("round-trips the boundary values of int64/uint64", () => {
40+
const values = [
41+
BigInt(0),
42+
BigInt(42),
43+
BigInt(2) ** BigInt(63) - BigInt(1), // max int64
44+
-(BigInt(2) ** BigInt(63)), // min int64
45+
BigInt(2) ** BigInt(64) - BigInt(1), // max uint64
46+
];
47+
for (const value of values) {
48+
const encoded = encode(value, { useBigInt64: true });
49+
assert.deepStrictEqual(decode(encoded, { useBigInt64: true }), value);
50+
}
51+
});
52+
53+
it("throws when a bigint is out of the int64/uint64 range", () => {
54+
const values = [
55+
BigInt(2) ** BigInt(64), // uint64 max + 1
56+
BigInt(2) ** BigInt(64) + BigInt(1),
57+
-(BigInt(2) ** BigInt(63)) - BigInt(1), // int64 min - 1
58+
-(BigInt(2) ** BigInt(100)),
59+
];
60+
for (const value of values) {
61+
assert.throws(() => encode(value, { useBigInt64: true }), /out of range/);
62+
}
63+
});
3864
});

0 commit comments

Comments
 (0)