1#[cfg(feature = "bevy_reflect")]
7use bevy_reflect::Reflect;
8
9use self::{error::IdentifierError, kinds::IdKind, masks::IdentifierMask};
10use std::{hash::Hash, num::NonZeroU32};
11
12pub mod error;
13pub(crate) mod kinds;
14pub(crate) mod masks;
15
16#[derive(Debug, Clone, Copy)]
21#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
22#[cfg_attr(feature = "bevy_reflect", reflect_value(Debug, Hash, PartialEq))]
23#[repr(C, align(8))]
26pub struct Identifier {
27 #[cfg(target_endian = "little")]
30 low: u32,
31 high: NonZeroU32,
32 #[cfg(target_endian = "big")]
33 low: u32,
34}
35
36impl Identifier {
37 #[inline(always)]
40 pub const fn new(low: u32, high: u32, kind: IdKind) -> Result<Self, IdentifierError> {
41 let masked_value = IdentifierMask::extract_value_from_high(high);
46
47 let packed_high = IdentifierMask::pack_kind_into_high(masked_value, kind);
48
49 if packed_high == 0 {
52 Err(IdentifierError::InvalidIdentifier)
53 } else {
54 unsafe {
57 Ok(Self {
58 low,
59 high: NonZeroU32::new_unchecked(packed_high),
60 })
61 }
62 }
63 }
64
65 #[inline(always)]
67 pub const fn low(self) -> u32 {
68 self.low
69 }
70
71 #[inline(always)]
74 pub const fn high(self) -> NonZeroU32 {
75 self.high
76 }
77
78 #[inline(always)]
81 pub const fn masked_high(self) -> u32 {
82 IdentifierMask::extract_value_from_high(self.high.get())
83 }
84
85 #[inline(always)]
87 pub const fn kind(self) -> IdKind {
88 IdentifierMask::extract_kind_from_high(self.high.get())
89 }
90
91 #[inline(always)]
93 pub const fn to_bits(self) -> u64 {
94 IdentifierMask::pack_into_u64(self.low, self.high.get())
95 }
96
97 #[inline(always)]
103 pub const fn from_bits(value: u64) -> Self {
104 let id = Self::try_from_bits(value);
105
106 match id {
107 Ok(id) => id,
108 Err(_) => panic!("Attempted to initialise invalid bits as an id"),
109 }
110 }
111
112 #[inline(always)]
116 pub const fn try_from_bits(value: u64) -> Result<Self, IdentifierError> {
117 let high = NonZeroU32::new(IdentifierMask::get_high(value));
118
119 match high {
120 Some(high) => Ok(Self {
121 low: IdentifierMask::get_low(value),
122 high,
123 }),
124 None => Err(IdentifierError::InvalidIdentifier),
125 }
126 }
127}
128
129impl PartialEq for Identifier {
132 #[inline]
133 fn eq(&self, other: &Self) -> bool {
134 self.to_bits() == other.to_bits()
138 }
139}
140
141impl Eq for Identifier {}
142
143impl PartialOrd for Identifier {
150 #[inline]
151 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
152 Some(self.cmp(other))
154 }
155}
156
157impl Ord for Identifier {
164 #[inline]
165 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
166 self.to_bits().cmp(&other.to_bits())
170 }
171}
172
173impl Hash for Identifier {
174 #[inline]
175 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
176 self.to_bits().hash(state);
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn id_construction() {
186 let id = Identifier::new(12, 55, IdKind::Entity).unwrap();
187
188 assert_eq!(id.low(), 12);
189 assert_eq!(id.high().get(), 55);
190 assert_eq!(
191 IdentifierMask::extract_kind_from_high(id.high().get()),
192 IdKind::Entity
193 );
194 }
195
196 #[test]
197 fn from_bits() {
198 let high = 0x7FFFFFFF;
201 let low = 0xC;
202 let bits: u64 = high << u32::BITS | low;
203
204 let id = Identifier::try_from_bits(bits).unwrap();
205
206 assert_eq!(id.to_bits(), 0x7FFFFFFF0000000C);
207 assert_eq!(id.low(), low as u32);
208 assert_eq!(id.high().get(), 0x7FFFFFFF);
209 assert_eq!(
210 IdentifierMask::extract_kind_from_high(id.high().get()),
211 IdKind::Entity
212 );
213 }
214
215 #[rustfmt::skip]
216 #[test]
217 #[allow(clippy::nonminimal_bool)] fn id_comparison() {
219 assert!(Identifier::new(123, 456, IdKind::Entity).unwrap() == Identifier::new(123, 456, IdKind::Entity).unwrap());
220 assert!(Identifier::new(123, 456, IdKind::Placeholder).unwrap() == Identifier::new(123, 456, IdKind::Placeholder).unwrap());
221 assert!(Identifier::new(123, 789, IdKind::Entity).unwrap() != Identifier::new(123, 456, IdKind::Entity).unwrap());
222 assert!(Identifier::new(123, 456, IdKind::Entity).unwrap() != Identifier::new(123, 789, IdKind::Entity).unwrap());
223 assert!(Identifier::new(123, 456, IdKind::Entity).unwrap() != Identifier::new(456, 123, IdKind::Entity).unwrap());
224 assert!(Identifier::new(123, 456, IdKind::Entity).unwrap() != Identifier::new(123, 456, IdKind::Placeholder).unwrap());
225
226 assert!(Identifier::new(123, 456, IdKind::Entity).unwrap() >= Identifier::new(123, 456, IdKind::Entity).unwrap());
229 assert!(Identifier::new(123, 456, IdKind::Entity).unwrap() <= Identifier::new(123, 456, IdKind::Entity).unwrap());
230 assert!(!(Identifier::new(123, 456, IdKind::Entity).unwrap() < Identifier::new(123, 456, IdKind::Entity).unwrap()));
231 assert!(!(Identifier::new(123, 456, IdKind::Entity).unwrap() > Identifier::new(123, 456, IdKind::Entity).unwrap()));
232
233 assert!(Identifier::new(9, 1, IdKind::Entity).unwrap() < Identifier::new(1, 9, IdKind::Entity).unwrap());
234 assert!(Identifier::new(1, 9, IdKind::Entity).unwrap() > Identifier::new(9, 1, IdKind::Entity).unwrap());
235
236 assert!(Identifier::new(9, 1, IdKind::Entity).unwrap() < Identifier::new(9, 1, IdKind::Placeholder).unwrap());
237 assert!(Identifier::new(1, 9, IdKind::Placeholder).unwrap() > Identifier::new(1, 9, IdKind::Entity).unwrap());
238
239 assert!(Identifier::new(1, 1, IdKind::Entity).unwrap() < Identifier::new(2, 1, IdKind::Entity).unwrap());
240 assert!(Identifier::new(1, 1, IdKind::Entity).unwrap() <= Identifier::new(2, 1, IdKind::Entity).unwrap());
241 assert!(Identifier::new(2, 2, IdKind::Entity).unwrap() > Identifier::new(1, 2, IdKind::Entity).unwrap());
242 assert!(Identifier::new(2, 2, IdKind::Entity).unwrap() >= Identifier::new(1, 2, IdKind::Entity).unwrap());
243 }
244}