glitcher_core/
registry.rs1use std::collections::HashMap;
2use std::marker::PhantomData;
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum RegistryError {
7 #[error("Registry capacity exceeded")]
8 CapacityExceeded,
9 #[error("Handler '{0}' already exists")]
10 AlreadyExists(String),
11 #[error("Slot generation exhausted")]
12 SlotExhausted,
13 #[error("Memory allocation failed: {0}")]
14 AllocationFailed(String),
15}
16
17pub struct HandlerId<T> {
19 pub(crate) index: u32,
20 pub(crate) generation: u32,
21 pub(crate) _marker: PhantomData<T>,
22}
23
24impl<T> Clone for HandlerId<T> {
26 fn clone(&self) -> Self {
27 *self
28 }
29}
30
31impl<T> Copy for HandlerId<T> {}
32
33impl<T> PartialEq for HandlerId<T> {
34 fn eq(&self, other: &Self) -> bool {
35 self.index == other.index && self.generation == other.generation
36 }
37}
38
39impl<T> Eq for HandlerId<T> {}
40
41impl<T> std::hash::Hash for HandlerId<T> {
42 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
43 self.index.hash(state);
44 self.generation.hash(state);
45 }
46}
47
48impl<T> std::fmt::Debug for HandlerId<T> {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 f.debug_struct("HandlerId")
51 .field("index", &self.index)
52 .field("generation", &self.generation)
53 .finish()
54 }
55}
56
57struct Slot<T> {
58 item: Option<T>,
59 generation: u32,
60 name: Option<String>,
61}
62
63pub struct Registry<T> {
66 slots: Vec<Slot<T>>,
67 free_indices: Vec<usize>,
68 name_map: HashMap<String, usize>,
69 capacity: usize,
70}
71
72impl<T> Registry<T> {
73 pub fn new(capacity: usize) -> Result<Self, RegistryError> {
76 let mut slots = Vec::new();
77 if let Err(e) = slots.try_reserve(capacity) {
78 return Err(RegistryError::AllocationFailed(e.to_string()));
79 }
80
81 for _ in 0..capacity {
83 slots.push(Slot {
84 item: None,
85 generation: 0,
86 name: None,
87 });
88 }
89
90 let mut free_indices = Vec::new();
91 if let Err(e) = free_indices.try_reserve(capacity) {
92 return Err(RegistryError::AllocationFailed(e.to_string()));
93 }
94
95 for i in (0..capacity).rev() {
97 free_indices.push(i);
98 }
99
100 let mut name_map = HashMap::new();
101 if let Err(e) = name_map.try_reserve(capacity) {
102 return Err(RegistryError::AllocationFailed(e.to_string()));
103 }
104
105 Ok(Self {
106 slots,
107 free_indices,
108 name_map,
109 capacity,
110 })
111 }
112
113 pub fn register(
116 &mut self,
117 name: impl Into<String>,
118 item: T,
119 ) -> Result<HandlerId<T>, RegistryError> {
120 let name = name.into();
121 if self.name_map.contains_key(&name) {
122 return Err(RegistryError::AlreadyExists(name));
123 }
124
125 let index = self
127 .free_indices
128 .pop()
129 .ok_or(RegistryError::CapacityExceeded)?;
130
131 let slot = &mut self.slots[index];
132
133 if slot.generation == u32::MAX {
135 return Err(RegistryError::SlotExhausted);
136 }
137
138 slot.item = Some(item);
139 slot.name = Some(name.clone());
140
141 self.name_map.insert(name, index);
143
144 Ok(HandlerId {
145 index: index as u32,
146 generation: slot.generation,
147 _marker: PhantomData,
148 })
149 }
150
151 #[inline]
153 pub fn get(&self, id: HandlerId<T>) -> Option<&T> {
154 if let Some(slot) = self.slots.get(id.index as usize) {
155 if slot.generation == id.generation {
156 return slot.item.as_ref();
157 }
158 }
159 None
160 }
161
162 #[inline]
164 pub fn get_mut(&mut self, id: HandlerId<T>) -> Option<&mut T> {
165 if let Some(slot) = self.slots.get_mut(id.index as usize) {
166 if slot.generation == id.generation {
167 return slot.item.as_mut();
168 }
169 }
170 None
171 }
172
173 pub fn lookup(&self, name: &str) -> Option<HandlerId<T>> {
175 if let Some(&index) = self.name_map.get(name) {
176 if let Some(slot) = self.slots.get(index) {
177 if slot.item.is_some() {
179 return Some(HandlerId {
180 index: index as u32,
181 generation: slot.generation,
182 _marker: PhantomData,
183 });
184 }
185 }
186 }
187 None
188 }
189
190 pub fn remove(&mut self, id: HandlerId<T>) -> Option<T> {
193 if let Some(slot) = self.slots.get_mut(id.index as usize) {
194 if slot.generation == id.generation {
195 slot.generation = slot.generation.wrapping_add(1);
197
198 let item = slot.item.take();
200
201 if let Some(name) = slot.name.take() {
203 self.name_map.remove(&name);
204 }
205
206 self.free_indices.push(id.index as usize);
208
209 return item;
210 }
211 }
212 None
213 }
214
215 pub fn capacity(&self) -> usize {
217 self.capacity
218 }
219
220 pub fn count(&self) -> usize {
222 self.capacity - self.free_indices.len()
223 }
224
225 pub fn keys(&self) -> impl Iterator<Item = &String> {
227 self.name_map.keys()
228 }
229
230 pub fn iter(&self) -> impl Iterator<Item = (&String, &T)> {
232 self.slots.iter().filter_map(|slot| {
233 if let (Some(name), Some(item)) = (&slot.name, &slot.item) {
234 Some((name, item))
235 } else {
236 None
237 }
238 })
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn test_capacity_limit() {
248 let mut registry = Registry::<i32>::new(2).unwrap();
249
250 assert!(registry.register("a", 1).is_ok());
251 assert!(registry.register("b", 2).is_ok());
252
253 let result = registry.register("c", 3);
254 assert!(matches!(result, Err(RegistryError::CapacityExceeded)));
255 }
256
257 #[test]
258 fn test_access_by_id() {
259 let mut registry = Registry::<String>::new(10).unwrap();
260 let id = registry.register("test", "Hello".to_string()).unwrap();
261
262 assert_eq!(registry.get(id).unwrap(), "Hello");
263 assert_eq!(registry.get_mut(id).unwrap(), "Hello");
264
265 *registry.get_mut(id).unwrap() = "World".to_string();
266 assert_eq!(registry.get(id).unwrap(), "World");
267 }
268
269 #[test]
270 fn test_removal_and_generation() {
271 let mut registry = Registry::<i32>::new(10).unwrap();
272 let id1 = registry.register("item", 100).unwrap();
273
274 let val = registry.remove(id1);
276 assert_eq!(val, Some(100));
277
278 assert!(registry.get(id1).is_none());
280
281 let id2 = registry.register("item2", 200).unwrap();
283
284 if id1.index == id2.index {
286 assert_ne!(id1.generation, id2.generation);
287 }
288
289 assert!(registry.get(id1).is_none());
291 assert_eq!(registry.get(id2).unwrap(), &200);
292 }
293
294 #[test]
295 fn test_name_map_consistency() {
296 let mut registry = Registry::<i32>::new(10).unwrap();
297 let id = registry.register("my_item", 123).unwrap();
298
299 let looked_up_id = registry.lookup("my_item").unwrap();
301 assert_eq!(id, looked_up_id);
302
303 registry.remove(id);
305
306 assert!(registry.lookup("my_item").is_none());
308
309 assert!(registry.register("my_item", 456).is_ok());
311 }
312
313 #[test]
314 fn test_duplicate_name() {
315 let mut registry = Registry::<i32>::new(10).unwrap();
316 registry.register("dup", 1).unwrap();
317
318 let result = registry.register("dup", 2);
319 assert!(matches!(result, Err(RegistryError::AlreadyExists(_))));
320 }
321}