syscall_map/
dynamic_map.rs1use crate::{SyscallMap, murmur3_32};
2
3pub struct DynamicSyscallMap {
6 pub entries: Vec<(u32, String)>,
8}
9
10impl DynamicSyscallMap {
11 pub fn new(syscalls: Vec<String>) -> Result<Self, String> {
13 let mut entries: Vec<(u32, String)> = syscalls
14 .into_iter()
15 .map(|name| (murmur3_32(&name), name))
16 .collect();
17
18 entries.sort_by_key(|(hash, _)| *hash);
19
20 for i in 0..entries.len().saturating_sub(1) {
22 if entries[i].0 == entries[i + 1].0 {
23 return Err(format!(
24 "Hash conflict detected between syscalls '{}' and '{}'",
25 entries[i].1,
26 entries[i + 1].1
27 ));
28 }
29 }
30
31 Ok(Self { entries })
32 }
33
34 pub fn from_names(names: &[&str]) -> Result<Self, String> {
36 Self::new(names.iter().map(|&s| s.to_string()).collect())
37 }
38
39 pub fn get(&self, hash: u32) -> Option<&str> {
41 match self.entries.binary_search_by_key(&hash, |(h, _)| *h) {
42 Ok(idx) => Some(&self.entries[idx].1),
43 Err(_) => None,
44 }
45 }
46
47 pub fn add(&mut self, name: String) -> Result<(), String> {
49 let hash = murmur3_32(&name);
50
51 match self.entries.binary_search_by_key(&hash, |(h, _)| *h) {
53 Ok(_) => Err(format!(
54 "Hash conflict: '{}' conflicts with existing syscall",
55 name
56 )),
57 Err(pos) => {
58 self.entries.insert(pos, (hash, name));
59 Ok(())
60 }
61 }
62 }
63
64 pub fn len(&self) -> usize {
65 self.entries.len()
66 }
67
68 pub fn is_empty(&self) -> bool {
69 self.entries.is_empty()
70 }
71}
72
73impl<'a> From<&SyscallMap<'a>> for DynamicSyscallMap {
75 fn from(static_map: &SyscallMap<'a>) -> Self {
76 let entries = static_map
77 .entries
78 .iter()
79 .map(|(hash, name)| (*hash, name.to_string()))
80 .collect();
81
82 Self { entries }
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_dynamic_mutable_map() {
92 let mut map = DynamicSyscallMap::from_names(&["initial_syscall"]).unwrap();
94
95 assert_eq!(
97 map.get(murmur3_32("initial_syscall")),
98 Some("initial_syscall")
99 );
100
101 map.add("runtime_syscall_1".to_string()).unwrap();
103 map.add("runtime_syscall_2".to_string()).unwrap();
104
105 assert_eq!(
107 map.get(murmur3_32("initial_syscall")),
108 Some("initial_syscall")
109 );
110 assert_eq!(
111 map.get(murmur3_32("runtime_syscall_1")),
112 Some("runtime_syscall_1")
113 );
114 assert_eq!(
115 map.get(murmur3_32("runtime_syscall_2")),
116 Some("runtime_syscall_2")
117 );
118
119 assert_eq!(map.get(0xDEADBEEF), None);
121
122 assert_eq!(map.len(), 3);
124 }
125
126 #[test]
127 fn test_dynamic_map_with_owned_strings() {
128 let syscalls = vec![
130 String::from("custom_1"),
131 String::from("custom_2"),
132 String::from("custom_3"),
133 ];
134
135 let mut map = DynamicSyscallMap::new(syscalls).unwrap();
136
137 assert_eq!(map.get(murmur3_32("custom_1")), Some("custom_1"));
138 assert_eq!(map.get(murmur3_32("custom_2")), Some("custom_2"));
139 assert_eq!(map.get(murmur3_32("custom_3")), Some("custom_3"));
140
141 map.add("custom_4".to_string()).unwrap();
143 assert_eq!(map.get(murmur3_32("custom_4")), Some("custom_4"));
144 }
145
146 #[test]
147 fn test_convert_static_to_dynamic() {
148 use crate::compute_syscall_entries_const;
149
150 const TEST_SYSCALLS: &[&str; 3] = &["abort", "sol_log_", "sol_panic_"];
152 const TEST_ENTRIES: &[(u32, &str); 3] = &compute_syscall_entries_const(TEST_SYSCALLS);
153 const TEST_MAP: SyscallMap<'static> = SyscallMap::from_entries(TEST_ENTRIES);
154
155 let dynamic = DynamicSyscallMap::from(&TEST_MAP);
157
158 for &name in TEST_SYSCALLS.iter() {
160 let hash = murmur3_32(name);
161 assert_eq!(
162 dynamic.get(hash),
163 Some(name),
164 "Failed to find syscall: {}",
165 name
166 );
167 }
168
169 let mut dynamic_mut = dynamic;
171 dynamic_mut.add("my_custom_syscall".to_string()).unwrap();
172
173 assert_eq!(
174 dynamic_mut.get(murmur3_32("my_custom_syscall")),
175 Some("my_custom_syscall")
176 );
177
178 assert_eq!(dynamic_mut.get(murmur3_32("abort")), Some("abort"));
180
181 assert_eq!(dynamic_mut.len(), TEST_SYSCALLS.len() + 1);
183 }
184
185 #[test]
186 fn test_dynamic_map_add_duplicate() {
187 let mut map = DynamicSyscallMap::from_names(&["existing"]).unwrap();
188
189 let result = map.add("existing".to_string());
191 assert!(result.is_err());
192 assert!(result.unwrap_err().contains("Hash conflict"));
193 }
194
195 #[test]
196 fn test_dynamic_map_len_and_is_empty() {
197 let empty_map = DynamicSyscallMap::from_names(&[]).unwrap();
198 assert!(empty_map.is_empty());
199 assert_eq!(empty_map.len(), 0);
200
201 let map = DynamicSyscallMap::from_names(&["syscall1", "syscall2"]).unwrap();
202 assert!(!map.is_empty());
203 assert_eq!(map.len(), 2);
204 }
205}