#![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 ComponentUnique: Debug + Copy + Eq + Hash + Ord {
fn inc(this: Option<Self>) -> Option<Self>;
}
unsafe impl ComponentUnique for NonZeroU8 {
fn inc(this: Option<Self>) -> Option<Self> {
Self::new(this.map_or(0, |x| x.get()).overflowing_add(1).0)
}
}
unsafe impl ComponentUnique for NonZeroU16 {
fn inc(this: Option<Self>) -> Option<Self> {
Self::new(this.map_or(0, |x| x.get()).overflowing_add(1).0)
}
}
unsafe impl ComponentUnique for NonZeroU32 {
fn inc(this: Option<Self>) -> Option<Self> {
Self::new(this.map_or(0, |x| x.get()).overflowing_add(1).0)
}
}
unsafe impl ComponentUnique for NonZeroU64 {
fn inc(this: Option<Self>) -> Option<Self> {
Self::new(this.map_or(0, |x| x.get()).overflowing_add(1).0)
}
}
unsafe impl ComponentUnique for NonZeroU128 {
fn inc(this: Option<Self>) -> Option<Self> {
Self::new(this.map_or(0, |x| x.get()).overflowing_add(1).0)
}
}
unsafe impl ComponentUnique 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 ComponentClassLock(AtomicBool);
impl ComponentClassLock {
pub const fn new() -> Self { ComponentClassLock(AtomicBool::new(false)) }
}
impl Default for ComponentClassLock {
fn default() -> Self { ComponentClassLock::new() }
}
pub unsafe trait ComponentClass {
type Index: ComponentIndex;
type Unique: ComponentUnique;
fn lock() -> &'static ComponentClassLock;
}
pub trait Component {
type Class: ComponentClass;
}
#[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>::Class as ComponentClass>::Index,
unique: <<C as Component>::Class as ComponentClass>::Unique,
}
#[derive(Debug)]
pub struct Arena<C: Component> {
items: Vec<Option<(<<C as Component>::Class as ComponentClass>::Unique, C)>>,
vacancies: Vec<<<C as Component>::Class as ComponentClass>::Index>,
}
pub struct ComponentClassToken<C: ComponentClass>(Option<C::Unique>);
impl<C: ComponentClass> ComponentClassToken<C> {
pub fn new() -> Option<ComponentClassToken<C>> {
let lock = C::lock();
if lock.0.compare_and_swap(false, true, Ordering::Relaxed) {
None
} else {
Some(ComponentClassToken(None))
}
}
}
impl<C: Component> Arena<C> {
pub fn new() -> Self {
Arena { items: Vec::new(), vacancies: Vec::new() }
}
pub fn push(&mut self, token: &mut ComponentClassToken<C::Class>, component: impl FnOnce(Id<C>) -> C) -> Id<C> {
let unique = <<C as Component>::Class as ComponentClass>::Unique::inc(token.0).expect("component ids exhausted");
token.0 = Some(unique);
if let Some(index) = self.vacancies.pop() {
let id = Id { index, unique };
let item = (unique, component(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());
id
} else {
let index = self.items.len().try_into().unwrap_or_else(|_| panic!("component indexes exhausted"));
let id = Id { index, unique };
let item = (unique, component(id));
self.items.push(Some(item));
id
}
}
#[must_use]
pub fn pop(&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; }
self.items[index_as_usize].take().and_then(|(unique, component)| {
if unique == id.unique {
self.vacancies.push(id.index);
Some(component)
} else {
let none = self.items[index_as_usize].replace((unique, component));
debug_assert!(none.is_none());
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(|&(unique, ref component)| {
if unique == id.unique {
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 (unique, ref mut component)| {
if unique == id.unique {
Some(component)
} else {
None
}
})
}
}
impl<C: Component> Default for Arena<C> {
fn default() -> Self { Arena::new() }
}
#[cfg(feature="std")]
pub struct ComponentClassMutex<C: ComponentClass>(sync::Lazy<Mutex<ComponentClassToken<C>>>);
#[cfg(feature="std")]
impl<C: ComponentClass> ComponentClassMutex<C> {
pub const fn new() -> Self {
ComponentClassMutex(sync::Lazy::new(|| Mutex::new(
ComponentClassToken::new().unwrap_or_else(|| unsafe { unreachable_unchecked() })
)))
}
}
#[cfg(feature="std")]
impl<C: ComponentClass> Deref for ComponentClassMutex<C> {
type Target = Mutex<ComponentClassToken<C>>;
fn deref(&self) -> &Self::Target { self.0.deref() }
}
#[macro_export]
macro_rules! Component {
((index=$index:ty, unique=$unique:ty, class=$class:ident)
enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((index=$index:ty, class=$class:ident, unique=$unique:ty)
enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, index=$index:ty, class=$class:ident)
enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, class=$class:ident, index=$index:ty)
enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, index=$index:ty, unique=$unique:ty)
enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, unique=$unique:ty, index=$index:ty)
enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((index=$index:ty, unique=$unique:ty, class=$class:ident)
pub(crate) enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((index=$index:ty, class=$class:ident, unique=$unique:ty)
pub(crate) enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, index=$index:ty, class=$class:ident)
pub(crate) enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, class=$class:ident, index=$index:ty)
pub(crate) enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, index=$index:ty, unique=$unique:ty)
pub(crate) enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, unique=$unique:ty, index=$index:ty)
pub(crate) enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((index=$index:ty, unique=$unique:ty, class=$class:ident)
pub enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((index=$index:ty, class=$class:ident, unique=$unique:ty)
pub enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, index=$index:ty, class=$class:ident)
pub enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, class=$class:ident, index=$index:ty)
pub enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, index=$index:ty, unique=$unique:ty)
pub enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, unique=$unique:ty, index=$index:ty)
pub enum $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, index=$index:ty)
$(pub $((crate))?)? enum $name:ident
$tail:tt ) => {
Component! {
@impl $name, $unique, $index
}
};
((index=$index:ty, unique=$unique:ty)
$(pub $((crate))?)? enum $name:ident
$tail:tt ) => {
Component! {
@impl $name, $unique, $index
}
};
((index=$index:ty, unique=$unique:ty, class=$class:ident)
struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((index=$index:ty, class=$class:ident, unique=$unique:ty)
struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, index=$index:ty, class=$class:ident)
struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, class=$class:ident, index=$index:ty)
struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, index=$index:ty, unique=$unique:ty)
struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, unique=$unique:ty, index=$index:ty)
struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl () $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((index=$index:ty, unique=$unique:ty, class=$class:ident)
pub(crate) struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((index=$index:ty, class=$class:ident, unique=$unique:ty)
pub(crate) struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, index=$index:ty, class=$class:ident)
pub(crate) struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, class=$class:ident, index=$index:ty)
pub(crate) struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, index=$index:ty, unique=$unique:ty)
pub(crate) struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, unique=$unique:ty, index=$index:ty)
pub(crate) struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub(crate)) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((index=$index:ty, unique=$unique:ty, class=$class:ident)
pub struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((index=$index:ty, class=$class:ident, unique=$unique:ty)
pub struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, index=$index:ty, class=$class:ident)
pub struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, class=$class:ident, index=$index:ty)
pub struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, index=$index:ty, unique=$unique:ty)
pub struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((class=$class:ident, unique=$unique:ty, index=$index:ty)
pub struct $name:ident
< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ > $tail:tt ) => {
Component! {
@impl (pub) $name, $unique, $index, $class,
< $( $lt ),+ >,
< $( $lt $( : $clt $(+ $dlt )* )? ),+ >
}
};
((unique=$unique:ty, index=$index:ty)
$(pub $((crate))?)? struct $name:ident
$tail:tt ) => {
Component! {
@impl $name, $unique, $index
}
};
((index=$index:ty, unique=$unique:ty)
$(pub $((crate))?)? struct $name:ident
$tail:tt ) => {
Component! {
@impl $name, $unique, $index
}
};
(@impl $name:ident, $unique:ty, $index:ty) => {
unsafe impl $crate::ComponentClass for $name {
type Unique = $unique;
type Index = $index;
fn lock() -> &'static $crate::ComponentClassLock {
static CLASS_LOCK: $crate::ComponentClassLock = $crate::ComponentClassLock::new();
&CLASS_LOCK
}
}
impl $crate::Component for $name {
type Class = Self;
}
};
(@impl ($($p:tt $($c:tt)?)?) $name:ident, $unique:ty, $index:ty, $class:ident, < $g:tt >, < $r:tt >) => {
$($p $($c)?)? enum $class { }
unsafe impl $crate::ComponentClass for $class {
type Unique = $unique;
type Index = $index;
fn lock() -> &'static $crate::ComponentClassLock {
static CLASS_LOCK: $crate::ComponentClassLock = $crate::ComponentClassLock::new();
&CLASS_LOCK
}
}
impl< $g > $crate::Component for $name < $r > {
type Class = $class;
}
};
}