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:
Zachary Harrold 2023-12-05 13:44:33 +11:00 committed by GitHub
parent 83ee6de1da
commit 72adf2ae2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 195 additions and 146 deletions

View File

@ -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,
},

View File

@ -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 => {

View File

@ -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;

View File

@ -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);

View File

@ -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.
///

View File

@ -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 {

View File

@ -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()]
}
}