components-arena 0.0.1

Simple library for creating complex domain-specific self-referential data structures.
Documentation
#![deny(warnings)]
#![feature(const_fn)]

#![cfg_attr(not(feature="std"), no_std)]
#[cfg(not(feature="std"))]
extern crate alloc;
#[cfg(not(feature="std"))]
pub(crate) mod std {
    pub use core::*;
    pub use ::alloc::vec;
}

#[macro_use]
extern crate derivative;

use std::convert::{TryFrom, TryInto};
use std::fmt::Debug;
use std::hash::Hash;
use std::hint::unreachable_unchecked;
use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize};
use std::sync::atomic::{AtomicBool, Ordering};
use std::vec::Vec;
#[cfg(feature="std")]
use once_cell::sync::{self};
#[cfg(feature="std")]
use std::sync::Mutex;
#[cfg(feature="std")]
use std::ops::Deref;

pub unsafe trait ComponentId: Debug + Copy + Eq + Hash + Ord {
    fn inc(this: Option<Self>) -> Option<Self>;
}

unsafe impl ComponentId for NonZeroU8 {
    fn inc(this: Option<Self>) -> Option<Self> {
        Self::new(this.map_or(0, |x| x.get()).overflowing_add(1).0)
    }
}

unsafe impl ComponentId for NonZeroU16 {
    fn inc(this: Option<Self>) -> Option<Self> {
        Self::new(this.map_or(0, |x| x.get()).overflowing_add(1).0)
    }
}

unsafe impl ComponentId for NonZeroU32 {
    fn inc(this: Option<Self>) -> Option<Self> {
        Self::new(this.map_or(0, |x| x.get()).overflowing_add(1).0)
    }
}

unsafe impl ComponentId for NonZeroU64 {
    fn inc(this: Option<Self>) -> Option<Self> {
        Self::new(this.map_or(0, |x| x.get()).overflowing_add(1).0)
    }
}

unsafe impl ComponentId for NonZeroU128 {
    fn inc(this: Option<Self>) -> Option<Self> {
        Self::new(this.map_or(0, |x| x.get()).overflowing_add(1).0)
    }
}

unsafe impl ComponentId for NonZeroUsize {
    fn inc(this: Option<Self>) -> Option<Self> {
        Self::new(this.map_or(0, |x| x.get()).overflowing_add(1).0)
    }
}

pub unsafe trait ComponentIndex: Debug + Copy + Eq + Hash + Ord + TryInto<usize> + TryFrom<usize> { }

unsafe impl ComponentIndex for u8 { }

unsafe impl ComponentIndex for u16 { }

unsafe impl ComponentIndex for u32 { }

unsafe impl ComponentIndex for u64 { }

unsafe impl ComponentIndex for u128 { }

unsafe impl ComponentIndex for usize { }

pub struct ComponentsTokenLock(AtomicBool);

impl ComponentsTokenLock {
    pub const fn new() -> Self { ComponentsTokenLock(AtomicBool::new(false)) }
}

impl Default for ComponentsTokenLock {
    fn default() -> Self { ComponentsTokenLock::new() }
}

pub unsafe trait ComponentImpl {
    type Id: ComponentId;
    type Index: ComponentIndex;
    fn components_token_lock() -> &'static ComponentsTokenLock;
}

pub trait Component {
    type Impl: ComponentImpl;
    /// # Safety
    /// This function should not be called at all. There are no circumstances when such call could be safe.
    unsafe fn impl_type(self) -> Self::Impl;
}

#[derive(Derivative)]
#[derivative(Debug(bound=""), Copy(bound=""), Clone(bound=""), Eq(bound=""), PartialEq(bound=""))]
#[derivative(Hash(bound=""), Ord(bound=""), PartialOrd(bound=""))]
pub struct Id<C: Component> {
    index: <<C as Component>::Impl as ComponentImpl>::Index,
    id: <<C as Component>::Impl as ComponentImpl>::Id,
}

#[derive(Debug)]
pub struct Components<C: Component> {
    items: Vec<Option<(<<C as Component>::Impl as ComponentImpl>::Id, C)>>,
    vacancies: Vec<<<C as Component>::Impl as ComponentImpl>::Index>,
}

pub struct ComponentsToken<C: ComponentImpl>(Option<C::Id>);

impl<C: ComponentImpl> ComponentsToken<C> {
    pub fn new() -> Option<ComponentsToken<C>> {
        let lock = C::components_token_lock();
        if lock.0.compare_and_swap(false, true, Ordering::Relaxed) {
            None
        } else {
            Some(ComponentsToken(None))
        }
    }
}

