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}