From e6337fb9fc81bf6a834b99d893c38673786afe20 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Sat, 1 Aug 2026 10:55:22 -0400 Subject: [PATCH] wai-web: expose the byte-exact JPEG-AI mANS decode to wasm (decodeJpegAiMans) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ISO/IEC 6048 JPEG-AI entropy decoder (`wai::jpegai_mans`) as a browser entry: given the reference stream, the integer PMF rows, the per-distribution bounds, and the per-position σ indices + coded-position masks, it rebuilds the decode tables and returns the decoded residual symbols. The same pure-i64 Rust a native sink runs, compiled to wasm32 — no float, no ML runtime — so the symbols are byte-identical to the native decode, and their hash is portable across host and browser. Verified: the wasm decode reproduces the native cross-arch golden exactly (172 coded positions, 0 mismatches) and hashes to the same digest. Drives the /jpegai reproduction-vector demo. Co-Authored-By: Claude Opus 4.8 --- wai/wai-web/src/neural_int.rs | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/wai/wai-web/src/neural_int.rs b/wai/wai-web/src/neural_int.rs index 8ed7d5d48..a3ce40e18 100644 --- a/wai/wai-web/src/neural_int.rs +++ b/wai/wai-web/src/neural_int.rs @@ -133,3 +133,42 @@ pub fn det_build_index(sigma_q: i64, edges_q: &[i64]) -> usize { pub fn blake3_hex(bytes: &[u8]) -> String { blake3::hash(bytes).to_hex().to_string() } + +// ---- JPEG-AI byte-exact mANS: the ISO entropy decode, in the browser ---- + +/// Decode the ISO/IEC 6048 JPEG-AI **mANS** entropy stream in the browser, byte-exact — +/// the same pure-i64 decoder native sinks run (`wai::jpegai_mans`), compiled to wasm32. +/// +/// A **reproduction vector**: given the reference stream `mem`, the integer PMF rows +/// (`pmf_flat`, `n_rows × 256` row-major), the per-distribution `bounds`, and the +/// per-position `sigma` distribution indices + coded-position `masks` (0/1), it returns +/// the decoded residual symbols. The decode is entirely integer, so the symbols — and +/// their hash — are identical on every machine. The page hashes them and compares to the +/// digest the native reference produced; a byte-identical match across host + browser is +/// the whole point of the integer-entropy contract. This is the same stream and +/// symbols the cross-architecture byte-equality matrix checks natively. +#[wasm_bindgen(js_name = decodeJpegAiMans)] +pub fn decode_jpegai_mans( + mem: &[u8], + pmf_flat: &[i64], + bounds: &[u8], + sigma: &[u8], + masks: &[u8], +) -> Vec { + use wai::jpegai_mans::{build_decode_transitions, cdf_from_pmf, MansDecoder}; + const WIDTH: usize = 256; + let rows = pmf_flat.len() / WIDTH; + let pmf: Vec> = + (0..rows).map(|r| pmf_flat[r * WIDTH..(r + 1) * WIDTH].to_vec()).collect(); + let flat: Vec = build_decode_transitions(&pmf, &cdf_from_pmf(&pmf)) + .iter() + .flatten() + .copied() + .collect(); + let mut dec = MansDecoder::new(mem, &[mem.len() as u32]); + dec.set_sgm_transitions(&flat, bounds); + let masks_b: Vec = masks.iter().map(|&m| m != 0).collect(); + let mut out = vec![0i16; sigma.len()]; + dec.decode_sgm(sigma, &mut out, &masks_b); + out +} -- 2.54.0 (Apple Git-157)