bevy_ecs/identifier/
masks.rs

1use std::num::NonZeroU32;
2
3use super::kinds::IdKind;
4
5/// Mask for extracting the value portion of a 32-bit high segment. This
6/// yields 31-bits of total value, as the final bit (the most significant)
7/// is reserved as a flag bit. Can be negated to extract the flag bit.
8pub(crate) const HIGH_MASK: u32 = 0x7FFF_FFFF;
9
10/// Abstraction over masks needed to extract values/components of an [`super::Identifier`].
11pub(crate) struct IdentifierMask;
12
13impl IdentifierMask {
14    /// Returns the low component from a `u64` value
15    #[inline(always)]
16    pub(crate) const fn get_low(value: u64) -> u32 {
17        // This will truncate to the lowest 32 bits
18        value as u32
19    }
20
21    /// Returns the high component from a `u64` value
22    #[inline(always)]
23    pub(crate) const fn get_high(value: u64) -> u32 {
24        // This will discard the lowest 32 bits
25        (value >> u32::BITS) as u32
26    }
27
28    /// Pack a low and high `u32` values into a single `u64` value.
29    #[inline(always)]
30    pub(crate) const fn pack_into_u64(low: u32, high: u32) -> u64 {
31        ((high as u64) << u32::BITS) | (low as u64)
32    }
33
34    /// Pack the [`IdKind`] bits into a high segment.
35    #[inline(always)]
36    pub(crate) const fn pack_kind_into_high(value: u32, kind: IdKind) -> u32 {
37        value | ((kind as u32) << 24)
38    }
39
40    /// Extract the value component from a high segment of an [`super::Identifier`].
41    #[inline(always)]
42    pub(crate) const fn extract_value_from_high(value: u32) -> u32 {
43        value & HIGH_MASK
44    }
45
46    /// Extract the ID kind component from a high segment of an [`super::Identifier`].
47    #[inline(always)]
48    pub(crate) const fn extract_kind_from_high(value: u32) -> IdKind {
49        // The negated HIGH_MASK will extract just the bit we need for kind.
50        let kind_mask = !HIGH_MASK;
51        let bit = value & kind_mask;
52
53        if bit == kind_mask {
54            IdKind::Placeholder
55        } else {
56            IdKind::Entity
57        }
58    }
59
60    /// Offsets a masked generation value by the specified amount, wrapping to 1 instead of 0.
61    /// Will never be greater than [`HIGH_MASK`] or less than `1`, and increments are masked to
62    /// never be greater than [`HIGH_MASK`].
63    #[inline(always)]
64    pub(crate) const fn inc_masked_high_by(lhs: NonZeroU32, rhs: u32) -> NonZeroU32 {
65        let lo = (lhs.get() & HIGH_MASK).wrapping_add(rhs & HIGH_MASK);
66        // Checks high 32 bit for whether we have overflowed 31 bits.
67        let overflowed = lo >> 31;
68
69        // SAFETY:
70        // - Adding the overflow flag will offset overflows to start at 1 instead of 0
71        // - The sum of `0x7FFF_FFFF` + `u32::MAX` + 1 (overflow) == `0x7FFF_FFFF`
72        // - If the operation doesn't overflow at 31 bits, no offsetting takes place
73        unsafe { NonZeroU32::new_unchecked(lo.wrapping_add(overflowed) & HIGH_MASK) }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn get_u64_parts() {
83        // Two distinct bit patterns per low/high component
84        let value: u64 = 0x7FFF_FFFF_0000_000C;
85
86        assert_eq!(IdentifierMask::get_low(value), 0x0000_000C);
87        assert_eq!(IdentifierMask::get_high(value), 0x7FFF_FFFF);
88    }
89
90    #[test]
91    fn extract_kind() {
92        // All bits are ones.
93        let high: u32 = 0xFFFF_FFFF;
94
95        assert_eq!(
96            IdentifierMask::extract_kind_from_high(high),
97            IdKind::Placeholder
98        );
99
100        // Second and second to last bits are ones.
101        let high: u32 = 0x4000_0002;
102
103        assert_eq!(IdentifierMask::extract_kind_from_high(high), IdKind::Entity);
104    }
105
106    #[test]
107    fn extract_high_value() {
108        // All bits are ones.
109        let high: u32 = 0xFFFF_FFFF;
110
111        // Excludes the most significant bit as that is a flag bit.
112        assert_eq!(IdentifierMask::extract_value_from_high(high), 0x7FFF_FFFF);
113
114        // Start bit and end bit are ones.
115        let high: u32 = 0x8000_0001;
116
117        assert_eq!(IdentifierMask::extract_value_from_high(high), 0x0000_0001);
118
119        // Classic bit pattern.
120        let high: u32 = 0xDEAD_BEEF;
121
122        assert_eq!(IdentifierMask::extract_value_from_high(high), 0x5EAD_BEEF);
123    }
124
125    #[test]
126    fn pack_kind_bits() {
127        // All bits are ones expect the most significant bit, which is zero
128        let high: u32 = 0x7FFF_FFFF;
129
130        assert_eq!(
131            IdentifierMask::pack_kind_into_high(high, IdKind::Placeholder),
132            0xFFFF_FFFF
133        );
134
135        // Arbitrary bit pattern
136        let high: u32 = 0x00FF_FF00;
137
138        assert_eq!(
139            IdentifierMask::pack_kind_into_high(high, IdKind::Entity),
140            // Remains unchanged as before
141            0x00FF_FF00
142        );
143
144        // Bit pattern that almost spells a word
145        let high: u32 = 0x40FF_EEEE;
146
147        assert_eq!(
148            IdentifierMask::pack_kind_into_high(high, IdKind::Placeholder),
149            0xC0FF_EEEE // Milk and no sugar, please.
150        );
151    }
152
153    #[test]
154    fn pack_into_u64() {
155        let high: u32 = 0x7FFF_FFFF;
156        let low: u32 = 0x0000_00CC;
157
158        assert_eq!(
159            IdentifierMask::pack_into_u64(low, high),
160            0x7FFF_FFFF_0000_00CC
161        );
162    }
163
164    #[test]
165    fn incrementing_masked_nonzero_high_is_safe() {
166        // Adding from lowest value with lowest to highest increment
167        // No result should ever be greater than 0x7FFF_FFFF or HIGH_MASK
168        assert_eq!(
169            NonZeroU32::MIN,
170            IdentifierMask::inc_masked_high_by(NonZeroU32::MIN, 0)
171        );
172        assert_eq!(
173            NonZeroU32::new(2).unwrap(),
174            IdentifierMask::inc_masked_high_by(NonZeroU32::MIN, 1)
175        );
176        assert_eq!(
177            NonZeroU32::new(3).unwrap(),
178            IdentifierMask::inc_masked_high_by(NonZeroU32::MIN, 2)
179        );
180        assert_eq!(
181            NonZeroU32::MIN,
182            IdentifierMask::inc_masked_high_by(NonZeroU32::MIN, HIGH_MASK)
183        );
184        assert_eq!(
185            NonZeroU32::MIN,
186            IdentifierMask::inc_masked_high_by(NonZeroU32::MIN, u32::MAX)
187        );
188        // Adding from absolute highest value with lowest to highest increment
189        // No result should ever be greater than 0x7FFF_FFFF or HIGH_MASK
190        assert_eq!(
191            NonZeroU32::new(HIGH_MASK).unwrap(),
192            IdentifierMask::inc_masked_high_by(NonZeroU32::MAX, 0)
193        );
194        assert_eq!(
195            NonZeroU32::MIN,
196            IdentifierMask::inc_masked_high_by(NonZeroU32::MAX, 1)
197        );
198        assert_eq!(
199            NonZeroU32::new(2).unwrap(),
200            IdentifierMask::inc_masked_high_by(NonZeroU32::MAX, 2)
201        );
202        assert_eq!(
203            NonZeroU32::new(HIGH_MASK).unwrap(),
204            IdentifierMask::inc_masked_high_by(NonZeroU32::MAX, HIGH_MASK)
205        );
206        assert_eq!(
207            NonZeroU32::new(HIGH_MASK).unwrap(),
208            IdentifierMask::inc_masked_high_by(NonZeroU32::MAX, u32::MAX)
209        );
210        // Adding from actual highest value with lowest to highest increment
211        // No result should ever be greater than 0x7FFF_FFFF or HIGH_MASK
212        assert_eq!(
213            NonZeroU32::new(HIGH_MASK).unwrap(),
214            IdentifierMask::inc_masked_high_by(NonZeroU32::new(HIGH_MASK).unwrap(), 0)
215        );
216        assert_eq!(
217            NonZeroU32::MIN,
218            IdentifierMask::inc_masked_high_by(NonZeroU32::new(HIGH_MASK).unwrap(), 1)
219        );
220        assert_eq!(
221            NonZeroU32::new(2).unwrap(),
222            IdentifierMask::inc_masked_high_by(NonZeroU32::new(HIGH_MASK).unwrap(), 2)
223        );
224        assert_eq!(
225            NonZeroU32::new(HIGH_MASK).unwrap(),
226            IdentifierMask::inc_masked_high_by(NonZeroU32::new(HIGH_MASK).unwrap(), HIGH_MASK)
227        );
228        assert_eq!(
229            NonZeroU32::new(HIGH_MASK).unwrap(),
230            IdentifierMask::inc_masked_high_by(NonZeroU32::new(HIGH_MASK).unwrap(), u32::MAX)
231        );
232    }
233}