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 +}