refactor: move internals from entity_ref to World, add SAFETY comments (#6402)

# Objective

There are some utility functions for actually working with `Storages` inside `entity_ref.rs` that are used both for `EntityRef/EntityMut` and `World`, with a `// TODO: move to Storages`.
This PR moves them to private methods on `World`, because that's the safest API boundary. On `Storages` you would need to ensure that you pass `Components` from the same world.

## Solution

- move get_component[_with_type], get_ticks[_with_type], get_component_and_ticks[_with_type] to `World` (still pub(crate))
- replace `pub use entity_ref::*;` with `pub use entity_ref::{EntityRef, EntityMut}` and qualified `entity_ref::get_mut[_by_id]` in `world.rs`
- add safety comments to a bunch of methods
This commit is contained in:
Jakob Hellermann 2023-01-13 16:50:26 +00:00
parent feac2c206c
commit 008c156991
2 changed files with 323 additions and 312 deletions

View file

@ -4,10 +4,9 @@ use crate::{
change_detection::{MutUntyped, TicksMut},
component::{
Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType,
TickCells,
},
entity::{Entities, Entity, EntityLocation},
storage::{Column, ComponentSparseSet, SparseSet, Storages},
storage::{SparseSet, Storages},
world::{Mut, World},
};
use bevy_ptr::{OwningPtr, Ptr};
@ -72,17 +71,18 @@ impl<'w> EntityRef<'w> {
pub fn get<T: Component>(&self) -> Option<&'w T> {
// SAFETY:
// - entity location and entity is valid
// - returned component is of type T
// - the storage type provided is correct for T
// - world access is immutable, lifetime tied to `&self`
unsafe {
get_component_with_type(
self.world,
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
self.entity,
self.location,
)
.map(|value| value.deref::<T>())
self.world
.get_component_with_type(
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
self.entity,
self.location,
)
// SAFETY: returned component is of type T
.map(|value| value.deref::<T>())
}
}
@ -92,10 +92,10 @@ impl<'w> EntityRef<'w> {
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
// SAFETY:
// - entity location and entity is valid
// - world access is immutable, lifetime tied to `&self`
// - the storage type provided is correct for T
unsafe {
get_ticks_with_type(
self.world,
self.world.get_ticks_with_type(
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
self.entity,
@ -112,15 +112,13 @@ impl<'w> EntityRef<'w> {
/// compile time.**
#[inline]
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks> {
if !self.contains_id(component_id) {
return None;
}
let info = self.world.components().get_info(component_id)?;
// SAFETY: Entity location is valid and component_id exists.
// SAFETY:
// - entity location and entity is valid
// - world access is immutable, lifetime tied to `&self`
// - the storage type provided is correct for T
unsafe {
get_ticks(
self.world,
self.world.get_ticks(
component_id,
info.storage_type(),
self.entity,
@ -149,20 +147,20 @@ impl<'w> EntityRef<'w> {
// - entity location and entity is valid
// - returned component is of type T
// - the storage type provided is correct for T
get_component_and_ticks_with_type(
self.world,
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
self.entity,
self.location,
)
.map(|(value, ticks)| Mut {
// SAFETY:
// - returned component is of type T
// - Caller guarantees that this reference will not alias.
value: value.assert_unique().deref_mut::<T>(),
ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick),
})
self.world
.get_component_and_ticks_with_type(
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
self.entity,
self.location,
)
.map(|(value, ticks)| Mut {
// SAFETY:
// - returned component is of type T
// - Caller guarantees that this reference will not alias.
value: value.assert_unique().deref_mut::<T>(),
ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick),
})
}
}
@ -179,12 +177,11 @@ impl<'w> EntityRef<'w> {
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
let info = self.world.components().get_info(component_id)?;
// SAFETY:
// - entity_location is valid,
// - component_id is valid as checked by the line above
// - entity_location and entity are valid
// . component_id is valid as checked by the line above
// - the storage type is accurate as checked by the fetched ComponentInfo
unsafe {
get_component(
self.world,
self.world.get_component(
component_id,
info.storage_type(),
self.entity,
@ -257,19 +254,19 @@ impl<'w> EntityMut<'w> {
#[inline]
pub fn get<T: Component>(&self) -> Option<&'_ T> {
// SAFETY:
// - lifetimes enforce correct usage of returned borrow
// - entity location and entity is valid
// - returned component is of type T
// - entity location is valid
// - world access is immutable, lifetime tied to `&self`
// - the storage type provided is correct for T
unsafe {
get_component_with_type(
self.world,
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
self.entity,
self.location,
)
.map(|value| value.deref::<T>())
self.world
.get_component_with_type(
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
self.entity,
self.location,
)
// SAFETY: returned component is of type T
.map(|value| value.deref::<T>())
}
}
@ -284,11 +281,11 @@ impl<'w> EntityMut<'w> {
#[inline]
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
// SAFETY:
// - entity location and entity is valid
// - entity location is valid
// - world access is immutable, lifetime tied to `&self`
// - the storage type provided is correct for T
unsafe {
get_ticks_with_type(
self.world,
self.world.get_ticks_with_type(
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
self.entity,
@ -305,15 +302,13 @@ impl<'w> EntityMut<'w> {
/// compile time.**
#[inline]
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks> {
if !self.contains_id(component_id) {
return None;
}
let info = self.world.components().get_info(component_id)?;
// SAFETY: Entity location is valid and component_id exists.
// SAFETY:
// - entity location is valid
// - world access is immutable, lifetime tied to `&self`
// - the storage type provided is correct for T
unsafe {
get_ticks(
self.world,
self.world.get_ticks(
component_id,
info.storage_type(),
self.entity,
@ -338,21 +333,21 @@ impl<'w> EntityMut<'w> {
// - entity location and entity is valid
// - returned component is of type T
// - the storage type provided is correct for T
get_component_and_ticks_with_type(
self.world,
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
self.entity,
self.location,
)
.map(|(value, ticks)| Mut {
value: value.assert_unique().deref_mut::<T>(),
ticks: TicksMut::from_tick_cells(
ticks,
self.world.last_change_tick(),
self.world.read_change_tick(),
),
})
self.world
.get_component_and_ticks_with_type(
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
self.entity,
self.location,
)
.map(|(value, ticks)| Mut {
value: value.assert_unique().deref_mut::<T>(),
ticks: TicksMut::from_tick_cells(
ticks,
self.world.last_change_tick(),
self.world.read_change_tick(),
),
})
}
/// Adds a [`Bundle`] of components to the entity.
@ -417,10 +412,13 @@ impl<'w> EntityMut<'w> {
let result = unsafe {
T::from_components(storages, &mut |storages| {
let component_id = bundle_components.next().unwrap();
// SAFETY: entity location is valid and table row is removed below
// SAFETY:
// - entity location is valid
// - table row is removed below, without dropping the contents
// - `components` comes from the same world as `storages`
take_component(
components,
storages,
components,
removed_components,
component_id,
entity,
@ -671,8 +669,7 @@ impl<'w> EntityMut<'w> {
// - component_id is valid as checked by the line above
// - the storage type is accurate as checked by the fetched ComponentInfo
unsafe {
get_component(
self.world,
self.world.get_component(
component_id,
info.storage_type(),
self.entity,
@ -697,213 +694,6 @@ impl<'w> EntityMut<'w> {
}
}
#[inline]
fn fetch_table(
world: &World,
location: EntityLocation,
component_id: ComponentId,
) -> Option<&Column> {
world.storages.tables[location.table_id].get_column(component_id)
}
#[inline]
fn fetch_sparse_set(world: &World, component_id: ComponentId) -> Option<&ComponentSparseSet> {
world.storages.sparse_sets.get(component_id)
}
// TODO: move to Storages?
/// Get a raw pointer to a particular [`Component`] on a particular [`Entity`] in the provided [`World`].
///
/// # Safety
/// - `location` must be within bounds of the given archetype and table and `entity` must exist inside
/// the archetype and table
/// - `component_id` must be valid
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
#[inline]
pub(crate) unsafe fn get_component(
world: &World,
component_id: ComponentId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<Ptr<'_>> {
match storage_type {
StorageType::Table => {
let components = fetch_table(world, location, component_id)?;
// SAFETY: archetypes only store valid table_rows and the stored component type is T
Some(components.get_data_unchecked(location.table_row))
}
StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get(entity),
}
}
// TODO: move to Storages?
/// Get a raw pointer to the [`ComponentTicks`] of a particular [`Component`] on a particular [`Entity`] in the provided [World].
///
/// # Safety
/// - Caller must ensure that `component_id` is valid
/// - `location` must be within bounds of the given archetype and `entity` must exist inside
/// the archetype
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
#[inline]
unsafe fn get_component_and_ticks(
world: &World,
component_id: ComponentId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<(Ptr<'_>, TickCells<'_>)> {
match storage_type {
StorageType::Table => {
let components = fetch_table(world, location, component_id)?;
// SAFETY: archetypes only store valid table_rows and the stored component type is T
Some((
components.get_data_unchecked(location.table_row),
TickCells {
added: components.get_added_ticks_unchecked(location.table_row),
changed: components.get_changed_ticks_unchecked(location.table_row),
},
))
}
StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get_with_ticks(entity),
}
}
/// # Safety
/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside
/// the archetype
/// - `component_id` must be valid
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
unsafe fn get_ticks(
world: &World,
component_id: ComponentId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<ComponentTicks> {
match storage_type {
StorageType::Table => {
let components = fetch_table(world, location, component_id)?;
// SAFETY: archetypes only store valid table_rows and the stored component type is T
Some(components.get_ticks_unchecked(location.table_row))
}
StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get_ticks(entity),
}
}
// TODO: move to Storages?
/// Moves component data out of storage.
///
/// This function leaves the underlying memory unchanged, but the component behind
/// returned pointer is semantically owned by the caller and will not be dropped in its original location.
/// Caller is responsible to drop component data behind returned pointer.
///
/// # Safety
/// - `location` must be within bounds of the given archetype and table and `entity` must exist inside the archetype
/// and table.
/// - `component_id` must be valid
/// - The relevant table row **must be removed** by the caller once all components are taken
#[inline]
unsafe fn take_component<'a>(
components: &Components,
storages: &'a mut Storages,
removed_components: &mut SparseSet<ComponentId, Vec<Entity>>,
component_id: ComponentId,
entity: Entity,
location: EntityLocation,
) -> OwningPtr<'a> {
let component_info = components.get_info_unchecked(component_id);
let removed_components = removed_components.get_or_insert_with(component_id, Vec::new);
removed_components.push(entity);
match component_info.storage_type() {
StorageType::Table => {
let table = &mut storages.tables[location.table_id];
// SAFETY: archetypes will always point to valid columns
let components = table.get_column_mut(component_id).unwrap();
// SAFETY: archetypes only store valid table_rows and the stored component type is T
components
.get_data_unchecked_mut(location.table_row)
.promote()
}
StorageType::SparseSet => storages
.sparse_sets
.get_mut(component_id)
.unwrap()
.remove_and_forget(entity)
.unwrap(),
}
}
/// Get a raw pointer to a particular [`Component`] by [`TypeId`] on a particular [`Entity`] in the provided [`World`].
///
/// # Safety
/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside
/// the archetype
/// - `type_id` must be correspond to a type that implements [`Component`]
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
#[inline]
unsafe fn get_component_with_type(
world: &World,
type_id: TypeId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<Ptr<'_>> {
get_component(
world,
world.components.get_id(type_id)?,
storage_type,
entity,
location,
)
}
/// Get a raw pointer to the [`ComponentTicks`] of a particular [`Component`] by [`TypeId`] on a particular [`Entity`] in the provided [`World`].
///
/// # Safety
/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside
/// the archetype
/// - `type_id` must be correspond to a type that implements [`Component`]
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
#[inline]
unsafe fn get_component_and_ticks_with_type(
world: &World,
type_id: TypeId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<(Ptr<'_>, TickCells<'_>)> {
get_component_and_ticks(
world,
world.components.get_id(type_id)?,
storage_type,
entity,
location,
)
}
/// # Safety
/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside
/// the archetype
/// - `type_id` must be correspond to a type that implements [`Component`]
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
#[inline]
unsafe fn get_ticks_with_type(
world: &World,
type_id: TypeId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<ComponentTicks> {
get_ticks(
world,
world.components.get_id(type_id)?,
storage_type,
entity,
location,
)
}
fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool {
if let Some(component_id) = world.components.get_id(type_id) {
contains_component_with_id(world, component_id, location)
@ -1044,21 +834,26 @@ pub(crate) unsafe fn get_mut<T: Component>(
entity: Entity,
location: EntityLocation,
) -> Option<Mut<'_, T>> {
// SAFETY: world access is unique, entity location is valid, and returned component is of type
// T
let change_tick = world.change_tick();
let last_change_tick = world.last_change_tick();
get_component_and_ticks_with_type(
world,
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
entity,
location,
)
.map(|(value, ticks)| Mut {
value: value.assert_unique().deref_mut::<T>(),
ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick),
})
// SAFETY:
// - world access is unique
// - entity location is valid
// - and returned component is of type T
world
.get_component_and_ticks_with_type(
TypeId::of::<T>(),
T::Storage::STORAGE_TYPE,
entity,
location,
)
.map(|(value, ticks)| Mut {
// SAFETY:
// - world access is unique and ties world lifetime to `Mut` lifetime
// - `value` is of type `T`
value: value.assert_unique().deref_mut::<T>(),
ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick),
})
}
// SAFETY: EntityLocation must be valid, component_id must be valid
@ -1068,16 +863,66 @@ pub(crate) unsafe fn get_mut_by_id(
entity: Entity,
location: EntityLocation,
component_id: ComponentId,
) -> Option<MutUntyped> {
) -> Option<MutUntyped<'_>> {
let change_tick = world.change_tick();
// SAFETY: component_id is valid
let info = world.components.get_info_unchecked(component_id);
// SAFETY: world access is unique, entity location and component_id required to be valid
get_component_and_ticks(world, component_id, info.storage_type(), entity, location).map(
|(value, ticks)| MutUntyped {
// SAFETY:
// - world access is unique
// - entity location is valid
// - and returned component is of type T
world
.get_component_and_ticks(component_id, info.storage_type(), entity, location)
.map(|(value, ticks)| MutUntyped {
// SAFETY: world access is unique and ties world lifetime to `MutUntyped` lifetime
value: value.assert_unique(),
ticks: TicksMut::from_tick_cells(ticks, world.last_change_tick(), change_tick),
},
)
})
}
/// Moves component data out of storage.
///
/// This function leaves the underlying memory unchanged, but the component behind
/// returned pointer is semantically owned by the caller and will not be dropped in its original location.
/// Caller is responsible to drop component data behind returned pointer.
///
/// # Safety
/// - `location.table_row` must be in bounds of column of component id `component_id`
/// - `component_id` must be valid
/// - `components` must come from the same world as `self`
/// - The relevant table row **must be removed** by the caller once all components are taken, without dropping the value
#[inline]
pub(crate) unsafe fn take_component<'a>(
storages: &'a mut Storages,
components: &Components,
removed_components: &mut SparseSet<ComponentId, Vec<Entity>>,
component_id: ComponentId,
entity: Entity,
location: EntityLocation,
) -> OwningPtr<'a> {
// SAFETY: caller promises component_id to be valid
let component_info = components.get_info_unchecked(component_id);
let removed_components = removed_components.get_or_insert_with(component_id, Vec::new);
removed_components.push(entity);
match component_info.storage_type() {
StorageType::Table => {
let table = &mut storages.tables[location.table_id];
let components = table.get_column_mut(component_id).unwrap();
// SAFETY:
// - archetypes only store valid table_rows
// - index is in bounds as promised by caller
// - promote is safe because the caller promises to remove the table row without dropping it immediately afterwards
components
.get_data_unchecked_mut(location.table_row)
.promote()
}
StorageType::SparseSet => storages
.sparse_sets
.get_mut(component_id)
.unwrap()
.remove_and_forget(entity)
.unwrap(),
}
}
#[cfg(test)]

View file

@ -3,7 +3,7 @@ mod spawn_batch;
mod world_cell;
pub use crate::change_detection::{Mut, Ref};
pub use entity_ref::*;
pub use entity_ref::{EntityMut, EntityRef};
pub use spawn_batch::*;
pub use world_cell::*;
@ -12,13 +12,14 @@ use crate::{
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
change_detection::{MutUntyped, TicksMut},
component::{
Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, TickCells,
Component, ComponentDescriptor, ComponentId, ComponentInfo, ComponentTicks, Components,
StorageType, TickCells,
},
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
event::{Event, Events},
ptr::UnsafeCellDeref,
query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery},
storage::{ResourceData, SparseSet, Storages},
storage::{Column, ComponentSparseSet, ResourceData, SparseSet, Storages, TableRow},
system::Resource,
};
use bevy_ptr::{OwningPtr, Ptr};
@ -564,8 +565,10 @@ impl World {
/// ```
#[inline]
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> {
// SAFETY: lifetimes enforce correct usage of returned borrow
unsafe { get_mut(self, entity, self.get_entity(entity)?.location()) }
// SAFETY:
// - lifetimes enforce correct usage of returned borrow
// - entity location is checked in `get_entity`
unsafe { entity_ref::get_mut(self, entity, self.get_entity(entity)?.location()) }
}
/// Despawns the given `entity`, if it exists. This will also remove all of the entity's
@ -1727,8 +1730,7 @@ impl World {
// - component_id is valid as checked by the line above
// - the storage type is accurate as checked by the fetched ComponentInfo
unsafe {
get_component(
self,
self.get_component(
component_id,
info.storage_type(),
entity,
@ -1751,7 +1753,7 @@ impl World {
self.components().get_info(component_id)?;
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
unsafe {
get_mut_by_id(
entity_ref::get_mut_by_id(
self,
entity,
self.get_entity(entity)?.location(),
@ -1761,6 +1763,170 @@ impl World {
}
}
impl World {
/// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] identified by their [`TypeId`]
///
/// # Safety
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
/// - `location` must refer to an archetype that contains `entity`
/// - the caller must ensure that no aliasing rules are violated
#[inline]
pub(crate) unsafe fn get_component_and_ticks_with_type(
&self,
type_id: TypeId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<(Ptr<'_>, TickCells<'_>)> {
let component_id = self.components.get_id(type_id)?;
// SAFETY: component_id is valid, the rest is deferred to caller
self.get_component_and_ticks(component_id, storage_type, entity, location)
}
/// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`]
///
/// # Safety
/// - `location` must refer to an archetype that contains `entity`
/// - `component_id` must be valid
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
/// - the caller must ensure that no aliasing rules are violated
#[inline]
pub(crate) unsafe fn get_component_and_ticks(
&self,
component_id: ComponentId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<(Ptr<'_>, TickCells<'_>)> {
match storage_type {
StorageType::Table => {
let (components, table_row) = self.fetch_table(location, component_id)?;
// SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules
Some((
components.get_data_unchecked(table_row),
TickCells {
added: components.get_added_ticks_unchecked(table_row),
changed: components.get_changed_ticks_unchecked(table_row),
},
))
}
StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get_with_ticks(entity),
}
}
/// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`], identified by the component's type
///
/// # Safety
/// - `location` must refer to an archetype that contains `entity`
/// the archetype
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
/// - the caller must ensure that no aliasing rules are violated
#[inline]
pub(crate) unsafe fn get_component_with_type(
&self,
type_id: TypeId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<Ptr<'_>> {
let component_id = self.components.get_id(type_id)?;
// SAFETY: component_id is valid, the rest is deferred to caller
self.get_component(component_id, storage_type, entity, location)
}
/// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`] in the provided [`World`](crate::world::World).
///
/// # Safety
/// - `location` must refer to an archetype that contains `entity`
/// the archetype
/// - `component_id` must be valid
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
/// - the caller must ensure that no aliasing rules are violated
#[inline]
pub(crate) unsafe fn get_component(
&self,
component_id: ComponentId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<Ptr<'_>> {
// SAFETY: component_id exists and is therefore valid
match storage_type {
StorageType::Table => {
let (components, table_row) = self.fetch_table(location, component_id)?;
// SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules
Some(components.get_data_unchecked(table_row))
}
StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get(entity),
}
}
/// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`], identified by the component's [`TypeId`]
///
/// # Safety
/// - `location` must refer to an archetype that contains `entity`
/// the archetype
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
/// - the caller must ensure that no aliasing rules are violated
#[inline]
pub(crate) unsafe fn get_ticks_with_type(
&self,
type_id: TypeId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<ComponentTicks> {
let component_id = self.components.get_id(type_id)?;
// SAFETY: component_id is valid, the rest is deferred to caller
self.get_ticks(component_id, storage_type, entity, location)
}
/// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`]
///
/// # Safety
/// - `location` must refer to an archetype that contains `entity`
/// the archetype
/// - `component_id` must be valid
/// - `storage_type` must accurately reflect where the components for `component_id` are stored.
/// - the caller must ensure that no aliasing rules are violated
#[inline]
pub(crate) unsafe fn get_ticks(
&self,
component_id: ComponentId,
storage_type: StorageType,
entity: Entity,
location: EntityLocation,
) -> Option<ComponentTicks> {
match storage_type {
StorageType::Table => {
let (components, table_row) = self.fetch_table(location, component_id)?;
// SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules
Some(components.get_ticks_unchecked(table_row))
}
StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get_ticks(entity),
}
}
#[inline]
fn fetch_table(
&self,
location: EntityLocation,
component_id: ComponentId,
) -> Option<(&Column, TableRow)> {
let archetype = &self.archetypes[location.archetype_id];
let table = &self.storages.tables[archetype.table_id()];
let components = table.get_column(component_id)?;
let table_row = archetype.entity_table_row(location.archetype_row);
Some((components, table_row))
}
#[inline]
fn fetch_sparse_set(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> {
self.storages.sparse_sets.get(component_id)
}
}
impl fmt::Debug for World {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("World")