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(&self) -> Option<NonNull<Self>> {
unsafe { (self.drop)(NonNull::from(self), self.count) };
self.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),
}
}
pub unsafe fn add<'a, T: ?Sized>(&self, item: &'a mut DropItem<T>) -> &'a mut T {
item.drops.next = self.root.take();
self.root.set(Some(NonNull::from(&mut item.drops)));
&mut item.value
}
pub fn reset(&mut self) {
let mut next = self.root.take();
while let Some(item_ptr) = next {
let item = unsafe { item_ptr.as_ref() };
unsafe {
next = item.drop();
}
}
}
}
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))
}