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}, change_detection::{MutUntyped, TicksMut},
component::{ component::{
Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType,
TickCells,
}, },
entity::{Entities, Entity, EntityLocation}, entity::{Entities, Entity, EntityLocation},
storage::{Column, ComponentSparseSet, SparseSet, Storages}, storage::{SparseSet, Storages},
world::{Mut, World}, world::{Mut, World},
}; };
use bevy_ptr::{OwningPtr, Ptr}; use bevy_ptr::{OwningPtr, Ptr};
@ -72,16 +71,17 @@ impl<'w> EntityRef<'w> {
pub fn get<T: Component>(&self) -> Option<&'w T> { pub fn get<T: Component>(&self) -> Option<&'w T> {
// SAFETY: // SAFETY:
// - entity location and entity is valid // - entity location and entity is valid
// - returned component is of type T
// - the storage type provided is correct for T // - the storage type provided is correct for T
// - world access is immutable, lifetime tied to `&self`
unsafe { unsafe {
get_component_with_type( self.world
self.world, .get_component_with_type(
TypeId::of::<T>(), TypeId::of::<T>(),
T::Storage::STORAGE_TYPE, T::Storage::STORAGE_TYPE,
self.entity, self.entity,
self.location, self.location,
) )
// SAFETY: returned component is of type T
.map(|value| value.deref::<T>()) .map(|value| value.deref::<T>())
} }
} }
@ -92,10 +92,10 @@ impl<'w> EntityRef<'w> {
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> { pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
// SAFETY: // SAFETY:
// - entity location and entity is valid // - entity location and entity is valid
// - world access is immutable, lifetime tied to `&self`
// - the storage type provided is correct for T // - the storage type provided is correct for T
unsafe { unsafe {
get_ticks_with_type( self.world.get_ticks_with_type(
self.world,
TypeId::of::<T>(), TypeId::of::<T>(),
T::Storage::STORAGE_TYPE, T::Storage::STORAGE_TYPE,
self.entity, self.entity,
@ -112,15 +112,13 @@ impl<'w> EntityRef<'w> {
/// compile time.** /// compile time.**
#[inline] #[inline]
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks> { 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)?; 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 { unsafe {
get_ticks( self.world.get_ticks(
self.world,
component_id, component_id,
info.storage_type(), info.storage_type(),
self.entity, self.entity,
@ -149,8 +147,8 @@ impl<'w> EntityRef<'w> {
// - entity location and entity is valid // - entity location and entity is valid
// - returned component is of type T // - returned component is of type T
// - the storage type provided is correct for T // - the storage type provided is correct for T
get_component_and_ticks_with_type( self.world
self.world, .get_component_and_ticks_with_type(
TypeId::of::<T>(), TypeId::of::<T>(),
T::Storage::STORAGE_TYPE, T::Storage::STORAGE_TYPE,
self.entity, self.entity,
@ -179,12 +177,11 @@ impl<'w> EntityRef<'w> {
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> { pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
let info = self.world.components().get_info(component_id)?; let info = self.world.components().get_info(component_id)?;
// SAFETY: // SAFETY:
// - entity_location is valid, // - entity_location and entity are valid
// - component_id is valid as checked by the line above // . component_id is valid as checked by the line above
// - the storage type is accurate as checked by the fetched ComponentInfo // - the storage type is accurate as checked by the fetched ComponentInfo
unsafe { unsafe {
get_component( self.world.get_component(
self.world,
component_id, component_id,
info.storage_type(), info.storage_type(),
self.entity, self.entity,
@ -257,18 +254,18 @@ impl<'w> EntityMut<'w> {
#[inline] #[inline]
pub fn get<T: Component>(&self) -> Option<&'_ T> { pub fn get<T: Component>(&self) -> Option<&'_ T> {
// SAFETY: // SAFETY:
// - lifetimes enforce correct usage of returned borrow // - entity location is valid
// - entity location and entity is valid // - world access is immutable, lifetime tied to `&self`
// - returned component is of type T
// - the storage type provided is correct for T // - the storage type provided is correct for T
unsafe { unsafe {
get_component_with_type( self.world
self.world, .get_component_with_type(
TypeId::of::<T>(), TypeId::of::<T>(),
T::Storage::STORAGE_TYPE, T::Storage::STORAGE_TYPE,
self.entity, self.entity,
self.location, self.location,
) )
// SAFETY: returned component is of type T
.map(|value| value.deref::<T>()) .map(|value| value.deref::<T>())
} }
} }
@ -284,11 +281,11 @@ impl<'w> EntityMut<'w> {
#[inline] #[inline]
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> { pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
// SAFETY: // 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 // - the storage type provided is correct for T
unsafe { unsafe {
get_ticks_with_type( self.world.get_ticks_with_type(
self.world,
TypeId::of::<T>(), TypeId::of::<T>(),
T::Storage::STORAGE_TYPE, T::Storage::STORAGE_TYPE,
self.entity, self.entity,
@ -305,15 +302,13 @@ impl<'w> EntityMut<'w> {
/// compile time.** /// compile time.**
#[inline] #[inline]
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks> { 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)?; 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 { unsafe {
get_ticks( self.world.get_ticks(
self.world,
component_id, component_id,
info.storage_type(), info.storage_type(),
self.entity, self.entity,
@ -338,8 +333,8 @@ impl<'w> EntityMut<'w> {
// - entity location and entity is valid // - entity location and entity is valid
// - returned component is of type T // - returned component is of type T
// - the storage type provided is correct for T // - the storage type provided is correct for T
get_component_and_ticks_with_type( self.world
self.world, .get_component_and_ticks_with_type(
TypeId::of::<T>(), TypeId::of::<T>(),
T::Storage::STORAGE_TYPE, T::Storage::STORAGE_TYPE,
self.entity, self.entity,
@ -417,10 +412,13 @@ impl<'w> EntityMut<'w> {
let result = unsafe { let result = unsafe {
T::from_components(storages, &mut |storages| { T::from_components(storages, &mut |storages| {
let component_id = bundle_components.next().unwrap(); 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( take_component(
components,
storages, storages,
components,
removed_components, removed_components,
component_id, component_id,
entity, entity,
@ -671,8 +669,7 @@ impl<'w> EntityMut<'w> {
// - component_id is valid as checked by the line above // - component_id is valid as checked by the line above
// - the storage type is accurate as checked by the fetched ComponentInfo // - the storage type is accurate as checked by the fetched ComponentInfo
unsafe { unsafe {
get_component( self.world.get_component(
self.world,
component_id, component_id,
info.storage_type(), info.storage_type(),
self.entity, 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 { fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool {
if let Some(component_id) = world.components.get_id(type_id) { if let Some(component_id) = world.components.get_id(type_id) {
contains_component_with_id(world, component_id, location) contains_component_with_id(world, component_id, location)
@ -1044,18 +834,23 @@ pub(crate) unsafe fn get_mut<T: Component>(
entity: Entity, entity: Entity,
location: EntityLocation, location: EntityLocation,
) -> Option<Mut<'_, T>> { ) -> 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 change_tick = world.change_tick();
let last_change_tick = world.last_change_tick(); let last_change_tick = world.last_change_tick();
get_component_and_ticks_with_type( // SAFETY:
world, // - 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>(), TypeId::of::<T>(),
T::Storage::STORAGE_TYPE, T::Storage::STORAGE_TYPE,
entity, entity,
location, location,
) )
.map(|(value, ticks)| Mut { .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>(), value: value.assert_unique().deref_mut::<T>(),
ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick),
}) })
@ -1068,16 +863,66 @@ pub(crate) unsafe fn get_mut_by_id(
entity: Entity, entity: Entity,
location: EntityLocation, location: EntityLocation,
component_id: ComponentId, component_id: ComponentId,
) -> Option<MutUntyped> { ) -> Option<MutUntyped<'_>> {
let change_tick = world.change_tick(); let change_tick = world.change_tick();
// SAFETY: component_id is valid
let info = world.components.get_info_unchecked(component_id); let info = world.components.get_info_unchecked(component_id);
// SAFETY: world access is unique, entity location and component_id required to be valid // SAFETY:
get_component_and_ticks(world, component_id, info.storage_type(), entity, location).map( // - world access is unique
|(value, ticks)| MutUntyped { // - 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(), value: value.assert_unique(),
ticks: TicksMut::from_tick_cells(ticks, world.last_change_tick(), change_tick), 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)] #[cfg(test)]

View File

@ -3,7 +3,7 @@ mod spawn_batch;
mod world_cell; mod world_cell;
pub use crate::change_detection::{Mut, Ref}; pub use crate::change_detection::{Mut, Ref};
pub use entity_ref::*; pub use entity_ref::{EntityMut, EntityRef};
pub use spawn_batch::*; pub use spawn_batch::*;
pub use world_cell::*; pub use world_cell::*;
@ -12,13 +12,14 @@ use crate::{
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles}, bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
change_detection::{MutUntyped, TicksMut}, change_detection::{MutUntyped, TicksMut},
component::{ component::{
Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, TickCells, Component, ComponentDescriptor, ComponentId, ComponentInfo, ComponentTicks, Components,
StorageType, TickCells,
}, },
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation}, entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
event::{Event, Events}, event::{Event, Events},
ptr::UnsafeCellDeref, ptr::UnsafeCellDeref,
query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery}, query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery},
storage::{ResourceData, SparseSet, Storages}, storage::{Column, ComponentSparseSet, ResourceData, SparseSet, Storages, TableRow},
system::Resource, system::Resource,
}; };
use bevy_ptr::{OwningPtr, Ptr}; use bevy_ptr::{OwningPtr, Ptr};
@ -564,8 +565,10 @@ impl World {
/// ``` /// ```
#[inline] #[inline]
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> { pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> {
// SAFETY: lifetimes enforce correct usage of returned borrow // SAFETY:
unsafe { get_mut(self, entity, self.get_entity(entity)?.location()) } // - 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 /// 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 // - component_id is valid as checked by the line above
// - the storage type is accurate as checked by the fetched ComponentInfo // - the storage type is accurate as checked by the fetched ComponentInfo
unsafe { unsafe {
get_component( self.get_component(
self,
component_id, component_id,
info.storage_type(), info.storage_type(),
entity, entity,
@ -1751,7 +1753,7 @@ impl World {
self.components().get_info(component_id)?; self.components().get_info(component_id)?;
// SAFETY: entity_location is valid, component_id is valid as checked by the line above // SAFETY: entity_location is valid, component_id is valid as checked by the line above
unsafe { unsafe {
get_mut_by_id( entity_ref::get_mut_by_id(
self, self,
entity, entity,
self.get_entity(entity)?.location(), 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 { impl fmt::Debug for World {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("World") f.debug_struct("World")