Skip to content

Commit 4842a91

Browse files
committed
Safety comments
1 parent 165a1d2 commit 4842a91

File tree

2 files changed

+39
-14
lines changed

2 files changed

+39
-14
lines changed

crates/bevy_ecs/src/storage/resource.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,11 @@ impl<const SEND: bool> ResourceData<SEND> {
190190
if !SEND {
191191
self.origin_thread_id = Some(std::thread::current().id());
192192
}
193+
// SAFETY:
194+
// - There is only one element, and it's always allocated.
195+
// - The caller guarantees must be valid for the underlying type and thus its
196+
// layout must be identical.
197+
// - The value was previously not present and thus must not have been initialized.
193198
unsafe { self.data.initialize_unchecked(Self::ROW, value) };
194199
*self.added_ticks.deref_mut() = change_tick;
195200
self.is_present = true;
@@ -223,14 +228,17 @@ impl<const SEND: bool> ResourceData<SEND> {
223228
// SAFETY: The caller ensures that the provided value is valid for the underlying type and
224229
// is properly initialized. We've ensured that a value is already present and previously
225230
// initialized.
226-
unsafe {
227-
self.data.replace_unchecked(Self::ROW, value);
228-
}
231+
unsafe { self.data.replace_unchecked(Self::ROW, value) };
229232
} else {
230233
#[cfg(feature = "std")]
231234
if !SEND {
232235
self.origin_thread_id = Some(std::thread::current().id());
233236
}
237+
// SAFETY:
238+
// - There is only one element, and it's always allocated.
239+
// - The caller guarantees must be valid for the underlying type and thus its
240+
// layout must be identical.
241+
// - The value was previously not present and thus must not have been initialized.
234242
unsafe { self.data.initialize_unchecked(Self::ROW, value) };
235243
self.is_present = true;
236244
}
@@ -290,6 +298,7 @@ impl<const SEND: bool> ResourceData<SEND> {
290298
pub(crate) fn remove_and_drop(&mut self) {
291299
if self.is_present() {
292300
self.validate_access();
301+
// SAFETY: There is only one element, and it's always allocated.
293302
unsafe { self.data.drop_last_element(Self::ROW) };
294303
self.is_present = false;
295304
}

crates/bevy_ecs/src/storage/sparse_set.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ impl ComponentSparseSet {
140140

141141
/// Removes all of the values stored within.
142142
pub(crate) fn clear(&mut self) {
143-
unsafe { self.dense.clear(self.entities.len()) };
143+
// SAFETY: This is using the size of the ComponentSparseSet.
144+
unsafe { self.dense.clear(self.len()) };
144145
self.entities.clear();
145146
self.sparse.clear();
146147
}
@@ -185,8 +186,10 @@ impl ComponentSparseSet {
185186

186187
let _guard = AbortOnPanic;
187188
if capacity != self.entities.capacity() {
189+
// SAFETY: An entity was just pushed onto `entities`, its capacity cannot be zero.
188190
let new_capacity = unsafe { NonZero::new_unchecked(self.entities.capacity()) };
189191
if let Some(capacity) = NonZero::new(capacity) {
192+
// SAFETY: This is using the layout of the previous allocation.
190193
unsafe { self.dense.realloc(capacity, new_capacity) };
191194
} else {
192195
self.dense.alloc(new_capacity);
@@ -333,31 +336,37 @@ impl ComponentSparseSet {
333336
if dense_index.index() >= last {
334337
#[cfg(debug_assertions)]
335338
assert_eq!(dense_index.index(), last);
336-
// SAFETY: TODO
339+
// SAFETY: This is strictly decreasing the length, so it cannot outgrow
340+
// it also cannot underflow as an item was just removed from the sparse array.
337341
unsafe { self.entities.set_len(last) };
338-
// SAFETY: TODO
342+
// SAFETY: `last` is guaranteed to be the last element in `dense` as the length is synced with
343+
// the `entities` store.
339344
unsafe {
340345
self.dense
341346
.get_data_unchecked(dense_index)
342347
.assert_unique()
343348
.promote()
344349
}
345350
} else {
346-
// SAFETY: TODO
351+
// SAFETY: The above check ensures that `dense_index` and the last element are not
352+
// overlapping, and thus also within bounds.
347353
unsafe {
348354
self.entities
349355
.swap_remove_nonoverlapping_unchecked(dense_index.index());
350356
};
351-
// SAFETY: TODO
357+
// SAFETY: The above check ensures that `dense_index` is in bounds.
352358
let swapped_entity = unsafe { self.entities.get_unchecked(dense_index.index()) };
353359
#[cfg(not(debug_assertions))]
354360
let index = swapped_entity;
355361
#[cfg(debug_assertions)]
356362
let index = swapped_entity.row();
363+
// SAFETY: The swapped entity was just fetched from the entity Vec, it must have already
364+
// been inserted and in bounds.
357365
unsafe {
358366
*self.sparse.get_mut(index).debug_checked_unwrap() = dense_index;
359367
}
360-
// SAFETY: dense_index was just removed from `sparse`, which ensures that it is valid
368+
// SAFETY: The above check ensures that `dense_index` and the last element are not
369+
// overlapping, and thus also within bounds.
361370
unsafe {
362371
self.dense
363372
.swap_remove_and_forget_unchecked_nonoverlapping(last, dense_index)
@@ -379,27 +388,33 @@ impl ComponentSparseSet {
379388
if dense_index.index() >= last {
380389
#[cfg(debug_assertions)]
381390
assert_eq!(dense_index.index(), last);
382-
// SAFETY: TODO
391+
// SAFETY: This is strictly decreasing the length, so it cannot outgrow
392+
// it also cannot underflow as an item was just removed from the sparse array.
383393
unsafe { self.entities.set_len(last) };
384-
// SAFETY: TODO
394+
// SAFETY: `last` is guaranteed to be the last element in `dense` as the length is synced with
395+
// the `entities` store.
385396
unsafe { self.dense.drop_last_component(last) };
386397
} else {
387-
// SAFETY: TODO
398+
// SAFETY: The above check ensures that `dense_index` and the last element are not
399+
// overlapping, and thus also within bounds.
388400
unsafe {
389401
self.entities
390402
.swap_remove_nonoverlapping_unchecked(dense_index.index());
391403
};
392-
// SAFETY: TODO
393404
let swapped_entity =
405+
// SAFETY: The above check ensures that `dense_index` is in bounds.
394406
unsafe { self.entities.get_unchecked(dense_index.index()) };
395407
#[cfg(not(debug_assertions))]
396408
let index = swapped_entity;
397409
#[cfg(debug_assertions)]
398410
let index = swapped_entity.row();
411+
// SAFETY: The swapped entity was just fetched from the entity Vec, it must have already
412+
// been inserted and in bounds.
399413
unsafe {
400414
*self.sparse.get_mut(index).debug_checked_unwrap() = dense_index;
401415
}
402-
// SAFETY: TODO
416+
// SAFETY: The above check ensures that `dense_index` and the last element are not
417+
// overlapping, and thus also within bounds.
403418
unsafe {
404419
self.dense
405420
.swap_remove_and_drop_unchecked_nonoverlapping(last, dense_index);
@@ -410,6 +425,7 @@ impl ComponentSparseSet {
410425
}
411426

412427
pub(crate) fn check_change_ticks(&mut self, check: CheckChangeTicks) {
428+
// SAFETY: This is using the valid size of the column.
413429
unsafe { self.dense.check_change_ticks(self.len(), check) };
414430
}
415431
}

0 commit comments

Comments
 (0)