Skip to main content

mnemosyne_heap/
branded_box.rs

1use crate::Heap;
2use crate::brand::{BrandedBlock, BrandedCell};
3use crate::heap::BlockFreeGuard;
4use core::marker::PhantomData;
5use core::ops::{Deref, DerefMut};
6use core::ptr::NonNull;
7use melinoe::ReadPermit;
8use mnemosyne_core::AllocPolicy;
9use mnemosyne_local::LocalAllocatorSelector;
10use mnemosyne_local::internal::HasSegmentPool;
11
12/// A uniquely owned, safe pointer to heap-allocated memory of type `T` from a `Heap`.
13///
14/// Automatically drops `T` and deallocates the memory back to the heap on drop.
15pub struct BrandedBox<
16    'brand,
17    'heap,
18    T: ?Sized,
19    P: AllocPolicy = mnemosyne_core::StandardPolicy,
20    B: HasSegmentPool + LocalAllocatorSelector<B> = mnemosyne_backend::MemoryBackendWrapper,
21> {
22    pub(crate) ptr: NonNull<T>,
23    pub(crate) heap: &'heap Heap<'brand, P, B>,
24    pub(crate) _non_send_sync: core::marker::PhantomData<*mut ()>,
25}
26
27impl<'brand, 'heap, T, P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>
28    BrandedBox<'brand, 'heap, T, P, B>
29{
30    /// Creates a new `BrandedBox` containing `val` allocated from the given `Heap`.
31    #[inline(always)]
32    pub fn new<Permit>(heap: &'heap Heap<'brand, P, B>, permit: Permit, val: T) -> Option<Self>
33    where
34        Permit: ReadPermit<'brand>,
35    {
36        if core::mem::size_of::<T>() == 0 {
37            let ptr: NonNull<T> = NonNull::dangling();
38            // SAFETY: `T` is zero-sized, so `NonNull::dangling()` is a valid,
39            // aligned pointer for a zero-byte write. `write` moves `val` into the
40            // (zero-sized) location, conceptually transferring ownership to the
41            // box; no storage is allocated, read, or aliased.
42            unsafe {
43                ptr.as_ptr().write(val);
44            }
45            return Some(Self {
46                ptr,
47                heap,
48                _non_send_sync: core::marker::PhantomData,
49            });
50        }
51
52        let block = heap.alloc_init(permit, val)?;
53        Some(Self {
54            ptr: block.ptr,
55            heap,
56            _non_send_sync: core::marker::PhantomData,
57        })
58    }
59}
60
61impl<'brand, 'heap, T: ?Sized, P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>
62    BrandedBox<'brand, 'heap, T, P, B>
63{
64    /// Consumes the `BrandedBox`, returning the wrapped raw block without dropping or deallocating.
65    #[inline(always)]
66    pub fn into_raw(self) -> BrandedBlock<'brand, T> {
67        let block = BrandedBlock {
68            ptr: self.ptr,
69            _marker: PhantomData,
70        };
71        core::mem::forget(self);
72        block
73    }
74
75    /// Converts this `BrandedBox` into a shared `BrandedCell`.
76    ///
77    /// The memory remains allocated until it is manually reclaimed.
78    #[inline(always)]
79    pub fn into_cell(self) -> BrandedCell<'brand, T> {
80        let block = self.into_raw();
81        // SAFETY: `from_block` requires the block to be initialized with a valid
82        // `T`. `self` is a live `BrandedBox`, whose invariant is that `self.ptr`
83        // points to an initialized `T`; `into_raw` transfers that block out
84        // without dropping or freeing, so the initialized-value invariant carries
85        // over unchanged.
86        unsafe { BrandedCell::from_block(block) }
87    }
88
89    /// Reconstructs a `BrandedBox` from a shared `BrandedCell`.
90    ///
91    /// # Safety
92    /// The caller must ensure that no other copies of this `BrandedCell` (or pointers derived from it)
93    /// are active or will be used.
94    #[inline(always)]
95    pub unsafe fn from_cell(heap: &'heap Heap<'brand, P, B>, cell: BrandedCell<'brand, T>) -> Self {
96        // SAFETY: a `BrandedCell` wraps a block holding an initialized `T`, and
97        // this function's contract makes `cell` the only live handle to it — so
98        // the block `into_block` yields carries a valid `T` and a single owner,
99        // which is `from_raw`'s requirement. The shared `'brand` proves the block
100        // came from `heap`.
101        unsafe { Self::from_raw(heap, cell.into_block()) }
102    }
103
104    /// Reconstructs a `BrandedBox` from a raw block.
105    ///
106    /// # Safety
107    /// The memory block must be initialized with a valid value of type `T`.
108    #[inline(always)]
109    pub unsafe fn from_raw(
110        heap: &'heap Heap<'brand, P, B>,
111        block: BrandedBlock<'brand, T>,
112    ) -> Self {
113        Self {
114            ptr: block.ptr,
115            heap,
116            _non_send_sync: core::marker::PhantomData,
117        }
118    }
119}
120
121impl<'brand, 'heap, T: ?Sized, P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>> Deref
122    for BrandedBox<'brand, 'heap, T, P, B>
123{
124    type Target = T;
125    #[inline(always)]
126    fn deref(&self) -> &Self::Target {
127        // SAFETY: the `BrandedBox` invariant guarantees `self.ptr` points to an
128        // initialized, live, aligned `T` owned by this box. `&self` ties the
129        // returned reference's lifetime to the borrow, and `BrandedBox` is
130        // `!Send`/`!Sync`, so no aliasing mutable access can occur concurrently.
131        unsafe { self.ptr.as_ref() }
132    }
133}
134
135impl<'brand, 'heap, T: ?Sized, P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>
136    DerefMut for BrandedBox<'brand, 'heap, T, P, B>
137{
138    #[inline(always)]
139    fn deref_mut(&mut self) -> &mut Self::Target {
140        // SAFETY: the `BrandedBox` invariant guarantees `self.ptr` points to an
141        // initialized, live, aligned `T` uniquely owned by this box. `&mut self`
142        // proves exclusive access, so the returned unique reference cannot alias
143        // any other reference for its lifetime.
144        unsafe { self.ptr.as_mut() }
145    }
146}
147
148impl<'brand, 'heap, T: ?Sized, P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>> Drop
149    for BrandedBox<'brand, 'heap, T, P, B>
150{
151    #[inline]
152    fn drop(&mut self) {
153        // SAFETY: the `BrandedBox` invariant guarantees `self.ptr` points to an
154        // initialized, live `T` (possibly unsized) uniquely owned by this box.
155        // `as_ref` reads the metadata to compute the value's size;
156        // `drop_in_place` runs the value's destructor exactly once (drop is
157        // invoked at most once per box). Only non-ZST values hold a block —
158        // a ZST's pointer is the dangling sentinel and was never allocated — so
159        // only that branch arms the guard, which owns the block for the
160        // destructor's duration and returns it to `self.heap` exactly once. The
161        // guard rather than a trailing call is what keeps the block from leaking
162        // when `T::drop` panics and unwinds out of `drop_in_place`.
163        unsafe {
164            if core::mem::size_of_val(self.ptr.as_ref()) == 0 {
165                core::ptr::drop_in_place(self.ptr.as_ptr());
166            } else {
167                let _free = BlockFreeGuard::new(self.heap, self.ptr.as_ptr() as *mut u8);
168                core::ptr::drop_in_place(self.ptr.as_ptr());
169            }
170        }
171    }
172}
173
174impl<'brand, 'heap, T: Clone, P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>
175    BrandedBox<'brand, 'heap, T, P, B>
176{
177    /// Clones the box using the given allocator read permit.
178    ///
179    /// Returns `None` if allocation fails.
180    #[inline]
181    pub fn clone_in<Permit>(&self, permit: &Permit) -> Option<Self>
182    where
183        for<'token> &'token Permit: ReadPermit<'brand>,
184    {
185        Self::new(self.heap, permit, (**self).clone())
186    }
187}
188
189impl<
190    'brand,
191    'heap,
192    T: ?Sized + core::fmt::Debug,
193    P: AllocPolicy,
194    B: HasSegmentPool + LocalAllocatorSelector<B>,
195> core::fmt::Debug for BrandedBox<'brand, 'heap, T, P, B>
196{
197    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
198        core::fmt::Debug::fmt(&**self, f)
199    }
200}
201
202impl<
203    'brand,
204    'heap,
205    T: ?Sized + core::fmt::Display,
206    P: AllocPolicy,
207    B: HasSegmentPool + LocalAllocatorSelector<B>,
208> core::fmt::Display for BrandedBox<'brand, 'heap, T, P, B>
209{
210    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
211        core::fmt::Display::fmt(&**self, f)
212    }
213}
214
215impl<'brand, 'heap, T: ?Sized, P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>
216    core::fmt::Pointer for BrandedBox<'brand, 'heap, T, P, B>
217{
218    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
219        core::fmt::Pointer::fmt(&self.ptr.as_ptr(), f)
220    }
221}
222
223impl<
224    'brand,
225    'heap,
226    T: ?Sized + PartialEq,
227    P: AllocPolicy,
228    B: HasSegmentPool + LocalAllocatorSelector<B>,
229> PartialEq for BrandedBox<'brand, 'heap, T, P, B>
230{
231    #[inline]
232    fn eq(&self, other: &Self) -> bool {
233        **self == **other
234    }
235}
236impl<'brand, 'heap, T: ?Sized + Eq, P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>
237    Eq for BrandedBox<'brand, 'heap, T, P, B>
238{
239}
240
241impl<
242    'brand,
243    'heap,
244    T: ?Sized + PartialOrd,
245    P: AllocPolicy,
246    B: HasSegmentPool + LocalAllocatorSelector<B>,
247> PartialOrd for BrandedBox<'brand, 'heap, T, P, B>
248{
249    #[inline]
250    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
251        (**self).partial_cmp(&**other)
252    }
253}
254impl<'brand, 'heap, T: ?Sized + Ord, P: AllocPolicy, B: HasSegmentPool + LocalAllocatorSelector<B>>
255    Ord for BrandedBox<'brand, 'heap, T, P, B>
256{
257    #[inline]
258    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
259        (**self).cmp(&**other)
260    }
261}
262impl<
263    'brand,
264    'heap,
265    T: ?Sized + core::hash::Hash,
266    P: AllocPolicy,
267    B: HasSegmentPool + LocalAllocatorSelector<B>,
268> core::hash::Hash for BrandedBox<'brand, 'heap, T, P, B>
269{
270    #[inline]
271    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
272        (**self).hash(state);
273    }
274}