oxihuman_core/
node_pool.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9#[allow(dead_code)]
10pub struct NodeHandle(u32, u32); #[derive(Debug)]
14#[allow(dead_code)]
15enum NodeSlot<T> {
16 Occupied {
17 value: T,
18 generation: u32,
19 },
20 Free {
21 next_free: Option<u32>,
22 generation: u32,
23 },
24}
25
26#[derive(Debug)]
28#[allow(dead_code)]
29pub struct NodePool<T> {
30 slots: Vec<NodeSlot<T>>,
31 free_head: Option<u32>,
32 count: usize,
33}
34
35#[allow(dead_code)]
37pub fn new_node_pool<T>() -> NodePool<T> {
38 NodePool {
39 slots: Vec::new(),
40 free_head: None,
41 count: 0,
42 }
43}
44
45#[allow(dead_code)]
47pub fn np_alloc<T>(pool: &mut NodePool<T>, value: T) -> NodeHandle {
48 if let Some(idx) = pool.free_head {
49 let idx_usize = idx as usize;
50 let gen = match &pool.slots[idx_usize] {
51 NodeSlot::Free {
52 generation,
53 next_free,
54 } => {
55 pool.free_head = *next_free;
56 *generation
57 }
58 _ => unreachable!(),
59 };
60 pool.slots[idx_usize] = NodeSlot::Occupied {
61 value,
62 generation: gen,
63 };
64 pool.count += 1;
65 NodeHandle(idx, gen)
66 } else {
67 let idx = pool.slots.len() as u32;
68 pool.slots.push(NodeSlot::Occupied {
69 value,
70 generation: 0,
71 });
72 pool.count += 1;
73 NodeHandle(idx, 0)
74 }
75}
76
77#[allow(dead_code)]
79pub fn np_free<T>(pool: &mut NodePool<T>, handle: NodeHandle) -> Option<T> {
80 let idx = handle.0 as usize;
81 if idx >= pool.slots.len() {
82 return None;
83 }
84 let gen = handle.1;
85 let old = std::mem::replace(
86 &mut pool.slots[idx],
87 NodeSlot::Free {
88 next_free: pool.free_head,
89 generation: gen + 1,
90 },
91 );
92 if let NodeSlot::Occupied { value, generation } = old {
93 if generation == gen {
94 pool.free_head = Some(handle.0);
95 pool.count -= 1;
96 return Some(value);
97 }
98 pool.slots[idx] = NodeSlot::Occupied { value, generation };
100 } else {
101 pool.slots[idx] = old;
102 }
103 None
104}
105
106#[allow(dead_code)]
108pub fn np_get<T>(pool: &NodePool<T>, handle: NodeHandle) -> Option<&T> {
109 let idx = handle.0 as usize;
110 if let Some(NodeSlot::Occupied { value, generation }) = pool.slots.get(idx) {
111 if *generation == handle.1 {
112 return Some(value);
113 }
114 }
115 None
116}
117
118#[allow(dead_code)]
120pub fn np_get_mut<T>(pool: &mut NodePool<T>, handle: NodeHandle) -> Option<&mut T> {
121 let idx = handle.0 as usize;
122 if let Some(NodeSlot::Occupied { value, generation }) = pool.slots.get_mut(idx) {
123 if *generation == handle.1 {
124 return Some(value);
125 }
126 }
127 None
128}
129
130#[allow(dead_code)]
132pub fn np_is_valid<T>(pool: &NodePool<T>, handle: NodeHandle) -> bool {
133 np_get(pool, handle).is_some()
134}
135
136#[allow(dead_code)]
138pub fn np_count<T>(pool: &NodePool<T>) -> usize {
139 pool.count
140}
141
142#[allow(dead_code)]
144pub fn np_capacity<T>(pool: &NodePool<T>) -> usize {
145 pool.slots.len()
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_alloc_get() {
154 let mut pool = new_node_pool();
155 let h = np_alloc(&mut pool, 42u32);
156 assert_eq!(np_get(&pool, h), Some(&42));
157 }
158
159 #[test]
160 fn test_free_and_reuse() {
161 let mut pool = new_node_pool();
162 let h1 = np_alloc(&mut pool, 1u32);
163 np_free(&mut pool, h1);
164 let h2 = np_alloc(&mut pool, 2u32);
165 assert_eq!(h2.0, h1.0);
166 assert_ne!(h2.1, h1.1); }
168
169 #[test]
170 fn test_stale_handle() {
171 let mut pool = new_node_pool();
172 let h1 = np_alloc(&mut pool, 1u32);
173 np_free(&mut pool, h1);
174 np_alloc(&mut pool, 2u32);
175 assert_eq!(np_get(&pool, h1), None); }
177
178 #[test]
179 fn test_count() {
180 let mut pool = new_node_pool();
181 let h = np_alloc(&mut pool, 1u32);
182 np_alloc(&mut pool, 2u32);
183 assert_eq!(np_count(&pool), 2);
184 np_free(&mut pool, h);
185 assert_eq!(np_count(&pool), 1);
186 }
187
188 #[test]
189 fn test_get_mut() {
190 let mut pool = new_node_pool();
191 let h = np_alloc(&mut pool, 5u32);
192 if let Some(v) = np_get_mut(&mut pool, h) {
193 *v = 99;
194 }
195 assert_eq!(np_get(&pool, h), Some(&99));
196 }
197
198 #[test]
199 fn test_invalid_handle() {
200 let pool: NodePool<u32> = new_node_pool();
201 assert!(!np_is_valid(&pool, NodeHandle(0, 0)));
202 }
203
204 #[test]
205 fn test_capacity_grows() {
206 let mut pool = new_node_pool();
207 np_alloc(&mut pool, 1u32);
208 np_alloc(&mut pool, 2u32);
209 np_alloc(&mut pool, 3u32);
210 assert_eq!(np_capacity(&pool), 3);
211 }
212
213 #[test]
214 fn test_free_out_of_bounds() {
215 let mut pool: NodePool<u32> = new_node_pool();
216 assert_eq!(np_free(&mut pool, NodeHandle(99, 0)), None);
217 }
218
219 #[test]
220 fn test_is_valid() {
221 let mut pool = new_node_pool();
222 let h = np_alloc(&mut pool, 0u32);
223 assert!(np_is_valid(&pool, h));
224 }
225}