syscalls/
map.rs

1use super::Sysno;
2use crate::set::SysnoSetIter;
3use crate::SysnoSet;
4use core::fmt;
5use core::mem::MaybeUninit;
6
7type DataArray<T> = [MaybeUninit<T>; Sysno::table_size()];
8
9/// A map of syscalls to a type `T`.
10///
11/// This provides constant-time lookup of syscalls within a static array.
12///
13/// # Examples
14///
15/// ```
16/// # use syscalls::{Sysno, SysnoMap};
17/// struct Point { x: i32, y: i32 }
18///
19/// let mut map = SysnoMap::new();
20/// map.insert(Sysno::openat, Point { x: 1, y: 2 });
21/// assert!(map.get(Sysno::openat).is_some());
22/// ```
23///
24/// Use function callbacks:
25/// ```
26/// # use syscalls::{Sysno, SysnoMap};
27/// let mut map = SysnoMap::<fn() -> i32>::new();
28/// map.insert(Sysno::openat, || 1);
29/// map.insert(Sysno::close, || -1);
30/// assert_eq!(map.get(Sysno::openat).unwrap()(), 1);
31/// assert_eq!(map.get(Sysno::close).unwrap()(), -1);
32/// ```
33///
34/// ```
35/// # use syscalls::{Sysno, SysnoMap};
36/// let mut syscalls = SysnoMap::from_iter([
37///     (Sysno::openat, 0),
38///     (Sysno::close, 42),
39/// ]);
40///
41/// assert!(!syscalls.is_empty());
42/// assert_eq!(syscalls.remove(Sysno::openat), Some(0));
43/// assert_eq!(syscalls.insert(Sysno::close, 4), Some(42));
44/// assert!(syscalls.contains_key(Sysno::close));
45/// assert_eq!(syscalls.get(Sysno::close), Some(&4));
46/// assert_eq!(syscalls.insert(Sysno::close, 11), Some(4));
47/// assert_eq!(syscalls.count(), 1);
48/// assert_eq!(syscalls.remove(Sysno::close), Some(11));
49/// assert!(syscalls.is_empty());
50/// ```
51pub struct SysnoMap<T> {
52    is_set: SysnoSet,
53    data: DataArray<T>,
54}
55
56/// Get internal data index based on sysno value
57#[inline]
58const fn get_idx(sysno: Sysno) -> usize {
59    (sysno.id() as usize) - (Sysno::first().id() as usize)
60}
61
62impl<T> Default for SysnoMap<T> {
63    fn default() -> Self {
64        Self::new()
65    }
66}
67
68impl<T> SysnoMap<T> {
69    /// Initializes an empty syscall map.
70    pub const fn new() -> Self {
71        Self {
72            is_set: SysnoSet::empty(),
73            data: unsafe { MaybeUninit::uninit().assume_init() },
74        }
75    }
76
77    /// Returns true if the map contains the given syscall.
78    pub const fn contains_key(&self, sysno: Sysno) -> bool {
79        self.is_set.contains(sysno)
80    }
81
82    /// Clears the map, removing all syscalls.
83    pub fn clear(&mut self) {
84        for sysno in &self.is_set {
85            unsafe { self.data[get_idx(sysno)].assume_init_drop() }
86        }
87        self.is_set.clear();
88    }
89
90    /// Returns true if the map is empty. Athough this is an O(1) operation
91    /// (because the total number of syscalls is always constant), it must
92    /// always iterate over the whole map to determine if it is empty or not.
93    /// Thus, this may have a large, constant overhead.
94    pub fn is_empty(&self) -> bool {
95        self.is_set.is_empty()
96    }
97
98    /// Returns the number of syscalls in the map. Although This is an O(1)
99    /// operation (because the total number of syscalls is always constant), it
100    /// must always iterate over the whole map to determine how many items it
101    /// has. Thus, this may have a large, constant overhead.
102    pub fn count(&self) -> usize {
103        self.is_set.count()
104    }
105
106    /// Inserts the given syscall into the map. Returns true if the syscall was
107    /// not already in the map.
108    pub fn insert(&mut self, sysno: Sysno, value: T) -> Option<T> {
109        let uninit = &mut self.data[get_idx(sysno)];
110        if self.is_set.insert(sysno) {
111            // Was not already in the set.
112            uninit.write(value);
113            None
114        } else {
115            // Was already in the set.
116            let old = core::mem::replace(uninit, MaybeUninit::new(value));
117            Some(unsafe { old.assume_init() })
118        }
119    }
120
121    /// Removes the given syscall from the map. Returns old value if the syscall
122    /// was in the map.
123    pub fn remove(&mut self, sysno: Sysno) -> Option<T> {
124        if self.is_set.remove(sysno) {
125            let old = core::mem::replace(
126                &mut self.data[get_idx(sysno)],
127                MaybeUninit::uninit(),
128            );
129            Some(unsafe { old.assume_init() })
130        } else {
131            None
132        }
133    }
134
135    /// Returns a reference to the value corresponding to `sysno`. Returns
136    /// `None` if the syscall is not in the map.
137    pub fn get(&self, sysno: Sysno) -> Option<&T> {
138        if self.is_set.contains(sysno) {
139            Some(unsafe { self.data[get_idx(sysno)].assume_init_ref() })
140        } else {
141            None
142        }
143    }
144
145    /// Returns a mutable reference to the value corresponding to `sysno`.
146    /// Returns `None` if the syscall is not in the map.
147    pub fn get_mut(&mut self, sysno: Sysno) -> Option<&mut T> {
148        if self.is_set.contains(sysno) {
149            Some(unsafe { self.data[get_idx(sysno)].assume_init_mut() })
150        } else {
151            None
152        }
153    }
154
155    /// Returns an iterator that iterates over the syscalls contained in the map.
156    pub fn iter(&self) -> SysnoMapIter<'_, T> {
157        SysnoMapIter {
158            iter: self.is_set.iter(),
159            data: &self.data,
160        }
161    }
162
163    /// Returns an iterator that iterates over all enabled values contained in
164    /// the map.
165    pub fn values(&self) -> SysnoMapValues<'_, T> {
166        SysnoMapValues(self.is_set.iter(), &self.data)
167    }
168}
169
170impl<T: Copy> SysnoMap<T> {
171    /// Initialize a syscall map from the given slice. Note that `T` must be
172    /// `Copy` due to `const fn` limitations.
173    ///
174    /// This is useful for constructing a static callback table.
175    ///
176    /// # Example
177    ///
178    /// ```
179    /// use syscalls::{Sysno, SysnoMap};
180    ///
181    /// static CALLBACKS: SysnoMap<fn() -> i32> = SysnoMap::from_slice(&[
182    ///     (Sysno::openat, || 42),
183    ///     (Sysno::close, || 43),
184    /// ]);
185    ///
186    /// static DESCRIPTIONS: SysnoMap<&'static str> = SysnoMap::from_slice(&[
187    ///     (Sysno::openat, "open and possibly create a file"),
188    ///     (Sysno::close, "close a file descriptor"),
189    /// ]);
190    ///
191    /// assert_eq!(CALLBACKS[Sysno::openat](), 42);
192    /// assert_eq!(DESCRIPTIONS[Sysno::close], "close a file descriptor");
193    /// ```
194    pub const fn from_slice(slice: &[(Sysno, T)]) -> Self {
195        let mut data: DataArray<T> =
196            unsafe { MaybeUninit::uninit().assume_init() };
197
198        let mut is_set = SysnoSet::empty();
199
200        // Use while-loop because for-loops are not yet allowed in const-fns.
201        // https://github.com/rust-lang/rust/issues/87575
202        let mut i = 0;
203        while i < slice.len() {
204            let sysno = slice[i].0;
205            let (idx, mask) = SysnoSet::get_idx_mask(sysno);
206            is_set.data[idx] |= mask;
207            data[get_idx(sysno)] = MaybeUninit::new(slice[i].1);
208            i += 1;
209        }
210
211        Self { is_set, data }
212    }
213}
214
215impl<T: Clone> SysnoMap<T> {
216    /// Initializes all possible syscalls in the map with the given default
217    /// value.
218    pub fn init_all(default: &T) -> Self {
219        SysnoSet::all()
220            .iter()
221            .map(|v| (v, default.clone()))
222            .collect()
223    }
224}
225
226impl<T> Drop for SysnoMap<T> {
227    fn drop(&mut self) {
228        self.clear();
229    }
230}
231
232/// An iterator over the syscall (number, value) pairs contained in a
233/// [`SysnoMap`].
234pub struct SysnoMapIter<'a, T> {
235    iter: SysnoSetIter<'a>,
236    data: &'a DataArray<T>,
237}
238
239impl<'a, T> Iterator for SysnoMapIter<'a, T> {
240    type Item = (Sysno, &'a T);
241
242    fn next(&mut self) -> Option<Self::Item> {
243        self.iter.next().map(|sysno| {
244            let value = unsafe { self.data[get_idx(sysno)].assume_init_ref() };
245            (sysno, value)
246        })
247    }
248}
249
250/// An iterator over the syscall values contained in a [`SysnoMap`].
251pub struct SysnoMapValues<'a, T>(SysnoSetIter<'a>, &'a DataArray<T>);
252
253impl<'a, T> Iterator for SysnoMapValues<'a, T> {
254    type Item = &'a T;
255
256    fn next(&mut self) -> Option<Self::Item> {
257        self.0
258            .next()
259            .map(|sysno| unsafe { self.1[get_idx(sysno)].assume_init_ref() })
260    }
261}
262
263impl<T: fmt::Debug> fmt::Debug for SysnoMap<T> {
264    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
265        f.debug_map().entries(self.iter()).finish()
266    }
267}
268
269impl<T> Extend<(Sysno, T)> for SysnoMap<T> {
270    fn extend<I: IntoIterator<Item = (Sysno, T)>>(&mut self, iter: I) {
271        for (sysno, value) in iter {
272            self.insert(sysno, value);
273        }
274    }
275}
276
277impl<T> FromIterator<(Sysno, T)> for SysnoMap<T> {
278    fn from_iter<I>(iter: I) -> Self
279    where
280        I: IntoIterator<Item = (Sysno, T)>,
281    {
282        let mut map = SysnoMap::new();
283        map.extend(iter);
284        map
285    }
286}
287
288impl<'a, T> IntoIterator for &'a SysnoMap<T> {
289    type Item = (Sysno, &'a T);
290    type IntoIter = SysnoMapIter<'a, T>;
291
292    fn into_iter(self) -> Self::IntoIter {
293        self.iter()
294    }
295}
296
297impl<T> core::ops::Index<Sysno> for SysnoMap<T> {
298    type Output = T;
299
300    fn index(&self, sysno: Sysno) -> &T {
301        self.get(sysno).expect("no entry found for key")
302    }
303}
304
305impl<T> core::ops::IndexMut<Sysno> for SysnoMap<T> {
306    fn index_mut(&mut self, sysno: Sysno) -> &mut T {
307        self.get_mut(sysno).expect("no entry found for key")
308    }
309}
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314
315    #[test]
316    fn test_default() {
317        assert_eq!(SysnoMap::<u8>::new().count(), 0);
318    }
319
320    #[test]
321    fn test_is_empty() {
322        let mut map = SysnoMap::new();
323        assert!(map.is_empty());
324        assert_eq!(map.insert(Sysno::openat, 42), None);
325        assert!(!map.is_empty());
326        assert_eq!(map.get(Sysno::openat), Some(&42));
327        map.remove(Sysno::openat);
328        assert!(map.is_empty());
329        assert_eq!(map.get(Sysno::openat), None);
330    }
331
332    #[test]
333    fn test_count() {
334        let mut map = SysnoMap::new();
335        assert_eq!(map.count(), 0);
336        assert_eq!(map.insert(Sysno::openat, 42), None);
337        assert_eq!(map.count(), 1);
338        assert_eq!(map.insert(Sysno::first(), 4), None);
339        assert_eq!(map.count(), 2);
340        assert_eq!(map.insert(Sysno::last(), 5), None);
341        assert_eq!(map.count(), 3);
342        assert_eq!(map.values().sum::<u8>(), 51);
343    }
344
345    #[test]
346    fn test_fn() {
347        let mut map = SysnoMap::<fn() -> i32>::new();
348        map.insert(Sysno::openat, || 1);
349        map.insert(Sysno::close, || -1);
350        assert_eq!(map.get(Sysno::openat).unwrap()(), 1);
351        assert_eq!(map.get(Sysno::close).unwrap()(), -1);
352    }
353
354    #[test]
355    fn test_fn_macro() {
356        type Handler = fn() -> i32;
357        let map = SysnoMap::from_iter([
358            (Sysno::openat, (|| 1) as Handler),
359            (Sysno::close, (|| -1) as Handler),
360        ]);
361        assert_eq!(map.get(Sysno::openat).unwrap()(), 1);
362        assert_eq!(map.get(Sysno::close).unwrap()(), -1);
363    }
364
365    #[test]
366    fn test_insert_remove() {
367        let mut map = SysnoMap::new();
368        assert_eq!(map.insert(Sysno::openat, 42), None);
369        assert!(map.contains_key(Sysno::openat));
370        assert_eq!(map.count(), 1);
371
372        assert_eq!(map.insert(Sysno::openat, 4), Some(42));
373        assert!(map.contains_key(Sysno::openat));
374        assert_eq!(map.count(), 1);
375
376        assert_eq!(map.remove(Sysno::openat), Some(4));
377        assert!(!map.contains_key(Sysno::openat));
378        assert_eq!(map.count(), 0);
379
380        assert_eq!(map.remove(Sysno::openat), None);
381    }
382
383    #[cfg(feature = "std")]
384    #[test]
385    fn test_debug() {
386        let map = SysnoMap::from_iter([(Sysno::read, 42), (Sysno::openat, 10)]);
387        let result = format!("{:?}", map);
388        // The order of the debug output is not guaranteed, so we can't do an
389        // exact match.
390        assert_eq!(result.len(), "{read: 42, openat: 10}".len());
391        assert!(result.starts_with('{'));
392        assert!(result.ends_with('}'));
393        assert!(result.contains(", "));
394        assert!(result.contains("read: 42"));
395        assert!(result.contains("openat: 10"));
396    }
397
398    #[cfg(feature = "std")]
399    #[test]
400    fn test_iter() {
401        let map = SysnoMap::from_iter([(Sysno::read, 42), (Sysno::openat, 10)]);
402        assert_eq!(map.iter().collect::<Vec<_>>().len(), 2);
403    }
404
405    #[test]
406    fn test_into_iter() {
407        let map = SysnoMap::from_iter([(Sysno::read, 42), (Sysno::openat, 10)]);
408        assert_eq!((&map).into_iter().count(), 2);
409    }
410
411    #[test]
412    fn test_init_all() {
413        let map = SysnoMap::init_all(&42);
414        assert_eq!(map.get(Sysno::openat), Some(&42));
415        assert_eq!(map.get(Sysno::close), Some(&42));
416    }
417}