diff --git a/src/libcretonne/entity_map.rs b/src/libcretonne/entity_map.rs new file mode 100644 index 0000000000..3e1f16a595 --- /dev/null +++ b/src/libcretonne/entity_map.rs @@ -0,0 +1,130 @@ +//! Densely numbered entity references as mapping keys. +//! +//! This module defines an `EntityRef` trait that should be implemented by reference types wrapping +//! a small integer index. The `EntityMap` data structure uses the dense index space to implement a +//! map with a vector. + +use std::vec::Vec; +use std::default::Default; +use std::marker::PhantomData; +use std::ops::{Index, IndexMut}; + +/// A type wrapping a small integer index should implement `EntityRef` so it can be used as the key +/// of an `EntityMap`. +pub trait EntityRef: Copy { + /// Create a new entity reference from a small integer. + /// This should crash if the requested index is not representable. + fn new(usize) -> Self; + + /// Get the index that was used to create this entity reference. + fn index(self) -> usize; +} + +/// A mapping `K -> V` for densely indexed entity references. +pub struct EntityMap + where K: EntityRef, + V: Clone + Default +{ + elems: Vec, + unused: PhantomData, +} + +impl EntityMap + where K: EntityRef, + V: Clone + Default +{ + /// Create a new empty map. + pub fn new() -> Self { + EntityMap { + elems: Vec::new(), + unused: PhantomData, + } + } + + /// Ensure that `k` is a valid key but adding default entries if necesssary. + pub fn ensure(&mut self, k: K) { + let idx = k.index(); + if idx >= self.elems.len() { + self.elems.resize(idx + 1, V::default()) + } + } + + /// Append `v` to the mapping, assigning a new key which is returned. + pub fn push(&mut self, v: V) -> K { + let k = K::new(self.elems.len()); + self.elems.push(v); + k + } +} + +/// Immutable indexing into an `EntityMap`. +/// The indexed value must have been accessed mutably previously, or the key passed to `ensure()`. +impl Index for EntityMap + where K: EntityRef, + V: Clone + Default +{ + type Output = V; + + fn index(&self, k: K) -> &V { + &self.elems[k.index()] + } +} + +/// Mutable indexing into an `EntityMap`. +/// The map is resized automatically if the key has not been used before. +impl IndexMut for EntityMap + where K: EntityRef, + V: Clone + Default +{ + fn index_mut(&mut self, k: K) -> &mut V { + self.ensure(k); + &mut self.elems[k.index()] + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // EntityRef impl for testing. + #[derive(Clone, Copy)] + struct E(u32); + + impl EntityRef for E { + fn new(i: usize) -> Self { + E(i as u32) + } + fn index(self) -> usize { + self.0 as usize + } + } + + #[test] + fn basic() { + let r0 = E(0); + let r1 = E(1); + let r2 = E(2); + let mut m = EntityMap::new(); + + m[r2] = 3; + m[r1] = 5; + + assert_eq!(m[r1], 5); + assert_eq!(m[r2], 3); + + let shared = &m; + assert_eq!(shared[r0], 0); + assert_eq!(shared[r1], 5); + assert_eq!(shared[r2], 3); + } + + #[test] + fn push() { + let mut m = EntityMap::new(); + let k1: E = m.push(12); + let k2 = m.push(33); + + assert_eq!(m[k1], 12); + assert_eq!(m[k2], 33); + } +} diff --git a/src/libcretonne/lib.rs b/src/libcretonne/lib.rs index f0bf55af39..55d601d38b 100644 --- a/src/libcretonne/lib.rs +++ b/src/libcretonne/lib.rs @@ -15,3 +15,5 @@ pub mod instructions; pub mod repr; pub mod write; pub mod cfg; + +pub mod entity_map;