1use std::collections::HashMap;
15use std::sync::Arc;
16
17use javm_cap::CapHash;
18use javm_exec::{PvmProgram, gas_cost::DEFAULT_MEM_CYCLES};
19
20use crate::error::VmError;
21
22#[derive(Default, Debug)]
28pub struct ImageCache {
29 entries: HashMap<CapHash, Arc<PvmProgram>>,
30}
31
32impl ImageCache {
33 pub fn new() -> Self {
34 Self::default()
35 }
36
37 pub fn len(&self) -> usize {
39 self.entries.len()
40 }
41
42 pub fn is_empty(&self) -> bool {
43 self.entries.is_empty()
44 }
45
46 pub fn get(&self, content_hash: &CapHash) -> Option<Arc<PvmProgram>> {
48 self.entries.get(content_hash).cloned()
49 }
50
51 pub fn insert(&mut self, content_hash: CapHash, program: Arc<PvmProgram>) {
53 self.entries.insert(content_hash, program);
54 }
55
56 pub fn get_or_decode(
61 &mut self,
62 content_hash: CapHash,
63 code: Vec<u8>,
64 bitmask: Vec<u8>,
65 jump_table: Vec<u32>,
66 ) -> Result<Arc<PvmProgram>, VmError> {
67 if let Some(prog) = self.entries.get(&content_hash) {
68 return Ok(prog.clone());
69 }
70 let prog = PvmProgram::new(code, bitmask, jump_table, DEFAULT_MEM_CYCLES)
71 .map_err(|e| VmError::InvalidBytecode(format!("{:?}", e)))?;
72 let arc = Arc::new(prog);
73 self.entries.insert(content_hash, arc.clone());
74 Ok(arc)
75 }
76
77 pub fn clear(&mut self) {
80 self.entries.clear();
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 fn trivial_prog() -> (Vec<u8>, Vec<u8>, Vec<u32>) {
89 (vec![0u8], vec![1u8], vec![])
92 }
93
94 #[test]
95 fn miss_then_hit() {
96 let mut cache = ImageCache::new();
97 let h = [1u8; 32];
98 assert!(cache.get(&h).is_none());
99
100 let (code, bm, jt) = trivial_prog();
101 let p = cache.get_or_decode(h, code, bm, jt).unwrap();
102 assert_eq!(cache.len(), 1);
103 assert!(Arc::ptr_eq(&p, &cache.get(&h).unwrap()));
104 }
105
106 #[test]
107 fn get_or_decode_reuses_existing_entry() {
108 let mut cache = ImageCache::new();
109 let h = [2u8; 32];
110 let (c1, b1, j1) = trivial_prog();
111 let (c2, b2, j2) = trivial_prog();
112 let p1 = cache.get_or_decode(h, c1, b1, j1).unwrap();
113 let p2 = cache.get_or_decode(h, c2, b2, j2).unwrap();
114 assert!(Arc::ptr_eq(&p1, &p2));
117 assert_eq!(cache.len(), 1);
118 }
119
120 #[test]
121 fn clear_drops_entries() {
122 let mut cache = ImageCache::new();
123 let (c, b, j) = trivial_prog();
124 cache.get_or_decode([3u8; 32], c, b, j).unwrap();
125 assert_eq!(cache.len(), 1);
126 cache.clear();
127 assert!(cache.is_empty());
128 }
129
130 #[test]
131 fn invalid_bytecode_returns_err() {
132 let mut cache = ImageCache::new();
133 let res = cache.get_or_decode([4u8; 32], vec![0u8, 0u8], vec![1u8], vec![]);
136 assert!(matches!(res, Err(VmError::InvalidBytecode(_))));
137 }
138}