use super::{QueryData, QueryFilter, ReadOnlyQueryData};
use crate::{
archetype::{Archetype, ArchetypeEntity, Archetypes},
bundle::Bundle,
change_detection::Tick,
entity::{ContainsEntity, Entities, Entity, EntityEquivalent, EntitySet, EntitySetIterator},
query::{ArchetypeFilter, ArchetypeQueryData, DebugCheckedUnwrap, QueryState, StorageId},
storage::{Table, TableRow, Tables},
world::{
unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept,
FilteredEntityMut, FilteredEntityRef,
},
};
use alloc::vec::Vec;
use core::{
cmp::Ordering,
fmt::{self, Debug, Formatter},
iter::FusedIterator,
mem::MaybeUninit,
ops::Range,
};
use nonmax::NonMaxU32;
pub struct QueryIter<'w, 's, D: QueryData, F: QueryFilter> {
world: UnsafeWorldCell<'w>,
tables: &'w Tables,
archetypes: &'w Archetypes,
query_state: &'s QueryState<D, F>,
cursor: QueryIterationCursor<'w, 's, D, F>,
}
impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
pub(crate) unsafe fn new(
world: UnsafeWorldCell<'w>,
query_state: &'s QueryState<D, F>,
last_run: Tick,
this_run: Tick,
) -> Self {
QueryIter {
world,
query_state,
tables: unsafe { &world.storages().tables },
archetypes: world.archetypes(),
cursor: unsafe { QueryIterationCursor::init(world, query_state, last_run, this_run) },
}
}
pub fn remaining(&self) -> QueryIter<'w, 's, D, F>
where
D: ReadOnlyQueryData,
{
QueryIter {
world: self.world,
tables: self.tables,
archetypes: self.archetypes,
query_state: self.query_state,
cursor: self.cursor.clone(),
}
}
pub fn remaining_mut(&mut self) -> QueryIter<'_, 's, D, F> {
QueryIter {
world: self.world,
tables: self.tables,
archetypes: self.archetypes,
query_state: self.query_state,
cursor: self.cursor.reborrow(),
}
}
#[inline]
pub(super) unsafe fn fold_over_storage_range<B, Func>(
&mut self,
mut accum: B,
func: &mut Func,
storage: StorageId,
range: Option<Range<u32>>,
) -> B
where
Func: FnMut(B, D::Item<'w, 's>) -> B,
{
if self.cursor.is_dense {
let table_id = unsafe { storage.table_id };
let table = unsafe { self.tables.get(table_id).debug_checked_unwrap() };
let range = range.unwrap_or(0..table.entity_count());
accum =
unsafe { self.fold_over_table_range(accum, func, table, range) };
} else {
let archetype_id = unsafe { storage.archetype_id };
let archetype = unsafe { self.archetypes.get(archetype_id).debug_checked_unwrap() };
let table = unsafe { self.tables.get(archetype.table_id()).debug_checked_unwrap() };
let range = range.unwrap_or(0..archetype.len());
if table.entity_count() == archetype.len() {
accum =
unsafe { self.fold_over_dense_archetype_range(accum, func, archetype,range) };
} else {
accum =
unsafe { self.fold_over_archetype_range(accum, func, archetype,range) };
}
}
accum
}
#[inline]
pub(super) unsafe fn fold_over_table_range<B, Func>(
&mut self,
mut accum: B,
func: &mut Func,
table: &'w Table,
rows: Range<u32>,
) -> B
where
Func: FnMut(B, D::Item<'w, 's>) -> B,
{
if table.is_empty() {
return accum;
}
D::set_table(&mut self.cursor.fetch, &self.query_state.fetch_state, table);
F::set_table(
&mut self.cursor.filter,
&self.query_state.filter_state,
table,
);
let entities = table.entities();
for row in rows {
let entity = unsafe { entities.get_unchecked(row as usize) };
let row = unsafe { TableRow::new(NonMaxU32::new_unchecked(row)) };
let fetched = unsafe {
!F::filter_fetch(
&self.query_state.filter_state,
&mut self.cursor.filter,
*entity,
row,
)
};
if fetched {
continue;
}
if let Some(item) = D::fetch(
&self.query_state.fetch_state,
&mut self.cursor.fetch,
*entity,
row,
) {
accum = func(accum, item);
}
}
accum
}
#[inline]
pub(super) unsafe fn fold_over_archetype_range<B, Func>(
&mut self,
mut accum: B,
func: &mut Func,
archetype: &'w Archetype,
indices: Range<u32>,
) -> B
where
Func: FnMut(B, D::Item<'w, 's>) -> B,
{
if archetype.is_empty() {
return accum;
}
let table = self.tables.get(archetype.table_id()).debug_checked_unwrap();
D::set_archetype(
&mut self.cursor.fetch,
&self.query_state.fetch_state,
archetype,
table,
);
F::set_archetype(
&mut self.cursor.filter,
&self.query_state.filter_state,
archetype,
table,
);
let entities = archetype.entities();
for index in indices {
let archetype_entity = unsafe { entities.get_unchecked(index as usize) };
let fetched = unsafe {
!F::filter_fetch(
&self.query_state.filter_state,
&mut self.cursor.filter,
archetype_entity.id(),
archetype_entity.table_row(),
)
};
if fetched {
continue;
}
if let Some(item) = unsafe {
D::fetch(
&self.query_state.fetch_state,
&mut self.cursor.fetch,
archetype_entity.id(),
archetype_entity.table_row(),
)
} {
accum = func(accum, item);
}
}
accum
}
#[inline]
pub(super) unsafe fn fold_over_dense_archetype_range<B, Func>(
&mut self,
mut accum: B,
func: &mut Func,
archetype: &'w Archetype,
rows: Range<u32>,
) -> B
where
Func: FnMut(B, D::Item<'w, 's>) -> B,
{
if archetype.is_empty() {
return accum;
}
let table = self.tables.get(archetype.table_id()).debug_checked_unwrap();
debug_assert!(
archetype.len() == table.entity_count(),
"archetype and its table must have the same length. "
);
D::set_archetype(
&mut self.cursor.fetch,
&self.query_state.fetch_state,
archetype,
table,
);
F::set_archetype(
&mut self.cursor.filter,
&self.query_state.filter_state,
archetype,
table,
);
let entities = table.entities();
for row in rows {
let entity = unsafe { *entities.get_unchecked(row as usize) };
let row = unsafe { TableRow::new(NonMaxU32::new_unchecked(row)) };
let filter_matched = unsafe {
F::filter_fetch(
&self.query_state.filter_state,
&mut self.cursor.filter,
entity,
row,
)
};
if !filter_matched {
continue;
}
if let Some(item) = D::fetch(
&self.query_state.fetch_state,
&mut self.cursor.fetch,
entity,
row,
) {
accum = func(accum, item);
}
}
accum
}
pub fn sort<L: ReadOnlyQueryData + 'w>(
self,
) -> QuerySortedIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
for<'lw, 'ls> L::Item<'lw, 'ls>: Ord,
{
self.sort_impl::<L>(|keyed_query| keyed_query.sort())
}
pub fn sort_unstable<L: ReadOnlyQueryData + 'w>(
self,
) -> QuerySortedIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
for<'lw, 'ls> L::Item<'lw, 'ls>: Ord,
{
self.sort_impl::<L>(|keyed_query| keyed_query.sort_unstable())
}
pub fn sort_by<L: ReadOnlyQueryData + 'w>(
self,
mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
> {
self.sort_impl::<L>(move |keyed_query| {
keyed_query.sort_by(|(key_1, _), (key_2, _)| compare(key_1, key_2));
})
}
pub fn sort_unstable_by<L: ReadOnlyQueryData + 'w>(
self,
mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
> {
self.sort_impl::<L>(move |keyed_query| {
keyed_query.sort_unstable_by(|(key_1, _), (key_2, _)| compare(key_1, key_2));
})
}
pub fn sort_by_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
K: Ord,
{
self.sort_impl::<L>(move |keyed_query| keyed_query.sort_by_key(|(lens, _)| f(lens)))
}
pub fn sort_unstable_by_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
K: Ord,
{
self.sort_impl::<L>(move |keyed_query| {
keyed_query.sort_unstable_by_key(|(lens, _)| f(lens));
})
}
pub fn sort_by_cached_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
K: Ord,
{
self.sort_impl::<L>(move |keyed_query| keyed_query.sort_by_cached_key(|(lens, _)| f(lens)))
}
fn sort_impl<L: ReadOnlyQueryData + 'w>(
self,
f: impl FnOnce(&mut Vec<(L::Item<'_, '_>, NeutralOrd<Entity>)>),
) -> QuerySortedIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
> {
if !self.cursor.archetype_entities.is_empty() || !self.cursor.table_entities.is_empty() {
panic!("it is not valid to call sort() after next()")
}
let world = self.world;
let query_lens_state = self.query_state.transmute_filtered::<(L, Entity), F>(world);
let query_lens = unsafe { query_lens_state.query_unchecked_manual(world) }.into_iter();
let mut keyed_query: Vec<_> = query_lens
.map(|(key, entity)| (key, NeutralOrd(entity)))
.collect();
f(&mut keyed_query);
let entity_iter = keyed_query
.into_iter()
.map(|(.., entity)| entity.0)
.collect::<Vec<_>>()
.into_iter();
unsafe {
QuerySortedIter::new(
world,
self.query_state,
entity_iter,
world.last_change_tick(),
world.change_tick(),
)
}
}
}
impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for QueryIter<'w, 's, D, F> {
type Item = D::Item<'w, 's>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
unsafe {
self.cursor
.next(self.tables, self.archetypes, self.query_state)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let max_size = self.cursor.max_remaining(self.tables, self.archetypes);
let archetype_query = D::IS_ARCHETYPAL && F::IS_ARCHETYPAL;
let min_size = if archetype_query { max_size } else { 0 };
(min_size as usize, Some(max_size as usize))
}
#[inline]
fn fold<B, Func>(mut self, init: B, mut func: Func) -> B
where
Func: FnMut(B, Self::Item) -> B,
{
let mut accum = init;
while self.cursor.current_row != self.cursor.current_len {
let Some(item) = self.next() else { break };
accum = func(accum, item);
}
for id in self.cursor.storage_id_iter.clone().copied() {
accum = unsafe { self.fold_over_storage_range(accum, &mut func, id, None) };
}
accum
}
}
impl<'w, 's, D: QueryData, F: QueryFilter> FusedIterator for QueryIter<'w, 's, D, F> {}
unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator for QueryIter<'w, 's, Entity, F> {}
unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator for QueryIter<'w, 's, EntityRef<'_>, F> {}
unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator for QueryIter<'w, 's, EntityMut<'_>, F> {}
unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator
for QueryIter<'w, 's, FilteredEntityRef<'_, '_>, F>
{
}
unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator
for QueryIter<'w, 's, FilteredEntityMut<'_, '_>, F>
{
}
unsafe impl<'w, 's, F: QueryFilter, B: Bundle> EntitySetIterator
for QueryIter<'w, 's, EntityRefExcept<'_, '_, B>, F>
{
}
unsafe impl<'w, 's, F: QueryFilter, B: Bundle> EntitySetIterator
for QueryIter<'w, 's, EntityMutExcept<'_, '_, B>, F>
{
}
impl<'w, 's, D: QueryData, F: QueryFilter> Debug for QueryIter<'w, 's, D, F> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QueryIter").finish()
}
}
impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter> Clone for QueryIter<'w, 's, D, F> {
fn clone(&self) -> Self {
self.remaining()
}
}
pub struct QuerySortedIter<'w, 's, D: QueryData, F: QueryFilter, I>
where
I: Iterator<Item = Entity>,
{
entity_iter: I,
entities: &'w Entities,
tables: &'w Tables,
archetypes: &'w Archetypes,
fetch: D::Fetch<'w>,
query_state: &'s QueryState<D, F>,
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> QuerySortedIter<'w, 's, D, F, I>
where
I: Iterator<Item = Entity>,
{
pub(crate) unsafe fn new<EntityList: IntoIterator<IntoIter = I>>(
world: UnsafeWorldCell<'w>,
query_state: &'s QueryState<D, F>,
entity_list: EntityList,
last_run: Tick,
this_run: Tick,
) -> QuerySortedIter<'w, 's, D, F, I> {
let fetch = D::init_fetch(world, &query_state.fetch_state, last_run, this_run);
QuerySortedIter {
query_state,
entities: world.entities(),
archetypes: world.archetypes(),
tables: &world.storages().tables,
fetch,
entity_iter: entity_list.into_iter(),
}
}
#[inline(always)]
unsafe fn fetch_next(&mut self, entity: Entity) -> Option<D::Item<'w, 's>> {
let (location, archetype, table);
unsafe {
location = self.entities.get_spawned(entity).debug_checked_unwrap();
archetype = self
.archetypes
.get(location.archetype_id)
.debug_checked_unwrap();
table = self.tables.get(location.table_id).debug_checked_unwrap();
}
unsafe {
D::set_archetype(
&mut self.fetch,
&self.query_state.fetch_state,
archetype,
table,
);
}
unsafe {
D::fetch(
&self.query_state.fetch_state,
&mut self.fetch,
entity,
location.table_row,
)
}
}
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> Iterator
for QuerySortedIter<'w, 's, D, F, I>
where
I: Iterator<Item = Entity>,
{
type Item = D::Item<'w, 's>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
while let Some(entity) = self.entity_iter.next() {
if let Some(item) = unsafe { self.fetch_next(entity) } {
return Some(item);
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (min_size, max_size) = self.entity_iter.size_hint();
let archetype_query = D::IS_ARCHETYPAL;
let min_size = if archetype_query { min_size } else { 0 };
(min_size, max_size)
}
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> DoubleEndedIterator
for QuerySortedIter<'w, 's, D, F, I>
where
I: DoubleEndedIterator<Item = Entity>,
{
#[inline(always)]
fn next_back(&mut self) -> Option<Self::Item> {
while let Some(entity) = self.entity_iter.next_back() {
if let Some(item) = unsafe { self.fetch_next(entity) } {
return Some(item);
}
}
None
}
}
impl<D: ArchetypeQueryData, F: QueryFilter, I: ExactSizeIterator<Item = Entity>> ExactSizeIterator
for QuerySortedIter<'_, '_, D, F, I>
{
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> FusedIterator
for QuerySortedIter<'w, 's, D, F, I>
where
I: FusedIterator<Item = Entity>,
{
}
unsafe impl<'w, 's, F: QueryFilter, I: Iterator<Item = Entity>> EntitySetIterator
for QuerySortedIter<'w, 's, Entity, F, I>
{
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item = Entity>> Debug
for QuerySortedIter<'w, 's, D, F, I>
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QuerySortedIter").finish()
}
}
pub struct QueryManyIter<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
{
world: UnsafeWorldCell<'w>,
entity_iter: I,
entities: &'w Entities,
tables: &'w Tables,
archetypes: &'w Archetypes,
fetch: D::Fetch<'w>,
filter: F::Fetch<'w>,
query_state: &'s QueryState<D, F>,
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
QueryManyIter<'w, 's, D, F, I>
{
pub(crate) unsafe fn new<EntityList: IntoIterator<IntoIter = I>>(
world: UnsafeWorldCell<'w>,
query_state: &'s QueryState<D, F>,
entity_list: EntityList,
last_run: Tick,
this_run: Tick,
) -> QueryManyIter<'w, 's, D, F, I> {
let fetch = D::init_fetch(world, &query_state.fetch_state, last_run, this_run);
let filter = F::init_fetch(world, &query_state.filter_state, last_run, this_run);
QueryManyIter {
world,
query_state,
entities: world.entities(),
archetypes: world.archetypes(),
tables: &world.storages().tables,
fetch,
filter,
entity_iter: entity_list.into_iter(),
}
}
#[inline(always)]
unsafe fn fetch_next_aliased_unchecked(
entity_iter: impl Iterator<Item: EntityEquivalent>,
entities: &'w Entities,
tables: &'w Tables,
archetypes: &'w Archetypes,
fetch: &mut D::Fetch<'w>,
filter: &mut F::Fetch<'w>,
query_state: &'s QueryState<D, F>,
) -> Option<D::Item<'w, 's>> {
for entity_borrow in entity_iter {
let entity = entity_borrow.entity();
let Ok(location) = entities.get_spawned(entity) else {
continue;
};
if !query_state
.matched_archetypes
.contains(location.archetype_id.index())
{
continue;
}
let archetype = archetypes.get(location.archetype_id).debug_checked_unwrap();
let table = tables.get(location.table_id).debug_checked_unwrap();
unsafe {
D::set_archetype(fetch, &query_state.fetch_state, archetype, table);
}
unsafe {
F::set_archetype(filter, &query_state.filter_state, archetype, table);
}
if unsafe {
F::filter_fetch(
&query_state.filter_state,
filter,
entity,
location.table_row,
)
} {
let item = unsafe {
D::fetch(&query_state.fetch_state, fetch, entity, location.table_row)
};
if let Some(item) = item {
return Some(item);
}
}
}
None
}
#[inline(always)]
pub fn fetch_next(&mut self) -> Option<D::Item<'_, 's>> {
unsafe {
Self::fetch_next_aliased_unchecked(
&mut self.entity_iter,
self.entities,
self.tables,
self.archetypes,
&mut self.fetch,
&mut self.filter,
self.query_state,
)
.map(D::shrink)
}
}
pub fn sort<L: ReadOnlyQueryData + 'w>(
self,
) -> QuerySortedManyIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
for<'lw, 'ls> L::Item<'lw, 'ls>: Ord,
{
self.sort_impl::<L>(|keyed_query| keyed_query.sort())
}
pub fn sort_unstable<L: ReadOnlyQueryData + 'w>(
self,
) -> QuerySortedManyIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
for<'lw, 'ls> L::Item<'lw, 'ls>: Ord,
{
self.sort_impl::<L>(|keyed_query| keyed_query.sort_unstable())
}
pub fn sort_by<L: ReadOnlyQueryData + 'w>(
self,
mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedManyIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
> {
self.sort_impl::<L>(move |keyed_query| {
keyed_query.sort_by(|(key_1, _), (key_2, _)| compare(key_1, key_2));
})
}
pub fn sort_unstable_by<L: ReadOnlyQueryData + 'w>(
self,
mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering,
) -> QuerySortedManyIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
> {
self.sort_impl::<L>(move |keyed_query| {
keyed_query.sort_unstable_by(|(key_1, _), (key_2, _)| compare(key_1, key_2));
})
}
pub fn sort_by_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedManyIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
K: Ord,
{
self.sort_impl::<L>(move |keyed_query| keyed_query.sort_by_key(|(lens, _)| f(lens)))
}
pub fn sort_unstable_by_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedManyIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
K: Ord,
{
self.sort_impl::<L>(move |keyed_query| {
keyed_query.sort_unstable_by_key(|(lens, _)| f(lens));
})
}
pub fn sort_by_cached_key<L: ReadOnlyQueryData + 'w, K>(
self,
mut f: impl FnMut(&L::Item<'_, '_>) -> K,
) -> QuerySortedManyIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
>
where
K: Ord,
{
self.sort_impl::<L>(move |keyed_query| keyed_query.sort_by_cached_key(|(lens, _)| f(lens)))
}
fn sort_impl<L: ReadOnlyQueryData + 'w>(
self,
f: impl FnOnce(&mut Vec<(L::Item<'_, '_>, NeutralOrd<Entity>)>),
) -> QuerySortedManyIter<
'w,
's,
D,
F,
impl ExactSizeIterator<Item = Entity> + DoubleEndedIterator + FusedIterator + 'w,
> {
let world = self.world;
let query_lens_state = self.query_state.transmute_filtered::<(L, Entity), F>(world);
let query_lens = unsafe { query_lens_state.query_unchecked_manual(world) }
.iter_many_inner(self.entity_iter);
let mut keyed_query: Vec<_> = query_lens
.map(|(key, entity)| (key, NeutralOrd(entity)))
.collect();
f(&mut keyed_query);
let entity_iter = keyed_query
.into_iter()
.map(|(.., entity)| entity.0)
.collect::<Vec<_>>()
.into_iter();
unsafe {
QuerySortedManyIter::new(
world,
self.query_state,
entity_iter,
world.last_change_tick(),
world.change_tick(),
)
}
}
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator<Item: EntityEquivalent>>
QueryManyIter<'w, 's, D, F, I>
{
#[inline(always)]
pub fn fetch_next_back(&mut self) -> Option<D::Item<'_, 's>> {
unsafe {
Self::fetch_next_aliased_unchecked(
self.entity_iter.by_ref().rev(),
self.entities,
self.tables,
self.archetypes,
&mut self.fetch,
&mut self.filter,
self.query_state,
)
.map(D::shrink)
}
}
}
impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>> Iterator
for QueryManyIter<'w, 's, D, F, I>
{
type Item = D::Item<'w, 's>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
unsafe {
Self::fetch_next_aliased_unchecked(
&mut self.entity_iter,
self.entities,
self.tables,
self.archetypes,
&mut self.fetch,
&mut self.filter,
self.query_state,
)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (_, max_size) = self.entity_iter.size_hint();
(0, max_size)
}
}
impl<
'w,
's,
D: ReadOnlyQueryData,
F: QueryFilter,
I: DoubleEndedIterator<Item: EntityEquivalent>,
> DoubleEndedIterator for QueryManyIter<'w, 's, D, F, I>
{
#[inline(always)]
fn next_back(&mut self) -> Option<Self::Item> {
unsafe {
Self::fetch_next_aliased_unchecked(
self.entity_iter.by_ref().rev(),
self.entities,
self.tables,
self.archetypes,
&mut self.fetch,
&mut self.filter,
self.query_state,
)
}
}
}
impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>>
FusedIterator for QueryManyIter<'w, 's, D, F, I>
{
}
unsafe impl<'w, 's, F: QueryFilter, I: EntitySetIterator> EntitySetIterator
for QueryManyIter<'w, 's, Entity, F, I>
{
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item: EntityEquivalent>> Debug
for QueryManyIter<'w, 's, D, F, I>
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QueryManyIter").finish()
}
}
pub struct QueryManyUniqueIter<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator>(
QueryManyIter<'w, 's, D, F, I>,
);
impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator>
QueryManyUniqueIter<'w, 's, D, F, I>
{
pub(crate) unsafe fn new<EntityList: EntitySet<IntoIter = I>>(
world: UnsafeWorldCell<'w>,
query_state: &'s QueryState<D, F>,
entity_list: EntityList,
last_run: Tick,
this_run: Tick,
) -> QueryManyUniqueIter<'w, 's, D, F, I> {
QueryManyUniqueIter(QueryManyIter::new(
world,
query_state,
entity_list,
last_run,
this_run,
))
}
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> Iterator
for QueryManyUniqueIter<'w, 's, D, F, I>
{
type Item = D::Item<'w, 's>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
unsafe {
QueryManyIter::<'w, 's, D, F, I>::fetch_next_aliased_unchecked(
&mut self.0.entity_iter,
self.0.entities,
self.0.tables,
self.0.archetypes,
&mut self.0.fetch,
&mut self.0.filter,
self.0.query_state,
)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (_, max_size) = self.0.entity_iter.size_hint();
(0, max_size)
}
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> FusedIterator
for QueryManyUniqueIter<'w, 's, D, F, I>
{
}
unsafe impl<'w, 's, F: QueryFilter, I: EntitySetIterator> EntitySetIterator
for QueryManyUniqueIter<'w, 's, Entity, F, I>
{
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> Debug
for QueryManyUniqueIter<'w, 's, D, F, I>
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QueryManyUniqueIter").finish()
}
}
pub struct QuerySortedManyIter<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item = Entity>> {
entity_iter: I,
entities: &'w Entities,
tables: &'w Tables,
archetypes: &'w Archetypes,
fetch: D::Fetch<'w>,
query_state: &'s QueryState<D, F>,
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item = Entity>>
QuerySortedManyIter<'w, 's, D, F, I>
{
pub(crate) unsafe fn new<EntityList: IntoIterator<IntoIter = I>>(
world: UnsafeWorldCell<'w>,
query_state: &'s QueryState<D, F>,
entity_list: EntityList,
last_run: Tick,
this_run: Tick,
) -> QuerySortedManyIter<'w, 's, D, F, I> {
let fetch = D::init_fetch(world, &query_state.fetch_state, last_run, this_run);
QuerySortedManyIter {
query_state,
entities: world.entities(),
archetypes: world.archetypes(),
tables: &world.storages().tables,
fetch,
entity_iter: entity_list.into_iter(),
}
}
#[inline(always)]
unsafe fn fetch_next_aliased_unchecked(&mut self, entity: Entity) -> Option<D::Item<'w, 's>> {
let (location, archetype, table);
unsafe {
location = self.entities.get_spawned(entity).debug_checked_unwrap();
archetype = self
.archetypes
.get(location.archetype_id)
.debug_checked_unwrap();
table = self.tables.get(location.table_id).debug_checked_unwrap();
}
unsafe {
D::set_archetype(
&mut self.fetch,
&self.query_state.fetch_state,
archetype,
table,
);
}
unsafe {
D::fetch(
&self.query_state.fetch_state,
&mut self.fetch,
entity,
location.table_row,
)
}
}
#[inline(always)]
pub fn fetch_next(&mut self) -> Option<D::Item<'_, 's>> {
while let Some(entity) = self.entity_iter.next() {
if let Some(item) = unsafe { self.fetch_next_aliased_unchecked(entity) } {
return Some(D::shrink(item));
}
}
None
}
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator<Item = Entity>>
QuerySortedManyIter<'w, 's, D, F, I>
{
#[inline(always)]
pub fn fetch_next_back(&mut self) -> Option<D::Item<'_, 's>> {
while let Some(entity) = self.entity_iter.next_back() {
if let Some(item) = unsafe { self.fetch_next_aliased_unchecked(entity) } {
return Some(D::shrink(item));
}
}
None
}
}
impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: Iterator<Item = Entity>> Iterator
for QuerySortedManyIter<'w, 's, D, F, I>
{
type Item = D::Item<'w, 's>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
while let Some(entity) = self.entity_iter.next() {
if let Some(item) = unsafe { self.fetch_next_aliased_unchecked(entity) } {
return Some(D::shrink(item));
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (min_size, max_size) = self.entity_iter.size_hint();
let archetype_query = D::IS_ARCHETYPAL;
let min_size = if archetype_query { min_size } else { 0 };
(min_size, max_size)
}
}
impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: DoubleEndedIterator<Item = Entity>>
DoubleEndedIterator for QuerySortedManyIter<'w, 's, D, F, I>
{
#[inline(always)]
fn next_back(&mut self) -> Option<Self::Item> {
while let Some(entity) = self.entity_iter.next_back() {
if let Some(item) = unsafe { self.fetch_next_aliased_unchecked(entity) } {
return Some(D::shrink(item));
}
}
None
}
}
impl<
D: ReadOnlyQueryData + ArchetypeQueryData,
F: QueryFilter,
I: ExactSizeIterator<Item = Entity>,
> ExactSizeIterator for QuerySortedManyIter<'_, '_, D, F, I>
{
}
impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator<Item = Entity>> Debug
for QuerySortedManyIter<'w, 's, D, F, I>
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QuerySortedManyIter").finish()
}
}
pub struct QueryCombinationIter<'w, 's, D: QueryData, F: QueryFilter, const K: usize> {
tables: &'w Tables,
archetypes: &'w Archetypes,
query_state: &'s QueryState<D, F>,
cursors: [QueryIterationCursor<'w, 's, D, F>; K],
}
impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter<'w, 's, D, F, K> {
pub(crate) unsafe fn new(
world: UnsafeWorldCell<'w>,
query_state: &'s QueryState<D, F>,
last_run: Tick,
this_run: Tick,
) -> Self {
assert!(K != 0, "K should not equal to zero");
let mut array: MaybeUninit<[QueryIterationCursor<'w, 's, D, F>; K]> = MaybeUninit::uninit();
let ptr = array
.as_mut_ptr()
.cast::<QueryIterationCursor<'w, 's, D, F>>();
ptr.write(QueryIterationCursor::init(
world,
query_state,
last_run,
this_run,
));
for slot in (1..K).map(|offset| ptr.add(offset)) {
slot.write(QueryIterationCursor::init_empty(
world,
query_state,
last_run,
this_run,
));
}
QueryCombinationIter {
query_state,
tables: unsafe { &world.storages().tables },
archetypes: world.archetypes(),
cursors: array.assume_init(),
}
}
#[inline]
unsafe fn fetch_next_aliased_unchecked(&mut self) -> Option<[D::Item<'w, 's>; K]> {
'outer: for i in (0..K).rev() {
match self.cursors[i].next(self.tables, self.archetypes, self.query_state) {
Some(_) => {
for j in (i + 1)..K {
self.cursors[j] = self.cursors[j - 1].clone();
match self.cursors[j].next(self.tables, self.archetypes, self.query_state) {
Some(_) => {}
None if i > 0 => continue 'outer,
None => return None,
}
}
break;
}
None if i > 0 => continue,
None => return None,
}
}
let mut values = MaybeUninit::<[D::Item<'w, 's>; K]>::uninit();
let ptr = values.as_mut_ptr().cast::<D::Item<'w, 's>>();
for (offset, cursor) in self.cursors.iter_mut().enumerate() {
ptr.add(offset)
.write(cursor.peek_last(self.query_state).unwrap());
}
Some(values.assume_init())
}
#[inline]
pub fn fetch_next(&mut self) -> Option<[D::Item<'_, 's>; K]> {
unsafe {
self.fetch_next_aliased_unchecked()
.map(|array| array.map(D::shrink))
}
}
}
impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, const K: usize> Iterator
for QueryCombinationIter<'w, 's, D, F, K>
{
type Item = [D::Item<'w, 's>; K];
#[inline]
fn next(&mut self) -> Option<Self::Item> {
unsafe { QueryCombinationIter::fetch_next_aliased_unchecked(self) }
}
fn size_hint(&self) -> (usize, Option<usize>) {
fn choose(n: usize, k: usize) -> Option<usize> {
if k > n || n == 0 {
return Some(0);
}
let k = k.min(n - k);
let ks = 1..=k;
let ns = (n - k + 1..=n).rev();
ks.zip(ns)
.try_fold(1_usize, |acc, (k, n)| Some(acc.checked_mul(n)? / k))
}
let max_combinations = self
.cursors
.iter()
.enumerate()
.try_fold(0, |acc, (i, cursor)| {
let n = cursor.max_remaining(self.tables, self.archetypes);
Some(acc + choose(n as usize, K - i)?)
});
let archetype_query = D::IS_ARCHETYPAL && F::IS_ARCHETYPAL;
let known_max = max_combinations.unwrap_or(usize::MAX);
let min_combinations = if archetype_query { known_max } else { 0 };
(min_combinations, max_combinations)
}
}
impl<'w, 's, D: ArchetypeQueryData, F: ArchetypeFilter> ExactSizeIterator
for QueryIter<'w, 's, D, F>
{
fn len(&self) -> usize {
self.size_hint().0
}
}
impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, const K: usize> FusedIterator
for QueryCombinationIter<'w, 's, D, F, K>
{
}
impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> Debug
for QueryCombinationIter<'w, 's, D, F, K>
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QueryCombinationIter").finish()
}
}
struct QueryIterationCursor<'w, 's, D: QueryData, F: QueryFilter> {
is_dense: bool,
storage_id_iter: core::slice::Iter<'s, StorageId>,
table_entities: &'w [Entity],
archetype_entities: &'w [ArchetypeEntity],
fetch: D::Fetch<'w>,
filter: F::Fetch<'w>,
current_len: u32,
current_row: u32,
}
impl<D: QueryData, F: QueryFilter> Clone for QueryIterationCursor<'_, '_, D, F> {
fn clone(&self) -> Self {
Self {
is_dense: self.is_dense,
storage_id_iter: self.storage_id_iter.clone(),
table_entities: self.table_entities,
archetype_entities: self.archetype_entities,
fetch: self.fetch.clone(),
filter: self.filter.clone(),
current_len: self.current_len,
current_row: self.current_row,
}
}
}
impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
unsafe fn init_empty(
world: UnsafeWorldCell<'w>,
query_state: &'s QueryState<D, F>,
last_run: Tick,
this_run: Tick,
) -> Self {
QueryIterationCursor {
storage_id_iter: [].iter(),
..Self::init(world, query_state, last_run, this_run)
}
}
unsafe fn init(
world: UnsafeWorldCell<'w>,
query_state: &'s QueryState<D, F>,
last_run: Tick,
this_run: Tick,
) -> Self {
let fetch = D::init_fetch(world, &query_state.fetch_state, last_run, this_run);
let filter = F::init_fetch(world, &query_state.filter_state, last_run, this_run);
QueryIterationCursor {
fetch,
filter,
table_entities: &[],
archetype_entities: &[],
storage_id_iter: query_state.matched_storage_ids.iter(),
is_dense: query_state.is_dense,
current_len: 0,
current_row: 0,
}
}
fn reborrow(&mut self) -> QueryIterationCursor<'_, 's, D, F> {
QueryIterationCursor {
is_dense: self.is_dense,
fetch: D::shrink_fetch(self.fetch.clone()),
filter: F::shrink_fetch(self.filter.clone()),
table_entities: self.table_entities,
archetype_entities: self.archetype_entities,
storage_id_iter: self.storage_id_iter.clone(),
current_len: self.current_len,
current_row: self.current_row,
}
}
#[inline]
unsafe fn peek_last(&mut self, query_state: &'s QueryState<D, F>) -> Option<D::Item<'w, 's>> {
if self.current_row > 0 {
let index = self.current_row - 1;
if self.is_dense {
let entity = unsafe { self.table_entities.get_unchecked(index as usize) };
unsafe {
D::fetch(
&query_state.fetch_state,
&mut self.fetch,
*entity,
TableRow::new(NonMaxU32::new_unchecked(index)),
)
}
} else {
let archetype_entity =
unsafe { self.archetype_entities.get_unchecked(index as usize) };
unsafe {
D::fetch(
&query_state.fetch_state,
&mut self.fetch,
archetype_entity.id(),
archetype_entity.table_row(),
)
}
}
} else {
None
}
}
fn max_remaining(&self, tables: &'w Tables, archetypes: &'w Archetypes) -> u32 {
let ids = self.storage_id_iter.clone();
let remaining_matched: u32 = if self.is_dense {
unsafe { ids.map(|id| tables[id.table_id].entity_count()).sum() }
} else {
unsafe { ids.map(|id| archetypes[id.archetype_id].len()).sum() }
};
remaining_matched + self.current_len - self.current_row
}
#[inline(always)]
unsafe fn next(
&mut self,
tables: &'w Tables,
archetypes: &'w Archetypes,
query_state: &'s QueryState<D, F>,
) -> Option<D::Item<'w, 's>> {
if self.is_dense {
loop {
if self.current_row == self.current_len {
let table_id = self.storage_id_iter.next()?.table_id;
let table = tables.get(table_id).debug_checked_unwrap();
if table.is_empty() {
continue;
}
unsafe {
D::set_table(&mut self.fetch, &query_state.fetch_state, table);
F::set_table(&mut self.filter, &query_state.filter_state, table);
}
self.table_entities = table.entities();
self.current_len = table.entity_count();
self.current_row = 0;
}
let entity =
unsafe { self.table_entities.get_unchecked(self.current_row as usize) };
let row = unsafe { TableRow::new(NonMaxU32::new_unchecked(self.current_row)) };
self.current_row += 1;
if !F::filter_fetch(&query_state.filter_state, &mut self.filter, *entity, row) {
continue;
}
let item =
unsafe { D::fetch(&query_state.fetch_state, &mut self.fetch, *entity, row) };
if let Some(item) = item {
return Some(item);
}
}
} else {
loop {
if self.current_row == self.current_len {
let archetype_id = self.storage_id_iter.next()?.archetype_id;
let archetype = archetypes.get(archetype_id).debug_checked_unwrap();
if archetype.is_empty() {
continue;
}
let table = tables.get(archetype.table_id()).debug_checked_unwrap();
unsafe {
D::set_archetype(
&mut self.fetch,
&query_state.fetch_state,
archetype,
table,
);
F::set_archetype(
&mut self.filter,
&query_state.filter_state,
archetype,
table,
);
}
self.archetype_entities = archetype.entities();
self.current_len = archetype.len();
self.current_row = 0;
}
let archetype_entity = unsafe {
self.archetype_entities
.get_unchecked(self.current_row as usize)
};
self.current_row += 1;
if !F::filter_fetch(
&query_state.filter_state,
&mut self.filter,
archetype_entity.id(),
archetype_entity.table_row(),
) {
continue;
}
let item = unsafe {
D::fetch(
&query_state.fetch_state,
&mut self.fetch,
archetype_entity.id(),
archetype_entity.table_row(),
)
};
if let Some(item) = item {
return Some(item);
}
}
}
}
}
#[derive(Copy, Clone)]
struct NeutralOrd<T>(T);
impl<T> PartialEq for NeutralOrd<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T> Eq for NeutralOrd<T> {}
#[expect(
clippy::non_canonical_partial_ord_impl,
reason = "`PartialOrd` and `Ord` on this struct must only ever return `Ordering::Equal`, so we prefer clarity"
)]
impl<T> PartialOrd for NeutralOrd<T> {
fn partial_cmp(&self, _other: &Self) -> Option<Ordering> {
Some(Ordering::Equal)
}
}
impl<T> Ord for NeutralOrd<T> {
fn cmp(&self, _other: &Self) -> Ordering {
Ordering::Equal
}
}
#[cfg(test)]
#[expect(clippy::print_stdout, reason = "Allowed in tests.")]
mod tests {
use alloc::vec::Vec;
use std::println;
use crate::component::Component;
use crate::entity::Entity;
use crate::prelude::{With, World};
#[derive(Component, Debug, PartialEq, PartialOrd, Clone, Copy)]
struct A(f32);
#[derive(Component, Debug, Eq, PartialEq, Clone, Copy)]
#[component(storage = "SparseSet")]
struct Sparse(usize);
#[derive(Component)]
struct Marker;
#[test]
fn query_iter_sorts() {
let mut world = World::new();
for i in 0..100 {
world.spawn((A(i as f32), Marker));
world.spawn((A(i as f32), Sparse(i), Marker));
world.spawn((Sparse(i), Marker));
}
let mut query = world.query_filtered::<Entity, With<Marker>>();
let sort = query.iter(&world).sort::<Entity>().collect::<Vec<_>>();
assert_eq!(sort.len(), 300);
let sort_unstable = query
.iter(&world)
.sort_unstable::<Entity>()
.collect::<Vec<_>>();
let sort_by = query
.iter(&world)
.sort_by::<Entity>(Ord::cmp)
.collect::<Vec<_>>();
let sort_unstable_by = query
.iter(&world)
.sort_unstable_by::<Entity>(Ord::cmp)
.collect::<Vec<_>>();
let sort_by_key = query
.iter(&world)
.sort_by_key::<Entity, _>(|&e| e)
.collect::<Vec<_>>();
let sort_unstable_by_key = query
.iter(&world)
.sort_unstable_by_key::<Entity, _>(|&e| e)
.collect::<Vec<_>>();
let sort_by_cached_key = query
.iter(&world)
.sort_by_cached_key::<Entity, _>(|&e| e)
.collect::<Vec<_>>();
let mut sort_v2 = query.iter(&world).collect::<Vec<_>>();
sort_v2.sort();
let mut sort_unstable_v2 = query.iter(&world).collect::<Vec<_>>();
sort_unstable_v2.sort_unstable();
let mut sort_by_v2 = query.iter(&world).collect::<Vec<_>>();
sort_by_v2.sort_by(Ord::cmp);
let mut sort_unstable_by_v2 = query.iter(&world).collect::<Vec<_>>();
sort_unstable_by_v2.sort_unstable_by(Ord::cmp);
let mut sort_by_key_v2 = query.iter(&world).collect::<Vec<_>>();
sort_by_key_v2.sort_by_key(|&e| e);
let mut sort_unstable_by_key_v2 = query.iter(&world).collect::<Vec<_>>();
sort_unstable_by_key_v2.sort_unstable_by_key(|&e| e);
let mut sort_by_cached_key_v2 = query.iter(&world).collect::<Vec<_>>();
sort_by_cached_key_v2.sort_by_cached_key(|&e| e);
assert_eq!(sort, sort_v2);
assert_eq!(sort_unstable, sort_unstable_v2);
assert_eq!(sort_by, sort_by_v2);
assert_eq!(sort_unstable_by, sort_unstable_by_v2);
assert_eq!(sort_by_key, sort_by_key_v2);
assert_eq!(sort_unstable_by_key, sort_unstable_by_key_v2);
assert_eq!(sort_by_cached_key, sort_by_cached_key_v2);
}
#[test]
#[should_panic]
fn query_iter_sort_after_next() {
let mut world = World::new();
world.spawn((A(0.),));
world.spawn((A(1.1),));
world.spawn((A(2.22),));
{
let mut query = world.query::<&A>();
let mut iter = query.iter(&world);
println!(
"archetype_entities: {} table_entities: {} current_len: {} current_row: {}",
iter.cursor.archetype_entities.len(),
iter.cursor.table_entities.len(),
iter.cursor.current_len,
iter.cursor.current_row
);
_ = iter.next();
println!(
"archetype_entities: {} table_entities: {} current_len: {} current_row: {}",
iter.cursor.archetype_entities.len(),
iter.cursor.table_entities.len(),
iter.cursor.current_len,
iter.cursor.current_row
);
println!("{}", iter.sort::<Entity>().len());
}
}
#[test]
#[should_panic]
fn query_iter_sort_after_next_dense() {
let mut world = World::new();
world.spawn((Sparse(11),));
world.spawn((Sparse(22),));
world.spawn((Sparse(33),));
{
let mut query = world.query::<&Sparse>();
let mut iter = query.iter(&world);
println!(
"before_next_call: archetype_entities: {} table_entities: {} current_len: {} current_row: {}",
iter.cursor.archetype_entities.len(),
iter.cursor.table_entities.len(),
iter.cursor.current_len,
iter.cursor.current_row
);
_ = iter.next();
println!(
"after_next_call: archetype_entities: {} table_entities: {} current_len: {} current_row: {}",
iter.cursor.archetype_entities.len(),
iter.cursor.table_entities.len(),
iter.cursor.current_len,
iter.cursor.current_row
);
println!("{}", iter.sort::<Entity>().len());
}
}
#[test]
fn empty_query_iter_sort_after_next_does_not_panic() {
let mut world = World::new();
{
let mut query = world.query::<(&A, &Sparse)>();
let mut iter = query.iter(&world);
println!(
"before_next_call: archetype_entities: {} table_entities: {} current_len: {} current_row: {}",
iter.cursor.archetype_entities.len(),
iter.cursor.table_entities.len(),
iter.cursor.current_len,
iter.cursor.current_row
);
_ = iter.next();
println!(
"after_next_call: archetype_entities: {} table_entities: {} current_len: {} current_row: {}",
iter.cursor.archetype_entities.len(),
iter.cursor.table_entities.len(),
iter.cursor.current_len,
iter.cursor.current_row
);
println!("{}", iter.sort::<Entity>().len());
}
}
#[test]
fn query_iter_cursor_state_non_empty_after_next() {
let mut world = World::new();
world.spawn((A(0.), Sparse(11)));
world.spawn((A(1.1), Sparse(22)));
world.spawn((A(2.22), Sparse(33)));
{
let mut query = world.query::<(&A, &Sparse)>();
let mut iter = query.iter(&world);
println!(
"before_next_call: archetype_entities: {} table_entities: {} current_len: {} current_row: {}",
iter.cursor.archetype_entities.len(),
iter.cursor.table_entities.len(),
iter.cursor.current_len,
iter.cursor.current_row
);
assert!(iter.cursor.table_entities.len() | iter.cursor.archetype_entities.len() == 0);
_ = iter.next();
println!(
"after_next_call: archetype_entities: {} table_entities: {} current_len: {} current_row: {}",
iter.cursor.archetype_entities.len(),
iter.cursor.table_entities.len(),
iter.cursor.current_len,
iter.cursor.current_row
);
assert!(
(
iter.cursor.table_entities.len(),
iter.cursor.archetype_entities.len()
) != (0, 0)
);
}
}
#[test]
fn query_iter_many_sorts() {
let mut world = World::new();
let entity_list: &Vec<_> = &world
.spawn_batch([A(0.), A(1.), A(2.), A(3.), A(4.)])
.collect();
let mut query = world.query::<Entity>();
let sort = query
.iter_many(&world, entity_list)
.sort::<Entity>()
.collect::<Vec<_>>();
let sort_unstable = query
.iter_many(&world, entity_list)
.sort_unstable::<Entity>()
.collect::<Vec<_>>();
let sort_by = query
.iter_many(&world, entity_list)
.sort_by::<Entity>(Ord::cmp)
.collect::<Vec<_>>();
let sort_unstable_by = query
.iter_many(&world, entity_list)
.sort_unstable_by::<Entity>(Ord::cmp)
.collect::<Vec<_>>();
let sort_by_key = query
.iter_many(&world, entity_list)
.sort_by_key::<Entity, _>(|&e| e)
.collect::<Vec<_>>();
let sort_unstable_by_key = query
.iter_many(&world, entity_list)
.sort_unstable_by_key::<Entity, _>(|&e| e)
.collect::<Vec<_>>();
let sort_by_cached_key = query
.iter_many(&world, entity_list)
.sort_by_cached_key::<Entity, _>(|&e| e)
.collect::<Vec<_>>();
let mut sort_v2 = query.iter_many(&world, entity_list).collect::<Vec<_>>();
sort_v2.sort();
let mut sort_unstable_v2 = query.iter_many(&world, entity_list).collect::<Vec<_>>();
sort_unstable_v2.sort_unstable();
let mut sort_by_v2 = query.iter_many(&world, entity_list).collect::<Vec<_>>();
sort_by_v2.sort_by(Ord::cmp);
let mut sort_unstable_by_v2 = query.iter_many(&world, entity_list).collect::<Vec<_>>();
sort_unstable_by_v2.sort_unstable_by(Ord::cmp);
let mut sort_by_key_v2 = query.iter_many(&world, entity_list).collect::<Vec<_>>();
sort_by_key_v2.sort_by_key(|&e| e);
let mut sort_unstable_by_key_v2 = query.iter_many(&world, entity_list).collect::<Vec<_>>();
sort_unstable_by_key_v2.sort_unstable_by_key(|&e| e);
let mut sort_by_cached_key_v2 = query.iter_many(&world, entity_list).collect::<Vec<_>>();
sort_by_cached_key_v2.sort_by_cached_key(|&e| e);
assert_eq!(sort, sort_v2);
assert_eq!(sort_unstable, sort_unstable_v2);
assert_eq!(sort_by, sort_by_v2);
assert_eq!(sort_unstable_by, sort_unstable_by_v2);
assert_eq!(sort_by_key, sort_by_key_v2);
assert_eq!(sort_unstable_by_key, sort_unstable_by_key_v2);
assert_eq!(sort_by_cached_key, sort_by_cached_key_v2);
}
#[test]
fn query_iter_many_sort_doesnt_panic_after_next() {
let mut world = World::new();
let entity_list: &Vec<_> = &world
.spawn_batch([A(0.), A(1.), A(2.), A(3.), A(4.)])
.collect();
let mut query = world.query::<Entity>();
let mut iter = query.iter_many(&world, entity_list);
_ = iter.next();
iter.sort::<Entity>();
let mut query_2 = world.query::<&mut A>();
let mut iter_2 = query_2.iter_many_mut(&mut world, entity_list);
_ = iter_2.fetch_next();
iter_2.sort::<Entity>();
}
#[test]
fn query_iter_many_sorts_duplicate_entities_no_ub() {
#[derive(Component, Ord, PartialOrd, Eq, PartialEq)]
struct C(usize);
let mut world = World::new();
let id = world.spawn(C(10)).id();
let mut query_state = world.query::<&mut C>();
{
let mut query = query_state.iter_many_mut(&mut world, [id, id]).sort::<&C>();
while query.fetch_next().is_some() {}
}
{
let mut query = query_state
.iter_many_mut(&mut world, [id, id])
.sort_unstable::<&C>();
while query.fetch_next().is_some() {}
}
{
let mut query = query_state
.iter_many_mut(&mut world, [id, id])
.sort_by::<&C>(|l, r| Ord::cmp(l, r));
while query.fetch_next().is_some() {}
}
{
let mut query = query_state
.iter_many_mut(&mut world, [id, id])
.sort_unstable_by::<&C>(|l, r| Ord::cmp(l, r));
while query.fetch_next().is_some() {}
}
{
let mut query = query_state
.iter_many_mut(&mut world, [id, id])
.sort_by_key::<&C, _>(|d| d.0);
while query.fetch_next().is_some() {}
}
{
let mut query = query_state
.iter_many_mut(&mut world, [id, id])
.sort_unstable_by_key::<&C, _>(|d| d.0);
while query.fetch_next().is_some() {}
}
{
let mut query = query_state
.iter_many_mut(&mut world, [id, id])
.sort_by_cached_key::<&C, _>(|d| d.0);
while query.fetch_next().is_some() {}
}
}
}