use std::any::TypeId;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use crate::archetype::{Archetype, ArchetypeLayout, ArchetypeStore, Column};
use crate::component::Component;
use crate::entity::Entity;
pub struct Mut<'w, T> {
value: &'w mut T,
changed_tick: *mut u64,
tick: u64,
}
impl<T> Mut<'_, T> {
pub fn set_changed(&mut self) {
unsafe {
*self.changed_tick = self.tick;
}
}
}
impl<T> Deref for Mut<'_, T> {
type Target = T;
fn deref(&self) -> &T {
self.value
}
}
impl<T> DerefMut for Mut<'_, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe {
*self.changed_tick = self.tick;
}
self.value
}
}
pub trait QuerySpec {
type Item<'w>;
type State<'w>;
fn matches(layout: &ArchetypeLayout) -> bool;
fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>>;
fn len(state: &Self::State<'_>) -> usize;
fn entity(state: &Self::State<'_>, row: usize) -> Entity;
fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w>;
}
pub trait QuerySpecMut {
type Item<'w>;
type State<'w>;
fn matches(layout: &ArchetypeLayout) -> bool;
fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>>;
fn len(state: &Self::State<'_>) -> usize;
fn entity(state: &Self::State<'_>, row: usize) -> Entity;
unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w>;
}
pub trait QueryFilter {
fn matches(layout: &ArchetypeLayout) -> bool;
}
pub struct NoFilter;
impl QueryFilter for NoFilter {
fn matches(_layout: &ArchetypeLayout) -> bool {
true
}
}
pub struct With<T>(PhantomData<T>);
impl<T: Component> QueryFilter for With<T> {
fn matches(layout: &ArchetypeLayout) -> bool {
layout.contains(TypeId::of::<T>())
}
}
pub struct Without<T>(PhantomData<T>);
impl<T: Component> QueryFilter for Without<T> {
fn matches(layout: &ArchetypeLayout) -> bool {
!layout.contains(TypeId::of::<T>())
}
}
impl<A, B> QueryFilter for (A, B)
where
A: QueryFilter,
B: QueryFilter,
{
fn matches(layout: &ArchetypeLayout) -> bool {
A::matches(layout) && B::matches(layout)
}
}
impl<A, B, C> QueryFilter for (A, B, C)
where
A: QueryFilter,
B: QueryFilter,
C: QueryFilter,
{
fn matches(layout: &ArchetypeLayout) -> bool {
A::matches(layout) && B::matches(layout) && C::matches(layout)
}
}
pub struct QueryIter<'w, Q, F = NoFilter>
where
Q: QuerySpec,
F: QueryFilter,
{
store: &'w ArchetypeStore,
archetype_index: usize,
row: usize,
current: Option<Q::State<'w>>,
_filter: PhantomData<F>,
}
impl<'w, Q, F> QueryIter<'w, Q, F>
where
Q: QuerySpec,
F: QueryFilter,
{
pub(crate) fn new(store: &'w ArchetypeStore) -> Self {
Self {
store,
archetype_index: 0,
row: 0,
current: None,
_filter: PhantomData,
}
}
fn remaining(&self) -> usize {
let current = self
.current
.as_ref()
.map_or(0, |state| Q::len(state) - self.row);
let future: usize = self
.store
.iter()
.skip(self.archetype_index)
.filter(|archetype| Q::matches(archetype.layout()) && F::matches(archetype.layout()))
.map(|archetype| archetype.len())
.sum();
current + future
}
}
impl<'w, Q, F> Iterator for QueryIter<'w, Q, F>
where
Q: QuerySpec + 'w,
F: QueryFilter,
{
type Item = (Entity, Q::Item<'w>);
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(state) = self.current.as_ref() {
if self.row < Q::len(state) {
let row = self.row;
self.row += 1;
return Some((Q::entity(state, row), Q::fetch(state, row)));
}
self.current = None;
}
let archetype = self.store.get_by_index(self.archetype_index)?;
self.archetype_index += 1;
if !Q::matches(archetype.layout()) || !F::matches(archetype.layout()) {
continue;
}
self.current = Q::init_state(archetype);
self.row = 0;
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.remaining();
(remaining, Some(remaining))
}
}
impl<'w, Q, F> ExactSizeIterator for QueryIter<'w, Q, F>
where
Q: QuerySpec + 'w,
F: QueryFilter,
{
fn len(&self) -> usize {
self.remaining()
}
}
pub struct QueryIterMut<'w, Q, F = NoFilter>
where
Q: QuerySpecMut,
F: QueryFilter,
{
store: *mut ArchetypeStore,
archetype_len: usize,
archetype_index: usize,
row: usize,
tick: u64,
current: Option<Q::State<'w>>,
_filter: PhantomData<F>,
_marker: PhantomData<&'w mut ArchetypeStore>,
}
impl<'w, Q, F> QueryIterMut<'w, Q, F>
where
Q: QuerySpecMut,
F: QueryFilter,
{
pub(crate) fn new(store: &'w mut ArchetypeStore, tick: u64) -> Self {
Self {
archetype_len: store.len(),
store,
archetype_index: 0,
row: 0,
tick,
current: None,
_filter: PhantomData,
_marker: PhantomData,
}
}
pub(crate) unsafe fn new_from_ptr(store: *mut ArchetypeStore, tick: u64) -> Self {
Self {
archetype_len: unsafe { (*store).len() },
store,
archetype_index: 0,
row: 0,
tick,
current: None,
_filter: PhantomData,
_marker: PhantomData,
}
}
fn remaining(&self) -> usize {
let current = self
.current
.as_ref()
.map_or(0, |state| Q::len(state) - self.row);
let future: usize = unsafe { &*self.store }
.iter()
.skip(self.archetype_index)
.filter(|archetype| Q::matches(archetype.layout()) && F::matches(archetype.layout()))
.map(|archetype| archetype.len())
.sum();
current + future
}
}
impl<'w, Q, F> Iterator for QueryIterMut<'w, Q, F>
where
Q: QuerySpecMut + 'w,
F: QueryFilter,
{
type Item = (Entity, Q::Item<'w>);
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(state) = self.current.as_mut() {
if self.row < Q::len(state) {
let row = self.row;
self.row += 1;
let entity = Q::entity(state, row);
let item = unsafe { Q::fetch(state, row) };
return Some((entity, item));
}
self.current = None;
}
if self.archetype_index >= self.archetype_len {
return None;
}
let archetype =
unsafe { ArchetypeStore::get_by_index_mut_ptr(self.store, self.archetype_index)? };
self.archetype_index += 1;
if !Q::matches(archetype.layout()) || !F::matches(archetype.layout()) {
continue;
}
self.current = Q::init_state(archetype, self.tick);
self.row = 0;
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.remaining();
(remaining, Some(remaining))
}
}
impl<'w, Q, F> ExactSizeIterator for QueryIterMut<'w, Q, F>
where
Q: QuerySpecMut + 'w,
F: QueryFilter,
{
fn len(&self) -> usize {
self.remaining()
}
}
pub type Query2Iter<'w, A, B, F = NoFilter> = QueryIter<'w, (&'w A, &'w B), F>;
pub type Query2MutIter<'w, A, B, F = NoFilter> = QueryIterMut<'w, (&'w mut A, &'w mut B), F>;
pub type Query3Iter<'w, A, B, C, F = NoFilter> = QueryIter<'w, (&'w A, &'w B, &'w C), F>;
pub type Query3MutIter<'w, A, B, C, F = NoFilter> =
QueryIterMut<'w, (&'w mut A, &'w mut B, &'w mut C), F>;
#[doc(hidden)]
pub struct OptionalMutState<'w, T> {
entities: &'w [Entity],
data: *mut T,
changed_ticks: *mut u64,
tick: u64,
_marker: PhantomData<&'w mut T>,
}
#[doc(hidden)]
pub struct SingleMutState<'w, T> {
entities: &'w [Entity],
data: *mut T,
changed_ticks: *mut u64,
tick: u64,
len: usize,
_marker: PhantomData<&'w mut T>,
}
#[doc(hidden)]
pub struct PairMutState<'w, A, B> {
entities: &'w [Entity],
col_a: *mut A,
col_b: *mut B,
changed_ticks_a: *mut u64,
changed_ticks_b: *mut u64,
tick: u64,
len: usize,
_marker: PhantomData<&'w mut (A, B)>,
}
#[doc(hidden)]
pub struct TripleMutState<'w, A, B, C> {
entities: &'w [Entity],
col_a: *mut A,
col_b: *mut B,
col_c: *mut C,
changed_ticks_a: *mut u64,
changed_ticks_b: *mut u64,
changed_ticks_c: *mut u64,
tick: u64,
len: usize,
_marker: PhantomData<&'w mut (A, B, C)>,
}
impl<T: Component> QuerySpec for &T {
type Item<'w> = &'w T;
type State<'w> = (&'w [Entity], &'w Column<T>);
fn matches(layout: &ArchetypeLayout) -> bool {
layout.contains(TypeId::of::<T>())
}
fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
Some((archetype.entities(), archetype.column::<T>()?))
}
fn len(state: &Self::State<'_>) -> usize {
state.0.len()
}
fn entity(state: &Self::State<'_>, row: usize) -> Entity {
state.0[row]
}
fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
state.1.get(row).unwrap()
}
}
impl<T: Component> QuerySpec for Option<&T> {
type Item<'w> = Option<&'w T>;
type State<'w> = (&'w [Entity], Option<&'w Column<T>>);
fn matches(_layout: &ArchetypeLayout) -> bool {
true
}
fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
Some((archetype.entities(), archetype.column::<T>()))
}
fn len(state: &Self::State<'_>) -> usize {
state.0.len()
}
fn entity(state: &Self::State<'_>, row: usize) -> Entity {
state.0[row]
}
fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
state.1.as_ref().and_then(|col| col.get(row))
}
}
impl<A: Component, B: Component> QuerySpec for (&A, &B) {
type Item<'w> = (&'w A, &'w B);
type State<'w> = (&'w [Entity], &'w Column<A>, &'w Column<B>);
fn matches(layout: &ArchetypeLayout) -> bool {
layout.contains(TypeId::of::<A>()) && layout.contains(TypeId::of::<B>())
}
fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
Some((
archetype.entities(),
archetype.column::<A>()?,
archetype.column::<B>()?,
))
}
fn len(state: &Self::State<'_>) -> usize {
state.0.len()
}
fn entity(state: &Self::State<'_>, row: usize) -> Entity {
state.0[row]
}
fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
(state.1.get(row).unwrap(), state.2.get(row).unwrap())
}
}
impl<A: Component, B: Component> QuerySpec for (&A, Option<&B>) {
type Item<'w> = (&'w A, Option<&'w B>);
type State<'w> = (&'w [Entity], &'w Column<A>, Option<&'w Column<B>>);
fn matches(layout: &ArchetypeLayout) -> bool {
layout.contains(TypeId::of::<A>())
}
fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
Some((
archetype.entities(),
archetype.column::<A>()?,
archetype.column::<B>(),
))
}
fn len(state: &Self::State<'_>) -> usize {
state.0.len()
}
fn entity(state: &Self::State<'_>, row: usize) -> Entity {
state.0[row]
}
fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
(
state.1.get(row).unwrap(),
state.2.as_ref().and_then(|col| col.get(row)),
)
}
}
impl<A: Component, B: Component, C: Component> QuerySpec for (&A, &B, &C) {
type Item<'w> = (&'w A, &'w B, &'w C);
type State<'w> = (&'w [Entity], &'w Column<A>, &'w Column<B>, &'w Column<C>);
fn matches(layout: &ArchetypeLayout) -> bool {
layout.contains(TypeId::of::<A>())
&& layout.contains(TypeId::of::<B>())
&& layout.contains(TypeId::of::<C>())
}
fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
Some((
archetype.entities(),
archetype.column::<A>()?,
archetype.column::<B>()?,
archetype.column::<C>()?,
))
}
fn len(state: &Self::State<'_>) -> usize {
state.0.len()
}
fn entity(state: &Self::State<'_>, row: usize) -> Entity {
state.0[row]
}
fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
(
state.1.get(row).unwrap(),
state.2.get(row).unwrap(),
state.3.get(row).unwrap(),
)
}
}
impl<A: Component, B: Component, C: Component, D: Component> QuerySpec
for (&A, Option<&B>, Option<&C>, Option<&D>)
{
type Item<'w> = (&'w A, Option<&'w B>, Option<&'w C>, Option<&'w D>);
type State<'w> = (
&'w [Entity],
&'w Column<A>,
Option<&'w Column<B>>,
Option<&'w Column<C>>,
Option<&'w Column<D>>,
);
fn matches(layout: &ArchetypeLayout) -> bool {
layout.contains(TypeId::of::<A>())
}
fn init_state<'w>(archetype: &'w Archetype) -> Option<Self::State<'w>> {
Some((
archetype.entities(),
archetype.column::<A>()?,
archetype.column::<B>(),
archetype.column::<C>(),
archetype.column::<D>(),
))
}
fn len(state: &Self::State<'_>) -> usize {
state.0.len()
}
fn entity(state: &Self::State<'_>, row: usize) -> Entity {
state.0[row]
}
fn fetch<'w>(state: &Self::State<'w>, row: usize) -> Self::Item<'w> {
(
state.1.get(row).unwrap(),
state.2.as_ref().and_then(|col| col.get(row)),
state.3.as_ref().and_then(|col| col.get(row)),
state.4.as_ref().and_then(|col| col.get(row)),
)
}
}
impl<T: Component> QuerySpecMut for &mut T {
type Item<'w> = Mut<'w, T>;
type State<'w> = SingleMutState<'w, T>;
fn matches(layout: &ArchetypeLayout) -> bool {
layout.contains(TypeId::of::<T>())
}
fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>> {
let (entities, column) = archetype.entities_and_column_mut::<T>()?;
Some(SingleMutState {
entities,
data: column.as_mut_ptr(),
changed_ticks: column.changed_ticks_mut_ptr(),
tick,
len: column.len(),
_marker: PhantomData,
})
}
fn len(state: &Self::State<'_>) -> usize {
state.len
}
fn entity(state: &Self::State<'_>, row: usize) -> Entity {
state.entities[row]
}
unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w> {
unsafe {
Mut {
value: &mut *state.data.add(row),
changed_tick: state.changed_ticks.add(row),
tick: state.tick,
}
}
}
}
impl<T: Component> QuerySpecMut for Option<&mut T> {
type Item<'w> = Option<Mut<'w, T>>;
type State<'w> = OptionalMutState<'w, T>;
fn matches(_layout: &ArchetypeLayout) -> bool {
true
}
fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>> {
let entities = archetype.entities() as *const [Entity];
let (data, changed_ticks) = match archetype.column_mut::<T>() {
Some(col) => (col.as_mut_ptr(), col.changed_ticks_mut_ptr()),
None => (std::ptr::null_mut(), std::ptr::null_mut()),
};
Some(OptionalMutState {
entities: unsafe { &*entities },
data,
changed_ticks,
tick,
_marker: PhantomData,
})
}
fn len(state: &Self::State<'_>) -> usize {
state.entities.len()
}
fn entity(state: &Self::State<'_>, row: usize) -> Entity {
state.entities[row]
}
unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w> {
if state.data.is_null() {
None
} else {
unsafe {
Some(Mut {
value: &mut *state.data.add(row),
changed_tick: state.changed_ticks.add(row),
tick: state.tick,
})
}
}
}
}
impl<A: Component, B: Component> QuerySpecMut for (&mut A, &mut B) {
type Item<'w> = (Mut<'w, A>, Mut<'w, B>);
type State<'w> = PairMutState<'w, A, B>;
fn matches(layout: &ArchetypeLayout) -> bool {
layout.contains(TypeId::of::<A>()) && layout.contains(TypeId::of::<B>())
}
fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>> {
let (entities, col_a, col_b) = archetype.entities_and_two_columns_mut::<A, B>()?;
Some(PairMutState {
entities,
col_a: col_a.as_mut_ptr(),
col_b: col_b.as_mut_ptr(),
changed_ticks_a: col_a.changed_ticks_mut_ptr(),
changed_ticks_b: col_b.changed_ticks_mut_ptr(),
tick,
len: entities.len(),
_marker: PhantomData,
})
}
fn len(state: &Self::State<'_>) -> usize {
state.len
}
fn entity(state: &Self::State<'_>, row: usize) -> Entity {
state.entities[row]
}
unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w> {
unsafe {
(
Mut {
value: &mut *state.col_a.add(row),
changed_tick: state.changed_ticks_a.add(row),
tick: state.tick,
},
Mut {
value: &mut *state.col_b.add(row),
changed_tick: state.changed_ticks_b.add(row),
tick: state.tick,
},
)
}
}
}
impl<A: Component, B: Component, C: Component> QuerySpecMut for (&mut A, &mut B, &mut C) {
type Item<'w> = (Mut<'w, A>, Mut<'w, B>, Mut<'w, C>);
type State<'w> = TripleMutState<'w, A, B, C>;
fn matches(layout: &ArchetypeLayout) -> bool {
layout.contains(TypeId::of::<A>())
&& layout.contains(TypeId::of::<B>())
&& layout.contains(TypeId::of::<C>())
}
fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>> {
let (entities, col_a, col_b, col_c) =
archetype.entities_and_three_columns_mut::<A, B, C>()?;
Some(TripleMutState {
entities,
col_a: col_a.as_mut_ptr(),
col_b: col_b.as_mut_ptr(),
col_c: col_c.as_mut_ptr(),
changed_ticks_a: col_a.changed_ticks_mut_ptr(),
changed_ticks_b: col_b.changed_ticks_mut_ptr(),
changed_ticks_c: col_c.changed_ticks_mut_ptr(),
tick,
len: entities.len(),
_marker: PhantomData,
})
}
fn len(state: &Self::State<'_>) -> usize {
state.len
}
fn entity(state: &Self::State<'_>, row: usize) -> Entity {
state.entities[row]
}
unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w> {
unsafe {
(
Mut {
value: &mut *state.col_a.add(row),
changed_tick: state.changed_ticks_a.add(row),
tick: state.tick,
},
Mut {
value: &mut *state.col_b.add(row),
changed_tick: state.changed_ticks_b.add(row),
tick: state.tick,
},
Mut {
value: &mut *state.col_c.add(row),
changed_tick: state.changed_ticks_c.add(row),
tick: state.tick,
},
)
}
}
}
pub struct ChangedIter<'w, T: Component> {
store: &'w ArchetypeStore,
archetype_index: usize,
row: usize,
since_tick: u64,
current: Option<(&'w [Entity], &'w Column<T>)>,
}
impl<'w, T: Component> ChangedIter<'w, T> {
pub(crate) fn new(store: &'w ArchetypeStore, since_tick: u64) -> Self {
Self {
store,
archetype_index: 0,
row: 0,
since_tick,
current: None,
}
}
}
impl<'w, T: Component> Iterator for ChangedIter<'w, T> {
type Item = (Entity, &'w T);
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some((entities, col)) = &self.current {
while self.row < entities.len() {
let row = self.row;
self.row += 1;
if col.changed_tick(row) > self.since_tick {
return Some((entities[row], col.get(row).unwrap()));
}
}
self.current = None;
}
let archetype = self.store.get_by_index(self.archetype_index)?;
self.archetype_index += 1;
if !archetype.layout().contains(TypeId::of::<T>()) {
continue;
}
if let (Some(col), entities) = (archetype.column::<T>(), archetype.entities()) {
self.current = Some((entities, col));
self.row = 0;
}
}
}
}
pub struct AddedIter<'w, T: Component> {
store: &'w ArchetypeStore,
archetype_index: usize,
row: usize,
since_tick: u64,
current: Option<(&'w [Entity], &'w Column<T>)>,
}
impl<'w, T: Component> AddedIter<'w, T> {
pub(crate) fn new(store: &'w ArchetypeStore, since_tick: u64) -> Self {
Self {
store,
archetype_index: 0,
row: 0,
since_tick,
current: None,
}
}
}
impl<'w, T: Component> Iterator for AddedIter<'w, T> {
type Item = (Entity, &'w T);
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some((entities, col)) = &self.current {
while self.row < entities.len() {
let row = self.row;
self.row += 1;
if col.added_tick(row) > self.since_tick {
return Some((entities[row], col.get(row).unwrap()));
}
}
self.current = None;
}
let archetype = self.store.get_by_index(self.archetype_index)?;
self.archetype_index += 1;
if !archetype.layout().contains(TypeId::of::<T>()) {
continue;
}
if let (Some(col), entities) = (archetype.column::<T>(), archetype.entities()) {
self.current = Some((entities, col));
self.row = 0;
}
}
}
}
#[doc(hidden)]
pub struct PairMutOptionalState<'w, A, B> {
entities: &'w [Entity],
col_a: *mut A,
col_b: *mut B,
changed_ticks_a: *mut u64,
changed_ticks_b: *mut u64,
tick: u64,
len: usize,
_marker: PhantomData<&'w mut (A, B)>,
}
impl<A: Component, B: Component> QuerySpecMut for (&mut A, Option<&mut B>) {
type Item<'w> = (Mut<'w, A>, Option<Mut<'w, B>>);
type State<'w> = PairMutOptionalState<'w, A, B>;
fn matches(layout: &ArchetypeLayout) -> bool {
layout.contains(TypeId::of::<A>())
}
fn init_state<'w>(archetype: &'w mut Archetype, tick: u64) -> Option<Self::State<'w>> {
assert_ne!(
TypeId::of::<A>(),
TypeId::of::<B>(),
"cannot borrow the same column mutably twice"
);
let (entities, col_a, col_b) =
archetype.entities_and_required_optional_columns_mut::<A, B>()?;
let col_a_ptr = col_a.as_mut_ptr();
let changed_ticks_a = col_a.changed_ticks_mut_ptr();
let len = col_a.len();
let (col_b_ptr, changed_ticks_b) = match col_b {
Some(col) => (col.as_mut_ptr(), col.changed_ticks_mut_ptr()),
None => (std::ptr::null_mut(), std::ptr::null_mut()),
};
Some(PairMutOptionalState {
entities,
col_a: col_a_ptr,
col_b: col_b_ptr,
changed_ticks_a,
changed_ticks_b,
tick,
len,
_marker: PhantomData,
})
}
fn len(state: &Self::State<'_>) -> usize {
state.len
}
fn entity(state: &Self::State<'_>, row: usize) -> Entity {
state.entities[row]
}
unsafe fn fetch<'w>(state: &mut Self::State<'w>, row: usize) -> Self::Item<'w> {
unsafe {
let a = Mut {
value: &mut *state.col_a.add(row),
changed_tick: state.changed_ticks_a.add(row),
tick: state.tick,
};
let b = if state.col_b.is_null() {
None
} else {
Some(Mut {
value: &mut *state.col_b.add(row),
changed_tick: state.changed_ticks_b.add(row),
tick: state.tick,
})
};
(a, b)
}
}
}
#[cfg(test)]
mod tests {
use crate::World;
#[derive(Debug, Clone, PartialEq)]
struct Pos {
x: f32,
y: f32,
}
impl crate::Component for Pos {}
#[derive(Debug, Clone, PartialEq)]
struct Vel {
x: f32,
y: f32,
}
impl crate::Component for Vel {}
#[derive(Debug, Clone, PartialEq)]
struct Name(String);
impl crate::Component for Name {}
#[derive(Debug, Clone, PartialEq)]
struct Health(i32);
impl crate::Component for Health {}
#[test]
fn optional_query_returns_some_when_present() {
let mut world = World::new();
world.spawn((Pos { x: 1.0, y: 2.0 },));
let results: Vec<_> = world.query::<Option<&Pos>>().collect();
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, Some(&Pos { x: 1.0, y: 2.0 }));
}
#[test]
fn optional_query_returns_none_when_absent() {
let mut world = World::new();
world.spawn((Vel { x: 1.0, y: 0.0 },));
let results: Vec<_> = world.query::<Option<&Pos>>().collect();
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, None);
}
#[test]
fn required_plus_optional_both_present() {
let mut world = World::new();
world.spawn((Pos { x: 1.0, y: 0.0 }, Vel { x: 2.0, y: 0.0 }));
let results: Vec<_> = world.query::<(&Pos, Option<&Vel>)>().collect();
assert_eq!(results.len(), 1);
assert_eq!(results[0].1.0, &Pos { x: 1.0, y: 0.0 });
assert_eq!(results[0].1.1, Some(&Vel { x: 2.0, y: 0.0 }));
}
#[test]
fn required_plus_optional_absent() {
let mut world = World::new();
world.spawn((Pos { x: 1.0, y: 0.0 },));
let results: Vec<_> = world.query::<(&Pos, Option<&Vel>)>().collect();
assert_eq!(results.len(), 1);
assert_eq!(results[0].1.1, None);
}
#[test]
fn required_plus_optional_filters_by_required() {
let mut world = World::new();
world.spawn((Pos { x: 1.0, y: 0.0 },));
world.spawn((Vel { x: 2.0, y: 0.0 },));
let results: Vec<_> = world.query::<(&Pos, Option<&Vel>)>().collect();
assert_eq!(results.len(), 1);
}
#[test]
fn four_tuple_all_present() {
let mut world = World::new();
world.spawn((
Pos { x: 1.0, y: 0.0 },
Vel { x: 2.0, y: 0.0 },
Name("a".into()),
Health(100),
));
let results: Vec<_> = world
.query::<(&Pos, Option<&Vel>, Option<&Name>, Option<&Health>)>()
.collect();
assert_eq!(results.len(), 1);
let (pos, vel, name, hp) = &results[0].1;
assert_eq!(*pos, &Pos { x: 1.0, y: 0.0 });
assert!(vel.is_some());
assert!(name.is_some());
assert!(hp.is_some());
}
#[test]
fn four_tuple_mixed_presence() {
let mut world = World::new();
world.spawn((Pos { x: 1.0, y: 0.0 }, Health(50)));
world.spawn((
Pos { x: 2.0, y: 0.0 },
Vel { x: 1.0, y: 0.0 },
Name("b".into()),
));
let mut results: Vec<_> = world
.query::<(&Pos, Option<&Vel>, Option<&Name>, Option<&Health>)>()
.map(|(_, (pos, vel, name, hp))| (pos.x, vel.is_some(), name.is_some(), hp.is_some()))
.collect();
results.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
assert_eq!(results.len(), 2);
assert_eq!(results[0], (1.0, false, false, true));
assert_eq!(results[1], (2.0, true, true, false));
}
#[test]
fn optional_mut_modifies_when_present() {
let mut world = World::new();
world.spawn((Pos { x: 1.0, y: 0.0 }, Vel { x: 0.0, y: 0.0 }));
for (_, vel) in world.query_mut::<Option<&mut Vel>>() {
if let Some(mut v) = vel {
v.x += 10.0;
}
}
let vel = world.query::<&Vel>().next().unwrap().1;
assert_eq!(vel.x, 10.0);
}
#[test]
fn optional_mut_skips_when_absent() {
let mut world = World::new();
world.spawn((Pos { x: 1.0, y: 0.0 },));
let mut count = 0;
for (_, vel) in world.query_mut::<Option<&mut Vel>>() {
count += 1;
assert!(vel.is_none());
}
assert_eq!(count, 1);
}
#[test]
fn required_mut_plus_optional_mut() {
let mut world = World::new();
world.spawn((Pos { x: 1.0, y: 0.0 }, Vel { x: 0.0, y: 0.0 }));
world.spawn((Pos { x: 2.0, y: 0.0 },));
for (_, (mut pos, vel)) in world.query_mut::<(&mut Pos, Option<&mut Vel>)>() {
pos.x += 100.0;
if let Some(mut v) = vel {
v.x += 50.0;
}
}
let mut results: Vec<_> = world.query::<&Pos>().map(|(_, p)| p.x).collect();
results.sort_by(|a, b| a.partial_cmp(b).unwrap());
assert_eq!(results, vec![101.0, 102.0]);
let vel = world.query::<&Vel>().next().unwrap().1;
assert_eq!(vel.x, 50.0);
}
#[test]
fn optional_query_spans_multiple_archetypes() {
let mut world = World::new();
world.spawn((Pos { x: 1.0, y: 0.0 },));
world.spawn((Pos { x: 2.0, y: 0.0 }, Vel { x: 5.0, y: 0.0 }));
world.spawn((Pos { x: 3.0, y: 0.0 }, Name("c".into())));
let mut results: Vec<_> = world
.query::<(&Pos, Option<&Vel>)>()
.map(|(_, (pos, vel))| (pos.x, vel.map(|v| v.x)))
.collect();
results.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
assert_eq!(results.len(), 3);
assert_eq!(results[0], (1.0, None));
assert_eq!(results[1], (2.0, Some(5.0)));
assert_eq!(results[2], (3.0, None));
}
}