#![cfg_attr(feature="nightly", feature(allocator_api))]
#![cfg_attr(feature="nightly", feature(associated_type_defaults))]
#![cfg_attr(feature="nightly", feature(const_trait_impl))]
#![deny(warnings)]
#![doc(test(attr(deny(warnings))))]
#![doc(test(attr(allow(dead_code))))]
#![doc(test(attr(allow(unused_variables))))]
#![allow(clippy::type_complexity)]
#![doc=document_features::document_features!()]
#![no_std]
#[doc=include_str!("../README.md")]
type _DocTestReadme = ();
extern crate alloc as alloc_crate;
#[doc(hidden)]
pub use core::compile_error as std_compile_error;
#[doc(hidden)]
pub use core::concat as std_concat;
#[doc(hidden)]
pub use core::default::Default as std_default_Default;
#[doc(hidden)]
pub use core::option::Option as std_option_Option;
#[doc(hidden)]
pub use core::stringify as std_stringify;
#[doc(hidden)]
pub use generics::parse as generics_parse;
#[cfg(feature="nightly")]
use alloc_crate::alloc::Allocator;
use alloc_crate::collections::TryReserveError;
use alloc_crate::vec::{self, Vec};
#[cfg(feature="nightly")]
use composable_allocators::Global as Global;
use core::fmt::Debug;
use core::hint::unreachable_unchecked;
use core::iter::{self, FusedIterator};
use core::mem::{align_of, replace, size_of};
use core::num::NonZeroUsize;
use core::ops::{Index, IndexMut};
use core::slice::{self};
use core::sync::atomic::{AtomicUsize, Ordering};
use educe::Educe;
use either::{Either, Left, Right};
use phantom_type::PhantomType;
use rand::rngs::SmallRng;
use rand::{RngCore, SeedableRng};
pub use components_arena_traits::*;
pub struct ComponentClassToken(AtomicUsize);
impl ComponentClassToken {
pub const fn new() -> Self { ComponentClassToken(AtomicUsize::new(0)) }
}
impl Default for ComponentClassToken {
fn default() -> Self { ComponentClassToken::new() }
}
pub trait ComponentClass {
fn token() -> &'static ComponentClassToken where Self: Sized;
}
pub trait Component {
type Class: ComponentClass;
#[cfg(feature="nightly")]
type Alloc: Allocator = Global;
}
#[derive(Educe)]
#[educe(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Id<C: Component> {
index: usize,
guard: NonZeroUsize,
phantom: PhantomType<C>
}
#[cfg(feature="nightly")]
include!("impl_id_nightly.rs");
#[cfg(not(feature="nightly"))]
include!("impl_id_stable.rs");
type ArenaItem<C> = Either<Option<usize>, (NonZeroUsize, C)>;
#[derive(Debug, Clone)]
pub struct ArenaItems<C: Component> {
#[cfg(feature="nightly")]
vec: Vec<ArenaItem<C>, C::Alloc>,
#[cfg(not(feature="nightly"))]
vec: Vec<ArenaItem<C>>,
vacancy: Option<usize>,
}
impl<C: Component> ArenaItems<C> {
pub const fn item_size() -> usize {
size_of::<ArenaItem<C>>()
}
pub const fn item_align() -> usize {
align_of::<ArenaItem<C>>()
}
#[cfg(feature="nightly")]
const fn new_in(alloc: C::Alloc) -> Self {
ArenaItems {
vec: Vec::new_in(alloc),
vacancy: None
}
}
#[cfg(not(feature="nightly"))]
const fn new() -> Self {
ArenaItems {
vec: Vec::new(),
vacancy: None
}
}
#[cfg(feature="nightly")]
fn with_capacity_in(capacity: usize, alloc: C::Alloc) -> Self {
ArenaItems {
vec: Vec::with_capacity_in(capacity, alloc),
vacancy: None
}
}
#[cfg(not(feature="nightly"))]
fn with_capacity(capacity: usize) -> Self {
ArenaItems {
vec: Vec::with_capacity(capacity),
vacancy: None
}
}
#[cfg(feature="nightly")]
pub fn allocator(&self) -> &C::Alloc { self.vec.allocator() }
pub fn capacity(&self) -> usize { self.vec.capacity() }
pub fn len(&self) -> usize {
let mut vacancies = 0;
let mut vacancy = self.vacancy;
while let Some(i) = vacancy {
vacancies += 1;
vacancy = *self.vec[i].as_ref().left().unwrap();
}
self.vec.len() - vacancies
}
pub fn len_equals_to_min_capacity(&self) -> bool {
self.vacancy.is_none()
}
pub fn is_empty(&self) -> bool { self.vec.iter().all(|x| x.is_left()) }
pub fn min_capacity(&self) -> usize { self.vec.len() }
pub fn reserve(&mut self, additional: usize) { self.vec.reserve(additional) }
pub fn reserve_exact(&mut self, additional: usize) { self.vec.reserve_exact(additional) }
pub fn shrink_to(&mut self, min_capacity: usize) { self.vec.shrink_to(min_capacity) }
pub fn shrink_to_fit(&mut self) { self.vec.shrink_to_fit() }
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
self.vec.try_reserve(additional)
}
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
self.vec.try_reserve_exact(additional)
}
pub fn get_id_value(&self, index: usize) -> Option<(Id<C>, &C)> {
self.vec[index].as_ref().right().map(|(guard, item)| (Id { index, guard: *guard, phantom: PhantomType::new() }, item))
}
pub fn get_id_value_mut(&mut self, index: usize) -> Option<(Id<C>, &mut C)> {
self.vec[index].as_mut().right().map(|(guard, item)| (Id { index, guard: *guard, phantom: PhantomType::new() }, item))
}
pub fn get_id(&self, index: usize) -> Option<Id<C>> {
self.vec[index].as_ref().right().map(|(guard, _)| Id { index, guard: *guard, phantom: PhantomType::new() })
}
pub fn get_value(&self, index: usize) -> Option<&C> {
self.vec[index].as_ref().right().map(|(_, item)| item)
}
pub fn get_value_mut(&mut self, index: usize) -> Option<&mut C> {
self.vec[index].as_mut().right().map(|(_, item)| item)
}
pub fn ids(&self) -> ArenaItemsIds<C> {
ArenaItemsIds(self.vec.iter().enumerate())
}
pub fn values(&self) -> ArenaItemsValues<C> {
ArenaItemsValues(self.vec.iter())
}
pub fn values_mut(&mut self) -> ArenaItemsValuesMut<C> {
ArenaItemsValuesMut(self.vec.iter_mut())
}
pub fn iter(&self) -> ArenaItemsIter<C> {
ArenaItemsIter(self.vec.iter().enumerate())
}
pub fn iter_mut(&mut self) -> ArenaItemsIterMut<C> {
ArenaItemsIterMut(self.vec.iter_mut().enumerate())
}
pub fn into_ids(self) -> ArenaItemsIntoIds<C> {
ArenaItemsIntoIds(self.vec.into_iter().enumerate())
}
pub fn into_values(self) -> ArenaItemsIntoValues<C> {
ArenaItemsIntoValues(self.vec.into_iter())
}
}
#[derive(Debug, Clone)]
pub struct ArenaItemsIter<'a, C: Component>(
iter::Enumerate<slice::Iter<'a, Either<Option<usize>, (NonZeroUsize, C)>>>
);
impl<'a, C: Component> Iterator for ArenaItemsIter<'a, C> {
type Item = (Id<C>, &'a C);
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
None => return None,
Some((_, Left(_))) => { },
Some((index, Right((guard, item)))) =>
return Some((Id { index, guard: *guard, phantom: PhantomType::new() }, item)),
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.0.size_hint().1)
}
}
impl<'a, C: Component> DoubleEndedIterator for ArenaItemsIter<'a, C> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
match self.0.next_back() {
None => return None,
Some((_, Left(_))) => { },
Some((index, Right((guard, item)))) =>
return Some((Id { index, guard: *guard, phantom: PhantomType::new() }, item)),
}
}
}
}
impl<'a, C: Component> FusedIterator for ArenaItemsIter<'a, C> { }
#[derive(Debug)]
pub struct ArenaItemsIterMut<'a, C: Component>(
iter::Enumerate<slice::IterMut<'a, Either<Option<usize>, (NonZeroUsize, C)>>>
);
impl<'a, C: Component> Iterator for ArenaItemsIterMut<'a, C> {
type Item = (Id<C>, &'a mut C);
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
None => return None,
Some((_, Left(_))) => { },
Some((index, Right((guard, item)))) =>
return Some((Id { index, guard: *guard, phantom: PhantomType::new() }, item)),
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.0.size_hint().1)
}
}
impl<'a, C: Component> DoubleEndedIterator for ArenaItemsIterMut<'a, C> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
match self.0.next_back() {
None => return None,
Some((_, Left(_))) => { },
Some((index, Right((guard, item)))) =>
return Some((Id { index, guard: *guard, phantom: PhantomType::new() }, item)),
}
}
}
}
impl<'a, C: Component> FusedIterator for ArenaItemsIterMut<'a, C> { }
#[derive(Debug, Clone)]
pub struct ArenaItemsIds<'a, C: Component>(
iter::Enumerate<slice::Iter<'a, Either<Option<usize>, (NonZeroUsize, C)>>>
);
impl<'a, C: Component> Iterator for ArenaItemsIds<'a, C> {
type Item = Id<C>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
None => return None,
Some((_, Left(_))) => { },
Some((index, Right((guard, _)))) => return Some(Id { index, guard: *guard, phantom: PhantomType::new() }),
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.0.size_hint().1)
}
}
impl<'a, C: Component> DoubleEndedIterator for ArenaItemsIds<'a, C> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
match self.0.next_back() {
None => return None,
Some((_, Left(_))) => { },
Some((index, Right((guard, _)))) => return Some(Id { index, guard: *guard, phantom: PhantomType::new() }),
}
}
}
}
impl<'a, C: Component> FusedIterator for ArenaItemsIds<'a, C> { }
#[derive(Debug, Clone)]
pub struct ArenaItemsValues<'a, C: Component>(
slice::Iter<'a, Either<Option<usize>, (NonZeroUsize, C)>>
);
impl<'a, C: Component> Iterator for ArenaItemsValues<'a, C> {
type Item = &'a C;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
None => return None,
Some(Left(_)) => { },
Some(Right((_, item))) => return Some(item),
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.0.size_hint().1)
}
}
impl<'a, C: Component> DoubleEndedIterator for ArenaItemsValues<'a, C> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
match self.0.next_back() {
None => return None,
Some(Left(_)) => { },
Some(Right((_, item))) => return Some(item),
}
}
}
}
impl<'a, C: Component> FusedIterator for ArenaItemsValues<'a, C> { }
#[derive(Debug)]
pub struct ArenaItemsValuesMut<'a, C: Component>(
slice::IterMut<'a, Either<Option<usize>, (NonZeroUsize, C)>>
);
impl<'a, C: Component> Iterator for ArenaItemsValuesMut<'a, C> {
type Item = &'a mut C;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
None => return None,
Some(Left(_)) => { },
Some(Right((_, item))) => return Some(item),
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.0.size_hint().1)
}
}
impl<'a, C: Component> DoubleEndedIterator for ArenaItemsValuesMut<'a, C> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
match self.0.next_back() {
None => return None,
Some(Left(_)) => { },
Some(Right((_, item))) => return Some(item),
}
}
}
}
impl<'a, C: Component> FusedIterator for ArenaItemsValuesMut<'a, C> { }
#[derive(Debug)]
pub struct ArenaItemsIntoIds<C: Component>(
#[cfg(feature="nightly")]
iter::Enumerate<vec::IntoIter<Either<Option<usize>, (NonZeroUsize, C)>, C::Alloc>>,
#[cfg(not(feature="nightly"))]
iter::Enumerate<vec::IntoIter<Either<Option<usize>, (NonZeroUsize, C)>>>,
);
impl<C: Component> Iterator for ArenaItemsIntoIds<C> {
type Item = Id<C>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
None => return None,
Some((_, Left(_))) => { },
Some((index, Right((guard, _)))) => return Some(Id { index, guard, phantom: PhantomType::new() }),
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.0.size_hint().1)
}
}
impl<C: Component> DoubleEndedIterator for ArenaItemsIntoIds<C> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
match self.0.next_back() {
None => return None,
Some((_, Left(_))) => { },
Some((index, Right((guard, _)))) => return Some(Id { index, guard, phantom: PhantomType::new() }),
}
}
}
}
impl<C: Component> FusedIterator for ArenaItemsIntoIds<C> { }
#[derive(Debug)]
pub struct ArenaItemsIntoValues<C: Component>(
#[cfg(feature="nightly")]
vec::IntoIter<Either<Option<usize>, (NonZeroUsize, C)>, C::Alloc>,
#[cfg(not(feature="nightly"))]
vec::IntoIter<Either<Option<usize>, (NonZeroUsize, C)>>,
);
impl<C: Component> Iterator for ArenaItemsIntoValues<C> {
type Item = C;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
None => return None,
Some(Left(_)) => { },
Some(Right((_, item))) => return Some(item),
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.0.size_hint().1)
}
}
impl<C: Component> DoubleEndedIterator for ArenaItemsIntoValues<C> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
match self.0.next_back() {
None => return None,
Some(Left(_)) => { },
Some(Right((_, item))) => return Some(item),
}
}
}
}
impl<C: Component> FusedIterator for ArenaItemsIntoValues<C> { }
#[derive(Debug, Clone)]
pub struct ArenaItemsIntoIter<C: Component>(
#[cfg(feature="nightly")]
iter::Enumerate<vec::IntoIter<Either<Option<usize>, (NonZeroUsize, C)>, C::Alloc>>,
#[cfg(not(feature="nightly"))]
iter::Enumerate<vec::IntoIter<Either<Option<usize>, (NonZeroUsize, C)>>>,
);
impl<C: Component> Iterator for ArenaItemsIntoIter<C> {
type Item = (Id<C>, C);
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next() {
None => return None,
Some((_, Left(_))) => { },
Some((index, Right((guard, item)))) =>
return Some((Id { index, guard, phantom: PhantomType::new() }, item)),
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.0.size_hint().1)
}
}
impl<C: Component> DoubleEndedIterator for ArenaItemsIntoIter<C> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
match self.0.next_back() {
None => return None,
Some((_, Left(_))) => { },
Some((index, Right((guard, item)))) =>
return Some((Id { index, guard, phantom: PhantomType::new() }, item)),
}
}
}
}
impl<C: Component> FusedIterator for ArenaItemsIntoIter<C> { }
impl<C: Component> IntoIterator for ArenaItems<C> {
type Item = (Id<C>, C);
type IntoIter = ArenaItemsIntoIter<C>;
fn into_iter(self) -> Self::IntoIter {
ArenaItemsIntoIter(self.vec.into_iter().enumerate())
}
}
impl<'a, C: Component> IntoIterator for &'a ArenaItems<C> {
type Item = (Id<C>, &'a C);
type IntoIter = ArenaItemsIter<'a, C>;
fn into_iter(self) -> Self::IntoIter { self.iter() }
}
mod forgettable_field {
use core::fmt::{self, Debug, Formatter};
use core::mem::{MaybeUninit, forget, replace};
use core::ops::{Deref, DerefMut};
pub struct ForgettableField<T>(MaybeUninit<T>);
impl<T> ForgettableField<T> {
pub const fn new(value: T) -> Self { ForgettableField(MaybeUninit::new(value)) }
pub fn into_inner(mut this: Self) -> T {
let inner = replace(&mut this.0, MaybeUninit::uninit());
forget(this);
unsafe { inner.assume_init() }
}
pub fn take_and_forget<Owner>(mut owner: Owner, f: impl FnOnce(&mut Owner) -> &mut Self) -> T {
let this = replace(f(&mut owner), ForgettableField(MaybeUninit::uninit()));
forget(owner);
Self::into_inner(this)
}
}
impl<T> Drop for ForgettableField<T> {
fn drop(&mut self) {
unsafe { self.0.assume_init_drop() }
}
}
impl<T> Deref for ForgettableField<T> {
type Target = T;
fn deref(&self) -> &T { unsafe { self.0.assume_init_ref() } }
}
impl<T> DerefMut for ForgettableField<T> {
fn deref_mut(&mut self) -> &mut T { unsafe { self.0.assume_init_mut() } }
}
impl<T: Default> Default for ForgettableField<T> {
fn default() -> Self { ForgettableField::new(T::default()) }
}
impl<T: Debug> Debug for ForgettableField<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.deref().fmt(f)
}
}
}
use forgettable_field::*;
#[cfg(feature="nightly")]
#[derive(Educe)]
#[educe(Debug(bound = "C: Debug, C::Alloc: Debug"))]
pub struct Arena<C: Component + 'static> {
guard_rng: Option<SmallRng>,
items: ForgettableField<ArenaItems<C>>,
}
#[cfg(not(feature="nightly"))]
#[derive(Debug)]
pub struct Arena<C: Component + 'static> {
guard_rng: Option<SmallRng>,
items: ForgettableField<ArenaItems<C>>,
}
#[cfg(feature="nightly")]
include!("arena_nightly.rs");
impl<C: Component> Arena<C> {
#[cfg(not(feature="nightly"))]
pub const fn new() -> Self {
Arena {
guard_rng: None,
items: ForgettableField::new(ArenaItems::new())
}
}
#[cfg(not(feature="nightly"))]
pub fn with_capacity(capacity: usize) -> Self {
Arena {
guard_rng: None,
items: ForgettableField::new(ArenaItems::with_capacity(capacity))
}
}
#[cfg(feature="nightly")]
pub const fn new_in(alloc: C::Alloc) -> Self {
Arena {
guard_rng: None,
items: ForgettableField::new(ArenaItems::new_in(alloc))
}
}
#[cfg(feature="nightly")]
pub fn with_capacity_in(capacity: usize, alloc: C::Alloc) -> Self {
Arena {
guard_rng: None,
items: ForgettableField::new(ArenaItems::with_capacity_in(capacity, alloc))
}
}
fn guard_rng(&mut self) -> &mut SmallRng {
if self.guard_rng.is_none() {
let seed = C::Class::token().0.fetch_add(1, Ordering::Relaxed);
self.guard_rng = Some(SmallRng::seed_from_u64(seed as u64));
}
unsafe { self.guard_rng.as_mut().unwrap_or_else(|| unreachable_unchecked()) }
}
pub fn into_items(#[allow(unused_mut)] mut self) -> ArenaItems<C> {
ForgettableField::take_and_forget(self, |x| &mut x.items)
}
pub fn items(&self) -> &ArenaItems<C> { &self.items }
pub fn items_mut(&mut self) -> &mut ArenaItems<C> { &mut self.items }
pub fn reserve(&mut self) {
if self.items().len_equals_to_min_capacity() {
self.items_mut().reserve(1);
}
}
pub fn reserve_exact(&mut self) {
if self.items().len_equals_to_min_capacity() {
self.items_mut().reserve_exact(1);
}
}
pub fn try_reserve(&mut self) -> Result<(), TryReserveError> {
if self.items().len_equals_to_min_capacity() {
self.items_mut().try_reserve(1)
} else {
Ok(())
}
}
pub fn try_reserve_exact(&mut self) -> Result<(), TryReserveError> {
if self.items().len_equals_to_min_capacity() {
self.items_mut().try_reserve_exact(1)
} else {
Ok(())
}
}
pub fn insert<T>(&mut self, component: impl FnOnce(Id<C>) -> (C, T)) -> T {
let mut guard = 0usize.to_le_bytes();
self.guard_rng().fill_bytes(&mut guard[..]);
let guard = NonZeroUsize::new(usize::from_le_bytes(guard)).unwrap_or(unsafe { NonZeroUsize::new_unchecked(42) });
if let Some(index) = self.items.vacancy {
let id = Id { index, guard, phantom: PhantomType::new() };
let (component, result) = component(id);
let item = (guard, component);
self.items.vacancy = replace(&mut self.items.vec[index], Right(item)).left()
.unwrap_or_else(|| unsafe { unreachable_unchecked() });
result
} else {
let index = self.items.len();
let id = Id { index, guard, phantom: PhantomType::new() };
let (component, result) = component(id);
let item = (guard, component);
self.items.vec.push(Right(item));
result
}
}
pub fn remove(&mut self, id: Id<C>) -> C {
let vacancy = self.items.vacancy;
match replace(&mut self.items.vec[id.index], Left(vacancy)) {
Left(vacancy) => {
self.items.vec[id.index] = Left(vacancy);
panic!("invalid id");
},
Right((guard, component)) => {
if guard == id.guard {
self.items.vacancy = Some(id.index);
component
} else {
self.items.vec[id.index] = Right((guard, component));
panic!("invalid id");
}
}
}
}
}
#[cfg(feature="nightly")]
impl<C: Component> Default for Arena<C> where C::Alloc: Default {
fn default() -> Self { Arena::new() }
}
#[cfg(not(feature="nightly"))]
impl<C: Component> Default for Arena<C> {
fn default() -> Self { Arena::new() }
}
impl<C: Component> Index<Id<C>> for Arena<C> {
type Output = C;
fn index(&self, id: Id<C>) -> &C {
let &(guard, ref component) = self.items.vec[id.index].as_ref().right().expect("invalid id");
if guard != id.guard { panic!("invalid id"); }
component
}
}
impl<C: Component> IndexMut<Id<C>> for Arena<C> {
fn index_mut(&mut self, id: Id<C>) -> &mut C {
let &mut (guard, ref mut component) = self.items.vec[id.index].as_mut().right().expect("invalid id");
if guard != id.guard { panic!("invalid id"); }
component
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! unexpected_token {
() => { };
}
#[macro_export]
macro_rules! Component {
(
($($arg:tt)*)
$vis:vis enum $name:ident
$($token:tt)+
) => {
$crate::generics_parse! {
$crate::Component_impl {
@args
[, $($arg)*]
[] []
[$vis] [$name]
}
$($token)+
}
};
(
($($arg:tt)*)
$vis:vis struct $name:ident
$($token:tt)+
) => {
$crate::generics_parse! {
$crate::Component_impl {
@args
[, $($arg)*]
[] []
[$vis] [$name]
}
$($token)+
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! Component_impl {
(
@args
[$(,)?]
[$($class:ident)?] [$($alloc:ty)?]
[$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
$crate::Component_impl! {
@impl [$vis] [$name] [$($class)?] [$($alloc)?]
[$($g)*] [$($r)*] [$($w)*]
}
};
(
@args
[, alloc = $alloc:ty $(, $($token:tt)*)?]
[$($class:ident)?] []
[$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
$crate::Component_impl! {
@args
[$(, $($token)*)?]
[$($class)?] [$alloc]
[$vis] [$name] [$($g)*] [$($r)*] [$($w)*]
}
};
(
@args
[, alloc = $alloc:ty $(, $($token:tt)*)?]
[$($class:ident)?] [$alloc_:ty]
[$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
$crate::std_compile_error!("duplicated 'alloc' parameter");
};
(
@args
[, alloc = $($token:tt)*]
[$($class:ident)?] [$($alloc:ty)?]
[$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
$crate::std_compile_error!("invalid 'alloc' parameter");
};
(
@args
[, class = $class:ident $($token:tt)*]
[] [$($alloc:ty)?]
[$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
$crate::Component_impl! {
@args
[$($token)*]
[$class] [$($alloc)?]
[$vis] [$name] [$($g)*] [$($r)*] [$($w)*]
}
};
(
@args
[, class = $class:ident $($token:tt)*]
[$class_:ident] [$($alloc:ty)?]
[$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
$crate::std_compile_error!("duplicated 'class' parameter");
};
(
@args
[, class = $token:tt $($tail:tt)*]
[$($class:ident)?] [$($alloc:ty)?]
[$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
$crate::unexpected_token!($token);
$crate::std_compile_error!("invalid 'class' parameter");
};
(
@args
[, class = ]
[$($class:ident)?] [$($alloc:ty)?]
[$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
$crate::std_compile_error!("invalid 'class' parameter");
};
(
@args
[, $param:ident = $($token:tt)*]
[$($class:ident)?] [$($alloc:ty)?]
[$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
$crate::unexpected_token!($param);
$crate::std_compile_error!($crate::std_concat!("unknown '", $crate::std_stringify!($param), "' parameter"));
};
(
@args
[, $token:tt $($tail:tt)*]
[$($class:ident)?] [$($alloc:ty)?]
[$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
$crate::unexpected_token!($token);
$crate::std_compile_error!("invalid parameter");
};
(
@args
[$token:tt $($tail:tt)*]
[$($class:ident)?] [$($alloc:ty)?]
[$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
$crate::unexpected_token!($token);
$crate::std_compile_error!("comma expected");
};
(
@impl [$vis:vis] [$name:ident] [] [$($alloc:ty)?] [] [] []
) => {
$crate::Component_impl! { @self [$name] [$($alloc)?] }
};
(
@impl [$vis:vis] [$name:ident] [$class:ident] [$($alloc:ty)?] [] [] []
) => {
$crate::Component_impl! { @class [$vis] [$name] [$class] [$($alloc)?] [] [] [] }
};
(
@impl [$vis:vis] [$name:ident] [] [$($alloc:ty)?] [$($g:tt)+] [$($r:tt)+] [$($w:tt)*]
) => {
$crate::std_compile_error!($crate::std_concat!(
"\
generic component requires separate non-generic component class; \
consider adding 'class' parameter: '#[derive(Component!(class=\
",
$crate::std_stringify!($name),
"Class)]'"
));
};
(
@impl
[$vis:vis] [$name:ident] [$class:ident]
[$($alloc:ty)?] $g:tt $r:tt $w:tt
) => {
$crate::Component_impl! { @class [$vis] [$name] [$class] [$($alloc)?] $g $r $w }
};
(
@self [$name:ident] [$($alloc:ty)?]
) => {
impl $crate::ComponentClass for $name {
fn token() -> &'static $crate::ComponentClassToken {
static TOKEN: $crate::ComponentClassToken = $crate::ComponentClassToken::new();
&TOKEN
}
}
impl $crate::Component for $name {
type Class = Self;
$(
type Alloc = $alloc;
)?
}
};
(
@class
[$vis:vis] [$name:ident] [$class:ident]
[$($alloc:ty)?] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
) => {
$vis enum $class { }
impl $crate::ComponentClass for $class {
fn token() -> &'static $crate::ComponentClassToken {
static TOKEN: $crate::ComponentClassToken = $crate::ComponentClassToken::new();
&TOKEN
}
}
impl $($g)* $crate::Component for $name $($r)* $($w)* {
type Class = $class;
$(
type Alloc = $alloc;
)?
}
};
}
#[macro_export]
macro_rules! NewtypeComponentId {
(
()
$vis:vis struct $name:ident $($token:tt)*
) => {
$crate::generics_parse! {
$crate::NewtypeComponentId_impl {
[$name]
}
$($token)*
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! NewtypeComponentId_impl {
(
[$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] (
$(#[$id_attr:meta])* $(pub)? $id:ty
$(, $(#[$phantom_attr:meta])* $(pub)? $phantom:ty)* $(,)?
);
) => {
impl $($g)* $crate::ComponentId for $name $($r)* $($w)* {
fn from_raw(raw: $crate::RawId) -> Self {
$name(
<$id as $crate::ComponentId>::from_raw(raw)
$(, <$phantom as $crate::std_default_Default>::default())*
)
}
fn into_raw(self) -> $crate::RawId {
<$id as $crate::ComponentId>::into_raw(self.0)
}
}
};
(
[$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
$token:tt $($tail:tt)*
) => {
$crate::unexpected_token!($token);
$crate::std_compile_error!("'NewtypeComponentId' supports deriving for non-empty tuple structs only");
};
(
[$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
) => {
$crate::std_compile_error!("'NewtypeComponentId' supports deriving for non-empty tuple structs only");
};
}
#[cfg(test)]
mod test {
use macro_attr_2018::macro_attr;
use quickcheck_macros::quickcheck;
use core::sync::atomic::{Ordering, AtomicI8};
use crate::*;
macro_attr! {
#[derive(Component!(class=GenericOneArgComponent))]
struct GenericOneArg<T>(T);
}
macro_attr! {
#[derive(Component!(class=GenericTwoArgsComponent))]
struct GenericTwoArgs<A, B>(A, B);
}
macro_attr! {
#[derive(Component!)]
struct Test {
this: Id<Test>,
value: i8
}
}
const fn _new_test_arena() -> Arena<Test> {
Arena::new()
}
macro_attr! {
#[derive(Component!)]
struct TestWithDrop {
value: i8
}
}
static TEST_DROP: AtomicI8 = AtomicI8::new(-1);
const fn _new_test_with_drop_arena() -> Arena<TestWithDrop> {
Arena::new()
}
impl Drop for TestWithDrop {
fn drop(&mut self) {
TEST_DROP.store(self.value, Ordering::SeqCst);
}
}
#[quickcheck]
fn new_arena_min_capacity_is_zero(capacity: Option<u8>) -> bool {
let capacity = capacity.map(|capacity| capacity as usize);
capacity.map_or_else(
|| <Arena::<Test>>::new(),
|capacity| <Arena::<Test>>::with_capacity(capacity)
).items().min_capacity() == 0
}
#[quickcheck]
fn arena_contains_inserted_item(capacity: Option<u8>, value: i8) -> bool {
let capacity = capacity.map(|capacity| capacity as usize);
let mut arena = capacity.map_or_else(
|| Arena::new(),
|capacity| Arena::with_capacity(capacity)
);
let id = arena.insert(|this| (Test { this, value }, this));
arena[id].this == id && arena[id].value == value
}
#[should_panic]
#[test]
fn foreign_id_cause_panic() {
let mut arena = Arena::new();
let id = arena.insert(|this| (Test { this, value: 7 }, this)).into_raw();
let id = Id::from_raw((id.0, unsafe { NonZeroUsize::new_unchecked(17) }));
let _ = &arena[id];
}
#[test]
fn drop_components() {
{
let mut arena = Arena::new();
arena.insert(|this| (TestWithDrop { value: 7 }, this)).into_raw();
TEST_DROP.store(-1, Ordering::SeqCst);
}
assert_eq!(TEST_DROP.load(Ordering::SeqCst), 7);
}
macro_attr! {
#[derive(NewtypeComponentId!, Educe)]
#[educe(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
struct IdWrap1(#[allow(dead_code)] Id<Test>);
}
macro_attr! {
#[derive(NewtypeComponentId!, Educe)]
#[educe(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
struct IdWrap2<X>(Id<Test>, PhantomType<X>);
}
macro_attr! {
#[derive(NewtypeComponentId!, Educe)]
#[educe(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
struct IdWrap3<X, Y: Copy>(Id<Test>, PhantomType<X>, PhantomType<Y>);
}
macro_attr! {
#[derive(NewtypeComponentId!, Educe)]
#[educe(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
struct IdWrap4<X, Y: Copy>((), PhantomType<X>, PhantomType<Y>);
}
}
#[cfg(all(test, feature="nightly"))]
mod test_nightly {
use macro_attr_2018::macro_attr;
use crate::*;
macro_attr! {
#[derive(Component!(alloc=&'static dyn Allocator))]
struct TestComponent {
}
}
}