use alloc::alloc::{alloc, dealloc, handle_alloc_error};
use core::{alloc::Layout, mem, ptr::NonNull};
use crate::item::Item;
#[repr(C)]
struct Header {
item: &'static Item,
}
fn layout(body: Layout) -> (Layout, usize) {
Layout::new::<Header>()
.extend(body)
.unwrap_or_else(|_| handle_alloc_error(body))
}
impl Header {
#[inline]
fn body(&self) -> NonNull<()> {
let (_, offset) = layout(self.item.layout);
unsafe {
NonNull::new_unchecked(self as *const Self as *mut Self)
.cast::<()>()
.byte_add(offset)
}
}
}
pub(crate) struct ItemBox {
ptr: NonNull<Header>,
}
unsafe impl Send for ItemBox {}
unsafe impl Sync for ItemBox {}
impl ItemBox {
pub(crate) fn new(item: &'static Item) -> Self {
let (layout, offset) = layout(item.layout);
let allocation = Allocation::new(layout);
let ptr = allocation.ptr.cast::<Header>();
unsafe {
ptr.write(Header { item });
(item.init)(ptr.cast().byte_add(offset));
}
Self {
ptr: allocation.commit().cast(),
}
}
#[inline]
fn header(&self) -> &Header {
unsafe { self.ptr.as_ref() }
}
}
struct Allocation {
ptr: NonNull<u8>,
layout: Layout,
}
impl Allocation {
fn new(layout: Layout) -> Self {
let ptr =
NonNull::new(unsafe { alloc(layout) }).unwrap_or_else(|| handle_alloc_error(layout));
Self { ptr, layout }
}
fn commit(self) -> NonNull<u8> {
let ptr = self.ptr;
mem::forget(self);
ptr
}
}
impl Drop for Allocation {
fn drop(&mut self) {
unsafe { dealloc(self.ptr.as_ptr(), self.layout) };
}
}
impl<T> AsRef<T> for ItemBox {
#[inline]
fn as_ref(&self) -> &T {
unsafe { self.header().body().cast().as_ref() }
}
}
impl<T> AsMut<T> for ItemBox {
#[inline]
fn as_mut(&mut self) -> &mut T {
unsafe { self.header().body().cast().as_mut() }
}
}
impl Drop for ItemBox {
fn drop(&mut self) {
let item = self.header().item;
let (layout, offset) = layout(item.layout);
unsafe {
(item.drop)(self.ptr.cast().byte_add(offset));
dealloc(self.ptr.cast().as_ptr(), layout);
}
}
}
#[cfg(all(axtest, feature = "axtest"))]
pub fn boxed_layout_rules_hold_for_test() -> bool {
let l1 = Layout::new::<u8>();
let (header_layout, offset1) = layout(l1);
assert!(header_layout.size() >= core::mem::size_of::<Header>());
assert!(offset1 >= core::mem::size_of::<Header>());
let l2 = Layout::new::<[u64; 8]>();
let (header_layout2, offset2) = layout(l2);
assert!(header_layout2.size() >= core::mem::size_of::<Header>());
assert!(offset2 >= core::mem::size_of::<Header>());
true
}
#[cfg(all(axtest, feature = "axtest"))]
pub fn boxed_layout_more_edge_cases_hold_for_test() -> bool {
use core::alloc::Layout;
let zst = Layout::new::<()>();
let (header_layout_z, offset_z) = layout(zst);
assert!(header_layout_z.size() >= core::mem::size_of::<Header>());
assert!(offset_z >= core::mem::size_of::<Header>());
let u32_layout = Layout::new::<u32>();
let (_hl3, offset3) = layout(u32_layout);
assert!(offset3 >= core::mem::size_of::<Header>());
let u64_layout = Layout::new::<u64>();
let (_hl4, offset4) = layout(u64_layout);
assert!(offset4 >= core::mem::size_of::<Header>());
let aligned = Layout::from_size_align(16, 16).unwrap();
let (header_layout_a, offset_a) = layout(aligned);
assert!(header_layout_a.size() >= core::mem::size_of::<Header>());
assert!(offset_a >= core::mem::size_of::<Header>());
assert!(header_layout_a.align() >= core::mem::align_of::<usize>());
true
}
#[cfg(all(axtest, feature = "axtest"))]
pub fn boxed_layout_comprehensive_hold_for_test() -> bool {
use core::alloc::Layout;
let types_to_test: &[Layout] = &[
Layout::new::<u8>(),
Layout::new::<u16>(),
Layout::new::<u32>(),
Layout::new::<u64>(),
Layout::new::<u128>(),
Layout::new::<[u8; 1]>(),
Layout::new::<[u8; 256]>(),
Layout::new::<[u64; 16]>(),
Layout::from_size_align(1, 1).unwrap(),
Layout::from_size_align(1024, 1024).unwrap(),
Layout::from_size_align(4096, 4096).unwrap(),
];
for &t in types_to_test {
let (_hl, offset) = layout(t);
assert!(offset >= core::mem::size_of::<Header>());
}
let header_size = core::mem::size_of::<Header>();
assert!(header_size > 0);
assert!(header_size <= 128);
true
}
#[cfg(all(axtest, feature = "axtest"))]
pub fn boxed_header_size_and_alignment_hold_for_test() -> bool {
use core::alloc::Layout;
let header_layout = Layout::new::<Header>();
assert!(header_layout.size() > 0);
assert!(header_layout.size() <= core::mem::size_of::<usize>() * 4);
assert!(header_layout.align() >= core::mem::align_of::<usize>());
let (combined, _offset) = layout(Layout::new::<u8>());
assert!(combined.size() > 0);
for size in [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] {
let body = Layout::from_size_align(size, 1).unwrap();
let (_hl, offset) = layout(body);
assert!(offset >= core::mem::size_of::<Header>());
assert!(offset % core::mem::align_of::<usize>() == 0); }
true
}
#[cfg(all(axtest, feature = "axtest"))]
pub fn boxed_layout_alignment_edge_cases_hold_for_test() -> bool {
use core::alloc::Layout;
for align in [1, 2, 4, 8, 16, 32, 64, 128] {
let body = Layout::from_size_align(align, align).unwrap();
let (hl, offset) = layout(body);
assert!(hl.align() >= align);
assert!(offset >= core::mem::size_of::<Header>());
assert!(offset % align == 0 || offset % core::mem::align_of::<Header>() == 0);
}
let big_align = Layout::from_size_align(256, 256).unwrap();
let (hl_big, offset_big) = layout(big_align);
assert!(hl_big.align() >= 256);
assert!(offset_big >= core::mem::size_of::<Header>());
true
}