use core::{any::TypeId, marker::PhantomData, ops::ControlFlow};
use crate::{
archetype::Archetype, component::ComponentInfo, entity::EntityId, epoch::EpochId,
system::QueryArg,
};
use super::{
fetch::Fetch, Access, AsQuery, DefaultQuery, ImmutableQuery, IntoQuery, Query, SendQuery,
WriteAlias,
};
pub trait BooleanFetchOp: 'static {
fn op(a: bool, b: bool) -> ControlFlow<bool, bool>;
fn mask(mask: u16, count: usize) -> bool;
}
pub enum AndOp {}
impl BooleanFetchOp for AndOp {
#[inline(always)]
fn op(a: bool, b: bool) -> ControlFlow<bool, bool> {
if a && b {
ControlFlow::Continue(true)
} else {
ControlFlow::Break(false)
}
}
#[inline(always)]
fn mask(mask: u16, count: usize) -> bool {
mask == (1 << count) - 1
}
}
pub enum OrOp {}
impl BooleanFetchOp for OrOp {
#[inline(always)]
fn op(a: bool, b: bool) -> ControlFlow<bool, bool> {
if a || b {
ControlFlow::Break(true)
} else {
ControlFlow::Continue(false)
}
}
#[inline(always)]
fn mask(mask: u16, _count: usize) -> bool {
mask != 0
}
}
pub enum XorOp {}
impl BooleanFetchOp for XorOp {
#[inline(always)]
fn op(a: bool, b: bool) -> ControlFlow<bool, bool> {
match (a, b) {
(false, false) => ControlFlow::Continue(false),
(true, true) => ControlFlow::Break(false),
_ => ControlFlow::Continue(true),
}
}
#[inline(always)]
fn mask(mask: u16, _count: usize) -> bool {
mask.is_power_of_two()
}
}
pub struct BooleanQuery<T, Op> {
tuple: T,
op: PhantomData<fn() -> Op>,
}
impl<T, Op> Clone for BooleanQuery<T, Op>
where
T: Clone,
{
#[inline(always)]
fn clone(&self) -> Self {
BooleanQuery {
tuple: self.tuple.clone(),
op: PhantomData,
}
}
}
impl<T, Op> Copy for BooleanQuery<T, Op> where T: Copy {}
impl<T, Op> BooleanQuery<T, Op> {
#[inline(always)]
pub fn from_tuple(tuple: T) -> Self {
BooleanQuery {
tuple,
op: PhantomData,
}
}
}
pub struct BooleanFetch<T, Op> {
tuple: T,
archetype: u16,
chunk: u16,
item: u16,
op: PhantomData<Op>,
}
macro_rules! boolean_shortcut {
([$archetype:ident $op:ident] $a:ident $($b:ident)*) => {{
#[allow(unused_mut)]
let mut result = $a.visit_archetype($archetype);
$(
match $op::op(result, $b.visit_archetype($archetype)) {
ControlFlow::Continue(r) => result = r,
ControlFlow::Break(r) => return r,
}
)*
result
}};
}
macro_rules! impl_boolean {
() => { };
($($a:ident)+) => {
#[allow(non_snake_case)]
#[allow(unused_variables, unused_mut, unused_assignments)]
unsafe impl<'a, Op $(, $a)+> Fetch<'a> for BooleanFetch<($($a,)+), Op>
where
$($a: Fetch<'a>,)+
Op: BooleanFetchOp,
{
type Item = ($(Option<$a::Item>,)+);
#[inline(always)]
fn dangling() -> Self {
BooleanFetch {
tuple: ($($a::dangling(),)+),
archetype: 0,
chunk: 0,
item: 0,
op: PhantomData,
}
}
#[inline(always)]
unsafe fn get_item(&mut self, idx: u32) -> ($(Option<$a::Item>,)+) {
let ($($a,)+) = &mut self.tuple;
let mut mi = 1;
($({
let elem = if self.item & mi != 0 {
Some($a.get_item(idx))
} else {
None
};
mi <<= 1;
elem
},)+)
}
#[inline(always)]
unsafe fn visit_item(&mut self, idx: u32) -> bool {
let ($($a,)+) = &mut self.tuple;
let mut mi = 1;
let mut count = 0;
$(
if self.chunk & mi != 0 {
if $a.visit_item(idx) {
self.item |= mi;
}
}
mi <<= 1;
count += 1;
)+
Op::mask(self.item, count)
}
#[inline(always)]
unsafe fn visit_chunk(&mut self, chunk_idx: u32) -> bool {
let ($($a,)+) = &mut self.tuple;
let mut mi = 1;
let mut count = 0;
$(
if self.archetype & mi != 0 {
if $a.visit_chunk(chunk_idx) {
self.chunk |= mi;
}
}
mi <<= 1;
count += 1;
)+
Op::mask(self.chunk, count)
}
#[inline(always)]
unsafe fn touch_chunk(&mut self, chunk_idx: u32) {
let ($($a,)+) = &mut self.tuple;
let mut mi = 1;
$(
if self.chunk & mi != 0 {
$a.touch_chunk(chunk_idx);
}
mi <<= 1;
)+
}
}
#[allow(non_snake_case)]
impl<'a, Op $(, $a)+> BooleanQuery<($($a,)+), Op>
where
Op: BooleanFetchOp,
{
#[inline(always)]
pub fn new($($a: $a),+) -> Self {
BooleanQuery {
tuple: ($($a,)+),
op: PhantomData
}
}
}
#[allow(non_snake_case)]
impl<Op $(, $a)+> AsQuery for BooleanQuery<($($a,)+), Op>
where
$($a: AsQuery,)+
Op: BooleanFetchOp,
{
type Query = BooleanQuery<($($a::Query,)+), Op>;
}
#[allow(non_snake_case)]
impl<Op $(, $a)+> IntoQuery for BooleanQuery<($($a,)+), Op>
where
$($a: IntoQuery,)+
Op: BooleanFetchOp,
{
#[inline(always)]
fn into_query(self) -> Self::Query {
let ($($a,)+) = self.tuple;
BooleanQuery {
tuple: ($($a.into_query(),)+),
op: PhantomData,
}
}
}
impl<Op $(, $a)+> DefaultQuery for BooleanQuery<($($a,)+), Op>
where
$($a: DefaultQuery,)+
Op: BooleanFetchOp,
{
#[inline(always)]
fn default_query() -> Self::Query {
BooleanQuery {
tuple: ($($a::default_query(),)+),
op: PhantomData,
}
}
}
impl<Op $(, $a)+> QueryArg for BooleanQuery<($($a,)+), Op>
where
$($a: QueryArg,)+
Op: BooleanFetchOp,
{
#[inline(always)]
fn new() -> Self {
BooleanQuery {
tuple: ($($a::new(),)+),
op: PhantomData,
}
}
}
#[allow(non_snake_case)]
#[allow(unused_variables, unused_mut, unused_assignments)]
unsafe impl<Op $(, $a)+> Query for BooleanQuery<($($a,)+), Op>
where
$($a: Query,)+
Op: BooleanFetchOp,
{
type Item<'a> = ($(Option<$a::Item<'a>>,)+) where $($a: 'a,)+;
type Fetch<'a> = BooleanFetch<($($a::Fetch<'a>,)+), Op> where $($a: 'a,)+;
const MUTABLE: bool = $($a::MUTABLE ||)+ false;
#[inline(always)]
fn component_access(&self, comp: &ComponentInfo) -> Result<Option<Access>, WriteAlias> {
let ($($a,)+) = &self.tuple;
let mut result = None;
$(
result = match (result, $a.component_access(comp)?) {
(None, one) | (one, None) => one,
(Some(Access::Read), Some(Access::Read)) => Some(Access::Read),
_ => return Err(WriteAlias),
};
)*
Ok(result)
}
#[inline(always)]
unsafe fn access_archetype(&self, archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) {
let ($($a,)+) = &self.tuple;
$(if $a.visit_archetype(archetype) {
$a.access_archetype(archetype, &mut f);
})+
}
#[inline(always)]
fn visit_archetype(&self, archetype: &Archetype) -> bool {
let ($($a,)+) = &self.tuple;
boolean_shortcut!([archetype Op] $($a)+)
}
#[inline(always)]
unsafe fn fetch<'a>(
&self,
arch_idx: u32,
archetype: &'a Archetype,
epoch: EpochId,
) -> BooleanFetch<($($a::Fetch<'a>,)+), Op> {
let ($($a,)+) = &self.tuple;
let mut mask = 0;
let mut mi = 0;
let mut cut = false;
$(
let $a = if $a.visit_archetype(archetype) {
mask |= (1 << mi);
$a.fetch(arch_idx, archetype, epoch)
} else {
Fetch::dangling()
};
mi += 1;
)+
BooleanFetch {
tuple: ($($a,)+),
archetype: mask,
chunk: 0,
item: 0,
op: PhantomData,
}
}
#[inline(always)]
fn reserved_entity_item<'a>(&self, id: EntityId, idx: u32) -> Option<Self::Item<'a>> {
let ($($a,)+) = &self.tuple;
let mut mask = 0;
let mut mi = 0;
$(
let $a = $a.reserved_entity_item(id, idx);
if $a.is_some() {
mask |= 1 << mi;
}
mi += 1;
)+
if Op::mask(mask, mi) {
Some(($($a,)+))
} else {
None
}
}
}
unsafe impl<Op $(, $a)+> ImmutableQuery for BooleanQuery<($($a,)+), Op>
where
$($a: ImmutableQuery,)+
Op: BooleanFetchOp,
{
}
unsafe impl<Op $(, $a)+> SendQuery for BooleanQuery<($($a,)+), Op>
where
$($a: SendQuery,)+
Op: BooleanFetchOp,
{
}
};
}
for_tuple!(impl_boolean);
pub type And<T> = BooleanQuery<T, AndOp>;
pub type Or<T> = BooleanQuery<T, OrOp>;
pub type Xor<T> = BooleanQuery<T, XorOp>;
pub type And2<A, B> = And<(A, B)>;
pub type And3<A, B, C> = And<(A, B, C)>;
pub type And4<A, B, C, D> = And<(A, B, C, D)>;
pub type And5<A, B, C, D, E> = And<(A, B, C, D, E)>;
pub type And6<A, B, C, D, E, F> = And<(A, B, C, D, E, F)>;
pub type And7<A, B, C, D, E, F, G> = And<(A, B, C, D, E, F, G)>;
pub type And8<A, B, C, D, E, F, G, H> = And<(A, B, C, D, E, F, G, H)>;
pub type Or2<A, B> = Or<(A, B)>;
pub type Or3<A, B, C> = Or<(A, B, C)>;
pub type Or4<A, B, C, D> = Or<(A, B, C, D)>;
pub type Or5<A, B, C, D, E> = Or<(A, B, C, D, E)>;
pub type Or6<A, B, C, D, E, F> = Or<(A, B, C, D, E, F)>;
pub type Or7<A, B, C, D, E, F, G> = Or<(A, B, C, D, E, F, G)>;
pub type Or8<A, B, C, D, E, F, G, H> = Or<(A, B, C, D, E, F, G, H)>;
pub type Xor2<A, B> = Xor<(A, B)>;
pub type Xor3<A, B, C> = Xor<(A, B, C)>;
pub type Xor4<A, B, C, D> = Xor<(A, B, C, D)>;
pub type Xor5<A, B, C, D, E> = Xor<(A, B, C, D, E)>;
pub type Xor6<A, B, C, D, E, F> = Xor<(A, B, C, D, E, F)>;
pub type Xor7<A, B, C, D, E, F, G> = Xor<(A, B, C, D, E, F, G)>;
pub type Xor8<A, B, C, D, E, F, G, H> = Xor<(A, B, C, D, E, F, G, H)>;