1pub struct SyscallMap<'a> {
4 entries: &'a [(u32, &'a str)],
5}
6
7impl<'a> SyscallMap<'a> {
8 pub const fn from_entries(entries: &'a [(u32, &'a str)]) -> Self {
11 let mut i = 0;
13 while i < entries.len() - 1 {
14 if entries[i].0 == entries[i + 1].0 {
15 panic!("Hash conflict detected between syscalls");
16 }
17 i += 1;
18 }
19
20 Self { entries }
21 }
22
23 pub const fn get(&self, hash: u32) -> Option<&'a str> {
24 let mut left = 0;
26 let mut right = self.entries.len();
27
28 while left < right {
29 let mid = (left + right) / 2;
30 if self.entries[mid].0 == hash {
31 return Some(self.entries[mid].1);
32 } else if self.entries[mid].0 < hash {
33 left = mid + 1;
34 } else {
35 right = mid;
36 }
37 }
38 None
39 }
40
41 pub const fn len(&self) -> usize {
42 self.entries.len()
43 }
44
45 pub const fn is_empty(&self) -> bool {
46 self.entries.is_empty()
47 }
48}
49
50pub struct DynamicSyscallMap {
53 entries: Vec<(u32, String)>,
55}
56
57impl DynamicSyscallMap {
58 pub fn new(syscalls: Vec<String>) -> Result<Self, String> {
60 let mut entries: Vec<(u32, String)> = syscalls
61 .into_iter()
62 .map(|name| (murmur3_32(&name), name))
63 .collect();
64
65 entries.sort_by_key(|(hash, _)| *hash);
66
67 for i in 0..entries.len().saturating_sub(1) {
69 if entries[i].0 == entries[i + 1].0 {
70 return Err(format!(
71 "Hash conflict detected between syscalls '{}' and '{}'",
72 entries[i].1, entries[i + 1].1
73 ));
74 }
75 }
76
77 Ok(Self { entries })
78 }
79
80 pub fn from_names(names: &[&str]) -> Result<Self, String> {
82 Self::new(names.iter().map(|&s| s.to_string()).collect())
83 }
84
85 pub fn get(&self, hash: u32) -> Option<&str> {
87 match self.entries.binary_search_by_key(&hash, |(h, _)| *h) {
88 Ok(idx) => Some(&self.entries[idx].1),
89 Err(_) => None,
90 }
91 }
92
93 pub fn add(&mut self, name: String) -> Result<(), String> {
95 let hash = murmur3_32(&name);
96
97 match self.entries.binary_search_by_key(&hash, |(h, _)| *h) {
99 Ok(_) => {
100 return Err(format!(
101 "Hash conflict: '{}' conflicts with existing syscall",
102 name
103 ));
104 }
105 Err(pos) => {
106 self.entries.insert(pos, (hash, name));
107 Ok(())
108 }
109 }
110 }
111
112 pub fn len(&self) -> usize {
113 self.entries.len()
114 }
115
116 pub fn is_empty(&self) -> bool {
117 self.entries.is_empty()
118 }
119}
120
121impl<'a> From<&SyscallMap<'a>> for DynamicSyscallMap {
123 fn from(static_map: &SyscallMap<'a>) -> Self {
124 let entries = static_map.entries
125 .iter()
126 .map(|(hash, name)| (*hash, name.to_string()))
127 .collect();
128
129 Self { entries }
130 }
131}
132
133pub const fn compute_syscall_entries_const<'a, const N: usize>(
136 syscalls: &'a [&'a str; N],
137) -> [(u32, &'a str); N] {
138 let mut entries: [(u32, &str); N] = [(0, ""); N];
139 let mut i = 0;
140 while i < N {
141 entries[i] = (murmur3_32(syscalls[i]), syscalls[i]);
142 i += 1;
143 }
144
145 let mut i = 0;
147 while i < N {
148 let mut j = 0;
149 while j < N - i - 1 {
150 if entries[j].0 > entries[j + 1].0 {
151 let temp = entries[j];
152 entries[j] = entries[j + 1];
153 entries[j + 1] = temp;
154 }
155 j += 1;
156 }
157 i += 1;
158 }
159
160 entries
161}
162
163pub fn compute_syscall_entries<'a, T: AsRef<str>>(
169 syscalls: &'a [T],
170) -> 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, entries[i + 1].1
184 );
185 }
186 }
187
188 entries
189}
190
191pub const fn murmur3_32(buf: &str) -> u32 {
192 const fn pre_mix(buf: [u8; 4]) -> u32 {
193 u32::from_le_bytes(buf)
194 .wrapping_mul(0xcc9e2d51)
195 .rotate_left(15)
196 .wrapping_mul(0x1b873593)
197 }
198
199 let mut hash = 0;
200 let mut i = 0;
201 let buf = buf.as_bytes();
202
203 while i < buf.len() / 4 {
204 let buf = [buf[i * 4], buf[i * 4 + 1], buf[i * 4 + 2], buf[i * 4 + 3]];
205 hash ^= pre_mix(buf);
206 hash = hash.rotate_left(13);
207 hash = hash.wrapping_mul(5).wrapping_add(0xe6546b64);
208
209 i += 1;
210 }
211
212 match buf.len() % 4 {
213 0 => {}
214 1 => {
215 hash = hash ^ pre_mix([buf[i * 4], 0, 0, 0]);
216 }
217 2 => {
218 hash = hash ^ pre_mix([buf[i * 4], buf[i * 4 + 1], 0, 0]);
219 }
220 3 => {
221 hash = hash ^ pre_mix([buf[i * 4], buf[i * 4 + 1], buf[i * 4 + 2], 0]);
222 }
223 _ => { }
224 }
225
226 hash = hash ^ buf.len() as u32;
227 hash = hash ^ (hash.wrapping_shr(16));
228 hash = hash.wrapping_mul(0x85ebca6b);
229 hash = hash ^ (hash.wrapping_shr(13));
230 hash = hash.wrapping_mul(0xc2b2ae35);
231 hash = hash ^ (hash.wrapping_shr(16));
232
233 hash
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn test_const_evaluation() {
242 const ABORT_HASH: u32 = murmur3_32("abort");
244 const SOL_LOG_HASH: u32 = murmur3_32("sol_log_");
245
246 const TEST_SYSCALLS: &[&str; 2] = &["abort", "sol_log_"];
248 const TEST_ENTRIES: &[(u32, &str); 2] = &compute_syscall_entries_const(TEST_SYSCALLS);
249 const TEST_MAP: SyscallMap<'static> = SyscallMap::from_entries(TEST_ENTRIES);
250
251 assert_eq!(TEST_MAP.get(ABORT_HASH), Some("abort"));
253 assert_eq!(TEST_MAP.get(SOL_LOG_HASH), Some("sol_log_"));
254 }
255
256 #[test]
257 fn test_nonexistent_syscall() {
258 const TEST_SYSCALLS: &[&str; 1] = &["test"];
260 const TEST_ENTRIES: &[(u32, &str); 1] = &compute_syscall_entries_const(TEST_SYSCALLS);
261 const TEST_MAP: SyscallMap<'static> = SyscallMap::from_entries(TEST_ENTRIES);
262
263 assert_eq!(TEST_MAP.get(0xDEADBEEF), None);
264 }
265
266 #[test]
267 fn test_dynamic_syscalls() {
268 let owned_syscalls: Vec<String> = vec![
271 String::from("my_custom_syscall"),
272 String::from("another_syscall"),
273 ];
274
275 let entries = compute_syscall_entries(&owned_syscalls);
277
278 let map = SyscallMap::from_entries(&entries);
280
281 let hash1 = murmur3_32("my_custom_syscall");
283 let hash2 = murmur3_32("another_syscall");
284
285 assert_eq!(map.get(hash1), Some("my_custom_syscall"));
286 assert_eq!(map.get(hash2), Some("another_syscall"));
287
288 }
290
291 #[test]
292 fn test_dynamic_syscalls_with_str_slices() {
293 let syscalls: Vec<&str> = vec!["syscall_a", "syscall_b", "syscall_c"];
295
296 let entries = compute_syscall_entries(&syscalls);
297 let map = SyscallMap::from_entries(&entries);
298
299 assert_eq!(map.get(murmur3_32("syscall_a")), Some("syscall_a"));
300 assert_eq!(map.get(murmur3_32("syscall_b")), Some("syscall_b"));
301 assert_eq!(map.get(murmur3_32("syscall_c")), Some("syscall_c"));
302 }
303
304 #[test]
305 fn test_static_custom_map() {
306 const CUSTOM_SYSCALLS: &[&str; 2] = &["test1", "test2"];
308 const CUSTOM_ENTRIES: &[(u32, &str); 2] = &compute_syscall_entries_const(CUSTOM_SYSCALLS);
309 const CUSTOM_MAP: SyscallMap<'static> = SyscallMap::from_entries(CUSTOM_ENTRIES);
310
311 assert_eq!(CUSTOM_MAP.get(murmur3_32("test1")), Some("test1"));
312 assert_eq!(CUSTOM_MAP.get(murmur3_32("test2")), Some("test2"));
313 }
314
315 #[test]
316 fn test_dynamic_mutable_map() {
317 let mut map = DynamicSyscallMap::from_names(&["initial_syscall"]).unwrap();
319
320 assert_eq!(
322 map.get(murmur3_32("initial_syscall")),
323 Some("initial_syscall")
324 );
325
326 map.add("runtime_syscall_1".to_string()).unwrap();
328 map.add("runtime_syscall_2".to_string()).unwrap();
329
330 assert_eq!(
332 map.get(murmur3_32("initial_syscall")),
333 Some("initial_syscall")
334 );
335 assert_eq!(
336 map.get(murmur3_32("runtime_syscall_1")),
337 Some("runtime_syscall_1")
338 );
339 assert_eq!(
340 map.get(murmur3_32("runtime_syscall_2")),
341 Some("runtime_syscall_2")
342 );
343
344 assert_eq!(map.get(0xDEADBEEF), None);
346
347 assert_eq!(map.len(), 3);
349 }
350
351 #[test]
352 fn test_dynamic_map_with_owned_strings() {
353 let syscalls = vec![
355 String::from("custom_1"),
356 String::from("custom_2"),
357 String::from("custom_3"),
358 ];
359
360 let mut map = DynamicSyscallMap::new(syscalls).unwrap();
361
362 assert_eq!(map.get(murmur3_32("custom_1")), Some("custom_1"));
363 assert_eq!(map.get(murmur3_32("custom_2")), Some("custom_2"));
364 assert_eq!(map.get(murmur3_32("custom_3")), Some("custom_3"));
365
366 map.add("custom_4".to_string()).unwrap();
368 assert_eq!(map.get(murmur3_32("custom_4")), Some("custom_4"));
369 }
370
371 #[test]
372 fn test_convert_static_to_dynamic() {
373 const TEST_SYSCALLS: &[&str; 3] = &["abort", "sol_log_", "sol_panic_"];
375 const TEST_ENTRIES: &[(u32, &str); 3] = &compute_syscall_entries_const(TEST_SYSCALLS);
376 const TEST_MAP: SyscallMap<'static> = SyscallMap::from_entries(TEST_ENTRIES);
377
378 let dynamic = DynamicSyscallMap::from(&TEST_MAP);
380
381 for &name in TEST_SYSCALLS.iter() {
383 let hash = murmur3_32(name);
384 assert_eq!(dynamic.get(hash), Some(name), "Failed to find syscall: {}", name);
385 }
386
387 let mut dynamic_mut = dynamic;
389 dynamic_mut.add("my_custom_syscall".to_string()).unwrap();
390
391 assert_eq!(
392 dynamic_mut.get(murmur3_32("my_custom_syscall")),
393 Some("my_custom_syscall")
394 );
395
396 assert_eq!(dynamic_mut.get(murmur3_32("abort")), Some("abort"));
398
399 assert_eq!(dynamic_mut.len(), TEST_SYSCALLS.len() + 1);
401 }
402}