use crate::{
change_detection::{CheckChangeTicks, ComponentTickCells, ComponentTicks, MaybeLocation, Tick},
component::{ComponentId, ComponentInfo},
entity::{Entity, EntityIndex},
query::DebugCheckedUnwrap,
storage::{AbortOnPanic, Column, TableRow, VecExtensions},
};
use alloc::{boxed::Box, vec::Vec};
use bevy_ptr::{OwningPtr, Ptr};
use core::{cell::UnsafeCell, hash::Hash, marker::PhantomData, num::NonZero, panic::Location};
use nonmax::{NonMaxU32, NonMaxUsize};
#[derive(Debug)]
pub(crate) struct SparseArray<I, V = I> {
values: Vec<Option<V>>,
marker: PhantomData<I>,
}
#[derive(Debug)]
pub(crate) struct ImmutableSparseArray<I, V = I> {
values: Box<[Option<V>]>,
marker: PhantomData<I>,
}
impl<I: SparseSetIndex, V> Default for SparseArray<I, V> {
fn default() -> Self {
Self::new()
}
}
impl<I, V> SparseArray<I, V> {
#[inline]
pub const fn new() -> Self {
Self {
values: Vec::new(),
marker: PhantomData,
}
}
}
macro_rules! impl_sparse_array {
($ty:ident) => {
impl<I: SparseSetIndex, V> $ty<I, V> {
#[inline]
pub fn contains(&self, index: I) -> bool {
let index = index.sparse_set_index();
self.values.get(index).is_some_and(Option::is_some)
}
#[inline]
pub fn get(&self, index: I) -> Option<&V> {
let index = index.sparse_set_index();
self.values.get(index).and_then(Option::as_ref)
}
}
};
}
impl_sparse_array!(SparseArray);
impl_sparse_array!(ImmutableSparseArray);
impl<I: SparseSetIndex, V> SparseArray<I, V> {
#[inline]
pub fn insert(&mut self, index: I, value: V) {
let index = index.sparse_set_index();
if index >= self.values.len() {
self.values.resize_with(index + 1, || None);
}
self.values[index] = Some(value);
}
#[inline]
pub fn get_mut(&mut self, index: I) -> Option<&mut V> {
let index = index.sparse_set_index();
self.values.get_mut(index).and_then(Option::as_mut)
}
#[inline]
pub fn remove(&mut self, index: I) -> Option<V> {
let index = index.sparse_set_index();
self.values.get_mut(index).and_then(Option::take)
}
pub fn clear(&mut self) {
self.values.clear();
}
pub(crate) fn into_immutable(self) -> ImmutableSparseArray<I, V> {
ImmutableSparseArray {
values: self.values.into_boxed_slice(),
marker: PhantomData,
}
}
#[inline]
pub(crate) fn iter(&self) -> impl Iterator<Item = (I, &V)> {
self.values.iter().enumerate().filter_map(|(index, value)| {
value
.as_ref()
.map(|value| (SparseSetIndex::get_sparse_set_index(index), value))
})
}
}
#[derive(Debug)]
pub struct ComponentSparseSet {
dense: Column,
#[cfg(not(debug_assertions))]
entities: Vec<EntityIndex>,
#[cfg(debug_assertions)]
entities: Vec<Entity>,
sparse: SparseArray<EntityIndex, TableRow>,
}
impl ComponentSparseSet {
pub(crate) fn new(component_info: &ComponentInfo, capacity: usize) -> Self {
let entities = Vec::with_capacity(capacity);
Self {
dense: Column::with_capacity(component_info, entities.capacity()),
entities,
sparse: Default::default(),
}
}
pub(crate) fn clear(&mut self) {
unsafe { self.dense.clear(self.len()) };
self.entities.clear();
self.sparse.clear();
}
#[inline]
pub fn len(&self) -> usize {
self.entities.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.entities.is_empty()
}
pub(crate) unsafe fn insert(
&mut self,
entity: Entity,
value: OwningPtr<'_>,
change_tick: Tick,
caller: MaybeLocation,
) {
if let Some(&dense_index) = self.sparse.get(entity.index()) {
#[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index.index()]);
self.dense.replace(dense_index, value, change_tick, caller);
} else {
let dense_index = self.entities.len();
let capacity = self.entities.capacity();
#[cfg(not(debug_assertions))]
self.entities.push(entity.index());
#[cfg(debug_assertions)]
self.entities.push(entity);
let _guard = AbortOnPanic;
if capacity != self.entities.capacity() {
let new_capacity = unsafe { NonZero::new_unchecked(self.entities.capacity()) };
if let Some(capacity) = NonZero::new(capacity) {
unsafe { self.dense.realloc(capacity, new_capacity) };
} else {
self.dense.alloc(new_capacity);
}
}
let table_row = unsafe { TableRow::new(NonMaxU32::new_unchecked(dense_index as u32)) };
self.dense.initialize(table_row, value, change_tick, caller);
self.sparse.insert(entity.index(), table_row);
core::mem::forget(_guard);
}
}
#[inline]
pub fn contains(&self, entity: Entity) -> bool {
#[cfg(debug_assertions)]
{
if let Some(&dense_index) = self.sparse.get(entity.index()) {
#[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index.index()]);
true
} else {
false
}
}
#[cfg(not(debug_assertions))]
self.sparse.contains(entity.index())
}
#[inline]
pub fn get(&self, entity: Entity) -> Option<Ptr<'_>> {
self.sparse.get(entity.index()).map(|&dense_index| {
#[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index.index()]);
unsafe { self.dense.get_data_unchecked(dense_index) }
})
}
#[inline]
pub fn get_with_ticks(&self, entity: Entity) -> Option<(Ptr<'_>, ComponentTickCells<'_>)> {
let dense_index = *self.sparse.get(entity.index())?;
#[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index.index()]);
unsafe {
Some((
self.dense.get_data_unchecked(dense_index),
ComponentTickCells {
added: self.dense.get_added_tick_unchecked(dense_index),
changed: self.dense.get_changed_tick_unchecked(dense_index),
changed_by: self.dense.get_changed_by_unchecked(dense_index),
},
))
}
}
#[inline]
pub fn get_added_tick(&self, entity: Entity) -> Option<&UnsafeCell<Tick>> {
let dense_index = *self.sparse.get(entity.index())?;
#[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index.index()]);
unsafe { Some(self.dense.get_added_tick_unchecked(dense_index)) }
}
#[inline]
pub fn get_changed_tick(&self, entity: Entity) -> Option<&UnsafeCell<Tick>> {
let dense_index = *self.sparse.get(entity.index())?;
#[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index.index()]);
unsafe { Some(self.dense.get_changed_tick_unchecked(dense_index)) }
}
#[inline]
pub fn get_ticks(&self, entity: Entity) -> Option<ComponentTicks> {
let dense_index = *self.sparse.get(entity.index())?;
#[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index.index()]);
unsafe { Some(self.dense.get_ticks_unchecked(dense_index)) }
}
#[inline]
pub fn get_changed_by(
&self,
entity: Entity,
) -> MaybeLocation<Option<&UnsafeCell<&'static Location<'static>>>> {
MaybeLocation::new_with_flattened(|| {
let dense_index = *self.sparse.get(entity.index())?;
#[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index.index()]);
unsafe { Some(self.dense.get_changed_by_unchecked(dense_index)) }
})
}
#[inline]
pub fn get_drop(&self) -> Option<unsafe fn(OwningPtr<'_>)> {
self.dense.get_drop()
}
#[must_use = "The returned pointer must be used to drop the removed component."]
pub(crate) fn remove_and_forget(&mut self, entity: Entity) -> Option<OwningPtr<'_>> {
self.sparse.remove(entity.index()).map(|dense_index| {
#[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index.index()]);
let last = self.entities.len() - 1;
if dense_index.index() >= last {
#[cfg(debug_assertions)]
assert_eq!(dense_index.index(), last);
unsafe { self.entities.set_len(last) };
unsafe {
self.dense
.get_data_unchecked(dense_index)
.assert_unique()
.promote()
}
} else {
unsafe {
self.entities
.swap_remove_nonoverlapping_unchecked(dense_index.index());
};
let swapped_entity = unsafe { self.entities.get_unchecked(dense_index.index()) };
#[cfg(not(debug_assertions))]
let index = *swapped_entity;
#[cfg(debug_assertions)]
let index = swapped_entity.index();
unsafe {
*self.sparse.get_mut(index).debug_checked_unwrap() = dense_index;
}
unsafe {
self.dense
.swap_remove_and_forget_unchecked_nonoverlapping(last, dense_index)
}
}
})
}
pub(crate) fn remove(&mut self, entity: Entity) -> bool {
self.sparse
.remove(entity.index())
.map(|dense_index| {
#[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index.index()]);
let last = self.entities.len() - 1;
if dense_index.index() >= last {
#[cfg(debug_assertions)]
assert_eq!(dense_index.index(), last);
unsafe { self.entities.set_len(last) };
unsafe { self.dense.drop_last_component(last) };
} else {
unsafe {
self.entities
.swap_remove_nonoverlapping_unchecked(dense_index.index());
};
let swapped_entity =
unsafe { self.entities.get_unchecked(dense_index.index()) };
#[cfg(not(debug_assertions))]
let index = *swapped_entity;
#[cfg(debug_assertions)]
let index = swapped_entity.index();
unsafe {
*self.sparse.get_mut(index).debug_checked_unwrap() = dense_index;
}
unsafe {
self.dense
.swap_remove_and_drop_unchecked_nonoverlapping(last, dense_index);
}
}
})
.is_some()
}
pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {
unsafe { self.dense.check_change_ticks(self.len(), check) };
}
}
impl Drop for ComponentSparseSet {
fn drop(&mut self) {
let len = self.entities.len();
self.entities.clear();
unsafe {
self.dense.drop(self.entities.capacity(), len);
}
}
}
#[derive(Debug)]
pub struct SparseSet<I, V: 'static> {
dense: Vec<V>,
indices: Vec<I>,
sparse: SparseArray<I, NonMaxUsize>,
}
#[derive(Debug)]
pub(crate) struct ImmutableSparseSet<I, V: 'static> {
dense: Box<[V]>,
indices: Box<[I]>,
sparse: ImmutableSparseArray<I, NonMaxUsize>,
}
macro_rules! impl_sparse_set {
($ty:ident) => {
impl<I: SparseSetIndex, V> $ty<I, V> {
#[inline]
pub fn len(&self) -> usize {
self.dense.len()
}
#[inline]
pub fn contains(&self, index: I) -> bool {
self.sparse.contains(index)
}
pub fn get(&self, index: I) -> Option<&V> {
self.sparse.get(index).map(|dense_index| {
unsafe { self.dense.get_unchecked(dense_index.get()) }
})
}
pub fn get_mut(&mut self, index: I) -> Option<&mut V> {
let dense = &mut self.dense;
self.sparse.get(index).map(move |dense_index| {
unsafe { dense.get_unchecked_mut(dense_index.get()) }
})
}
pub fn indices(&self) -> &[I] {
&self.indices
}
pub fn values(&self) -> impl Iterator<Item = &V> {
self.dense.iter()
}
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
self.dense.iter_mut()
}
pub fn iter(&self) -> impl Iterator<Item = (&I, &V)> {
self.indices.iter().zip(self.dense.iter())
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&I, &mut V)> {
self.indices.iter().zip(self.dense.iter_mut())
}
}
};
}
impl_sparse_set!(SparseSet);
impl_sparse_set!(ImmutableSparseSet);
impl<I: SparseSetIndex, V> Default for SparseSet<I, V> {
fn default() -> Self {
Self::new()
}
}
impl<I, V> SparseSet<I, V> {
pub const fn new() -> Self {
Self {
dense: Vec::new(),
indices: Vec::new(),
sparse: SparseArray::new(),
}
}
}
impl<I: SparseSetIndex, V> SparseSet<I, V> {
pub fn with_capacity(capacity: usize) -> Self {
Self {
dense: Vec::with_capacity(capacity),
indices: Vec::with_capacity(capacity),
sparse: Default::default(),
}
}
#[inline]
pub fn capacity(&self) -> usize {
self.dense.capacity()
}
pub fn insert(&mut self, index: I, value: V) {
if let Some(dense_index) = self.sparse.get(index.clone()).cloned() {
unsafe {
*self.dense.get_unchecked_mut(dense_index.get()) = value;
}
} else {
self.sparse
.insert(index.clone(), NonMaxUsize::new(self.dense.len()).unwrap());
self.indices.push(index);
self.dense.push(value);
}
}
pub fn get_or_insert_with(&mut self, index: I, func: impl FnOnce() -> V) -> &mut V {
if let Some(dense_index) = self.sparse.get(index.clone()).cloned() {
unsafe { self.dense.get_unchecked_mut(dense_index.get()) }
} else {
let value = func();
let dense_index = self.dense.len();
self.sparse
.insert(index.clone(), NonMaxUsize::new(dense_index).unwrap());
self.indices.push(index);
self.dense.push(value);
unsafe { self.dense.get_unchecked_mut(dense_index) }
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.dense.len() == 0
}
pub fn remove(&mut self, index: I) -> Option<V> {
self.sparse.remove(index).map(|dense_index| {
let index = dense_index.get();
let is_last = index == self.dense.len() - 1;
let value = self.dense.swap_remove(index);
self.indices.swap_remove(index);
if !is_last {
let swapped_index = self.indices[index].clone();
*self.sparse.get_mut(swapped_index).unwrap() = dense_index;
}
value
})
}
pub fn clear(&mut self) {
self.dense.clear();
self.indices.clear();
self.sparse.clear();
}
pub(crate) fn into_immutable(self) -> ImmutableSparseSet<I, V> {
ImmutableSparseSet {
dense: self.dense.into_boxed_slice(),
indices: self.indices.into_boxed_slice(),
sparse: self.sparse.into_immutable(),
}
}
}
pub trait SparseSetIndex: Clone + PartialEq + Eq + Hash {
fn sparse_set_index(&self) -> usize;
fn get_sparse_set_index(value: usize) -> Self;
}
macro_rules! impl_sparse_set_index {
($($ty:ty),+) => {
$(impl SparseSetIndex for $ty {
#[inline]
fn sparse_set_index(&self) -> usize {
*self as usize
}
#[inline]
fn get_sparse_set_index(value: usize) -> Self {
value as $ty
}
})*
};
}
impl_sparse_set_index!(u8, u16, u32, u64, usize);
#[derive(Default)]
pub struct SparseSets {
sets: SparseSet<ComponentId, ComponentSparseSet>,
}
impl SparseSets {
#[inline]
pub fn len(&self) -> usize {
self.sets.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.sets.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (ComponentId, &ComponentSparseSet)> {
self.sets.iter().map(|(id, data)| (*id, data))
}
#[inline]
pub fn get(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> {
self.sets.get(component_id)
}
pub(crate) fn get_or_insert(
&mut self,
component_info: &ComponentInfo,
) -> &mut ComponentSparseSet {
if !self.sets.contains(component_info.id()) {
self.sets.insert(
component_info.id(),
ComponentSparseSet::new(component_info, 64),
);
}
self.sets.get_mut(component_info.id()).unwrap()
}
pub(crate) fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ComponentSparseSet> {
self.sets.get_mut(component_id)
}
pub(crate) fn clear_entities(&mut self) {
for set in self.sets.values_mut() {
set.clear();
}
}
pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {
for set in self.sets.values_mut() {
set.check_change_ticks(check);
}
}
}
#[cfg(test)]
mod tests {
use super::SparseSets;
use crate::{
component::{Component, ComponentDescriptor, ComponentId, ComponentInfo},
entity::{Entity, EntityIndex},
storage::SparseSet,
};
use alloc::{vec, vec::Vec};
#[derive(Debug, Eq, PartialEq)]
struct Foo(usize);
#[test]
fn sparse_set() {
let mut set = SparseSet::<Entity, Foo>::default();
let e0 = Entity::from_index(EntityIndex::from_raw_u32(0).unwrap());
let e1 = Entity::from_index(EntityIndex::from_raw_u32(1).unwrap());
let e2 = Entity::from_index(EntityIndex::from_raw_u32(2).unwrap());
let e3 = Entity::from_index(EntityIndex::from_raw_u32(3).unwrap());
let e4 = Entity::from_index(EntityIndex::from_raw_u32(4).unwrap());
set.insert(e1, Foo(1));
set.insert(e2, Foo(2));
set.insert(e3, Foo(3));
assert_eq!(set.get(e0), None);
assert_eq!(set.get(e1), Some(&Foo(1)));
assert_eq!(set.get(e2), Some(&Foo(2)));
assert_eq!(set.get(e3), Some(&Foo(3)));
assert_eq!(set.get(e4), None);
{
let iter_results = set.values().collect::<Vec<_>>();
assert_eq!(iter_results, vec![&Foo(1), &Foo(2), &Foo(3)]);
}
assert_eq!(set.remove(e2), Some(Foo(2)));
assert_eq!(set.remove(e2), None);
assert_eq!(set.get(e0), None);
assert_eq!(set.get(e1), Some(&Foo(1)));
assert_eq!(set.get(e2), None);
assert_eq!(set.get(e3), Some(&Foo(3)));
assert_eq!(set.get(e4), None);
assert_eq!(set.remove(e1), Some(Foo(1)));
assert_eq!(set.get(e0), None);
assert_eq!(set.get(e1), None);
assert_eq!(set.get(e2), None);
assert_eq!(set.get(e3), Some(&Foo(3)));
assert_eq!(set.get(e4), None);
set.insert(e1, Foo(10));
assert_eq!(set.get(e1), Some(&Foo(10)));
*set.get_mut(e1).unwrap() = Foo(11);
assert_eq!(set.get(e1), Some(&Foo(11)));
}
#[test]
fn sparse_sets() {
let mut sets = SparseSets::default();
#[derive(Component, Default, Debug)]
struct TestComponent1;
#[derive(Component, Default, Debug)]
struct TestComponent2;
assert_eq!(sets.len(), 0);
assert!(sets.is_empty());
register_component::<TestComponent1>(&mut sets, 1);
assert_eq!(sets.len(), 1);
register_component::<TestComponent2>(&mut sets, 2);
assert_eq!(sets.len(), 2);
let mut collected_sets = sets
.iter()
.map(|(id, set)| (id, set.len()))
.collect::<Vec<_>>();
collected_sets.sort();
assert_eq!(
collected_sets,
vec![(ComponentId::new(1), 0), (ComponentId::new(2), 0),]
);
fn register_component<T: Component>(sets: &mut SparseSets, id: usize) {
let descriptor = ComponentDescriptor::new::<T>();
let id = ComponentId::new(id);
let info = ComponentInfo::new(id, descriptor);
sets.get_or_insert(&info);
}
}
}