use crate::engine::entity::Entity;
use crate::engine::error::{ECSError, ECSResult, InternalViolation};
use crate::engine::query::BuiltQuery;
use crate::engine::storage::{cast_slice, cast_slice_mut};
pub struct Read<T>(std::marker::PhantomData<T>);
pub struct Write<T>(std::marker::PhantomData<T>);
pub unsafe trait QueryParam: 'static {
type Item<'a>;
fn validate(query: &BuiltQuery) -> ECSResult<()>;
unsafe fn for_each_chunk<F>(reads: &[&[u8]], writes: &mut [&mut [u8]], f: &F)
where
F: for<'a> Fn(Self::Item<'a>);
}
pub unsafe trait EntityQueryParam: QueryParam {
type EntityItem<'a>;
unsafe fn for_each_entity_chunk<F>(
entities: &[Entity],
reads: &[&[u8]],
writes: &mut [&mut [u8]],
f: &F,
) where
F: for<'a> Fn(Self::EntityItem<'a>);
unsafe fn for_each_entity_chunk_fallible<F>(
entities: &[Entity],
reads: &[&[u8]],
writes: &mut [&mut [u8]],
f: &F,
) -> ECSResult<()>
where
F: for<'a> Fn(Self::EntityItem<'a>) -> ECSResult<()>;
}
unsafe impl<A: 'static + Send + Sync> QueryParam for Read<A> {
type Item<'a> = &'a A;
fn validate(query: &BuiltQuery) -> ECSResult<()> {
if query.reads().len() != 1 || !query.writes().is_empty() {
return Err(ECSError::from(InternalViolation::QueryShapeMismatch {
method: "for_each<Read<A>>",
expected_reads: 1,
expected_writes: 0,
}));
}
query.validate_read_type::<A>(0, "for_each<Read<A>>")?;
Ok(())
}
unsafe fn for_each_chunk<F>(reads: &[&[u8]], _writes: &mut [&mut [u8]], f: &F)
where
F: for<'a> Fn(Self::Item<'a>),
{
let a = unsafe { cast_slice::<A>(reads[0].as_ptr(), reads[0].len()) };
for v in a {
f(v);
}
}
}
unsafe impl<A: 'static + Send + Sync> EntityQueryParam for Read<A> {
type EntityItem<'a> = (Entity, &'a A);
unsafe fn for_each_entity_chunk<F>(
entities: &[Entity],
reads: &[&[u8]],
_writes: &mut [&mut [u8]],
f: &F,
) where
F: for<'a> Fn(Self::EntityItem<'a>),
{
let a = unsafe { cast_slice::<A>(reads[0].as_ptr(), reads[0].len()) };
debug_assert_eq!(entities.len(), a.len());
for i in 0..a.len() {
f((entities[i], &a[i]));
}
}
unsafe fn for_each_entity_chunk_fallible<F>(
entities: &[Entity],
reads: &[&[u8]],
_writes: &mut [&mut [u8]],
f: &F,
) -> ECSResult<()>
where
F: for<'a> Fn(Self::EntityItem<'a>) -> ECSResult<()>,
{
let a = unsafe { cast_slice::<A>(reads[0].as_ptr(), reads[0].len()) };
debug_assert_eq!(entities.len(), a.len());
for i in 0..a.len() {
f((entities[i], &a[i]))?;
}
Ok(())
}
}
unsafe impl<A: 'static + Send + Sync> QueryParam for Write<A> {
type Item<'a> = &'a mut A;
fn validate(query: &BuiltQuery) -> ECSResult<()> {
if !query.reads().is_empty() || query.writes().len() != 1 {
return Err(ECSError::from(InternalViolation::QueryShapeMismatch {
method: "for_each<Write<A>>",
expected_reads: 0,
expected_writes: 1,
}));
}
query.validate_write_type::<A>(0, "for_each<Write<A>>")?;
Ok(())
}
unsafe fn for_each_chunk<F>(_reads: &[&[u8]], writes: &mut [&mut [u8]], f: &F)
where
F: for<'a> Fn(Self::Item<'a>),
{
let a = unsafe { cast_slice_mut::<A>(writes[0].as_mut_ptr(), writes[0].len()) };
for v in a {
f(v);
}
}
}
unsafe impl<A: 'static + Send + Sync> EntityQueryParam for Write<A> {
type EntityItem<'a> = (Entity, &'a mut A);
unsafe fn for_each_entity_chunk<F>(
entities: &[Entity],
_reads: &[&[u8]],
writes: &mut [&mut [u8]],
f: &F,
) where
F: for<'a> Fn(Self::EntityItem<'a>),
{
let a = unsafe { cast_slice_mut::<A>(writes[0].as_mut_ptr(), writes[0].len()) };
debug_assert_eq!(entities.len(), a.len());
for i in 0..a.len() {
f((entities[i], &mut a[i]));
}
}
unsafe fn for_each_entity_chunk_fallible<F>(
entities: &[Entity],
_reads: &[&[u8]],
writes: &mut [&mut [u8]],
f: &F,
) -> ECSResult<()>
where
F: for<'a> Fn(Self::EntityItem<'a>) -> ECSResult<()>,
{
let a = unsafe { cast_slice_mut::<A>(writes[0].as_mut_ptr(), writes[0].len()) };
debug_assert_eq!(entities.len(), a.len());
for i in 0..a.len() {
f((entities[i], &mut a[i]))?;
}
Ok(())
}
}
macro_rules! impl_query_param_tuple {
(reads=[$($R:ident : $ri:tt),*], writes=[$($W:ident : $wi:tt),*], method=$method:expr) => {
unsafe impl<$($R: 'static + Send + Sync,)* $($W: 'static + Send + Sync,)*>
QueryParam for ($(Read<$R>,)* $(Write<$W>,)*)
{
type Item<'a> = ($(&'a $R,)* $(&'a mut $W,)*);
fn validate(query: &BuiltQuery) -> ECSResult<()> {
let expected_reads = impl_query_param_tuple!(@count $($R)*);
let expected_writes = impl_query_param_tuple!(@count $($W)*);
if query.reads().len() != expected_reads || query.writes().len() != expected_writes {
return Err(ECSError::from(InternalViolation::QueryShapeMismatch {
method: $method,
expected_reads,
expected_writes,
}));
}
$(
query.validate_read_type::<$R>($ri, $method)?;
)*
$(
query.validate_write_type::<$W>($wi, $method)?;
)*
Ok(())
}
#[allow(unused_variables, unused_assignments, non_snake_case)]
unsafe fn for_each_chunk<Func>(
reads: &[&[u8]],
writes: &mut [&mut [u8]],
f: &Func,
)
where
Func: for<'a> Fn(Self::Item<'a>),
{
$(
let $R = unsafe {
cast_slice::<$R>(reads[$ri].as_ptr(), reads[$ri].len())
};
)*
$(
let $W = unsafe {
cast_slice_mut::<$W>(writes[$wi].as_mut_ptr(), writes[$wi].len())
};
)*
let _len: usize;
impl_query_param_tuple!(@first_len _len, [$($R),*], [$($W),*]);
$( debug_assert_eq!($R.len(), _len); )*
$( debug_assert_eq!($W.len(), _len); )*
for _i in 0.._len {
f(($(&$R[_i],)* $(&mut $W[_i],)*));
}
}
}
unsafe impl<$($R: 'static + Send + Sync,)* $($W: 'static + Send + Sync,)*>
EntityQueryParam for ($(Read<$R>,)* $(Write<$W>,)*)
{
type EntityItem<'a> = (Entity, $(&'a $R,)* $(&'a mut $W,)*);
#[allow(unused_variables, unused_assignments, non_snake_case)]
unsafe fn for_each_entity_chunk<Func>(
entities: &[Entity],
reads: &[&[u8]],
writes: &mut [&mut [u8]],
f: &Func,
)
where
Func: for<'a> Fn(Self::EntityItem<'a>),
{
$(
let $R = unsafe {
cast_slice::<$R>(reads[$ri].as_ptr(), reads[$ri].len())
};
)*
$(
let $W = unsafe {
cast_slice_mut::<$W>(writes[$wi].as_mut_ptr(), writes[$wi].len())
};
)*
let _len: usize;
impl_query_param_tuple!(@first_len _len, [$($R),*], [$($W),*]);
debug_assert_eq!(entities.len(), _len);
$( debug_assert_eq!($R.len(), _len); )*
$( debug_assert_eq!($W.len(), _len); )*
for _i in 0.._len {
f((entities[_i], $(&$R[_i],)* $(&mut $W[_i],)*));
}
}
#[allow(unused_variables, unused_assignments, non_snake_case)]
unsafe fn for_each_entity_chunk_fallible<Func>(
entities: &[Entity],
reads: &[&[u8]],
writes: &mut [&mut [u8]],
f: &Func,
) -> ECSResult<()>
where
Func: for<'a> Fn(Self::EntityItem<'a>) -> ECSResult<()>,
{
$(
let $R = unsafe {
cast_slice::<$R>(reads[$ri].as_ptr(), reads[$ri].len())
};
)*
$(
let $W = unsafe {
cast_slice_mut::<$W>(writes[$wi].as_mut_ptr(), writes[$wi].len())
};
)*
let _len: usize;
impl_query_param_tuple!(@first_len _len, [$($R),*], [$($W),*]);
debug_assert_eq!(entities.len(), _len);
$( debug_assert_eq!($R.len(), _len); )*
$( debug_assert_eq!($W.len(), _len); )*
for _i in 0.._len {
f((entities[_i], $(&$R[_i],)* $(&mut $W[_i],)*))?;
}
Ok(())
}
}
};
(@count) => { 0usize };
(@count $head:ident $($tail:ident)*) => { 1usize + impl_query_param_tuple!(@count $($tail)*) };
(@first_len $len:ident, [$first:ident $(, $rest:ident)*], [$($W:ident),*]) => {
$len = $first.len();
};
(@first_len $len:ident, [], [$first:ident $(, $rest:ident)*]) => {
$len = $first.len();
};
(@first_len $len:ident, [], []) => {
$len = 0;
};
}
impl_query_param_tuple!(reads=[A:0], writes=[], method="for_each<(Read,)>");
impl_query_param_tuple!(reads=[A:0, B:1], writes=[], method="for_each<(Read, Read)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2], writes=[], method="for_each<(Read x3)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3], writes=[], method="for_each<(Read x4)>");
impl_query_param_tuple!(reads=[], writes=[A:0], method="for_each<(Write,)>");
impl_query_param_tuple!(reads=[], writes=[A:0, B:1], method="for_each<(Write x2)>");
impl_query_param_tuple!(reads=[], writes=[A:0, B:1, C:2], method="for_each<(Write x3)>");
impl_query_param_tuple!(reads=[], writes=[A:0, B:1, C:2, D:3], method="for_each<(Write x4)>");
impl_query_param_tuple!(reads=[A:0], writes=[B:0], method="for_each<(Read, Write)>");
impl_query_param_tuple!(reads=[A:0], writes=[B:0, C:1], method="for_each<(Read, Write x2)>");
impl_query_param_tuple!(reads=[A:0], writes=[B:0, C:1, D:2], method="for_each<(Read, Write x3)>");
impl_query_param_tuple!(reads=[A:0], writes=[B:0, C:1, D:2, E:3], method="for_each<(Read, Write x4)>");
impl_query_param_tuple!(reads=[A:0, B:1], writes=[C:0], method="for_each<(Read x2, Write)>");
impl_query_param_tuple!(reads=[A:0, B:1], writes=[C:0, D:1], method="for_each<(Read x2, Write x2)>");
impl_query_param_tuple!(reads=[A:0, B:1], writes=[C:0, D:1, E:2], method="for_each<(Read x2, Write x3)>");
impl_query_param_tuple!(reads=[A:0, B:1], writes=[C:0, D:1, E:2, F:3], method="for_each<(Read x2, Write x4)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2], writes=[D:0], method="for_each<(Read x3, Write)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2], writes=[D:0, E:1], method="for_each<(Read x3, Write x2)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2], writes=[D:0, E:1, F:2], method="for_each<(Read x3, Write x3)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2], writes=[D:0, E:1, F:2, G:3], method="for_each<(Read x3, Write x4)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3], writes=[E:0], method="for_each<(Read x4, Write)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3], writes=[E:0, F:1], method="for_each<(Read x4, Write x2)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3], writes=[E:0, F:1, G:2], method="for_each<(Read x4, Write x3)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3], writes=[E:0, F:1, G:2, H:3], method="for_each<(Read x4, Write x4)>");
impl_query_param_tuple!(reads=[], writes=[A:0, B:1, C:2, D:3, E:4], method="for_each<(Read x0, Write x5)>");
impl_query_param_tuple!(reads=[], writes=[A:0, B:1, C:2, D:3, E:4, F:5], method="for_each<(Read x0, Write x6)>");
impl_query_param_tuple!(reads=[], writes=[A:0, B:1, C:2, D:3, E:4, F:5, G:6], method="for_each<(Read x0, Write x7)>");
impl_query_param_tuple!(reads=[], writes=[A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7], method="for_each<(Read x0, Write x8)>");
impl_query_param_tuple!(reads=[A:0], writes=[B:0, C:1, D:2, E:3, F:4], method="for_each<(Read x1, Write x5)>");
impl_query_param_tuple!(reads=[A:0], writes=[B:0, C:1, D:2, E:3, F:4, G:5], method="for_each<(Read x1, Write x6)>");
impl_query_param_tuple!(reads=[A:0], writes=[B:0, C:1, D:2, E:3, F:4, G:5, H:6], method="for_each<(Read x1, Write x7)>");
impl_query_param_tuple!(reads=[A:0], writes=[B:0, C:1, D:2, E:3, F:4, G:5, H:6, I:7], method="for_each<(Read x1, Write x8)>");
impl_query_param_tuple!(reads=[A:0, B:1], writes=[C:0, D:1, E:2, F:3, G:4], method="for_each<(Read x2, Write x5)>");
impl_query_param_tuple!(reads=[A:0, B:1], writes=[C:0, D:1, E:2, F:3, G:4, H:5], method="for_each<(Read x2, Write x6)>");
impl_query_param_tuple!(reads=[A:0, B:1], writes=[C:0, D:1, E:2, F:3, G:4, H:5, I:6], method="for_each<(Read x2, Write x7)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2], writes=[D:0, E:1, F:2, G:3, H:4], method="for_each<(Read x3, Write x5)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2], writes=[D:0, E:1, F:2, G:3, H:4, I:5], method="for_each<(Read x3, Write x6)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3], writes=[E:0, F:1, G:2, H:3, I:4], method="for_each<(Read x4, Write x5)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4], writes=[], method="for_each<(Read x5, Write x0)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4], writes=[F:0], method="for_each<(Read x5, Write x1)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4], writes=[F:0, G:1], method="for_each<(Read x5, Write x2)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4], writes=[F:0, G:1, H:2], method="for_each<(Read x5, Write x3)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4], writes=[F:0, G:1, H:2, I:3], method="for_each<(Read x5, Write x4)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4, F:5], writes=[], method="for_each<(Read x6, Write x0)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4, F:5], writes=[G:0], method="for_each<(Read x6, Write x1)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4, F:5], writes=[G:0, H:1], method="for_each<(Read x6, Write x2)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4, F:5], writes=[G:0, H:1, I:2], method="for_each<(Read x6, Write x3)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4, F:5, G:6], writes=[], method="for_each<(Read x7, Write x0)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4, F:5, G:6], writes=[H:0], method="for_each<(Read x7, Write x1)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4, F:5, G:6], writes=[H:0, I:1], method="for_each<(Read x7, Write x2)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7], writes=[], method="for_each<(Read x8, Write x0)>");
impl_query_param_tuple!(reads=[A:0, B:1, C:2, D:3, E:4, F:5, G:6, H:7], writes=[I:0], method="for_each<(Read x8, Write x1)>");