use std::convert::TryFrom; use std::fmt; use std::mem; pub struct Table { elems: Vec>, next: usize, } #[derive(Debug)] pub enum RemoveError { NotAllocated, } enum Slot { Empty { next_empty: usize }, Full { item: Box }, } impl Table { /// Creates a new empty table pub fn new() -> Table { Table { elems: Vec::new(), next: 0, } } /// Inserts an item into this table, returning the index that it was /// inserted at. pub fn insert(&mut self, item: T) -> u32 { if self.next == self.elems.len() { let next_empty = self.next + 1; self.elems.push(Slot::Empty { next_empty }); } let index = self.next; let ret = u32::try_from(index).unwrap(); self.next = match &self.elems[index] { Slot::Empty { next_empty } => *next_empty, Slot::Full { .. } => unreachable!(), }; self.elems[index] = Slot::Full { item: Box::new(item), }; return ret; } /// Borrows an item from this table. /// /// Returns `None` if the index is not allocated at this time. Otherwise /// returns `Some` with a borrow of the item from this table. pub fn get(&self, item: u32) -> Option<&T> { let index = usize::try_from(item).unwrap(); match self.elems.get(index)? { Slot::Empty { .. } => None, Slot::Full { item } => Some(item), } } /// Removes an item from this table. /// /// On success it returns back the original item. pub fn remove(&mut self, item: u32) -> Result { let index = usize::try_from(item).unwrap(); let new_empty = Slot::Empty { next_empty: self.next, }; let slot = self.elems.get_mut(index).ok_or(RemoveError::NotAllocated)?; // Assume that `item` is valid, and if it is, we can return quickly match mem::replace(slot, new_empty) { Slot::Full { item } => { self.next = index; Ok(*item) } // Oops `item` wasn't valid, put it back where we found it and then // figure out why it was invalid Slot::Empty { next_empty } => { *slot = Slot::Empty { next_empty }; Err(RemoveError::NotAllocated) } } } } impl Default for Table { fn default() -> Table { Table::new() } } impl fmt::Debug for Table { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Table") .field("capacity", &self.elems.capacity()) .finish() } } impl fmt::Display for RemoveError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { RemoveError::NotAllocated => f.write_str("invalid handle index"), } } } impl std::error::Error for RemoveError {} #[cfg(test)] mod tests { use super::*; #[test] fn simple() { let mut table = Table::new(); assert_eq!(table.insert(0), 0); assert_eq!(table.insert(100), 1); assert_eq!(table.insert(200), 2); assert_eq!(*table.get(0).unwrap(), 0); assert_eq!(*table.get(1).unwrap(), 100); assert_eq!(*table.get(2).unwrap(), 200); assert!(table.get(100).is_none()); assert!(table.remove(0).is_ok()); assert!(table.get(0).is_none()); assert_eq!(table.insert(1), 0); assert!(table.get(0).is_some()); table.get(1).unwrap(); assert!(table.remove(1).is_ok()); assert!(table.remove(1).is_err()); assert!(table.remove(2).is_ok()); assert!(table.remove(0).is_ok()); assert_eq!(table.insert(100), 0); assert_eq!(table.insert(100), 2); assert_eq!(table.insert(100), 1); assert_eq!(table.insert(100), 3); } }