Skip to content

Commit 7dc4e5e

Browse files
committed
feat: big number support for cbor
1 parent e5bb1e2 commit 7dc4e5e

File tree

3 files changed

+61
-6
lines changed

3 files changed

+61
-6
lines changed

packages/core/src/submodules/cbor/cbor-decode.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,34 @@ export function decode(at: Uint32, to: Uint32): CborValueType {
119119
_offset = offset;
120120
return castBigInt(negativeInt);
121121
} else {
122-
const value = decode(at + offset, to);
123-
const valueOffset = _offset;
122+
if (minor === 2) {
123+
const length = decode(at + offset, to);
124+
const valueOffset = _offset;
125+
_offset = offset + valueOffset;
126+
127+
const bigIntBytes = payload.slice(at + valueOffset, at + valueOffset + length);
128+
129+
console.log({
130+
length,
131+
bytes: Uint8Array.from(bigIntBytes),
132+
});
133+
134+
let b = BigInt(0);
135+
for (let i = bigIntBytes.length - 1; i >= 0; --i) {
136+
b = (b << BigInt(8)) | BigInt(bigIntBytes[i]);
137+
}
138+
139+
return b;
140+
} else if (minor === 3) {
141+
return BigInt(3);
142+
} else {
143+
const value = decode(at + offset, to);
144+
const valueOffset = _offset;
145+
146+
_offset = offset + valueOffset;
124147

125-
_offset = offset + valueOffset;
126-
return tag({ tag: castBigInt(unsignedInt), value });
148+
return tag({ tag: castBigInt(unsignedInt), value });
149+
}
127150
}
128151
case majorUtf8String:
129152
case majorMap:

packages/core/src/submodules/cbor/cbor-encode.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { fromUtf8 } from "@smithy/util-utf8";
22

33
import {
4+
alloc,
45
CborMajorType,
56
extendedFloat16,
67
extendedFloat32,
@@ -19,7 +20,6 @@ import {
1920
tagSymbol,
2021
Uint64,
2122
} from "./cbor-types";
22-
import { alloc } from "./cbor-types";
2323

2424
const USE_BUFFER = typeof Buffer !== "undefined";
2525

@@ -152,10 +152,30 @@ export function encode(_input: any): void {
152152
data[cursor++] = (major << 5) | extendedFloat32;
153153
dataView.setUint32(cursor, n);
154154
cursor += 4;
155-
} else {
155+
} else if (n < BigInt("18446744073709551616")) {
156156
data[cursor++] = (major << 5) | extendedFloat64;
157157
dataView.setBigUint64(cursor, value);
158158
cursor += 8;
159+
} else {
160+
// refer to https://www.rfc-editor.org/rfc/rfc8949.html#name-bignums
161+
const binaryBigInt = input.toString(2);
162+
const bigIntBytes = new Uint8Array(Math.ceil(binaryBigInt.length / 8));
163+
let b = input;
164+
let i = 0;
165+
while (bigIntBytes.byteLength - ++i >= 0) {
166+
bigIntBytes[bigIntBytes.byteLength - i] = Number(b & BigInt(255));
167+
b >>= BigInt(8);
168+
}
169+
ensureSpace(bigIntBytes.byteLength * 2);
170+
data[cursor++] = nonNegative ? 0b110_00010 : 0b110_00011;
171+
172+
if (USE_BUFFER) {
173+
encodeHeader(majorUtf8String, Buffer.byteLength(bigIntBytes));
174+
} else {
175+
encodeHeader(majorUtf8String, bigIntBytes.byteLength);
176+
}
177+
data.set(bigIntBytes, cursor);
178+
cursor += bigIntBytes.byteLength;
159179
}
160180
continue;
161181
} else if (input === null) {

packages/core/src/submodules/cbor/cbor.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,18 @@ describe("cbor", () => {
226226
};
227227

228228
describe("locally curated scenarios", () => {
229+
it.only("should round-trip bigInteger to major 6 with tag 2 or tag 3", () => {
230+
const bigInt = BigInt("1".repeat(100));
231+
const serialized = cbor.serialize(bigInt);
232+
233+
const major = serialized[0] >> 5;
234+
235+
const deserialized = cbor.deserialize(serialized);
236+
expect(deserialized).toEqual(bigInt);
237+
});
238+
239+
it("should round-trip NumericValue to major 6 with tag 4", () => {});
240+
229241
it("should throw an error if serializing a tag with missing properties", () => {
230242
expect(() =>
231243
cbor.serialize({

0 commit comments

Comments
 (0)