use std::{collections::HashMap, marker::PhantomData, ops::Range, slice::Iter};
use filter::{DynamicFilter, EntityFilter, GroupMatcher};
use parking_lot::Mutex;
use view::{DefaultFilter, Fetch, IntoIndexableIter, IntoView, ReadOnlyFetch, View};
use super::world::EntityAccessError;
use crate::internals::{
entity::Entity,
storage::{
archetype::{Archetype, ArchetypeIndex},
component::Component,
group::SubGroup,
},
world::{EntityStore, StorageAccessor, WorldId},
};
pub mod filter;
pub mod view;
pub trait IntoQuery: IntoView + Sized {
fn query() -> Query<Self, <Self::View as DefaultFilter>::Filter>;
}
impl<T: IntoView> IntoQuery for T {
fn query() -> Query<Self, <Self::View as DefaultFilter>::Filter> {
Self::View::validate();
Query {
_view: PhantomData,
filter: Mutex::new(<<Self::View as DefaultFilter>::Filter as Default>::default()),
layout_matches: HashMap::new(),
is_view_filter: true,
}
}
}
#[derive(Debug, Clone)]
pub struct QueryResult<'a> {
index: &'a [ArchetypeIndex],
range: Range<usize>,
is_ordered: bool,
}
impl<'a> QueryResult<'a> {
fn unordered(index: &'a [ArchetypeIndex]) -> Self {
Self {
range: 0..index.len(),
index,
is_ordered: false,
}
}
fn ordered(index: &'a [ArchetypeIndex]) -> Self {
Self {
range: 0..index.len(),
index,
is_ordered: true,
}
}
pub fn index(&self) -> &'a [ArchetypeIndex] {
let (_, slice) = self.index.split_at(self.range.start);
let (slice, _) = slice.split_at(self.range.len());
slice
}
pub fn range(&self) -> &Range<usize> {
&self.range
}
pub fn is_ordered(&self) -> bool {
self.is_ordered
}
pub fn len(&self) -> usize {
self.range.len()
}
pub fn is_empty(&self) -> bool {
self.index().is_empty()
}
#[cfg(feature = "parallel")]
pub(crate) fn split_at(self, index: usize) -> (Self, Self) {
let index = self.range.start + index;
(
Self {
range: self.range.start..index,
index: self.index,
is_ordered: self.is_ordered,
},
Self {
range: index..self.range.end,
index: self.index,
is_ordered: self.is_ordered,
},
)
}
}
#[derive(Debug, Clone)]
enum Cache {
Unordered {
archetypes: Vec<ArchetypeIndex>,
seen: usize,
},
Ordered {
group: usize,
subgroup: SubGroup,
},
}
pub struct Query<V: IntoView, F: EntityFilter = <<V as IntoView>::View as DefaultFilter>::Filter> {
_view: PhantomData<V>,
filter: Mutex<F>,
layout_matches: HashMap<WorldId, Cache>,
is_view_filter: bool,
}
impl<V: IntoView, F: EntityFilter> Default for Query<V, F> {
fn default() -> Self {
Self::new()
}
}
impl<V: IntoView, F: EntityFilter> Query<V, F> {
pub fn new() -> Self {
V::View::validate();
Self {
_view: PhantomData,
filter: Mutex::new(Default::default()),
layout_matches: HashMap::new(),
is_view_filter: true,
}
}
pub fn filter<T: EntityFilter>(self, filter: T) -> Query<V, <F as std::ops::BitAnd<T>>::Output>
where
F: std::ops::BitAnd<T>,
<F as std::ops::BitAnd<T>>::Output: EntityFilter,
{
Query {
_view: self._view,
filter: Mutex::new(self.filter.into_inner() & filter),
layout_matches: HashMap::default(),
is_view_filter: false,
}
}
fn validate_archetype_access(storage: &StorageAccessor, archetypes: &[ArchetypeIndex]) {
for arch in archetypes {
if !storage.can_access_archetype(*arch) {
panic!("query attempted to access archetype which not available in subworld");
}
}
}
fn evaluate_query<'a>(
&'a mut self,
world: &StorageAccessor<'a>,
) -> (&mut Mutex<F>, QueryResult<'a>) {
let cache = self.layout_matches.entry(world.id()).or_insert_with(|| {
let cache = if F::can_match_group() {
let components = F::group_components();
components
.get(0)
.and_then(|t| world.group(*t))
.map(|(i, g)| (i, g.exact_match(&components)))
.and_then(|(group, subgroup)| {
subgroup.map(|subgroup| Cache::Ordered { group, subgroup })
})
} else {
None
};
cache.unwrap_or_else(|| {
Cache::Unordered {
archetypes: Vec::new(),
seen: 0,
}
})
});
let filter = self.filter.get_mut();
let result = match cache {
Cache::Unordered { archetypes, seen } => {
for archetype in world.layout_index().search_from(&*filter, *seen) {
archetypes.push(archetype);
}
*seen = world.archetypes().len();
QueryResult::unordered(archetypes.as_slice())
}
Cache::Ordered { group, subgroup } => {
let archetypes = &world.groups()[*group][*subgroup];
QueryResult::ordered(archetypes)
}
};
Self::validate_archetype_access(world, result.index());
(&mut self.filter, result)
}
pub(crate) fn find_archetypes<'a, T: EntityStore + 'a>(
&'a mut self,
world: &'a T,
) -> &'a [ArchetypeIndex] {
let accessor = world.get_component_storage::<V::View>().unwrap();
let (_, result) = self.evaluate_query(&accessor);
result.index()
}
pub unsafe fn get_unchecked<'query, 'world, T>(
&'query mut self,
world: &'world T,
entity: Entity,
) -> Result<<V::View as View<'world>>::Element, EntityAccessError>
where
T: EntityStore,
{
let location = world.entry_ref(entity)?.location();
let accessor = world.get_component_storage::<V::View>().unwrap();
let arch_slice = [location.archetype()];
let arch_slice_ref: &[ArchetypeIndex] = &arch_slice;
let arch_slice_ref = std::mem::transmute(arch_slice_ref);
let result = QueryResult::unordered(arch_slice_ref);
let mut fetch = if let Some(Some(fetch)) =
<V::View as View<'world>>::fetch(accessor.components(), accessor.archetypes(), result)
.next()
{
fetch
} else {
return Err(EntityAccessError::EntityNotFound);
};
if !self.is_view_filter {
let (_, result) = self.evaluate_query(&accessor);
if !result.index().contains(&location.archetype()) {
return Err(EntityAccessError::AccessDenied);
}
}
fetch.accepted();
let view = ChunkView::new(&accessor.archetypes()[location.archetype()], fetch);
let mut iter = view.into_iter();
use crate::internals::iter::indexed::TrustedRandomAccess;
Ok(iter.get_unchecked(location.component().0))
}
pub fn get_mut<'query, 'world, T>(
&'query mut self,
world: &'world mut T,
entity: Entity,
) -> Result<<V::View as View<'world>>::Element, EntityAccessError>
where
T: EntityStore,
{
unsafe { self.get_unchecked(world, entity) }
}
pub fn get<'query, 'world, T>(
&'query mut self,
world: &'world T,
entity: Entity,
) -> Result<<V::View as View<'world>>::Element, EntityAccessError>
where
T: EntityStore,
<V::View as View<'world>>::Fetch: ReadOnlyFetch,
{
unsafe { self.get_unchecked(world, entity) }
}
pub unsafe fn iter_chunks_unchecked<'query, 'world, T: EntityStore>(
&'query mut self,
world: &'world T,
) -> ChunkIter<'world, 'query, V::View, F> {
let accessor = world.get_component_storage::<V::View>().unwrap();
let (_, result) = self.evaluate_query(&accessor);
let result = std::mem::transmute::<QueryResult<'_>, QueryResult<'world>>(result);
let indices = std::mem::transmute::<Iter<'_, ArchetypeIndex>, Iter<'query, ArchetypeIndex>>(
result.index.iter(),
);
let fetch =
<V::View as View<'world>>::fetch(accessor.components(), accessor.archetypes(), result);
let filter = self.filter.get_mut();
filter.prepare(world.id());
ChunkIter {
inner: fetch,
filter,
archetypes: accessor.archetypes(),
max_count: indices.len(),
indices,
}
}
#[cfg(feature = "parallel")]
pub unsafe fn par_iter_chunks_unchecked<'a, T: EntityStore>(
&'a mut self,
world: &'a T,
) -> par_iter::ParChunkIter<'a, V::View, F> {
let accessor = world.get_component_storage::<V::View>().unwrap();
let (filter, result) = self.evaluate_query(&accessor);
par_iter::ParChunkIter::new(accessor, result, filter)
}
#[inline]
pub fn iter_chunks_mut<'query, 'world, T: EntityStore>(
&'query mut self,
world: &'world mut T,
) -> ChunkIter<'world, 'query, V::View, F> {
unsafe { self.iter_chunks_unchecked(world) }
}
#[cfg(feature = "parallel")]
#[inline]
pub fn par_iter_chunks_mut<'a, T: EntityStore>(
&'a mut self,
world: &'a mut T,
) -> par_iter::ParChunkIter<'a, V::View, F> {
unsafe { self.par_iter_chunks_unchecked(world) }
}
#[inline]
pub fn iter_chunks<'query, 'world, T: EntityStore>(
&'query mut self,
world: &'world T,
) -> ChunkIter<'world, 'query, V::View, F>
where
<V::View as View<'world>>::Fetch: ReadOnlyFetch,
{
unsafe { self.iter_chunks_unchecked(world) }
}
#[cfg(feature = "parallel")]
#[inline]
pub fn par_iter_chunks<'a, T: EntityStore>(
&'a mut self,
world: &'a T,
) -> par_iter::ParChunkIter<'a, V::View, F>
where
<V::View as View<'a>>::Fetch: ReadOnlyFetch,
{
unsafe { self.par_iter_chunks_unchecked(world) }
}
#[inline]
pub unsafe fn iter_unchecked<'query, 'world, T: EntityStore>(
&'query mut self,
world: &'world T,
) -> std::iter::Flatten<ChunkIter<'world, 'query, V::View, F>> {
self.iter_chunks_unchecked(world).flatten()
}
#[cfg(feature = "parallel")]
#[inline]
pub unsafe fn par_iter_unchecked<'a, T: EntityStore>(
&'a mut self,
world: &'a T,
) -> rayon::iter::Flatten<par_iter::ParChunkIter<'a, V::View, F>> {
use rayon::iter::ParallelIterator;
self.par_iter_chunks_unchecked(world).flatten()
}
#[inline]
pub fn iter_mut<'query, 'world, T: EntityStore>(
&'query mut self,
world: &'world mut T,
) -> std::iter::Flatten<ChunkIter<'world, 'query, V::View, F>> {
unsafe { self.iter_unchecked(world) }
}
#[cfg(feature = "parallel")]
#[inline]
pub fn par_iter_mut<'a, T: EntityStore>(
&'a mut self,
world: &'a mut T,
) -> rayon::iter::Flatten<par_iter::ParChunkIter<'a, V::View, F>> {
unsafe { self.par_iter_unchecked(world) }
}
#[inline]
pub fn iter<'query, 'world, T: EntityStore>(
&'query mut self,
world: &'world T,
) -> std::iter::Flatten<ChunkIter<'world, 'query, V::View, F>>
where
<V::View as View<'world>>::Fetch: ReadOnlyFetch,
{
unsafe { self.iter_unchecked(world) }
}
#[cfg(feature = "parallel")]
#[inline]
pub fn par_iter<'a, T: EntityStore>(
&'a mut self,
world: &'a T,
) -> rayon::iter::Flatten<par_iter::ParChunkIter<'a, V::View, F>>
where
<V::View as View<'a>>::Fetch: ReadOnlyFetch,
{
unsafe { self.par_iter_unchecked(world) }
}
#[inline]
pub unsafe fn for_each_chunk_unchecked<'query, 'world, T: EntityStore, Body>(
&'query mut self,
world: &'world T,
mut f: Body,
) where
Body: FnMut(ChunkView<<V::View as View<'world>>::Fetch>),
{
for chunk in self.iter_chunks_unchecked(world) {
f(chunk);
}
}
#[cfg(feature = "parallel")]
#[inline]
pub unsafe fn par_for_each_chunk_unchecked<'a, T: EntityStore, Body>(
&'a mut self,
world: &'a T,
f: Body,
) where
Body: Fn(ChunkView<<V::View as View<'a>>::Fetch>) + Send + Sync,
{
use rayon::iter::ParallelIterator;
self.par_iter_chunks_unchecked(world).for_each(f);
}
#[inline]
pub fn for_each_chunk_mut<'query, 'world, T: EntityStore, Body>(
&'query mut self,
world: &'world mut T,
f: Body,
) where
Body: FnMut(ChunkView<<V::View as View<'world>>::Fetch>),
{
unsafe { self.for_each_chunk_unchecked(world, f) };
}
#[cfg(feature = "parallel")]
#[inline]
pub fn par_for_each_chunk_mut<'a, T: EntityStore, Body>(&'a mut self, world: &'a mut T, f: Body)
where
Body: Fn(ChunkView<<V::View as View<'a>>::Fetch>) + Send + Sync,
{
unsafe { self.par_for_each_chunk_unchecked(world, f) };
}
#[inline]
pub fn for_each_chunk<'query, 'world, T: EntityStore, Body>(
&'query mut self,
world: &'world T,
f: Body,
) where
Body: FnMut(ChunkView<<V::View as View<'world>>::Fetch>),
<V::View as View<'world>>::Fetch: ReadOnlyFetch,
{
unsafe { self.for_each_chunk_unchecked(world, f) };
}
#[cfg(feature = "parallel")]
#[inline]
pub fn par_for_each_chunk<'a, T: EntityStore, Body>(&'a mut self, world: &'a T, f: Body)
where
Body: Fn(ChunkView<<V::View as View<'a>>::Fetch>) + Send + Sync,
<V::View as View<'a>>::Fetch: ReadOnlyFetch,
{
unsafe { self.par_for_each_chunk_unchecked(world, f) };
}
#[inline]
pub unsafe fn for_each_unchecked<'query, 'world, T: EntityStore, Body>(
&'query mut self,
world: &'world T,
mut f: Body,
) where
Body: FnMut(<V::View as View<'world>>::Element),
{
for chunk in self.iter_chunks_unchecked(world) {
for entities in chunk {
f(entities);
}
}
}
#[cfg(feature = "parallel")]
#[inline]
pub unsafe fn par_for_each_unchecked<'a, T: EntityStore, Body>(
&'a mut self,
world: &'a T,
f: Body,
) where
Body: Fn(<V::View as View<'a>>::Element) + Send + Sync,
{
use rayon::iter::ParallelIterator;
self.par_iter_unchecked(world).for_each(&f);
}
#[inline]
pub fn for_each_mut<'query, 'world, T: EntityStore, Body>(
&'query mut self,
world: &'world mut T,
f: Body,
) where
Body: FnMut(<V::View as View<'world>>::Element),
{
unsafe { self.for_each_unchecked(world, f) };
}
#[cfg(feature = "parallel")]
#[inline]
pub fn par_for_each_mut<'a, T: EntityStore, Body>(&'a mut self, world: &'a mut T, f: Body)
where
Body: Fn(<V::View as View<'a>>::Element) + Send + Sync,
{
unsafe { self.par_for_each_unchecked(world, f) };
}
#[inline]
pub fn for_each<'query, 'world, T: EntityStore, Body>(
&'query mut self,
world: &'world T,
f: Body,
) where
Body: FnMut(<V::View as View<'world>>::Element),
<V::View as View<'world>>::Fetch: ReadOnlyFetch,
{
unsafe { self.for_each_unchecked(world, f) };
}
#[cfg(feature = "parallel")]
#[inline]
pub fn par_for_each<'a, T: EntityStore, Body>(&'a mut self, world: &'a T, f: Body)
where
Body: Fn(<V::View as View<'a>>::Element) + Send + Sync,
<V::View as View<'a>>::Fetch: ReadOnlyFetch,
{
unsafe { self.par_for_each_unchecked(world, f) };
}
}
pub struct ChunkView<'a, F: Fetch> {
archetype: &'a Archetype,
fetch: F,
}
impl<'a, F: Fetch> ChunkView<'a, F> {
fn new(archetype: &'a Archetype, fetch: F) -> Self {
Self { archetype, fetch }
}
pub fn archetype(&self) -> &Archetype {
&self.archetype
}
pub fn component_slice<T: Component>(&self) -> Option<&[T]> {
self.fetch.find::<T>()
}
pub fn component_slice_mut<T: Component>(&mut self) -> Option<&mut [T]> {
self.fetch.find_mut::<T>()
}
pub fn into_components(self) -> F::Data {
self.fetch.into_components()
}
pub fn get_components(&self) -> F::Data
where
F: ReadOnlyFetch,
{
self.fetch.get_components()
}
pub fn into_iter_entities(
self,
) -> impl Iterator<Item = (Entity, <F as IntoIndexableIter>::Item)> + 'a
where
<F as IntoIndexableIter>::IntoIter: 'a,
{
let iter = self.fetch.into_indexable_iter();
self.archetype.entities().iter().copied().zip(iter)
}
}
impl<'a, F: Fetch> IntoIterator for ChunkView<'a, F> {
type IntoIter = <F as IntoIndexableIter>::IntoIter;
type Item = <F as IntoIndexableIter>::Item;
fn into_iter(self) -> Self::IntoIter {
self.fetch.into_indexable_iter()
}
}
#[cfg(feature = "parallel")]
impl<'a, F: Fetch> rayon::iter::IntoParallelIterator for ChunkView<'a, F> {
type Iter = crate::internals::iter::indexed::par_iter::Par<<F as IntoIndexableIter>::IntoIter>;
type Item = <<F as IntoIndexableIter>::IntoIter as crate::internals::iter::indexed::TrustedRandomAccess>::Item;
fn into_par_iter(self) -> Self::Iter {
use crate::internals::iter::indexed::par_iter::Par;
Par::new(self.fetch.into_indexable_iter())
}
}
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct ChunkIter<'data, 'index, V, D>
where
V: View<'data>,
D: DynamicFilter + 'index,
{
inner: V::Iter,
indices: Iter<'index, ArchetypeIndex>,
filter: &'index mut D,
archetypes: &'data [Archetype],
max_count: usize,
}
impl<'world, 'query, V, D> Iterator for ChunkIter<'world, 'query, V, D>
where
V: View<'world>,
D: DynamicFilter + 'query,
{
type Item = ChunkView<'world, V::Fetch>;
fn next(&mut self) -> Option<Self::Item> {
for fetch in &mut self.inner {
let mut fetch = fetch.unwrap();
let idx = self.indices.next().unwrap();
if self.filter.matches_archetype(&fetch).is_pass() {
fetch.accepted();
return Some(ChunkView::new(&self.archetypes[*idx], fetch));
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.max_count))
}
}
#[cfg(feature = "parallel")]
pub mod par_iter {
use std::marker::PhantomData;
use rayon::iter::{
plumbing::{bridge_unindexed, Folder, UnindexedConsumer, UnindexedProducer},
ParallelIterator,
};
use super::*;
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Iter<'world, 'query, V, D>
where
V: View<'world>,
D: DynamicFilter + 'query,
{
inner: V::Iter,
indices: std::slice::Iter<'query, ArchetypeIndex>,
filter: &'query Mutex<D>,
archetypes: &'world [Archetype],
max_count: usize,
}
impl<'world, 'query, V, D> Iterator for Iter<'world, 'query, V, D>
where
V: View<'world>,
D: DynamicFilter + 'query,
{
type Item = ChunkView<'world, V::Fetch>;
fn next(&mut self) -> Option<Self::Item> {
let mut filter = self.filter.lock();
for fetch in &mut self.inner {
let mut fetch = fetch.unwrap();
let idx = self.indices.next().unwrap();
if filter.matches_archetype(&fetch).is_pass() {
fetch.accepted();
return Some(ChunkView::new(&self.archetypes[*idx], fetch));
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.max_count))
}
}
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct ParChunkIter<'a, V, D>
where
V: View<'a>,
D: DynamicFilter + 'a,
{
world: StorageAccessor<'a>,
result: QueryResult<'a>,
filter: &'a Mutex<D>,
_view: PhantomData<V>,
}
impl<'a, V, D> ParChunkIter<'a, V, D>
where
V: View<'a>,
D: DynamicFilter + 'a,
{
pub(super) fn new(
world: StorageAccessor<'a>,
result: QueryResult<'a>,
filter: &'a Mutex<D>,
) -> Self {
Self {
world,
result,
filter,
_view: PhantomData,
}
}
}
unsafe impl<'a, V, D> Send for ParChunkIter<'a, V, D>
where
V: View<'a>,
D: DynamicFilter + 'a,
{
}
unsafe impl<'a, V, D> Sync for ParChunkIter<'a, V, D>
where
V: View<'a>,
D: DynamicFilter + 'a,
{
}
impl<'a, V, D> UnindexedProducer for ParChunkIter<'a, V, D>
where
V: View<'a>,
D: DynamicFilter + 'a,
{
type Item = <Iter<'a, 'a, V, D> as Iterator>::Item;
fn split(self) -> (Self, Option<Self>) {
let index = self.result.len() / 2;
let (left, right) = self.result.split_at(index);
(
Self {
world: self.world,
result: right,
filter: self.filter,
_view: PhantomData,
},
if !left.is_empty() {
Some(Self {
world: self.world,
result: left,
filter: self.filter,
_view: PhantomData,
})
} else {
None
},
)
}
fn fold_with<F>(self, folder: F) -> F
where
F: Folder<Self::Item>,
{
let indices = self.result.index().iter();
let fetch = unsafe {
<V as View<'a>>::fetch(
self.world.components(),
self.world.archetypes(),
self.result,
)
};
let iter = Iter::<'a, 'a, V, D> {
inner: fetch,
filter: self.filter,
archetypes: self.world.archetypes(),
max_count: indices.len(),
indices,
};
folder.consume_iter(iter)
}
}
impl<'a, V, D> ParallelIterator for ParChunkIter<'a, V, D>
where
V: View<'a>,
D: DynamicFilter + 'a,
{
type Item = ChunkView<'a, V::Fetch>;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: UnindexedConsumer<Self::Item>,
{
bridge_unindexed(self, consumer)
}
}
}
#[cfg(test)]
mod test {
use super::{
view::{read::Read, write::Write},
IntoQuery, QueryResult,
};
use crate::internals::{storage::archetype::ArchetypeIndex, world::World};
#[test]
fn query() {
let mut world = World::default();
world.extend(vec![(1usize, true), (2usize, true), (3usize, false)]);
let entity = world.push((10usize, 5f32, false));
let mut query = <(Read<usize>, Write<bool>)>::query();
for (x, y) in query.iter_mut(&mut world) {
println!("{}, {}", x, y);
}
for chunk in query.iter_chunks_mut(&mut world) {
let (x, y) = chunk.into_components();
println!("{:?}, {:?}", x, y);
}
#[cfg(feature = "parallel")]
{
query.par_for_each_mut(&mut world, |(x, y)| println!("{:?}, {:?}", x, y));
use rayon::iter::ParallelIterator;
query.par_iter_chunks_mut(&mut world).for_each(|chunk| {
println!("arch {:?}", chunk.archetype());
let (x, y) = chunk.into_components();
println!("{:?}, {:?}", x, y);
})
}
let single = query.get_mut(&mut world, entity);
assert_eq!(single, Ok((&10usize, &mut false)));
}
#[test]
fn complex_query_split() {
#[derive(Debug)]
struct A;
#[derive(Debug)]
struct B;
#[derive(Debug)]
struct C;
let mut world = World::default();
world.push((A, B));
world.push((A, C));
let mut query_a = <(&A, &B)>::query();
let mut query_b = <(&A, &mut C)>::query();
let (mut left, right) = world.split_for_query(&query_b);
for (a, c) in query_b.iter_mut(&mut left) {
println!("{:?} {:?}", a, c);
for (a, b) in query_a.iter(&right) {
println!("{:?} {:?}", a, b);
}
}
}
#[test]
fn query_split() {
let mut world = World::default();
world.extend(vec![(1usize, true), (2usize, true), (3usize, false)]);
let mut query_a = Write::<usize>::query();
let mut query_b = Write::<bool>::query();
let (mut left, mut right) = world.split::<Write<usize>>();
for x in query_a.iter_mut(&mut left) {
println!("{:}", x);
}
for x in query_b.iter_mut(&mut right) {
println!("{:}", x);
}
}
#[test]
#[should_panic]
fn query_split_disallow_component_left() {
let mut world = World::default();
world.extend(vec![(1usize, true), (2usize, true), (3usize, false)]);
let mut query_a = Write::<usize>::query();
let mut query_b = Write::<bool>::query();
let (mut left, _) = world.split::<Write<usize>>();
for x in query_a.iter_mut(&mut left) {
println!("{:}", x);
}
for x in query_b.iter_mut(&mut left) {
println!("{:}", x);
}
}
#[test]
#[should_panic]
fn query_split_disallow_component_right() {
let mut world = World::default();
world.extend(vec![(1usize, true), (2usize, true), (3usize, false)]);
let mut query_a = Write::<usize>::query();
let mut query_b = Write::<bool>::query();
let (_, mut right) = world.split::<Write<usize>>();
for x in query_a.iter_mut(&mut right) {
println!("{:}", x);
}
for x in query_b.iter_mut(&mut right) {
println!("{:}", x);
}
}
#[test]
fn query_component_lifetime() {
let mut world = World::default();
world.extend(vec![(1usize, true), (2usize, true), (3usize, false)]);
let mut components: Vec<&usize> = Vec::new();
{
let mut query_a = Read::<usize>::query();
components.extend(query_a.iter(&world));
}
assert_eq!(components[0], &1usize);
}
#[test]
#[cfg(feature = "parallel")]
fn queryresult_split_zero() {
let result = QueryResult::unordered(&[ArchetypeIndex(0), ArchetypeIndex(1)]);
let (left, right) = result.split_at(0);
assert_eq!(left.range(), &(0..0));
assert_eq!(right.range(), &(0..2));
}
#[test]
#[cfg(feature = "parallel")]
fn queryresult_split_one() {
let result = QueryResult::unordered(&[ArchetypeIndex(0), ArchetypeIndex(1)]);
let (left, right) = result.split_at(1);
assert_eq!(left.range(), &(0..1));
assert_eq!(right.range(), &(1..2));
}
#[test]
#[cfg(feature = "parallel")]
fn par_iter() {
use std::sync::atomic::*;
for _ in 0..1000 {
let mut w = World::default();
w.push((1usize,));
w.push((2usize, 3.5f32));
let count = AtomicUsize::new(0);
<&usize>::query().par_for_each(&w, |_: &usize| {
count.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(count.load(Ordering::SeqCst), 2);
}
}
#[test]
#[cfg(feature = "parallel")]
fn par_iter_option() {
use crate::Entity;
for _ in 0..1 {
let mut w = World::default();
w.push((2usize, 3.5f32));
w.push((1usize,));
w.push((3usize,));
w.push((4usize, 6.12f32));
<(Entity, &usize, Option<&f32>)>::query()
.par_for_each(&w, |(e, x, y)| println!("{:?} {} {:?}", e, x, y));
}
}
}