avian2d/data_structures/
sparse_secondary_map.rs

1//! A map for associating data with previously stored entities in a generational arena.
2//!
3//! This is an adaptation of [`slotmap::SparseSecondaryMap`], tailored for Avian.
4//! Some modifications include:
5//!
6//! - The key is always an [`Entity`] instead of a generic key type.
7//! - Much more minimalistic. No entry API or iterators.
8//! - `no_std` compatible.
9//!
10//! [`slotmap::SparseSecondaryMap`]: https://docs.rs/slotmap/1.0.7/slotmap/struct.SparseSecondaryMap.html
11
12use alloc::collections::TryReserveError;
13use bevy::platform::hash::RandomState;
14use core::mem::MaybeUninit;
15use std::collections::hash_map::{self, HashMap};
16use std::hash;
17
18use bevy::ecs::entity::{Entity, EntityGeneration};
19
20#[derive(Debug, Clone)]
21struct Slot<T> {
22    generation: EntityGeneration,
23    value: T,
24}
25
26/// Sparse secondary map for associating data with previously stored entities
27/// in a generational arena.
28#[derive(Debug, Clone)]
29pub struct SparseSecondaryEntityMap<V, S: hash::BuildHasher = RandomState> {
30    slots: HashMap<u32, Slot<V>, S>,
31}
32
33impl<V> SparseSecondaryEntityMap<V, hash_map::RandomState> {
34    /// Constructs a new, empty [`SparseSecondaryEntityMap`].
35    #[inline]
36    pub fn new() -> Self {
37        Self::with_capacity(0)
38    }
39
40    /// Creates an empty [`SparseSecondaryEntityMap`] with the given capacity of slots.
41    ///
42    /// The secondary map will not reallocate until it holds at least `capacity`
43    /// slots.
44    #[inline]
45    pub fn with_capacity(capacity: usize) -> Self {
46        Self {
47            slots: HashMap::with_capacity(capacity),
48        }
49    }
50}
51
52/// Returns if a is an older generation than b, taking into account wrapping of
53/// generations.
54fn is_older_generation(a: u32, b: u32) -> bool {
55    let diff = a.wrapping_sub(b);
56    diff >= (1 << 31)
57}
58
59impl<V, S: hash::BuildHasher> SparseSecondaryEntityMap<V, S> {
60    /// Creates an empty [`SparseSecondaryEntityMap`] which will use the given hash
61    /// builder to hash keys.
62    ///
63    /// The secondary map will not reallocate until it holds at least `capacity`
64    /// slots.
65    #[inline]
66    pub fn with_hasher(hash_builder: S) -> Self {
67        Self {
68            slots: HashMap::with_hasher(hash_builder),
69        }
70    }
71
72    /// Creates an empty [`SparseSecondaryEntityMap`] with the given capacity of slots,
73    /// using `hash_builder` to hash the keys.
74    ///
75    /// The secondary map will not reallocate until it holds at least `capacity`
76    /// slots.
77    #[inline]
78    pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> Self {
79        Self {
80            slots: HashMap::with_capacity_and_hasher(capacity, hash_builder),
81        }
82    }
83
84    /// Returns the number of elements in the secondary map.
85    #[inline]
86    pub fn len(&self) -> usize {
87        self.slots.len()
88    }
89
90    /// Returns if the secondary map is empty.
91    #[inline]
92    pub fn is_empty(&self) -> bool {
93        self.slots.is_empty()
94    }
95
96    /// Returns the number of elements the [`SparseSecondaryEntityMap`] can hold without
97    /// reallocating.
98    #[inline]
99    pub fn capacity(&self) -> usize {
100        self.slots.capacity()
101    }
102
103    /// Reserves capacity for at least `additional` more slots in the
104    /// [`SparseSecondaryEntityMap`]. The collection may reserve more space to avoid
105    /// frequent reallocations.
106    ///
107    /// # Panics
108    ///
109    /// Panics if the new allocation size overflows [`usize`].
110    #[inline]
111    pub fn reserve(&mut self, additional: usize) {
112        self.slots.reserve(additional);
113    }
114
115    /// Tries to reserve capacity for at least `additional` more slots in the
116    /// [`SparseSecondaryEntityMap`].  The collection may reserve more space to avoid
117    /// frequent reallocations.
118    #[inline]
119    pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
120        self.slots.try_reserve(additional)
121    }
122
123    /// Returns `true` if the secondary map contains the given `entity`.
124    #[inline]
125    pub fn contains(&self, entity: Entity) -> bool {
126        self.slots
127            .get(&entity.index())
128            .is_some_and(|slot| slot.generation == entity.generation())
129    }
130
131    /// Inserts a value into the secondary map at the given `entity`.
132    ///
133    /// Returns [`None`] if this entity was not present in the map,
134    /// and the old value otherwise.
135    #[inline]
136    pub fn insert(&mut self, entity: Entity, value: V) -> Option<V> {
137        if entity == Entity::PLACEHOLDER {
138            return None;
139        }
140
141        let (index, generation) = (entity.index(), entity.generation());
142
143        if let Some(slot) = self.slots.get_mut(&index) {
144            if slot.generation == generation {
145                return Some(core::mem::replace(&mut slot.value, value));
146            }
147
148            // Don't replace existing newer values.
149            if unsafe {
150                is_older_generation(
151                    core::mem::transmute::<EntityGeneration, u32>(generation),
152                    core::mem::transmute::<EntityGeneration, u32>(slot.generation),
153                )
154            } {
155                return None;
156            }
157
158            *slot = Slot { generation, value };
159
160            return None;
161        }
162
163        self.slots.insert(index, Slot { generation, value });
164
165        None
166    }
167
168    /// Removes a entity from the secondary map, returning the value at the entity if
169    /// the entity was not previously removed.
170    #[inline]
171    pub fn remove(&mut self, entity: Entity) -> Option<V> {
172        if let hash_map::Entry::Occupied(entry) = self.slots.entry(entity.index())
173            && entry.get().generation == entity.generation()
174        {
175            return Some(entry.remove_entry().1.value);
176        }
177
178        None
179    }
180
181    /// Clears the secondary map. Keeps the allocated memory for reuse.
182    #[inline]
183    pub fn clear(&mut self) {
184        self.slots.clear();
185    }
186
187    /// Returns a reference to the value corresponding to the entity.
188    #[inline]
189    pub fn get(&self, entity: Entity) -> Option<&V> {
190        self.slots
191            .get(&entity.index())
192            .filter(|slot| slot.generation == entity.generation())
193            .map(|slot| &slot.value)
194    }
195
196    /// Returns a reference to the value corresponding to the entity without
197    /// version or bounds checking.
198    ///
199    /// # Safety
200    ///
201    /// This should only be used if `contains(entity)` is true. Otherwise it is
202    /// potentially unsafe.
203    #[inline]
204    pub unsafe fn get_unchecked(&self, entity: Entity) -> &V {
205        debug_assert!(self.contains(entity));
206        unsafe { self.get(entity).unwrap_unchecked() }
207    }
208
209    /// Returns a mutable reference to the value corresponding to the entity.
210    #[inline]
211    pub fn get_mut(&mut self, entity: Entity) -> Option<&mut V> {
212        self.slots
213            .get_mut(&entity.index())
214            .filter(|slot| slot.generation == entity.generation())
215            .map(|slot| &mut slot.value)
216    }
217
218    /// Returns a mutable reference to the value corresponding to the entity
219    /// without version or bounds checking.
220    ///
221    /// # Safety
222    ///
223    /// This should only be used if `contains(entity)` is true. Otherwise it is
224    /// potentially unsafe.
225    #[inline]
226    pub unsafe fn get_unchecked_mut(&mut self, entity: Entity) -> &mut V {
227        debug_assert!(self.contains(entity));
228        unsafe { self.get_mut(entity).unwrap_unchecked() }
229    }
230
231    /// Returns the value corresponding to the entity if it exists, otherwise inserts
232    /// the value returned by `f` and returns it.
233    #[inline]
234    pub fn get_or_insert_with<F>(&mut self, entity: Entity, f: F) -> V
235    where
236        F: FnOnce() -> V,
237        V: Clone + Copy,
238    {
239        if let Some(slot) = self
240            .slots
241            .get(&entity.index())
242            .filter(|s| s.generation == entity.generation())
243        {
244            slot.value
245        } else {
246            let value = f();
247            self.insert(entity, value);
248            value
249        }
250    }
251
252    /// Returns mutable references to the values corresponding to the given
253    /// keys. All keys must be valid and disjoint, otherwise `None` is returned.
254    #[inline]
255    pub fn get_disjoint_mut<const N: usize>(
256        &mut self,
257        entities: [Entity; N],
258    ) -> Option<[&mut V; N]> {
259        // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
260        // safe because the type we are claiming to have initialized here is a
261        // bunch of `MaybeUninit`s, which do not require initialization.
262        let mut ptrs: [MaybeUninit<*mut V>; N] = unsafe { MaybeUninit::uninit().assume_init() };
263
264        let mut i = 0;
265        while i < N {
266            let entity = entities[i];
267
268            match self.slots.get_mut(&entity.index()) {
269                Some(Slot { generation, value }) if *generation == entity.generation() => {
270                    // This entity is valid, and the slot is occupied. Temporarily
271                    // make the generation even so duplicate keys would show up as
272                    // invalid, since keys always have an odd generation. This
273                    // gives us a linear time disjointness check.
274                    ptrs[i] = MaybeUninit::new(&mut *value);
275                    unsafe {
276                        *core::mem::transmute::<&mut EntityGeneration, &mut u32>(generation) ^= 1;
277                    }
278                }
279
280                _ => break,
281            }
282
283            i += 1;
284        }
285
286        // Undo temporary even versions.
287        for entity in &entities[0..i] {
288            match self.slots.get_mut(&entity.index()) {
289                Some(Slot { generation, .. }) => unsafe {
290                    *core::mem::transmute::<&mut EntityGeneration, &mut u32>(generation) ^= 1;
291                },
292                _ => unsafe { core::hint::unreachable_unchecked() },
293            }
294        }
295
296        if i == N {
297            // All were valid and disjoint.
298            Some(unsafe { core::mem::transmute_copy::<_, [&mut V; N]>(&ptrs) })
299        } else {
300            None
301        }
302    }
303
304    /// Returns mutable references to the values corresponding to the given
305    /// keys. All keys must be valid and disjoint.
306    ///
307    /// # Safety
308    ///
309    /// This should only be used if `contains(entity)` is true for every given
310    /// entity and no two keys are equal. Otherwise it is potentially unsafe.
311    #[inline]
312    pub unsafe fn get_disjoint_unchecked_mut<const N: usize>(
313        &mut self,
314        entities: [Entity; N],
315    ) -> [&mut V; N] {
316        // Safe, see get_disjoint_mut.
317        unsafe {
318            let mut ptrs: [MaybeUninit<*mut V>; N] = MaybeUninit::uninit().assume_init();
319            for i in 0..N {
320                ptrs[i] = MaybeUninit::new(self.get_unchecked_mut(entities[i]));
321            }
322            core::mem::transmute_copy::<_, [&mut V; N]>(&ptrs)
323        }
324    }
325}
326
327impl<V, S> Default for SparseSecondaryEntityMap<V, S>
328where
329    S: hash::BuildHasher + Default,
330{
331    fn default() -> Self {
332        Self::with_hasher(Default::default())
333    }
334}