summaryrefslogtreecommitdiff
path: root/engine-ecs/src/uid.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine-ecs/src/uid.rs')
-rw-r--r--engine-ecs/src/uid.rs207
1 files changed, 207 insertions, 0 deletions
diff --git a/engine-ecs/src/uid.rs b/engine-ecs/src/uid.rs
new file mode 100644
index 0000000..26fbaee
--- /dev/null
+++ b/engine-ecs/src/uid.rs
@@ -0,0 +1,207 @@
+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<u64> = BitMask::new(gen_mask_64!(32..=63));
+const RELATION_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(6..=31));
+const IS_PAIR_BITS: BitMask<u64> = 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<ComponentT: Component> With for ComponentT
+{
+ fn uid() -> Uid
+ {
+ Self::id()
+ }
+}
+
+pub trait WithUidTuple
+{
+ type UidsArray: Array<Uid>;
+
+ 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);
+});