Skip to main content

facet_reflect/partial/partial_api/
alloc.rs

1use super::*;
2use crate::AllocatedShape;
3
4////////////////////////////////////////////////////////////////////////////////////////////////////
5// Allocation, constructors etc.
6////////////////////////////////////////////////////////////////////////////////////////////////////
7
8/// Allocate a Partial that can borrow from input with lifetime 'facet.
9/// This is the default mode - use this when deserializing from a buffer that outlives the result.
10impl<'facet> Partial<'facet, true> {
11    /// Allocates a new [Partial] instance on the heap, with the given shape and type.
12    ///
13    /// This creates a borrowing Partial that can hold references with lifetime 'facet.
14    pub fn alloc<T>() -> Result<Self, ReflectError>
15    where
16        T: Facet<'facet> + ?Sized,
17    {
18        // SAFETY: T::SHAPE comes from the Facet implementation for T,
19        // which is an unsafe trait requiring accurate shape descriptions.
20        unsafe { Self::alloc_shape(T::SHAPE) }
21    }
22
23    /// Allocates a new [Partial] instance on the heap, with the given shape.
24    ///
25    /// This creates a borrowing Partial that can hold references with lifetime 'facet.
26    ///
27    /// # Safety
28    ///
29    /// The caller must ensure that `shape` accurately describes the memory layout
30    /// and invariants of any type `T` that will be materialized from this `Partial`.
31    ///
32    /// In particular:
33    /// - `shape.id` must match `TypeId::of::<T>()`
34    /// - `shape.layout` must match `Layout::of::<T>()`
35    /// - `shape.ty` and `shape.def` must accurately describe T's structure
36    /// - All vtable operations must be valid for type T
37    ///
38    /// Violating these requirements may cause undefined behavior when accessing
39    /// fields, materializing values, or calling vtable methods.
40    ///
41    /// **Safe alternative**: Use [`Partial::alloc::<T>()`](Self::alloc) which gets the shape
42    /// from `T::SHAPE` (guaranteed safe by `unsafe impl Facet for T`).
43    pub unsafe fn alloc_shape(shape: &'static Shape) -> Result<Self, ReflectError> {
44        alloc_shape_inner(shape)
45    }
46}
47
48/// Allocate a Partial that cannot borrow - all data must be owned.
49/// Use this when deserializing from a temporary buffer (e.g., HTTP request body).
50impl Partial<'static, false> {
51    /// Allocates a new [Partial] instance on the heap, with the given shape and type.
52    ///
53    /// This creates an owned Partial that cannot hold borrowed references.
54    /// Use this when the input buffer is temporary and won't outlive the result.
55    pub fn alloc_owned<T>() -> Result<Self, ReflectError>
56    where
57        T: Facet<'static> + ?Sized,
58    {
59        // SAFETY: T::SHAPE comes from the Facet implementation for T,
60        // which is an unsafe trait requiring accurate shape descriptions.
61        unsafe { Self::alloc_shape_owned(T::SHAPE) }
62    }
63
64    /// Allocates a new [Partial] instance on the heap, with the given shape.
65    ///
66    /// This creates an owned Partial that cannot hold borrowed references.
67    ///
68    /// # Safety
69    ///
70    /// The caller must ensure that `shape` accurately describes the memory layout
71    /// and invariants of any type `T` that will be materialized from this `Partial`.
72    ///
73    /// In particular:
74    /// - `shape.id` must match `TypeId::of::<T>()`
75    /// - `shape.layout` must match `Layout::of::<T>()`
76    /// - `shape.ty` and `shape.def` must accurately describe T's structure
77    /// - All vtable operations must be valid for type T
78    ///
79    /// Violating these requirements may cause undefined behavior when accessing
80    /// fields, materializing values, or calling vtable methods.
81    ///
82    /// **Safe alternative**: Use [`Partial::alloc_owned::<T>()`](Self::alloc_owned) which gets the shape
83    /// from `T::SHAPE` (guaranteed safe by `unsafe impl Facet for T`).
84    pub unsafe fn alloc_shape_owned(shape: &'static Shape) -> Result<Self, ReflectError> {
85        alloc_shape_inner(shape)
86    }
87}
88
89/// Create a Partial that writes into externally-owned memory (e.g., caller's stack).
90/// This enables stack-friendly deserialization without heap allocation.
91impl<'facet, const BORROW: bool> Partial<'facet, BORROW> {
92    /// Creates a [Partial] that writes into caller-provided memory.
93    ///
94    /// This is useful for stack-friendly deserialization where you want to avoid
95    /// heap allocation. The caller provides a `MaybeUninit<T>` and this Partial
96    /// will deserialize directly into that memory.
97    ///
98    /// After successful deserialization, call [`finish_in_place`](Self::finish_in_place)
99    /// to validate the value is fully initialized. On success, the caller can safely
100    /// call `MaybeUninit::assume_init()`.
101    ///
102    /// # Safety
103    ///
104    /// The caller must ensure:
105    /// - `ptr` points to valid, properly aligned memory for the type described by `shape`
106    /// - The memory has at least `shape.layout.size()` bytes available
107    /// - The memory is not accessed (read or written) through any other pointer while
108    ///   the Partial exists, except through this Partial's methods
109    /// - If deserialization fails (returns Err) or panics, the memory must not be
110    ///   assumed to contain a valid value - it may be partially initialized
111    /// - The memory must remain valid for the lifetime of the Partial
112    ///
113    /// # Example
114    ///
115    /// ```ignore
116    /// use std::mem::MaybeUninit;
117    /// use facet_core::{Facet, PtrUninit};
118    /// use facet_reflect::Partial;
119    ///
120    /// let mut slot = MaybeUninit::<MyStruct>::uninit();
121    /// let ptr = PtrUninit::new(slot.as_mut_ptr().cast());
122    ///
123    /// let partial = unsafe { Partial::from_raw(ptr, MyStruct::SHAPE)? };
124    /// // ... deserialize into partial ...
125    /// partial.finish_in_place()?;
126    ///
127    /// // Now safe to assume initialized
128    /// let value = unsafe { slot.assume_init() };
129    /// ```
130    pub unsafe fn from_raw(ptr: PtrUninit, shape: &'static Shape) -> Result<Self, ReflectError> {
131        crate::trace!(
132            "from_raw({:p}, {:?}), with layout {:?}",
133            ptr.as_mut_byte_ptr(),
134            shape,
135            shape.layout.sized_layout()
136        );
137
138        // Verify the shape is sized
139        let allocated_size = shape
140            .layout
141            .sized_layout()
142            .map_err(|_| ReflectError::Unsized {
143                shape,
144                operation: "from_raw",
145            })?
146            .size();
147
148        // Preallocate a couple of frames for nested structures
149        let mut stack = Vec::with_capacity(4);
150        stack.push(Frame::new(
151            ptr,
152            AllocatedShape::new(shape, allocated_size),
153            FrameOwnership::External,
154        ));
155
156        Ok(Partial {
157            mode: FrameMode::Strict { stack },
158            state: PartialState::Active,
159            invariant: PhantomData,
160        })
161    }
162}
163
164fn alloc_shape_inner<'facet, const BORROW: bool>(
165    shape: &'static Shape,
166) -> Result<Partial<'facet, BORROW>, ReflectError> {
167    crate::trace!(
168        "alloc_shape({:?}), with layout {:?}",
169        shape,
170        shape.layout.sized_layout()
171    );
172
173    let data = shape.allocate().map_err(|_| ReflectError::Unsized {
174        shape,
175        operation: "alloc_shape",
176    })?;
177
178    // Get the actual allocated size
179    let allocated_size = shape.layout.sized_layout().expect("must be sized").size();
180
181    // Preallocate a couple of frames. The cost of allocating 4 frames is
182    // basically identical to allocating 1 frame, so for every type that
183    // has at least 1 level of nesting, this saves at least one guaranteed reallocation.
184    let mut stack = Vec::with_capacity(4);
185    stack.push(Frame::new(
186        data,
187        AllocatedShape::new(shape, allocated_size),
188        FrameOwnership::Owned,
189    ));
190
191    Ok(Partial {
192        mode: FrameMode::Strict { stack },
193        state: PartialState::Active,
194        invariant: PhantomData,
195    })
196}