use crate::convert::*;
use serde::Serialize;
use tensogram::{self as core, DecodeOptions};
use tensogram_encodings::simple_packing;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn decode_range(
buf: &[u8],
object_index: usize,
ranges: &js_sys::BigUint64Array,
verify_hash: Option<bool>,
) -> Result<JsValue, JsError> {
let flat: Vec<u64> = ranges.to_vec();
if !flat.len().is_multiple_of(2) {
return Err(JsError::new(
"ranges length must be a multiple of 2 (flat [offset, count] pairs)",
));
}
let range_pairs: Vec<(u64, u64)> = flat.chunks_exact(2).map(|w| (w[0], w[1])).collect();
let options = DecodeOptions {
verify_hash: verify_hash.unwrap_or(false),
..Default::default()
};
let (descriptor, parts) =
core::decode_range(buf, object_index, &range_pairs, &options).map_err(js_err)?;
let result = js_sys::Object::new();
js_sys::Reflect::set(&result, &"descriptor".into(), &to_js(&descriptor)?)
.map_err(|_| JsError::new("failed to set descriptor"))?;
let parts_js = js_sys::Array::new_with_length(parts.len() as u32);
for (i, bytes) in parts.iter().enumerate() {
parts_js.set(i as u32, js_sys::Uint8Array::from(bytes.as_slice()).into());
}
js_sys::Reflect::set(&result, &"parts".into(), &parts_js)
.map_err(|_| JsError::new("failed to set parts"))?;
Ok(result.into())
}
#[wasm_bindgen]
pub fn compute_hash(data: &[u8], algo: Option<String>) -> Result<String, JsError> {
let name = algo.as_deref().unwrap_or("xxh3");
let algorithm = core::HashAlgorithm::parse(name).map_err(js_err)?;
Ok(core::compute_hash(data, algorithm))
}
#[derive(Serialize)]
struct SimplePackingParamsJs {
sp_reference_value: f64,
sp_binary_scale_factor: i32,
sp_decimal_scale_factor: i32,
sp_bits_per_value: u32,
}
#[wasm_bindgen]
pub fn simple_packing_compute_params(
values: &[f64],
bits_per_value: u32,
decimal_scale_factor: i32,
) -> Result<JsValue, JsError> {
let params = simple_packing::compute_params(values, bits_per_value, decimal_scale_factor)
.map_err(|e| JsError::new(&format!("encoding error: {e}")))?;
to_js(&SimplePackingParamsJs {
sp_reference_value: params.reference_value,
sp_binary_scale_factor: params.binary_scale_factor,
sp_decimal_scale_factor: params.decimal_scale_factor,
sp_bits_per_value: params.bits_per_value,
})
}
#[wasm_bindgen]
pub fn encode_pre_encoded(
metadata_js: JsValue,
objects_js: js_sys::Array,
hash: Option<bool>,
) -> Result<js_sys::Uint8Array, JsError> {
let metadata = metadata_from_js(&metadata_js)?;
let (descriptors, data_vec) = extract_descriptor_data_pairs(&objects_js)?;
let pairs: Vec<(&core::DataObjectDescriptor, &[u8])> = descriptors
.iter()
.zip(data_vec.iter())
.map(|(d, v)| (d, v.as_slice()))
.collect();
let encoded =
core::encode_pre_encoded(&metadata, &pairs, &build_encode_options(hash)).map_err(js_err)?;
Ok(js_sys::Uint8Array::from(encoded.as_slice()))
}
#[wasm_bindgen]
pub fn validate_buffer(
buf: &[u8],
level: Option<String>,
check_canonical: bool,
) -> Result<String, JsError> {
let options = parse_validate_options(level.as_deref(), check_canonical)?;
let report = core::validate::validate_message(buf, &options);
serde_json::to_string(&report).map_err(|e| JsError::new(&format!("encoding error: {e}")))
}
fn parse_validate_options(
level: Option<&str>,
check_canonical: bool,
) -> Result<core::validate::ValidateOptions, JsError> {
use core::validate::{ValidateOptions, ValidationLevel};
let (max_level, checksum_only) = match level.unwrap_or("default") {
"quick" => (ValidationLevel::Structure, false),
"default" => (ValidationLevel::Integrity, false),
"checksum" => (ValidationLevel::Integrity, true),
"full" => (ValidationLevel::Fidelity, false),
other => {
return Err(JsError::new(&format!(
"unknown validation level '{other}', expected one of: quick, default, checksum, full",
)));
}
};
Ok(ValidateOptions {
max_level,
check_canonical,
checksum_only,
})
}