open-standards/wai/wai-web/demo/verify_int_payload_wasm.mjs
Transaction Science e2a93241e7 wai: keep loaded parameter sets between decodes, keyed by the SHA-256 of their bytes (WIP)
A decode through the integer operator set lowers its set and loads each graph,
the bounds pass included, on every call. int_set_cache::LoadedSets keeps a set
once it has loaded, keyed by (capability, lowering, profile, SHA-256 of the
listing of the members read), within a byte budget (256 MiB, LRU, 0 = off).
int_payload::decode_int / declared_cost and int_synth::decode_from_spec take
the load step from it; every other check runs as before. A refused set is
never kept. wai_ops::Graph::heap_bytes counts an entry's graphs.

Committed before the first build; results follow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:07:27 -04:00

92 lines
4.8 KiB
JavaScript

// The wasm32 decode of the integer payloads (extensions/integer-payloads.md) equals
// the native decode: each committed WIV1, WIH1, WIA1 and WIS1 payload, decoded by
// decodeWiv1 / decodeWih1 / decodeWia1 / decodeWis1 in the browser crate built for
// wasm32, must give the SHA-256 its vector's sha256.json sidecar states, and a
// tool's JSON form must be refused with bad_magic. A second decode through the same
// set is answered from the page's store of loaded sets (loadedSetStats: one more hit,
// no more misses) with the same bytes, and a budget of 0 keeps and looks up nothing.
//
// node verify_int_payload_wasm.mjs [path/to/wai_web.js] [path/to/wai-rs/tests/vectors]
//
// Exit 0 iff every check passes.
import { readFileSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { dirname, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const here = dirname(fileURLToPath(import.meta.url));
const pkg = resolve(here, process.argv[2] ?? './pkg-node/wai_web.js');
const vectors = resolve(process.argv[3] ?? resolve(here, '../../wai-rs/tests/vectors'));
const wai = await import(pathToFileURL(pkg).href);
const rd = (rel) => new Uint8Array(readFileSync(`${vectors}/${rel}`));
const side = (rel, key) => JSON.parse(readFileSync(`${vectors}/${rel}`, 'utf8'))[key];
const sha = (b) => createHash('sha256').update(b).digest();
const hex = (b) => Buffer.from(b).toString('hex');
const clip = (v) => {
const n = v.width * v.height * 3, rgb = v.rgb, digests = [];
for (let f = 0; f < v.nFrames; f++) {
const one = v.frame(f);
if (one.length !== n || !one.every((b, i) => b === rgb[f * n + i])) throw new Error(`frame ${f} differs from rgb`);
digests.push(sha(one));
}
return hex(sha(Buffer.concat(digests)));
};
const [g, h, e] = [rd('int_hyper/tiny_g_s.bin'), rd('int_hyper/tiny_h_s.bin'), rd('int_hyper/tiny_entropy.json')];
const checks = [
['int_video (WIV1, keyframe 0)', () => clip(wai.decodeWiv1('wai.video.int_motion', rd('int_video/int_video_bitstream.wiv1'))),
side('int_video/sha256.json', 'int_video').clip_sha256],
['video_real (WIV1, half-pel)', () => clip(wai.decodeWiv1('wai.video.int_motion', rd('int_video/video_real_bitstream.wiv1'))),
side('int_video/sha256.json', 'video_real').clip_sha256],
['video_nk (WIV1, keyframe 1)', () => clip(wai.decodeWiv1('wai.video.int_hyper', rd('int_video/video_nk_bitstream.wiv1'), g, h, e)),
side('int_video/sha256.json', 'video_nk').clip_sha256],
['tiny (WIH1)', () => hex(sha(wai.decodeWih1(rd('int_hyper/tiny_bitstream.wih1'), g, h, e).rgb)),
side('int_hyper/sha256.json', 'tiny').sha256],
['tiny again, its set held', () => {
const before = wai.loadedSetStats();
const got = hex(sha(wai.decodeWih1(rd('int_hyper/tiny_bitstream.wih1'), g, h, e).rgb));
const after = wai.loadedSetStats();
if (after.hits !== before.hits + 1 || after.misses !== before.misses) {
return `hits ${before.hits} -> ${after.hits}, misses ${before.misses} -> ${after.misses}`;
}
return got;
}, side('int_hyper/sha256.json', 'tiny').sha256],
['tiny with a budget of 0', () => {
const budget = wai.loadedSetStats().budget;
wai.setLoadedSetBudget(0);
const before = wai.loadedSetStats();
const got = hex(sha(wai.decodeWih1(rd('int_hyper/tiny_bitstream.wih1'), g, h, e).rgb));
const after = wai.loadedSetStats();
wai.setLoadedSetBudget(budget);
if (before.entries !== 0 || after.entries !== 0 || after.hits !== before.hits || after.misses !== before.misses) {
return `entries ${before.entries} -> ${after.entries}, a lookup was made`;
}
return got;
}, side('int_hyper/sha256.json', 'tiny').sha256],
['audio (WIA1)', () => {
const a = wai.decodeWia1(rd('int_audio/audio_bitstream.wia1'), rd('int_audio/audio_model.bin'));
if (a.sampleRate !== 16000) return `sample rate ${a.sampleRate}`;
const pcm = a.pcm;
return hex(sha(Buffer.from(pcm.buffer, pcm.byteOffset, pcm.byteLength)));
}, side('int_audio/sha256.json', 'audio').sha256],
['splat (WIS1)', () => {
const s = wai.decodeWis1(rd('int_splat/splat.wis1'), rd('int_splat/splat_gs.bin'));
if (s.ranges.length !== 2 * s.nAttr || s.nGaussians !== 1024) return 'ranges or Gaussian count';
return hex(sha(s.attr));
}, side('int_splat/sha256.json', 'splat').sha256],
['a JSON form is refused', () => {
try { wai.decodeWiv1('wai.video.int_motion', rd('int_video/int_video_bitstream.json')); return 'decoded'; }
catch (err) { return String(err.message ?? err).split(':')[0]; }
}, 'bad_magic'],
];
let pass = true;
for (const [name, run, want] of checks) {
const got = run();
const ok = got === want;
pass = pass && ok;
console.log(`${ok ? 'PASS' : 'FAIL'} ${name.padEnd(30)} ${String(got).slice(0, 16)}${ok ? '' : ` want ${want}`}`);
}
process.exit(pass ? 0 : 1);