use core::{
cell::Cell,
mem::MaybeUninit,
ptr::{self, addr_of_mut, slice_from_raw_parts_mut, NonNull},
};
struct Drops {
count: usize,
drop: unsafe fn(NonNull<Drops>, usize),
next: Option<NonNull<Self>>,
}
impl Drops {
unsafe fn drop(ptr: NonNull<Self>) -> Option<NonNull<Self>> {
let Drops { count, drop, next } = *ptr.as_ref();
unsafe { (drop)(ptr, count) };
next
}
}
#[repr(C)]
pub struct DropItem<T: ?Sized> {
drops: Drops,
pub value: T,
}
impl<T> DropItem<T> {
pub unsafe fn init_value<'a, I>(
mut ptr: NonNull<DropItem<T>>,
init: I,
f: impl FnOnce(&mut MaybeUninit<T>, I),
) -> &'a mut Self {
let drops_ptr = addr_of_mut!((*ptr.as_ptr()).drops);
f(&mut *addr_of_mut!((*ptr.as_ptr()).value).cast(), init);
ptr::write(
drops_ptr,
Drops {
count: 1,
drop: drop_from_item::<T>,
next: None,
},
);
ptr.as_mut()
}
}
impl<T> DropItem<[T; 0]> {
pub unsafe fn init_slice<'a>(
mut ptr: NonNull<DropItem<[T; 0]>>,
count: usize,
) -> (&'a mut Self, &'a mut [T]) {
debug_assert_ne!(
count, 0,
"DropItem<[T]> should not be constructed with count 0"
);
ptr::write(
ptr.as_ptr().cast(),
Drops {
count,
drop: drop_from_item::<T>,
next: None,
},
);
let slice = core::slice::from_raw_parts_mut(ptr.as_ptr().add(1).cast(), count);
(ptr.as_mut(), slice)
}
}
pub struct DropList {
root: Cell<Option<NonNull<Drops>>>,
}
impl DropList {
pub const fn new() -> Self {
DropList {
root: Cell::new(None),
}
}
#[allow(clippy::mut_from_ref)]
pub unsafe fn add<'a, 'b: 'a, T: ?Sized>(&'a self, item: &'b mut DropItem<T>) -> &'a mut T {
item.drops.next = self.root.take();
let item = NonNull::from(item);
self.root.set(Some(item.cast()));
&mut *addr_of_mut!((*item.as_ptr()).value)
}
pub fn reset(&mut self) {
let mut next = self.root.take();
while let Some(item_ptr) = next {
unsafe {
next = Drops::drop(item_ptr);
}
}
}
}
unsafe fn drop_from_item<T>(ptr: NonNull<Drops>, count: usize) {
let ptr = ptr.cast::<DropItem<T>>();
let value_ptr = addr_of_mut!((*ptr.as_ptr()).value);
core::ptr::drop_in_place(slice_from_raw_parts_mut(value_ptr, count))
}