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}