scope_local/
boxed.rs

1use alloc::alloc::{alloc, dealloc, handle_alloc_error};
2use core::{alloc::Layout, ptr::NonNull};
3
4use crate::item::Item;
5
6#[repr(C)]
7struct Header {
8    item: &'static Item,
9}
10
11fn layout(body: Layout) -> (Layout, usize) {
12    Layout::new::<Header>()
13        .extend(body)
14        .unwrap_or_else(|_| handle_alloc_error(body))
15}
16
17impl Header {
18    #[inline]
19    fn body(&self) -> NonNull<()> {
20        let (_, offset) = layout(self.item.layout);
21        unsafe {
22            // FIXME: `NonNull::from_ref` is not stable yet
23            NonNull::new_unchecked(self as *const Self as *mut Self)
24                .cast::<()>()
25                .byte_add(offset)
26        }
27    }
28}
29
30impl Drop for Header {
31    fn drop(&mut self) {
32        (self.item.drop)(self.body());
33    }
34}
35
36pub(crate) struct ItemBox {
37    ptr: NonNull<Header>,
38}
39
40unsafe impl Send for ItemBox {}
41unsafe impl Sync for ItemBox {}
42
43impl ItemBox {
44    pub(crate) fn new(item: &'static Item) -> Self {
45        let (layout, offset) = layout(item.layout);
46        let ptr = NonNull::new(unsafe { alloc(layout) })
47            .unwrap_or_else(|| handle_alloc_error(layout))
48            .cast();
49
50        unsafe {
51            ptr.write(Header { item });
52            (item.init)(ptr.cast().byte_add(offset));
53        }
54
55        Self { ptr }
56    }
57
58    #[inline]
59    fn header(&self) -> &Header {
60        unsafe { self.ptr.as_ref() }
61    }
62}
63
64impl<T> AsRef<T> for ItemBox {
65    #[inline]
66    fn as_ref(&self) -> &T {
67        unsafe { self.header().body().cast().as_ref() }
68    }
69}
70
71impl<T> AsMut<T> for ItemBox {
72    #[inline]
73    fn as_mut(&mut self) -> &mut T {
74        unsafe { self.header().body().cast().as_mut() }
75    }
76}
77
78impl Drop for ItemBox {
79    fn drop(&mut self) {
80        let item = self.header().item;
81        let (layout, offset) = layout(item.layout);
82        unsafe {
83            (item.drop)(self.ptr.cast().byte_add(offset));
84            dealloc(self.ptr.cast().as_ptr(), layout);
85        }
86    }
87}