1use std::num::NonZeroU32;
17
18#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
19pub struct NodeId {
20 index: u32,
21 generation: NonZeroU32,
22}
23
24impl NodeId {
25 #[inline]
27 pub(crate) fn index(self) -> usize {
28 self.index as usize
29 }
30
31 #[inline]
35 pub(crate) fn from_parts(index: usize, generation: NonZeroU32) -> Self {
36 let index = u32::try_from(index).expect("arena overflow: more than u32::MAX nodes");
37 NodeId { index, generation }
38 }
39
40 #[inline]
42 pub(crate) fn generation_raw(self) -> NonZeroU32 {
43 self.generation
44 }
45
46 #[inline]
49 pub fn generation(self) -> u32 {
50 self.generation.get()
51 }
52
53 pub fn as_u32(self) -> u32 {
57 self.index + 1
58 }
59}
60
61impl std::fmt::Debug for NodeId {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(f, "NodeId({}", self.index + 1)?;
67 if self.generation.get() > 1 {
68 write!(f, "@{}", self.generation.get())?;
69 }
70 write!(f, ")")
71 }
72}
73
74impl std::fmt::Display for NodeId {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 write!(f, "#{}", self.index + 1)?;
77 if self.generation.get() > 1 {
78 write!(f, "@{}", self.generation.get())?;
79 }
80 Ok(())
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 const G1: NonZeroU32 = NonZeroU32::MIN;
89
90 #[test]
91 fn index_round_trip() {
92 for i in [0, 1, 2, 7, 99, 1000, 1_000_000] {
93 let id = NodeId::from_parts(i, G1);
94 assert_eq!(id.index(), i);
95 assert_eq!(id.generation(), 1);
96 }
97 }
98
99 #[test]
102 fn option_node_id_is_eight_bytes() {
103 assert_eq!(std::mem::size_of::<NodeId>(), 8);
104 assert_eq!(std::mem::size_of::<Option<NodeId>>(), 8);
105 }
106
107 #[test]
108 fn same_slot_different_generation_is_a_different_id() {
109 let a = NodeId::from_parts(5, G1);
110 let b = NodeId::from_parts(5, NonZeroU32::new(2).unwrap());
111 assert_ne!(a, b);
112 assert_eq!(a.index(), b.index());
113 }
114
115 #[test]
118 fn debug_is_compact() {
119 assert_eq!(format!("{:?}", NodeId::from_parts(8, G1)), "NodeId(9)");
120 assert_eq!(
121 format!("{:?}", NodeId::from_parts(8, NonZeroU32::new(2).unwrap())),
122 "NodeId(9@2)"
123 );
124 }
125
126 #[test]
127 fn display_shows_slot_and_generation_after_reuse() {
128 assert_eq!(format!("{}", NodeId::from_parts(41, G1)), "#42");
129 assert_eq!(
130 format!("{}", NodeId::from_parts(41, NonZeroU32::new(3).unwrap())),
131 "#42@3"
132 );
133 }
134}