shipyard/entity_id/
mod.rs

1#[cfg(feature = "serde1")]
2mod serde;
3
4use core::num::NonZeroU64;
5
6/// Entity handle.
7//
8// the id is 64 bits long
9// <- 46 index -> <- 16 gen -> <- 2 meta ->
10// a generation of !0 is used as a dead entity
11//
12// inserted and modified component are flagged using metadata
13#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14#[repr(transparent)]
15pub struct EntityId(pub(super) NonZeroU64);
16
17/// Allows [`EntityId`] to be stored in collections requiring [`Default`], like `TinyVec`.
18impl Default for EntityId {
19    fn default() -> Self {
20        Self::dead()
21    }
22}
23
24impl EntityId {
25    // Number of bits used by the generation
26    const GEN_LEN: u64 = 16;
27    const INDEX_LEN: u64 = 64 - Self::GEN_LEN;
28    const INDEX_MASK: u64 = !(!0 << Self::INDEX_LEN);
29    const GEN_MASK: u64 = !(!0 >> Self::GEN_LEN);
30    const MAX_GEN: u16 = u16::MAX - 1;
31
32    /// Returns the index part of the `EntityId`.\
33    /// ⚠️ You shouldn't use it to index a storage.
34    #[inline]
35    pub fn index(self) -> u64 {
36        (self.0.get() & Self::INDEX_MASK) - 1
37    }
38    /// Returns the index part of the `EntityId` as an usize.\
39    /// ⚠️ You shouldn't use it to index a storage.
40    #[inline]
41    pub fn uindex(self) -> usize {
42        self.index() as usize
43    }
44    /// Modify the index.
45    #[inline]
46    pub(crate) fn set_index(&mut self, index: u64) {
47        assert!(index < Self::INDEX_MASK);
48        // SAFE never zero
49        self.0 =
50            unsafe { NonZeroU64::new_unchecked((index + 1) | (self.0.get() & !Self::INDEX_MASK)) }
51    }
52    /// Returns the generation part of the `EntityId`.
53    #[inline]
54    pub fn gen(self) -> u16 {
55        ((self.0.get() & Self::GEN_MASK) >> Self::INDEX_LEN) as u16
56    }
57    /// Increments the generation, returns Err if gen + 1 == gen::MAX().
58    #[inline]
59    pub(super) fn bump_gen(&mut self) -> Result<(), ()> {
60        if self.gen() < Self::MAX_GEN - 1 {
61            // SAFE never zero
62            self.0 = unsafe {
63                NonZeroU64::new_unchecked(
64                    (self.0.get() & !Self::GEN_MASK)
65                        | (((self.gen() + 1) as u64) << Self::INDEX_LEN),
66                )
67            };
68            Ok(())
69        } else {
70            Err(())
71        }
72    }
73    /// Make a new `EntityId` with the given index.
74    #[inline]
75    pub(crate) fn new(index: u64) -> Self {
76        assert!(index < Self::INDEX_MASK);
77        // SAFE never zero
78        EntityId(unsafe { NonZeroU64::new_unchecked(index + 1) })
79    }
80    #[inline]
81    pub(crate) const fn new_from_parts(index: u64, gen: u16) -> Self {
82        assert!(index < Self::INDEX_MASK);
83
84        EntityId(unsafe {
85            NonZeroU64::new_unchecked((index + 1) | ((gen as u64) << Self::INDEX_LEN))
86        })
87    }
88    /// Build a new `EntityId` with the given index and generation.
89    #[inline]
90    pub const fn new_from_index_and_gen(index: u64, gen: u16) -> Self {
91        EntityId::new_from_parts(index, gen)
92    }
93    #[cfg(test)]
94    pub(crate) fn zero() -> Self {
95        EntityId(NonZeroU64::new(1).unwrap())
96    }
97    /// Returns a dead `EntityId`, it can be used as a null entity.
98    #[inline]
99    pub fn dead() -> Self {
100        // SAFE not zero
101        EntityId(unsafe { NonZeroU64::new_unchecked(!0) })
102    }
103    #[inline]
104    pub(crate) fn bucket(self) -> usize {
105        self.uindex() / crate::sparse_set::BUCKET_SIZE
106    }
107    #[inline]
108    pub(crate) fn bucket_index(self) -> usize {
109        self.uindex() % crate::sparse_set::BUCKET_SIZE
110    }
111    #[inline]
112    pub(crate) fn max_index() -> u64 {
113        Self::INDEX_MASK - 1
114    }
115    /// Maximum generation of a valid [`EntityId`].\
116    /// A dead id will be above that.
117    #[inline]
118    pub(crate) const fn max_gen() -> u16 {
119        Self::MAX_GEN
120    }
121    #[inline]
122    pub(crate) fn is_dead(&self) -> bool {
123        (self.0.get() & Self::GEN_MASK) == Self::GEN_MASK
124    }
125    #[inline]
126    pub(crate) fn copy_index(&mut self, other: EntityId) {
127        unsafe {
128            self.0 = NonZeroU64::new_unchecked(
129                (self.0.get() & !Self::INDEX_MASK) | (other.0.get() & Self::INDEX_MASK),
130            );
131        }
132    }
133    #[inline]
134    pub(crate) fn copy_gen(&mut self, other: EntityId) {
135        unsafe {
136            self.0 = NonZeroU64::new_unchecked(
137                (self.0.get() & !Self::GEN_MASK) | (other.0.get() & Self::GEN_MASK),
138            );
139        }
140    }
141    #[inline]
142    pub(crate) fn copy_index_gen(&mut self, other: EntityId) {
143        unsafe {
144            self.0 = NonZeroU64::new_unchecked(self.0.get() | other.0.get());
145        }
146    }
147    /// Returns `EntityId`'s inner representation.
148    #[inline]
149    pub fn inner(self) -> u64 {
150        self.0.get()
151    }
152    /// Build an `EntityId` from its inner representation.
153    #[inline]
154    pub fn from_inner(inner: u64) -> Option<EntityId> {
155        Some(EntityId(NonZeroU64::new(inner)?))
156    }
157}
158
159impl core::fmt::Debug for EntityId {
160    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
161        if *self == EntityId::dead() {
162            f.write_str("EId(dead)")
163        } else {
164            write!(f, "EId({}.{})", self.index(), self.gen())
165        }
166    }
167}
168
169#[test]
170fn entity_id() {
171    let mut entity_id = EntityId::new(0);
172    assert_eq!(entity_id.index(), 0);
173    assert_eq!(entity_id.gen(), 0);
174    entity_id.set_index(701);
175    assert_eq!(entity_id.index(), 701);
176    assert_eq!(entity_id.gen(), 0);
177    entity_id.bump_gen().unwrap();
178    entity_id.bump_gen().unwrap();
179    entity_id.bump_gen().unwrap();
180    assert_eq!(entity_id.index(), 701);
181    assert_eq!(entity_id.gen(), 3);
182    entity_id.set_index(554);
183    assert_eq!(entity_id.index(), 554);
184    assert_eq!(entity_id.gen(), 3);
185}