Skip to main content

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
103impl<T> SparseSet<T> {
104    /// Creates a new, empty sparse set.
105    ///
106    /// # Example
107    ///
108    /// ```
109    /// use goud_engine::ecs::SparseSet;
110    ///
111    /// let set: SparseSet<i32> = SparseSet::new();
112    /// assert!(set.is_empty());
113    /// assert_eq!(set.len(), 0);
114    /// ```
115    #[inline]
116    pub fn new() -> Self {
117        Self {
118            sparse: Vec::new(),
119            dense: Vec::new(),
120            values: Vec::new(),
121        }
122    }
123
124    /// Creates a sparse set with pre-allocated capacity.
125    ///
126    /// This pre-allocates the dense and values vectors, but not the sparse
127    /// vector (which grows on demand based on entity indices).
128    ///
129    /// # Arguments
130    ///
131    /// * `capacity` - Number of elements to pre-allocate
132    ///
133    /// # Example
134    ///
135    /// ```
136    /// use goud_engine::ecs::SparseSet;
137    ///
138    /// let set: SparseSet<String> = SparseSet::with_capacity(1000);
139    /// assert!(set.is_empty());
140    /// ```
141    #[inline]
142    pub fn with_capacity(capacity: usize) -> Self {
143        Self {
144            sparse: Vec::new(),
145            dense: Vec::with_capacity(capacity),
146            values: Vec::with_capacity(capacity),
147        }
148    }
149
150    /// Inserts a value for the given entity.
151    ///
152    /// If the entity already has a value, it is replaced and the old value
153    /// is returned. Otherwise, `None` is returned.
154    ///
155    /// # Arguments
156    ///
157    /// * `entity` - The entity to associate with the value
158    /// * `value` - The value to store
159    ///
160    /// # Returns
161    ///
162    /// The previous value if one existed, or `None`.
163    ///
164    /// # Panics
165    ///
166    /// Panics if the entity is a placeholder (`Entity::PLACEHOLDER`).
167    ///
168    /// # Example
169    ///
170    /// ```
171    /// use goud_engine::ecs::{Entity, SparseSet};
172    ///
173    /// let mut set = SparseSet::new();
174    /// let entity = Entity::new(0, 1);
175    ///
176    /// assert_eq!(set.insert(entity, 42), None);
177    /// assert_eq!(set.insert(entity, 99), Some(42)); // Returns old value
178    /// assert_eq!(set.get(entity), Some(&99));
179    /// ```
180    pub fn insert(&mut self, entity: Entity, value: T) -> Option<T> {
181        assert!(
182            !entity.is_placeholder(),
183            "Cannot insert with placeholder entity"
184        );
185
186        let index = entity.index() as usize;
187
188        // Grow sparse vec if needed
189        if index >= self.sparse.len() {
190            self.sparse.resize(index + 1, None);
191        }
192
193        if let Some(dense_index) = self.sparse[index] {
194            // Entity already has a value - replace it
195            let old_value = std::mem::replace(&mut self.values[dense_index], value);
196            Some(old_value)
197        } else {
198            // New entity - add to dense arrays
199            let dense_index = self.dense.len();
200            self.sparse[index] = Some(dense_index);
201            self.dense.push(entity);
202            self.values.push(value);
203            None
204        }
205    }
206
207    /// Removes the value for the given entity.
208    ///
209    /// Uses swap-remove to maintain dense packing: the last element is
210    /// moved to fill the gap, keeping iteration cache-friendly.
211    ///
212    /// # Arguments
213    ///
214    /// * `entity` - The entity whose value to remove
215    ///
216    /// # Returns
217    ///
218    /// The removed value if one existed, or `None`.
219    ///
220    /// # Example
221    ///
222    /// ```
223    /// use goud_engine::ecs::{Entity, SparseSet};
224    ///
225    /// let mut set = SparseSet::new();
226    /// let entity = Entity::new(0, 1);
227    ///
228    /// set.insert(entity, "hello");
229    /// assert_eq!(set.remove(entity), Some("hello"));
230    /// assert_eq!(set.remove(entity), None); // Already removed
231    /// ```
232    pub fn remove(&mut self, entity: Entity) -> Option<T> {
233        if entity.is_placeholder() {
234            return None;
235        }
236
237        let index = entity.index() as usize;
238
239        // Check bounds
240        if index >= self.sparse.len() {
241            return None;
242        }
243
244        // Get dense index
245        let dense_index = self.sparse[index]?;
246        self.sparse[index] = None;
247
248        // Get the last entity in the dense array
249        let last_index = self.dense.len() - 1;
250
251        if dense_index != last_index {
252            // Swap with last element
253            let last_entity = self.dense[last_index];
254            self.dense.swap(dense_index, last_index);
255            self.values.swap(dense_index, last_index);
256
257            // Update sparse pointer for swapped entity
258            self.sparse[last_entity.index() as usize] = Some(dense_index);
259        }
260
261        // Remove last element (which is now our removed entity)
262        self.dense.pop();
263        self.values.pop()
264    }
265
266    /// Returns a reference to the value for the given entity.
267    ///
268    /// # Arguments
269    ///
270    /// * `entity` - The entity to look up
271    ///
272    /// # Returns
273    ///
274    /// A reference to the value if the entity has one, or `None`.
275    ///
276    /// # Example
277    ///
278    /// ```
279    /// use goud_engine::ecs::{Entity, SparseSet};
280    ///
281    /// let mut set = SparseSet::new();
282    /// let entity = Entity::new(0, 1);
283    ///
284    /// set.insert(entity, 42);
285    /// assert_eq!(set.get(entity), Some(&42));
286    ///
287    /// let missing = Entity::new(999, 1);
288    /// assert_eq!(set.get(missing), None);
289    /// ```
290    #[inline]
291    pub fn get(&self, entity: Entity) -> Option<&T> {
292        if entity.is_placeholder() {
293            return None;
294        }
295
296        let index = entity.index() as usize;
297
298        if index >= self.sparse.len() {
299            return None;
300        }
301
302        let dense_index = self.sparse[index]?;
303        Some(&self.values[dense_index])
304    }
305
306    /// Returns a mutable reference to the value for the given entity.
307    ///
308    /// # Arguments
309    ///
310    /// * `entity` - The entity to look up
311    ///
312    /// # Returns
313    ///
314    /// A mutable reference to the value if the entity has one, or `None`.
315    ///
316    /// # Example
317    ///
318    /// ```
319    /// use goud_engine::ecs::{Entity, SparseSet};
320    ///
321    /// let mut set = SparseSet::new();
322    /// let entity = Entity::new(0, 1);
323    ///
324    /// set.insert(entity, 42);
325    ///
326    /// if let Some(value) = set.get_mut(entity) {
327    ///     *value = 100;
328    /// }
329    ///
330    /// assert_eq!(set.get(entity), Some(&100));
331    /// ```
332    #[inline]
333    pub fn get_mut(&mut self, entity: Entity) -> Option<&mut T> {
334        if entity.is_placeholder() {
335            return None;
336        }
337
338        let index = entity.index() as usize;
339
340        if index >= self.sparse.len() {
341            return None;
342        }
343
344        let dense_index = self.sparse[index]?;
345        Some(&mut self.values[dense_index])
346    }
347
348    /// Returns `true` if the entity has a value in this set.
349    ///
350    /// # Arguments
351    ///
352    /// * `entity` - The entity to check
353    ///
354    /// # Example
355    ///
356    /// ```
357    /// use goud_engine::ecs::{Entity, SparseSet};
358    ///
359    /// let mut set = SparseSet::new();
360    /// let entity = Entity::new(0, 1);
361    ///
362    /// assert!(!set.contains(entity));
363    /// set.insert(entity, "value");
364    /// assert!(set.contains(entity));
365    /// ```
366    #[inline]
367    pub fn contains(&self, entity: Entity) -> bool {
368        if entity.is_placeholder() {
369            return false;
370        }
371
372        let index = entity.index() as usize;
373
374        if index >= self.sparse.len() {
375            return false;
376        }
377
378        self.sparse[index].is_some()
379    }
380
381    /// Returns the number of entities with values in this set.
382    ///
383    /// # Example
384    ///
385    /// ```
386    /// use goud_engine::ecs::{Entity, SparseSet};
387    ///
388    /// let mut set = SparseSet::new();
389    /// assert_eq!(set.len(), 0);
390    ///
391    /// set.insert(Entity::new(0, 1), "a");
392    /// set.insert(Entity::new(5, 1), "b");
393    /// assert_eq!(set.len(), 2);
394    /// ```
395    #[inline]
396    pub fn len(&self) -> usize {
397        self.dense.len()
398    }
399
400    /// Returns `true` if the set contains no values.
401    ///
402    /// # Example
403    ///
404    /// ```
405    /// use goud_engine::ecs::{Entity, SparseSet};
406    ///
407    /// let mut set: SparseSet<i32> = SparseSet::new();
408    /// assert!(set.is_empty());
409    ///
410    /// set.insert(Entity::new(0, 1), 42);
411    /// assert!(!set.is_empty());
412    /// ```
413    #[inline]
414    pub fn is_empty(&self) -> bool {
415        self.dense.is_empty()
416    }
417
418    /// Removes all values from the set.
419    ///
420    /// This clears all three internal arrays. After calling `clear()`,
421    /// `len()` will return 0.
422    ///
423    /// # Example
424    ///
425    /// ```
426    /// use goud_engine::ecs::{Entity, SparseSet};
427    ///
428    /// let mut set = SparseSet::new();
429    /// set.insert(Entity::new(0, 1), "a");
430    /// set.insert(Entity::new(1, 1), "b");
431    /// assert_eq!(set.len(), 2);
432    ///
433    /// set.clear();
434    /// assert!(set.is_empty());
435    /// ```
436    pub fn clear(&mut self) {
437        self.sparse.clear();
438        self.dense.clear();
439        self.values.clear();
440    }
441
442    /// Reserves capacity for at least `additional` more elements.
443    ///
444    /// This affects the dense and values arrays, not the sparse array
445    /// (which grows based on entity indices).
446    ///
447    /// # Arguments
448    ///
449    /// * `additional` - Number of additional elements to reserve
450    ///
451    /// # Example
452    ///
453    /// ```
454    /// use goud_engine::ecs::SparseSet;
455    ///
456    /// let mut set: SparseSet<i32> = SparseSet::new();
457    /// set.reserve(1000);
458    /// ```
459    pub fn reserve(&mut self, additional: usize) {
460        self.dense.reserve(additional);
461        self.values.reserve(additional);
462    }
463}
464
465impl<T> Default for SparseSet<T> {
466    fn default() -> Self {
467        Self::new()
468    }
469}
470
471impl<T: Clone> Clone for SparseSet<T> {
472    fn clone(&self) -> Self {
473        Self {
474            sparse: self.sparse.clone(),
475            dense: self.dense.clone(),
476            values: self.values.clone(),
477        }
478    }
479}