Skip to main content

kozan_primitives/arena/
raw_id.rs

1//! Raw generational identifier — the foundation of arena identity.
2//!
3//! `RawId` is 8 bytes: `(index: u32, generation: u32)`.
4//! It is `Copy + Send + Sync` — safe to pass anywhere.
5//!
6//! When a slot is freed, the generation is bumped. Any `RawId` holding
7//! the old generation becomes **stale** — detected in O(1) by the arena.
8//!
9//! Chrome equivalent: slot indices with generation counters in arena-based
10//! allocators.
11
12use core::fmt;
13
14/// Sentinel value meaning "no link" (empty free-list, no parent, etc.).
15pub const INVALID: u32 = u32::MAX;
16
17/// A raw generational identifier. 8 bytes, `Copy`, `Send + Sync`.
18///
19/// Carries no pointer and no methods beyond identity — just an index
20/// and a generation. Type-safe wrappers (via [`Arena`](super::Arena))
21/// prevent mixing IDs from different arenas.
22///
23/// # Usage
24///
25/// ```
26/// use kozan_primitives::arena::RawId;
27///
28/// let id = RawId::new(0, 0);
29/// assert_eq!(id.index(), 0);
30/// assert_eq!(id.generation(), 0);
31/// ```
32#[derive(Copy, Clone, Eq, PartialEq, Hash)]
33pub struct RawId {
34    index: u32,
35    generation: u32,
36}
37
38impl RawId {
39    /// Create a new `RawId` with the given index and generation.
40    #[inline]
41    #[must_use]
42    pub const fn new(index: u32, generation: u32) -> Self {
43        Self { index, generation }
44    }
45
46    /// The slot index in the arena.
47    #[inline]
48    #[must_use]
49    pub const fn index(self) -> u32 {
50        self.index
51    }
52
53    /// The generation counter (for stale-handle detection).
54    #[inline]
55    #[must_use]
56    pub const fn generation(self) -> u32 {
57        self.generation
58    }
59}
60
61impl fmt::Debug for RawId {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        write!(f, "RawId({}v{})", self.index, self.generation)
64    }
65}
66
67impl fmt::Display for RawId {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        write!(f, "{}v{}", self.index, self.generation)
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn new_and_accessors() {
79        let id = RawId::new(5, 3);
80        assert_eq!(id.index(), 5);
81        assert_eq!(id.generation(), 3);
82    }
83
84    #[test]
85    fn copy_and_eq() {
86        let a = RawId::new(1, 2);
87        let b = a; // Copy
88        assert_eq!(a, b);
89    }
90
91    #[test]
92    fn different_generation_not_equal() {
93        let a = RawId::new(1, 0);
94        let b = RawId::new(1, 1);
95        assert_ne!(a, b);
96    }
97
98    #[test]
99    fn debug_format() {
100        let id = RawId::new(3, 7);
101        assert_eq!(format!("{:?}", id), "RawId(3v7)");
102    }
103
104    #[test]
105    fn display_format() {
106        let id = RawId::new(3, 7);
107        assert_eq!(format!("{}", id), "3v7");
108    }
109
110    #[test]
111    fn hash_works() {
112        use std::collections::HashSet;
113        let mut set = HashSet::new();
114        set.insert(RawId::new(0, 0));
115        set.insert(RawId::new(0, 1));
116        set.insert(RawId::new(1, 0));
117        assert_eq!(set.len(), 3);
118    }
119
120    #[test]
121    fn send_sync() {
122        fn assert_send_sync<T: Send + Sync>() {}
123        assert_send_sync::<RawId>();
124    }
125}