1use crate::archetype::EntityLocation;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11pub struct Entity {
12 pub(crate) index: u32,
13 pub(crate) generation: u32,
14}
15
16impl Entity {
17 pub fn from_raw(index: u32, generation: u32) -> Self {
24 Self { index, generation }
25 }
26
27 pub fn index(self) -> u32 {
29 self.index
30 }
31
32 pub fn generation(self) -> u32 {
34 self.generation
35 }
36}
37
38#[derive(Clone, Debug)]
44pub(crate) struct EntityMeta {
45 pub generation: u32,
47 pub alive: bool,
49 pub location: Option<EntityLocation>,
51}
52
53impl EntityMeta {
54 fn new_alive() -> Self {
55 Self {
56 generation: 0,
57 alive: true,
58 location: None,
59 }
60 }
61}
62
63pub(crate) struct EntityMetaStore {
72 metas: Vec<EntityMeta>,
73 free: Vec<u32>,
74}
75
76impl EntityMetaStore {
77 pub fn new() -> Self {
78 Self {
79 metas: Vec::new(),
80 free: Vec::new(),
81 }
82 }
83
84 pub fn alloc(&mut self) -> Entity {
86 if let Some(index) = self.free.pop() {
87 let meta = &mut self.metas[index as usize];
88 meta.alive = true;
89 meta.location = None;
90 Entity {
91 index,
92 generation: meta.generation,
93 }
94 } else {
95 let index = self.metas.len() as u32;
96 self.metas.push(EntityMeta::new_alive());
97 Entity {
98 index,
99 generation: 0,
100 }
101 }
102 }
103
104 pub fn dealloc(&mut self, entity: Entity) -> bool {
106 let idx = entity.index as usize;
107 if idx < self.metas.len() {
108 let meta = &mut self.metas[idx];
109 if meta.generation == entity.generation && meta.alive {
110 meta.alive = false;
111 meta.generation += 1;
112 meta.location = None;
113 self.free.push(entity.index);
114 return true;
115 }
116 }
117 false
118 }
119
120 pub fn is_alive(&self, entity: Entity) -> bool {
122 let idx = entity.index as usize;
123 idx < self.metas.len()
124 && self.metas[idx].generation == entity.generation
125 && self.metas[idx].alive
126 }
127
128 #[allow(dead_code)]
130 pub fn len(&self) -> usize {
131 self.metas.len()
132 }
133
134 pub fn set_location(&mut self, entity: Entity, location: EntityLocation) {
136 let idx = entity.index as usize;
137 debug_assert!(self.is_alive(entity), "set_location on dead entity");
138 self.metas[idx].location = Some(location);
139 }
140
141 pub fn get_location(&self, entity: Entity) -> Option<EntityLocation> {
143 let idx = entity.index as usize;
144 if self.is_alive(entity) {
145 self.metas[idx].location
146 } else {
147 None
148 }
149 }
150
151 #[allow(dead_code)]
153 pub fn entity_at(&self, index: u32) -> Option<Entity> {
154 let idx = index as usize;
155 if idx < self.metas.len() && self.metas[idx].alive {
156 Some(Entity {
157 index,
158 generation: self.metas[idx].generation,
159 })
160 } else {
161 None
162 }
163 }
164
165 pub fn alive_entities(&self) -> impl Iterator<Item = Entity> + '_ {
167 self.metas
168 .iter()
169 .enumerate()
170 .filter(|(_, meta)| meta.alive)
171 .map(|(i, meta)| Entity {
172 index: i as u32,
173 generation: meta.generation,
174 })
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use crate::archetype::{ArchetypeId, EntityLocation};
182
183 #[test]
186 fn meta_store_alloc_returns_sequential_indices() {
187 let mut store = EntityMetaStore::new();
188 let e0 = store.alloc();
189 let e1 = store.alloc();
190 assert_eq!(e0.index, 0);
191 assert_eq!(e1.index, 1);
192 assert_eq!(e0.generation, 0);
193 assert_eq!(e1.generation, 0);
194 }
195
196 #[test]
197 fn meta_store_dealloc_and_reuse_bumps_generation() {
198 let mut store = EntityMetaStore::new();
199 let e0 = store.alloc();
200 assert!(store.dealloc(e0));
201
202 let e0_reused = store.alloc();
203 assert_eq!(e0_reused.index, 0);
204 assert_eq!(e0_reused.generation, 1);
205 }
206
207 #[test]
208 fn meta_store_is_alive_returns_false_after_dealloc() {
209 let mut store = EntityMetaStore::new();
210 let e = store.alloc();
211 assert!(store.is_alive(e));
212 store.dealloc(e);
213 assert!(!store.is_alive(e));
214 }
215
216 #[test]
217 fn meta_store_stale_handle_is_not_alive() {
218 let mut store = EntityMetaStore::new();
219 let old = store.alloc();
220 store.dealloc(old);
221 let _new = store.alloc();
222 assert!(!store.is_alive(old));
223 }
224
225 #[test]
226 fn meta_store_double_dealloc_returns_false() {
227 let mut store = EntityMetaStore::new();
228 let e = store.alloc();
229 assert!(store.dealloc(e));
230 assert!(!store.dealloc(e));
231 }
232
233 #[test]
234 fn meta_store_alive_entities_iterates_only_living() {
235 let mut store = EntityMetaStore::new();
236 let _e0 = store.alloc();
237 let e1 = store.alloc();
238 let _e2 = store.alloc();
239 store.dealloc(e1);
240
241 let alive: Vec<_> = store.alive_entities().collect();
242 assert_eq!(alive.len(), 2);
243 assert_eq!(alive[0].index, 0);
244 assert_eq!(alive[1].index, 2);
245 }
246
247 #[test]
248 fn meta_store_location_tracking() {
249 let mut store = EntityMetaStore::new();
250 let e = store.alloc();
251
252 assert_eq!(store.get_location(e), None);
254
255 let loc = EntityLocation {
256 archetype_id: ArchetypeId(0),
257 row: 3,
258 };
259 store.set_location(e, loc);
260 assert_eq!(store.get_location(e), Some(loc));
261
262 store.dealloc(e);
264 assert_eq!(store.get_location(e), None);
265 }
266
267 #[test]
268 fn meta_store_realloc_clears_location() {
269 let mut store = EntityMetaStore::new();
270 let e = store.alloc();
271 store.set_location(
272 e,
273 EntityLocation {
274 archetype_id: ArchetypeId(5),
275 row: 2,
276 },
277 );
278 store.dealloc(e);
279
280 let e2 = store.alloc(); assert_eq!(e2.index, 0);
282 assert_eq!(store.get_location(e2), None); }
284}