ASCILINE/experiments/check_vectors.js
Nate e3f282910d feat: adaptive raw/zlib/delta frame codec (opt-in, backward compatible)
The binary protocol re-sent the full grid every frame. This adds an opt-in
per-frame codec that picks the smallest of three encodings and tags it in a
1-byte header, without changing the rendered output:

  0 RAW    framebuffer as-is (legacy)
  1 ZLIB   zlib(framebuffer)
  2 DELTA  only the cells changed since the previous frame, patched on top

Clients opt in via /ws?codec=adaptive; omitting it yields the original protocol
byte-for-byte, so existing clients are unaffected. A keyframe is forced
periodically for resync. codec.js is shared by the browser and the Node test,
so the shipped decode path is the tested one.

Optional --quality {lossless,high,balanced,low} enables lossy temporal delta
(conditional replenishment): a colour cell is only re-sent once it drifts past a
tolerance from what the viewer already sees; the character plane stays exact.
Default lossless = bit-exact.

Measured wire savings (mode 5, 200x80): static screen 0.3% of legacy (~375x),
pixel mode 11.6%, high-motion 63% (never worse). Encoder tuned (zlib level 3,
smart candidate selection) to stay well under the frame budget.

Verified bit-exact two independent ways: Python->Node vectors and a live
adaptive-vs-legacy WebSocket diff. (A fuller mutation + Autobahn conformance
harness exists on request.)
2026-06-13 02:22:29 -04:00

55 lines
2.2 KiB
JavaScript

/**
* Decode the Python-generated test vectors with the SHIPPED codec.js and verify
* every frame matches the ground-truth framebuffer byte-for-byte.
*
* This exercises the real cross-language risk surface: zlib (Python) ->
* DecompressionStream (JS), little-endian delta indices, and delta patching.
*/
const fs = require('fs');
const path = require('path');
const codec = require('../codec.js');
function readChunks(buf) {
const out = [];
let off = 0;
while (off + 4 <= buf.length) {
const len = buf.readUInt32BE(off); off += 4;
out.push(new Uint8Array(buf.subarray(off, off + len))); off += len;
}
return out;
}
async function checkDir(name) {
const dir = path.join(__dirname, 'vectors', name);
const meta = JSON.parse(fs.readFileSync(path.join(dir, 'meta.json')));
const msgs = readChunks(fs.readFileSync(path.join(dir, 'adaptive.bin')));
const truth = readChunks(fs.readFileSync(path.join(dir, 'truth.bin')));
const dec = codec.makeDecoder(meta.cellBytes);
let mismatches = 0, firstBad = null;
for (let i = 0; i < msgs.length; i++) {
const { frame } = await dec.decode(msgs[i]);
const want = truth[i];
if (frame.length !== want.length) { mismatches++; firstBad ??= [i, 'len', want.length, frame.length]; continue; }
for (let j = 0; j < want.length; j++) {
if (frame[j] !== want[j]) { mismatches++; firstBad ??= [i, 'byte@' + j, want[j], frame[j]]; break; }
}
}
const pct = (100 * meta.adaptiveBytes / meta.legacyBytes).toFixed(1);
const status = mismatches === 0 ? 'PASS bit-exact' : `FAIL (${mismatches})`;
console.log(
`${name.padEnd(20)} ${String(msgs.length).padStart(3)} frames ` +
`${status.padEnd(16)} wire ${pct}% of legacy` +
(firstBad ? ` firstBad=${JSON.stringify(firstBad)}` : '')
);
return mismatches === 0;
}
(async () => {
const names = fs.readdirSync(path.join(__dirname, 'vectors'));
console.log('Decoding with codec.js, comparing to ground truth:\n');
let allPass = true;
for (const n of names) allPass = (await checkDir(n)) && allPass;
console.log('\n' + (allPass ? 'ALL VECTORS BIT-EXACT' : 'SOME VECTORS FAILED'));
process.exit(allPass ? 0 : 1);
})().catch((e) => { console.error(e); process.exit(2); });