impl<C: Component> Components<C> {
    pub fn new() -> Self {
        Components { items: Vec::new(), vacancies: Vec::new() }
    }

    pub fn attach(&mut self, token: &mut ComponentsToken<C::Impl>, component: impl FnOnce(Id<C>) -> C) -> Id<C> {
        let id = <<C as Component>::Impl as ComponentImpl>::Id::inc(token.0).expect("component ids exhausted");
        token.0 = Some(id);
        if let Some(index) = self.vacancies.pop() {
            let index_id = Id { index, id };
            let item = (id, component(index_id));
            let index_as_usize = index.try_into().unwrap_or_else(|_| unsafe { unreachable_unchecked() });
            let none = self.items[index_as_usize].replace(item);
            debug_assert!(none.is_none());
            index_id
        } else {
            let index = self.items.len().try_into().unwrap_or_else(|_| panic!("component indexes exhausted"));
            let index_id = Id { index, id };
            let item = (id, component(index_id));
            self.items.push(Some(item));
            index_id
        }
    }

    #[must_use]
    pub fn detach(&mut self, id: Id<C>) -> Option<C> {
        let index_as_usize = id.index.try_into().unwrap_or_else(|_| unsafe { unreachable_unchecked() });
        if self.items.len() <= index_as_usize { return None; }
        if let Some((item_id, component)) = self.items[index_as_usize].take() {
            if item_id == id.id {
                self.vacancies.push(id.index);
                Some(component)
            } else {
                let none = self.items[index_as_usize].replace((item_id, component));
                debug_assert!(none.is_none());
                None
            }
        } else {
            None
        }
    }

    pub fn get(&self, id: Id<C>) -> Option<&C> {
        let index_as_usize = id.index.try_into().unwrap_or_else(|_| unsafe { unreachable_unchecked() });
        if self.items.len() <= index_as_usize { return None; }
        self.items[index_as_usize].as_ref().and_then(|&(item_id, ref component)| {
            if item_id == id.id {
                Some(component)
            } else {
                None
            }
        })
    }

    pub fn get_mut(&mut self, id: Id<C>) -> Option<&mut C> {
        let index_as_usize = id.index.try_into().unwrap_or_else(|_| unsafe { unreachable_unchecked() });
        if self.items.len() <= index_as_usize { return None; }
        self.items[index_as_usize].as_mut().and_then(|&mut (item_id, ref mut component)| {
            if item_id == id.id {
                Some(component)
            } else {
                None
            }
        })
    }
}

impl<C: Component> Default for Components<C> {
    fn default() -> Self { Components::new() }
}

#[cfg(feature="std")]
pub struct ComponentsTokenMutex<C: ComponentImpl>(sync::Lazy<Mutex<ComponentsToken<C>>>);

#[cfg(feature="std")]
impl<C: ComponentImpl> ComponentsTokenMutex<C> {
    pub const fn new() -> Self {
        ComponentsTokenMutex(sync::Lazy::new(|| Mutex::new(
            ComponentsToken::new().unwrap_or_else(|| unsafe { unreachable_unchecked() })
        )))
    }
}

#[cfg(feature="std")]
impl<C: ComponentImpl> Deref for ComponentsTokenMutex<C> {
    type Target = Mutex<ComponentsToken<C>>;

    fn deref(&self) -> &Self::Target { self.0.deref() }
}

