feat(serializer): add aot serializer
This commit is contained in:
@@ -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);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user