Skip to content

Commit 1fc8f95

Browse files
move take_component to Storages and add some safety comments
1 parent 7af25ca commit 1fc8f95

File tree

2 files changed

+55
-49
lines changed

2 files changed

+55
-49
lines changed

crates/bevy_ecs/src/storage/mod.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ pub use sparse_set::*;
1010
pub use table::*;
1111

1212
use crate::{
13-
archetype::Archetypes,
13+
archetype::{Archetype, Archetypes},
1414
component::{ComponentId, ComponentTicks, Components, StorageType},
1515
entity::{Entity, EntityLocation},
1616
};
17-
use bevy_ptr::Ptr;
17+
use bevy_ptr::{OwningPtr, Ptr};
1818
use std::{any::TypeId, cell::UnsafeCell};
1919

2020
/// The raw data stores of a [World](crate::world::World)
@@ -193,3 +193,49 @@ impl Storages {
193193
}
194194
}
195195
}
196+
197+
impl Storages {
198+
/// Moves component data out of storage.
199+
///
200+
/// This function leaves the underlying memory unchanged, but the component behind
201+
/// returned pointer is semantically owned by the caller and will not be dropped in its original location.
202+
/// Caller is responsible to drop component data behind returned pointer.
203+
///
204+
/// # Safety
205+
/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside the `archetype`
206+
/// - `component_id` must be valid
207+
/// - `components` must come from the same world as `self`
208+
/// - The relevant table row **must be removed** by the caller once all components are taken
209+
#[inline]
210+
pub(crate) unsafe fn take_component<'a>(
211+
&'a mut self,
212+
components: &Components,
213+
archetype: &Archetype,
214+
removed_components: &mut SparseSet<ComponentId, Vec<Entity>>,
215+
component_id: ComponentId,
216+
entity: Entity,
217+
location: EntityLocation,
218+
) -> OwningPtr<'a> {
219+
// SAFETY: component id is valid as per the functions safety requirements
220+
let component_info = components.get_info_unchecked(component_id);
221+
let removed_components = removed_components.get_or_insert_with(component_id, Vec::new);
222+
removed_components.push(entity);
223+
match component_info.storage_type() {
224+
StorageType::Table => {
225+
let table = &mut self.tables[archetype.table_id()];
226+
// SAFETY: archetypes will always point to valid columns
227+
let components = table.get_column_mut(component_id).unwrap();
228+
let table_row = archetype.entity_table_row(location.index);
229+
// SAFETY: archetypes only store valid table_rows and the stored component type is T
230+
// SAFETY: promote is safe because the caller promises to remove the table row without dropping it immediately afterwards
231+
components.get_data_unchecked_mut(table_row).promote()
232+
}
233+
StorageType::SparseSet => self
234+
.sparse_sets
235+
.get_mut(component_id)
236+
.unwrap()
237+
.remove_and_forget(entity)
238+
.unwrap(),
239+
}
240+
}
241+
}

crates/bevy_ecs/src/world/entity_ref.rs

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ use crate::{
44
change_detection::{MutUntyped, Ticks},
55
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
66
entity::{Entities, Entity, EntityLocation},
7-
storage::{SparseSet, Storages},
7+
storage::Storages,
88
world::{Mut, World},
99
};
10-
use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
10+
use bevy_ptr::{Ptr, UnsafeCellDeref};
1111
use std::any::TypeId;
1212

1313
/// A read-only reference to a particular [`Entity`] and all of its components
@@ -389,10 +389,12 @@ impl<'w> EntityMut<'w> {
389389
let result = unsafe {
390390
T::from_components(storages, &mut |storages| {
391391
let component_id = bundle_components.next().unwrap();
392-
// SAFETY: entity location is valid and table row is removed below
393-
take_component(
392+
// SAFETY:
393+
// - entity location is valid
394+
// - table row is removed below
395+
// - `components` comes from the same world as `storages`
396+
storages.take_component(
394397
components,
395-
storages,
396398
old_archetype,
397399
removed_components,
398400
component_id,
@@ -663,48 +665,6 @@ impl<'w> EntityMut<'w> {
663665
}
664666
}
665667

666-
// TODO: move to Storages?
667-
/// Moves component data out of storage.
668-
///
669-
/// This function leaves the underlying memory unchanged, but the component behind
670-
/// returned pointer is semantically owned by the caller and will not be dropped in its original location.
671-
/// Caller is responsible to drop component data behind returned pointer.
672-
///
673-
/// # Safety
674-
/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside the archetype
675-
/// - `component_id` must be valid
676-
/// - The relevant table row **must be removed** by the caller once all components are taken
677-
#[inline]
678-
unsafe fn take_component<'a>(
679-
components: &Components,
680-
storages: &'a mut Storages,
681-
archetype: &Archetype,
682-
removed_components: &mut SparseSet<ComponentId, Vec<Entity>>,
683-
component_id: ComponentId,
684-
entity: Entity,
685-
location: EntityLocation,
686-
) -> OwningPtr<'a> {
687-
let component_info = components.get_info_unchecked(component_id);
688-
let removed_components = removed_components.get_or_insert_with(component_id, Vec::new);
689-
removed_components.push(entity);
690-
match component_info.storage_type() {
691-
StorageType::Table => {
692-
let table = &mut storages.tables[archetype.table_id()];
693-
// SAFETY: archetypes will always point to valid columns
694-
let components = table.get_column_mut(component_id).unwrap();
695-
let table_row = archetype.entity_table_row(location.index);
696-
// SAFETY: archetypes only store valid table_rows and the stored component type is T
697-
components.get_data_unchecked_mut(table_row).promote()
698-
}
699-
StorageType::SparseSet => storages
700-
.sparse_sets
701-
.get_mut(component_id)
702-
.unwrap()
703-
.remove_and_forget(entity)
704-
.unwrap(),
705-
}
706-
}
707-
708668
fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool {
709669
if let Some(component_id) = world.components.get_id(type_id) {
710670
contains_component_with_id(world, component_id, location)

0 commit comments

Comments
 (0)