1use syscall_map::murmur3_32;
2
3pub struct SyscallMap<'a> {
6 entries: &'a [(u32, &'a str)],
7}
8
9impl<'a> SyscallMap<'a> {
10 pub const fn from_entries(entries: &'a [(u32, &'a str)]) -> Self {
13 let mut i = 0;
15 while i < entries.len() - 1 {
16 if entries[i].0 == entries[i + 1].0 {
17 panic!("Hash conflict detected between syscalls");
18 }
19 i += 1;
20 }
21
22 Self { entries }
23 }
24
25 pub const fn get(&self, hash: u32) -> Option<&'a str> {
26 let mut left = 0;
28 let mut right = self.entries.len();
29
30 while left < right {
31 let mid = (left + right) / 2;
32 if self.entries[mid].0 == hash {
33 return Some(self.entries[mid].1);
34 } else if self.entries[mid].0 < hash {
35 left = mid + 1;
36 } else {
37 right = mid;
38 }
39 }
40 None
41 }
42
43 pub const fn len(&self) -> usize {
44 self.entries.len()
45 }
46
47 pub const fn is_empty(&self) -> bool {
48 self.entries.is_empty()
49 }
50}
51
52pub struct DynamicSyscallMap {
55 entries: Vec<(u32, String)>,
57}
58
59impl DynamicSyscallMap {
60 pub fn new(syscalls: Vec<String>) -> Result<Self, String> {
62 let mut entries: Vec<(u32, String)> = syscalls
63 .into_iter()
64 .map(|name| (murmur3_32(&name), name))
65 .collect();
66
67 entries.sort_by_key(|(hash, _)| *hash);
68
69 for i in 0..entries.len().saturating_sub(1) {
71 if entries[i].0 == entries[i + 1].0 {
72 return Err(format!(
73 "Hash conflict detected between syscalls '{}' and '{}'",
74 entries[i].1,
75 entries[i + 1].1
76 ));
77 }
78 }
79
80 Ok(Self { entries })
81 }
82
83 pub fn from_names(names: &[&str]) -> Result<Self, String> {
85 Self::new(names.iter().map(|&s| s.to_string()).collect())
86 }
87
88 pub fn get(&self, hash: u32) -> Option<&str> {
90 match self.entries.binary_search_by_key(&hash, |(h, _)| *h) {
91 Ok(idx) => Some(&self.entries[idx].1),
92 Err(_) => None,
93 }
94 }
95
96 pub fn add(&mut self, name: String) -> Result<(), String> {
98 let hash = murmur3_32(&name);
99
100 match self.entries.binary_search_by_key(&hash, |(h, _)| *h) {
102 Ok(_) => Err(format!(
103 "Hash conflict: '{}' conflicts with existing syscall",
104 name
105 )),
106 Err(pos) => {
107 self.entries.insert(pos, (hash, name));
108 Ok(())
109 }
110 }
111 }
112
113 pub fn len(&self) -> usize {
114 self.entries.len()
115 }
116
117 pub fn is_empty(&self) -> bool {
118 self.entries.is_empty()
119 }
120}
121
122impl<'a> From<&SyscallMap<'a>> for DynamicSyscallMap {
124 fn from(static_map: &SyscallMap<'a>) -> Self {
125 let entries = static_map
126 .entries
127 .iter()
128 .map(|(hash, name)| (*hash, name.to_string()))
129 .collect();
130
131 Self { entries }
132 }
133}
134
135pub const fn compute_syscall_entries_const<'a, const N: usize>(
138 syscalls: &'a [&'a str],
139) -> [(u32, &'a str); N] {
140 let mut entries: [(u32, &str); N] = [(0, ""); N];
141 let mut i = 0;
142 while i < N {
143 entries[i] = (murmur3_32(syscalls[i]), syscalls[i]);
144 i += 1;
145 }
146
147 let mut i = 0;
149 while i < N {
150 let mut j = 0;
151 while j < N - i - 1 {
152 if entries[j].0 > entries[j + 1].0 {
153 let temp = entries[j];
154 entries[j] = entries[j + 1];
155 entries[j + 1] = temp;
156 }
157 j += 1;
158 }
159 i += 1;
160 }
161
162 entries
163}
164
165pub fn compute_syscall_entries<'a, T: AsRef<str>>(syscalls: &'a [T]) -> Vec<(u32, &'a str)> {
171 let mut entries: Vec<(u32, &'a str)> = syscalls
172 .iter()
173 .map(|name| (murmur3_32(name.as_ref()), name.as_ref()))
174 .collect();
175
176 entries.sort_by_key(|(hash, _)| *hash);
177
178 for i in 0..entries.len().saturating_sub(1) {
180 if entries[i].0 == entries[i + 1].0 {
181 panic!(
182 "Hash conflict detected between syscalls '{}' and '{}'",
183 entries[i].1,
184 entries[i + 1].1
185 );
186 }
187 }
188
189 entries
190}
191
192#[cfg(test)]
193mod tests {
194 use {
195 super::*,
196 crate::syscalls::{REGISTERED_SYSCALLS, SYSCALLS},
197 syscall_map::murmur3_32,
198 };
199
200 #[test]
201 fn test_syscall_lookup() {
202 for &name in REGISTERED_SYSCALLS.iter() {
204 let hash = murmur3_32(name);
205 assert_eq!(
206 SYSCALLS.get(hash),
207 Some(name),
208 "Failed to find syscall: {}",
209 name
210 );
211 }
212 }
213
214 #[test]
215 fn test_const_evaluation() {
216 const ABORT_HASH: u32 = murmur3_32("abort");
218 const SOL_LOG_HASH: u32 = murmur3_32("sol_log_");
219
220 assert_eq!(SYSCALLS.get(ABORT_HASH), Some("abort"));
222 assert_eq!(SYSCALLS.get(SOL_LOG_HASH), Some("sol_log_"));
223 }
224
225 #[test]
226 fn test_nonexistent_syscall() {
227 assert_eq!(SYSCALLS.get(0xDEADBEEF), None);
229 }
230
231 #[test]
232 fn test_dynamic_syscalls() {
233 let owned_syscalls: Vec<String> = vec![
236 String::from("my_custom_syscall"),
237 String::from("another_syscall"),
238 ];
239
240 let entries = compute_syscall_entries(&owned_syscalls);
242
243 let map = SyscallMap::from_entries(&entries);
245
246 let hash1 = murmur3_32("my_custom_syscall");
248 let hash2 = murmur3_32("another_syscall");
249
250 assert_eq!(map.get(hash1), Some("my_custom_syscall"));
251 assert_eq!(map.get(hash2), Some("another_syscall"));
252
253 }
255
256 #[test]
257 fn test_dynamic_syscalls_with_str_slices() {
258 let syscalls: Vec<&str> = vec!["syscall_a", "syscall_b", "syscall_c"];
260
261 let entries = compute_syscall_entries(&syscalls);
262 let map = SyscallMap::from_entries(&entries);
263
264 assert_eq!(map.get(murmur3_32("syscall_a")), Some("syscall_a"));
265 assert_eq!(map.get(murmur3_32("syscall_b")), Some("syscall_b"));
266 assert_eq!(map.get(murmur3_32("syscall_c")), Some("syscall_c"));
267 }
268
269 #[test]
270 fn test_static_custom_map() {
271 const CUSTOM_SYSCALLS: &[&str; 2] = &["test1", "test2"];
273 const CUSTOM_ENTRIES: &[(u32, &str); 2] = &compute_syscall_entries_const(CUSTOM_SYSCALLS);
274 const CUSTOM_MAP: SyscallMap<'static> = SyscallMap::from_entries(CUSTOM_ENTRIES);
275
276 assert_eq!(CUSTOM_MAP.get(murmur3_32("test1")), Some("test1"));
277 assert_eq!(CUSTOM_MAP.get(murmur3_32("test2")), Some("test2"));
278 }
279
280 #[test]
281 fn test_dynamic_mutable_map() {
282 let mut map = DynamicSyscallMap::from_names(&["initial_syscall"]).unwrap();
284
285 assert_eq!(
287 map.get(murmur3_32("initial_syscall")),
288 Some("initial_syscall")
289 );
290
291 map.add("runtime_syscall_1".to_string()).unwrap();
293 map.add("runtime_syscall_2".to_string()).unwrap();
294
295 assert_eq!(
297 map.get(murmur3_32("initial_syscall")),
298 Some("initial_syscall")
299 );
300 assert_eq!(
301 map.get(murmur3_32("runtime_syscall_1")),
302 Some("runtime_syscall_1")
303 );
304 assert_eq!(
305 map.get(murmur3_32("runtime_syscall_2")),
306 Some("runtime_syscall_2")
307 );
308
309 assert_eq!(map.get(0xDEADBEEF), None);
311
312 assert_eq!(map.len(), 3);
314 }
315
316 #[test]
317 fn test_dynamic_map_with_owned_strings() {
318 let syscalls = vec![
320 String::from("custom_1"),
321 String::from("custom_2"),
322 String::from("custom_3"),
323 ];
324
325 let mut map = DynamicSyscallMap::new(syscalls).unwrap();
326
327 assert_eq!(map.get(murmur3_32("custom_1")), Some("custom_1"));
328 assert_eq!(map.get(murmur3_32("custom_2")), Some("custom_2"));
329 assert_eq!(map.get(murmur3_32("custom_3")), Some("custom_3"));
330
331 map.add("custom_4".to_string()).unwrap();
333 assert_eq!(map.get(murmur3_32("custom_4")), Some("custom_4"));
334 }
335
336 #[test]
337 fn test_convert_static_to_dynamic() {
338 let dynamic = DynamicSyscallMap::from(&SYSCALLS);
340
341 for &name in REGISTERED_SYSCALLS.iter() {
343 let hash = murmur3_32(name);
344 assert_eq!(
345 dynamic.get(hash),
346 Some(name),
347 "Failed to find syscall: {}",
348 name
349 );
350 }
351
352 let mut dynamic_mut = dynamic;
354 dynamic_mut.add("my_custom_syscall".to_string()).unwrap();
355
356 assert_eq!(
357 dynamic_mut.get(murmur3_32("my_custom_syscall")),
358 Some("my_custom_syscall")
359 );
360
361 assert_eq!(dynamic_mut.get(murmur3_32("abort")), Some("abort"));
363
364 assert_eq!(dynamic_mut.len(), REGISTERED_SYSCALLS.len() + 1);
366 }
367
368 #[test]
369 fn test_syscall_map_len_and_is_empty() {
370 assert!(!SYSCALLS.is_empty());
372
373 const SINGLE_ENTRIES: &[(u32, &str)] = &[(123, "test")];
375 const SINGLE_MAP: SyscallMap = SyscallMap::from_entries(SINGLE_ENTRIES);
376 assert_eq!(SINGLE_MAP.len(), 1);
377 assert!(!SINGLE_MAP.is_empty());
378 }
379
380 #[test]
381 fn test_dynamic_map_len_and_is_empty() {
382 let empty_map = DynamicSyscallMap::new(vec![]).unwrap();
384 assert_eq!(empty_map.len(), 0);
385 assert!(empty_map.is_empty());
386
387 let map = DynamicSyscallMap::from_names(&["test"]).unwrap();
389 assert_eq!(map.len(), 1);
390 assert!(!map.is_empty());
391 }
392
393 #[test]
394 fn test_dynamic_map_add_duplicate() {
395 let mut map = DynamicSyscallMap::from_names(&["existing"]).unwrap();
396 let result = map.add("existing".to_string());
397 assert!(result.is_err());
398 }
399
400 #[test]
401 fn test_dynamic_map_hash_conflict_in_creation() {
402 let syscalls = vec![String::from("test"), String::from("test")];
403 let result = DynamicSyscallMap::new(syscalls);
404 assert!(result.is_err());
406 if let Err(msg) = result {
407 assert!(msg.contains("Hash conflict"));
408 assert!(msg.contains("test"));
409 }
410 }
411}