const MAX_DROP_LIST_ITEMS: usize = 1022;
pub struct DropList {
items: [Option<DropItem>; MAX_DROP_LIST_ITEMS],
next_list: Option<*mut DropList>,
used_items: u16,
}
impl DropList {
#[inline(always)]
pub fn empty() -> DropList {
DropList {
items: [None; MAX_DROP_LIST_ITEMS],
next_list: None,
used_items: 0,
}
}
unsafe fn write_item(&mut self, item: DropItem) -> (DropListWriteResult, *const Option<DropItem>) {
let ix = self.used_items as usize;
self.items[ix] = Some(item);
self.used_items += 1;
let item_ptr = self.items.get_unchecked(ix) as *const Option<DropItem>;
(
if self.used_items as usize == MAX_DROP_LIST_ITEMS {
DropListWriteResult::ListFull
} else {
DropListWriteResult::ListNotFull
},
item_ptr
)
}
pub unsafe fn push_drop_fn<T>(&mut self, data: *const u8) -> (DropListWriteResult, *const Option<DropItem>) {
let drop_item = DropItem {
fun: drop::<T>,
data,
};
self.write_item(drop_item)
}
pub unsafe fn push_custom_drop_fn(&mut self, fun: DropFn, data: *const u8) -> (DropListWriteResult, *const Option<DropItem>) {
let drop_item = DropItem {
fun,
data,
};
self.write_item(drop_item)
}
#[inline(always)]
pub unsafe fn set_next_list(&mut self, list: *mut DropList) {
self.next_list = Some(list)
}
pub unsafe fn execute_drop_chain(&mut self) {
let mut maybe_head = Some(self);
while let Some(list) = maybe_head {
for i in list.items.iter_mut() {
if let Some(drop_item) = i {
drop_item.execute();
} else {
return;
}
*i = None;
}
maybe_head = list.next_list
.map(|ptr| std::mem::transmute::<*mut DropList, &mut DropList>(ptr));
list.next_list = None;
}
}
}
pub enum DropListWriteResult {
ListFull,
ListNotFull,
}
#[derive(Copy, Clone)]
pub struct DropItem {
pub fun: DropFn,
pub data: *const u8,
}
impl DropItem {
#[inline(always)]
pub unsafe fn execute(&self) {
(self.fun)(self.data);
}
}
pub type DropFn = unsafe fn(data: *const u8) -> ();
#[inline(always)]
pub unsafe fn drop<T: Sized>(bytes: *const u8) {
let ptr_to_t = bytes as *const T;
debug_assert_eq!(ptr_to_t.align_offset(std::mem::align_of::<T>()), 0, "drop alignment incorrect");
let ref_to_t = std::mem::transmute::<*const u8, &T>(bytes);
let _ = std::mem::transmute_copy::<T, T>(ref_to_t);
}
#[cfg(test)]
mod tests {
use crate::droplist::{DropList, DropListWriteResult};
use crate::dontdothis;
use crate::dropflag::{DropFlag, DropableWithData};
use std::cell::RefCell;
#[test]
fn droplist_single() {
let mut list = DropList::empty();
let flag1 = DropFlag::new(RefCell::new(0));
let droppable1 = DropableWithData { data: 42, dropflag: flag1.clone() };
let mut bytes = vec![0u8; std::mem::size_of::<DropableWithData>()];
std::io::copy(
&mut unsafe { dontdothis::value_as_slice(&droppable1) },
&mut std::io::Cursor::new(&mut bytes)).unwrap();
std::mem::forget(droppable1);
unsafe { list.push_drop_fn::<DropableWithData>(bytes.as_ptr()); }
assert_eq!(0, *flag1.borrow());
unsafe { list.execute_drop_chain() };
assert_eq!(42, *flag1.borrow());
unsafe { list.execute_drop_chain() };
assert_eq!(42, *flag1.borrow());
}
#[test]
fn droplist_chain() {
let mut list1 = DropList::empty();
let mut list2 = DropList::empty();
let flags = (0..super::MAX_DROP_LIST_ITEMS+1)
.map(|_| DropFlag::new(RefCell::new(0)))
.collect::<Vec<_>>()
.into_boxed_slice();
let droppables: Vec<_> = flags.iter()
.map(|flag| DropableWithData { data: 42, dropflag: flag.clone() })
.collect();
let mut bytes = vec![0u8; std::mem::size_of::<DropableWithData>() * (super::MAX_DROP_LIST_ITEMS*2)]
.into_boxed_slice();
let mut copy_target_slice = &mut bytes[..];
let mut first_full = false;
for droppable in droppables {
std::io::copy(
&mut unsafe { dontdothis::value_as_slice(&droppable) },
&mut copy_target_slice).unwrap();
std::mem::forget(droppable);
unsafe {
let location_of_struct_start = copy_target_slice.as_ptr().offset(-(std::mem::size_of::<DropableWithData>() as isize));
if !first_full {
match list1.push_drop_fn::<DropableWithData>(location_of_struct_start) {
(DropListWriteResult::ListFull, _) => {
first_full = true;
list1.set_next_list((&mut list2) as *mut DropList);
},
(DropListWriteResult::ListNotFull, _) => (),
}
} else {
match list2.push_drop_fn::<DropableWithData>(location_of_struct_start) {
(DropListWriteResult::ListFull, _) => {
panic!("second list full");
},
(DropListWriteResult::ListNotFull, _) => (),
}
}
}
}
for flag in flags.iter() {
assert_eq!(0, *flag.borrow());
}
unsafe { list1.execute_drop_chain() };
for flag in flags.iter() {
assert_eq!(42, *flag.borrow());
}
}
}