use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet, FxHasher};
use slotmap::{DenseSlotMap, Key};
use smallvec::SmallVec;
pub type FacetIndex = u8;
pub use uuid::Uuid;
#[derive(Clone, Debug)]
pub struct StorageMap<K: Key, V> {
slots: DenseSlotMap<K, Option<V>>,
live_len: usize,
}
#[derive(Debug)]
pub(crate) struct StorageKeys<'a, K: Key, V> {
inner: slotmap::dense::Iter<'a, K, Option<V>>,
}
impl<K: Key, V> Clone for StorageKeys<'_, K, V> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<K: Key, V> Iterator for StorageKeys<'_, K, V> {
type Item = K;
fn next(&mut self) -> Option<Self::Item> {
self.inner
.find_map(|(key, value)| value.is_some().then_some(key))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (_, upper) = self.inner.size_hint();
(0, upper)
}
}
impl<K: Key, V> StorageMap<K, V> {
#[must_use]
pub fn with_key() -> Self {
Self {
slots: DenseSlotMap::with_key(),
live_len: 0,
}
}
#[must_use]
pub fn with_capacity_and_key(capacity: usize) -> Self {
Self {
slots: DenseSlotMap::with_capacity_and_key(capacity),
live_len: 0,
}
}
pub fn insert(&mut self, value: V) -> K {
let key = self.slots.insert(Some(value));
self.live_len = self.live_len.saturating_add(1);
key
}
pub fn remove(&mut self, key: K) -> Option<V> {
let value = self.slots.remove(key).flatten()?;
self.live_len = self.live_len.saturating_sub(1);
Some(value)
}
pub(crate) fn tombstone(&mut self, key: K) -> Option<V> {
let value = self.slots.get_mut(key)?.take()?;
self.live_len = self.live_len.saturating_sub(1);
Some(value)
}
pub(crate) fn restore_tombstone(&mut self, key: K, value: V) -> Result<(), V> {
let Some(slot) = self.slots.get_mut(key) else {
return Err(value);
};
if slot.is_some() {
return Err(value);
}
*slot = Some(value);
self.live_len = self.live_len.saturating_add(1);
Ok(())
}
pub(crate) fn finalize_tombstone(&mut self, key: K) -> bool {
if !matches!(self.slots.get(key), Some(None)) {
return false;
}
matches!(self.slots.remove(key), Some(None))
}
#[must_use]
pub fn get(&self, key: K) -> Option<&V> {
self.slots.get(key).and_then(Option::as_ref)
}
#[must_use]
pub fn get_mut(&mut self, key: K) -> Option<&mut V> {
self.slots.get_mut(key).and_then(Option::as_mut)
}
#[must_use]
pub fn contains_key(&self, key: K) -> bool {
self.get(key).is_some()
}
#[must_use]
pub const fn len(&self) -> usize {
self.live_len
}
pub fn iter(&self) -> impl Iterator<Item = (K, &V)> {
self.slots
.iter()
.filter_map(|(key, value)| value.as_ref().map(|value| (key, value)))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (K, &mut V)> {
self.slots
.iter_mut()
.filter_map(|(key, value)| value.as_mut().map(|value| (key, value)))
}
pub(crate) fn keys(&self) -> StorageKeys<'_, K, V> {
StorageKeys {
inner: self.slots.iter(),
}
}
pub fn values(&self) -> impl Iterator<Item = &V> {
self.iter().map(|(_, value)| value)
}
}
impl<K: Key, V> Default for StorageMap<K, V> {
fn default() -> Self {
Self::with_key()
}
}
#[cfg(test)]
mod test_support {
use super::*;
impl<K: Key, V> StorageMap<K, V> {
pub(crate) fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
self.iter_mut().map(|(_, value)| value)
}
}
}
pub type FastHashMap<K, V> = FxHashMap<K, V>;
pub type SecureHashMap<K, V> =
std::collections::HashMap<K, V, std::collections::hash_map::RandomState>;
pub type SecureHashSet<T> = std::collections::HashSet<T, std::collections::hash_map::RandomState>;
pub type FastHasher = FxHasher;
pub type FastBuildHasher = FxBuildHasher;
pub use std::collections::hash_map::Entry;
pub type FastHashSet<T> = FxHashSet<T>;
pub type SmallBuffer<T, const N: usize> = SmallVec<[T; N]>;
pub const MAX_PRACTICAL_DIMENSION_SIZE: usize = 8;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn storage_key_clones_preserve_cursor_and_skip_tombstones() {
struct NonClonePayload;
let mut storage: StorageMap<slotmap::DefaultKey, NonClonePayload> = StorageMap::with_key();
let first = storage.insert(NonClonePayload);
let removed = storage.insert(NonClonePayload);
let middle = storage.insert(NonClonePayload);
let last = storage.insert(NonClonePayload);
let trailing = storage.insert(NonClonePayload);
assert!(storage.tombstone(removed).is_some());
assert!(storage.tombstone(trailing).is_some());
assert_eq!(storage.len(), 3);
let mut keys = storage.keys();
assert_eq!(keys.next(), Some(first));
let mut cloned = keys.clone();
assert_eq!(cloned.size_hint(), keys.size_hint());
assert_eq!(keys.by_ref().collect::<Vec<_>>(), vec![middle, last]);
assert!(keys.next().is_none());
assert_eq!(cloned.by_ref().collect::<Vec<_>>(), vec![middle, last]);
assert!(cloned.next().is_none());
assert!(cloned.clone().next().is_none());
assert_eq!(
storage.keys().collect::<Vec<_>>(),
vec![first, middle, last]
);
}
#[test]
fn storage_finalizing_a_live_slot_preserves_value_and_length() {
let mut storage: StorageMap<slotmap::DefaultKey, String> = StorageMap::with_key();
let key = storage.insert("live payload".to_string());
assert!(!storage.finalize_tombstone(key));
assert_eq!(storage.get(key).map(String::as_str), Some("live payload"));
assert_eq!(storage.len(), 1);
assert_eq!(storage.iter().count(), storage.len());
assert_eq!(storage.remove(key).as_deref(), Some("live payload"));
assert_eq!(storage.len(), 0);
}
#[test]
fn storage_tombstone_restore_preserves_key_payload_and_live_iteration() {
let mut storage: StorageMap<slotmap::DefaultKey, String> = StorageMap::with_key();
let first = storage.insert("first".to_string());
let removed = storage.insert("removed".to_string());
let last = storage.insert("last".to_string());
let before: Vec<_> = storage
.iter()
.map(|(key, value)| (key, value.clone()))
.collect();
let payload = storage.tombstone(removed).unwrap();
assert_eq!(payload, "removed");
assert_eq!(storage.len(), 2);
assert!(!storage.contains_key(removed));
assert!(storage.get_mut(removed).is_none());
assert_eq!(storage.keys().collect::<Vec<_>>(), vec![first, last]);
assert_eq!(storage.values().count(), storage.len());
assert!(storage.tombstone(removed).is_none());
storage.restore_tombstone(removed, payload).unwrap();
assert_eq!(storage.len(), 3);
assert_eq!(
storage
.iter()
.map(|(key, value)| (key, value.clone()))
.collect::<Vec<_>>(),
before
);
}
#[test]
fn storage_restore_rejects_live_and_missing_keys_without_replacing_payloads() {
let mut storage: StorageMap<slotmap::DefaultKey, String> = StorageMap::with_key();
let live = storage.insert("live".to_string());
let missing = storage.insert("removed".to_string());
assert_eq!(storage.remove(missing).as_deref(), Some("removed"));
for key in [live, missing] {
assert_eq!(
storage.restore_tombstone(key, "rejected".to_string()),
Err("rejected".to_string())
);
assert_eq!(storage.len(), 1);
assert_eq!(storage.get(live).map(String::as_str), Some("live"));
assert_eq!(storage.keys().collect::<Vec<_>>(), vec![live]);
}
}
#[test]
fn storage_finalization_invalidates_old_keys_without_affecting_replacements() {
let mut storage: StorageMap<slotmap::DefaultKey, String> = StorageMap::with_key();
let old = storage.insert("old".to_string());
let payload = storage.tombstone(old).unwrap();
assert_eq!(storage.len(), 0);
assert!(storage.finalize_tombstone(old));
assert!(!storage.finalize_tombstone(old));
let replacement = storage.insert("replacement".to_string());
assert_ne!(old, replacement);
assert_eq!(
storage.restore_tombstone(old, payload),
Err("old".to_string())
);
assert!(storage.remove(old).is_none());
assert!(!storage.finalize_tombstone(old));
assert_eq!(
storage.get(replacement).map(String::as_str),
Some("replacement")
);
assert_eq!(storage.len(), 1);
assert_eq!(storage.iter().count(), storage.len());
}
#[test]
fn test_fast_collections_basic_operations() {
let mut map: FastHashMap<u64, usize> = FastHashMap::default();
assert!(map.is_empty());
map.insert(123, 456);
assert_eq!(map.get(&123), Some(&456));
assert_eq!(map.len(), 1);
map.insert(789, 101_112);
assert_eq!(map.len(), 2);
let mut set: FastHashSet<u64> = FastHashSet::default();
assert!(set.is_empty());
set.insert(789);
assert!(set.contains(&789));
assert_eq!(set.len(), 1);
set.insert(456);
assert_eq!(set.len(), 2);
assert!(set.contains(&456));
assert!(!set.contains(&999));
let mut secure_map: SecureHashMap<u64, usize> = SecureHashMap::default();
secure_map.insert(123, 456);
assert_eq!(secure_map.get(&123), Some(&456));
let mut secure_set: SecureHashSet<u64> = SecureHashSet::default();
secure_set.insert(123);
assert!(secure_set.contains(&123));
}
#[test]
fn test_small_buffer_stack_allocation() {
let mut buffer: SmallBuffer<i32, 4> = SmallBuffer::new();
for i in 0..4 {
buffer.push(i);
}
assert_eq!(buffer.len(), 4);
assert!(!buffer.spilled());
buffer.push(4);
assert_eq!(buffer.len(), 5);
assert!(buffer.spilled()); }
}