facet_reflect/partial/partial_api/alloc.rs
1use super::*;
2use crate::typeplan::{TypePlan, TypePlanCore, TypePlanNode};
3use crate::{AllocError, AllocatedShape, partial::arena::Idx};
4use ::alloc::collections::BTreeMap;
5use ::alloc::sync::Arc;
6use ::alloc::vec;
7use core::marker::PhantomData;
8
9impl<'facet> Partial<'facet, true> {
10 /// Create a new borrowing Partial for the given type.
11 ///
12 /// This allocates memory for a value of type `T` and returns a `Partial`
13 /// that can be used to initialize it incrementally.
14 #[inline]
15 pub fn alloc<T: Facet<'facet>>() -> Result<Self, AllocError> {
16 TypePlan::<T>::build()?.partial()
17 }
18
19 /// Create a new borrowing Partial from a prebuilt TypePlanCore.
20 ///
21 /// This is useful when callers manage their own plan caching.
22 #[inline]
23 pub fn alloc_with_plan(plan: Arc<TypePlanCore>) -> Result<Self, AllocError> {
24 let root_id = plan.root_id();
25 create_partial_internal::<true>(plan, root_id)
26 }
27
28 /// Create a new borrowing Partial from a shape.
29 ///
30 /// This allocates memory for a value described by the shape and returns a `Partial`
31 /// that can be used to initialize it incrementally.
32 ///
33 /// # Safety
34 ///
35 /// The caller must ensure that the shape is valid and corresponds to a real type.
36 /// Using an incorrect or maliciously crafted shape can lead to undefined behavior
37 /// when materializing values.
38 #[inline]
39 pub unsafe fn alloc_shape(shape: &'static facet_core::Shape) -> Result<Self, AllocError> {
40 // SAFETY: caller guarantees shape is valid
41 let plan = unsafe { TypePlanCore::from_shape(shape)? };
42 let root_id = plan.root_id();
43 create_partial_internal::<true>(plan, root_id)
44 }
45}
46
47impl Partial<'static, false> {
48 /// Create a new owned Partial for the given type.
49 ///
50 /// This allocates memory for a value of type `T` and returns a `Partial`
51 /// that can be used to initialize it incrementally.
52 #[inline]
53 pub fn alloc_owned<T: Facet<'static>>() -> Result<Self, AllocError> {
54 TypePlan::<T>::build()?.partial_owned()
55 }
56
57 /// Create a new owned Partial from a prebuilt TypePlanCore.
58 ///
59 /// This is useful when callers manage their own plan caching.
60 #[inline]
61 pub fn alloc_owned_with_plan(plan: Arc<TypePlanCore>) -> Result<Self, AllocError> {
62 let root_id = plan.root_id();
63 create_partial_internal::<false>(plan, root_id)
64 }
65
66 /// Create a new owned Partial from a shape.
67 ///
68 /// This allocates memory for a value described by the shape and returns a `Partial`
69 /// that can be used to initialize it incrementally. The resulting value will be
70 /// fully owned ('static lifetime).
71 ///
72 /// # Safety
73 ///
74 /// The caller must ensure that the shape is valid and corresponds to a real type.
75 /// Using an incorrect or maliciously crafted shape can lead to undefined behavior
76 /// when materializing values.
77 #[inline]
78 pub unsafe fn alloc_shape_owned(shape: &'static facet_core::Shape) -> Result<Self, AllocError> {
79 // SAFETY: caller guarantees shape is valid
80 let plan = unsafe { TypePlanCore::from_shape(shape)? };
81 let root_id = plan.root_id();
82 create_partial_internal::<false>(plan, root_id)
83 }
84}
85
86////////////////////////////////////////////////////////////////////////////////////////////////////
87// Partial::from_raw - direct initialization from external memory
88////////////////////////////////////////////////////////////////////////////////////////////////////
89
90impl<'facet, const BORROW: bool> Partial<'facet, BORROW> {
91 /// Creates a new Partial pointing to caller-provided memory.
92 ///
93 /// This is a low-level API that lets the caller:
94 /// - Control where the value is allocated (stack, existing heap allocation, etc.)
95 /// - Avoid the heap allocation that [Partial::alloc] does
96 /// - Use MaybeUninit on the stack for the final value
97 ///
98 /// # Safety
99 ///
100 /// The caller MUST ensure:
101 /// - `data` points to properly aligned, writable memory of at least `shape.layout.size()` bytes
102 /// - The memory remains valid for the lifetime of this Partial and any value built from it
103 /// - The memory is not aliased by any other mutable references while the Partial exists
104 /// - If the Partial is dropped without calling `build()`, the caller handles the uninitialized memory
105 ///
106 /// # Example: Stack allocation with MaybeUninit
107 ///
108 /// ```ignore
109 /// use std::mem::MaybeUninit;
110 ///
111 /// // Stack-allocate space for the value
112 /// let mut slot = MaybeUninit::<MyStruct>::uninit();
113 /// let data = PtrUninit::new(slot.as_mut_ptr().cast());
114 ///
115 /// // Build the TypePlan (can be reused via Arc)
116 /// let plan = TypePlan::<MyStruct>::build()?;
117 ///
118 /// // Create Partial pointing to our stack memory
119 /// let partial = unsafe { Partial::from_raw(data, plan.core(), plan.core().root_id())? };
120 ///
121 /// // Initialize fields...
122 /// let partial = partial.set_field("name", "test")?.set_field("value", 42)?;
123 ///
124 /// // Build consumes the Partial but does NOT allocate - value is already in `slot`
125 /// let heap_value = partial.build()?;
126 ///
127 /// // SAFETY: We fully initialized the value, so we can assume_init
128 /// let value = unsafe { slot.assume_init() };
129 /// ```
130 ///
131 /// # Memory ownership
132 ///
133 /// The returned Partial has external ownership, which means:
134 /// - On successful `build()`: memory ownership transfers to the returned HeapValue
135 /// - On drop without `build()`: partially initialized memory is dropped in place,
136 /// but memory is NOT deallocated (caller must handle the memory)
137 pub unsafe fn from_raw(
138 data: PtrUninit,
139 plan: Arc<TypePlanCore>,
140 type_plan_id: crate::typeplan::NodeId,
141 ) -> Result<Self, AllocError> {
142 let shape = plan.node(type_plan_id).shape;
143 let layout = shape.layout.sized_layout().map_err(|_| AllocError {
144 shape,
145 operation: "type is not sized",
146 })?;
147 let allocated = AllocatedShape::new(shape, layout.size());
148
149 Ok(Self {
150 mode: FrameMode::Strict {
151 stack: vec![Frame::new(
152 data,
153 allocated,
154 FrameOwnership::External,
155 type_plan_id,
156 )],
157 },
158 state: PartialState::Active,
159 root_plan: plan,
160 _marker: PhantomData,
161 })
162 }
163
164 /// Creates a new Partial from caller-provided memory and a shape.
165 ///
166 /// This is a convenience API over [`Self::from_raw`] that resolves the root
167 /// type plan from `shape` and reuses the process-global TypePlan cache under
168 /// `std`.
169 ///
170 /// # Safety
171 ///
172 /// Same requirements as [`Self::from_raw`], plus the caller must ensure that
173 /// `shape` is valid and corresponds to a real type.
174 pub unsafe fn from_raw_with_shape(
175 data: PtrUninit,
176 shape: &'static facet_core::Shape,
177 ) -> Result<Self, AllocError> {
178 // SAFETY: caller guarantees shape is valid.
179 let plan = unsafe { TypePlanCore::from_shape(shape)? };
180 let root_id = plan.root_id();
181 // SAFETY: same as from_raw; this method forwards caller guarantees.
182 unsafe { Self::from_raw(data, plan, root_id) }
183 }
184}
185
186////////////////////////////////////////////////////////////////////////////////////////////////////
187// TypePlan methods for creating Partials
188////////////////////////////////////////////////////////////////////////////////////////////////////
189
190impl<'a, T: Facet<'a> + ?Sized> TypePlan<T> {
191 /// Create a borrowing Partial from this plan.
192 ///
193 /// The Partial borrows from this TypePlan and can be used to deserialize
194 /// values that may borrow from the input.
195 #[inline]
196 pub fn partial<'facet>(&self) -> Result<Partial<'facet, true>, AllocError> {
197 create_partial_internal::<true>(self.core(), self.core().root_id())
198 }
199
200 /// Create an owned Partial from this plan.
201 ///
202 /// The Partial borrows from this TypePlan. The deserialized value will be
203 /// fully owned ('static lifetime for borrowed data).
204 #[inline]
205 pub fn partial_owned(&self) -> Result<Partial<'static, false>, AllocError> {
206 create_partial_internal::<false>(self.core(), self.core().root_id())
207 }
208}
209
210/// Internal helper to create a Partial from plan and node.
211fn create_partial_internal<'facet, const BORROW: bool>(
212 plan: Arc<TypePlanCore>,
213 type_plan_id: Idx<TypePlanNode>,
214) -> Result<Partial<'facet, BORROW>, AllocError> {
215 let node = plan.node(type_plan_id);
216 let shape = node.shape;
217 let layout = shape.layout.sized_layout().map_err(|_| AllocError {
218 shape,
219 operation: "type is not sized",
220 })?;
221
222 // Allocate memory for the value
223 let data = unsafe { alloc_layout(layout)? };
224 let allocated = AllocatedShape::new(shape, layout.size());
225
226 Ok(Partial {
227 mode: FrameMode::Strict {
228 stack: vec![Frame::new(
229 data,
230 allocated,
231 FrameOwnership::Owned,
232 type_plan_id,
233 )],
234 },
235 state: PartialState::Active,
236 root_plan: plan,
237 _marker: PhantomData,
238 })
239}
240
241/// Allocate memory with the given layout.
242///
243/// # Safety
244/// The caller must ensure the returned pointer is used correctly.
245unsafe fn alloc_layout(layout: core::alloc::Layout) -> Result<PtrUninit, AllocError> {
246 use ::alloc::alloc::{alloc, handle_alloc_error};
247
248 if layout.size() == 0 {
249 // For ZSTs, use NonNull::dangling() aligned properly
250 let ptr: *mut u8 = core::ptr::NonNull::dangling().as_ptr();
251 return Ok(PtrUninit::new(ptr));
252 }
253
254 // SAFETY: Layout is guaranteed to be valid (non-zero size checked above)
255 let ptr = unsafe { alloc(layout) };
256 if ptr.is_null() {
257 handle_alloc_error(layout);
258 }
259
260 Ok(PtrUninit::new(ptr))
261}
262
263////////////////////////////////////////////////////////////////////////////////////////////////////
264// Deferred mode entry points
265////////////////////////////////////////////////////////////////////////////////////////////////////
266
267impl<'facet, const BORROW: bool> Partial<'facet, BORROW> {
268 /// Enter deferred mode for the current frame.
269 ///
270 /// In deferred mode, frames can be stored when popped via `end()` and restored
271 /// when re-entered via `begin_field()`. This enables formats like TOML where
272 /// keys for the same table may appear non-contiguously.
273 ///
274 /// # Returns
275 ///
276 /// A new `Partial` in deferred mode. The original `Partial` is consumed.
277 ///
278 /// # Deferred mode behavior
279 ///
280 /// - When `end()` is called on a frame that isn't fully initialized, the frame
281 /// is stored (keyed by its path) instead of being validated.
282 /// - When `begin_field()` is called for a stored frame, it's restored instead
283 /// of creating a new one.
284 /// - Call `finish_deferred()` when done to validate all stored frames and
285 /// exit deferred mode.
286 pub fn enter_deferred(self) -> Self {
287 use core::mem::ManuallyDrop;
288
289 let start_depth = self.frames().len();
290 // Prevent Drop from running on self, we're taking ownership of its contents
291 let mut this = ManuallyDrop::new(self);
292 // Take the stack from the mode
293 let stack = core::mem::take(this.frames_mut());
294 let mut root_plan = Arc::new(TypePlanCore::empty());
295 core::mem::swap(&mut root_plan, &mut this.root_plan);
296
297 Self {
298 mode: FrameMode::Deferred {
299 stack,
300 start_depth,
301 stored_frames: BTreeMap::new(),
302 },
303 state: this.state,
304 root_plan,
305 _marker: PhantomData,
306 }
307 }
308}