use std::alloc::{alloc, dealloc, Layout};
use std::mem::{self, ManuallyDrop};
use std::panic::UnwindSafe;
use std::ptr;
use crate::collector::InternalGcRef;
use crate::marker::GcDrop;
use crate::{Finalize, Scan, Scanner, ToScan};
#[derive(Copy, Clone, Debug, Hash)]
pub struct GcAllocation {
pub(crate) scan_ptr: *const dyn Scan,
deallocation_action: DeallocationAction,
}
#[derive(Copy, Clone, Debug, Hash)]
pub enum DeallocationAction {
BoxDrop,
DoNothing,
RunDrop,
RunFinalizer { finalize_ptr: *const dyn Finalize },
}
unsafe impl Send for GcAllocation {}
impl UnwindSafe for GcAllocation {}
unsafe impl Sync for GcAllocation {}
impl GcAllocation {
pub fn allocate_with_drop<T: Scan + GcDrop>(v: T) -> (Self, *const T) {
let (scan_ptr, raw_ptr) = Self::raw_allocate(v);
(
Self {
scan_ptr,
deallocation_action: DeallocationAction::RunDrop,
},
raw_ptr,
)
}
pub fn allocate_no_drop<T: Scan>(v: T) -> (Self, *const T) {
let (scan_ptr, raw_ptr) = Self::raw_allocate(v);
(
Self {
scan_ptr,
deallocation_action: DeallocationAction::DoNothing,
},
raw_ptr,
)
}
#[allow(clippy::transmute_ptr_to_ptr)]
pub fn allocate_with_finalization<T: Scan + Finalize>(v: T) -> (Self, *const T) {
let (scan_ptr, raw_ptr) = Self::raw_allocate(v);
let finalize_ptr = unsafe { mem::transmute(raw_ptr as *const dyn Finalize) };
(
Self {
scan_ptr,
deallocation_action: DeallocationAction::RunFinalizer { finalize_ptr },
},
raw_ptr,
)
}
pub fn from_box<T: Scan + ToScan + GcDrop + ?Sized>(v: Box<T>) -> (Self, *const T) {
let scan_ptr: *const dyn Scan = v.to_scan();
let raw_ptr: *const T = Box::into_raw(v);
(
Self {
scan_ptr,
deallocation_action: DeallocationAction::BoxDrop,
},
raw_ptr,
)
}
#[allow(clippy::transmute_ptr_to_ptr)]
fn raw_allocate<'a, T: Scan + 'a>(v: T) -> (*const dyn Scan, *const T) {
let data_ptr = unsafe {
let heap_space = alloc(Layout::new::<T>()) as *mut T;
ptr::write(heap_space, v);
heap_space as *const T
};
let fat_ptr: *const (dyn Scan + 'a) = data_ptr;
let fat_ptr: *const dyn Scan = unsafe { mem::transmute(fat_ptr) };
(fat_ptr, data_ptr)
}
pub unsafe fn deallocate(self) {
let scan_ptr: *const dyn Scan = self.scan_ptr;
match self.deallocation_action {
DeallocationAction::DoNothing => {
let mut scanner = Scanner::new(|h| {
h.invalidate();
});
(&*scan_ptr).scan(&mut scanner);
}
DeallocationAction::RunDrop => {
let droppable_ptr = scan_ptr as *mut ManuallyDrop<dyn Scan>;
let droppable_ref = &mut *droppable_ptr;
ManuallyDrop::drop(droppable_ref);
}
DeallocationAction::RunFinalizer { finalize_ptr } => {
{
let mut scanner = Scanner::new(|h| {
h.invalidate();
});
(&*scan_ptr).scan(&mut scanner);
}
(&mut *(finalize_ptr as *mut dyn Finalize)).finalize();
}
DeallocationAction::BoxDrop => {
let box_ptr = Box::from_raw(scan_ptr as *mut dyn Scan);
drop(box_ptr);
}
}
if !matches!(self.deallocation_action, DeallocationAction::BoxDrop) {
let dealloc_layout = Layout::for_value(&*scan_ptr);
let heap_ptr = scan_ptr as *mut u8;
dealloc(heap_ptr, dealloc_layout);
}
}
pub fn scan<F: FnMut(InternalGcRef)>(&self, callback: F) {
unsafe {
let mut scanner = Scanner::new(callback);
let to_scan = &*self.scan_ptr;
to_scan.scan(&mut scanner);
}
}
#[cfg(test)]
pub(crate) unsafe fn raw(v: *const dyn Scan) -> Self {
Self {
scan_ptr: v,
deallocation_action: DeallocationAction::DoNothing,
}
}
}