Skip to main content

mnemosyne_heap/brand/
block.rs

1//! [`BrandedBlock`]: a heap block branded with a compile-time unique
2//! lifetime, plus its `Debug`/`Pointer`/`Eq`/`Ord`/`Hash` impls.
3
4use core::ptr::NonNull;
5use melinoe::InvariantLifetime;
6
7/// A wrapper representing a heap block branded with a compile-time unique lifetime.
8pub struct BrandedBlock<'brand, T: ?Sized> {
9    pub(crate) ptr: NonNull<T>,
10    pub(crate) _marker: InvariantLifetime<'brand>,
11}
12
13impl<'brand, T: ?Sized> BrandedBlock<'brand, T> {
14    /// Returns the raw pointer to the block's managed memory.
15    #[inline(always)]
16    pub fn as_ptr(&self) -> *mut T {
17        self.ptr.as_ptr()
18    }
19}
20
21impl<'brand, T> BrandedBlock<'brand, T> {
22    /// Casts this branded block to managed memory of a different type,
23    /// preserving the brand.
24    ///
25    /// # Safety
26    ///
27    /// The returned `BrandedBlock<'brand, U>` is trusted by safe APIs that
28    /// interpret the pointee as a `U`: [`crate::Heap::free`] runs
29    /// `core::ptr::drop_in_place::<U>` on it and derives its deallocation
30    /// path from `size_of_val` of the `U`, [`crate::Heap::realloc`] reads
31    /// the pointee's layout the same way, and
32    /// [`super::BrandedCell::from_block`] hands out `&U`/`&mut U`. The caller must
33    /// therefore guarantee:
34    ///
35    /// - **Layout**: the block's allocation is at least `size_of::<U>()`
36    ///   bytes and aligned to `align_of::<U>()` (e.g. it was allocated for a
37    ///   layout that covers `U`), and
38    /// - **Initialization/drop discipline**: either the memory holds a valid
39    ///   `U` before any path reads or drops it as one, or the block is
40    ///   treated as uninitialized `U` storage — written with a valid `U`
41    ///   before such a path (as [`crate::Heap::alloc_init`] does), or
42    ///   released exclusively through the non-dropping
43    ///   [`crate::Heap::free_uninit`].
44    ///
45    /// Violating either (for example casting an initialized `usize` block to
46    /// `String` and freeing it) is a transmute-and-drop and undefined
47    /// behavior.
48    #[inline(always)]
49    pub unsafe fn cast<U>(self) -> BrandedBlock<'brand, U> {
50        BrandedBlock {
51            ptr: self.ptr.cast(),
52            _marker: self._marker,
53        }
54    }
55}
56
57impl<'brand, T: ?Sized> core::fmt::Debug for BrandedBlock<'brand, T> {
58    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
59        f.debug_tuple("BrandedBlock")
60            .field(&self.ptr.as_ptr())
61            .finish()
62    }
63}
64
65impl<'brand, T: ?Sized> core::fmt::Pointer for BrandedBlock<'brand, T> {
66    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
67        core::fmt::Pointer::fmt(&self.ptr.as_ptr(), f)
68    }
69}
70
71impl<'brand, T: ?Sized> PartialEq for BrandedBlock<'brand, T> {
72    #[inline(always)]
73    fn eq(&self, other: &Self) -> bool {
74        core::ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr())
75    }
76}
77impl<'brand, T: ?Sized> Eq for BrandedBlock<'brand, T> {}
78
79impl<'brand, T: ?Sized> PartialOrd for BrandedBlock<'brand, T> {
80    #[inline(always)]
81    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
82        Some(self.cmp(other))
83    }
84}
85impl<'brand, T: ?Sized> Ord for BrandedBlock<'brand, T> {
86    #[inline(always)]
87    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
88        self.ptr
89            .as_ptr()
90            .cast::<()>()
91            .cmp(&other.ptr.as_ptr().cast::<()>())
92    }
93}
94impl<'brand, T: ?Sized> core::hash::Hash for BrandedBlock<'brand, T> {
95    #[inline(always)]
96    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
97        self.ptr.hash(state);
98    }
99}