use std::marker::PhantomData;
use std::{any, ops};
use derive_trait::derive_trait;
use rayon::prelude::ParallelIterator;
use crate::entity::{self, ealloc, Raw as _};
use crate::storage::{self, Access as _, Chunked as _};
use crate::{comp, util, Archetype, Storage};
pub struct Single<A, C, StorageRef> {
storage: StorageRef,
_ph: PhantomData<(A, C)>,
}
impl<A, C, StorageRef> Single<A, C, StorageRef> {
pub(crate) fn new(storage: StorageRef) -> Self { Self { storage, _ph: PhantomData } }
}
#[derive_trait(pub Get{
/// The archetype that this accessor retrieves for.
type Arch: Archetype = A;
/// The component that this accessor retrieves.
type Comp: comp::SimpleOrIsotope<Self::Arch> = C;
})]
impl<A, C, StorageRef> Single<A, C, StorageRef>
where
A: Archetype,
C: comp::SimpleOrIsotope<A>,
StorageRef: ops::Deref + Sync,
StorageRef::Target: Storage<RawEntity = <A as Archetype>::RawEntity, Comp = C>,
{
pub fn try_get(&self, entity: impl entity::Ref<Archetype = A>) -> Option<&C> {
self.storage.get(entity.id())
}
pub fn iter<'t>(&'t self) -> impl Iterator<Item = (entity::TempRef<'t, A>, &'t C)> + 't {
self.storage.iter().map(|(entity, comp)| (entity::TempRef::new(entity), comp))
}
}
#[derive_trait(pub MustGet{
/// The archetype that this accessor retrieves for.
type Arch: Archetype = A;
/// The component that this accessor retrieves.
type Comp: comp::SimpleOrIsotope<Self::Arch> + comp::Must<Self::Arch> = C;
})]
impl<A, C, StorageRef> Single<A, C, StorageRef>
where
A: Archetype,
C: comp::SimpleOrIsotope<A> + comp::Must<A>,
StorageRef: ops::Deref + Sync,
StorageRef::Target: Storage<RawEntity = <A as Archetype>::RawEntity, Comp = C>,
{
pub fn get(&self, entity: impl entity::Ref<Archetype = A>) -> &C {
match self.try_get(entity) {
Some(comp) => comp,
None => panic!(
"Component {}/{} implements comp::Must but is not present",
any::type_name::<A>(),
any::type_name::<C>()
),
}
}
pub fn par_iter<'t>(
&'t self,
snapshot: &'t ealloc::Snapshot<<A as Archetype>::RawEntity>,
) -> impl ParallelIterator<Item = (entity::TempRef<'t, A>, &'t C)> {
rayon::iter::split(snapshot.as_slice(), |slice| slice.split()).flat_map_iter(|slice| {
slice.iter_chunks().flat_map(<<A as Archetype>::RawEntity as entity::Raw>::range).map(
|id| {
let entity = entity::TempRef::new(id);
let data = self.get(entity);
(entity, data)
},
)
})
}
}
#[derive_trait(pub GetChunked {
/// The archetype that this accessor retrieves for.
type Arch: Archetype = A;
/// The component that this accessor retrieves.
type Comp: comp::SimpleOrIsotope<Self::Arch> = C;
})]
impl<A, C, StorageRef> Single<A, C, StorageRef>
where
A: Archetype,
C: comp::SimpleOrIsotope<A>,
StorageRef: ops::Deref + Sync,
StorageRef::Target: storage::Chunked<RawEntity = <A as Archetype>::RawEntity, Comp = C>,
{
pub fn get_chunk(&self, chunk: entity::TempRefChunk<A>) -> &[C] {
self.storage.get_chunk(chunk.start, chunk.end).expect("chunk is not completely filled")
}
}
#[derive_trait(pub MustGetChunked{
/// The archetype that this accessor retrieves for.
type Arch: Archetype = A;
/// The component that this accessor retrieves.
type Comp: comp::SimpleOrIsotope<Self::Arch> + comp::Must<Self::Arch> = C;
})]
impl<A, C, StorageRef> Single<A, C, StorageRef>
where
A: Archetype,
C: comp::SimpleOrIsotope<A> + comp::Must<A>,
StorageRef: ops::Deref + Sync,
StorageRef::Target: storage::Chunked<RawEntity = <A as Archetype>::RawEntity, Comp = C>,
{
pub fn par_iter_chunks<'t>(
&'t self,
snapshot: &'t ealloc::Snapshot<<A as Archetype>::RawEntity>,
) -> impl ParallelIterator<Item = (entity::TempRefChunk<'t, A>, &'t [C])> {
rayon::iter::split(snapshot.as_slice(), |slice| slice.split()).flat_map_iter(|slice| {
slice.iter_chunks().map(|chunk| {
let chunk = entity::TempRefChunk::new(chunk.start, chunk.end);
let data = self.get_chunk(chunk);
(chunk, data)
})
})
}
}
#[derive_trait(pub GetMut{
/// The archetype that this accessor retrieves for.
type Arch: Archetype = A;
/// The component that this accessor retrieves.
type Comp: comp::SimpleOrIsotope<Self::Arch> = C;
})]
impl<A, C, StorageRef> Single<A, C, StorageRef>
where
A: Archetype,
C: comp::SimpleOrIsotope<A>,
StorageRef: ops::DerefMut + Sync,
StorageRef::Target: storage::Access<RawEntity = <A as Archetype>::RawEntity, Comp = C>,
{
pub fn try_get_mut(&mut self, entity: impl entity::Ref<Archetype = A>) -> Option<&mut C> {
self.storage.get_mut(entity.id())
}
pub fn try_get_many_mut<const N: usize>(
&mut self,
entities: [impl entity::Ref<Archetype = A>; N],
) -> Option<[&mut C; N]> {
self.storage.get_many_mut(entities.map(|entity| entity.id()))
}
pub fn iter_mut<'t>(
&'t mut self,
) -> impl Iterator<Item = (entity::TempRef<'t, A>, &'t mut C)> + 't {
self.storage.iter_mut().map(|(entity, comp)| (entity::TempRef::new(entity), comp))
}
}
#[derive_trait(pub MustGetMut{
/// The archetype that this accessor retrieves for.
type Arch: Archetype = A;
/// The component that this accessor retrieves.
type Comp: comp::SimpleOrIsotope<Self::Arch> + comp::Must<Self::Arch> = C;
})]
impl<A, C, StorageRef> Single<A, C, StorageRef>
where
A: Archetype,
C: comp::SimpleOrIsotope<A> + comp::Must<A>,
StorageRef: ops::DerefMut + Sync,
StorageRef::Target: storage::Access<RawEntity = <A as Archetype>::RawEntity, Comp = C>,
{
pub fn get_mut(&mut self, entity: impl entity::Ref<Archetype = A>) -> &mut C {
match self.try_get_mut(entity) {
Some(comp) => comp,
None => panic!(
"Component {}/{} implements comp::Must but is not present",
any::type_name::<A>(),
any::type_name::<C>(),
),
}
}
pub fn get_many_mut<const N: usize>(
&mut self,
entities: [impl entity::Ref<Archetype = A>; N],
) -> [&mut C; N] {
match self.try_get_many_mut(entities) {
Some(comps) => comps,
None => panic!(
"Parameter contains duplicate entities, or component {}/{} implements comp::Must \
but is not present",
any::type_name::<A>(),
any::type_name::<C>(),
),
}
}
}
#[derive_trait(pub Set{
/// The archetype that this accessor retrieves for.
type Arch: Archetype = A;
/// The component that this accessor retrieves.
type Comp: comp::SimpleOrIsotope<Self::Arch> = C;
})]
impl<A, C, StorageRef> Single<A, C, StorageRef>
where
A: Archetype,
C: comp::SimpleOrIsotope<A>,
StorageRef: ops::DerefMut + Sync,
StorageRef::Target: Storage<RawEntity = <A as Archetype>::RawEntity, Comp = C>,
{
pub fn set(&mut self, entity: impl entity::Ref<Archetype = A>, value: Option<C>) -> Option<C> {
self.storage.set(entity.id(), value)
}
}
impl<A, C, StorageRef> Single<A, C, StorageRef>
where
A: Archetype,
C: comp::SimpleOrIsotope<A>,
StorageRef: ops::DerefMut + Sync,
StorageRef::Target: Storage<RawEntity = A::RawEntity, Comp = C>,
{
pub fn as_partition(
&mut self,
) -> Single<A, C, util::OwnedDeref<<StorageRef::Target as Storage>::Partition<'_>>> {
Single { storage: util::OwnedDeref(self.storage.as_partition()), _ph: PhantomData }
}
}
#[derive_trait(pub MustSet{
/// The archetype that this accessor retrieves for.
type Arch: Archetype = A;
/// The component that this accessor retrieves.
type Comp: comp::SimpleOrIsotope<Self::Arch> + comp::Must<Self::Arch> = C;
})]
impl<A, C, StorageRef> Single<A, C, StorageRef>
where
A: Archetype,
C: comp::SimpleOrIsotope<A> + comp::Must<A>,
StorageRef: ops::DerefMut + Sync,
StorageRef::Target: Storage<RawEntity = <A as Archetype>::RawEntity, Comp = C>,
{
pub fn par_iter_mut<'t>(
&'t mut self,
snapshot: &'t ealloc::Snapshot<<A as Archetype>::RawEntity>,
) -> impl ParallelIterator<Item = (entity::TempRef<'t, A>, &'t mut C)> {
rayon::iter::split((self.as_partition(), snapshot.as_slice()), |(partition, slice)| {
let Some(midpt) = slice.midpoint_for_split() else { return ((partition, slice), None) };
let (slice_left, slice_right) = slice.split_at(midpt);
let (partition_left, partition_right) = partition.split_at(midpt);
((partition_left, slice_left), Some((partition_right, slice_right)))
})
.flat_map_iter(|(partition, _slice)| partition.into_iter_mut())
}
}
impl<'t, A, C, StorageT> Single<A, C, util::OwnedDeref<StorageT>>
where
A: Archetype,
C: comp::SimpleOrIsotope<A>,
StorageT: storage::Partition<'t, RawEntity = A::RawEntity, Comp = C>,
{
pub fn split_at(mut self, entity: A::RawEntity) -> (Self, Self) {
let right = self.split_out(entity);
(self, right)
}
pub fn split_out(&mut self, entity: A::RawEntity) -> Self {
let right = self.storage.0.split_out(entity);
Self { storage: util::OwnedDeref(right), _ph: PhantomData }
}
pub fn try_into_mut(self, entity: impl entity::Ref<Archetype = A>) -> Option<&'t mut C> {
self.storage.0.into_mut(entity.id())
}
}
impl<'t, A, C, StorageT> Single<A, C, util::OwnedDeref<StorageT>>
where
A: Archetype,
C: comp::SimpleOrIsotope<A> + comp::Must<A>,
StorageT: storage::Partition<'t, RawEntity = A::RawEntity, Comp = C>,
{
pub fn into_mut(self, entity: impl entity::Ref<Archetype = A>) -> &'t mut C {
match self.try_into_mut(entity) {
Some(comp) => comp,
None => panic!(
"Component {}/{} implements comp::Must but is not present",
any::type_name::<A>(),
any::type_name::<C>(),
),
}
}
pub fn into_iter_mut(self) -> impl Iterator<Item = (entity::TempRef<'t, A>, &'t mut C)> {
self.storage.0.into_iter_mut().map(|(entity, data)| (entity::TempRef::new(entity), data))
}
}
#[derive_trait(pub GetMutChunked{
/// The archetype that this accessor retrieves for.
type Arch: Archetype = A;
/// The component that this accessor retrieves.
type Comp: comp::SimpleOrIsotope<Self::Arch> + comp::Must<Self::Arch> = C;
})]
impl<A, C, StorageRef> Single<A, C, StorageRef>
where
A: Archetype,
C: comp::SimpleOrIsotope<A> + comp::Must<A>,
StorageRef: ops::DerefMut + Sync,
StorageRef::Target: storage::Chunked<RawEntity = <A as Archetype>::RawEntity, Comp = C>,
for<'u> <StorageRef::Target as Storage>::Partition<'u>: storage::PartitionChunked<'u>,
{
pub fn get_chunk_mut(&mut self, chunk: entity::TempRefChunk<A>) -> &[C] {
self.storage.get_chunk(chunk.start, chunk.end).expect("chunk is not completely filled")
}
pub fn par_iter_chunks_mut<'t>(
&'t mut self,
snapshot: &'t ealloc::Snapshot<<A as Archetype>::RawEntity>,
) -> impl ParallelIterator<Item = (entity::TempRefChunk<'t, A>, &'t mut [C])> {
rayon::iter::split((self.as_partition(), snapshot.as_slice()), |(partition, slice)| {
let Some(midpt) = slice.midpoint_for_split() else { return ((partition, slice), None) };
let (slice_left, slice_right) = slice.split_at(midpt);
let (partition_left, partition_right) = partition.split_at(midpt);
((partition_left, slice_left), Some((partition_right, slice_right)))
})
.flat_map_iter(|(partition, _slice)| partition.into_iter_chunks_mut())
}
}
impl<'t, A, C, StorageT> Single<A, C, util::OwnedDeref<StorageT>>
where
A: Archetype,
C: comp::SimpleOrIsotope<A> + comp::Must<A>,
StorageT: storage::PartitionChunked<'t, RawEntity = A::RawEntity, Comp = C>,
{
pub fn into_chunk_mut(self, chunk: entity::TempRefChunk<A>) -> &'t mut [C] {
match self.storage.0.into_chunk_mut(chunk.start, chunk.end) {
Some(comp) => comp,
None => panic!(
"Component {}/{} implements comp::Must but is not present",
any::type_name::<A>(),
any::type_name::<C>()
),
}
}
pub fn into_iter_chunks_mut(
self,
) -> impl Iterator<Item = (entity::TempRefChunk<'t, A>, &'t mut [C])> {
self.storage
.0
.into_iter_chunks_mut()
.map(|(entity, data)| (entity::TempRefChunk::new(entity, entity.add(data.len())), data))
}
}
#[cfg(test)]
mod tests;