1use core::fmt;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub struct Entity {
20 index: u32,
21 generation: u32,
22}
23
24impl Entity {
25 #[must_use]
28 pub const fn index(self) -> u32 {
29 self.index
30 }
31
32 #[must_use]
34 pub const fn generation(self) -> u32 {
35 self.generation
36 }
37
38 pub(crate) const fn new(index: u32, generation: u32) -> Self {
42 Self { index, generation }
43 }
44}
45
46impl fmt::Display for Entity {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 write!(f, "e{}v{}", self.index, self.generation)
49 }
50}
51
52#[derive(Debug, Default)]
54pub struct Entities {
55 generations: Vec<u32>,
60 alive: Vec<bool>,
61 free: Vec<u32>,
63 live_count: usize,
64}
65
66impl Entities {
67 #[must_use]
69 pub fn new() -> Self {
70 Self::default()
71 }
72
73 #[must_use]
75 pub fn len(&self) -> usize {
76 self.live_count
77 }
78
79 #[must_use]
81 pub fn is_empty(&self) -> bool {
82 self.live_count == 0
83 }
84
85 #[must_use]
87 pub fn capacity(&self) -> usize {
88 self.generations.len()
89 }
90
91 pub fn spawn(&mut self) -> Entity {
104 self.live_count = self.live_count.saturating_add(1);
105 if let Some(index) = self.free.pop() {
106 let slot = index as usize;
107 if let Some(alive) = self.alive.get_mut(slot) {
108 *alive = true;
109 }
110 let generation = self.generations.get(slot).copied().unwrap_or_default();
111 return Entity::new(index, generation);
112 }
113 let index = u32::try_from(self.generations.len()).unwrap_or(u32::MAX);
114 self.generations.push(0);
115 self.alive.push(true);
116 Entity::new(index, 0)
117 }
118
119 #[must_use]
125 pub fn is_alive(&self, entity: Entity) -> bool {
126 let slot = entity.index() as usize;
127 self.alive.get(slot).copied().unwrap_or(false)
128 && self.generations.get(slot).copied() == Some(entity.generation())
129 }
130
131 pub fn despawn(&mut self, entity: Entity) -> bool {
138 if !self.is_alive(entity) {
139 return false;
140 }
141 let slot = entity.index() as usize;
142 if let Some(alive) = self.alive.get_mut(slot) {
143 *alive = false;
144 }
145 if let Some(generation) = self.generations.get_mut(slot) {
146 *generation = generation.wrapping_add(1);
155 }
156 self.free.push(entity.index());
157 self.live_count = self.live_count.saturating_sub(1);
158 true
159 }
160
161 pub fn iter(&self) -> impl Iterator<Item = Entity> + '_ {
166 self.alive
167 .iter()
168 .enumerate()
169 .filter(|(_, alive)| **alive)
170 .filter_map(|(slot, _)| {
171 let index = u32::try_from(slot).ok()?;
172 let generation = self.generations.get(slot).copied()?;
173 Some(Entity::new(index, generation))
174 })
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn a_fresh_allocator_is_empty() {
184 let entities = Entities::new();
185 assert!(entities.is_empty());
186 assert_eq!(entities.len(), 0);
187 assert_eq!(entities.capacity(), 0);
188 }
189
190 #[test]
191 fn spawning_hands_out_distinct_slots() {
192 let mut entities = Entities::new();
193 let first = entities.spawn();
194 let second = entities.spawn();
195 assert_ne!(first, second);
196 assert_eq!(first.index(), 0);
197 assert_eq!(second.index(), 1);
198 assert_eq!(entities.len(), 2);
199 assert!(entities.is_alive(first));
200 assert!(entities.is_alive(second));
201 }
202
203 #[test]
206 fn a_stale_handle_does_not_name_the_slots_new_owner() {
207 let mut entities = Entities::new();
208 let old = entities.spawn();
209 assert!(entities.despawn(old));
210
211 let new = entities.spawn();
212 assert_eq!(new.index(), old.index(), "the slot must be reused");
213 assert_ne!(new.generation(), old.generation());
214
215 assert!(!entities.is_alive(old), "the stale handle must be dead");
216 assert!(entities.is_alive(new));
217 }
218
219 #[test]
220 fn despawning_twice_is_a_no_op_the_second_time() {
221 let mut entities = Entities::new();
222 let entity = entities.spawn();
223 assert!(entities.despawn(entity));
224 assert!(!entities.despawn(entity));
225 assert_eq!(entities.len(), 0);
226 }
227
228 #[test]
229 fn a_handle_from_another_allocator_is_not_alive_here() {
230 let mut one = Entities::new();
231 let mut other = Entities::new();
232 let _ = one.spawn();
233 let stranger = other.spawn();
234 assert!(one.is_alive(stranger), "documented limit, not a promise");
238 }
239
240 #[test]
241 fn iteration_is_in_ascending_slot_order() {
242 let mut entities = Entities::new();
243 let made: Vec<Entity> = (0..8).map(|_| entities.spawn()).collect();
244 for index in [1usize, 4, 5] {
246 assert!(entities.despawn(made[index]));
247 }
248 let seen: Vec<u32> = entities.iter().map(Entity::index).collect();
249 assert_eq!(seen, vec![0, 2, 3, 6, 7]);
250 }
251
252 #[test]
253 fn reuse_keeps_the_slot_range_compact() {
254 let mut entities = Entities::new();
255 let first = entities.spawn();
256 let second = entities.spawn();
257 entities.despawn(first);
258 entities.despawn(second);
259 let a = entities.spawn();
260 let b = entities.spawn();
261 assert_eq!(entities.capacity(), 2, "no new slots were needed");
262 assert!(entities.is_alive(a));
263 assert!(entities.is_alive(b));
264 }
265
266 #[test]
267 fn a_handle_prints_its_slot_and_generation() {
268 let mut entities = Entities::new();
269 let entity = entities.spawn();
270 assert_eq!(entity.to_string(), "e0v0");
271 }
272}