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}