facet_reflect/partial/
heap_value.rs

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