Skip to content

Commit d183448

Browse files
committed
Yeet swap-scratch
1 parent 589c52a commit d183448

File tree

4 files changed

+51
-32
lines changed

4 files changed

+51
-32
lines changed

crates/bevy_ecs/src/component.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
system::Resource,
77
};
88
pub use bevy_ecs_macros::Component;
9-
use bevy_ptr::OwningPtr;
9+
use bevy_ptr::{OwningPtr, Ptr};
1010
use std::{
1111
alloc::Layout,
1212
any::{Any, TypeId},
@@ -122,6 +122,11 @@ impl ComponentInfo {
122122
self.descriptor.drop
123123
}
124124

125+
#[inline]
126+
pub fn swap(&self) -> unsafe fn(Ptr<'_>, Ptr<'_>) {
127+
self.descriptor.swap
128+
}
129+
125130
#[inline]
126131
pub fn storage_type(&self) -> StorageType {
127132
self.descriptor.storage_type
@@ -177,6 +182,9 @@ pub struct ComponentDescriptor {
177182
// this descriptor describes.
178183
// None if the underlying type doesn't need to be dropped
179184
drop: Option<for<'a> unsafe fn(OwningPtr<'a>)>,
185+
// SAFETY: this function must be safe to call with pointers pointing to items of the type
186+
// this descriptor describes. Must never panic.
187+
swap: for<'a> unsafe fn(Ptr<'a>, Ptr<'a>),
180188
}
181189

182190
// We need to ignore the `drop` field in our `Debug` impl
@@ -198,6 +206,11 @@ impl ComponentDescriptor {
198206
x.drop_as::<T>()
199207
}
200208

209+
// SAFETY: The pointers point to a valid value of type `T`. Can never panic.
210+
unsafe fn swap_ptr<T>(a: Ptr<'_>, b: Ptr<'_>) {
211+
core::ptr::swap(a.as_ptr().cast::<T>(), b.as_ptr().cast::<T>());
212+
}
213+
201214
pub fn new<T: Component>() -> Self {
202215
Self {
203216
name: std::any::type_name::<T>().to_string(),
@@ -206,6 +219,7 @@ impl ComponentDescriptor {
206219
type_id: Some(TypeId::of::<T>()),
207220
layout: Layout::new::<T>(),
208221
drop: needs_drop::<T>().then(|| Self::drop_ptr::<T> as _),
222+
swap: Self::swap_ptr::<T>,
209223
}
210224
}
211225

@@ -222,6 +236,7 @@ impl ComponentDescriptor {
222236
type_id: Some(TypeId::of::<T>()),
223237
layout: Layout::new::<T>(),
224238
drop: needs_drop::<T>().then(|| Self::drop_ptr::<T> as _),
239+
swap: Self::swap_ptr::<T>,
225240
}
226241
}
227242

@@ -233,6 +248,7 @@ impl ComponentDescriptor {
233248
type_id: Some(TypeId::of::<T>()),
234249
layout: Layout::new::<T>(),
235250
drop: needs_drop::<T>().then(|| Self::drop_ptr::<T> as _),
251+
swap: Self::swap_ptr::<T>,
236252
}
237253
}
238254

