Skip to main content

concinnity_physics/
handle.rs

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