use std::fmt::{Debug, Display, Formatter}; use std::sync::atomic::{AtomicU32, Ordering}; use seq_macro::seq; use crate::component::Component; use crate::util::{gen_mask_64, Array, BitMask, NumberExt}; static NEXT: AtomicU32 = AtomicU32::new(Uid::FIRST_UNIQUE_ID); static WILDCARD_ID: u32 = 1; const ID_BITS: BitMask = BitMask::new(gen_mask_64!(32..=63)); const RELATION_BITS: BitMask = BitMask::new(gen_mask_64!(6..=31)); const IS_PAIR_BITS: BitMask = BitMask::new(1 << 0); /// A unique identifier. #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Uid { inner: u64, } impl Uid { /// The id part of the first unique `Uid`. The ids `0..Uid::FIRST_UNIQUE_ID` are /// reserved. pub const FIRST_UNIQUE_ID: u32 = 5; /// Returns a new unique entity/component ID. pub fn new_unique() -> Self { let id = NEXT.fetch_add(1, Ordering::Relaxed); Self { inner: ID_BITS.field_prep(u64::from(id)) | IS_PAIR_BITS.field_prep(0), } } #[must_use] pub fn wildcard() -> Self { Self { inner: ID_BITS.field_prep(u64::from(WILDCARD_ID)) | IS_PAIR_BITS.field_prep(0), } } /// Returns a new pair UID. /// /// # Panics /// Will panic if either the given relation or target is a pair UID. #[must_use] pub fn new_pair(params: &PairParams) -> Self { assert!(!params.relation.is_pair(), "Pair relation cannot be a pair"); assert!(!params.target.is_pair(), "Pair target cannot be a pair"); Self { inner: ID_BITS.field_prep(u64::from(params.target.id())) | RELATION_BITS.field_prep(u64::from(params.relation.id())) | IS_PAIR_BITS.field_prep(1), } } #[must_use] pub fn id(&self) -> u32 { let Ok(id) = u32::try_from(self.inner.field_get(ID_BITS)) else { unreachable!("Uid id does not fit in u32"); }; id } #[must_use] pub fn is_pair(&self) -> bool { let Ok(is_pair) = u8::try_from(self.inner.field_get(IS_PAIR_BITS)) else { unreachable!(); }; is_pair == 1 } /// If this `Uid` is a pair, returns the relation `Uid`. /// /// # Panics /// Will panic if this `Uid` is not a pair. #[must_use] pub fn relation(&self) -> Self { assert!(self.is_pair(), "Uid is not a pair"); let Ok(relation) = u32::try_from(self.inner.field_get(RELATION_BITS)) else { unreachable!("Uid relation does not fit in u32"); }; Self { inner: ID_BITS.field_prep(u64::from(relation)) | IS_PAIR_BITS.field_prep(0), } } /// Compares the relation of this pair `Uid` with the relation of another pair `Uid`. /// /// # Panics /// Will panic if any of the `Uid`s are not a pair. #[must_use] pub fn has_same_relation_as(&self, other: Self) -> bool { self.relation() == other.relation() } /// If this `Uid` is a pair, returns the target `Uid`. /// /// # Panics /// Will panic if this `Uid` is not a pair. #[must_use] pub fn target(&self) -> Self { assert!(self.is_pair(), "Uid is not a pair"); Self { inner: ID_BITS.field_prep(u64::from(self.id())) | IS_PAIR_BITS.field_prep(0), } } } impl Debug for Uid { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { let mut debug_struct = formatter.debug_struct("Uid"); if self.is_pair() { return debug_struct .field("relation", &self.relation()) .field("target", &self.target()) .finish(); } debug_struct.field("id", &self.id()).finish() } } impl Display for Uid { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { if self.is_pair() { return write!(formatter, "({}, {})", self.relation(), self.target()); } if *self == Uid::wildcard() { return write!(formatter, "*"); } write!(formatter, "{}", self.id()) } } #[derive(Debug, Clone)] pub struct PairParams { pub relation: Uid, pub target: Uid, } pub trait With { fn uid() -> Uid; } impl With for ComponentT { fn uid() -> Uid { Self::id() } } pub trait WithUidTuple { type UidsArray: Array; fn uids() -> Self::UidsArray; } macro_rules! impl_with_uid_tuple { ($c: tt) => { seq!(I in 0..=$c { impl<#(WithUid~I: With,)*> WithUidTuple for (#(WithUid~I,)*) { type UidsArray = [Uid; $c + 1]; fn uids() -> Self::UidsArray { [#(WithUid~I::uid(),)*] } } }); }; } seq!(C in 0..=16 { impl_with_uid_tuple!(C); });