use alloc::boxed::Box;
use core::alloc::Layout;
pub unsafe trait Dealloc: Send + Sync + 'static {
unsafe fn dealloc(self, ptr: *mut u8, len: usize);
}
pub struct HeapDealloc {
layout: Layout,
}
impl HeapDealloc {
pub fn new(layout: Layout) -> Self {
Self { layout }
}
}
unsafe impl Dealloc for HeapDealloc {
unsafe fn dealloc(self, ptr: *mut u8, _len: usize) {
unsafe { alloc::alloc::dealloc(ptr, self.layout) }
}
}
pub struct NoDealloc;
unsafe impl Dealloc for NoDealloc {
unsafe fn dealloc(self, _ptr: *mut u8, _len: usize) {}
}
pub(crate) struct ErasedDealloc {
data: *mut (),
drop_fn: unsafe fn(*mut (), *mut u8, usize),
free_fn: unsafe fn(*mut ()),
}
unsafe impl Send for ErasedDealloc {}
unsafe impl Sync for ErasedDealloc {}
impl ErasedDealloc {
pub(crate) fn new<D: Dealloc>(dealloc: D) -> Self {
unsafe fn drop_fn<D: Dealloc>(data: *mut (), ptr: *mut u8, len: usize) {
debug_assert!(!data.is_null());
let dealloc = unsafe { *Box::from_raw(data as *mut D) };
unsafe { dealloc.dealloc(ptr, len) };
}
unsafe fn free_fn<D>(data: *mut ()) {
if !data.is_null() {
unsafe { drop(Box::from_raw(data as *mut D)) };
}
}
let data = Box::into_raw(Box::new(dealloc)) as *mut ();
Self {
data,
drop_fn: drop_fn::<D>,
free_fn: free_fn::<D>,
}
}
pub(crate) fn noop() -> Self {
unsafe fn noop_fn(_data: *mut (), _ptr: *mut u8, _len: usize) {}
unsafe fn noop_free(_data: *mut ()) {}
Self {
data: core::ptr::null_mut(),
drop_fn: noop_fn,
free_fn: noop_free,
}
}
pub(crate) unsafe fn dealloc(mut self, ptr: *mut u8, len: usize) {
let data = self.data;
self.data = core::ptr::null_mut();
unsafe { (self.drop_fn)(data, ptr, len) };
}
}
impl Drop for ErasedDealloc {
fn drop(&mut self) {
unsafe { (self.free_fn)(self.data) };
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn heap_dealloc_frees_memory() {
let layout = Layout::from_size_align(4096, 8).unwrap();
let ptr = unsafe { alloc::alloc::alloc(layout) };
assert!(!ptr.is_null());
let dealloc = HeapDealloc::new(layout);
unsafe { dealloc.dealloc(ptr, 4096) };
}
#[test]
fn no_dealloc_is_noop() {
let ptr = core::ptr::NonNull::<u8>::dangling().as_ptr();
unsafe { NoDealloc.dealloc(ptr, 0) };
}
#[test]
fn erased_dealloc_heap() {
let layout = Layout::from_size_align(4096, 8).unwrap();
let ptr = unsafe { alloc::alloc::alloc(layout) };
assert!(!ptr.is_null());
let erased = ErasedDealloc::new(HeapDealloc::new(layout));
unsafe { erased.dealloc(ptr, 4096) };
}
#[test]
fn erased_dealloc_noop() {
let ptr = core::ptr::NonNull::<u8>::dangling().as_ptr();
let erased = ErasedDealloc::new(NoDealloc);
unsafe { erased.dealloc(ptr, 0) };
}
}