1use std::{
2 borrow::BorrowMut,
3 sync::{Arc, RwLock},
4};
5
6use crate::checksum::Checksum;
7use crate::error::Error;
8
9use clru::CLruCache;
10use wasmer::{Instance, Module, Store};
11
12pub struct InMemoryCache {
14 modules: CLruCache<Checksum, Module>,
15}
16
17impl InMemoryCache {
18 pub fn new(max_entries: u32) -> Self {
19 InMemoryCache { modules: CLruCache::new(max_entries as usize) }
20 }
21
22 pub fn store(&mut self, checksum: &Checksum, module: Module) -> Option<Module> {
23 self.modules.put(*checksum, module)
24 }
25
26 pub fn load(&mut self, checksum: &Checksum) -> Option<Module> {
28 self.modules.get(checksum).cloned()
29 }
30}
31
32#[derive(Clone, Debug)]
33pub struct CacheOptions {
34 pub cache_size: u32,
35}
36
37pub struct Cache {
38 memory_cache: Arc<RwLock<InMemoryCache>>,
39}
40
41impl Cache {
42 pub fn new(options: CacheOptions) -> Self {
43 let CacheOptions { cache_size } = options;
44
45 Self { memory_cache: Arc::new(RwLock::new(InMemoryCache::new(cache_size))) }
46 }
47
48 fn with_in_memory_cache<C, R>(&mut self, callback: C) -> R
49 where
50 C: FnOnce(&mut InMemoryCache) -> R,
51 {
52 let mut guard = self.memory_cache.as_ref().write().unwrap();
53 let in_memory_cache = guard.borrow_mut();
54 callback(in_memory_cache)
55 }
56
57 pub fn get_instance(
58 &mut self,
59 wasm: &[u8],
60 store: &Store,
61 import_object: &wasmer::ImportObject,
62 ) -> Result<(wasmer::Instance, bool), Error> {
63 let checksum = Checksum::generate(wasm);
64 self.with_in_memory_cache(|in_memory_cache| {
65 if let Some(module) = in_memory_cache.load(&checksum) {
67 return Ok((Instance::new(&module, &import_object).unwrap(), true));
68 }
69
70 let module = Module::new(store, &wasm).map_err(|_| Error::InstantiationError)?;
72 let instance =
73 Instance::new(&module, &import_object).map_err(|_| Error::InstantiationError)?;
74
75 in_memory_cache.store(&checksum, module);
76
77 Ok((instance, false))
78 })
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use std::io::{Read, Write};
86 use std::process::Command;
87 use tempfile::NamedTempFile;
88 use wasmer::{imports, Singlepass, Store, Universal};
89
90 fn wat2wasm(wat: impl AsRef<[u8]>) -> Vec<u8> {
91 let mut input_file = NamedTempFile::new().unwrap();
92 let mut output_file = NamedTempFile::new().unwrap();
93 input_file.write_all(wat.as_ref()).unwrap();
94 Command::new("wat2wasm")
95 .args(&[
96 input_file.path().to_str().unwrap(),
97 "-o",
98 output_file.path().to_str().unwrap(),
99 ])
100 .output()
101 .unwrap();
102 let mut wasm = Vec::new();
103 output_file.read_to_end(&mut wasm).unwrap();
104 wasm
105 }
106
107 fn get_instance_without_err(cache: &mut Cache, wasm: &[u8]) -> (wasmer::Instance, bool) {
108 let compiler = Singlepass::new();
109 let store = Store::new(&Universal::new(compiler).engine());
110 let import_object = imports! {};
111
112 match cache.get_instance(&wasm, &store, &import_object) {
113 Ok((instance, is_hit)) => (instance, is_hit),
114 Err(_) => panic!("Fail to get instance"),
115 }
116 }
117
118 #[test]
119 fn test_cache_catch() {
120 let mut cache = Cache::new(CacheOptions { cache_size: 10000 });
121 let wasm = wat2wasm(
122 r#"(module
123 (func $execute (export "execute"))
124 (func $prepare (export "prepare"))
125 )"#,
126 );
127
128 let wasm2 = wat2wasm(
129 r#"(module
130 (func $execute (export "execute"))
131 (func $prepare (export "prepare"))
132 (func $foo2 (export "foo2"))
133 )"#,
134 );
135
136 let (instance1, is_hit) = get_instance_without_err(&mut cache, &wasm);
137 assert_eq!(false, is_hit);
138
139 let (instance2, is_hit) = get_instance_without_err(&mut cache, &wasm);
140 assert_eq!(true, is_hit);
141
142 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm2);
143 assert_eq!(false, is_hit);
144
145 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm);
146 assert_eq!(true, is_hit);
147
148 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm2);
149 assert_eq!(true, is_hit);
150
151 let ser1 = match instance1.module().serialize() {
152 Ok(r) => r,
153 Err(_) => panic!("Fail to serialize module"),
154 };
155
156 let ser2 = match instance2.module().serialize() {
157 Ok(r) => r,
158 Err(_) => panic!("Fail to serialize module"),
159 };
160
161 assert_eq!(ser1, ser2);
162 }
163
164 #[test]
165 fn test_cache_size() {
166 let mut cache = Cache::new(CacheOptions { cache_size: 2 });
167 let wasm1 = wat2wasm(
168 r#"(module
169 (func $execute (export "execute"))
170 (func $prepare (export "prepare"))
171 (func $foo (export "foo"))
172 )"#,
173 );
174
175 let wasm2 = wat2wasm(
176 r#"(module
177 (func $execute (export "execute"))
178 (func $prepare (export "prepare"))
179 (func $foo2 (export "foo2"))
180 )"#,
181 );
182
183 let wasm3 = wat2wasm(
184 r#"(module
185 (func $execute (export "execute"))
186 (func $prepare (export "prepare"))
187 (func $foo3 (export "foo3"))
188 )"#,
189 );
190
191 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm1);
193 assert_eq!(false, is_hit);
194
195 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm2);
197 assert_eq!(false, is_hit);
198
199 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm3);
201 assert_eq!(false, is_hit);
202
203 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm2);
205 assert_eq!(true, is_hit);
206
207 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm1);
209 assert_eq!(false, is_hit);
210
211 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm2);
213 assert_eq!(true, is_hit);
214
215 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm3);
217 assert_eq!(false, is_hit);
218
219 cache = Cache::new(CacheOptions { cache_size: 0 });
220
221 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm1);
222 assert_eq!(false, is_hit);
223
224 let (_, is_hit) = get_instance_without_err(&mut cache, &wasm1);
225 assert_eq!(false, is_hit);
226 }
227}