rs.test.ts

2.9 KB · raw

1import { describe, it, expect } from "vitest";2import {3  reedSolomonEncode,4  reedSolomonDecode,5  RS_BLOCK_DATA,6  RS_BLOCK_PARITY,7  RS_BLOCK_TOTAL,8} from "./rs";910function randomBytes(n: number): Uint8Array {11  const out = new Uint8Array(n);12  for (let i = 0; i < n; i++) out[i] = (Math.random() * 256) | 0;13  return out;14}1516function corruptBytes(buf: Uint8Array, n: number): Uint8Array {17  const out = new Uint8Array(buf);18  const positions = new Set<number>();19  while (positions.size < n) {20    positions.add((Math.random() * buf.length) | 0);21  }22  for (const p of positions) out[p] ^= ((Math.random() * 255) | 0) + 1;23  return out;24}2526describe("Reed-Solomon RS(255,178)", () => {27  it("constants are coherent", () => {28    expect(RS_BLOCK_DATA + RS_BLOCK_PARITY).toBe(RS_BLOCK_TOTAL);29    expect(RS_BLOCK_TOTAL).toBe(255);30  });3132  it("round-trips a small payload (no errors)", () => {33    const data = new Uint8Array([0x4d, 0x01, 0x05, 0x00, 1, 2, 3, 4, 5]);34    const encoded = reedSolomonEncode(data);35    expect(encoded.length).toBe(RS_BLOCK_TOTAL); // single block36    const decoded = reedSolomonDecode(encoded, data.length);37    expect(Array.from(decoded)).toEqual(Array.from(data));38  });3940  it("round-trips a multi-block payload (no errors)", () => {41    const data = randomBytes(RS_BLOCK_DATA * 3 + 17);42    const encoded = reedSolomonEncode(data);43    expect(encoded.length).toBe(RS_BLOCK_TOTAL * 4); // 4 blocks44    const decoded = reedSolomonDecode(encoded, data.length);45    expect(Array.from(decoded)).toEqual(Array.from(data));46  });4748  it("corrects 1 byte error per block", () => {49    const data = randomBytes(RS_BLOCK_DATA);50    const encoded = reedSolomonEncode(data);51    const corrupted = corruptBytes(encoded, 1);52    const decoded = reedSolomonDecode(corrupted, data.length);53    expect(Array.from(decoded)).toEqual(Array.from(data));54  });5556  it("corrects up to floor(parity/2) byte errors per block", () => {57    const data = randomBytes(RS_BLOCK_DATA);58    const encoded = reedSolomonEncode(data);59    // RS_BLOCK_PARITY=77, can correct up to 38 errors per block60    const corrupted = corruptBytes(encoded, 38);61    const decoded = reedSolomonDecode(corrupted, data.length);62    expect(Array.from(decoded)).toEqual(Array.from(data));63  });6465  it("fails when more than floor(parity/2) errors per block", () => {66    const data = randomBytes(RS_BLOCK_DATA);67    const encoded = reedSolomonEncode(data);68    const corrupted = corruptBytes(encoded, 50); // > 3869    expect(() => reedSolomonDecode(corrupted, data.length)).toThrow(/uncorrectable/);70  });7172  it("handles empty payload", () => {73    const encoded = reedSolomonEncode(new Uint8Array(0));74    expect(encoded.length).toBe(0);75    const decoded = reedSolomonDecode(encoded, 0);76    expect(decoded.length).toBe(0);77  });7879  it("rejects malformed received length", () => {80    expect(() =>81      reedSolomonDecode(new Uint8Array(RS_BLOCK_TOTAL + 1), 10),82    ).toThrow(/not a multiple/);83  });84});