import { describe, it, expect } from "vitest"; import { reedSolomonEncode, reedSolomonDecode, RS_BLOCK_DATA, RS_BLOCK_PARITY, RS_BLOCK_TOTAL, } from "./rs"; function randomBytes(n: number): Uint8Array { const out = new Uint8Array(n); for (let i = 0; i < n; i++) out[i] = (Math.random() * 256) | 0; return out; } function corruptBytes(buf: Uint8Array, n: number): Uint8Array { const out = new Uint8Array(buf); const positions = new Set(); while (positions.size < n) { positions.add((Math.random() * buf.length) | 0); } for (const p of positions) out[p] ^= ((Math.random() * 255) | 0) + 1; return out; } describe("Reed-Solomon RS(255,178)", () => { it("constants are coherent", () => { expect(RS_BLOCK_DATA + RS_BLOCK_PARITY).toBe(RS_BLOCK_TOTAL); expect(RS_BLOCK_TOTAL).toBe(255); }); it("round-trips a small payload (no errors)", () => { const data = new Uint8Array([0x4d, 0x01, 0x05, 0x00, 1, 2, 3, 4, 5]); const encoded = reedSolomonEncode(data); expect(encoded.length).toBe(RS_BLOCK_TOTAL); // single block const decoded = reedSolomonDecode(encoded, data.length); expect(Array.from(decoded)).toEqual(Array.from(data)); }); it("round-trips a multi-block payload (no errors)", () => { const data = randomBytes(RS_BLOCK_DATA * 3 + 17); const encoded = reedSolomonEncode(data); expect(encoded.length).toBe(RS_BLOCK_TOTAL * 4); // 4 blocks const decoded = reedSolomonDecode(encoded, data.length); expect(Array.from(decoded)).toEqual(Array.from(data)); }); it("corrects 1 byte error per block", () => { const data = randomBytes(RS_BLOCK_DATA); const encoded = reedSolomonEncode(data); const corrupted = corruptBytes(encoded, 1); const decoded = reedSolomonDecode(corrupted, data.length); expect(Array.from(decoded)).toEqual(Array.from(data)); }); it("corrects up to floor(parity/2) byte errors per block", () => { const data = randomBytes(RS_BLOCK_DATA); const encoded = reedSolomonEncode(data); // RS_BLOCK_PARITY=77, can correct up to 38 errors per block const corrupted = corruptBytes(encoded, 38); const decoded = reedSolomonDecode(corrupted, data.length); expect(Array.from(decoded)).toEqual(Array.from(data)); }); it("fails when more than floor(parity/2) errors per block", () => { const data = randomBytes(RS_BLOCK_DATA); const encoded = reedSolomonEncode(data); const corrupted = corruptBytes(encoded, 50); // > 38 expect(() => reedSolomonDecode(corrupted, data.length)).toThrow(/uncorrectable/); }); it("handles empty payload", () => { const encoded = reedSolomonEncode(new Uint8Array(0)); expect(encoded.length).toBe(0); const decoded = reedSolomonDecode(encoded, 0); expect(decoded.length).toBe(0); }); it("rejects malformed received length", () => { expect(() => reedSolomonDecode(new Uint8Array(RS_BLOCK_TOTAL + 1), 10), ).toThrow(/not a multiple/); }); });