From 767753ad225077252c65cec39006b9b5adf8b8c0 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 4 Jun 2022 05:12:21 -0700 Subject: [PATCH 01/11] Immutable sparse sets for metadata storage --- crates/bevy_ecs/src/storage/sparse_set.rs | 94 +++++++++++++++++++++++ crates/bevy_ecs/src/storage/table.rs | 49 +++++++----- 2 files changed, 122 insertions(+), 21 deletions(-) diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 74e99d4853502..1bf867d1e70dc 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -89,6 +89,33 @@ impl SparseArray { pub fn clear(&mut self) { self.values.clear(); } + + pub fn to_immutable(self) -> ImmutableSparseArray { + ImmutableSparseArray { + values: self.values.into_boxed_slice(), + marker: PhantomData, + } + } +} + +#[derive(Debug)] +pub struct ImmutableSparseArray { + values: Box<[Option]>, + marker: PhantomData, +} + +impl ImmutableSparseArray { + #[inline] + pub fn contains(&self, index: I) -> bool { + let index = index.sparse_set_index(); + self.values.get(index).map(|v| v.is_some()).unwrap_or(false) + } + + #[inline] + pub fn get(&self, index: I) -> Option<&V> { + let index = index.sparse_set_index(); + self.values.get(index).map(|v| v.as_ref()).unwrap_or(None) + } } /// A sparse data structure of [Components](crate::component::Component) @@ -426,6 +453,73 @@ impl SparseSet { pub fn iter_mut(&mut self) -> impl Iterator { self.indices.iter().zip(self.dense.iter_mut()) } + + pub fn to_immutable(self) -> ImmutableSparseSet { + ImmutableSparseSet { + dense: self.dense.into_boxed_slice(), + indices: self.indices.into_boxed_slice(), + sparse: self.sparse.to_immutable(), + } + } +} + +#[derive(Debug)] +pub struct ImmutableSparseSet { + dense: Box<[V]>, + indices: Box<[I]>, + sparse: ImmutableSparseArray, +} + +impl ImmutableSparseSet { + #[inline] + pub fn len(&self) -> usize { + self.dense.len() + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.dense.len() == 0 + } + + #[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| { + // SAFE: if the sparse index points to something in the dense vec, it exists + unsafe { self.dense.get_unchecked(*dense_index) } + }) + } + + pub fn get_mut(&mut self, index: I) -> Option<&mut V> { + let dense = &mut self.dense; + self.sparse.get(index).map(move |dense_index| { + // SAFE: if the sparse index points to something in the dense vec, it exists + unsafe { dense.get_unchecked_mut(*dense_index) } + }) + } + + pub fn indices(&self) -> impl Iterator + '_ { + self.indices.iter().cloned() + } + + pub fn values(&self) -> impl Iterator { + self.dense.iter() + } + + pub fn values_mut(&mut self) -> impl Iterator { + self.dense.iter_mut() + } + + pub fn iter(&self) -> impl Iterator { + self.indices.iter().zip(self.dense.iter()) + } + + pub fn iter_mut(&mut self) -> impl Iterator { + self.indices.iter().zip(self.dense.iter_mut()) + } } pub trait SparseSetIndex: Clone + PartialEq + Eq + Hash { diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index a571a631d66c2..2c57dd50c2f22 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -1,7 +1,7 @@ use crate::{ component::{ComponentId, ComponentInfo, ComponentTicks, Components}, entity::Entity, - storage::{BlobVec, SparseSet}, + storage::{BlobVec, ImmutableSparseSet, SparseSet}, }; use bevy_ptr::{OwningPtr, Ptr, PtrMut}; use bevy_utils::HashMap; @@ -186,38 +186,45 @@ impl Column { } } -pub struct Table { +pub struct TableBuilder { columns: SparseSet, - entities: Vec, + capacity: usize, } -impl Table { - pub const fn new() -> Table { +impl TableBuilder { + pub fn with_capacity(capacity: usize, column_capacity: usize) -> Self { Self { - columns: SparseSet::new(), - entities: Vec::new(), + columns: SparseSet::with_capacity(column_capacity), + capacity, } } - pub fn with_capacity(capacity: usize, column_capacity: usize) -> Table { - Self { - columns: SparseSet::with_capacity(column_capacity), - entities: Vec::with_capacity(capacity), + pub fn add_column(&mut self, component_info: &ComponentInfo) { + self.columns.insert( + component_info.id(), + Column::with_capacity(component_info, self.capacity), + ); + } + + pub fn build(self) -> Table { + Table { + columns: self.columns.to_immutable(), + entities: Vec::with_capacity(self.capacity), } } +} + +pub struct Table { + columns: ImmutableSparseSet, + entities: Vec, +} +impl Table { #[inline] pub fn entities(&self) -> &[Entity] { &self.entities } - pub fn add_column(&mut self, component_info: &ComponentInfo) { - self.columns.insert( - component_info.id(), - Column::with_capacity(component_info, self.entities.capacity()), - ); - } - /// Removes the entity at the given row and returns the entity swapped in to replace it (if an /// entity was swapped in) /// @@ -414,7 +421,7 @@ pub struct Tables { impl Default for Tables { fn default() -> Self { - let empty_table = Table::with_capacity(0, 0); + let empty_table = TableBuilder::with_capacity(0, 0).build(); Tables { tables: vec![empty_table], table_ids: HashMap::default(), @@ -472,11 +479,11 @@ impl Tables { .raw_entry_mut() .from_key(component_ids) .or_insert_with(|| { - let mut table = Table::with_capacity(0, component_ids.len()); + let mut table = TableBuilder::with_capacity(0, component_ids.len()); for component_id in component_ids.iter() { table.add_column(components.get_info_unchecked(*component_id)); } - tables.push(table); + tables.push(table.build()); (component_ids.to_vec(), TableId(tables.len() - 1)) }); From 7e85fe66752d8e2aa0046f937d778bea0555418a Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 4 Jun 2022 15:10:33 -0700 Subject: [PATCH 02/11] Fix tests --- crates/bevy_ecs/src/storage/table.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index 2c57dd50c2f22..e11a111fd557e 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -533,7 +533,7 @@ mod tests { use crate::component::Component; use crate::ptr::OwningPtr; use crate::storage::Storages; - use crate::{component::Components, entity::Entity, storage::Table}; + use crate::{component::Components, entity::Entity, storage::TableBuilder}; #[derive(Component)] struct W(T); @@ -543,8 +543,9 @@ mod tests { let mut storages = Storages::default(); let component_id = components.init_component::>(&mut storages); let columns = &[component_id]; - let mut table = Table::with_capacity(0, columns.len()); - table.add_column(components.get_info(component_id).unwrap()); + let mut builder = TableBuilder::with_capacity(0, columns.len()); + builder.add_column(components.get_info(component_id).unwrap()); + let mut table = builder.build(); let entities = (0..200).map(Entity::from_raw).collect::>(); for entity in &entities { // SAFE: we allocate and immediately set data afterwards From c759212e4e282d41c8355f3b2d25e43405a5a6ba Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 28 Oct 2022 22:43:12 -0700 Subject: [PATCH 03/11] Use ImmutableSparseSet for Archetype's components --- crates/bevy_ecs/src/archetype.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index 5725b3c6c7c40..7dea01e07819d 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -5,7 +5,7 @@ use crate::{ bundle::BundleId, component::{ComponentId, StorageType}, entity::{Entity, EntityLocation}, - storage::{SparseArray, SparseSet, SparseSetIndex, TableId}, + storage::{SparseArray, ImmutableSparseSet, SparseSet, SparseSetIndex, TableId}, }; use std::{ collections::HashMap, @@ -153,7 +153,7 @@ pub struct Archetype { entities: Vec, table_components: Box<[ComponentId]>, sparse_set_components: Box<[ComponentId]>, - components: SparseSet, + components: ImmutableSparseSet, } impl Archetype { @@ -195,7 +195,7 @@ impl Archetype { id, table_id, entities: Vec::new(), - components, + components: components.to_immutable(), table_components, sparse_set_components, edges: Default::default(), From c46c1868983031525b06a81ab589ef5d9892dd57 Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 28 Oct 2022 22:46:37 -0700 Subject: [PATCH 04/11] Make the Immutable types pub(crate) --- crates/bevy_ecs/src/storage/sparse_set.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 1f53ad7e3a8f0..5bd0cf528bad5 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -71,7 +71,7 @@ impl SparseArray { self.values.clear(); } - pub fn to_immutable(self) -> ImmutableSparseArray { + pub(crate) fn to_immutable(self) -> ImmutableSparseArray { ImmutableSparseArray { values: self.values.into_boxed_slice(), marker: PhantomData, @@ -80,7 +80,7 @@ impl SparseArray { } #[derive(Debug)] -pub struct ImmutableSparseArray { +pub(crate) struct ImmutableSparseArray { values: Box<[Option]>, marker: PhantomData, } @@ -396,7 +396,7 @@ impl SparseSet { self.indices.iter().zip(self.dense.iter_mut()) } - pub fn to_immutable(self) -> ImmutableSparseSet { + pub(crate) fn to_immutable(self) -> ImmutableSparseSet { ImmutableSparseSet { dense: self.dense.into_boxed_slice(), indices: self.indices.into_boxed_slice(), @@ -406,7 +406,7 @@ impl SparseSet { } #[derive(Debug)] -pub struct ImmutableSparseSet { +pub(crate) struct ImmutableSparseSet { dense: Box<[V]>, indices: Box<[I]>, sparse: ImmutableSparseArray, @@ -418,11 +418,6 @@ impl ImmutableSparseSet { self.dense.len() } - #[inline] - pub fn is_empty(&self) -> bool { - self.dense.len() == 0 - } - #[inline] pub fn contains(&self, index: I) -> bool { self.sparse.contains(index) @@ -455,10 +450,6 @@ impl ImmutableSparseSet { self.dense.iter_mut() } - pub fn iter(&self) -> impl Iterator { - self.indices.iter().zip(self.dense.iter()) - } - pub fn iter_mut(&mut self) -> impl Iterator { self.indices.iter().zip(self.dense.iter_mut()) } From c723c1c9609d96476a2cd77238b61f7d0449ee12 Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 28 Oct 2022 22:55:01 -0700 Subject: [PATCH 05/11] Document TableBuilder and make it pub(crate) --- crates/bevy_ecs/src/archetype.rs | 2 +- crates/bevy_ecs/src/storage/sparse_set.rs | 4 ++++ crates/bevy_ecs/src/storage/table.rs | 15 +++++++++++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index 7dea01e07819d..d6713e0937157 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -5,7 +5,7 @@ use crate::{ bundle::BundleId, component::{ComponentId, StorageType}, entity::{Entity, EntityLocation}, - storage::{SparseArray, ImmutableSparseSet, SparseSet, SparseSetIndex, TableId}, + storage::{ImmutableSparseSet, SparseArray, SparseSet, SparseSetIndex, TableId}, }; use std::{ collections::HashMap, diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 5bd0cf528bad5..7a8e668a7524c 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -79,6 +79,8 @@ impl SparseArray { } } +/// A space-optimized version of [`SparseArray`] that cannot be changed +/// after contrusction. #[derive(Debug)] pub(crate) struct ImmutableSparseArray { values: Box<[Option]>, @@ -405,6 +407,8 @@ impl SparseSet { } } +/// A space-optimized version of [`SparseSet`] that cannot be changed +/// after contrusction. #[derive(Debug)] pub(crate) struct ImmutableSparseSet { dense: Box<[V]>, diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index 86a1476fd8b9f..c8651a4f4a0b5 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -262,12 +262,23 @@ impl Column { } } -pub struct TableBuilder { +/// A builder type for constructing [`Table`]s. +/// +/// - Use [`with_capacity`] to initialize the builder. +/// - Repeatedly call [`add_column`] to add columns for components. +/// - Finalize with [`build`] to get the constructed [`Table`]. +/// +/// [`with_capacity`]: Self::with_capacity +/// [`add_column`]: Self::add_column +/// [`build`]: Self::build +pub(crate) struct TableBuilder { columns: SparseSet, capacity: usize, } impl TableBuilder { + /// Creates a blank [`Table`], allocating space for `column_capacity` columns + /// with the capacity to hold `capacity` entities worth of components each. pub fn with_capacity(capacity: usize, column_capacity: usize) -> Self { Self { columns: SparseSet::with_capacity(column_capacity), @@ -610,7 +621,7 @@ mod tests { use crate::{ component::{ComponentTicks, Components}, entity::Entity, - storage::TableBuilder + storage::TableBuilder, }; #[derive(Component)] struct W(T); From 6c7a2692bc2220d537ca559c3327a55ea6a938d7 Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 28 Oct 2022 23:27:41 -0700 Subject: [PATCH 06/11] Document Table --- crates/bevy_ecs/src/storage/table.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index c8651a4f4a0b5..9d2b88c340986 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -301,6 +301,16 @@ impl TableBuilder { } } +/// A column-oriented [structure-of-arrays] based storage for [`Component`]s of entities +/// in a [`World`]. +/// +/// Conceptually, a `Table` can be thought of as an `HashMap`, where +/// each `Column` is a type-erased `Vec`. Each row corresponds to a single entity +/// (i.e. index 3 in Column A and index 3 in Column B point to different components on the same +/// entity). Fetching components from a table involves fetching the associated column for a +/// component type (via it's [`ComponentId`]), then fetching the entity's row within that column. +/// +/// [structure-of-arrays]: https://en.wikipedia.org/wiki/AoS_and_SoA#Structure_of_arrays pub struct Table { columns: ImmutableSparseSet, entities: Vec, From 7c9ffed952cfa14e9127a3a59e831c50d78fe0f2 Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 28 Oct 2022 23:42:14 -0700 Subject: [PATCH 07/11] Fix CI --- crates/bevy_ecs/src/archetype.rs | 2 +- crates/bevy_ecs/src/storage/sparse_set.rs | 10 +++++----- crates/bevy_ecs/src/storage/table.rs | 12 +++++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index d6713e0937157..d49af628d33e8 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -195,7 +195,7 @@ impl Archetype { id, table_id, entities: Vec::new(), - components: components.to_immutable(), + components: components.into_immutable(), table_components, sparse_set_components, edges: Default::default(), diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 7a8e668a7524c..8b937316715c3 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -71,7 +71,7 @@ impl SparseArray { self.values.clear(); } - pub(crate) fn to_immutable(self) -> ImmutableSparseArray { + pub(crate) fn into_immutable(self) -> ImmutableSparseArray { ImmutableSparseArray { values: self.values.into_boxed_slice(), marker: PhantomData, @@ -398,11 +398,11 @@ impl SparseSet { self.indices.iter().zip(self.dense.iter_mut()) } - pub(crate) fn to_immutable(self) -> ImmutableSparseSet { + pub(crate) fn into_immutable(self) -> ImmutableSparseSet { ImmutableSparseSet { dense: self.dense.into_boxed_slice(), indices: self.indices.into_boxed_slice(), - sparse: self.sparse.to_immutable(), + sparse: self.sparse.into_immutable(), } } } @@ -429,7 +429,7 @@ impl ImmutableSparseSet { pub fn get(&self, index: I) -> Option<&V> { self.sparse.get(index).map(|dense_index| { - // SAFE: if the sparse index points to something in the dense vec, it exists + // SAFETY: if the sparse index points to something in the dense vec, it exists unsafe { self.dense.get_unchecked(*dense_index) } }) } @@ -437,7 +437,7 @@ impl ImmutableSparseSet { pub fn get_mut(&mut self, index: I) -> Option<&mut V> { let dense = &mut self.dense; self.sparse.get(index).map(move |dense_index| { - // SAFE: if the sparse index points to something in the dense vec, it exists + // SAFETY: if the sparse index points to something in the dense vec, it exists unsafe { dense.get_unchecked_mut(*dense_index) } }) } diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index 9d2b88c340986..a7139191dd89b 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -295,22 +295,24 @@ impl TableBuilder { pub fn build(self) -> Table { Table { - columns: self.columns.to_immutable(), + columns: self.columns.into_immutable(), entities: Vec::with_capacity(self.capacity), } } } -/// A column-oriented [structure-of-arrays] based storage for [`Component`]s of entities +/// A column-oriented [structure-of-arrays] based storage for [`Component`]s of entities /// in a [`World`]. -/// +/// /// Conceptually, a `Table` can be thought of as an `HashMap`, where /// each `Column` is a type-erased `Vec`. Each row corresponds to a single entity /// (i.e. index 3 in Column A and index 3 in Column B point to different components on the same -/// entity). Fetching components from a table involves fetching the associated column for a +/// entity). Fetching components from a table involves fetching the associated column for a /// component type (via it's [`ComponentId`]), then fetching the entity's row within that column. -/// +/// /// [structure-of-arrays]: https://en.wikipedia.org/wiki/AoS_and_SoA#Structure_of_arrays +/// [`Component`]: crate::component::Component +/// [`World`]: crate::world::World pub struct Table { columns: ImmutableSparseSet, entities: Vec, From 43f2b0e9ca4260bb241a4755c8a0b5d9b5531fa3 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 29 Oct 2022 03:25:03 -0700 Subject: [PATCH 08/11] Reduce code duplication with macro_rules --- crates/bevy_ecs/src/storage/sparse_set.rs | 216 +++++++++------------- 1 file changed, 89 insertions(+), 127 deletions(-) diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 8b937316715c3..d72cf47760005 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -14,6 +14,14 @@ pub(crate) struct SparseArray { marker: PhantomData, } +/// A space-optimized version of [`SparseArray`] that cannot be changed +/// after contrusction. +#[derive(Debug)] +pub(crate) struct ImmutableSparseArray { + values: Box<[Option]>, + marker: PhantomData, +} + impl Default for SparseArray { fn default() -> Self { Self::new() @@ -30,6 +38,27 @@ impl SparseArray { } } +macro_rules! impl_sparse_array { + ($ty:ident) => { + impl $ty { + #[inline] + pub fn contains(&self, index: I) -> bool { + let index = index.sparse_set_index(); + self.values.get(index).map(|v| v.is_some()).unwrap_or(false) + } + + #[inline] + pub fn get(&self, index: I) -> Option<&V> { + let index = index.sparse_set_index(); + self.values.get(index).map(|v| v.as_ref()).unwrap_or(None) + } + } + }; +} + +impl_sparse_array!(SparseArray); +impl_sparse_array!(ImmutableSparseArray); + impl SparseArray { #[inline] pub fn insert(&mut self, index: I, value: V) { @@ -40,18 +69,6 @@ impl SparseArray { self.values[index] = Some(value); } - #[inline] - pub fn contains(&self, index: I) -> bool { - let index = index.sparse_set_index(); - self.values.get(index).map(|v| v.is_some()).unwrap_or(false) - } - - #[inline] - pub fn get(&self, index: I) -> Option<&V> { - let index = index.sparse_set_index(); - self.values.get(index).map(|v| v.as_ref()).unwrap_or(None) - } - #[inline] pub fn get_mut(&mut self, index: I) -> Option<&mut V> { let index = index.sparse_set_index(); @@ -79,28 +96,6 @@ impl SparseArray { } } -/// A space-optimized version of [`SparseArray`] that cannot be changed -/// after contrusction. -#[derive(Debug)] -pub(crate) struct ImmutableSparseArray { - values: Box<[Option]>, - marker: PhantomData, -} - -impl ImmutableSparseArray { - #[inline] - pub fn contains(&self, index: I) -> bool { - let index = index.sparse_set_index(); - self.values.get(index).map(|v| v.is_some()).unwrap_or(false) - } - - #[inline] - pub fn get(&self, index: I) -> Option<&V> { - let index = index.sparse_set_index(); - self.values.get(index).map(|v| v.as_ref()).unwrap_or(None) - } -} - /// A sparse data structure of [Components](crate::component::Component) /// /// Designed for relatively fast insertions and deletions. @@ -278,11 +273,71 @@ pub struct SparseSet { sparse: SparseArray, } +/// A space-optimized version of [`SparseSet`] that cannot be changed +/// after contrusction. +#[derive(Debug)] +pub(crate) struct ImmutableSparseSet { + dense: Box<[V]>, + indices: Box<[I]>, + sparse: ImmutableSparseArray, +} + +macro_rules! impl_sparse_set { + ($ty:ident) => { + impl $ty { + #[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| { + // SAFETY: if the sparse index points to something in the dense vec, it exists + unsafe { self.dense.get_unchecked(*dense_index) } + }) + } + + pub fn get_mut(&mut self, index: I) -> Option<&mut V> { + let dense = &mut self.dense; + self.sparse.get(index).map(move |dense_index| { + // SAFETY: if the sparse index points to something in the dense vec, it exists + unsafe { dense.get_unchecked_mut(*dense_index) } + }) + } + + pub fn indices(&self) -> impl Iterator + '_ { + self.indices.iter().cloned() + } + + pub fn values(&self) -> impl Iterator { + self.dense.iter() + } + + pub fn values_mut(&mut self) -> impl Iterator { + self.dense.iter_mut() + } + + pub fn iter_mut(&mut self) -> impl Iterator { + self.indices.iter().zip(self.dense.iter_mut()) + } + } + }; +} + +impl_sparse_set!(SparseSet); +impl_sparse_set!(ImmutableSparseSet); + impl Default for SparseSet { fn default() -> Self { Self::new() } } + impl SparseSet { pub const fn new() -> Self { Self { @@ -335,36 +390,11 @@ impl SparseSet { } } - #[inline] - pub fn len(&self) -> usize { - self.dense.len() - } - #[inline] pub fn is_empty(&self) -> bool { self.dense.len() == 0 } - #[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| { - // SAFETY: if the sparse index points to something in the dense vec, it exists - unsafe { self.dense.get_unchecked(*dense_index) } - }) - } - - pub fn get_mut(&mut self, index: I) -> Option<&mut V> { - let dense = &mut self.dense; - self.sparse.get(index).map(move |dense_index| { - // SAFETY: if the sparse index points to something in the dense vec, it exists - unsafe { dense.get_unchecked_mut(*dense_index) } - }) - } - pub fn remove(&mut self, index: I) -> Option { self.sparse.remove(index).map(|dense_index| { let is_last = dense_index == self.dense.len() - 1; @@ -378,26 +408,10 @@ impl SparseSet { }) } - pub fn indices(&self) -> impl Iterator + '_ { - self.indices.iter().cloned() - } - - pub fn values(&self) -> impl Iterator { - self.dense.iter() - } - - pub fn values_mut(&mut self) -> impl Iterator { - self.dense.iter_mut() - } - pub fn iter(&self) -> impl Iterator { self.indices.iter().zip(self.dense.iter()) } - pub fn iter_mut(&mut self) -> impl Iterator { - self.indices.iter().zip(self.dense.iter_mut()) - } - pub(crate) fn into_immutable(self) -> ImmutableSparseSet { ImmutableSparseSet { dense: self.dense.into_boxed_slice(), @@ -407,58 +421,6 @@ impl SparseSet { } } -/// A space-optimized version of [`SparseSet`] that cannot be changed -/// after contrusction. -#[derive(Debug)] -pub(crate) struct ImmutableSparseSet { - dense: Box<[V]>, - indices: Box<[I]>, - sparse: ImmutableSparseArray, -} - -impl ImmutableSparseSet { - #[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| { - // SAFETY: if the sparse index points to something in the dense vec, it exists - unsafe { self.dense.get_unchecked(*dense_index) } - }) - } - - pub fn get_mut(&mut self, index: I) -> Option<&mut V> { - let dense = &mut self.dense; - self.sparse.get(index).map(move |dense_index| { - // SAFETY: if the sparse index points to something in the dense vec, it exists - unsafe { dense.get_unchecked_mut(*dense_index) } - }) - } - - pub fn indices(&self) -> impl Iterator + '_ { - self.indices.iter().cloned() - } - - pub fn values(&self) -> impl Iterator { - self.dense.iter() - } - - pub fn values_mut(&mut self) -> impl Iterator { - self.dense.iter_mut() - } - - pub fn iter_mut(&mut self) -> impl Iterator { - self.indices.iter().zip(self.dense.iter_mut()) - } -} - pub trait SparseSetIndex: Clone + PartialEq + Eq + Hash { fn sparse_set_index(&self) -> usize; fn get_sparse_set_index(value: usize) -> Self; From 3193bc72bacfb6f5693ab098b77b36c4d91cf4f1 Mon Sep 17 00:00:00 2001 From: James Liu Date: Mon, 31 Oct 2022 16:05:35 -0700 Subject: [PATCH 09/11] Fix typo. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kristoffer Søholm --- crates/bevy_ecs/src/storage/sparse_set.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index d72cf47760005..fb9a6ba779057 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -15,7 +15,7 @@ pub(crate) struct SparseArray { } /// A space-optimized version of [`SparseArray`] that cannot be changed -/// after contrusction. +/// after construction. #[derive(Debug)] pub(crate) struct ImmutableSparseArray { values: Box<[Option]>, From 0694407ce8624f0dbefba232d139878c7d48a523 Mon Sep 17 00:00:00 2001 From: James Liu Date: Mon, 31 Oct 2022 16:05:46 -0700 Subject: [PATCH 10/11] Fix typo. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kristoffer Søholm --- crates/bevy_ecs/src/storage/sparse_set.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index fb9a6ba779057..4657926f1446e 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -274,7 +274,7 @@ pub struct SparseSet { } /// A space-optimized version of [`SparseSet`] that cannot be changed -/// after contrusction. +/// after construction. #[derive(Debug)] pub(crate) struct ImmutableSparseSet { dense: Box<[V]>, From 84ba5d4f830b41c4e6f82e14a01d2fcef740e36c Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 15 Nov 2022 14:20:26 -0800 Subject: [PATCH 11/11] Fix CI --- crates/bevy_ecs/src/storage/sparse_set.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 8b7d690315443..4084c7b2b3627 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -322,6 +322,10 @@ macro_rules! impl_sparse_set { self.dense.iter_mut() } + pub fn iter(&self) -> impl Iterator { + self.indices.iter().zip(self.dense.iter()) + } + pub fn iter_mut(&mut self) -> impl Iterator { self.indices.iter().zip(self.dense.iter_mut()) } @@ -408,10 +412,6 @@ impl SparseSet { }) } - pub fn iter(&self) -> impl Iterator { - self.indices.iter().zip(self.dense.iter()) - } - pub(crate) fn into_immutable(self) -> ImmutableSparseSet { ImmutableSparseSet { dense: self.dense.into_boxed_slice(),