use std::fmt::{Debug, Display, Formatter}; use std::mem::transmute; use std::sync::atomic::{AtomicU32, Ordering}; use crate::component::Component; use crate::util::{gen_mask_64, 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 KIND_BITS: BitMask = BitMask::new(gen_mask_64!(0..=1)); #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[repr(u8)] pub enum Kind { Pair = 3, Entity = 2, Component = 1, } /// 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(kind: Kind) -> Self { let id = NEXT.fetch_add(1, Ordering::Relaxed); Self { inner: ID_BITS.field_prep(u64::from(id)) | KIND_BITS.field_prep(kind as u64), } } #[must_use] pub fn wildcard() -> Self { Self { inner: ID_BITS.field_prep(u64::from(WILDCARD_ID)) | KIND_BITS.field_prep(Kind::Component as u64), } } #[must_use] pub fn new_pair(params: PairParams) -> Self { assert_ne!( params.relation.kind(), Kind::Pair, "Pair relation cannot be a pair" ); assert_ne!( params.target.kind(), Kind::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())) | KIND_BITS.field_prep(Kind::Pair as u64), } } #[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 kind(&self) -> Kind { let Ok(kind) = u8::try_from(self.inner.field_get(KIND_BITS)) else { unreachable!("Uid kind does not fit in u8"); }; // SAFETY: The kind bits cannot be invalid since they are set using the Kind enum // in the new_unique function unsafe { transmute::(kind) } } /// If this `Uid` is a pair, returns the relation as a component `Uid`. /// /// # Panics /// Will panic if this `Uid` is not a pair. pub fn relation_component(&self) -> Self { assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); Self { inner: ID_BITS.field_prep(u64::from(self.relation())) | KIND_BITS.field_prep(Kind::Component as u64), } } pub fn has_same_relation_as(&self, other: Self) -> bool { self.relation() == other.relation() } /// If this `Uid` is a pair, returns the relation as a entity `Uid`. /// /// # Panics /// Will panic if this `Uid` is not a pair. pub fn relation_entity(&self) -> Self { assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); Self { inner: ID_BITS.field_prep(u64::from(self.relation())) | KIND_BITS.field_prep(Kind::Entity as u64), } } /// If this `Uid` is a pair, returns the target as a component `Uid`. /// /// # Panics /// Will panic if this `Uid` is not a pair. pub fn target_component(&self) -> Self { assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); Self { inner: ID_BITS.field_prep(u64::from(self.id())) | KIND_BITS.field_prep(Kind::Component as u64), } } /// If this `Uid` is a pair, returns the target as a entity `Uid`. /// /// # Panics /// Will panic if this `Uid` is not a pair. pub fn target_entity(&self) -> Self { assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair"); Self { inner: ID_BITS.field_prep(u64::from(self.id())) | KIND_BITS.field_prep(Kind::Entity as u64), } } fn relation(&self) -> u32 { let Ok(relation) = u32::try_from(self.inner.field_get(RELATION_BITS)) else { unreachable!("Uid relation does not fit in u32"); }; relation } } impl Debug for Uid { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { formatter .debug_struct("Uid") .field("id", &self.id()) .field("kind", &self.kind()) .finish_non_exhaustive() } } impl Display for Uid { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { if self.kind() == Kind::Pair { return write!( formatter, "({}, {})", self.relation(), self.target_component() ); } 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: 'static { fn uid() -> Uid; } impl With for ComponentT { fn uid() -> Uid { Self::id() } } #[derive(Debug)] pub enum Wildcard {} impl With for Wildcard { fn uid() -> Uid { Uid::wildcard() } }