1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub struct Entity {
7 pub(crate) index: u32,
8 pub(crate) generation: u32,
9}
10
11#[derive(Clone)]
14pub(crate) struct EntityAllocator {
15 generations: Vec<u32>,
16 free_list: Vec<u32>,
17}
18
19impl EntityAllocator {
20 pub fn new() -> Self {
21 Self { generations: Vec::new(), free_list: Vec::new() }
22 }
23
24 pub fn spawn(&mut self) -> Entity {
25 if let Some(index) = self.free_list.pop() {
26 Entity { index, generation: self.generations[index as usize] }
27 } else {
28 let index = self.generations.len() as u32;
29 self.generations.push(0);
30 Entity { index, generation: 0 }
31 }
32 }
33
34 pub fn despawn(&mut self, entity: Entity) -> bool {
35 if !self.is_alive(entity) {
36 return false;
37 }
38 self.generations[entity.index as usize] = self.generations[entity.index as usize].wrapping_add(1);
39 self.free_list.push(entity.index);
40 true
41 }
42
43 pub fn is_alive(&self, entity: Entity) -> bool {
44 self.generations.get(entity.index as usize).copied() == Some(entity.generation)
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn recycled_index_gets_a_new_generation() {
54 let mut allocator = EntityAllocator::new();
55 let a = allocator.spawn();
56 allocator.despawn(a);
57 let b = allocator.spawn();
58
59 assert_eq!(a.index, b.index);
60 assert_ne!(a.generation, b.generation);
61 assert!(!allocator.is_alive(a));
62 assert!(allocator.is_alive(b));
63 }
64
65 #[test]
66 fn distinct_spawns_get_distinct_indices() {
67 let mut allocator = EntityAllocator::new();
68 let a = allocator.spawn();
69 let b = allocator.spawn();
70 assert_ne!(a.index, b.index);
71 }
72}