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