1use anyhow::{self as anyhow, anyhow as anyhow_, Context};
2use esvc_traits::Engine;
3use rayon::prelude::*;
4
5#[derive(Clone)]
6pub struct WasmEngine {
7 wte: wasmtime::Engine,
8 cmds: Vec<wasmtime::Module>,
9}
10
11impl Engine for WasmEngine {
12 type Error = anyhow::Error;
13 type Arg = Vec<u8>;
14 type Dat = Vec<u8>;
15
16 fn run_event_bare(&self, cmd: u32, arg: &Vec<u8>, dat: &Vec<u8>) -> anyhow::Result<Vec<u8>> {
17 let cmd: usize = cmd
18 .try_into()
19 .map_err(|_| anyhow_!("command ID overflow cmd={}", cmd))?;
20 let cmd = self
21 .cmds
22 .get(cmd)
23 .ok_or_else(|| anyhow_!("command {} not found", cmd))?;
24
25 let datlen: i32 = dat
26 .len()
27 .try_into()
28 .map_err(|_| anyhow_!("argument buffer overflow dat.len={}", dat.len()))?;
29 let evarglen: i32 = arg
30 .len()
31 .try_into()
32 .map_err(|_| anyhow_!("argument buffer overflow ev.arg.len={}", arg.len()))?;
33
34 let mut store = wasmtime::Store::new(&self.wte, ());
37 let instance = wasmtime::Instance::new(&mut store, cmd, &[])?;
38
39 let memory = instance
40 .get_memory(&mut store, "memory")
41 .ok_or_else(|| anyhow_!("unable to get export `memory`"))?;
42
43 let retptr = instance
44 .get_typed_func::<i32, i32, _>(&mut store, "__wbindgen_add_to_stack_pointer")?
45 .call(&mut store, -16)?;
46 let malloc = instance.get_typed_func::<i32, i32, _>(&mut store, "__wbindgen_malloc")?;
47 let transform =
51 instance.get_typed_func::<(i32, i32, i32, i32, i32), (), _>(&mut store, "transform")?;
52
53 let evargptr = malloc.call(&mut store, evarglen)?;
54 memory.write(&mut store, evargptr.try_into()?, arg)?;
55
56 let datptr = malloc.call(&mut store, datlen)?;
57 memory.write(&mut store, datptr.try_into()?, dat)?;
58
59 let () = transform.call(&mut store, (retptr, evargptr, evarglen, datptr, datlen))?;
61
62 let ret = {
64 let mut retbuf = [0u8; 8];
66 memory.read(&mut store, retptr.try_into()?, &mut retbuf)?;
67 let (retp0, retp1) = retbuf.split_at(4);
68 let retptr2: usize =
69 i32::from_le_bytes(<[u8; 4]>::try_from(retp0).unwrap()).try_into()?;
70 let retlen2: usize =
71 i32::from_le_bytes(<[u8; 4]>::try_from(retp1).unwrap()).try_into()?;
72 memory
73 .data(&mut store)
74 .get(retptr2..retptr2 + retlen2)
75 .with_context(|| "return value length out of bounds".to_string())?
76 .to_vec()
77 };
78
79 Ok(ret)
80 }
81}
82
83impl WasmEngine {
84 pub fn new() -> anyhow::Result<Self> {
85 let wtc = wasmtime::Config::default();
86 Ok(Self {
87 wte: wasmtime::Engine::new(&wtc)?,
88 cmds: Vec::new(),
89 })
90 }
91
92 pub fn add_commands<II, Iter, Item>(&mut self, wasms: II) -> anyhow::Result<(u32, usize)>
93 where
94 II: IntoIterator<IntoIter = Iter>,
95 Iter: Iterator<Item = Item> + Send,
96 Item: AsRef<[u8]> + Send,
97 {
98 let orig_id = self.cmds.len();
99 let id: u32 = orig_id.try_into()?;
100 self.cmds.extend(
101 wasms
102 .into_iter()
103 .par_bridge()
104 .map(|cmd| wasmtime::Module::new(&self.wte, cmd))
105 .collect::<Result<Vec<_>, _>>()?,
106 );
107 Ok((id, self.cmds.len() - orig_id))
108 }
109}