use crate::entity::{Entity, Version};
use alloc::vec::Vec;
use core::{fmt, iter, mem};
#[derive(Clone, Default)]
pub struct SparseVec {
slots: Vec<Option<SparseVecSlot>>,
}
impl SparseVec {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self { slots: Vec::new() }
}
#[inline]
#[must_use]
pub fn get(&self, entity: Entity) -> Option<u32> {
self.slots
.get(entity.sparse())?
.filter(|slot| slot.version == entity.version)
.map(|slot| slot.index)
}
#[inline]
#[must_use]
pub fn contains(&self, entity: Entity) -> bool {
self.slots
.get(entity.sparse())
.and_then(Option::as_ref)
.is_some_and(|slot| slot.version == entity.version)
}
#[inline]
pub fn remove(&mut self, entity: Entity) -> Option<u32> {
self.slots
.get_mut(entity.sparse())?
.take_if(|slot| slot.version == entity.version)
.map(|slot| slot.index)
}
#[inline]
#[must_use]
pub fn get_sparse(&self, sparse: usize) -> Option<u32> {
self.slots.get(sparse)?.map(|slot| slot.index)
}
#[inline]
#[must_use]
pub unsafe fn get_sparse_unchecked(&self, sparse: usize) -> usize {
self.slots.get_unchecked(sparse).unwrap_unchecked().dense()
}
#[inline]
#[must_use]
pub fn contains_sparse(&self, sparse: usize) -> bool {
self.slots.get(sparse).and_then(Option::as_ref).is_some()
}
#[inline]
pub fn remove_sparse(&mut self, sparse: usize) -> Option<SparseVecSlot> {
self.slots.get_mut(sparse)?.take()
}
#[inline]
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut Option<SparseVecSlot> {
self.slots.get_unchecked_mut(index)
}
#[inline]
pub fn get_mut_or_allocate_at(&mut self, index: usize) -> &mut Option<SparseVecSlot> {
if index >= self.slots.len() {
self.extend_to_index(index);
}
unsafe { self.slots.get_unchecked_mut(index) }
}
#[inline]
pub unsafe fn swap_nonoverlapping(&mut self, a: usize, b: usize) {
debug_assert!(a < self.slots.len());
debug_assert!(b < self.slots.len());
debug_assert_ne!(a, b);
let index_a = &mut (*self.slots.as_mut_ptr().add(a))
.as_mut()
.unwrap_unchecked()
.index;
let index_b = &mut (*self.slots.as_mut_ptr().add(b))
.as_mut()
.unwrap_unchecked()
.index;
mem::swap(index_a, index_b);
}
#[inline]
pub fn clear(&mut self) {
self.slots.clear();
}
#[cold]
fn extend_to_index(&mut self, index: usize) {
let extra_len = index.checked_next_power_of_two().unwrap_or(index) - self.slots.len() + 1;
self.slots.extend(iter::repeat_n(None, extra_len));
}
}
impl fmt::Debug for SparseVec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let entries = self
.slots
.iter()
.enumerate()
.filter_map(|(i, &dense_entity)| {
let dense_entity = dense_entity?;
let entity = Entity::new(i as u32, dense_entity.version);
Some((entity, dense_entity.index))
});
f.debug_map().entries(entries).finish()
}
}
#[derive(Clone, Copy)]
#[cfg_attr(target_pointer_width = "64", repr(align(8)))]
pub struct SparseVecSlot {
pub index: u32,
pub version: Version,
}
impl SparseVecSlot {
#[inline]
#[must_use]
pub fn dense(&self) -> usize {
self.index as usize
}
}
impl fmt::Debug for SparseVecSlot {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(stringify!(SparseVecSlot))
.field("index", &self.index)
.field("version", &self.version.0)
.finish()
}
}