use super::trace::Trace;
use crate::header::{GcHeader, SizedHeader, SliceHeader, StrHeader};
use alloc::alloc::Layout;
use core::marker::PhantomData;
use core::ptr::{slice_from_raw_parts, NonNull};
pub struct Thin<T: ?Sized> {
kind: PhantomData<T>,
}
pub trait GcPointee {
type GcHeader: GcHeader;
fn as_fat<'a>(thin_ptr: NonNull<Thin<Self>>) -> *const Self;
fn deref<'a>(thin_ptr: NonNull<Thin<Self>>) -> &'a Self {
let fat_ptr = Self::as_fat(thin_ptr);
debug_assert!(!fat_ptr.is_null(), "Attempting to dereference null pointer");
unsafe { &*fat_ptr }
}
fn get_header<'a>(thin_ptr: NonNull<Thin<Self>>) -> &'a Self::GcHeader {
unsafe { &*Self::get_header_ptr(thin_ptr) }
}
fn get_header_ptr(thin_ptr: NonNull<Thin<Self>>) -> *const Self::GcHeader;
}
impl<T: Trace> GcPointee for T {
type GcHeader = SizedHeader<T>;
fn as_fat<'a>(thin_ptr: NonNull<Thin<Self>>) -> *const Self {
let ptr = thin_ptr.cast().as_ptr();
debug_assert!(
ptr as usize % core::mem::align_of::<T>() == 0,
"Pointer {:p} is not aligned to {} bytes (required for type {})",
ptr,
core::mem::align_of::<T>(),
core::any::type_name::<T>()
);
ptr
}
fn get_header_ptr(thin_ptr: NonNull<Thin<Self>>) -> *const Self::GcHeader {
let (_, item_offset) = sized_alloc_layout::<T>();
let header_ptr =
unsafe { thin_ptr.as_ptr().byte_sub(item_offset) as *const Self::GcHeader };
debug_assert!(
header_ptr as usize % core::mem::align_of::<Self::GcHeader>() == 0,
"Header pointer {:p} is not aligned to {} bytes (required for SizedHeader<{}>)",
header_ptr,
core::mem::align_of::<Self::GcHeader>(),
core::any::type_name::<T>()
);
header_ptr
}
}
impl<T: Trace> GcPointee for [T] {
type GcHeader = SliceHeader<T>;
fn as_fat<'a>(thin_ptr: NonNull<Thin<Self>>) -> *const Self {
let slice_ptr: *const T = thin_ptr.cast().as_ptr();
debug_assert!(
slice_ptr as usize % core::mem::align_of::<T>() == 0,
"Slice pointer {:p} is not aligned to {} bytes (required for [{}])",
slice_ptr,
core::mem::align_of::<T>(),
core::any::type_name::<T>()
);
let header: &SliceHeader<T> = Self::get_header(thin_ptr);
let len = header.len();
slice_from_raw_parts(slice_ptr, len)
}
fn get_header_ptr(thin_ptr: NonNull<Thin<Self>>) -> *const Self::GcHeader {
let (_, item_offset) = slice_alloc_layout::<T>(1);
let ptr: *mut Self::GcHeader = thin_ptr.cast().as_ptr();
let header_ptr = unsafe { ptr.byte_sub(item_offset) as *const Self::GcHeader };
debug_assert!(
header_ptr as usize % core::mem::align_of::<Self::GcHeader>() == 0,
"Header pointer {:p} is not aligned to {} bytes (required for SliceHeader<{}>)",
header_ptr,
core::mem::align_of::<Self::GcHeader>(),
core::any::type_name::<T>()
);
header_ptr
}
}
impl GcPointee for str {
type GcHeader = StrHeader;
fn as_fat<'a>(thin_ptr: NonNull<Thin<Self>>) -> *const Self {
let str_ptr: *const u8 = thin_ptr.cast().as_ptr();
debug_assert!(
str_ptr as usize % core::mem::align_of::<u8>() == 0,
"Str pointer {:p} is not aligned to {} bytes",
str_ptr,
core::mem::align_of::<u8>()
);
let header: &StrHeader = Self::get_header(thin_ptr);
let len = header.len();
let byte_slice = slice_from_raw_parts(str_ptr, len);
byte_slice as *const [u8] as *const str
}
fn get_header_ptr(thin_ptr: NonNull<Thin<Self>>) -> *const Self::GcHeader {
let (_, item_offset) = str_alloc_layout(1);
let ptr: *mut Self::GcHeader = thin_ptr.cast().as_ptr();
let header_ptr = unsafe { ptr.byte_sub(item_offset) as *const Self::GcHeader };
debug_assert!(
header_ptr as usize % core::mem::align_of::<Self::GcHeader>() == 0,
"Header pointer {:p} is not aligned to {} bytes (required for StrHeader)",
header_ptr,
core::mem::align_of::<Self::GcHeader>()
);
header_ptr
}
}
pub fn sized_alloc_layout<T>() -> (Layout, usize) {
let header_layout = Layout::new::<SizedHeader<T>>();
let val_layout = Layout::new::<T>();
let (unpadded_layout, offset) = header_layout.extend(val_layout).unwrap();
let layout = unpadded_layout.pad_to_align();
(layout, offset)
}
pub fn slice_alloc_layout<T>(len: usize) -> (Layout, usize) {
let header_layout = Layout::new::<SliceHeader<T>>();
let slice_layout = Layout::array::<T>(len).unwrap();
let (unpadded_layout, offset) = header_layout.extend(slice_layout).unwrap();
let layout = unpadded_layout.pad_to_align();
(layout, offset)
}
pub fn str_alloc_layout(len: usize) -> (Layout, usize) {
let header_layout = Layout::new::<StrHeader>();
let str_layout = Layout::array::<u8>(len).unwrap();
let (unpadded_layout, offset) = header_layout.extend(str_layout).unwrap();
let layout = unpadded_layout.pad_to_align();
(layout, offset)
}