wai_bindgen_wasmer/
table.rs

1use std::convert::TryFrom;
2use std::fmt;
3use std::mem;
4
5pub struct Table<T> {
6    elems: Vec<Slot<T>>,
7    next: usize,
8}
9
10#[derive(Debug)]
11pub enum RemoveError {
12    NotAllocated,
13}
14
15enum Slot<T> {
16    Empty { next_empty: usize },
17    Full { item: Box<T> },
18}
19
20impl<T> Table<T> {
21    /// Creates a new empty table
22    pub fn new() -> Table<T> {
23        Table {
24            elems: Vec::new(),
25            next: 0,
26        }
27    }
28
29    /// Inserts an item into this table, returning the index that it was
30    /// inserted at.
31    pub fn insert(&mut self, item: T) -> u32 {
32        if self.next == self.elems.len() {
33            let next_empty = self.next + 1;
34            self.elems.push(Slot::Empty { next_empty });
35        }
36        let index = self.next;
37        let ret = u32::try_from(index).unwrap();
38        self.next = match &self.elems[index] {
39            Slot::Empty { next_empty } => *next_empty,
40            Slot::Full { .. } => unreachable!(),
41        };
42        self.elems[index] = Slot::Full {
43            item: Box::new(item),
44        };
45        ret
46    }
47
48    /// Borrows an item from this table.
49    ///
50    /// Returns `None` if the index is not allocated at this time. Otherwise
51    /// returns `Some` with a borrow of the item from this table.
52    pub fn get(&self, item: u32) -> Option<&T> {
53        let index = usize::try_from(item).unwrap();
54        match self.elems.get(index)? {
55            Slot::Empty { .. } => None,
56            Slot::Full { item } => Some(item),
57        }
58    }
59
60    /// Removes an item from this table.
61    ///
62    /// On success it returns back the original item.
63    pub fn remove(&mut self, item: u32) -> Result<T, RemoveError> {
64        let index = usize::try_from(item).unwrap();
65        let new_empty = Slot::Empty {
66            next_empty: self.next,
67        };
68        let slot = self.elems.get_mut(index).ok_or(RemoveError::NotAllocated)?;
69
70        // Assume that `item` is valid, and if it is, we can return quickly
71        match mem::replace(slot, new_empty) {
72            Slot::Full { item } => {
73                self.next = index;
74                Ok(*item)
75            }
76
77            // Oops `item` wasn't valid, put it back where we found it and then
78            // figure out why it was invalid
79            Slot::Empty { next_empty } => {
80                *slot = Slot::Empty { next_empty };
81                Err(RemoveError::NotAllocated)
82            }
83        }
84    }
85}
86
87impl<T> Default for Table<T> {
88    fn default() -> Table<T> {
89        Table::new()
90    }
91}
92
93impl<T> fmt::Debug for Table<T> {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        f.debug_struct("Table")
96            .field("capacity", &self.elems.capacity())
97            .finish()
98    }
99}
100
101impl fmt::Display for RemoveError {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        match self {
104            RemoveError::NotAllocated => f.write_str("invalid handle index"),
105        }
106    }
107}
108
109impl std::error::Error for RemoveError {}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn simple() {
117        let mut table = Table::new();
118        assert_eq!(table.insert(0), 0);
119        assert_eq!(table.insert(100), 1);
120        assert_eq!(table.insert(200), 2);
121
122        assert_eq!(*table.get(0).unwrap(), 0);
123        assert_eq!(*table.get(1).unwrap(), 100);
124        assert_eq!(*table.get(2).unwrap(), 200);
125        assert!(table.get(100).is_none());
126
127        assert!(table.remove(0).is_ok());
128        assert!(table.get(0).is_none());
129        assert_eq!(table.insert(1), 0);
130        assert!(table.get(0).is_some());
131
132        table.get(1).unwrap();
133        assert!(table.remove(1).is_ok());
134        assert!(table.remove(1).is_err());
135
136        assert!(table.remove(2).is_ok());
137        assert!(table.remove(0).is_ok());
138
139        assert_eq!(table.insert(100), 0);
140        assert_eq!(table.insert(100), 2);
141        assert_eq!(table.insert(100), 1);
142        assert_eq!(table.insert(100), 3);
143    }
144}