Skip to main content

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}