avian3d/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;
19
20#[derive(Debug, Clone)]
21struct Slot<T> {
22    generation: u32,
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 is_older_generation(generation, slot.generation) {
150                return None;
151            }
152
153            *slot = Slot { generation, value };
154
155            return None;
156        }
157
158        self.slots.insert(index, Slot { generation, value });
159
160        None
161    }
162
163    /// Removes a entity from the secondary map, returning the value at the entity if
164    /// the entity was not previously removed.
165    #[inline]
166    pub fn remove(&mut self, entity: Entity) -> Option<V> {
167        if let hash_map::Entry::Occupied(entry) = self.slots.entry(entity.index()) {
168            if entry.get().generation == entity.generation() {
169                return Some(entry.remove_entry().1.value);
170            }
171        }
172
173        None
174    }
175
176    /// Clears the secondary map. Keeps the allocated memory for reuse.
177    #[inline]
178    pub fn clear(&mut self) {
179        self.slots.clear();
180    }
181
182    /// Returns a reference to the value corresponding to the entity.
183    #[inline]
184    pub fn get(&self, entity: Entity) -> Option<&V> {
185        self.slots
186            .get(&entity.index())
187            .filter(|slot| slot.generation == entity.generation())
188            .map(|slot| &slot.value)
189    }
190
191    /// Returns a reference to the value corresponding to the entity without
192    /// version or bounds checking.
193    ///
194    /// # Safety
195    ///
196    /// This should only be used if `contains(entity)` is true. Otherwise it is
197    /// potentially unsafe.
198    #[inline]
199    pub unsafe fn get_unchecked(&self, entity: Entity) -> &V {
200        debug_assert!(self.contains(entity));
201        self.get(entity).unwrap_unchecked()
202    }
203
204    /// Returns a mutable reference to the value corresponding to the entity.
205    #[inline]
206    pub fn get_mut(&mut self, entity: Entity) -> Option<&mut V> {
207        self.slots
208            .get_mut(&entity.index())
209            .filter(|slot| slot.generation == entity.generation())
210            .map(|slot| &mut slot.value)
211    }
212
213    /// Returns a mutable reference to the value corresponding to the entity
214    /// without version or bounds checking.
215    ///
216    /// # Safety
217    ///
218    /// This should only be used if `contains(entity)` is true. Otherwise it is
219    /// potentially unsafe.
220    #[inline]
221    pub unsafe fn get_unchecked_mut(&mut self, entity: Entity) -> &mut V {
222        debug_assert!(self.contains(entity));
223        self.get_mut(entity).unwrap_unchecked()
224    }
225
226    /// Returns the value corresponding to the entity if it exists, otherwise inserts
227    /// the value returned by `f` and returns it.
228    #[inline]
229    pub fn get_or_insert_with<F>(&mut self, entity: Entity, f: F) -> V
230    where
231        F: FnOnce() -> V,
232        V: Clone + Copy,
233    {
234        if let Some(slot) = self
235            .slots
236            .get(&entity.index())
237            .filter(|s| s.generation == entity.generation())
238        {
239            slot.value
240        } else {
241            let value = f();
242            self.insert(entity, value);
243            value
244        }
245    }
246
247    /// Returns mutable references to the values corresponding to the given
248    /// keys. All keys must be valid and disjoint, otherwise `None` is returned.
249    #[inline]
250    pub fn get_disjoint_mut<const N: usize>(
251        &mut self,
252        entities: [Entity; N],
253    ) -> Option<[&mut V; N]> {
254        // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
255        // safe because the type we are claiming to have initialized here is a
256        // bunch of `MaybeUninit`s, which do not require initialization.
257        let mut ptrs: [MaybeUninit<*mut V>; N] = unsafe { MaybeUninit::uninit().assume_init() };
258
259        let mut i = 0;
260        while i < N {
261            let entity = entities[i];
262
263            match self.slots.get_mut(&entity.index()) {
264                Some(Slot { generation, value }) if *generation == entity.generation() => {
265                    // This entity is valid, and the slot is occupied. Temporarily
266                    // make the generation even so duplicate keys would show up as
267                    // invalid, since keys always have an odd generation. This
268                    // gives us a linear time disjointness check.
269                    ptrs[i] = MaybeUninit::new(&mut *value);
270                    *generation ^= 1;
271                }
272
273                _ => break,
274            }
275
276            i += 1;
277        }
278
279        // Undo temporary even versions.
280        for entity in &entities[0..i] {
281            match self.slots.get_mut(&entity.index()) {
282                Some(Slot { generation, .. }) => {
283                    *generation ^= 1;
284                }
285                _ => unsafe { core::hint::unreachable_unchecked() },
286            }
287        }
288
289        if i == N {
290            // All were valid and disjoint.
291            Some(unsafe { core::mem::transmute_copy::<_, [&mut V; N]>(&ptrs) })
292        } else {
293            None
294        }
295    }
296
297    /// Returns mutable references to the values corresponding to the given
298    /// keys. All keys must be valid and disjoint.
299    ///
300    /// # Safety
301    ///
302    /// This should only be used if `contains(entity)` is true for every given
303    /// entity and no two keys are equal. Otherwise it is potentially unsafe.
304    #[inline]
305    pub unsafe fn get_disjoint_unchecked_mut<const N: usize>(
306        &mut self,
307        entities: [Entity; N],
308    ) -> [&mut V; N] {
309        // Safe, see get_disjoint_mut.
310        let mut ptrs: [MaybeUninit<*mut V>; N] = MaybeUninit::uninit().assume_init();
311        for i in 0..N {
312            ptrs[i] = MaybeUninit::new(self.get_unchecked_mut(entities[i]));
313        }
314        core::mem::transmute_copy::<_, [&mut V; N]>(&ptrs)
315    }
316}
317
318impl<V, S> Default for SparseSecondaryEntityMap<V, S>
319where
320    S: hash::BuildHasher + Default,
321{
322    fn default() -> Self {
323        Self::with_hasher(Default::default())
324    }
325}