Path: blob/main/crates/bevy_ecs/src/storage/sparse_set.rs
6604 views
use crate::{1change_detection::MaybeLocation,2component::{CheckChangeTicks, ComponentId, ComponentInfo, ComponentTicks, Tick, TickCells},3entity::{Entity, EntityRow},4storage::{Column, TableRow},5};6use alloc::{boxed::Box, vec::Vec};7use bevy_ptr::{OwningPtr, Ptr};8use core::{cell::UnsafeCell, hash::Hash, marker::PhantomData, panic::Location};9use nonmax::{NonMaxU32, NonMaxUsize};1011#[derive(Debug)]12pub(crate) struct SparseArray<I, V = I> {13values: Vec<Option<V>>,14marker: PhantomData<I>,15}1617/// A space-optimized version of [`SparseArray`] that cannot be changed18/// after construction.19#[derive(Debug)]20pub(crate) struct ImmutableSparseArray<I, V = I> {21values: Box<[Option<V>]>,22marker: PhantomData<I>,23}2425impl<I: SparseSetIndex, V> Default for SparseArray<I, V> {26fn default() -> Self {27Self::new()28}29}3031impl<I, V> SparseArray<I, V> {32#[inline]33pub const fn new() -> Self {34Self {35values: Vec::new(),36marker: PhantomData,37}38}39}4041macro_rules! impl_sparse_array {42($ty:ident) => {43impl<I: SparseSetIndex, V> $ty<I, V> {44/// Returns `true` if the collection contains a value for the specified `index`.45#[inline]46pub fn contains(&self, index: I) -> bool {47let index = index.sparse_set_index();48self.values.get(index).is_some_and(Option::is_some)49}5051/// Returns a reference to the value at `index`.52///53/// Returns `None` if `index` does not have a value or if `index` is out of bounds.54#[inline]55pub fn get(&self, index: I) -> Option<&V> {56let index = index.sparse_set_index();57self.values.get(index).and_then(Option::as_ref)58}59}60};61}6263impl_sparse_array!(SparseArray);64impl_sparse_array!(ImmutableSparseArray);6566impl<I: SparseSetIndex, V> SparseArray<I, V> {67/// Inserts `value` at `index` in the array.68///69/// If `index` is out-of-bounds, this will enlarge the buffer to accommodate it.70#[inline]71pub fn insert(&mut self, index: I, value: V) {72let index = index.sparse_set_index();73if index >= self.values.len() {74self.values.resize_with(index + 1, || None);75}76self.values[index] = Some(value);77}7879/// Returns a mutable reference to the value at `index`.80///81/// Returns `None` if `index` does not have a value or if `index` is out of bounds.82#[inline]83pub fn get_mut(&mut self, index: I) -> Option<&mut V> {84let index = index.sparse_set_index();85self.values.get_mut(index).and_then(Option::as_mut)86}8788/// Removes and returns the value stored at `index`.89///90/// Returns `None` if `index` did not have a value or if `index` is out of bounds.91#[inline]92pub fn remove(&mut self, index: I) -> Option<V> {93let index = index.sparse_set_index();94self.values.get_mut(index).and_then(Option::take)95}9697/// Removes all of the values stored within.98pub fn clear(&mut self) {99self.values.clear();100}101102/// Converts the [`SparseArray`] into an immutable variant.103pub(crate) fn into_immutable(self) -> ImmutableSparseArray<I, V> {104ImmutableSparseArray {105values: self.values.into_boxed_slice(),106marker: PhantomData,107}108}109}110111/// A sparse data structure of [`Component`](crate::component::Component)s.112///113/// Designed for relatively fast insertions and deletions.114#[derive(Debug)]115pub struct ComponentSparseSet {116dense: Column,117// Internally this only relies on the Entity index to keep track of where the component data is118// stored for entities that are alive. The generation is not required, but is stored119// in debug builds to validate that access is correct.120#[cfg(not(debug_assertions))]121entities: Vec<EntityRow>,122#[cfg(debug_assertions)]123entities: Vec<Entity>,124sparse: SparseArray<EntityRow, TableRow>,125}126127impl ComponentSparseSet {128/// Creates a new [`ComponentSparseSet`] with a given component type layout and129/// initial `capacity`.130pub(crate) fn new(component_info: &ComponentInfo, capacity: usize) -> Self {131Self {132dense: Column::with_capacity(component_info, capacity),133entities: Vec::with_capacity(capacity),134sparse: Default::default(),135}136}137138/// Removes all of the values stored within.139pub(crate) fn clear(&mut self) {140self.dense.clear();141self.entities.clear();142self.sparse.clear();143}144145/// Returns the number of component values in the sparse set.146#[inline]147pub fn len(&self) -> usize {148self.dense.len()149}150151/// Returns `true` if the sparse set contains no component values.152#[inline]153pub fn is_empty(&self) -> bool {154self.dense.len() == 0155}156157/// Inserts the `entity` key and component `value` pair into this sparse158/// set.159///160/// # Safety161/// The `value` pointer must point to a valid address that matches the [`Layout`](std::alloc::Layout)162/// inside the [`ComponentInfo`] given when constructing this sparse set.163pub(crate) unsafe fn insert(164&mut self,165entity: Entity,166value: OwningPtr<'_>,167change_tick: Tick,168caller: MaybeLocation,169) {170if let Some(&dense_index) = self.sparse.get(entity.row()) {171#[cfg(debug_assertions)]172assert_eq!(entity, self.entities[dense_index.index()]);173self.dense.replace(dense_index, value, change_tick, caller);174} else {175let dense_index = self.dense.len();176self.dense177.push(value, ComponentTicks::new(change_tick), caller);178179// SAFETY: This entity row does not exist here yet, so there are no duplicates,180// and the entity index can not be the max, so the length must not be max either.181// To do so would have caused a panic in the entity alloxator.182let table_row = unsafe { TableRow::new(NonMaxU32::new_unchecked(dense_index as u32)) };183184self.sparse.insert(entity.row(), table_row);185#[cfg(debug_assertions)]186assert_eq!(self.entities.len(), dense_index);187#[cfg(not(debug_assertions))]188self.entities.push(entity.row());189#[cfg(debug_assertions)]190self.entities.push(entity);191}192}193194/// Returns `true` if the sparse set has a component value for the provided `entity`.195#[inline]196pub fn contains(&self, entity: Entity) -> bool {197#[cfg(debug_assertions)]198{199if let Some(&dense_index) = self.sparse.get(entity.row()) {200#[cfg(debug_assertions)]201assert_eq!(entity, self.entities[dense_index.index()]);202true203} else {204false205}206}207#[cfg(not(debug_assertions))]208self.sparse.contains(entity.row())209}210211/// Returns a reference to the entity's component value.212///213/// Returns `None` if `entity` does not have a component in the sparse set.214#[inline]215pub fn get(&self, entity: Entity) -> Option<Ptr<'_>> {216self.sparse.get(entity.row()).map(|&dense_index| {217#[cfg(debug_assertions)]218assert_eq!(entity, self.entities[dense_index.index()]);219// SAFETY: if the sparse index points to something in the dense vec, it exists220unsafe { self.dense.get_data_unchecked(dense_index) }221})222}223224/// Returns references to the entity's component value and its added and changed ticks.225///226/// Returns `None` if `entity` does not have a component in the sparse set.227#[inline]228pub fn get_with_ticks(229&self,230entity: Entity,231) -> Option<(232Ptr<'_>,233TickCells<'_>,234MaybeLocation<&UnsafeCell<&'static Location<'static>>>,235)> {236let dense_index = *self.sparse.get(entity.row())?;237#[cfg(debug_assertions)]238assert_eq!(entity, self.entities[dense_index.index()]);239// SAFETY: if the sparse index points to something in the dense vec, it exists240unsafe {241Some((242self.dense.get_data_unchecked(dense_index),243TickCells {244added: self.dense.get_added_tick_unchecked(dense_index),245changed: self.dense.get_changed_tick_unchecked(dense_index),246},247self.dense.get_changed_by_unchecked(dense_index),248))249}250}251252/// Returns a reference to the "added" tick of the entity's component value.253///254/// Returns `None` if `entity` does not have a component in the sparse set.255#[inline]256pub fn get_added_tick(&self, entity: Entity) -> Option<&UnsafeCell<Tick>> {257let dense_index = *self.sparse.get(entity.row())?;258#[cfg(debug_assertions)]259assert_eq!(entity, self.entities[dense_index.index()]);260// SAFETY: if the sparse index points to something in the dense vec, it exists261unsafe { Some(self.dense.get_added_tick_unchecked(dense_index)) }262}263264/// Returns a reference to the "changed" tick of the entity's component value.265///266/// Returns `None` if `entity` does not have a component in the sparse set.267#[inline]268pub fn get_changed_tick(&self, entity: Entity) -> Option<&UnsafeCell<Tick>> {269let dense_index = *self.sparse.get(entity.row())?;270#[cfg(debug_assertions)]271assert_eq!(entity, self.entities[dense_index.index()]);272// SAFETY: if the sparse index points to something in the dense vec, it exists273unsafe { Some(self.dense.get_changed_tick_unchecked(dense_index)) }274}275276/// Returns a reference to the "added" and "changed" ticks of the entity's component value.277///278/// Returns `None` if `entity` does not have a component in the sparse set.279#[inline]280pub fn get_ticks(&self, entity: Entity) -> Option<ComponentTicks> {281let dense_index = *self.sparse.get(entity.row())?;282#[cfg(debug_assertions)]283assert_eq!(entity, self.entities[dense_index.index()]);284// SAFETY: if the sparse index points to something in the dense vec, it exists285unsafe { Some(self.dense.get_ticks_unchecked(dense_index)) }286}287288/// Returns a reference to the calling location that last changed the entity's component value.289///290/// Returns `None` if `entity` does not have a component in the sparse set.291#[inline]292pub fn get_changed_by(293&self,294entity: Entity,295) -> MaybeLocation<Option<&UnsafeCell<&'static Location<'static>>>> {296MaybeLocation::new_with_flattened(|| {297let dense_index = *self.sparse.get(entity.row())?;298#[cfg(debug_assertions)]299assert_eq!(entity, self.entities[dense_index.index()]);300// SAFETY: if the sparse index points to something in the dense vec, it exists301unsafe { Some(self.dense.get_changed_by_unchecked(dense_index)) }302})303}304305/// Returns the drop function for the component type stored in the sparse set,306/// or `None` if it doesn't need to be dropped.307#[inline]308pub fn get_drop(&self) -> Option<unsafe fn(OwningPtr<'_>)> {309self.dense.get_drop()310}311312/// Removes the `entity` from this sparse set and returns a pointer to the associated value (if313/// it exists).314#[must_use = "The returned pointer must be used to drop the removed component."]315pub(crate) fn remove_and_forget(&mut self, entity: Entity) -> Option<OwningPtr<'_>> {316self.sparse.remove(entity.row()).map(|dense_index| {317#[cfg(debug_assertions)]318assert_eq!(entity, self.entities[dense_index.index()]);319self.entities.swap_remove(dense_index.index());320let is_last = dense_index.index() == self.dense.len() - 1;321// SAFETY: dense_index was just removed from `sparse`, which ensures that it is valid322let (value, _, _) = unsafe { self.dense.swap_remove_and_forget_unchecked(dense_index) };323if !is_last {324let swapped_entity = self.entities[dense_index.index()];325#[cfg(not(debug_assertions))]326let index = swapped_entity;327#[cfg(debug_assertions)]328let index = swapped_entity.row();329*self.sparse.get_mut(index).unwrap() = dense_index;330}331value332})333}334335/// Removes (and drops) the entity's component value from the sparse set.336///337/// Returns `true` if `entity` had a component value in the sparse set.338pub(crate) fn remove(&mut self, entity: Entity) -> bool {339if let Some(dense_index) = self.sparse.remove(entity.row()) {340#[cfg(debug_assertions)]341assert_eq!(entity, self.entities[dense_index.index()]);342self.entities.swap_remove(dense_index.index());343let is_last = dense_index.index() == self.dense.len() - 1;344// SAFETY: if the sparse index points to something in the dense vec, it exists345unsafe {346self.dense.swap_remove_unchecked(dense_index);347}348if !is_last {349let swapped_entity = self.entities[dense_index.index()];350#[cfg(not(debug_assertions))]351let index = swapped_entity;352#[cfg(debug_assertions)]353let index = swapped_entity.row();354*self.sparse.get_mut(index).unwrap() = dense_index;355}356true357} else {358false359}360}361362pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {363self.dense.check_change_ticks(check);364}365}366367/// A data structure that blends dense and sparse storage368///369/// `I` is the type of the indices, while `V` is the type of data stored in the dense storage.370#[derive(Debug)]371pub struct SparseSet<I, V: 'static> {372dense: Vec<V>,373indices: Vec<I>,374sparse: SparseArray<I, NonMaxUsize>,375}376377/// A space-optimized version of [`SparseSet`] that cannot be changed378/// after construction.379#[derive(Debug)]380pub(crate) struct ImmutableSparseSet<I, V: 'static> {381dense: Box<[V]>,382indices: Box<[I]>,383sparse: ImmutableSparseArray<I, NonMaxUsize>,384}385386macro_rules! impl_sparse_set {387($ty:ident) => {388impl<I: SparseSetIndex, V> $ty<I, V> {389/// Returns the number of elements in the sparse set.390#[inline]391pub fn len(&self) -> usize {392self.dense.len()393}394395/// Returns `true` if the sparse set contains a value for `index`.396#[inline]397pub fn contains(&self, index: I) -> bool {398self.sparse.contains(index)399}400401/// Returns a reference to the value for `index`.402///403/// Returns `None` if `index` does not have a value in the sparse set.404pub fn get(&self, index: I) -> Option<&V> {405self.sparse.get(index).map(|dense_index| {406// SAFETY: if the sparse index points to something in the dense vec, it exists407unsafe { self.dense.get_unchecked(dense_index.get()) }408})409}410411/// Returns a mutable reference to the value for `index`.412///413/// Returns `None` if `index` does not have a value in the sparse set.414pub fn get_mut(&mut self, index: I) -> Option<&mut V> {415let dense = &mut self.dense;416self.sparse.get(index).map(move |dense_index| {417// SAFETY: if the sparse index points to something in the dense vec, it exists418unsafe { dense.get_unchecked_mut(dense_index.get()) }419})420}421422/// Returns an iterator visiting all keys (indices) in arbitrary order.423pub fn indices(&self) -> &[I] {424&self.indices425}426427/// Returns an iterator visiting all values in arbitrary order.428pub fn values(&self) -> impl Iterator<Item = &V> {429self.dense.iter()430}431432/// Returns an iterator visiting all values mutably in arbitrary order.433pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {434self.dense.iter_mut()435}436437/// Returns an iterator visiting all key-value pairs in arbitrary order, with references to the values.438pub fn iter(&self) -> impl Iterator<Item = (&I, &V)> {439self.indices.iter().zip(self.dense.iter())440}441442/// Returns an iterator visiting all key-value pairs in arbitrary order, with mutable references to the values.443pub fn iter_mut(&mut self) -> impl Iterator<Item = (&I, &mut V)> {444self.indices.iter().zip(self.dense.iter_mut())445}446}447};448}449450impl_sparse_set!(SparseSet);451impl_sparse_set!(ImmutableSparseSet);452453impl<I: SparseSetIndex, V> Default for SparseSet<I, V> {454fn default() -> Self {455Self::new()456}457}458459impl<I, V> SparseSet<I, V> {460/// Creates a new [`SparseSet`].461pub const fn new() -> Self {462Self {463dense: Vec::new(),464indices: Vec::new(),465sparse: SparseArray::new(),466}467}468}469470impl<I: SparseSetIndex, V> SparseSet<I, V> {471/// Creates a new [`SparseSet`] with a specified initial capacity.472pub fn with_capacity(capacity: usize) -> Self {473Self {474dense: Vec::with_capacity(capacity),475indices: Vec::with_capacity(capacity),476sparse: Default::default(),477}478}479480/// Returns the total number of elements the [`SparseSet`] can hold without needing to reallocate.481#[inline]482pub fn capacity(&self) -> usize {483self.dense.capacity()484}485486/// Inserts `value` at `index`.487///488/// If a value was already present at `index`, it will be overwritten.489pub fn insert(&mut self, index: I, value: V) {490if let Some(dense_index) = self.sparse.get(index.clone()).cloned() {491// SAFETY: dense indices stored in self.sparse always exist492unsafe {493*self.dense.get_unchecked_mut(dense_index.get()) = value;494}495} else {496self.sparse497.insert(index.clone(), NonMaxUsize::new(self.dense.len()).unwrap());498self.indices.push(index);499self.dense.push(value);500}501}502503/// Returns a reference to the value for `index`, inserting one computed from `func`504/// if not already present.505pub fn get_or_insert_with(&mut self, index: I, func: impl FnOnce() -> V) -> &mut V {506if let Some(dense_index) = self.sparse.get(index.clone()).cloned() {507// SAFETY: dense indices stored in self.sparse always exist508unsafe { self.dense.get_unchecked_mut(dense_index.get()) }509} else {510let value = func();511let dense_index = self.dense.len();512self.sparse513.insert(index.clone(), NonMaxUsize::new(dense_index).unwrap());514self.indices.push(index);515self.dense.push(value);516// SAFETY: dense index was just populated above517unsafe { self.dense.get_unchecked_mut(dense_index) }518}519}520521/// Returns `true` if the sparse set contains no elements.522#[inline]523pub fn is_empty(&self) -> bool {524self.dense.len() == 0525}526527/// Removes and returns the value for `index`.528///529/// Returns `None` if `index` does not have a value in the sparse set.530pub fn remove(&mut self, index: I) -> Option<V> {531self.sparse.remove(index).map(|dense_index| {532let index = dense_index.get();533let is_last = index == self.dense.len() - 1;534let value = self.dense.swap_remove(index);535self.indices.swap_remove(index);536if !is_last {537let swapped_index = self.indices[index].clone();538*self.sparse.get_mut(swapped_index).unwrap() = dense_index;539}540value541})542}543544/// Clears all of the elements from the sparse set.545pub fn clear(&mut self) {546self.dense.clear();547self.indices.clear();548self.sparse.clear();549}550551/// Converts the sparse set into its immutable variant.552pub(crate) fn into_immutable(self) -> ImmutableSparseSet<I, V> {553ImmutableSparseSet {554dense: self.dense.into_boxed_slice(),555indices: self.indices.into_boxed_slice(),556sparse: self.sparse.into_immutable(),557}558}559}560561/// Represents something that can be stored in a [`SparseSet`] as an integer.562///563/// Ideally, the `usize` values should be very small (ie: incremented starting from564/// zero), as the number of bits needed to represent a `SparseSetIndex` in a `FixedBitSet`565/// is proportional to the **value** of those `usize`.566pub trait SparseSetIndex: Clone + PartialEq + Eq + Hash {567/// Gets the sparse set index corresponding to this instance.568fn sparse_set_index(&self) -> usize;569/// Creates a new instance of this type with the specified index.570fn get_sparse_set_index(value: usize) -> Self;571}572573macro_rules! impl_sparse_set_index {574($($ty:ty),+) => {575$(impl SparseSetIndex for $ty {576#[inline]577fn sparse_set_index(&self) -> usize {578*self as usize579}580581#[inline]582fn get_sparse_set_index(value: usize) -> Self {583value as $ty584}585})*586};587}588589impl_sparse_set_index!(u8, u16, u32, u64, usize);590591/// A collection of [`ComponentSparseSet`] storages, indexed by [`ComponentId`]592///593/// Can be accessed via [`Storages`](crate::storage::Storages)594#[derive(Default)]595pub struct SparseSets {596sets: SparseSet<ComponentId, ComponentSparseSet>,597}598599impl SparseSets {600/// Returns the number of [`ComponentSparseSet`]s this collection contains.601#[inline]602pub fn len(&self) -> usize {603self.sets.len()604}605606/// Returns true if this collection contains no [`ComponentSparseSet`]s.607#[inline]608pub fn is_empty(&self) -> bool {609self.sets.is_empty()610}611612/// An Iterator visiting all ([`ComponentId`], [`ComponentSparseSet`]) pairs.613/// NOTE: Order is not guaranteed.614pub fn iter(&self) -> impl Iterator<Item = (ComponentId, &ComponentSparseSet)> {615self.sets.iter().map(|(id, data)| (*id, data))616}617618/// Gets a reference to the [`ComponentSparseSet`] of a [`ComponentId`]. This may be `None` if the component has never been spawned.619#[inline]620pub fn get(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> {621self.sets.get(component_id)622}623624/// Gets a mutable reference of [`ComponentSparseSet`] of a [`ComponentInfo`].625/// Create a new [`ComponentSparseSet`] if not exists.626pub(crate) fn get_or_insert(627&mut self,628component_info: &ComponentInfo,629) -> &mut ComponentSparseSet {630if !self.sets.contains(component_info.id()) {631self.sets.insert(632component_info.id(),633ComponentSparseSet::new(component_info, 64),634);635}636637self.sets.get_mut(component_info.id()).unwrap()638}639640/// Gets a mutable reference to the [`ComponentSparseSet`] of a [`ComponentId`]. This may be `None` if the component has never been spawned.641pub(crate) fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ComponentSparseSet> {642self.sets.get_mut(component_id)643}644645/// Clear entities stored in each [`ComponentSparseSet`]646pub(crate) fn clear_entities(&mut self) {647for set in self.sets.values_mut() {648set.clear();649}650}651652pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {653for set in self.sets.values_mut() {654set.check_change_ticks(check);655}656}657}658659#[cfg(test)]660mod tests {661use super::SparseSets;662use crate::{663component::{Component, ComponentDescriptor, ComponentId, ComponentInfo},664entity::{Entity, EntityRow},665storage::SparseSet,666};667use alloc::{vec, vec::Vec};668use nonmax::NonMaxU32;669670#[derive(Debug, Eq, PartialEq)]671struct Foo(usize);672673#[test]674fn sparse_set() {675let mut set = SparseSet::<Entity, Foo>::default();676let e0 = Entity::from_raw(EntityRow::new(NonMaxU32::new(0).unwrap()));677let e1 = Entity::from_raw(EntityRow::new(NonMaxU32::new(1).unwrap()));678let e2 = Entity::from_raw(EntityRow::new(NonMaxU32::new(2).unwrap()));679let e3 = Entity::from_raw(EntityRow::new(NonMaxU32::new(3).unwrap()));680let e4 = Entity::from_raw(EntityRow::new(NonMaxU32::new(4).unwrap()));681682set.insert(e1, Foo(1));683set.insert(e2, Foo(2));684set.insert(e3, Foo(3));685686assert_eq!(set.get(e0), None);687assert_eq!(set.get(e1), Some(&Foo(1)));688assert_eq!(set.get(e2), Some(&Foo(2)));689assert_eq!(set.get(e3), Some(&Foo(3)));690assert_eq!(set.get(e4), None);691692{693let iter_results = set.values().collect::<Vec<_>>();694assert_eq!(iter_results, vec![&Foo(1), &Foo(2), &Foo(3)]);695}696697assert_eq!(set.remove(e2), Some(Foo(2)));698assert_eq!(set.remove(e2), None);699700assert_eq!(set.get(e0), None);701assert_eq!(set.get(e1), Some(&Foo(1)));702assert_eq!(set.get(e2), None);703assert_eq!(set.get(e3), Some(&Foo(3)));704assert_eq!(set.get(e4), None);705706assert_eq!(set.remove(e1), Some(Foo(1)));707708assert_eq!(set.get(e0), None);709assert_eq!(set.get(e1), None);710assert_eq!(set.get(e2), None);711assert_eq!(set.get(e3), Some(&Foo(3)));712assert_eq!(set.get(e4), None);713714set.insert(e1, Foo(10));715716assert_eq!(set.get(e1), Some(&Foo(10)));717718*set.get_mut(e1).unwrap() = Foo(11);719assert_eq!(set.get(e1), Some(&Foo(11)));720}721722#[test]723fn sparse_sets() {724let mut sets = SparseSets::default();725726#[derive(Component, Default, Debug)]727struct TestComponent1;728729#[derive(Component, Default, Debug)]730struct TestComponent2;731732assert_eq!(sets.len(), 0);733assert!(sets.is_empty());734735register_component::<TestComponent1>(&mut sets, 1);736assert_eq!(sets.len(), 1);737738register_component::<TestComponent2>(&mut sets, 2);739assert_eq!(sets.len(), 2);740741// check its shape by iter742let mut collected_sets = sets743.iter()744.map(|(id, set)| (id, set.len()))745.collect::<Vec<_>>();746collected_sets.sort();747assert_eq!(748collected_sets,749vec![(ComponentId::new(1), 0), (ComponentId::new(2), 0),]750);751752fn register_component<T: Component>(sets: &mut SparseSets, id: usize) {753let descriptor = ComponentDescriptor::new::<T>();754let id = ComponentId::new(id);755let info = ComponentInfo::new(id, descriptor);756sets.get_or_insert(&info);757}758}759}760761762