oxihuman_core/
pool_allocator.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
9pub struct PoolSlot<T> {
10 pub value: T,
11 pub generation: u32,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct PoolHandle {
17 pub index: usize,
18 pub generation: u32,
19}
20
21pub struct PoolAllocator<T> {
23 slots: Vec<Option<PoolSlot<T>>>,
24 free: Vec<usize>,
25 generations: Vec<u32>,
26 capacity: usize,
27 live_count: usize,
28}
29
30#[allow(dead_code)]
31impl<T: Clone> PoolAllocator<T> {
32 pub fn new(capacity: usize) -> Self {
33 let slots = (0..capacity).map(|_| None).collect();
34 let free = (0..capacity).rev().collect();
35 let generations = vec![0u32; capacity];
36 PoolAllocator {
37 slots,
38 free,
39 generations,
40 capacity,
41 live_count: 0,
42 }
43 }
44
45 pub fn alloc(&mut self, value: T) -> Option<PoolHandle> {
46 let index = self.free.pop()?;
47 self.generations[index] += 1;
48 let gen = self.generations[index];
49 self.slots[index] = Some(PoolSlot {
50 value,
51 generation: gen,
52 });
53 self.live_count += 1;
54 Some(PoolHandle {
55 index,
56 generation: gen,
57 })
58 }
59
60 pub fn free(&mut self, handle: PoolHandle) -> bool {
61 if handle.index >= self.capacity {
62 return false;
63 }
64 match &self.slots[handle.index] {
65 Some(s) if s.generation == handle.generation => {
66 self.slots[handle.index] = None;
67 self.free.push(handle.index);
68 self.live_count -= 1;
69 true
70 }
71 _ => false,
72 }
73 }
74
75 pub fn get(&self, handle: PoolHandle) -> Option<&T> {
76 if handle.index >= self.capacity {
77 return None;
78 }
79 self.slots[handle.index]
80 .as_ref()
81 .filter(|s| s.generation == handle.generation)
82 .map(|s| &s.value)
83 }
84
85 pub fn get_mut(&mut self, handle: PoolHandle) -> Option<&mut T> {
86 if handle.index >= self.capacity {
87 return None;
88 }
89 self.slots[handle.index]
90 .as_mut()
91 .filter(|s| s.generation == handle.generation)
92 .map(|s| &mut s.value)
93 }
94
95 pub fn is_valid(&self, handle: PoolHandle) -> bool {
96 handle.index < self.capacity
97 && self.slots[handle.index]
98 .as_ref()
99 .is_some_and(|s| s.generation == handle.generation)
100 }
101
102 pub fn live_count(&self) -> usize {
103 self.live_count
104 }
105
106 pub fn capacity(&self) -> usize {
107 self.capacity
108 }
109
110 pub fn free_count(&self) -> usize {
111 self.free.len()
112 }
113
114 pub fn reset(&mut self) {
115 for slot in &mut self.slots {
116 *slot = None;
117 }
118 self.free.clear();
119 for i in (0..self.capacity).rev() {
120 self.free.push(i);
121 }
122 self.live_count = 0;
123 }
124
125 pub fn iter(&self) -> impl Iterator<Item = (PoolHandle, &T)> {
126 self.slots.iter().enumerate().filter_map(|(i, slot)| {
127 slot.as_ref().map(|s| {
128 (
129 PoolHandle {
130 index: i,
131 generation: s.generation,
132 },
133 &s.value,
134 )
135 })
136 })
137 }
138}
139
140pub fn new_pool<T: Clone>(capacity: usize) -> PoolAllocator<T> {
141 PoolAllocator::new(capacity)
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn alloc_and_get() {
150 let mut pool: PoolAllocator<i32> = new_pool(4);
151 let h = pool.alloc(42).expect("should succeed");
152 assert_eq!(*pool.get(h).expect("should succeed"), 42);
153 }
154
155 #[test]
156 fn free_invalidates_handle() {
157 let mut pool: PoolAllocator<i32> = new_pool(4);
158 let h = pool.alloc(10).expect("should succeed");
159 assert!(pool.free(h));
160 assert!(pool.get(h).is_none());
161 }
162
163 #[test]
164 fn generation_prevents_use_after_free() {
165 let mut pool: PoolAllocator<i32> = new_pool(4);
166 let h = pool.alloc(1).expect("should succeed");
167 pool.free(h);
168 let h2 = pool.alloc(2).expect("should succeed");
169 assert_eq!(h2.index, h.index);
170 assert!(pool.get(h).is_none());
171 assert_eq!(*pool.get(h2).expect("should succeed"), 2);
172 }
173
174 #[test]
175 fn live_count_tracking() {
176 let mut pool: PoolAllocator<u8> = new_pool(8);
177 let h1 = pool.alloc(1).expect("should succeed");
178 let h2 = pool.alloc(2).expect("should succeed");
179 assert_eq!(pool.live_count(), 2);
180 pool.free(h1);
181 assert_eq!(pool.live_count(), 1);
182 pool.free(h2);
183 assert_eq!(pool.live_count(), 0);
184 }
185
186 #[test]
187 fn capacity_exhausted() {
188 let mut pool: PoolAllocator<u8> = new_pool(2);
189 pool.alloc(1).expect("should succeed");
190 pool.alloc(2).expect("should succeed");
191 assert!(pool.alloc(3).is_none());
192 }
193
194 #[test]
195 fn reset_restores_capacity() {
196 let mut pool: PoolAllocator<u8> = new_pool(2);
197 pool.alloc(1).expect("should succeed");
198 pool.alloc(2).expect("should succeed");
199 pool.reset();
200 assert_eq!(pool.free_count(), 2);
201 assert!(pool.alloc(3).is_some());
202 }
203
204 #[test]
205 fn get_mut_modifies_value() {
206 let mut pool: PoolAllocator<i32> = new_pool(4);
207 let h = pool.alloc(10).expect("should succeed");
208 *pool.get_mut(h).expect("should succeed") = 99;
209 assert_eq!(*pool.get(h).expect("should succeed"), 99);
210 }
211
212 #[test]
213 fn iter_live_items() {
214 let mut pool: PoolAllocator<i32> = new_pool(4);
215 let h1 = pool.alloc(1).expect("should succeed");
216 let _h2 = pool.alloc(2).expect("should succeed");
217 pool.free(h1);
218 let vals: Vec<i32> = pool.iter().map(|(_, v)| *v).collect();
219 assert_eq!(vals, vec![2]);
220 }
221
222 #[test]
223 fn is_valid_check() {
224 let mut pool: PoolAllocator<i32> = new_pool(4);
225 let h = pool.alloc(5).expect("should succeed");
226 assert!(pool.is_valid(h));
227 pool.free(h);
228 assert!(!pool.is_valid(h));
229 }
230}