#![deny(unsafe_op_in_unsafe_fn)]
#![warn(missing_docs)]
use std::alloc::{alloc, dealloc, Layout};
use wasm_bindgen::prelude::*;
use web_sys::console;
pub mod array;
pub mod error;
pub mod fft;
pub mod integrate;
pub mod interpolate;
pub mod linalg;
pub mod optimize;
pub mod random;
pub mod signal;
pub mod stats;
pub mod utils;
pub mod async_streaming;
pub mod shared_memory;
pub mod incremental_pca;
pub mod mfcc;
pub mod parallel;
pub mod streaming_fft;
pub mod wavelets;
pub mod webgpu;
#[wasm_bindgen(start)]
pub fn init() {
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
log("SciRS2-WASM initialized successfully");
}
#[wasm_bindgen]
pub fn version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
#[wasm_bindgen]
pub fn log(message: &str) {
console::log_1(&JsValue::from_str(message));
}
#[wasm_bindgen]
pub fn has_simd_support() -> bool {
#[cfg(target_feature = "simd128")]
{
true
}
#[cfg(not(target_feature = "simd128"))]
{
false
}
}
#[wasm_bindgen]
pub fn capabilities() -> JsValue {
let caps = serde_json::json!({
"version": env!("CARGO_PKG_VERSION"),
"simd": has_simd_support(),
"features": {
"array": true,
"linalg": cfg!(feature = "linalg"),
"stats": cfg!(feature = "stats"),
"fft": cfg!(feature = "fft"),
"signal": cfg!(feature = "signal"),
"integrate": cfg!(feature = "integrate"),
"optimize": cfg!(feature = "optimize"),
"interpolate": cfg!(feature = "interpolate"),
},
"target": {
"arch": std::env::consts::ARCH,
"os": "wasm32",
"family": std::env::consts::FAMILY,
}
});
serde_wasm_bindgen::to_value(&caps).unwrap_or(JsValue::NULL)
}
#[wasm_bindgen]
pub struct PerformanceTimer {
start: f64,
label: String,
}
#[wasm_bindgen]
impl PerformanceTimer {
#[wasm_bindgen(constructor)]
pub fn new(label: String) -> Result<PerformanceTimer, JsValue> {
let start = web_sys::window()
.ok_or_else(|| JsValue::from_str("No window object available"))?
.performance()
.ok_or_else(|| JsValue::from_str("No performance object available"))?
.now();
Ok(PerformanceTimer { start, label })
}
pub fn elapsed(&self) -> Result<f64, JsValue> {
let now = web_sys::window()
.ok_or_else(|| JsValue::from_str("No window object available"))?
.performance()
.ok_or_else(|| JsValue::from_str("No performance object available"))?
.now();
Ok(now - self.start)
}
pub fn log_elapsed(&self) -> Result<(), JsValue> {
let elapsed = self.elapsed()?;
let message = format!("{}: {:.3}ms", self.label, elapsed);
console::log_1(&JsValue::from_str(&message));
Ok(())
}
}
#[wasm_bindgen]
pub fn memory_usage() -> JsValue {
let info = serde_json::json!({
"note": "WASM memory usage should be checked via JavaScript Memory API"
});
serde_wasm_bindgen::to_value(&info).unwrap_or(JsValue::NULL)
}
#[wasm_bindgen]
pub fn alloc_f64_array(len: usize) -> usize {
if len == 0 {
return 0;
}
let layout = Layout::array::<f64>(len).expect("alloc_f64_array: layout overflow");
let ptr = unsafe { alloc(layout) };
assert!(!ptr.is_null(), "alloc_f64_array: allocation failed");
ptr as usize
}
#[wasm_bindgen]
pub fn free_f64_array(ptr: usize, len: usize) {
if ptr == 0 || len == 0 {
return;
}
let layout = Layout::array::<f64>(len).expect("free_f64_array: layout overflow");
unsafe { dealloc(ptr as *mut u8, layout) };
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub fn view_f64_array(ptr: usize, len: usize) -> js_sys::Float64Array {
let slice = unsafe { std::slice::from_raw_parts(ptr as *const f64, len) };
unsafe { js_sys::Float64Array::view(slice) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version() {
let v = version();
assert!(!v.is_empty());
}
#[test]
fn test_has_simd_support() {
let _simd = has_simd_support();
}
#[test]
fn test_alloc_zero_returns_zero() {
let ptr = alloc_f64_array(0);
assert_eq!(ptr, 0);
free_f64_array(0, 0);
}
#[test]
fn test_alloc_free_single_element() {
let len = 1_usize;
let ptr = alloc_f64_array(len);
assert_ne!(ptr, 0);
unsafe {
let p = ptr as *mut f64;
p.write(std::f64::consts::PI);
let read_back = p.read();
assert!(
(read_back - std::f64::consts::PI).abs() < f64::EPSILON,
"expected PI, got {read_back}"
);
}
free_f64_array(ptr, len);
}
#[test]
fn test_alloc_free_round_trip() {
let len = 256_usize;
let ptr = alloc_f64_array(len);
assert_ne!(ptr, 0);
unsafe {
let slice = std::slice::from_raw_parts_mut(ptr as *mut f64, len);
for (i, elem) in slice.iter_mut().enumerate() {
*elem = i as f64 * 1.5;
}
for (i, &val) in slice.iter().enumerate() {
let expected = i as f64 * 1.5;
assert!(
(val - expected).abs() < f64::EPSILON,
"index {i}: expected {expected}, got {val}"
);
}
}
free_f64_array(ptr, len);
}
#[test]
fn test_free_noop_on_null_or_zero_len() {
free_f64_array(0, 128);
free_f64_array(0, 0);
let ptr = alloc_f64_array(4);
free_f64_array(ptr, 0); free_f64_array(ptr, 4); }
}