use core::{num::NonZeroUsize, usize}; use allocator_api2::{ alloc::{Allocator, Global}, boxed::Box, vec::Vec, }; 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 datastructure 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(), dense: Vec::new(), dense_to_id: Vec::new(), } } } 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) => { let previous = core::mem::replace(&mut self.dense[idx], value); self.dense_to_id[idx] = id; Some(previous) } None => { self.increase_page_usage_count(id); self.set_dense_idx(id, Some(self.dense.len())); self.dense.push(value); self.dense_to_id.push(id); None } } } /// 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; if page >= self.sparse.len() { self.sparse.resize(page + 1, None); } if self.sparse[page].is_none() { self.sparse[page] = Some(( Box::new_in([None; 4096], self.sparse.allocator().clone()), 1, )) } match &mut self.sparse[page] { Some(page) => { page.0[sparse_index] = idx.map(|i| NonZeroUsize::new(i + 1).unwrap()); } None => unreachable!("wtf, failed to init sparse page 5 lines above??"), } } /// 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; let page = self.sparse.get(page)?.as_ref()?; 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 { return; }; usage.1 -= 1; let usage = usage.1; if usage == 0 { self.sparse[page] = None; } } /// 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() { return; } let Some(usage) = &mut self.sparse[page] else { return; }; 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() { return None; } self.set_dense_idx(*self.dense_to_id.last().unwrap(), Some(index)); self.set_dense_idx(id, None); self.reduce_page_usage_count(id); let previous = self.dense.swap_remove(index); self.dense_to_id.swap_remove(index); 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), sparse: Vec::new_in(sparse_alloc.clone()), dense_to_id: Vec::new_in(sparse_alloc), } } } 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(), dense: Vec::new_in(packed_alloc), dense_to_id: Vec::new(), } } } #[cfg(test)] mod tests { use super::*; #[test] fn insert() { let mut sparse_set = SparseSet::::new(); sparse_set.insert(10, 1); assert_eq!(sparse_set.keys(), &[10]); assert_eq!(sparse_set.values(), &[1]); assert_eq!( sparse_set.sparse[0].as_ref().unwrap().0[10].unwrap(), NonZeroUsize::new(1).unwrap() ); assert_eq!(sparse_set.sparse[0].as_ref().unwrap().1, 1); assert_eq!(sparse_set.insert(10, 2).unwrap(), 1); assert_eq!(sparse_set.values(), &[2]); assert_eq!(sparse_set.sparse[0].as_ref().unwrap().1, 1); sparse_set.insert(11, 4); assert_eq!(sparse_set.keys(), &[10, 11]); assert_eq!(sparse_set.values(), &[2, 4]); assert_eq!( sparse_set.sparse[0].as_ref().unwrap().0[11].unwrap(), NonZeroUsize::new(2).unwrap() ); assert_eq!(sparse_set.sparse[0].as_ref().unwrap().1, 2); sparse_set.insert(5000, 3); assert_eq!(sparse_set.keys(), &[10, 11, 5000]); assert_eq!(sparse_set.values(), &[2, 4, 3]); assert_eq!( sparse_set.sparse[5000 / SPARSE_PAGESIZE] .as_ref() .unwrap() .0[5000 % SPARSE_PAGESIZE] .unwrap(), NonZeroUsize::new(3).unwrap() ); assert_eq!( sparse_set.sparse[5000 / SPARSE_PAGESIZE] .as_ref() .unwrap() .1, 1 ); assert_eq!(*sparse_set.get(10).unwrap(), 2); assert_eq!(*sparse_set.get(11).unwrap(), 4); assert_eq!(*sparse_set.get(5000).unwrap(), 3); } #[test] fn remove() { let mut sparse_set = SparseSet::::new(); sparse_set.insert(10, 1); sparse_set.insert(11, 2); sparse_set.insert(12, 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(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.values(), [1, 2, 2, 1, 2]); assert_eq!(sparse_set.remove(SPARSE_PAGESIZE + 1).unwrap(), 2); assert_eq!(sparse_set.sparse[1].as_ref().unwrap().1, 1); assert_eq!(sparse_set.keys(), [10, 11, 12, SPARSE_PAGESIZE]); assert_eq!(sparse_set.values(), [1, 2, 2, 1]); assert_eq!(sparse_set.remove(SPARSE_PAGESIZE).unwrap(), 1); assert!(sparse_set.sparse[1].is_none()); 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 ] ); 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.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.values(), [3, 2, 1]); } }