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,
73 entries[i + 1].1
74 ));
75 }
76 }
77
78 Ok(Self { entries })
79 }
80
81 pub fn from_names(names: &[&str]) -> Result<Self, String> {
83 Self::new(names.iter().map(|&s| s.to_string()).collect())
84 }
85
86 pub fn get(&self, hash: u32) -> Option<&str> {
88 match self.entries.binary_search_by_key(&hash, |(h, _)| *h) {
89 Ok(idx) => Some(&self.entries[idx].1),
90 Err(_) => None,
91 }
92 }
93
94 pub fn add(&mut self, name: String) -> Result<(), String> {
96 let hash = murmur3_32(&name);
97
98 match self.entries.binary_search_by_key(&hash, |(h, _)| *h) {
100 Ok(_) => Err(format!(
101 "Hash conflict: '{}' conflicts with existing syscall",
102 name
103 )),
104 Err(pos) => {
105 self.entries.insert(pos, (hash, name));
106 Ok(())
107 }
108 }
109 }
110
111 pub fn len(&self) -> usize {
112 self.entries.len()
113 }
114
115 pub fn is_empty(&self) -> bool {
116 self.entries.is_empty()
117 }
118}
119
120impl<'a> From<&SyscallMap<'a>> for DynamicSyscallMap {
122 fn from(static_map: &SyscallMap<'a>) -> Self {
123 let entries = static_map
124 .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],
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>>(syscalls: &'a [T]) -> Vec<(u32, &'a str)> {
169 let mut entries: Vec<(u32, &'a str)> = syscalls
170 .iter()
171 .map(|name| (murmur3_32(name.as_ref()), name.as_ref()))
172 .collect();
173
174 entries.sort_by_key(|(hash, _)| *hash);
175
176 for i in 0..entries.len().saturating_sub(1) {
178 if entries[i].0 == entries[i + 1].0 {
179 panic!(
180 "Hash conflict detected between syscalls '{}' and '{}'",
181 entries[i].1,
182 entries[i + 1].1
183 );
184 }
185 }
186
187 entries
188}
189
190pub const fn murmur3_32(buf: &str) -> u32 {
191 const fn pre_mix(buf: [u8; 4]) -> u32 {
192 u32::from_le_bytes(buf)
193 .wrapping_mul(0xcc9e2d51)
194 .rotate_left(15)
195 .wrapping_mul(0x1b873593)
196 }
197
198 let mut hash = 0;
199 let mut i = 0;
200 let buf = buf.as_bytes();
201
202 while i < buf.len() / 4 {
203 let buf = [buf[i * 4], buf[i * 4 + 1], buf[i * 4 + 2], buf[i * 4 + 3]];
204 hash ^= pre_mix(buf);
205 hash = hash.rotate_left(13);
206 hash = hash.wrapping_mul(5).wrapping_add(0xe6546b64);
207
208 i += 1;
209 }
210
211 match buf.len() % 4 {
212 0 => {}
213 1 => {
214 hash = hash ^ pre_mix([buf[i * 4], 0, 0, 0]);
215 }
216 2 => {
217 hash = hash ^ pre_mix([buf[i * 4], buf[i * 4 + 1], 0, 0]);
218 }
219 3 => {
220 hash = hash ^ pre_mix([buf[i * 4], buf[i * 4 + 1], buf[i * 4 + 2], 0]);
221 }
222 _ => { }
223 }
224
225 hash = hash ^ buf.len() as u32;
226 hash = hash ^ (hash.wrapping_shr(16));
227 hash = hash.wrapping_mul(0x85ebca6b);
228 hash = hash ^ (hash.wrapping_shr(13));
229 hash = hash.wrapping_mul(0xc2b2ae35);
230 hash = hash ^ (hash.wrapping_shr(16));
231
232 hash
233}
234
235#[cfg(test)]
236mod tests {
237 use {
238 super::*,
239 crate::syscalls::{REGISTERED_SYSCALLS, SYSCALLS},
240 };
241
242 #[test]
243 fn test_syscall_lookup() {
244 for &name in REGISTERED_SYSCALLS.iter() {
246 let hash = murmur3_32(name);
247 assert_eq!(
248 SYSCALLS.get(hash),
249 Some(name),
250 "Failed to find syscall: {}",
251 name
252 );
253 }
254 }
255
256 #[test]
257 fn test_const_evaluation() {
258 const ABORT_HASH: u32 = murmur3_32("abort");
260 const SOL_LOG_HASH: u32 = murmur3_32("sol_log_");
261
262 assert_eq!(SYSCALLS.get(ABORT_HASH), Some("abort"));
264 assert_eq!(SYSCALLS.get(SOL_LOG_HASH), Some("sol_log_"));
265 }
266
267 #[test]
268 fn test_nonexistent_syscall() {
269 assert_eq!(SYSCALLS.get(0xDEADBEEF), None);
271 }
272
273 #[test]
274 fn test_dynamic_syscalls() {
275 let owned_syscalls: Vec<String> = vec![
278 String::from("my_custom_syscall"),
279 String::from("another_syscall"),
280 ];
281
282 let entries = compute_syscall_entries(&owned_syscalls);
284
285 let map = SyscallMap::from_entries(&entries);
287
288 let hash1 = murmur3_32("my_custom_syscall");
290 let hash2 = murmur3_32("another_syscall");
291
292 assert_eq!(map.get(hash1), Some("my_custom_syscall"));
293 assert_eq!(map.get(hash2), Some("another_syscall"));
294
295 }
297
298 #[test]
299 fn test_dynamic_syscalls_with_str_slices() {
300 let syscalls: Vec<&str> = vec!["syscall_a", "syscall_b", "syscall_c"];
302
303 let entries = compute_syscall_entries(&syscalls);
304 let map = SyscallMap::from_entries(&entries);
305
306 assert_eq!(map.get(murmur3_32("syscall_a")), Some("syscall_a"));
307 assert_eq!(map.get(murmur3_32("syscall_b")), Some("syscall_b"));
308 assert_eq!(map.get(murmur3_32("syscall_c")), Some("syscall_c"));
309 }
310
311 #[test]
312 fn test_static_custom_map() {
313 const CUSTOM_SYSCALLS: &[&str; 2] = &["test1", "test2"];
315 const CUSTOM_ENTRIES: &[(u32, &str); 2] = &compute_syscall_entries_const(CUSTOM_SYSCALLS);
316 const CUSTOM_MAP: SyscallMap<'static> = SyscallMap::from_entries(CUSTOM_ENTRIES);
317
318 assert_eq!(CUSTOM_MAP.get(murmur3_32("test1")), Some("test1"));
319 assert_eq!(CUSTOM_MAP.get(murmur3_32("test2")), Some("test2"));
320 }
321
322 #[test]
323 fn test_dynamic_mutable_map() {
324 let mut map = DynamicSyscallMap::from_names(&["initial_syscall"]).unwrap();
326
327 assert_eq!(
329 map.get(murmur3_32("initial_syscall")),
330 Some("initial_syscall")
331 );
332
333 map.add("runtime_syscall_1".to_string()).unwrap();
335 map.add("runtime_syscall_2".to_string()).unwrap();
336
337 assert_eq!(
339 map.get(murmur3_32("initial_syscall")),
340 Some("initial_syscall")
341 );
342 assert_eq!(
343 map.get(murmur3_32("runtime_syscall_1")),
344 Some("runtime_syscall_1")
345 );
346 assert_eq!(
347 map.get(murmur3_32("runtime_syscall_2")),
348 Some("runtime_syscall_2")
349 );
350
351 assert_eq!(map.get(0xDEADBEEF), None);
353
354 assert_eq!(map.len(), 3);
356 }
357
358 #[test]
359 fn test_dynamic_map_with_owned_strings() {
360 let syscalls = vec![
362 String::from("custom_1"),
363 String::from("custom_2"),
364 String::from("custom_3"),
365 ];
366
367 let mut map = DynamicSyscallMap::new(syscalls).unwrap();
368
369 assert_eq!(map.get(murmur3_32("custom_1")), Some("custom_1"));
370 assert_eq!(map.get(murmur3_32("custom_2")), Some("custom_2"));
371 assert_eq!(map.get(murmur3_32("custom_3")), Some("custom_3"));
372
373 map.add("custom_4".to_string()).unwrap();
375 assert_eq!(map.get(murmur3_32("custom_4")), Some("custom_4"));
376 }
377
378 #[test]
379 fn test_convert_static_to_dynamic() {
380 let dynamic = DynamicSyscallMap::from(&SYSCALLS);
382
383 for &name in REGISTERED_SYSCALLS.iter() {
385 let hash = murmur3_32(name);
386 assert_eq!(
387 dynamic.get(hash),
388 Some(name),
389 "Failed to find syscall: {}",
390 name
391 );
392 }
393
394 let mut dynamic_mut = dynamic;
396 dynamic_mut.add("my_custom_syscall".to_string()).unwrap();
397
398 assert_eq!(
399 dynamic_mut.get(murmur3_32("my_custom_syscall")),
400 Some("my_custom_syscall")
401 );
402
403 assert_eq!(dynamic_mut.get(murmur3_32("abort")), Some("abort"));
405
406 assert_eq!(dynamic_mut.len(), REGISTERED_SYSCALLS.len() + 1);
408 }
409
410 #[test]
411 fn test_syscall_map_len_and_is_empty() {
412 assert!(SYSCALLS.len() > 0);
414 assert!(!SYSCALLS.is_empty());
415
416 const SINGLE_ENTRIES: &[(u32, &str)] = &[(123, "test")];
418 const SINGLE_MAP: SyscallMap = SyscallMap::from_entries(SINGLE_ENTRIES);
419 assert_eq!(SINGLE_MAP.len(), 1);
420 assert!(!SINGLE_MAP.is_empty());
421 }
422
423 #[test]
424 fn test_dynamic_map_len_and_is_empty() {
425 let empty_map = DynamicSyscallMap::new(vec![]).unwrap();
427 assert_eq!(empty_map.len(), 0);
428 assert!(empty_map.is_empty());
429
430 let map = DynamicSyscallMap::from_names(&["test"]).unwrap();
432 assert_eq!(map.len(), 1);
433 assert!(!map.is_empty());
434 }
435
436 #[test]
437 fn test_dynamic_map_add_duplicate() {
438 let mut map = DynamicSyscallMap::from_names(&["existing"]).unwrap();
439 let result = map.add("existing".to_string());
440 assert!(result.is_err());
441 }
442
443 #[test]
444 fn test_dynamic_map_hash_conflict_in_creation() {
445 let syscalls = vec![String::from("test"), String::from("test")];
446 let result = DynamicSyscallMap::new(syscalls);
447 assert!(result.is_err());
449 if let Err(msg) = result {
450 assert!(msg.contains("Hash conflict"));
451 assert!(msg.contains("test"));
452 }
453 }
454}