syscall_map/
static_map.rs1use crate::murmur3_32;
2
3pub struct SyscallMap<'a> {
6 pub(crate) 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 const fn compute_syscall_entries_const<'a, const N: usize>(
55 syscalls: &'a [&'a str; N],
56) -> [(u32, &'a str); N] {
57 let mut entries: [(u32, &str); N] = [(0, ""); N];
58 let mut i = 0;
59 while i < N {
60 entries[i] = (murmur3_32(syscalls[i]), syscalls[i]);
61 i += 1;
62 }
63
64 let mut i = 0;
66 while i < N {
67 let mut j = 0;
68 while j < N - i - 1 {
69 if entries[j].0 > entries[j + 1].0 {
70 let temp = entries[j];
71 entries[j] = entries[j + 1];
72 entries[j + 1] = temp;
73 }
74 j += 1;
75 }
76 i += 1;
77 }
78
79 entries
80}
81
82pub fn compute_syscall_entries<'a, T: AsRef<str>>(syscalls: &'a [T]) -> Vec<(u32, &'a str)> {
88 let mut entries: Vec<(u32, &'a str)> = syscalls
89 .iter()
90 .map(|name| (murmur3_32(name.as_ref()), name.as_ref()))
91 .collect();
92
93 entries.sort_by_key(|(hash, _)| *hash);
94
95 for i in 0..entries.len().saturating_sub(1) {
97 if entries[i].0 == entries[i + 1].0 {
98 panic!(
99 "Hash conflict detected between syscalls '{}' and '{}'",
100 entries[i].1,
101 entries[i + 1].1
102 );
103 }
104 }
105
106 entries
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn test_const_evaluation() {
115 const ABORT_HASH: u32 = murmur3_32("abort");
117 const SOL_LOG_HASH: u32 = murmur3_32("sol_log_");
118
119 const TEST_SYSCALLS: &[&str; 2] = &["abort", "sol_log_"];
121 const TEST_ENTRIES: &[(u32, &str); 2] = &compute_syscall_entries_const(TEST_SYSCALLS);
122 const TEST_MAP: SyscallMap<'static> = SyscallMap::from_entries(TEST_ENTRIES);
123
124 assert_eq!(TEST_MAP.get(ABORT_HASH), Some("abort"));
126 assert_eq!(TEST_MAP.get(SOL_LOG_HASH), Some("sol_log_"));
127 }
128
129 #[test]
130 fn test_nonexistent_syscall() {
131 const TEST_SYSCALLS: &[&str; 1] = &["test"];
133 const TEST_ENTRIES: &[(u32, &str); 1] = &compute_syscall_entries_const(TEST_SYSCALLS);
134 const TEST_MAP: SyscallMap<'static> = SyscallMap::from_entries(TEST_ENTRIES);
135
136 assert_eq!(TEST_MAP.get(0xDEADBEEF), None);
137 }
138
139 #[test]
140 fn test_dynamic_syscalls() {
141 let owned_syscalls: Vec<String> = vec![
144 String::from("my_custom_syscall"),
145 String::from("another_syscall"),
146 ];
147
148 let entries = compute_syscall_entries(&owned_syscalls);
150
151 let map = SyscallMap::from_entries(&entries);
153
154 let hash1 = murmur3_32("my_custom_syscall");
156 let hash2 = murmur3_32("another_syscall");
157
158 assert_eq!(map.get(hash1), Some("my_custom_syscall"));
159 assert_eq!(map.get(hash2), Some("another_syscall"));
160
161 }
163
164 #[test]
165 fn test_dynamic_syscalls_with_str_slices() {
166 let syscalls: Vec<&str> = vec!["syscall_a", "syscall_b", "syscall_c"];
168
169 let entries = compute_syscall_entries(&syscalls);
170 let map = SyscallMap::from_entries(&entries);
171
172 assert_eq!(map.get(murmur3_32("syscall_a")), Some("syscall_a"));
173 assert_eq!(map.get(murmur3_32("syscall_b")), Some("syscall_b"));
174 assert_eq!(map.get(murmur3_32("syscall_c")), Some("syscall_c"));
175 }
176
177 #[test]
178 fn test_static_custom_map() {
179 const CUSTOM_SYSCALLS: &[&str; 2] = &["test1", "test2"];
181 const CUSTOM_ENTRIES: &[(u32, &str); 2] = &compute_syscall_entries_const(CUSTOM_SYSCALLS);
182 const CUSTOM_MAP: SyscallMap<'static> = SyscallMap::from_entries(CUSTOM_ENTRIES);
183
184 assert_eq!(CUSTOM_MAP.get(murmur3_32("test1")), Some("test1"));
185 assert_eq!(CUSTOM_MAP.get(murmur3_32("test2")), Some("test2"));
186 }
187
188 #[test]
189 fn test_syscall_map_len_and_is_empty() {
190 const TEST_SYSCALLS: &[&str; 3] = &["a", "b", "c"];
192 const TEST_ENTRIES: &[(u32, &str); 3] = &compute_syscall_entries_const(TEST_SYSCALLS);
193 const TEST_MAP: SyscallMap<'static> = SyscallMap::from_entries(TEST_ENTRIES);
194
195 assert!(!TEST_MAP.is_empty());
196 assert_eq!(TEST_MAP.len(), 3);
197
198 const SINGLE: &[&str; 1] = &["x"];
200 const SINGLE_ENTRIES: &[(u32, &str); 1] = &compute_syscall_entries_const(SINGLE);
201 const SINGLE_MAP: SyscallMap<'static> = SyscallMap::from_entries(SINGLE_ENTRIES);
202
203 assert!(!SINGLE_MAP.is_empty());
204 assert_eq!(SINGLE_MAP.len(), 1);
205 }
206
207 #[test]
208 fn test_binary_search_edge_cases() {
209 const SINGLE: &[&str; 1] = &["single"];
211 const SINGLE_ENTRIES: &[(u32, &str); 1] = &compute_syscall_entries_const(SINGLE);
212 const SINGLE_MAP: SyscallMap<'static> = SyscallMap::from_entries(SINGLE_ENTRIES);
213
214 assert_eq!(SINGLE_MAP.get(murmur3_32("single")), Some("single"));
215 assert_eq!(SINGLE_MAP.get(0xFFFFFFFF), None);
216
217 const MULTI: &[&str; 5] = &["a", "b", "c", "d", "e"];
219 const MULTI_ENTRIES: &[(u32, &str); 5] = &compute_syscall_entries_const(MULTI);
220 const MULTI_MAP: SyscallMap<'static> = SyscallMap::from_entries(MULTI_ENTRIES);
221
222 assert_eq!(MULTI_MAP.get(murmur3_32("a")), Some("a"));
224 assert_eq!(MULTI_MAP.get(murmur3_32("b")), Some("b"));
225 assert_eq!(MULTI_MAP.get(murmur3_32("c")), Some("c"));
226 assert_eq!(MULTI_MAP.get(murmur3_32("d")), Some("d"));
227 assert_eq!(MULTI_MAP.get(murmur3_32("e")), Some("e"));
228 }
229
230 #[test]
231 #[should_panic(expected = "Hash conflict")]
232 fn test_compute_syscall_entries_hash_conflict_panic() {
233 let syscalls = vec!["duplicate".to_string(), "duplicate".to_string()];
234 let _ = compute_syscall_entries(&syscalls); }
236}