#[cfg(feature="std")]
#[macro_export]
macro_rules! Component {
    ((Id=$id:ty, $($x:tt)*) $($y:tt)*) => {
        Component! { ($($x)*, Id=$id) $($y)* }
    };
    ((Impl=$impl:ident, $($x:tt),*) $($y:tt)*) => {
        Component! { ($($x)*, Impl=$impl) $($y)* }
    };
    ((TOKEN=$token:ident, $($x:tt)*) $($y:tt)*) => {
        Component! { ($($x)*, TOKEN=$token) $($y)* }
    };
    ((Index=$index:ty, $($x:tt)*) $($y:tt)*) => {
        Component! { ($($x)*, Index=$index) $($y)* }
    };
    ((TOKEN_LOCK=$token_lock:ident, Id=$id:ty, $($x:tt)*) $($y:tt)*) => {
        Component! { (TOKEN_LOCK=$token_lock, $($x)*, Id=$id) $($y)* }
    };
    ((TOKEN_LOCK=$token_lock:ident, Impl=$impl:ident, $($x:tt)*) $($y:tt)*) => {
        Component! { (TOKEN_LOCK=$token_lock, $($x)*, Impl=$impl) $($y)* }
    };
    ((TOKEN_LOCK=$token_lock:ident, TOKEN=$token:ident, $($x:tt)*) $($y:tt)*) => {
        Component! { (TOKEN_LOCK=$token_lock, $($x)*, TOKEN=$token) $($y)* }
    };
    ((TOKEN_LOCK=$token_lock:ident, Index=$index:ty, TOKEN=$token:ident, $($x:tt)*) $($y:tt)*) => {
        Component! { (TOKEN_LOCK=$token_lock, Index=$index, $($x)*, TOKEN=$token) $($y)* }
    };
    ((TOKEN_LOCK=$token_lock:ident, Index=$index:ty, Impl=$impl:ident, $($x:tt)*) $($y:tt)*) => {
        Component! { (TOKEN_LOCK=$token_lock, Index=$index, $($x)*, Impl=$impl) $($y)* }
    };
    ((TOKEN_LOCK=$token_lock:ident, Index=$index:ty, Id=$id:ty, Impl=$impl:ident, TOKEN=$token:ident) $($y:tt)*) => {
        Component! { ( TOKEN_LOCK=$token_lock, Index=$index, Id=$id, TOKEN=$token, Impl=$impl ) $($y)* }
    };
    ((TOKEN_LOCK=$token_lock:ident, Index=$index:ty, Id=$id:ty, TOKEN=$token:ident, Impl=$impl:ident)
        $(pub $((crate))?)? struct $name:ident
        < $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > { $($tail:tt)* } ) => {
        Component! {
            @impl $name, $id, $index, $token_lock, $token, $impl,
            < $( $lt ),+ >,
            < $( $lt $( : $clt $(+ $dlt )* )? ),+ >
        }
    };
    ((TOKEN_LOCK=$token_lock:ident, Index=$index:ty, Id=$id:ty, Impl=$impl:ident)
        $(pub $((crate))?)? struct $name:ident
        < $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > { $($tail:tt)* } ) => {
        Component! {
            @impl $name, $id, $index, $token_lock, $impl,
            < $( $lt ),+ >,
            < $( $lt $( : $clt $(+ $dlt )* )? ),+ >
        }
    };
    ((TOKEN_LOCK=$token_lock:ident, Index=$index:ty, Id=$id:ty, TOKEN=$token:ident)
        $(pub $((crate))?)? struct $name:ident
        { $($tail:tt)* } ) => {
        Component! {
            @impl $name, $id, $index, $token_lock, $token
        }
    };
    ((TOKEN_LOCK=$token_lock:ident, Index=$index:ty, Id=$id:ty)
        $(pub $((crate))?)? struct $name:ident
        { $($tail:tt)* } ) => {
        Component! {
            @impl $name, $id, $index, $token_lock
        }
    };
    (@impl $name:ident, $id:ty, $index:ty, $token_lock:ident) => {
        static $token_lock: $crate::ComponentsTokenLock = $crate::ComponentsTokenLock::new();
        unsafe impl $crate::ComponentImpl for $name {
            type Id = $id;
            type Index = $index;
            fn components_token_lock() -> &'static $crate::ComponentsTokenLock { &$token_lock }
        }
        unsafe impl $crate::Component for $name {
            type Impl = Self;
            unsafe fn impl_type(self) -> Self { self }
        }
    };
    (@impl $name:ident, $id:ty, $index:ty, $token_lock:ident, $impl:ident, < $g:tt >, < $r:tt >) => {
        static $token_lock: $crate::ComponentsTokenLock = $crate::ComponentsTokenLock::new();
        struct $impl;
        unsafe impl $crate::ComponentImpl for $impl {
            type Id = $id;
            type Index = $index;
            fn components_token_lock() -> &'static $crate::ComponentsTokenLock { &$token_lock }
        }
        unsafe impl< $g > $crate::Component for $name < $r > {
            type Impl = impl $crate::ComponentImpl<Id=$id, Index=$index>;
            unsafe fn impl_type(self) -> Self::Impl { $impl }
        }
    };
    (@impl $name:ident, $id:ty, $index:ty, $token_lock:ident, $token:ident) => {
        static $token_lock: $crate::ComponentsTokenLock = $crate::ComponentsTokenLock::new();
        static $token: $crate::ComponentsTokenMutex<$name> = $crate::ComponentsTokenMutex::new();
        unsafe impl $crate::ComponentImpl for $name {
            type Id = $id;
            type Index = $index;
            fn components_token_lock() -> &'static $crate::ComponentsTokenLock { &$token_lock }
        }
        unsafe impl $crate::Component for $name {
            type Impl = Self;
            unsafe fn impl_type(self) -> Self { self }
        }
    };
    (@impl $name:ident, $id:ty, $index:ty, $token_lock:ident, $token:ident, $impl:ident, < $g:tt >, < $r:tt >) => {
        static $token_lock: $crate::ComponentsTokenLock = $crate::ComponentsTokenLock::new();
        static $token: $crate::ComponentsTokenMutex<$impl> = $crate::ComponentsTokenMutex::new();
        struct $impl;
        unsafe impl $crate::ComponentImpl for $impl {
            type Id = $id;
            type Index = $index;
            fn components_token_lock() -> &'static $crate::ComponentsTokenLock { &$token_lock }
        }
        unsafe impl< $g > $crate::Component for $name < $r > {
            type Impl = impl $crate::ComponentImpl<Id=$id, Index=$index>;
            unsafe fn impl_type(self) -> Self::Impl { $impl }
        }
    };
}

