sparsey 0.13.3

Entity Component System based on sparse sets
Documentation
use crate::component::{ComponentSparseSet, GroupMask, QueryMask};
use crate::entity::Entity;
use atomic_refcell::AtomicRefCell;
use core::ops::Range;

#[derive(Clone, Copy, Debug)]
pub(crate) struct Group {
    pub metadata: GroupMetadata,
    pub len: usize,
}

/// Example:
///
/// Say we have a family made of two groups:
/// - Group 0: (A, B)
/// - Group 1: (A, B, C, D)
///
/// For 'Group 1', we get such a `GroupMetadata` struct:
///
/// ```text
/// storages: A B C D
///           ^   ^   ^
///           |   |   +-- storage_end = storage_start + 4
///           |   +------ new_storage_start = storage_start + 2
///           +---------- storage_start
/// ```
#[derive(Clone, Copy, Debug)]
pub(crate) struct GroupMetadata {
    pub storage_start: usize,
    pub new_storage_start: usize,
    pub storage_end: usize,
    pub skip_mask: GroupMask,
    pub include_mask: QueryMask,
    pub exclude_mask: QueryMask,
}

impl GroupMetadata {
    #[inline]
    #[must_use]
    pub fn storage_range(&self) -> Range<usize> {
        self.storage_start..self.storage_end
    }

    #[inline]
    #[must_use]
    pub fn new_storage_range(&self) -> Range<usize> {
        self.new_storage_start..self.storage_end
    }
}

pub(crate) unsafe fn group(
    components: &mut [AtomicRefCell<ComponentSparseSet>],
    groups: &mut [Group],
    group_mask: GroupMask,
    entity: Entity,
) {
    let mut group_index_iter = group_mask.iter_bit_indexes();

    while let Some(group_index) = group_index_iter.next() {
        let group = groups.get_unchecked_mut(group_index as usize);

        let status = get_group_status(
            &mut components[group.metadata.new_storage_range()],
            group.len,
            entity,
        );

        match status {
            GroupStatus::Incomplete => {
                group_index_iter.0 &= group.metadata.skip_mask.0;
            }
            GroupStatus::Ungrouped => {
                group_components(
                    &mut components[group.metadata.storage_range()],
                    &mut group.len,
                    entity,
                );
            }
            GroupStatus::Grouped => (),
        }
    }
}

pub(crate) unsafe fn ungroup(
    components: &mut [AtomicRefCell<ComponentSparseSet>],
    groups: &mut [Group],
    group_mask: GroupMask,
    entity: Entity,
) {
    for group_index in group_mask.iter_bit_indexes().rev() {
        let group = groups.get_unchecked_mut(group_index as usize);

        let status = get_group_status(
            &mut components[group.metadata.new_storage_range()],
            group.len,
            entity,
        );

        if status == GroupStatus::Grouped {
            ungroup_components(
                &mut components[group.metadata.storage_range()],
                &mut group.len,
                entity,
            );
        }
    }
}

pub(crate) unsafe fn ungroup_all(
    components: &mut [AtomicRefCell<ComponentSparseSet>],
    groups: &mut [Group],
    entity: Entity,
) {
    for group in groups.iter_mut().rev() {
        let status = get_group_status(
            &mut components[group.metadata.new_storage_range()],
            group.len,
            entity,
        );

        if status == GroupStatus::Grouped {
            ungroup_components(
                &mut components[group.metadata.storage_range()],
                &mut group.len,
                entity,
            );
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
enum GroupStatus {
    Incomplete,
    Ungrouped,
    Grouped,
}

unsafe fn get_group_status(
    components: &mut [AtomicRefCell<ComponentSparseSet>],
    group_len: usize,
    entity: Entity,
) -> GroupStatus {
    let (first, others) = components.split_first_mut().unwrap_unchecked();

    let status = match first.get_mut().sparse().get(entity) {
        Some(dense) => {
            if (dense as usize) < group_len {
                GroupStatus::Grouped
            } else {
                GroupStatus::Ungrouped
            }
        }
        None => return GroupStatus::Incomplete,
    };

    let sparse = entity.sparse();

    if others
        .iter_mut()
        .all(|sparse_set| sparse_set.get_mut().sparse().contains_sparse(sparse))
    {
        status
    } else {
        GroupStatus::Incomplete
    }
}

#[inline]
unsafe fn group_components(
    components: &mut [AtomicRefCell<ComponentSparseSet>],
    group_len: &mut usize,
    entity: Entity,
) {
    let swap_index = *group_len;
    let sparse = entity.sparse();

    components
        .iter_mut()
        .map(AtomicRefCell::get_mut)
        .for_each(|sparse_set| {
            let dense = sparse_set.sparse().get_sparse_unchecked(sparse);

            if dense != swap_index {
                sparse_set.swap(dense, swap_index);
            }
        });

    *group_len += 1;
}

#[inline]
unsafe fn ungroup_components(
    components: &mut [AtomicRefCell<ComponentSparseSet>],
    group_len: &mut usize,
    entity: Entity,
) {
    *group_len -= 1;
    let swap_index = *group_len;
    let sparse = entity.sparse();

    components
        .iter_mut()
        .map(AtomicRefCell::get_mut)
        .for_each(|sparse_set| {
            let dense = sparse_set.sparse().get_sparse_unchecked(sparse);

            if dense != swap_index {
                sparse_set.swap(dense, swap_index);
            }
        });
}