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}