Skip to main content

concinnity_core/physics/
handle.rs

1// The handle a simulation addresses its bodies by. A slot index paired with a
2// generation, packed into one word: the generation is what makes a handle to a
3// removed body read as absent rather than silently addressing whatever took
4// its slot.
5//
6// Packed index-major so the derived `Ord` orders by slot, giving callers a
7// stable key for a body pair without reaching into the body storage.
8
9/// Opaque handle to a body inside a simulation.
10///
11/// The simulation maps it onto its own storage; callers treat it as an opaque
12/// key. Handles order deterministically, so a pair of them makes a stable
13/// map key.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct BodyHandle(u64);
16
17impl BodyHandle {
18    /// Build a handle from a slot index and generation.
19    pub const fn from_parts(index: u32, generation: u32) -> Self {
20        Self(((index as u64) << 32) | generation as u64)
21    }
22
23    /// The slot index this handle addresses.
24    pub const fn index(self) -> u32 {
25        (self.0 >> 32) as u32
26    }
27
28    /// The generation this handle was minted at.
29    pub const fn generation(self) -> u32 {
30        self.0 as u32
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn parts_round_trip() {
40        for (index, generation) in [(0, 0), (1, 0), (0, 1), (7, 3), (u32::MAX, u32::MAX)] {
41            let handle = BodyHandle::from_parts(index, generation);
42            assert_eq!(handle.index(), index);
43            assert_eq!(handle.generation(), generation);
44        }
45    }
46
47    #[test]
48    fn a_reused_slot_is_a_different_handle() {
49        let first = BodyHandle::from_parts(4, 0);
50        let reused = BodyHandle::from_parts(4, 1);
51        assert_ne!(first, reused, "the generation must distinguish the slot");
52    }
53
54    #[test]
55    fn handles_order_by_slot_then_generation() {
56        let mut handles = [
57            BodyHandle::from_parts(2, 0),
58            BodyHandle::from_parts(1, 5),
59            BodyHandle::from_parts(1, 2),
60        ];
61        handles.sort();
62        assert_eq!(
63            handles,
64            [
65                BodyHandle::from_parts(1, 2),
66                BodyHandle::from_parts(1, 5),
67                BodyHandle::from_parts(2, 0),
68            ]
69        );
70    }
71}