Lines
84.96 %
Functions
17.65 %
Branches
100 %
use anyhow::Result;
use std::sync::{Arc, Mutex};
use wasmtime::{Caller, Func, Memory, Store};
pub struct DataStore<Data> {
pub data: Mutex<Data>,
pub memory: Mutex<Option<Memory>>,
}
pub fn wrap_wasm_str_transform<Data, Transform>(
store: &mut Store<Arc<DataStore<Data>>>,
transform: Transform,
) -> Func
where
Data: Send + 'static,
Transform: Fn(&Data, String) -> Result<String, anyhow::Error> + Send + Sync + 'static,
{
Func::wrap(
store,
move |mut caller: Caller<'_, Arc<DataStore<Data>>>,
tag_ptr: i32,
tag_len: i32,
buf_ptr: i32,
buf_len: i32|
-> i32 {
let data = Arc::clone(caller.data());
// Define the inner logic
let result = (|| -> Result<i32, anyhow::Error> {
// Lock the WASM memory
let memory = data
.memory
.lock()
.map_err(|_| anyhow::anyhow!("Failed to lock memory"))?;
let memory = memory
.as_ref()
.ok_or_else(|| anyhow::anyhow!("Memory not initialized"))?;
let mem = memory.data(&caller);
// Validate memory bounds for the input key
if (tag_ptr as usize + tag_len as usize) > mem.len() {
return Err(anyhow::anyhow!("Input memory bounds error"));
// Extract the key string from memory
let key = String::from_utf8(
mem[tag_ptr as usize..tag_ptr as usize + tag_len as usize].to_vec(),
)
.map_err(|_| anyhow::anyhow!("Failed to decode input string"))?;
// Lock the data and call the transform function
let data_lock = data
.data
.map_err(|_| anyhow::anyhow!("Failed to lock data"))?;
let output = transform(&*data_lock, key)?;
// Get output bytes
let output_bytes = output.as_bytes();
let output_len = output_bytes.len();
// Validate that the output fits within the provided buffer length
if output_len > buf_len as usize {
return Err(anyhow::anyhow!("Output exceeds buffer size"));
// Validate memory bounds for the output buffer
if (buf_ptr as usize + output_len) > mem.len() {
return Err(anyhow::anyhow!("Output memory bounds error"));
// Write the result string into the output buffer
memory.data_mut(&mut caller)[buf_ptr as usize..buf_ptr as usize + output_len]
.copy_from_slice(output_bytes);
Ok(output_len as i32)
})();
// Handle the result or return an error code
match result {
Ok(len) => len,
Err(e) => {
tracing::error!("Error in wrap_wasm_str_transform: {e:?}");
-1 // Indicate failure
},
pub fn wrap_wasm_str_list_get<Data, Getter>(
getter: Getter,
Getter: Fn(&Data) -> Result<Vec<String>, anyhow::Error> + Send + Sync + 'static,
move |mut caller: Caller<'_, Arc<DataStore<Data>>>, loc: i32, max_len: i32| -> i32 {
// Try to execute the getter and handle potential errors
let result = (|| -> Result<i32> {
let builder = data
let list = getter(&builder)?; // Call getter, which may return an error
let mut bytes = Vec::new();
for s in list {
bytes.extend_from_slice(s.as_bytes());
bytes.push(0); // Null terminator
let len = bytes.len().min(max_len as usize);
if let Some(memory) = &*memory {
let mem = memory.data_mut(&mut caller);
if (loc as usize + len) <= mem.len() {
mem[loc as usize..loc as usize + len].copy_from_slice(&bytes[..len]);
return Ok(len as i32);
return Err(anyhow::anyhow!("Memory bounds error"));
Err(anyhow::anyhow!("Memory not available"))
// Return the result or indicate failure (-1)
tracing::error!("Error in wrap_wasm_str_list_get: {e:?}");
-1
pub fn wrap_wasm_str_map<Data>(
updater: impl Fn(&mut Data, String, String) -> Result<(), anyhow::Error> + Send + Sync + 'static,
move |caller: Caller<'_, Arc<DataStore<Data>>>,
key_loc: i32,
key_len: i32,
value_loc: i32,
value_len: i32|
let data = caller.data();
let result = (|| -> Result<()> {
if (key_loc as usize + key_len as usize) > mem.len()
|| (value_loc as usize + value_len as usize) > mem.len()
mem[key_loc as usize..key_loc as usize + key_len as usize].to_vec(),
)?;
let value = String::from_utf8(
mem[value_loc as usize..value_loc as usize + value_len as usize].to_vec(),
let mut builder = data
updater(&mut builder, key, value)?;
Ok(())
} else {
Ok(()) => 0,
tracing::error!("Error in wrap_wasm_str_map: {e:?}");