diff --git a/src/collections/mod.rs b/src/collections/mod.rs index fa842ed..0802f59 100644 --- a/src/collections/mod.rs +++ b/src/collections/mod.rs @@ -1,3 +1,6 @@ +/// Collections types for Zenyx +/// +/// - [`SparseSet`] mod sparse_set; pub use sparse_set::SparseSet; diff --git a/src/collections/sparse_set.rs b/src/collections/sparse_set.rs index 5e0ec84..a23117c 100644 --- a/src/collections/sparse_set.rs +++ b/src/collections/sparse_set.rs @@ -10,17 +10,36 @@ use bytemuck::Contiguous; const SPARSE_PAGESIZE: usize = (1 << 10) * 4; type SparsePage = Option<(Box<[Option; SPARSE_PAGESIZE], A>, usize)>; +/// A sparse set for fast lookup of large indices. +/// +/// The sparse allocator is mainly used for bulk allocations in the system's page size +/// for the lookup array. It will also be used for the array of pointers into those +/// bulk allocations. Additionally it will be used for the reverse map that generates keys +/// from the value in the internal packed array. +/// +/// The packed allocator will exclusively be used to store the values of type `T`. +/// +/// All operations on this datastructure, meaning insertion, lookup, and deletion, are `O(1)`. +/// +/// This data structure does not in any way guarantee ordering of the values on +/// its own. +#[derive(Hash)] pub struct SparseSet where PackedAlloc: Allocator, SparseAlloc: Allocator, { + /// The paginated array of keys. The value at the key is an index into the dense array minus + /// one where the value corresponding to that key is stored. sparse: Vec, SparseAlloc>, + /// The dense array where the values corresponding to the keys are stored. dense: Vec, + /// The reverse map to get the index in the sparse array from the index in the dense array. dense_to_id: Vec, } impl SparseSet { + /// Creates a new [`SparseSet`] with the global allocator. pub const fn new() -> Self { Self { sparse: Vec::new(), @@ -30,11 +49,45 @@ impl SparseSet { } } +impl Default for SparseSet { + fn default() -> Self { + Self::new() + } +} + +impl core::fmt::Debug for SparseSet +where + T: core::fmt::Debug, + PackedAlloc: Allocator, + SparseAlloc: Allocator, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_map() + .entries(self.dense_to_id.iter().zip(self.dense.iter())) + .finish() + } +} + impl SparseSet where PackedAlloc: Allocator, SparseAlloc: Allocator + Clone, { + /// Inserts an element into the sparse set with the key `id`. This will + /// return the previous value if it already exists. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// + /// let mut sparse_set: SparseSet = SparseSet::new(); + /// + /// sparse_set.insert(10, 123); + /// assert_eq!(sparse_set.get(10), Some(&123)); + /// + /// let prev = sparse_set.insert(10, 9); + /// assert_eq!(prev, Some(123)); + /// assert_eq!(sparse_set.get(10), Some(&9)); + /// ``` pub fn insert(&mut self, id: usize, value: T) -> Option { match self.get_dense_idx(id) { Some(idx) => { @@ -52,15 +105,39 @@ where } } + /// Gets the value with the key `id`. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// + /// let mut sparse_set: SparseSet = SparseSet::new(); + /// + /// sparse_set.insert(10, 123); + /// assert_eq!(sparse_set.get(10), Some(&123)); + /// ``` pub fn get(&self, id: usize) -> Option<&T> { self.dense.get(self.get_dense_idx(id)?) } + /// Gets the value with the key `id` mutably. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// + /// let mut sparse_set: SparseSet = SparseSet::new(); + /// + /// sparse_set.insert(10, 123); + /// let value = sparse_set.get_mut(10).unwrap(); + /// *value = 0; + /// assert_eq!(sparse_set.get(10), Some(&0)); + /// ``` pub fn get_mut(&mut self, id: usize) -> Option<&mut T> { let idx = self.get_dense_idx(id)?; self.dense.get_mut(idx) } + /// Sets the dense index of an `key` to `idx`. This will remove said index + /// if it is [`None`]. fn set_dense_idx(&mut self, id: usize, idx: Option) { let page = id / SPARSE_PAGESIZE; let sparse_index = id % SPARSE_PAGESIZE; @@ -84,6 +161,15 @@ where } } + /// Gets the index in the dense array for a key `id`. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// + /// let mut sparse_set: SparseSet = SparseSet::new(); + /// sparse_set.insert(10, 123); + /// assert_eq!(sparse_set.values()[sparse_set.get_dense_idx(10).unwrap()], 123); + /// ``` pub fn get_dense_idx(&self, id: usize) -> Option { let page = id / SPARSE_PAGESIZE; let sparse_index = id % SPARSE_PAGESIZE; @@ -91,6 +177,8 @@ where page.0[sparse_index].map(|idx| idx.into_integer() - 1) } + /// This reduces the usage count for a page in the sparse array, deallocating + /// it if it is not used anymore. fn reduce_page_usage_count(&mut self, id: usize) { let page = id / SPARSE_PAGESIZE; let Some(usage) = &mut self.sparse[page] else { @@ -103,6 +191,7 @@ where } } + /// Increase the page usage count for a page in the sparse array. fn increase_page_usage_count(&mut self, id: usize) { let page = id / SPARSE_PAGESIZE; if page >= self.sparse.len() { @@ -114,6 +203,18 @@ where usage.1 += 1; } + /// Removes the value with the key `id` from the sparse set, returning the + /// value if it existed. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// + /// let mut sparse_set: SparseSet = SparseSet::new(); + /// + /// sparse_set.insert(10, 123); + /// assert_eq!(sparse_set.remove(10), Some(123)); + /// assert_eq!(sparse_set.remove(10), None); + /// ``` pub fn remove(&mut self, id: usize) -> Option { let index = self.get_dense_idx(id)?; if self.dense.is_empty() { @@ -129,26 +230,126 @@ where Some(previous) } + /// Returns if there are values in this sparse set. + /// ``` + /// use zenyx::collections::SparseSet; + /// + /// let mut sparse_set: SparseSet = SparseSet::new(); + /// assert!(sparse_set.is_empty()); + /// sparse_set.insert(10, 123); + /// assert!(!sparse_set.is_empty()); + /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } + /// Returns the number of values in this sparse set. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// + /// let mut sparse_set: SparseSet = SparseSet::new(); + /// sparse_set.insert(10, 123); + /// sparse_set.insert(10, 9); + /// sparse_set.insert(11, 10); + /// assert_eq!(sparse_set.len(), 2); + /// ``` pub fn len(&self) -> usize { self.dense.len() } + /// Checks if the sparse set contains a value with key `id`. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// + /// let mut sparse_set: SparseSet = SparseSet::new(); + /// assert!(!sparse_set.contains(10)); + /// sparse_set.insert(10, 123); + /// assert!(sparse_set.contains(10)); + /// ``` pub fn contains(&self, id: usize) -> bool { self.get_dense_idx(id).is_some() } + /// Gets the keys of all values in the sparse set. This method does not provide + /// any ordering guarantees other than the keys contained corresponding to + /// the values with the same index returned by [`Self::values`]. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// + /// let mut sparse_set: SparseSet = SparseSet::new(); + /// assert!(sparse_set.keys().is_empty()); + /// sparse_set.insert(10, 10); + /// sparse_set.insert(9, 10); + /// sparse_set.insert(11, 10); + /// + /// assert_eq!(sparse_set.keys(), &[10, 9, 11]); + /// sparse_set.remove(10); + /// assert_eq!(sparse_set.keys(), &[11, 9]); + /// ``` pub fn keys(&self) -> &[usize] { &self.dense_to_id } + /// Gets all values in the sparse set, the corresponding `key` is at the same + /// position in the slice returned by [`Self::keys`]. + /// + /// Otherwise there are no ordering guarantees. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// + /// let mut sparse_set: SparseSet = SparseSet::new(); + /// assert!(sparse_set.values().is_empty()); + /// sparse_set.insert(10, 10); + /// sparse_set.insert(9, 9); + /// sparse_set.insert(11, 11); + /// + /// assert_eq!(sparse_set.values(), &[10, 9, 11]); + /// sparse_set.remove(10); + /// assert_eq!(sparse_set.values(), &[11, 9]); + /// ``` pub fn values(&self) -> &[T] { &self.dense } + /// Mutable version of [`Self::keys`]. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// + /// let mut sparse_set: SparseSet = SparseSet::new(); + /// assert!(sparse_set.values().is_empty()); + /// sparse_set.insert(10, 10); + /// sparse_set.insert(9, 9); + /// sparse_set.insert(11, 11); + /// + /// let dense_of_9 = sparse_set.get_dense_idx(9).unwrap(); + /// let dense_of_10 = sparse_set.get_dense_idx(10).unwrap(); + /// + /// let values = sparse_set.values_mut(); + /// values[dense_of_10] = 9; + /// values[dense_of_9] = 10; + /// + /// assert_eq!(sparse_set.get(10), Some(&9)); + /// assert_eq!(sparse_set.get(9), Some(&10)); + /// ``` + pub fn values_mut(&mut self) -> &mut [T] { + &mut self.dense + } + + /// Creates a new [`SparseSet`] with the values allocated by the `packed_alloc` + /// and everything else, as described in the top level documentation for [`SparseSet`] + /// in the `sparse_alloc`. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// use allocator_api2::alloc::Global; + /// + /// let sparse_set = SparseSet::::new_in(Global, Global); + /// ``` pub fn new_in(packed_alloc: PackedAlloc, sparse_alloc: SparseAlloc) -> Self { Self { dense: Vec::new_in(packed_alloc), @@ -162,6 +363,16 @@ impl SparseSet where PackedAlloc: Allocator, { + /// Creates a new [`SparseSet`] with the values allocated by the `packed_alloc` + /// Everything else, as described in the top level documentation for [`SparseSet`] + /// is allocated using the global allocator. + /// + /// ``` + /// use zenyx::collections::SparseSet; + /// use allocator_api2::alloc::Global; + /// + /// let sparse_set = SparseSet::::new_in_packed(Global); + /// ``` pub const fn new_in_packed(packed_alloc: PackedAlloc) -> Self { Self { sparse: Vec::new(), @@ -236,7 +447,10 @@ mod tests { assert_eq!(sparse_set.remove(SPARSE_PAGESIZE + 2).unwrap(), 3); assert_eq!(sparse_set.sparse[1].as_ref().unwrap().1, 2); - assert_eq!(sparse_set.keys(), [10, 11, 12, SPARSE_PAGESIZE, SPARSE_PAGESIZE + 1]); + assert_eq!( + sparse_set.keys(), + [10, 11, 12, SPARSE_PAGESIZE, SPARSE_PAGESIZE + 1] + ); assert_eq!(sparse_set.values(), [1, 2, 2, 1, 2]); assert_eq!(sparse_set.remove(SPARSE_PAGESIZE + 1).unwrap(), 2); @@ -249,25 +463,44 @@ mod tests { assert_eq!(sparse_set.keys(), [10, 11, 12]); assert_eq!(sparse_set.values(), [1, 2, 2]); - sparse_set.insert(SPARSE_PAGESIZE, 1); sparse_set.insert(SPARSE_PAGESIZE + 1, 2); sparse_set.insert(SPARSE_PAGESIZE + 2, 3); assert_eq!(sparse_set.remove(10).unwrap(), 1); assert_eq!(sparse_set.sparse[0].as_ref().unwrap().1, 2); - // swap-remove - assert_eq!(sparse_set.keys(), [SPARSE_PAGESIZE + 2, 11, 12, SPARSE_PAGESIZE, SPARSE_PAGESIZE + 1]); + // swap-remove + assert_eq!( + sparse_set.keys(), + [ + SPARSE_PAGESIZE + 2, + 11, + 12, + SPARSE_PAGESIZE, + SPARSE_PAGESIZE + 1 + ] + ); assert_eq!(sparse_set.values(), [3, 2, 2, 1, 2]); assert_eq!(sparse_set.remove(11).unwrap(), 2); assert_eq!(sparse_set.sparse[0].as_ref().unwrap().1, 1); - assert_eq!(sparse_set.keys(), [SPARSE_PAGESIZE + 2, SPARSE_PAGESIZE + 1, 12, SPARSE_PAGESIZE]); + assert_eq!( + sparse_set.keys(), + [ + SPARSE_PAGESIZE + 2, + SPARSE_PAGESIZE + 1, + 12, + SPARSE_PAGESIZE + ] + ); assert_eq!(sparse_set.values(), [3, 2, 2, 1]); assert_eq!(sparse_set.remove(12).unwrap(), 2); assert!(sparse_set.sparse[0].is_none()); - assert_eq!(sparse_set.keys(), [SPARSE_PAGESIZE + 2, SPARSE_PAGESIZE + 1, SPARSE_PAGESIZE]); + assert_eq!( + sparse_set.keys(), + [SPARSE_PAGESIZE + 2, SPARSE_PAGESIZE + 1, SPARSE_PAGESIZE] + ); assert_eq!(sparse_set.values(), [3, 2, 1]); } }