facet_reflect/partial/
heap_value.rs

1use crate::Peek;
2use crate::ReflectError;
3use crate::trace;
4use core::ptr::NonNull;
5use core::{alloc::Layout, marker::PhantomData};
6use facet_core::{Facet, PtrConst, PtrMut, Shape};
7
8/// A type-erased value stored on the heap
9///
10/// The `BORROW` const generic indicates whether this value may contain borrowed data:
11/// - `BORROW = true` (default): The value may contain references with lifetime `'facet`
12/// - `BORROW = false`: The value is fully owned and contains no borrowed data
13pub struct HeapValue<'facet, const BORROW: bool = true> {
14    pub(crate) guard: Option<Guard>,
15    pub(crate) shape: &'static Shape,
16    pub(crate) phantom: PhantomData<&'facet ()>,
17}
18
19impl<'facet, const BORROW: bool> Drop for HeapValue<'facet, BORROW> {
20    fn drop(&mut self) {
21        if let Some(guard) = self.guard.take() {
22            unsafe {
23                self.shape
24                    .call_drop_in_place(PtrMut::new(guard.ptr.as_ptr()));
25            }
26            drop(guard);
27        }
28    }
29}
30
31impl<'facet, const BORROW: bool> HeapValue<'facet, BORROW> {
32    /// Returns a peek that allows exploring the heap value.
33    pub fn peek(&self) -> Peek<'_, 'facet> {
34        unsafe {
35            Peek::unchecked_new(
36                PtrConst::new(self.guard.as_ref().unwrap().ptr.as_ptr()),
37                self.shape,
38            )
39        }
40    }
41
42    /// Returns the shape of this heap value.
43    pub fn shape(&self) -> &'static Shape {
44        self.shape
45    }
46
47    /// Turn this heapvalue into a concrete type
48    pub fn materialize<T: Facet<'facet>>(mut self) -> Result<T, ReflectError> {
49        trace!(
50            "HeapValue::materialize: Materializing heap value with shape {} to type {}",
51            self.shape,
52            T::SHAPE
53        );
54        if self.shape != T::SHAPE {
55            trace!(
56                "HeapValue::materialize: Shape mismatch! Expected {}, but heap value has {}",
57                T::SHAPE,
58                self.shape
59            );
60            return Err(ReflectError::WrongShape {
61                expected: self.shape,
62                actual: T::SHAPE,
63            });
64        }
65
66        trace!("HeapValue::materialize: Shapes match, proceeding with materialization");
67        let guard = self.guard.take().unwrap();
68        let data = PtrConst::new(guard.ptr.as_ptr());
69        let res = unsafe { data.read::<T>() };
70        drop(guard); // free memory (but don't drop in place)
71        trace!("HeapValue::materialize: Successfully materialized value");
72        Ok(res)
73    }
74}
75
76impl<'facet, const BORROW: bool> HeapValue<'facet, BORROW> {
77    /// Formats the value using its Display implementation, if available
78    pub fn fmt_display(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
79        let ptr = PtrConst::new(self.guard.as_ref().unwrap().ptr.as_ptr());
80        if let Some(result) = unsafe { self.shape.call_display(ptr, f) } {
81            return result;
82        }
83        write!(f, "⟨{}⟩", self.shape)
84    }
85
86    /// Formats the value using its Debug implementation, if available
87    pub fn fmt_debug(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88        let ptr = PtrConst::new(self.guard.as_ref().unwrap().ptr.as_ptr());
89        if let Some(result) = unsafe { self.shape.call_debug(ptr, f) } {
90            return result;
91        }
92        write!(f, "⟨{}⟩", self.shape)
93    }
94}
95
96impl<'facet, const BORROW: bool> core::fmt::Display for HeapValue<'facet, BORROW> {
97    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
98        self.fmt_display(f)
99    }
100}
101
102impl<'facet, const BORROW: bool> core::fmt::Debug for HeapValue<'facet, BORROW> {
103    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104        self.fmt_debug(f)
105    }
106}
107
108impl<'facet, const BORROW: bool> PartialEq for HeapValue<'facet, BORROW> {
109    fn eq(&self, other: &Self) -> bool {
110        if self.shape != other.shape {
111            return false;
112        }
113        let self_ptr = PtrConst::new(self.guard.as_ref().unwrap().ptr.as_ptr());
114        let other_ptr = PtrConst::new(other.guard.as_ref().unwrap().ptr.as_ptr());
115        unsafe { self.shape.call_partial_eq(self_ptr, other_ptr) }.unwrap_or(false)
116    }
117}
118
119impl<'facet, const BORROW: bool> PartialOrd for HeapValue<'facet, BORROW> {
120    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
121        if self.shape != other.shape {
122            return None;
123        }
124        let self_ptr = PtrConst::new(self.guard.as_ref().unwrap().ptr.as_ptr());
125        let other_ptr = PtrConst::new(other.guard.as_ref().unwrap().ptr.as_ptr());
126        unsafe { self.shape.call_partial_cmp(self_ptr, other_ptr) }.flatten()
127    }
128}
129
130/// A guard structure to manage memory allocation and deallocation.
131///
132/// This struct holds a raw pointer to the allocated memory and the layout
133/// information used for allocation. It's responsible for deallocating
134/// the memory when dropped, unless the allocation is managed elsewhere.
135pub struct Guard {
136    /// Raw pointer to the allocated memory.
137    pub(crate) ptr: NonNull<u8>,
138    /// Layout information of the allocated memory.
139    pub(crate) layout: Layout,
140    /// Whether this guard should deallocate the memory on drop.
141    /// Set to false when the allocation is managed elsewhere (e.g., `Arc<[T]>` from slice builder).
142    pub(crate) should_dealloc: bool,
143}
144
145impl Drop for Guard {
146    fn drop(&mut self) {
147        if self.should_dealloc && self.layout.size() != 0 {
148            trace!(
149                "Deallocating memory at ptr: {:p}, size: {}, align: {}",
150                self.ptr,
151                self.layout.size(),
152                self.layout.align()
153            );
154            // SAFETY: `ptr` has been allocated via the global allocator with the given layout
155            unsafe { alloc::alloc::dealloc(self.ptr.as_ptr(), self.layout) };
156        }
157    }
158}
159
160impl<'facet, const BORROW: bool> HeapValue<'facet, BORROW> {
161    /// Unsafely get a reference to the underlying value as type T.
162    ///
163    /// # Safety
164    ///
165    /// Caller must guarantee that the underlying value is of type T.
166    pub unsafe fn as_ref<T>(&self) -> &T {
167        unsafe { &*(self.guard.as_ref().unwrap().ptr.as_ptr() as *const T) }
168    }
169}