crates/bevy_ecs/src/storage/blob_vec.rs

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub struct BlobVec {
1515
/// Number of elements, not bytes
1616
len: usize,
1717
data: NonNull<u8>,
18-
swap_scratch: NonNull<u8>,
18+
swap: unsafe fn(Ptr<'_>, Ptr<'_>),
1919
// None if the underlying type doesn't need to be dropped
2020
drop: Option<unsafe fn(OwningPtr<'_>)>,
2121
}
@@ -28,7 +28,6 @@ impl std::fmt::Debug for BlobVec {
2828
.field("capacity", &self.capacity)
2929
.field("len", &self.len)
3030
.field("data", &self.data)
31-
.field("swap_scratch", &self.swap_scratch)
3231
.finish()
3332
}
3433
}
@@ -43,27 +42,26 @@ impl BlobVec {
4342
/// [`needs_drop`]: core::mem::needs_drop
4443
pub unsafe fn new(
4544
item_layout: Layout,
45+
swap: unsafe fn(Ptr<'_>, Ptr<'_>),
4646
drop: Option<unsafe fn(OwningPtr<'_>)>,
4747
capacity: usize,
4848
) -> BlobVec {
4949
if item_layout.size() == 0 {
5050
BlobVec {
51-
swap_scratch: NonNull::dangling(),
5251
data: NonNull::dangling(),
5352
capacity: usize::MAX,
5453
len: 0,
5554
item_layout,
55+
swap,
5656
drop,
5757
}
5858
} else {
59-
let swap_scratch = NonNull::new(std::alloc::alloc(item_layout))
60-
.unwrap_or_else(|| std::alloc::handle_alloc_error(item_layout));
6159
let mut blob_vec = BlobVec {
62-
swap_scratch,
6360
data: NonNull::dangling(),
6461
capacity: 0,
6562
len: 0,
6663
item_layout,
64+
swap,
6765
drop,
6866
};
6967
blob_vec.reserve_exact(capacity);
@@ -180,28 +178,17 @@ impl BlobVec {
180178
/// caller's responsibility to drop the returned pointer, if that is desirable.
181179
///
182180
/// # Safety
183-
/// It is the caller's responsibility to ensure that `index` is < `self.len()`
181+
/// It is the caller's responsibility to ensure that `index` is less than `self.len()`.
184182
#[inline]
185183
#[must_use = "The returned pointer should be used to dropped the removed element"]
186184
pub unsafe fn swap_remove_and_forget_unchecked(&mut self, index: usize) -> OwningPtr<'_> {
187-
// FIXME: This should probably just use `core::ptr::swap` and return an `OwningPtr`
188-
// into the underlying `BlobVec` allocation, and remove swap_scratch
189-
190185
debug_assert!(index < self.len());
191-
let last = self.len - 1;
192-
let swap_scratch = self.swap_scratch.as_ptr();
193-
std::ptr::copy_nonoverlapping::<u8>(
194-
self.get_unchecked_mut(index).as_ptr(),
195-
swap_scratch,
196-
self.item_layout.size(),
197-
);
198-
std::ptr::copy::<u8>(
199-
self.get_unchecked_mut(last).as_ptr(),
200-
self.get_unchecked_mut(index).as_ptr(),
201-
self.item_layout.size(),
202-
);
203-
self.len -= 1;
204-
OwningPtr::new(self.swap_scratch)
186+
let new_len = self.len - 1;
187+
let size = self.item_layout.size();
188+
(self.swap)(self.get_unchecked(new_len), self.get_unchecked(index));
189+
self.len = new_len;
190+
// Cannot use get_unchecked here as this is technically out of bounds after changing len.
191+
self.get_ptr_mut().byte_add(new_len * size).promote()
205192
}
206193

207194
/// # Safety
@@ -283,7 +270,6 @@ impl Drop for BlobVec {
283270
if array_layout.size() > 0 {
284271
unsafe {
285272
std::alloc::dealloc(self.get_ptr_mut().as_ptr(), array_layout);
286-
std::alloc::dealloc(self.swap_scratch.as_ptr(), self.item_layout);
287273
}
288274
}
289275
}
@@ -345,7 +331,7 @@ const fn padding_needed_for(layout: &Layout, align: usize) -> usize {
345331

346332
#[cfg(test)]
347333
mod tests {
348-
use crate::ptr::OwningPtr;
334+
use crate::ptr::{OwningPtr, Ptr};
349335

350336
use super::BlobVec;
351337
use std::{alloc::Layout, cell::RefCell, rc::Rc};
@@ -355,6 +341,11 @@ mod tests {
355341
x.drop_as::<T>()
356342
}
357343

344+
// SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value.
345+
unsafe fn swap_ptr<T>(a: Ptr<'_>, b: Ptr<'_>) {
346+
core::ptr::swap(a.as_ptr().cast::<T>(), b.as_ptr().cast::<T>());
347+
}
348+
358349
/// # Safety
359350
///
360351
/// `blob_vec` must have a layout that matches `Layout::new::<T>()`
@@ -386,7 +377,7 @@ mod tests {
386377
fn resize_test() {
387378
let item_layout = Layout::new::<usize>();
388379
// usize doesn't need dropping
389-
let mut blob_vec = unsafe { BlobVec::new(item_layout, None, 64) };
380+
let mut blob_vec = unsafe { BlobVec::new(item_layout, swap_ptr::<usize>, None, 64) };
390381
unsafe {
391382
for i in 0..1_000 {
392383
push(&mut blob_vec, i as usize);
@@ -416,7 +407,7 @@ mod tests {
416407
{
417408
let item_layout = Layout::new::<Foo>();
418409
let drop = drop_ptr::<Foo>;
419-
let mut blob_vec = unsafe { BlobVec::new(item_layout, Some(drop), 2) };
410+
let mut blob_vec = unsafe { BlobVec::new(item_layout, swap_ptr::<Foo>, Some(drop), 2) };
420411
assert_eq!(blob_vec.capacity(), 2);
421412
unsafe {
422413
let foo1 = Foo {
@@ -476,6 +467,6 @@ mod tests {
476467
fn blob_vec_drop_empty_capacity() {
477468
let item_layout = Layout::new::<Foo>();
478469
let drop = drop_ptr::<Foo>;
479-
let _ = unsafe { BlobVec::new(item_layout, Some(drop), 0) };
470+
let _ = unsafe { BlobVec::new(item_layout, swap_ptr::<Foo>, Some(drop), 0) };
480471
}
481472
}

crates/bevy_ecs/src/storage/sparse_set.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,12 @@ impl ComponentSparseSet {
105105
Self {
106106
// SAFE: component_info.drop() is compatible with the items that will be inserted.
107107
dense: unsafe {
108-
BlobVec::new(component_info.layout(), component_info.drop(), capacity)
108+
BlobVec::new(
109+
component_info.layout(),
110+
component_info.swap(),
111+
component_info.drop(),
112+
capacity,
113+
)
109114
},
110115
ticks: Vec::with_capacity(capacity),
111116
entities: Vec::with_capacity(capacity),

crates/bevy_ecs/src/storage/table.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,14 @@ impl Column {
4242
Column {
4343
component_id: component_info.id(),
4444
// SAFE: component_info.drop() is valid for the types that will be inserted.
45-
data: unsafe { BlobVec::new(component_info.layout(), component_info.drop(), capacity) },
45+
data: unsafe {
46+
BlobVec::new(
47+
component_info.layout(),
48+
component_info.swap(),
49+
component_info.drop(),
50+
capacity,
51+
)
52+
},
4653
ticks: Vec::with_capacity(capacity),
4754
}
4855
}

0 commit comments

Comments
 (0)