use std;
use hibitset::{BitIter, BitSetAll, BitSetAnd, BitSetLike};
use shred::{Fetch, FetchMut, Read, ReadExpect, Resource, Write, WriteExpect};
use std::ops::{Deref, DerefMut};
use tuple_utils::Split;
use crate::world::{Entities, Entity, Index};
#[cfg(feature = "parallel")]
mod par_join;
#[cfg(feature = "parallel")]
pub use self::par_join::{JoinParIter, ParJoin};
pub trait BitAnd {
type Value: BitSetLike;
fn and(self) -> Self::Value;
}
impl<A> BitAnd for (A,)
where
A: BitSetLike,
{
type Value = A;
fn and(self) -> Self::Value {
self.0
}
}
macro_rules! bitset_and {
($($from:ident),*) => {
impl<$($from),*> BitAnd for ($($from),*)
where $($from: BitSetLike),*
{
type Value = BitSetAnd<
<<Self as Split>::Left as BitAnd>::Value,
<<Self as Split>::Right as BitAnd>::Value
>;
fn and(self) -> Self::Value {
let (l, r) = self.split();
BitSetAnd(l.and(), r.and())
}
}
}
}
bitset_and! {A, B}
bitset_and! {A, B, C}
bitset_and! {A, B, C, D}
bitset_and! {A, B, C, D, E}
bitset_and! {A, B, C, D, E, F}
bitset_and! {A, B, C, D, E, F, G}
bitset_and! {A, B, C, D, E, F, G, H}
bitset_and! {A, B, C, D, E, F, G, H, I}
bitset_and! {A, B, C, D, E, F, G, H, I, J}
bitset_and! {A, B, C, D, E, F, G, H, I, J, K}
bitset_and! {A, B, C, D, E, F, G, H, I, J, K, L}
bitset_and! {A, B, C, D, E, F, G, H, I, J, K, L, M}
bitset_and! {A, B, C, D, E, F, G, H, I, J, K, L, M, N}
bitset_and! {A, B, C, D, E, F, G, H, I, J, K, L, M, N, O}
bitset_and! {A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P}
pub trait Join {
type Type;
type Value;
type Mask: BitSetLike;
fn join(self) -> JoinIter<Self>
where
Self: Sized,
{
JoinIter::new(self)
}
fn maybe(self) -> MaybeJoin<Self>
where
Self: Sized,
{
MaybeJoin(self)
}
unsafe fn open(self) -> (Self::Mask, Self::Value);
unsafe fn get(value: &mut Self::Value, id: Index) -> Self::Type;
#[inline]
fn is_unconstrained() -> bool {
false
}
}
pub struct MaybeJoin<J: Join>(pub J);
impl<T> Join for MaybeJoin<T>
where
T: Join,
{
type Mask = BitSetAll;
type Type = Option<<T as Join>::Type>;
type Value = (<T as Join>::Mask, <T as Join>::Value);
unsafe fn open(self) -> (Self::Mask, Self::Value) {
let (mask, value) = self.0.open();
(BitSetAll, (mask, value))
}
unsafe fn get((mask, value): &mut Self::Value, id: Index) -> Self::Type {
if mask.contains(id) {
Some(<T as Join>::get(value, id))
} else {
None
}
}
#[inline]
fn is_unconstrained() -> bool {
true
}
}
#[cfg(feature = "parallel")]
unsafe impl<T> ParJoin for MaybeJoin<T> where T: ParJoin {}
#[must_use]
pub struct JoinIter<J: Join> {
keys: BitIter<J::Mask>,
values: J::Value,
}
impl<J: Join> JoinIter<J> {
pub fn new(j: J) -> Self {
if <J as Join>::is_unconstrained() {
log::warn!(
"`Join` possibly iterating through all indices, you might've made a join with all `MaybeJoin`s, which is unbounded in length."
);
}
let (keys, values) = unsafe { j.open() };
JoinIter {
keys: keys.iter(),
values,
}
}
}
impl<J: Join> JoinIter<J> {
pub fn get(&mut self, entity: Entity, entities: &Entities) -> Option<J::Type> {
if self.keys.contains(entity.id()) && entities.is_alive(entity) {
Some(unsafe { J::get(&mut self.values, entity.id()) })
} else {
None
}
}
pub fn get_unchecked(&mut self, index: Index) -> Option<J::Type> {
if self.keys.contains(index) {
Some(unsafe { J::get(&mut self.values, index) })
} else {
None
}
}
}
impl<J: Join> std::iter::Iterator for JoinIter<J> {
type Item = J::Type;
fn next(&mut self) -> Option<J::Type> {
self.keys
.next()
.map(|idx| unsafe { J::get(&mut self.values, idx) })
}
}
impl<J: Join> Clone for JoinIter<J>
where
J::Mask: Clone,
J::Value: Clone,
{
fn clone(&self) -> Self {
Self {
keys: self.keys.clone(),
values: self.values.clone(),
}
}
}
macro_rules! define_open {
($($from:ident),*) => {
impl<$($from,)*> Join for ($($from),*,)
where $($from: Join),*,
($(<$from as Join>::Mask,)*): BitAnd,
{
type Type = ($($from::Type),*,);
type Value = ($($from::Value),*,);
type Mask = <($($from::Mask,)*) as BitAnd>::Value;
#[allow(non_snake_case)]
unsafe fn open(self) -> (Self::Mask, Self::Value) {
let ($($from,)*) = self;
let ($($from,)*) = ($($from.open(),)*);
(
($($from.0),*,).and(),
($($from.1),*,)
)
}
#[allow(non_snake_case)]
unsafe fn get(v: &mut Self::Value, i: Index) -> Self::Type {
let &mut ($(ref mut $from,)*) = v;
($($from::get($from, i),)*)
}
#[inline]
fn is_unconstrained() -> bool {
let mut unconstrained = true;
$( unconstrained = unconstrained && $from::is_unconstrained(); )*
unconstrained
}
}
#[cfg(feature = "parallel")]
unsafe impl<$($from,)*> ParJoin for ($($from),*,)
where $($from: ParJoin),*,
($(<$from as Join>::Mask,)*): BitAnd,
{}
}
}
define_open! {A}
define_open! {A, B}
define_open! {A, B, C}
define_open! {A, B, C, D}
define_open! {A, B, C, D, E}
define_open! {A, B, C, D, E, F}
define_open! {A, B, C, D, E, F, G}
define_open! {A, B, C, D, E, F, G, H}
define_open! {A, B, C, D, E, F, G, H, I}
define_open! {A, B, C, D, E, F, G, H, I, J}
define_open! {A, B, C, D, E, F, G, H, I, J, K}
define_open! {A, B, C, D, E, F, G, H, I, J, K, L}
define_open! {A, B, C, D, E, F, G, H, I, J, K, L, M}
define_open! {A, B, C, D, E, F, G, H, I, J, K, L, M, N}
define_open! {A, B, C, D, E, F, G, H, I, J, K, L, M, N, O}
define_open! {A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P}
define_open!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
define_open!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
macro_rules! immutable_resource_join {
($($ty:ty),*) => {
$(
impl<'a, 'b, T> Join for &'a $ty
where
&'a T: Join,
T: Resource,
{
type Type = <&'a T as Join>::Type;
type Value = <&'a T as Join>::Value;
type Mask = <&'a T as Join>::Mask;
unsafe fn open(self) -> (Self::Mask, Self::Value) {
self.deref().open()
}
unsafe fn get(v: &mut Self::Value, i: Index) -> Self::Type {
<&'a T as Join>::get(v, i)
}
#[inline]
fn is_unconstrained() -> bool {
<&'a T as Join>::is_unconstrained()
}
}
#[cfg(feature = "parallel")]
unsafe impl<'a, 'b, T> ParJoin for &'a $ty
where
&'a T: ParJoin,
T: Resource
{}
)*
};
}
macro_rules! mutable_resource_join {
($($ty:ty),*) => {
$(
impl<'a, 'b, T> Join for &'a mut $ty
where
&'a mut T: Join,
T: Resource,
{
type Type = <&'a mut T as Join>::Type;
type Value = <&'a mut T as Join>::Value;
type Mask = <&'a mut T as Join>::Mask;
unsafe fn open(self) -> (Self::Mask, Self::Value) {
self.deref_mut().open()
}
unsafe fn get(v: &mut Self::Value, i: Index) -> Self::Type {
<&'a mut T as Join>::get(v, i)
}
#[inline]
fn is_unconstrained() -> bool {
<&'a mut T as Join>::is_unconstrained()
}
}
#[cfg(feature = "parallel")]
unsafe impl<'a, 'b, T> ParJoin for &'a mut $ty
where
&'a mut T: ParJoin,
T: Resource
{}
)*
};
}
immutable_resource_join!(Fetch<'b, T>, Read<'b, T>, ReadExpect<'b, T>);
mutable_resource_join!(FetchMut<'b, T>, Write<'b, T>, WriteExpect<'b, T>);