#![cfg(all(feature = "wasm", target_arch = "wasm32"))]
use wasm_bindgen::prelude::*;
use crate::pipeline::{Pipeline, PipelineConfig};
use crate::scene::Scene;
pub const NVSIM_BUILD_VERSION: &str = env!("CARGO_PKG_VERSION");
fn js_err(msg: impl AsRef<str>) -> JsValue {
JsValue::from_str(msg.as_ref())
}
#[wasm_bindgen]
pub struct WasmPipeline {
inner: Pipeline,
}
#[wasm_bindgen]
impl WasmPipeline {
#[wasm_bindgen(constructor)]
pub fn new(scene_json: &str, config_json: &str, seed: f64) -> Result<WasmPipeline, JsValue> {
let scene: Scene =
serde_json::from_str(scene_json).map_err(|e| js_err(format!("scene parse: {e}")))?;
let config: PipelineConfig =
serde_json::from_str(config_json).map_err(|e| js_err(format!("config parse: {e}")))?;
let seed_u64 = seed as u64;
Ok(WasmPipeline {
inner: Pipeline::new(scene, config, seed_u64),
})
}
#[wasm_bindgen]
pub fn run(&self, n_samples: usize) -> Vec<u8> {
let frames = self.inner.run(n_samples);
let mut out = Vec::with_capacity(frames.len() * 60);
for f in &frames {
out.extend_from_slice(&f.to_bytes());
}
out
}
#[wasm_bindgen(js_name = runWithWitness)]
pub fn run_with_witness(&self, n_samples: usize) -> Result<JsValue, JsValue> {
let (frames, witness) = self.inner.run_with_witness(n_samples);
let mut bytes = Vec::with_capacity(frames.len() * 60);
for f in &frames {
bytes.extend_from_slice(&f.to_bytes());
}
let obj = js_sys::Object::new();
let frames_arr = js_sys::Uint8Array::new_with_length(bytes.len() as u32);
frames_arr.copy_from(&bytes);
let witness_arr = js_sys::Uint8Array::new_with_length(32);
witness_arr.copy_from(&witness);
js_sys::Reflect::set(&obj, &JsValue::from_str("frames"), &frames_arr)?;
js_sys::Reflect::set(&obj, &JsValue::from_str("witness"), &witness_arr)?;
js_sys::Reflect::set(
&obj,
&JsValue::from_str("frameCount"),
&JsValue::from_f64(frames.len() as f64),
)?;
Ok(obj.into())
}
#[wasm_bindgen(js_name = buildVersion)]
pub fn build_version() -> String {
NVSIM_BUILD_VERSION.to_string()
}
#[wasm_bindgen(js_name = frameMagic)]
pub fn frame_magic() -> u32 {
crate::frame::MAG_FRAME_MAGIC
}
#[wasm_bindgen(js_name = frameBytes)]
pub fn frame_bytes() -> u32 {
crate::frame::MAG_FRAME_BYTES as u32
}
}
#[wasm_bindgen(js_name = referenceSceneJson)]
pub fn reference_scene_json() -> String {
crate::proof::Proof::REFERENCE_SCENE_JSON.to_string()
}
#[wasm_bindgen(js_name = hexWitness)]
pub fn hex_witness(witness: &[u8]) -> Result<String, JsValue> {
if witness.len() != 32 {
return Err(js_err(format!(
"witness must be 32 bytes, got {}",
witness.len()
)));
}
let mut a = [0u8; 32];
a.copy_from_slice(witness);
Ok(crate::proof::Proof::hex(&a))
}
#[wasm_bindgen(js_name = expectedReferenceWitnessHex)]
pub fn expected_reference_witness_hex() -> String {
"cc8de9b01b0ff5bd97a6c17848a3f156c174ea7589d0888164a441584ec593b4".to_string()
}
#[wasm_bindgen(js_name = referenceWitness)]
pub fn reference_witness() -> Result<js_sys::Uint8Array, JsValue> {
let bytes = crate::proof::Proof::generate().map_err(|e| js_err(format!("{e}")))?;
let arr = js_sys::Uint8Array::new_with_length(32);
arr.copy_from(&bytes);
Ok(arr)
}
#[wasm_bindgen(js_name = runTransient)]
pub fn run_transient(
scene_json: &str,
config_json: &str,
seed: f64,
n_samples: usize,
) -> Result<JsValue, JsValue> {
let scene: crate::scene::Scene =
serde_json::from_str(scene_json).map_err(|e| js_err(format!("scene parse: {e}")))?;
let config: crate::pipeline::PipelineConfig =
serde_json::from_str(config_json).map_err(|e| js_err(format!("config parse: {e}")))?;
let pipeline = crate::pipeline::Pipeline::new(scene, config, seed as u64);
let (frames, witness) = pipeline.run_with_witness(n_samples);
let mut sum_b = [0.0_f64; 3];
let mut sum_s = [0.0_f64; 3];
let mut sum_nf = 0.0_f64;
let n = frames.len().max(1) as f64;
for f in &frames {
for k in 0..3 {
sum_b[k] += f.b_pt[k] as f64;
sum_s[k] += f.sigma_pt[k] as f64;
}
sum_nf += f.noise_floor_pt_sqrt_hz as f64;
}
let avg_b_pt = [sum_b[0] / n, sum_b[1] / n, sum_b[2] / n];
let avg_s_pt = [sum_s[0] / n, sum_s[1] / n, sum_s[2] / n];
let avg_nf = sum_nf / n;
let b_t = [
avg_b_pt[0] * 1.0e-12,
avg_b_pt[1] * 1.0e-12,
avg_b_pt[2] * 1.0e-12,
];
let bmag_t = (b_t[0] * b_t[0] + b_t[1] * b_t[1] + b_t[2] * b_t[2]).sqrt();
let obj = js_sys::Object::new();
let b_arr = js_sys::Float64Array::new_with_length(3);
b_arr.copy_from(&b_t);
let s_arr = js_sys::Float64Array::new_with_length(3);
s_arr.copy_from(&avg_s_pt);
js_sys::Reflect::set(&obj, &JsValue::from_str("bRecoveredT"), &b_arr)?;
js_sys::Reflect::set(
&obj,
&JsValue::from_str("bMagT"),
&JsValue::from_f64(bmag_t),
)?;
js_sys::Reflect::set(
&obj,
&JsValue::from_str("noiseFloorPtSqrtHz"),
&JsValue::from_f64(avg_nf),
)?;
js_sys::Reflect::set(&obj, &JsValue::from_str("sigmaPt"), &s_arr)?;
js_sys::Reflect::set(
&obj,
&JsValue::from_str("nFrames"),
&JsValue::from_f64(frames.len() as f64),
)?;
let witness_hex = crate::proof::Proof::hex(&witness);
js_sys::Reflect::set(
&obj,
&JsValue::from_str("witnessHex"),
&JsValue::from_str(&witness_hex),
)?;
Ok(obj.into())
}