goud_engine/ecs/sparse_set/core.rs
1//! Core sparse set data structure for component storage.
2//!
3//! A sparse set provides O(1) insertion, deletion, and lookup while maintaining
4//! cache-friendly iteration over dense data. This is the foundation for ECS
5//! component storage.
6//!
7//! # Design Pattern: Sparse Set
8//!
9//! The sparse set uses two arrays:
10//!
11//! - **Sparse**: Maps entity indices to dense array indices (may have gaps)
12//! - **Dense**: Packed array of entities for cache-friendly iteration
13//! - **Values**: Packed array of component values, parallel to dense
14//!
15//! ```text
16//! Entity(3) has component "A"
17//! Entity(7) has component "B"
18//! Entity(1) has component "C"
19//!
20//! Sparse: [_, Some(2), _, Some(0), _, _, _, Some(1)]
21//! 0 1 2 3 4 5 6 7
22//!
23//! Dense: [Entity(3), Entity(7), Entity(1)]
24//! 0 1 2
25//!
26//! Values: ["A", "B", "C"]
27//! 0 1 2
28//! ```
29//!
30//! # Performance Characteristics
31//!
32//! | Operation | Time Complexity | Notes |
33//! |-----------|-----------------|-------|
34//! | insert | O(1) amortized | May grow sparse vec |
35//! | remove | O(1) | Uses swap-remove |
36//! | get | O(1) | |
37//! | contains | O(1) | |
38//! | iterate | O(n) | Cache-friendly, contiguous |
39//!
40//! # Example
41//!
42//! ```
43//! use goud_engine::ecs::{Entity, SparseSet};
44//!
45//! let mut set: SparseSet<String> = SparseSet::new();
46//!
47//! let e1 = Entity::new(0, 1);
48//! let e2 = Entity::new(5, 1);
49//!
50//! set.insert(e1, "Hello".to_string());
51//! set.insert(e2, "World".to_string());
52//!
53//! assert_eq!(set.get(e1), Some(&"Hello".to_string()));
54//! assert!(set.contains(e2));
55//!
56//! // Cache-friendly iteration
57//! for (entity, value) in set.iter() {
58//! println!("{}: {}", entity, value);
59//! }
60//! ```
61//!
62//! # Thread Safety
63//!
64//! `SparseSet<T>` is `Send` if `T: Send` and `Sync` if `T: Sync`.
65//! The sparse set itself is not internally synchronized - wrap in
66//! `RwLock` or similar for concurrent access.
67
68use super::super::Entity;
69
70/// A sparse set storing values of type `T` indexed by [`Entity`].
71///
72/// Provides O(1) access operations while maintaining cache-friendly iteration.
73/// This is the primary storage backend for ECS components.
74///
75/// # Type Parameters
76///
77/// - `T`: The value type stored in the set
78///
79/// # Memory Layout
80///
81/// The sparse set trades memory for performance:
82///
83/// - Sparse vec grows to max entity index seen (sparse memory usage)
84/// - Dense and values vecs are tightly packed (no gaps)
85///
86/// For entities with indices 0, 100, 1000, the sparse vec will have 1001 entries,
87/// but dense/values will only have 3 entries.
88#[derive(Debug)]
89pub struct SparseSet<T> {
90 /// Maps entity index to position in dense array.
91 /// `sparse[entity.index()]` = Some(dense_index) if entity has a value.
92 pub(super) sparse: Vec<Option<usize>>,
93
94 /// Packed array of entities that have values.
95 /// Enables O(1) removal via swap-remove.
96 pub(super) dense: Vec<Entity>,
97
98 /// Packed array of values, parallel to `dense`.
99 /// `values[i]` is the value for `dense[i]`.
100 pub(super) values: Vec<T>,
101
102 /// Tick at which each component was added, parallel to `dense`/`values`.
103 pub(super) added_ticks: Vec<u32>,
104
105 /// Tick at which each component was last changed, parallel to `dense`/`values`.
106 pub(super) changed_ticks: Vec<u32>,
107}
108
109impl<T> SparseSet<T> {
110 /// Creates a new, empty sparse set.
111 ///
112 /// # Example
113 ///
114 /// ```
115 /// use goud_engine::ecs::SparseSet;
116 ///
117 /// let set: SparseSet<i32> = SparseSet::new();
118 /// assert!(set.is_empty());
119 /// assert_eq!(set.len(), 0);
120 /// ```
121 #[inline]
122 pub fn new() -> Self {
123 Self {
124 sparse: Vec::new(),
125 dense: Vec::new(),
126 values: Vec::new(),
127 added_ticks: Vec::new(),
128 changed_ticks: Vec::new(),
129 }
130 }
131
132 /// Creates a sparse set with pre-allocated capacity.
133 ///
134 /// This pre-allocates the dense and values vectors, but not the sparse
135 /// vector (which grows on demand based on entity indices).
136 ///
137 /// # Arguments
138 ///
139 /// * `capacity` - Number of elements to pre-allocate
140 ///
141 /// # Example
142 ///
143 /// ```
144 /// use goud_engine::ecs::SparseSet;
145 ///
146 /// let set: SparseSet<String> = SparseSet::with_capacity(1000);
147 /// assert!(set.is_empty());
148 /// ```
149 #[inline]
150 pub fn with_capacity(capacity: usize) -> Self {
151 Self {
152 sparse: Vec::new(),
153 dense: Vec::with_capacity(capacity),
154 values: Vec::with_capacity(capacity),
155 added_ticks: Vec::with_capacity(capacity),
156 changed_ticks: Vec::with_capacity(capacity),
157 }
158 }
159
160 /// Inserts a value for the given entity.
161 ///
162 /// If the entity already has a value, it is replaced and the old value
163 /// is returned. Otherwise, `None` is returned.
164 ///
165 /// # Arguments
166 ///
167 /// * `entity` - The entity to associate with the value
168 /// * `value` - The value to store
169 ///
170 /// # Returns
171 ///
172 /// The previous value if one existed, or `None`.
173 ///
174 /// # Panics
175 ///
176 /// Panics if the entity is a placeholder (`Entity::PLACEHOLDER`).
177 ///
178 /// # Example
179 ///
180 /// ```
181 /// use goud_engine::ecs::{Entity, SparseSet};
182 ///
183 /// let mut set = SparseSet::new();
184 /// let entity = Entity::new(0, 1);
185 ///
186 /// assert_eq!(set.insert(entity, 42), None);
187 /// assert_eq!(set.insert(entity, 99), Some(42)); // Returns old value
188 /// assert_eq!(set.get(entity), Some(&99));
189 /// ```
190 pub fn insert(&mut self, entity: Entity, value: T) -> Option<T> {
191 self.insert_with_tick(entity, value, 0)
192 }
193
194 /// Removes the value for the given entity.
195 ///
196 /// Uses swap-remove to maintain dense packing: the last element is
197 /// moved to fill the gap, keeping iteration cache-friendly.
198 ///
199 /// # Arguments
200 ///
201 /// * `entity` - The entity whose value to remove
202 ///
203 /// # Returns
204 ///
205 /// The removed value if one existed, or `None`.
206 ///
207 /// # Example
208 ///
209 /// ```
210 /// use goud_engine::ecs::{Entity, SparseSet};
211 ///
212 /// let mut set = SparseSet::new();
213 /// let entity = Entity::new(0, 1);
214 ///
215 /// set.insert(entity, "hello");
216 /// assert_eq!(set.remove(entity), Some("hello"));
217 /// assert_eq!(set.remove(entity), None); // Already removed
218 /// ```
219 pub fn remove(&mut self, entity: Entity) -> Option<T> {
220 if entity.is_placeholder() {
221 return None;
222 }
223
224 let index = entity.index() as usize;
225
226 // Check bounds
227 if index >= self.sparse.len() {
228 return None;
229 }
230
231 // Get dense index
232 let dense_index = self.sparse[index]?;
233 self.sparse[index] = None;
234
235 // Get the last entity in the dense array
236 let last_index = self.dense.len() - 1;
237
238 if dense_index != last_index {
239 // Swap with last element
240 let last_entity = self.dense[last_index];
241 self.dense.swap(dense_index, last_index);
242 self.values.swap(dense_index, last_index);
243 self.added_ticks.swap(dense_index, last_index);
244 self.changed_ticks.swap(dense_index, last_index);
245
246 // Update sparse pointer for swapped entity
247 self.sparse[last_entity.index() as usize] = Some(dense_index);
248 }
249
250 // Remove last element (which is now our removed entity)
251 self.dense.pop();
252 self.added_ticks.pop();
253 self.changed_ticks.pop();
254 self.values.pop()
255 }
256
257 /// Returns a reference to the value for the given entity.
258 ///
259 /// # Arguments
260 ///
261 /// * `entity` - The entity to look up
262 ///
263 /// # Returns
264 ///
265 /// A reference to the value if the entity has one, or `None`.
266 ///
267 /// # Example
268 ///
269 /// ```
270 /// use goud_engine::ecs::{Entity, SparseSet};
271 ///
272 /// let mut set = SparseSet::new();
273 /// let entity = Entity::new(0, 1);
274 ///
275 /// set.insert(entity, 42);
276 /// assert_eq!(set.get(entity), Some(&42));
277 ///
278 /// let missing = Entity::new(999, 1);
279 /// assert_eq!(set.get(missing), None);
280 /// ```
281 #[inline]
282 pub fn get(&self, entity: Entity) -> Option<&T> {
283 if entity.is_placeholder() {
284 return None;
285 }
286
287 let index = entity.index() as usize;
288
289 if index >= self.sparse.len() {
290 return None;
291 }
292
293 let dense_index = self.sparse[index]?;
294 Some(&self.values[dense_index])
295 }
296
297 /// Returns a mutable reference to the value for the given entity.
298 ///
299 /// # Arguments
300 ///
301 /// * `entity` - The entity to look up
302 ///
303 /// # Returns
304 ///
305 /// A mutable reference to the value if the entity has one, or `None`.
306 ///
307 /// # Example
308 ///
309 /// ```
310 /// use goud_engine::ecs::{Entity, SparseSet};
311 ///
312 /// let mut set = SparseSet::new();
313 /// let entity = Entity::new(0, 1);
314 ///
315 /// set.insert(entity, 42);
316 ///
317 /// if let Some(value) = set.get_mut(entity) {
318 /// *value = 100;
319 /// }
320 ///
321 /// assert_eq!(set.get(entity), Some(&100));
322 /// ```
323 #[inline]
324 pub fn get_mut(&mut self, entity: Entity) -> Option<&mut T> {
325 if entity.is_placeholder() {
326 return None;
327 }
328
329 let index = entity.index() as usize;
330
331 if index >= self.sparse.len() {
332 return None;
333 }
334
335 let dense_index = self.sparse[index]?;
336 Some(&mut self.values[dense_index])
337 }
338
339 /// Returns `true` if the entity has a value in this set.
340 ///
341 /// # Arguments
342 ///
343 /// * `entity` - The entity to check
344 ///
345 /// # Example
346 ///
347 /// ```
348 /// use goud_engine::ecs::{Entity, SparseSet};
349 ///
350 /// let mut set = SparseSet::new();
351 /// let entity = Entity::new(0, 1);
352 ///
353 /// assert!(!set.contains(entity));
354 /// set.insert(entity, "value");
355 /// assert!(set.contains(entity));
356 /// ```
357 #[inline]
358 pub fn contains(&self, entity: Entity) -> bool {
359 if entity.is_placeholder() {
360 return false;
361 }
362
363 let index = entity.index() as usize;
364
365 if index >= self.sparse.len() {
366 return false;
367 }
368
369 self.sparse[index].is_some()
370 }
371
372 /// Returns the number of entities with values in this set.
373 ///
374 /// # Example
375 ///
376 /// ```
377 /// use goud_engine::ecs::{Entity, SparseSet};
378 ///
379 /// let mut set = SparseSet::new();
380 /// assert_eq!(set.len(), 0);
381 ///
382 /// set.insert(Entity::new(0, 1), "a");
383 /// set.insert(Entity::new(5, 1), "b");
384 /// assert_eq!(set.len(), 2);
385 /// ```
386 #[inline]
387 pub fn len(&self) -> usize {
388 self.dense.len()
389 }
390
391 /// Returns `true` if the set contains no values.
392 ///
393 /// # Example
394 ///
395 /// ```
396 /// use goud_engine::ecs::{Entity, SparseSet};
397 ///
398 /// let mut set: SparseSet<i32> = SparseSet::new();
399 /// assert!(set.is_empty());
400 ///
401 /// set.insert(Entity::new(0, 1), 42);
402 /// assert!(!set.is_empty());
403 /// ```
404 #[inline]
405 pub fn is_empty(&self) -> bool {
406 self.dense.is_empty()
407 }
408
409 /// Removes all values from the set.
410 ///
411 /// This clears all three internal arrays. After calling `clear()`,
412 /// `len()` will return 0.
413 ///
414 /// # Example
415 ///
416 /// ```
417 /// use goud_engine::ecs::{Entity, SparseSet};
418 ///
419 /// let mut set = SparseSet::new();
420 /// set.insert(Entity::new(0, 1), "a");
421 /// set.insert(Entity::new(1, 1), "b");
422 /// assert_eq!(set.len(), 2);
423 ///
424 /// set.clear();
425 /// assert!(set.is_empty());
426 /// ```
427 pub fn clear(&mut self) {
428 self.sparse.clear();
429 self.dense.clear();
430 self.values.clear();
431 self.added_ticks.clear();
432 self.changed_ticks.clear();
433 }
434
435 /// Reserves capacity for at least `additional` more elements.
436 ///
437 /// This affects the dense and values arrays, not the sparse array
438 /// (which grows based on entity indices).
439 ///
440 /// # Arguments
441 ///
442 /// * `additional` - Number of additional elements to reserve
443 ///
444 /// # Example
445 ///
446 /// ```
447 /// use goud_engine::ecs::SparseSet;
448 ///
449 /// let mut set: SparseSet<i32> = SparseSet::new();
450 /// set.reserve(1000);
451 /// ```
452 pub fn reserve(&mut self, additional: usize) {
453 self.dense.reserve(additional);
454 self.values.reserve(additional);
455 self.added_ticks.reserve(additional);
456 self.changed_ticks.reserve(additional);
457 }
458}
459
460impl<T> Default for SparseSet<T> {
461 fn default() -> Self {
462 Self::new()
463 }
464}
465
466impl<T: Clone> Clone for SparseSet<T> {
467 fn clone(&self) -> Self {
468 Self {
469 sparse: self.sparse.clone(),
470 dense: self.dense.clone(),
471 values: self.values.clone(),
472 added_ticks: self.added_ticks.clone(),
473 changed_ticks: self.changed_ticks.clone(),
474 }
475 }
476}