Reduced TableRow
as
Casting (#10811)
# Objective - Fixes #10806 ## Solution Replaced `new` and `index` methods for both `TableRow` and `TableId` with `from_*` and `as_*` methods. These remove the need to perform casting at call sites, reducing the total number of casts in the Bevy codebase. Within these methods, an appropriate `debug_assertion` ensures the cast will behave in an expected manner (no wrapping, etc.). I am using a `debug_assertion` instead of an `assert` to reduce any possible runtime overhead, however minimal. This choice is something I am open to changing (or leaving up to another PR) if anyone has any strong arguments for it. --- ## Changelog - `ComponentSparseSet::sparse` stores a `TableRow` instead of a `u32` (private change) - Replaced `TableRow::new` and `TableRow::index` methods with `TableRow::from_*` and `TableRow::as_*`, with `debug_assertions` protecting any internal casting. - Replaced `TableId::new` and `TableId::index` methods with `TableId::from_*` and `TableId::as_*`, with `debug_assertions` protecting any internal casting. - All `TableId` methods are now `const` ## Migration Guide - `TableRow::new` -> `TableRow::from_usize` - `TableRow::index` -> `TableRow::as_usize` - `TableId::new` -> `TableId::from_usize` - `TableId::index` -> `TableId::as_usize` --- ## Notes I have chosen to remove the `index` and `new` methods for the following chain of reasoning: - Across the codebase, `new` was called with a mixture of `u32` and `usize` values. Likewise for `index`. - Choosing `new` to either be `usize` or `u32` would break half of these call-sites, requiring `as` casting at the site. - Adding a second method `new_u32` or `new_usize` avoids the above, bu looks visually inconsistent. - Therefore, they should be replaced with `from_*` and `as_*` methods instead. Worth noting is that by updating `ComponentSparseSet`, there are now zero instances of interacting with the inner value of `TableRow` as a `u32`, it is exclusively used as a `usize` value (due to interactions with methods like `len` and slice indexing). I have left the `as_u32` and `from_u32` methods as the "proper" constructors/getters.
This commit is contained in:
parent
83ee6de1da
commit
72adf2ae2a
@ -605,7 +605,7 @@ unsafe impl<T: Component> WorldQuery for &T {
|
||||
StorageType::Table => fetch
|
||||
.table_components
|
||||
.debug_checked_unwrap()
|
||||
.get(table_row.index())
|
||||
.get(table_row.as_usize())
|
||||
.deref(),
|
||||
StorageType::SparseSet => fetch
|
||||
.sparse_set
|
||||
@ -760,10 +760,10 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
|
||||
let (table_components, added_ticks, changed_ticks) =
|
||||
fetch.table_data.debug_checked_unwrap();
|
||||
Ref {
|
||||
value: table_components.get(table_row.index()).deref(),
|
||||
value: table_components.get(table_row.as_usize()).deref(),
|
||||
ticks: Ticks {
|
||||
added: added_ticks.get(table_row.index()).deref(),
|
||||
changed: changed_ticks.get(table_row.index()).deref(),
|
||||
added: added_ticks.get(table_row.as_usize()).deref(),
|
||||
changed: changed_ticks.get(table_row.as_usize()).deref(),
|
||||
this_run: fetch.this_run,
|
||||
last_run: fetch.last_run,
|
||||
},
|
||||
@ -927,10 +927,10 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
|
||||
let (table_components, added_ticks, changed_ticks) =
|
||||
fetch.table_data.debug_checked_unwrap();
|
||||
Mut {
|
||||
value: table_components.get(table_row.index()).deref_mut(),
|
||||
value: table_components.get(table_row.as_usize()).deref_mut(),
|
||||
ticks: TicksMut {
|
||||
added: added_ticks.get(table_row.index()).deref_mut(),
|
||||
changed: changed_ticks.get(table_row.index()).deref_mut(),
|
||||
added: added_ticks.get(table_row.as_usize()).deref_mut(),
|
||||
changed: changed_ticks.get(table_row.as_usize()).deref_mut(),
|
||||
this_run: fetch.this_run,
|
||||
last_run: fetch.last_run,
|
||||
},
|
||||
|
@ -626,7 +626,7 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
|
||||
StorageType::Table => fetch
|
||||
.table_ticks
|
||||
.debug_checked_unwrap()
|
||||
.get(table_row.index())
|
||||
.get(table_row.as_usize())
|
||||
.deref()
|
||||
.is_newer_than(fetch.last_run, fetch.this_run),
|
||||
StorageType::SparseSet => {
|
||||
@ -802,7 +802,7 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
|
||||
StorageType::Table => fetch
|
||||
.table_ticks
|
||||
.debug_checked_unwrap()
|
||||
.get(table_row.index())
|
||||
.get(table_row.as_usize())
|
||||
.deref()
|
||||
.is_newer_than(fetch.last_run, fetch.this_run),
|
||||
StorageType::SparseSet => {
|
||||
|
@ -106,6 +106,11 @@ impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> QueryIter<'w, 's, Q, F> {
|
||||
where
|
||||
Func: FnMut(B, Q::Item<'w>) -> B,
|
||||
{
|
||||
assert!(
|
||||
rows.end <= u32::MAX as usize,
|
||||
"TableRow is only valid up to u32::MAX"
|
||||
);
|
||||
|
||||
Q::set_table(&mut self.cursor.fetch, &self.query_state.fetch_state, table);
|
||||
F::set_table(
|
||||
&mut self.cursor.filter,
|
||||
@ -117,7 +122,7 @@ impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> QueryIter<'w, 's, Q, F> {
|
||||
for row in rows {
|
||||
// SAFETY: Caller assures `row` in range of the current archetype.
|
||||
let entity = entities.get_unchecked(row);
|
||||
let row = TableRow::new(row);
|
||||
let row = TableRow::from_usize(row);
|
||||
// SAFETY: set_table was called prior.
|
||||
// Caller assures `row` in range of the current archetype.
|
||||
if !F::filter_fetch(&mut self.cursor.filter, *entity, row) {
|
||||
@ -707,7 +712,11 @@ impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> QueryIterationCursor<'w, 's
|
||||
let index = self.current_row - 1;
|
||||
if Self::IS_DENSE {
|
||||
let entity = self.table_entities.get_unchecked(index);
|
||||
Some(Q::fetch(&mut self.fetch, *entity, TableRow::new(index)))
|
||||
Some(Q::fetch(
|
||||
&mut self.fetch,
|
||||
*entity,
|
||||
TableRow::from_usize(index),
|
||||
))
|
||||
} else {
|
||||
let archetype_entity = self.archetype_entities.get_unchecked(index);
|
||||
Some(Q::fetch(
|
||||
@ -768,7 +777,7 @@ impl<'w, 's, Q: WorldQueryData, F: WorldQueryFilter> QueryIterationCursor<'w, 's
|
||||
// SAFETY: set_table was called prior.
|
||||
// `current_row` is a table row in range of the current table, because if it was not, then the if above would have been executed.
|
||||
let entity = self.table_entities.get_unchecked(self.current_row);
|
||||
let row = TableRow::new(self.current_row);
|
||||
let row = TableRow::from_usize(self.current_row);
|
||||
if !F::filter_fetch(&mut self.filter, *entity, row) {
|
||||
self.current_row += 1;
|
||||
continue;
|
||||
|
@ -267,7 +267,7 @@ impl<Q: WorldQueryData, F: WorldQueryFilter> QueryState<Q, F> {
|
||||
self.matched_archetypes.set(archetype_index, true);
|
||||
self.matched_archetype_ids.push(archetype.id());
|
||||
}
|
||||
let table_index = archetype.table_id().index();
|
||||
let table_index = archetype.table_id().as_usize();
|
||||
if !self.matched_tables.contains(table_index) {
|
||||
self.matched_tables.grow(table_index + 1);
|
||||
self.matched_tables.set(table_index, true);
|
||||
|
@ -40,7 +40,7 @@ impl<const SEND: bool> Drop for ResourceData<SEND> {
|
||||
|
||||
impl<const SEND: bool> ResourceData<SEND> {
|
||||
/// The only row in the underlying column.
|
||||
const ROW: TableRow = TableRow::new(0);
|
||||
const ROW: TableRow = TableRow::from_u32(0);
|
||||
|
||||
/// Validates the access to `!Send` resources is only done on the thread they were created from.
|
||||
///
|
||||
|
@ -124,7 +124,7 @@ pub struct ComponentSparseSet {
|
||||
entities: Vec<EntityIndex>,
|
||||
#[cfg(debug_assertions)]
|
||||
entities: Vec<Entity>,
|
||||
sparse: SparseArray<EntityIndex, u32>,
|
||||
sparse: SparseArray<EntityIndex, TableRow>,
|
||||
}
|
||||
|
||||
impl ComponentSparseSet {
|
||||
@ -171,13 +171,13 @@ impl ComponentSparseSet {
|
||||
) {
|
||||
if let Some(&dense_index) = self.sparse.get(entity.index()) {
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(entity, self.entities[dense_index as usize]);
|
||||
self.dense
|
||||
.replace(TableRow::new(dense_index as usize), value, change_tick);
|
||||
assert_eq!(entity, self.entities[dense_index.as_usize()]);
|
||||
self.dense.replace(dense_index, value, change_tick);
|
||||
} else {
|
||||
let dense_index = self.dense.len();
|
||||
self.dense.push(value, ComponentTicks::new(change_tick));
|
||||
self.sparse.insert(entity.index(), dense_index as u32);
|
||||
self.sparse
|
||||
.insert(entity.index(), TableRow::from_usize(dense_index));
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(self.entities.len(), dense_index);
|
||||
#[cfg(not(debug_assertions))]
|
||||
@ -194,7 +194,7 @@ impl ComponentSparseSet {
|
||||
{
|
||||
if let Some(&dense_index) = self.sparse.get(entity.index()) {
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(entity, self.entities[dense_index as usize]);
|
||||
assert_eq!(entity, self.entities[dense_index.as_usize()]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
@ -209,12 +209,11 @@ impl ComponentSparseSet {
|
||||
/// Returns `None` if `entity` does not have a component in the sparse set.
|
||||
#[inline]
|
||||
pub fn get(&self, entity: Entity) -> Option<Ptr<'_>> {
|
||||
self.sparse.get(entity.index()).map(|dense_index| {
|
||||
let dense_index = (*dense_index) as usize;
|
||||
self.sparse.get(entity.index()).map(|&dense_index| {
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(entity, self.entities[dense_index]);
|
||||
assert_eq!(entity, self.entities[dense_index.as_usize()]);
|
||||
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||
unsafe { self.dense.get_data_unchecked(TableRow::new(dense_index)) }
|
||||
unsafe { self.dense.get_data_unchecked(dense_index) }
|
||||
})
|
||||
}
|
||||
|
||||
@ -223,9 +222,9 @@ impl ComponentSparseSet {
|
||||
/// Returns `None` if `entity` does not have a component in the sparse set.
|
||||
#[inline]
|
||||
pub fn get_with_ticks(&self, entity: Entity) -> Option<(Ptr<'_>, TickCells<'_>)> {
|
||||
let dense_index = TableRow::new(*self.sparse.get(entity.index())? as usize);
|
||||
let dense_index = *self.sparse.get(entity.index())?;
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(entity, self.entities[dense_index.index()]);
|
||||
assert_eq!(entity, self.entities[dense_index.as_usize()]);
|
||||
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||
unsafe {
|
||||
Some((
|
||||
@ -243,16 +242,11 @@ impl ComponentSparseSet {
|
||||
/// Returns `None` if `entity` does not have a component in the sparse set.
|
||||
#[inline]
|
||||
pub fn get_added_tick(&self, entity: Entity) -> Option<&UnsafeCell<Tick>> {
|
||||
let dense_index = *self.sparse.get(entity.index())? as usize;
|
||||
let dense_index = *self.sparse.get(entity.index())?;
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(entity, self.entities[dense_index]);
|
||||
assert_eq!(entity, self.entities[dense_index.as_usize()]);
|
||||
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||
unsafe {
|
||||
Some(
|
||||
self.dense
|
||||
.get_added_tick_unchecked(TableRow::new(dense_index)),
|
||||
)
|
||||
}
|
||||
unsafe { Some(self.dense.get_added_tick_unchecked(dense_index)) }
|
||||
}
|
||||
|
||||
/// Returns a reference to the "changed" tick of the entity's component value.
|
||||
@ -260,16 +254,11 @@ impl ComponentSparseSet {
|
||||
/// Returns `None` if `entity` does not have a component in the sparse set.
|
||||
#[inline]
|
||||
pub fn get_changed_tick(&self, entity: Entity) -> Option<&UnsafeCell<Tick>> {
|
||||
let dense_index = *self.sparse.get(entity.index())? as usize;
|
||||
let dense_index = *self.sparse.get(entity.index())?;
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(entity, self.entities[dense_index]);
|
||||
assert_eq!(entity, self.entities[dense_index.as_usize()]);
|
||||
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||
unsafe {
|
||||
Some(
|
||||
self.dense
|
||||
.get_changed_tick_unchecked(TableRow::new(dense_index)),
|
||||
)
|
||||
}
|
||||
unsafe { Some(self.dense.get_changed_tick_unchecked(dense_index)) }
|
||||
}
|
||||
|
||||
/// Returns a reference to the "added" and "changed" ticks of the entity's component value.
|
||||
@ -277,11 +266,11 @@ impl ComponentSparseSet {
|
||||
/// Returns `None` if `entity` does not have a component in the sparse set.
|
||||
#[inline]
|
||||
pub fn get_ticks(&self, entity: Entity) -> Option<ComponentTicks> {
|
||||
let dense_index = *self.sparse.get(entity.index())? as usize;
|
||||
let dense_index = *self.sparse.get(entity.index())?;
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(entity, self.entities[dense_index]);
|
||||
assert_eq!(entity, self.entities[dense_index.as_usize()]);
|
||||
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||
unsafe { Some(self.dense.get_ticks_unchecked(TableRow::new(dense_index))) }
|
||||
unsafe { Some(self.dense.get_ticks_unchecked(dense_index)) }
|
||||
}
|
||||
|
||||
/// Removes the `entity` from this sparse set and returns a pointer to the associated value (if
|
||||
@ -289,23 +278,19 @@ impl ComponentSparseSet {
|
||||
#[must_use = "The returned pointer must be used to drop the removed component."]
|
||||
pub(crate) fn remove_and_forget(&mut self, entity: Entity) -> Option<OwningPtr<'_>> {
|
||||
self.sparse.remove(entity.index()).map(|dense_index| {
|
||||
let dense_index = dense_index as usize;
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(entity, self.entities[dense_index]);
|
||||
self.entities.swap_remove(dense_index);
|
||||
let is_last = dense_index == self.dense.len() - 1;
|
||||
assert_eq!(entity, self.entities[dense_index.as_usize()]);
|
||||
self.entities.swap_remove(dense_index.as_usize());
|
||||
let is_last = dense_index.as_usize() == self.dense.len() - 1;
|
||||
// SAFETY: dense_index was just removed from `sparse`, which ensures that it is valid
|
||||
let (value, _) = unsafe {
|
||||
self.dense
|
||||
.swap_remove_and_forget_unchecked(TableRow::new(dense_index))
|
||||
};
|
||||
let (value, _) = unsafe { self.dense.swap_remove_and_forget_unchecked(dense_index) };
|
||||
if !is_last {
|
||||
let swapped_entity = self.entities[dense_index];
|
||||
let swapped_entity = self.entities[dense_index.as_usize()];
|
||||
#[cfg(not(debug_assertions))]
|
||||
let index = swapped_entity;
|
||||
#[cfg(debug_assertions)]
|
||||
let index = swapped_entity.index();
|
||||
*self.sparse.get_mut(index).unwrap() = dense_index as u32;
|
||||
*self.sparse.get_mut(index).unwrap() = dense_index;
|
||||
}
|
||||
value
|
||||
})
|
||||
@ -316,20 +301,21 @@ impl ComponentSparseSet {
|
||||
/// Returns `true` if `entity` had a component value in the sparse set.
|
||||
pub(crate) fn remove(&mut self, entity: Entity) -> bool {
|
||||
if let Some(dense_index) = self.sparse.remove(entity.index()) {
|
||||
let dense_index = dense_index as usize;
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(entity, self.entities[dense_index]);
|
||||
self.entities.swap_remove(dense_index);
|
||||
let is_last = dense_index == self.dense.len() - 1;
|
||||
assert_eq!(entity, self.entities[dense_index.as_usize()]);
|
||||
self.entities.swap_remove(dense_index.as_usize());
|
||||
let is_last = dense_index.as_usize() == self.dense.len() - 1;
|
||||
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||
unsafe { self.dense.swap_remove_unchecked(TableRow::new(dense_index)) }
|
||||
unsafe {
|
||||
self.dense.swap_remove_unchecked(dense_index);
|
||||
}
|
||||
if !is_last {
|
||||
let swapped_entity = self.entities[dense_index];
|
||||
let swapped_entity = self.entities[dense_index.as_usize()];
|
||||
#[cfg(not(debug_assertions))]
|
||||
let index = swapped_entity;
|
||||
#[cfg(debug_assertions)]
|
||||
let index = swapped_entity.index();
|
||||
*self.sparse.get_mut(index).unwrap() = dense_index as u32;
|
||||
*self.sparse.get_mut(index).unwrap() = dense_index;
|
||||
}
|
||||
true
|
||||
} else {
|
||||
|
@ -35,25 +35,48 @@ impl TableId {
|
||||
|
||||
/// Creates a new [`TableId`].
|
||||
///
|
||||
/// `index` *must* be retrieved from calling [`TableId::index`] on a `TableId` you got
|
||||
/// `index` *must* be retrieved from calling [`TableId::as_u32`] on a `TableId` you got
|
||||
/// from a table of a given [`World`] or the created ID may be invalid.
|
||||
///
|
||||
/// [`World`]: crate::world::World
|
||||
#[inline]
|
||||
pub fn new(index: usize) -> Self {
|
||||
TableId(index as u32)
|
||||
pub const fn from_u32(index: u32) -> Self {
|
||||
Self(index)
|
||||
}
|
||||
|
||||
/// Creates a new [`TableId`].
|
||||
///
|
||||
/// `index` *must* be retrieved from calling [`TableId::as_usize`] on a `TableId` you got
|
||||
/// from a table of a given [`World`] or the created ID may be invalid.
|
||||
///
|
||||
/// [`World`]: crate::world::World
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if the provided value does not fit within a [`u32`].
|
||||
#[inline]
|
||||
pub const fn from_usize(index: usize) -> Self {
|
||||
assert!(index as u32 as usize == index);
|
||||
Self(index as u32)
|
||||
}
|
||||
|
||||
/// Gets the underlying table index from the ID.
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
pub const fn as_u32(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Gets the underlying table index from the ID.
|
||||
#[inline]
|
||||
pub const fn as_usize(self) -> usize {
|
||||
// usize is at least u32 in Bevy
|
||||
self.0 as usize
|
||||
}
|
||||
|
||||
/// The [`TableId`] of the [`Table`] without any components.
|
||||
#[inline]
|
||||
pub const fn empty() -> TableId {
|
||||
TableId(0)
|
||||
pub const fn empty() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,15 +105,33 @@ impl TableRow {
|
||||
|
||||
/// Creates a `TableRow`.
|
||||
#[inline]
|
||||
pub const fn new(index: usize) -> Self {
|
||||
pub const fn from_u32(index: u32) -> Self {
|
||||
Self(index)
|
||||
}
|
||||
|
||||
/// Creates a `TableRow` from a [`usize`] index.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if the provided value does not fit within a [`u32`].
|
||||
#[inline]
|
||||
pub const fn from_usize(index: usize) -> Self {
|
||||
assert!(index as u32 as usize == index);
|
||||
Self(index as u32)
|
||||
}
|
||||
|
||||
/// Gets the index of the row.
|
||||
/// Gets the index of the row as a [`usize`].
|
||||
#[inline]
|
||||
pub const fn index(self) -> usize {
|
||||
pub const fn as_usize(self) -> usize {
|
||||
// usize is at least u32 in Bevy
|
||||
self.0 as usize
|
||||
}
|
||||
|
||||
/// Gets the index of the row as a [`usize`].
|
||||
#[inline]
|
||||
pub const fn as_u32(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-erased contiguous container for data of a homogeneous type.
|
||||
@ -139,10 +180,13 @@ impl Column {
|
||||
/// Assumes data has already been allocated for the given row.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn initialize(&mut self, row: TableRow, data: OwningPtr<'_>, tick: Tick) {
|
||||
debug_assert!(row.index() < self.len());
|
||||
self.data.initialize_unchecked(row.index(), data);
|
||||
*self.added_ticks.get_unchecked_mut(row.index()).get_mut() = tick;
|
||||
*self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = tick;
|
||||
debug_assert!(row.as_usize() < self.len());
|
||||
self.data.initialize_unchecked(row.as_usize(), data);
|
||||
*self.added_ticks.get_unchecked_mut(row.as_usize()).get_mut() = tick;
|
||||
*self
|
||||
.changed_ticks
|
||||
.get_unchecked_mut(row.as_usize())
|
||||
.get_mut() = tick;
|
||||
}
|
||||
|
||||
/// Writes component data to the column at given row.
|
||||
@ -152,9 +196,12 @@ impl Column {
|
||||
/// Assumes data has already been allocated for the given row.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn replace(&mut self, row: TableRow, data: OwningPtr<'_>, change_tick: Tick) {
|
||||
debug_assert!(row.index() < self.len());
|
||||
self.data.replace_unchecked(row.index(), data);
|
||||
*self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = change_tick;
|
||||
debug_assert!(row.as_usize() < self.len());
|
||||
self.data.replace_unchecked(row.as_usize(), data);
|
||||
*self
|
||||
.changed_ticks
|
||||
.get_unchecked_mut(row.as_usize())
|
||||
.get_mut() = change_tick;
|
||||
}
|
||||
|
||||
/// Writes component data to the column at given row.
|
||||
@ -165,8 +212,8 @@ impl Column {
|
||||
/// Assumes data has already been allocated for the given row.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn replace_untracked(&mut self, row: TableRow, data: OwningPtr<'_>) {
|
||||
debug_assert!(row.index() < self.len());
|
||||
self.data.replace_unchecked(row.index(), data);
|
||||
debug_assert!(row.as_usize() < self.len());
|
||||
self.data.replace_unchecked(row.as_usize(), data);
|
||||
}
|
||||
|
||||
/// Gets the current number of elements stored in the column.
|
||||
@ -193,9 +240,9 @@ impl Column {
|
||||
///
|
||||
#[inline]
|
||||
pub(crate) unsafe fn swap_remove_unchecked(&mut self, row: TableRow) {
|
||||
self.data.swap_remove_and_drop_unchecked(row.index());
|
||||
self.added_ticks.swap_remove(row.index());
|
||||
self.changed_ticks.swap_remove(row.index());
|
||||
self.data.swap_remove_and_drop_unchecked(row.as_usize());
|
||||
self.added_ticks.swap_remove(row.as_usize());
|
||||
self.changed_ticks.swap_remove(row.as_usize());
|
||||
}
|
||||
|
||||
/// Removes an element from the [`Column`] and returns it and its change detection ticks.
|
||||
@ -214,11 +261,11 @@ impl Column {
|
||||
&mut self,
|
||||
row: TableRow,
|
||||
) -> Option<(OwningPtr<'_>, ComponentTicks)> {
|
||||
(row.index() < self.data.len()).then(|| {
|
||||
(row.as_usize() < self.data.len()).then(|| {
|
||||
// SAFETY: The row was length checked before this.
|
||||
let data = unsafe { self.data.swap_remove_and_forget_unchecked(row.index()) };
|
||||
let added = self.added_ticks.swap_remove(row.index()).into_inner();
|
||||
let changed = self.changed_ticks.swap_remove(row.index()).into_inner();
|
||||
let data = unsafe { self.data.swap_remove_and_forget_unchecked(row.as_usize()) };
|
||||
let added = self.added_ticks.swap_remove(row.as_usize()).into_inner();
|
||||
let changed = self.changed_ticks.swap_remove(row.as_usize()).into_inner();
|
||||
(data, ComponentTicks { added, changed })
|
||||
})
|
||||
}
|
||||
@ -241,9 +288,9 @@ impl Column {
|
||||
&mut self,
|
||||
row: TableRow,
|
||||
) -> (OwningPtr<'_>, ComponentTicks) {
|
||||
let data = self.data.swap_remove_and_forget_unchecked(row.index());
|
||||
let added = self.added_ticks.swap_remove(row.index()).into_inner();
|
||||
let changed = self.changed_ticks.swap_remove(row.index()).into_inner();
|
||||
let data = self.data.swap_remove_and_forget_unchecked(row.as_usize());
|
||||
let added = self.added_ticks.swap_remove(row.as_usize()).into_inner();
|
||||
let changed = self.changed_ticks.swap_remove(row.as_usize()).into_inner();
|
||||
(data, ComponentTicks { added, changed })
|
||||
}
|
||||
|
||||
@ -266,12 +313,12 @@ impl Column {
|
||||
dst_row: TableRow,
|
||||
) {
|
||||
debug_assert!(self.data.layout() == other.data.layout());
|
||||
let ptr = self.data.get_unchecked_mut(dst_row.index());
|
||||
other.data.swap_remove_unchecked(src_row.index(), ptr);
|
||||
*self.added_ticks.get_unchecked_mut(dst_row.index()) =
|
||||
other.added_ticks.swap_remove(src_row.index());
|
||||
*self.changed_ticks.get_unchecked_mut(dst_row.index()) =
|
||||
other.changed_ticks.swap_remove(src_row.index());
|
||||
let ptr = self.data.get_unchecked_mut(dst_row.as_usize());
|
||||
other.data.swap_remove_unchecked(src_row.as_usize(), ptr);
|
||||
*self.added_ticks.get_unchecked_mut(dst_row.as_usize()) =
|
||||
other.added_ticks.swap_remove(src_row.as_usize());
|
||||
*self.changed_ticks.get_unchecked_mut(dst_row.as_usize()) =
|
||||
other.changed_ticks.swap_remove(src_row.as_usize());
|
||||
}
|
||||
|
||||
/// Pushes a new value onto the end of the [`Column`].
|
||||
@ -338,15 +385,15 @@ impl Column {
|
||||
/// Returns `None` if `row` is out of bounds.
|
||||
#[inline]
|
||||
pub fn get(&self, row: TableRow) -> Option<(Ptr<'_>, TickCells<'_>)> {
|
||||
(row.index() < self.data.len())
|
||||
(row.as_usize() < self.data.len())
|
||||
// SAFETY: The row is length checked before fetching the pointer. This is being
|
||||
// accessed through a read-only reference to the column.
|
||||
.then(|| unsafe {
|
||||
(
|
||||
self.data.get_unchecked(row.index()),
|
||||
self.data.get_unchecked(row.as_usize()),
|
||||
TickCells {
|
||||
added: self.added_ticks.get_unchecked(row.index()),
|
||||
changed: self.changed_ticks.get_unchecked(row.index()),
|
||||
added: self.added_ticks.get_unchecked(row.as_usize()),
|
||||
changed: self.changed_ticks.get_unchecked(row.as_usize()),
|
||||
},
|
||||
)
|
||||
})
|
||||
@ -357,9 +404,11 @@ impl Column {
|
||||
/// Returns `None` if `row` is out of bounds.
|
||||
#[inline]
|
||||
pub fn get_data(&self, row: TableRow) -> Option<Ptr<'_>> {
|
||||
// SAFETY: The row is length checked before fetching the pointer. This is being
|
||||
// accessed through a read-only reference to the column.
|
||||
(row.index() < self.data.len()).then(|| unsafe { self.data.get_unchecked(row.index()) })
|
||||
(row.as_usize() < self.data.len()).then(|| {
|
||||
// SAFETY: The row is length checked before fetching the pointer. This is being
|
||||
// accessed through a read-only reference to the column.
|
||||
unsafe { self.data.get_unchecked(row.as_usize()) }
|
||||
})
|
||||
}
|
||||
|
||||
/// Fetches a read-only reference to the data at `row`. Unlike [`Column::get`] this does not
|
||||
@ -370,8 +419,8 @@ impl Column {
|
||||
/// - no other mutable reference to the data of the same row can exist at the same time
|
||||
#[inline]
|
||||
pub unsafe fn get_data_unchecked(&self, row: TableRow) -> Ptr<'_> {
|
||||
debug_assert!(row.index() < self.data.len());
|
||||
self.data.get_unchecked(row.index())
|
||||
debug_assert!(row.as_usize() < self.data.len());
|
||||
self.data.get_unchecked(row.as_usize())
|
||||
}
|
||||
|
||||
/// Fetches a mutable reference to the data at `row`.
|
||||
@ -379,9 +428,11 @@ impl Column {
|
||||
/// Returns `None` if `row` is out of bounds.
|
||||
#[inline]
|
||||
pub fn get_data_mut(&mut self, row: TableRow) -> Option<PtrMut<'_>> {
|
||||
// SAFETY: The row is length checked before fetching the pointer. This is being
|
||||
// accessed through an exclusive reference to the column.
|
||||
(row.index() < self.data.len()).then(|| unsafe { self.data.get_unchecked_mut(row.index()) })
|
||||
(row.as_usize() < self.data.len()).then(|| {
|
||||
// SAFETY: The row is length checked before fetching the pointer. This is being
|
||||
// accessed through an exclusive reference to the column.
|
||||
unsafe { self.data.get_unchecked_mut(row.as_usize()) }
|
||||
})
|
||||
}
|
||||
|
||||
/// Fetches a mutable reference to the data at `row`. Unlike [`Column::get_data_mut`] this does not
|
||||
@ -392,8 +443,8 @@ impl Column {
|
||||
/// - no other reference to the data of the same row can exist at the same time
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get_data_unchecked_mut(&mut self, row: TableRow) -> PtrMut<'_> {
|
||||
debug_assert!(row.index() < self.data.len());
|
||||
self.data.get_unchecked_mut(row.index())
|
||||
debug_assert!(row.as_usize() < self.data.len());
|
||||
self.data.get_unchecked_mut(row.as_usize())
|
||||
}
|
||||
|
||||
/// Fetches the "added" change detection tick for the value at `row`.
|
||||
@ -405,7 +456,7 @@ impl Column {
|
||||
/// adhere to the safety invariants of [`UnsafeCell`].
|
||||
#[inline]
|
||||
pub fn get_added_tick(&self, row: TableRow) -> Option<&UnsafeCell<Tick>> {
|
||||
self.added_ticks.get(row.index())
|
||||
self.added_ticks.get(row.as_usize())
|
||||
}
|
||||
|
||||
/// Fetches the "changed" change detection tick for the value at `row`.
|
||||
@ -417,7 +468,7 @@ impl Column {
|
||||
/// adhere to the safety invariants of [`UnsafeCell`].
|
||||
#[inline]
|
||||
pub fn get_changed_tick(&self, row: TableRow) -> Option<&UnsafeCell<Tick>> {
|
||||
self.changed_ticks.get(row.index())
|
||||
self.changed_ticks.get(row.as_usize())
|
||||
}
|
||||
|
||||
/// Fetches the change detection ticks for the value at `row`.
|
||||
@ -425,7 +476,7 @@ impl Column {
|
||||
/// Returns `None` if `row` is out of bounds.
|
||||
#[inline]
|
||||
pub fn get_ticks(&self, row: TableRow) -> Option<ComponentTicks> {
|
||||
if row.index() < self.data.len() {
|
||||
if row.as_usize() < self.data.len() {
|
||||
// SAFETY: The size of the column has already been checked.
|
||||
Some(unsafe { self.get_ticks_unchecked(row) })
|
||||
} else {
|
||||
@ -440,8 +491,8 @@ impl Column {
|
||||
/// `row` must be within the range `[0, self.len())`.
|
||||
#[inline]
|
||||
pub unsafe fn get_added_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
|
||||
debug_assert!(row.index() < self.added_ticks.len());
|
||||
self.added_ticks.get_unchecked(row.index())
|
||||
debug_assert!(row.as_usize() < self.added_ticks.len());
|
||||
self.added_ticks.get_unchecked(row.as_usize())
|
||||
}
|
||||
|
||||
/// Fetches the "changed" change detection tick for the value at `row`. Unlike [`Column::get_changed_tick`]
|
||||
@ -451,8 +502,8 @@ impl Column {
|
||||
/// `row` must be within the range `[0, self.len())`.
|
||||
#[inline]
|
||||
pub unsafe fn get_changed_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
|
||||
debug_assert!(row.index() < self.changed_ticks.len());
|
||||
self.changed_ticks.get_unchecked(row.index())
|
||||
debug_assert!(row.as_usize() < self.changed_ticks.len());
|
||||
self.changed_ticks.get_unchecked(row.as_usize())
|
||||
}
|
||||
|
||||
/// Fetches the change detection ticks for the value at `row`. Unlike [`Column::get_ticks`]
|
||||
@ -462,11 +513,11 @@ impl Column {
|
||||
/// `row` must be within the range `[0, self.len())`.
|
||||
#[inline]
|
||||
pub unsafe fn get_ticks_unchecked(&self, row: TableRow) -> ComponentTicks {
|
||||
debug_assert!(row.index() < self.added_ticks.len());
|
||||
debug_assert!(row.index() < self.changed_ticks.len());
|
||||
debug_assert!(row.as_usize() < self.added_ticks.len());
|
||||
debug_assert!(row.as_usize() < self.changed_ticks.len());
|
||||
ComponentTicks {
|
||||
added: self.added_ticks.get_unchecked(row.index()).read(),
|
||||
changed: self.changed_ticks.get_unchecked(row.index()).read(),
|
||||
added: self.added_ticks.get_unchecked(row.as_usize()).read(),
|
||||
changed: self.changed_ticks.get_unchecked(row.as_usize()).read(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -565,12 +616,12 @@ impl Table {
|
||||
for column in self.columns.values_mut() {
|
||||
column.swap_remove_unchecked(row);
|
||||
}
|
||||
let is_last = row.index() == self.entities.len() - 1;
|
||||
self.entities.swap_remove(row.index());
|
||||
let is_last = row.as_usize() == self.entities.len() - 1;
|
||||
self.entities.swap_remove(row.as_usize());
|
||||
if is_last {
|
||||
None
|
||||
} else {
|
||||
Some(self.entities[row.index()])
|
||||
Some(self.entities[row.as_usize()])
|
||||
}
|
||||
}
|
||||
|
||||
@ -587,9 +638,9 @@ impl Table {
|
||||
row: TableRow,
|
||||
new_table: &mut Table,
|
||||
) -> TableMoveResult {
|
||||
debug_assert!(row.index() < self.entity_count());
|
||||
let is_last = row.index() == self.entities.len() - 1;
|
||||
let new_row = new_table.allocate(self.entities.swap_remove(row.index()));
|
||||
debug_assert!(row.as_usize() < self.entity_count());
|
||||
let is_last = row.as_usize() == self.entities.len() - 1;
|
||||
let new_row = new_table.allocate(self.entities.swap_remove(row.as_usize()));
|
||||
for (component_id, column) in self.columns.iter_mut() {
|
||||
if let Some(new_column) = new_table.get_column_mut(*component_id) {
|
||||
new_column.initialize_from_unchecked(column, row, new_row);
|
||||
@ -603,7 +654,7 @@ impl Table {
|
||||
swapped_entity: if is_last {
|
||||
None
|
||||
} else {
|
||||
Some(self.entities[row.index()])
|
||||
Some(self.entities[row.as_usize()])
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -619,9 +670,9 @@ impl Table {
|
||||
row: TableRow,
|
||||
new_table: &mut Table,
|
||||
) -> TableMoveResult {
|
||||
debug_assert!(row.index() < self.entity_count());
|
||||
let is_last = row.index() == self.entities.len() - 1;
|
||||
let new_row = new_table.allocate(self.entities.swap_remove(row.index()));
|
||||
debug_assert!(row.as_usize() < self.entity_count());
|
||||
let is_last = row.as_usize() == self.entities.len() - 1;
|
||||
let new_row = new_table.allocate(self.entities.swap_remove(row.as_usize()));
|
||||
for (component_id, column) in self.columns.iter_mut() {
|
||||
if let Some(new_column) = new_table.get_column_mut(*component_id) {
|
||||
new_column.initialize_from_unchecked(column, row, new_row);
|
||||
@ -634,7 +685,7 @@ impl Table {
|
||||
swapped_entity: if is_last {
|
||||
None
|
||||
} else {
|
||||
Some(self.entities[row.index()])
|
||||
Some(self.entities[row.as_usize()])
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -650,9 +701,9 @@ impl Table {
|
||||
row: TableRow,
|
||||
new_table: &mut Table,
|
||||
) -> TableMoveResult {
|
||||
debug_assert!(row.index() < self.entity_count());
|
||||
let is_last = row.index() == self.entities.len() - 1;
|
||||
let new_row = new_table.allocate(self.entities.swap_remove(row.index()));
|
||||
debug_assert!(row.as_usize() < self.entity_count());
|
||||
let is_last = row.as_usize() == self.entities.len() - 1;
|
||||
let new_row = new_table.allocate(self.entities.swap_remove(row.as_usize()));
|
||||
for (component_id, column) in self.columns.iter_mut() {
|
||||
new_table
|
||||
.get_column_mut(*component_id)
|
||||
@ -664,7 +715,7 @@ impl Table {
|
||||
swapped_entity: if is_last {
|
||||
None
|
||||
} else {
|
||||
Some(self.entities[row.index()])
|
||||
Some(self.entities[row.as_usize()])
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -728,7 +779,7 @@ impl Table {
|
||||
column.added_ticks.push(UnsafeCell::new(Tick::new(0)));
|
||||
column.changed_ticks.push(UnsafeCell::new(Tick::new(0)));
|
||||
}
|
||||
TableRow::new(index)
|
||||
TableRow::from_usize(index)
|
||||
}
|
||||
|
||||
/// Gets the number of entities currently being stored in the table.
|
||||
@ -819,7 +870,7 @@ impl Tables {
|
||||
/// Returns `None` if `id` is invalid.
|
||||
#[inline]
|
||||
pub fn get(&self, id: TableId) -> Option<&Table> {
|
||||
self.tables.get(id.index())
|
||||
self.tables.get(id.as_usize())
|
||||
}
|
||||
|
||||
/// Fetches mutable references to two different [`Table`]s.
|
||||
@ -829,12 +880,12 @@ impl Tables {
|
||||
/// Panics if `a` and `b` are equal.
|
||||
#[inline]
|
||||
pub(crate) fn get_2_mut(&mut self, a: TableId, b: TableId) -> (&mut Table, &mut Table) {
|
||||
if a.index() > b.index() {
|
||||
let (b_slice, a_slice) = self.tables.split_at_mut(a.index());
|
||||
(&mut a_slice[0], &mut b_slice[b.index()])
|
||||
if a.as_usize() > b.as_usize() {
|
||||
let (b_slice, a_slice) = self.tables.split_at_mut(a.as_usize());
|
||||
(&mut a_slice[0], &mut b_slice[b.as_usize()])
|
||||
} else {
|
||||
let (a_slice, b_slice) = self.tables.split_at_mut(b.index());
|
||||
(&mut a_slice[a.index()], &mut b_slice[0])
|
||||
let (a_slice, b_slice) = self.tables.split_at_mut(b.as_usize());
|
||||
(&mut a_slice[a.as_usize()], &mut b_slice[0])
|
||||
}
|
||||
}
|
||||
|
||||
@ -859,7 +910,10 @@ impl Tables {
|
||||
table = table.add_column(components.get_info_unchecked(*component_id));
|
||||
}
|
||||
tables.push(table.build());
|
||||
(component_ids.to_vec(), TableId::new(tables.len() - 1))
|
||||
(
|
||||
component_ids.to_vec(),
|
||||
TableId::from_usize(tables.len() - 1),
|
||||
)
|
||||
});
|
||||
|
||||
*value
|
||||
@ -889,14 +943,14 @@ impl Index<TableId> for Tables {
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: TableId) -> &Self::Output {
|
||||
&self.tables[index.index()]
|
||||
&self.tables[index.as_usize()]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<TableId> for Tables {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: TableId) -> &mut Self::Output {
|
||||
&mut self.tables[index.index()]
|
||||
&mut self.tables[index.as_usize()]
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user