facet_reflect/partial/
heap_value.rs

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