diff --git a/src/protocol/arena.rs b/src/protocol/arena.rs index df2d5ce1cd2ce9d45e97f3d4260f835a6fe7009a..68dc8b5cd6f8ad83dbeef578b351f0d2055e557a 100644 --- a/src/protocol/arena.rs +++ b/src/protocol/arena.rs @@ -3,14 +3,16 @@ use core::hash::Hash; use core::marker::PhantomData; pub struct Id { - pub(crate) index: u32, + // Not actually a signed index into the heap. But the index is set to -1 if + // we don't know an ID yet. This is checked during debug mode. + pub(crate) index: i32, _phantom: PhantomData, } impl Id { - pub(crate) fn new(index: u32) -> Self { - Self{ index, _phantom: Default::default() } - } + pub(crate) fn new_invalid() -> Self { Self{ index: -1, _phantom: Default::default() } } + pub(crate) fn new(index: i32) -> Self { Self{ index, _phantom: Default::default() } } + pub(crate) fn is_invalid(&self) -> bool { self.index < 0 } } #[derive(Debug)] @@ -46,15 +48,19 @@ impl Arena { pub fn new() -> Self { Self { store: vec![] } } + pub fn alloc_with_id(&mut self, f: impl FnOnce(Id) -> T) -> Id { - use std::convert::TryFrom; - let id = Id::new(u32::try_from(self.store.len()).expect("Out of capacity!")); + // Lets keep this a runtime assert. + assert!(self.store.len() < i32::max_value() as usize, "Arena out of capacity"); + let id = Id::new(self.store.len() as i32); self.store.push(f(id)); id } + pub fn iter(&self) -> impl Iterator { self.store.iter() } + pub fn len(&self) -> usize { self.store.len() } @@ -62,11 +68,13 @@ impl Arena { impl core::ops::Index> for Arena { type Output = T; fn index(&self, id: Id) -> &Self::Output { + debug_assert!(!id.is_invalid(), "attempted to index into Arena with an invalid id (index < 0)"); self.store.index(id.index as usize) } } impl core::ops::IndexMut> for Arena { fn index_mut(&mut self, id: Id) -> &mut Self::Output { + debug_assert!(!id.is_invalid(), "attempted to index_mut into Arena with an invalid id (index < 0)"); self.store.index_mut(id.index as usize) } } \ No newline at end of file