use std::collections::HashMap;
use std::sync::{Arc, Mutex};
pub struct ModuleCache {
modules: Mutex<HashMap<String, Arc<wasmtime::Module>>>,
}
impl ModuleCache {
pub fn new() -> Self {
Self {
modules: Mutex::new(HashMap::new()),
}
}
pub fn compile(
&self,
engine: &wasmtime::Engine,
module_bytes: &[u8],
) -> anyhow::Result<Arc<wasmtime::Module>> {
use sha2::{Digest, Sha256};
let key = crate::hex::encode(Sha256::digest(module_bytes));
if let Some(cached) = self.modules.lock().unwrap().get(&key) {
return Ok(cached.clone());
}
let module = Arc::new(wasmtime::Module::new(engine, module_bytes)?);
self.modules.lock().unwrap().insert(key, module.clone());
Ok(module)
}
}
impl Default for ModuleCache {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn trivial_wasm() -> Vec<u8> {
wat::parse_str("(module)").unwrap()
}
#[test]
fn compiling_the_same_bytes_twice_returns_the_same_arc() {
let engine = wasmtime::Engine::default();
let cache = ModuleCache::new();
let bytes = trivial_wasm();
let a = cache.compile(&engine, &bytes).unwrap();
let b = cache.compile(&engine, &bytes).unwrap();
assert!(
Arc::ptr_eq(&a, &b),
"second call should hit the cache, not recompile"
);
}
#[test]
fn compiling_different_bytes_returns_different_modules() {
let engine = wasmtime::Engine::default();
let cache = ModuleCache::new();
let a = cache.compile(&engine, &trivial_wasm()).unwrap();
let other_wasm = wat::parse_str("(module (func))").unwrap();
let b = cache.compile(&engine, &other_wasm).unwrap();
assert!(!Arc::ptr_eq(&a, &b));
}
}