#[cfg(not(feature="std"))]
#[macro_export]
macro_rules! Component {
    ((Id=$id:ty, $($x:tt)*) $($y:tt)*) => {
        Component! { ($($x)*, Id=$id) $($y)* }
    };
    ((Impl=$impl:ident, $($x:tt)*) $($y:tt)*) => {
        Component! { ($($x)*, Impl=$impl) $($y)* }
    };
    ((Index=$index:ty, $($x:tt)*) $($y:tt)*) => {
        Component! { ($($x)*, Index=$index) $($y)* }
    };
    ((TOKEN_LOCK=$token_lock:ident, Id=$id:ty, $($x:tt)*) $($y:tt)*) => {
        Component! { (TOKEN_LOCK=$token_lock, $($x)*, Id=$id) $($y)* }
    };
    ((TOKEN_LOCK=$token_lock:ident, Impl=$impl:ident, $($x:tt)*) $($y:tt)*) => {
        Component! { (TOKEN_LOCK=$token_lock, $($x)*, Impl=$impl) $($y)* }
    };
    ((TOKEN_LOCK=$token_lock:ident, Index=$index:ty, Impl=$impl:ident, Id=$id:ty) $($y:tt)*) => {
        Component! { (TOKEN_LOCK=$token_lock, Index=$index, Id=$id, Impl=$impl) $($y)* }
    };
    ((TOKEN_LOCK=$token_lock:ident, Index=$index:ty, Id=$id:ty, Impl=$impl:ident)
        $(pub $((crate))?)? struct $name:ident
        < $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > { $($tail:tt)* } ) => {
        Component! {
            @impl $name, $id, $index, $token_lock, $impl,
            < $( $lt ),+ >,
            < $( $lt $( : $clt $(+ $dlt )* )? ),+ >
        }
    };
    ((TOKEN_LOCK=$token_lock:ident, Index=$index:ty, Id=$id:ty)
        $(pub $((crate))?)? struct $name:ident
        { $($tail:tt)* } ) => {
        Component! {
            @impl $name, $id, $index, $token_lock
        }
    };
    (@impl $name:ident, $id:ty, $index:ty, $token_lock:ident) => {
        static $token_lock: $crate::ComponentsTokenLock = $crate::ComponentsTokenLock::new();
        unsafe impl $crate::ComponentImpl for $name {
            type Id = $id;
            type Index = $index;
            fn components_token_lock() -> &'static $crate::ComponentsTokenLock { &$token_lock }
        }
        unsafe impl $crate::Component for $name {
            type Impl = Self;
            unsafe fn impl_type(self) -> Self { self }
        }
    };
    (@impl $name:ident, $id:ty, $index:ty, $token_lock:ident, $impl:ident, < $g:tt >, < $r:tt >) => {
        static $token_lock: $crate::ComponentsTokenLock = $crate::ComponentsTokenLock::new();
        struct $impl;
        unsafe impl $crate::ComponentImpl for $impl {
            type Id = $id;
            type Index = $index;
            fn components_token_lock() -> &'static $crate::ComponentsTokenLock { &$token_lock }
        }
        unsafe impl< $g > $crate::Component for $name < $r > {
            type Impl = impl $crate::ComponentImpl<Id=$id, Index=$index>;
            unsafe fn impl_type(self) -> Self::Impl { $impl }
        }
    };
}