rs-ecs 0.7.12

reasonably simple entity component system
Documentation
use std::any::{type_name, TypeId};
use std::cell::UnsafeCell;

use crate::archetype::TypeMetadataSet;

#[derive(Default)]
pub struct BorrowFlags(Vec<(TypeId, UnsafeCell<isize>)>);

impl BorrowFlags {
    pub fn insert(&mut self, types: &TypeMetadataSet) {
        for id in types.ids() {
            if let Err(idx) = self.0.binary_search_by_key(&id, |(id, _)| *id) {
                assert!(self.0.len() < u16::MAX as usize);

                self.0.insert(idx, (id, UnsafeCell::new(0)));
            }
        }
    }

    pub fn find<C>(&self) -> Option<u16>
    where
        C: 'static,
    {
        self.find_impl(&TypeId::of::<C>())
    }

    fn find_impl(&self, key: &TypeId) -> Option<u16> {
        self.0
            .binary_search_by_key(key, |(id, _)| *id)
            .map(|idx| idx as u16)
            .ok()
    }

    #[cfg(debug_assertions)]
    fn flag<C>(&self, ty: u16) -> &UnsafeCell<isize>
    where
        C: 'static,
    {
        let (id, flag) = &self.0[ty as usize];
        assert_eq!(id, &TypeId::of::<C>());
        flag
    }

    #[cfg(not(debug_assertions))]
    unsafe fn flag_unchecked(&self, ty: u16) -> &UnsafeCell<isize> {
        &self.0.get_unchecked(ty as usize).1
    }

    pub unsafe fn borrow<C>(&self, ty: u16) -> Ref<'_>
    where
        C: 'static,
    {
        #[cfg(debug_assertions)]
        let flag = self.flag::<C>(ty);
        #[cfg(not(debug_assertions))]
        let flag = self.flag_unchecked(ty);

        Ref::new(flag).unwrap_or_else(|| panic!("Component {} already borrowed", type_name::<C>()))
    }

    pub unsafe fn borrow_mut<C>(&self, ty: u16) -> RefMut<'_>
    where
        C: 'static,
    {
        #[cfg(debug_assertions)]
        let flag = self.flag::<C>(ty);
        #[cfg(not(debug_assertions))]
        let flag = self.flag_unchecked(ty);

        RefMut::new(flag)
            .unwrap_or_else(|| panic!("Component {} already borrowed", type_name::<C>()))
    }
}

impl Clone for BorrowFlags {
    fn clone(&self) -> Self {
        Self(
            self.0
                .iter()
                .map(|(id, _)| (*id, UnsafeCell::new(0)))
                .collect(),
        )
    }
}

pub struct Ref<'a>(&'a UnsafeCell<isize>);

impl<'a> Ref<'a> {
    pub(crate) fn new(borrow: &'a UnsafeCell<isize>) -> Option<Self> {
        let readers = unsafe { &mut *borrow.get() };

        let new_readers = readers.wrapping_add(1);

        if new_readers <= 0 {
            cold();
            return None;
        }

        *readers = new_readers;

        Some(Self(borrow))
    }
}

impl Drop for Ref<'_> {
    fn drop(&mut self) {
        unsafe {
            *self.0.get() -= 1;
        }
    }
}

pub struct RefMut<'a>(&'a UnsafeCell<isize>);

impl<'a> RefMut<'a> {
    pub(crate) fn new(borrow: &'a UnsafeCell<isize>) -> Option<Self> {
        let writers = unsafe { &mut *borrow.get() };

        if *writers != 0 {
            cold();
            return None;
        }

        *writers = -1;

        Some(Self(borrow))
    }
}

impl Drop for RefMut<'_> {
    fn drop(&mut self) {
        unsafe {
            *self.0.get() = 0;
        }
    }
}

#[cold]
#[inline(always)]
fn cold() {}