feat(serializer): add aot serializer

This commit is contained in:
2026-05-21 09:11:51 +00:00
parent 6f417ba514
commit f327e64a6a
30 changed files with 6720 additions and 0 deletions
+175
View File
@@ -0,0 +1,175 @@
import { test, expect } from 'vitest';
import { Reader, Writer } from '../plugin/io.ts';
function roundtrip<T>(write: (w: Writer) => void, read: (r: Reader) => T): T {
const w = new Writer(16);
write(w);
return read(new Reader(w.bytes()));
}
test('u8/u16/u32 round-trip with boundary values', () => {
for (const v of [0, 1, 127, 128, 255]) {
expect(roundtrip((w) => w.u8(v), (r) => r.u8())).toBe(v);
}
for (const v of [0, 1, 0xff, 0x100, 0xffff]) {
expect(roundtrip((w) => w.u16(v), (r) => r.u16())).toBe(v);
}
for (const v of [0, 1, 0xffff, 0x10000, 0xffffffff]) {
expect(roundtrip((w) => w.u32(v), (r) => r.u32())).toBe(v);
}
});
test('i16/i32 signed round-trip including negatives', () => {
for (const v of [-32768, -1, 0, 1, 32767]) {
expect(roundtrip((w) => w.i16(v), (r) => r.i16())).toBe(v);
}
for (const v of [-2147483648, -1, 0, 1, 2147483647]) {
expect(roundtrip((w) => w.i32(v), (r) => r.i32())).toBe(v);
}
});
test('f32/f64 round-trip including special values', () => {
for (const v of [0, -0, 1, -1, 3.14159, Infinity, -Infinity]) {
expect(roundtrip((w) => w.f64(v), (r) => r.f64())).toBe(v);
}
expect(Number.isNaN(roundtrip((w) => w.f64(NaN), (r) => r.f64()))).toBe(true);
for (const v of [0, 1, -1, 0.5, -0.5, 2.0, 1024, -1024, 0.125]) {
expect(roundtrip((w) => w.f32(v), (r) => r.f32())).toBe(v);
}
});
test('varu32 LEB128 round-trip including 5-byte values', () => {
const cases = [0, 1, 127, 128, 16383, 16384, 0x1fffff, 0x10000000, 0xffffffff];
for (const v of cases) {
expect(roundtrip((w) => w.varu32(v), (r) => r.varu32())).toBe(v);
}
});
test('varu32 byte lengths follow LEB128 spec', () => {
const sizes: Array<[number, number]> = [
[0, 1],
[127, 1],
[128, 2],
[16383, 2],
[16384, 3],
[0x1fffff, 3],
[0x200000, 4],
[0xfffffff, 4],
[0x10000000, 5],
];
for (const [v, expectedSize] of sizes) {
const w = new Writer(16);
w.varu32(v);
expect(w.pos, `varu32(${v}) should be ${expectedSize} bytes`).toBe(expectedSize);
}
});
test('vari32 zigzag round-trip', () => {
const cases = [0, -1, 1, -2, 2, -64, 63, -8192, 8191, -2147483648, 2147483647];
for (const v of cases) {
expect(roundtrip((w) => w.vari32(v), (r) => r.vari32())).toBe(v);
}
});
test('varu53 round-trip up to 2^53', () => {
const cases = [
0,
1,
127,
128,
2 ** 16,
2 ** 32 - 1,
2 ** 32,
2 ** 40,
Number.MAX_SAFE_INTEGER,
];
for (const v of cases) {
expect(roundtrip((w) => w.varu53(v), (r) => r.varu53())).toBe(v);
}
});
test('vari53 round-trip', () => {
const cases = [
0,
-1,
1,
-(2 ** 30),
2 ** 30,
Number.MIN_SAFE_INTEGER,
Number.MAX_SAFE_INTEGER,
];
for (const v of cases) {
expect(roundtrip((w) => w.vari53(v), (r) => r.vari53())).toBe(v);
}
});
test('varbu/varbi bigint round-trip', () => {
const u: bigint[] = [0n, 1n, 127n, 128n, 1n << 32n, 1n << 63n, (1n << 64n) - 1n];
for (const v of u) {
expect(roundtrip((w) => w.varbu(v), (r) => r.varbu())).toBe(v);
}
const s: bigint[] = [0n, -1n, 1n, -(1n << 32n), 1n << 32n, -(1n << 63n), (1n << 63n) - 1n];
for (const v of s) {
expect(roundtrip((w) => w.varbi(v), (r) => r.varbi())).toBe(v);
}
});
test('str round-trip ASCII short and long', () => {
for (const s of ['', 'a', 'hello', 'BTC-USD', 'abcdefghijklmnopqrstuvwxyz']) {
expect(roundtrip((w) => w.str(s), (r) => r.str())).toBe(s);
}
const long = 'x'.repeat(200);
expect(roundtrip((w) => w.str(long), (r) => r.str())).toBe(long);
});
test('str round-trip non-ASCII', () => {
for (const s of ['héllo', 'café', '日本語', '🚀', 'mix αβγ 漢字 🎉']) {
expect(roundtrip((w) => w.str(s), (r) => r.str())).toBe(s);
}
});
test('bytes round-trip', () => {
const data = new Uint8Array([1, 2, 3, 4, 255, 0, 128]);
const result = roundtrip(
(w) => w.bytesPrefixed(data),
(r) => r.bytesPrefixed(),
);
expect(Array.from(result)).toEqual(Array.from(data));
});
test('Writer grows beyond initial capacity', () => {
const w = new Writer(4);
for (let i = 0; i < 1000; i++) w.u8(i & 0xff);
expect(w.pos).toBe(1000);
expect(w.buf.byteLength).toBeGreaterThanOrEqual(1000);
});
test('Writer reset reuses buffer', () => {
const w = new Writer(16);
w.u32(42);
const cap1 = w.buf.byteLength;
w.reset();
expect(w.pos).toBe(0);
w.u32(99);
expect(w.buf.byteLength).toBe(cap1);
});
test('multi-write/multi-read interleaved', () => {
const w = new Writer(16);
w.u8(1);
w.f64(3.14);
w.str('hi');
w.varu53(42);
const r = new Reader(w.bytes());
expect(r.u8()).toBe(1);
expect(r.f64()).toBe(3.14);
expect(r.str()).toBe('hi');
expect(r.varu53()).toBe(42);
});
test('bool round-trip', () => {
for (const v of [true, false]) {
expect(roundtrip((w) => w.bool(v), (r) => r.bool())).toBe(v);
}
});