Skip to main content

cel_runtime/
owned_value.rs

1//! Owns an erased whole value together with its frozen ownership descriptor.
2//!
3//! Checked host conversion validates the complete representation before moving:
4//!
5//! ```
6//! use cel_runtime::owned_value::OwnedValue;
7//! use cel_runtime::value_type::{ArrayOf, Leaf};
8//! use cel_runtime::Array;
9//! let array = Array::from_vec(vec![String::from("owned")]);
10//! let pointer = array.as_slice().as_ptr();
11//! let owner = OwnedValue::new::<ArrayOf<Leaf<String>>>(array);
12//! let array = owner.try_into_typed::<ArrayOf<Leaf<String>>>().unwrap();
13//! assert_eq!(array.as_slice().as_ptr(), pointer);
14//! ```
15
16use std::alloc::{Layout, alloc, dealloc, handle_alloc_error};
17use std::mem::MaybeUninit;
18use std::ptr::{self, NonNull};
19
20use crate::array::RawArray;
21use crate::value_type::{AssociatedType, ValueKind, ValueRepr, ValueType};
22
23#[cfg(test)]
24thread_local! {
25    /// Counts nonzero canonical owner allocations on the current test thread.
26    pub(crate) static ALLOCATIONS: std::cell::Cell<usize> = const {
27        std::cell::Cell::new(0)
28    };
29}
30
31/// Owns one aligned allocation, including an aligned dangling pointer for ZSTs.
32///
33/// This owner deallocates storage but does not destroy its contents.
34pub(crate) struct Allocation {
35    pub(crate) pointer: NonNull<u8>,
36    pub(crate) layout: Layout,
37}
38
39impl Allocation {
40    /// Allocates uninitialized storage or an aligned dangling pointer for a ZST.
41    pub(crate) fn new(layout: Layout) -> Self {
42        let pointer = if layout.size() == 0 {
43            NonNull::new(ptr::without_provenance_mut(layout.align())).unwrap()
44        } else {
45            #[cfg(test)]
46            ALLOCATIONS.with(|count| count.set(count.get() + 1));
47            // SAFETY: Layout is nonzero and valid; allocation failure aborts.
48            NonNull::new(unsafe { alloc(layout) }).unwrap_or_else(|| handle_alloc_error(layout))
49        };
50        Self { pointer, layout }
51    }
52}
53
54impl Drop for Allocation {
55    /// Releases nonzero storage after its live contents have been destroyed.
56    fn drop(&mut self) {
57        if self.layout.size() != 0 {
58            // SAFETY: This owner holds the unique allocation with this exact layout.
59            unsafe { dealloc(self.pointer.as_ptr(), self.layout) };
60        }
61    }
62}
63
64/// Owns exactly one live erased value and its complete immutable descriptor.
65///
66/// No clone, equality, formatting, or thread-safety bounds are imposed on the
67/// stored value. Consuming errors destroy the original value exactly once;
68/// borrowed capability errors leave the original value unchanged.
69///
70/// - Complexity: Destruction visits every contained value in reverse logical
71///   field/element order, using O(d) call space for nesting depth d.
72///
73/// # Examples
74///
75/// ```
76/// use cel_runtime::{owned_value::OwnedValue, value_type::Leaf};
77/// let owner = OwnedValue::new::<Leaf<String>>(String::from("hello"));
78/// assert_eq!(owner.try_into_typed::<Leaf<String>>().unwrap(), "hello");
79/// ```
80pub struct OwnedValue {
81    allocation: Allocation,
82    value_type: ValueType,
83    live: bool,
84}
85
86/// Binds recursive debug capabilities independently of a value's ownership.
87///
88/// The formatter never clones or consumes a value. Each checked formatting
89/// boundary validates the complete shape before invoking any leaf hook.
90///
91/// # Examples
92/// ```
93/// use cel_runtime::{Leaf, OwnedValue, ValueType};
94/// let value = OwnedValue::new::<Leaf<i32>>(7)
95///     .with_value_type(ValueType::leaf::<i32>().with_debug::<Leaf<i32>>().unwrap()).unwrap();
96/// let formatter = value.prepare_debug().unwrap();
97/// assert_eq!(formatter.format(&value).unwrap(), "7");
98/// ```
99#[derive(Clone)]
100pub struct ValueFormatter {
101    value_type: ValueType,
102}
103
104impl ValueFormatter {
105    /// Formats a matching live value using this previously prepared debug capability.
106    ///
107    /// # Errors
108    /// Rejects a complete shape mismatch before invoking any formatter.
109    /// - Complexity: O(n + v + s) for n type nodes, v values and s output bytes,
110    ///   excluding leaf formatting costs.
111    ///
112    /// # Examples
113    /// See [`OwnedValue::prepare_debug`].
114    pub fn format(&self, value: &OwnedValue) -> anyhow::Result<String> {
115        anyhow::ensure!(
116            self.value_type.same_shape(value.value_type()),
117            "formatter value shape mismatch"
118        );
119        let mut output = String::new();
120        // SAFETY: Preparation checks every debug hook; the boundary checks full shape.
121        unsafe { debug_raw(&self.value_type, value.as_ptr(), &mut output) };
122        Ok(output)
123    }
124}
125
126impl OwnedValue {
127    /// Prepares an independent reusable formatter without consuming or formatting this value.
128    ///
129    /// # Errors
130    /// Rejects absent debug capability at any recursive node.
131    ///
132    /// # Examples
133    /// ```
134    /// use cel_runtime::{Leaf, OwnedValue, ValueType};
135    /// let value = OwnedValue::new::<Leaf<i32>>(7)
136    ///     .with_value_type(ValueType::leaf::<i32>().with_debug::<Leaf<i32>>().unwrap()).unwrap();
137    /// assert_eq!(value.prepare_debug().unwrap().format(&value).unwrap(), "7");
138    /// ```
139    pub fn prepare_debug(&self) -> anyhow::Result<ValueFormatter> {
140        anyhow::ensure!(self.value_type.can_debug(), "debug capability unavailable");
141        Ok(ValueFormatter {
142            value_type: self.value_type.clone(),
143        })
144    }
145
146    /// Moves one bound array element and destroys every unselected element.
147    ///
148    /// # Safety
149    /// This owner must have the canonical array descriptor bound by the caller.
150    /// # Errors
151    /// Returns an error for an out-of-bounds index, destroying the consumed array.
152    /// - Complexity: O(n) sibling destruction for n array elements plus one raw move.
153    pub(crate) unsafe fn take_array_element(mut self, index: usize) -> anyhow::Result<Self> {
154        let ValueKind::Array(element) = self.value_type.kind() else {
155            unreachable!("validated canonical array projection")
156        };
157        let raw = unsafe { &*self.as_ptr().cast::<RawArray>() };
158        anyhow::ensure!(
159            index < raw.len,
160            "array index {index} is out of bounds for length {}",
161            raw.len
162        );
163        let allocation = Allocation::new(element.layout());
164        unsafe {
165            std::ptr::copy_nonoverlapping(
166                raw.ptr.as_ptr().cast::<u8>().add(index * element.size()),
167                allocation.pointer.as_ptr(),
168                element.size(),
169            );
170        }
171        let result = unsafe { Self::from_allocation(allocation, element.clone()) };
172        let _payload = Allocation {
173            pointer: raw.ptr.cast(),
174            layout: array_layout(element, raw.capacity),
175        };
176        /// Destroys the still-owned array elements during normal and unwinding cleanup.
177        struct Siblings<'a> {
178            base: *mut u8,
179            element: &'a ValueType,
180            remaining: usize,
181            skip: usize,
182        }
183        impl Siblings<'_> {
184            /// Destroys unselected elements in reverse index order.
185            fn destroy(&mut self) {
186                while self.remaining != 0 {
187                    self.remaining -= 1;
188                    if self.remaining != self.skip {
189                        unsafe {
190                            drop_raw(
191                                self.element,
192                                self.base.add(self.remaining * self.element.size()),
193                            )
194                        };
195                    }
196                }
197            }
198        }
199        impl Drop for Siblings<'_> {
200            /// Continues remaining element destruction after a sibling panics.
201            fn drop(&mut self) {
202                self.destroy();
203            }
204        }
205        let mut siblings = Siblings {
206            base: raw.ptr.as_ptr().cast(),
207            element,
208            remaining: raw.len,
209            skip: index,
210        };
211        self.live = false;
212        // The element allocation is handled below; release the header's metadata separately.
213        drop(
214            unsafe { &mut *self.allocation.pointer.as_ptr().cast::<RawArray>() }
215                .element_type
216                .take(),
217        );
218        siblings.destroy();
219        Ok(result)
220    }
221
222    /// Splits a canonical tuple into independent source-ordered field owners.
223    ///
224    /// Nested arrays retain their payload allocations; no leaf is cloned.
225    /// # Errors
226    /// Rejects a nontuple owner without transferring any field ownership.
227    /// - Complexity: O(n + s) field allocations and raw moves for n fields and s bytes.
228    ///
229    /// # Examples
230    /// ```
231    /// use cel_runtime::{OwnedValue, Cons, Leaf, Nil, IntoCStackList};
232    /// let tuple = OwnedValue::new::<Cons<Leaf<i32>, Nil>>((7,).into_c_stack_list());
233    /// let fields = tuple.into_fields().unwrap();
234    /// assert_eq!(fields.len(), 1);
235    /// ```
236    pub fn into_fields(mut self) -> anyhow::Result<Vec<Self>> {
237        let crate::value_type::ValueKind::Tuple(fields) = self.value_type.kind() else {
238            anyhow::bail!("canonical tuple owner required");
239        };
240        let destinations: Vec<_> = fields
241            .iter()
242            .map(|field| {
243                (
244                    Allocation::new(field.value_type.layout()),
245                    field.offset,
246                    field.value_type.clone(),
247                )
248            })
249            .collect();
250        let mut result = Vec::with_capacity(destinations.len());
251        let source = self.as_ptr();
252        // Every allocation precedes the first transfer; no callback, allocation,
253        // or fallible operation follows disarming this enclosing owner.
254        self.live = false;
255        for (allocation, offset, value_type) in destinations {
256            unsafe {
257                std::ptr::copy_nonoverlapping(
258                    source.add(offset),
259                    allocation.pointer.as_ptr(),
260                    value_type.size(),
261                );
262                result.push(Self::from_allocation(allocation, value_type));
263            }
264        }
265        Ok(result)
266    }
267
268    /// Moves a previously validated tuple field and destroys the remaining fields.
269    ///
270    /// # Safety
271    /// This owner must be a canonical tuple and `index` must be in range.
272    /// The caller must bind the returned field's full descriptor before execution.
273    /// - Complexity: O(n) destruction calls for n fields plus one field move.
274    pub(crate) unsafe fn take_field(mut self, index: usize) -> Self {
275        let crate::value_type::ValueKind::Tuple(fields) = self.value_type.kind() else {
276            unreachable!("validated tuple projection")
277        };
278        let selected = &fields[index];
279        let allocation = Allocation::new(selected.value_type.layout());
280        unsafe {
281            std::ptr::copy_nonoverlapping(
282                self.as_ptr().add(selected.offset),
283                allocation.pointer.as_ptr(),
284                selected.value_type.size(),
285            );
286        }
287        let result = unsafe { Self::from_allocation(allocation, selected.value_type.clone()) };
288        self.live = false;
289        /// Continues sibling destruction if one sibling destructor unwinds.
290        struct Siblings<'a> {
291            pointer: *mut u8,
292            fields: &'a [crate::value_type::AssociatedType],
293            remaining: usize,
294            skip: usize,
295        }
296        impl Siblings<'_> {
297            /// Destroys unselected fields in reverse source order.
298            fn destroy(&mut self) {
299                while self.remaining != 0 {
300                    self.remaining -= 1;
301                    if self.remaining != self.skip {
302                        let field = &self.fields[self.remaining];
303                        unsafe { drop_raw(&field.value_type, self.pointer.add(field.offset)) };
304                    }
305                }
306            }
307        }
308        impl Drop for Siblings<'_> {
309            /// Continues cleanup after the first destructor panic.
310            fn drop(&mut self) {
311                self.destroy();
312            }
313        }
314        let mut siblings = Siblings {
315            pointer: self.allocation.pointer.as_ptr(),
316            fields,
317            remaining: fields.len(),
318            skip: index,
319        };
320        siblings.destroy();
321        result
322    }
323    /// Adopts an allocation containing one fully initialized canonical value.
324    ///
325    /// # Safety
326    ///
327    /// The allocation must uniquely own one live value of exactly `value_type`,
328    /// including all nested allocations, with no other destruction responsibility.
329    pub(crate) unsafe fn from_allocation(allocation: Allocation, value_type: ValueType) -> Self {
330        debug_assert_eq!(allocation.layout, value_type.layout());
331        Self {
332            allocation,
333            value_type,
334            live: true,
335        }
336    }
337
338    /// Takes ownership using the sealed witness's canonical descriptor.
339    ///
340    /// Moving an array copies only its header and preserves its element allocation.
341    ///
342    /// - Complexity: O(n) descriptor construction plus one concrete value move,
343    ///   for n witness nodes; no contained values are cloned.
344    ///
345    /// # Examples
346    ///
347    /// ```
348    /// use cel_runtime::{owned_value::OwnedValue, value_type::Leaf};
349    /// let _ = OwnedValue::new::<Leaf<String>>(String::from("owned"));
350    /// ```
351    pub fn new<R: ValueRepr>(value: R::Value) -> Self {
352        let value_type = R::value_type();
353        debug_assert_eq!(value_type.layout(), Layout::new::<R::Value>());
354        let allocation = Allocation::new(value_type.layout());
355        // SAFETY: The sealed witness supplies precisely R::Value's layout.
356        unsafe { allocation.pointer.as_ptr().cast::<R::Value>().write(value) };
357        Self {
358            allocation,
359            value_type,
360            live: true,
361        }
362    }
363
364    /// Borrows this whole value's frozen descriptor.
365    ///
366    /// # Examples
367    ///
368    /// ```
369    /// use cel_runtime::{owned_value::OwnedValue, value_type::{Leaf, ValueType}};
370    /// let owner = OwnedValue::new::<Leaf<i32>>(1);
371    /// assert!(owner.value_type().same_shape(&ValueType::leaf::<i32>()));
372    /// ```
373    pub fn value_type(&self) -> &ValueType {
374        &self.value_type
375    }
376
377    /// Attaches independently configured capabilities after complete shape validation.
378    ///
379    /// The live representation and allocation do not change.
380    ///
381    /// - Complexity: O(n) comparison time and O(d) call space for n type nodes
382    ///   and nesting depth d.
383    ///
384    /// # Errors
385    ///
386    /// Returns an error if any identity or physical layout differs. On error the
387    /// consumed original is destroyed exactly once using its original descriptor;
388    /// none of the supplied descriptor's value hooks execute.
389    ///
390    /// # Examples
391    ///
392    /// ```
393    /// use cel_runtime::{owned_value::OwnedValue, value_type::{Leaf, ValueType}};
394    /// let owner = OwnedValue::new::<Leaf<String>>(String::from("clone me"))
395    ///     .with_value_type(ValueType::cloneable_leaf::<String>()).unwrap();
396    /// assert!(owner.try_clone().is_ok());
397    /// ```
398    pub fn with_value_type(mut self, value_type: ValueType) -> anyhow::Result<Self> {
399        anyhow::ensure!(
400            self.value_type.same_shape(&value_type),
401            "value representation mismatch"
402        );
403        self.value_type = value_type;
404        Ok(self)
405    }
406
407    /// Moves into the concrete witness type after checking the entire descriptor.
408    ///
409    /// - Postcondition: Success preserves contained allocations and transfers
410    ///   ownership without cloning or running value destructors.
411    /// - Complexity: O(n) witness construction and comparison for n type nodes,
412    ///   plus a move of the concrete representation.
413    ///
414    /// # Errors
415    ///
416    /// Returns an error on any identity or physical layout mismatch. The consumed
417    /// original is then destroyed exactly once; no unrelated typed access occurs.
418    ///
419    /// # Examples
420    ///
421    /// ```
422    /// use cel_runtime::{owned_value::OwnedValue, value_type::Leaf};
423    /// assert_eq!(OwnedValue::new::<Leaf<i32>>(7)
424    ///     .try_into_typed::<Leaf<i32>>().unwrap(), 7);
425    /// ```
426    pub fn try_into_typed<R: ValueRepr>(mut self) -> anyhow::Result<R::Value> {
427        self.value_type.validate::<R>()?;
428        let mut destination = MaybeUninit::<R::Value>::uninit();
429        // SAFETY: Complete validation established the exact destination type.
430        unsafe {
431            self.move_into(destination.as_mut_ptr().cast());
432            Ok(destination.assume_init())
433        }
434    }
435
436    /// Moves into an exact previously bound concrete representation.
437    ///
438    /// # Safety
439    /// This owner's complete descriptor must match `R`, not merely its carrier.
440    /// - Complexity: O(s) for s representation bytes.
441    ///
442    /// # Examples
443    /// Prefer [`Self::try_into_typed`] at checked host boundaries.
444    pub unsafe fn into_typed_bound<R: ValueRepr>(mut self) -> R::Value {
445        let mut destination = MaybeUninit::<R::Value>::uninit();
446        unsafe {
447            self.move_into(destination.as_mut_ptr().cast());
448            destination.assume_init()
449        }
450    }
451
452    /// Clones through the frozen plan without consuming or mutating this value.
453    ///
454    /// If a leaf clone panics, every completed cloned prefix is destroyed and
455    /// its storage released; this original remains live.
456    ///
457    /// - Complexity: O(v) traversal time, O(d) call space, and storage for the
458    ///   cloned values, excluding leaf clone costs; v counts contained values.
459    ///
460    /// # Errors
461    ///
462    /// Returns an error before cloning anything if cloning is unavailable.
463    ///
464    /// # Examples
465    ///
466    /// ```
467    /// use cel_runtime::{owned_value::OwnedValue, value_type::{Leaf, ValueType}};
468    /// let original = OwnedValue::new::<Leaf<String>>(String::from("leaf"))
469    ///     .with_value_type(ValueType::cloneable_leaf::<String>()).unwrap();
470    /// assert_eq!(original.try_clone().unwrap()
471    ///     .try_into_typed::<Leaf<String>>().unwrap(), "leaf");
472    /// ```
473    pub fn try_clone(&self) -> anyhow::Result<Self> {
474        anyhow::ensure!(self.value_type.can_clone(), "clone capability unavailable");
475        let mut cloned = Self {
476            allocation: Allocation::new(self.value_type.layout()),
477            value_type: self.value_type.clone(),
478            live: false,
479        };
480        // SAFETY: The frozen plan is cloneable and both allocations match it.
481        unsafe {
482            clone_raw(
483                &self.value_type,
484                self.as_ptr(),
485                cloned.allocation.pointer.as_ptr(),
486            )
487        };
488        cloned.live = true;
489        Ok(cloned)
490    }
491
492    /// Compares same-shaped values through this owner's independent equality plan.
493    ///
494    /// The other owner's capability configuration need not match this one's.
495    ///
496    /// - Complexity: O(n + v) worst-case traversal time and O(d) call space,
497    ///   excluding leaf equality costs, for n type nodes and v contained values.
498    ///
499    /// # Errors
500    ///
501    /// Returns an error without invoking equality if shapes differ or this
502    /// owner's equality capability is absent. Neither owner is consumed.
503    ///
504    /// # Examples
505    ///
506    /// ```
507    /// use cel_runtime::{owned_value::OwnedValue, value_type::{Leaf, ValueType}};
508    /// let a = OwnedValue::new::<Leaf<i32>>(1)
509    ///     .with_value_type(ValueType::leaf::<i32>().with_equality::<Leaf<i32>>().unwrap())
510    ///     .unwrap();
511    /// assert!(a.try_eq(&OwnedValue::new::<Leaf<i32>>(1)).unwrap());
512    /// ```
513    pub fn try_eq(&self, other: &Self) -> anyhow::Result<bool> {
514        anyhow::ensure!(
515            self.value_type.same_shape(&other.value_type),
516            "value representation mismatch"
517        );
518        anyhow::ensure!(
519            self.value_type.can_compare(),
520            "equality capability unavailable"
521        );
522        // SAFETY: Both live values match this prevalidated comparable plan.
523        Ok(unsafe { equal_raw(&self.value_type, self.as_ptr(), other.as_ptr()) })
524    }
525
526    /// Formats through the independent debug plan without consuming the value.
527    ///
528    /// Structural arrays use brackets; tuples use parentheses in source order.
529    ///
530    /// - Complexity: O(v + s) time, O(d) call space, and O(s) output storage,
531    ///   excluding leaf formatting costs, for v values and s output bytes.
532    ///
533    /// # Errors
534    ///
535    /// Returns an error before formatting anything if debug support is absent.
536    ///
537    /// # Examples
538    ///
539    /// ```
540    /// use cel_runtime::{owned_value::OwnedValue, value_type::{Leaf, ValueType}};
541    /// let owner = OwnedValue::new::<Leaf<i32>>(7)
542    ///     .with_value_type(ValueType::leaf::<i32>().with_debug::<Leaf<i32>>().unwrap())
543    ///     .unwrap();
544    /// assert_eq!(owner.try_debug().unwrap(), "7");
545    /// ```
546    pub fn try_debug(&self) -> anyhow::Result<String> {
547        anyhow::ensure!(self.value_type.can_debug(), "debug capability unavailable");
548        let mut output = String::new();
549        // SAFETY: This live value matches its prevalidated debug plan.
550        unsafe { debug_raw(&self.value_type, self.as_ptr(), &mut output) };
551        Ok(output)
552    }
553
554    /// Borrows the raw representation for trusted, already validated execution.
555    ///
556    /// The pointer remains live only while this owner remains borrowed.
557    ///
558    /// # Examples
559    /// ```
560    /// let owner = cel_runtime::OwnedValue::new::<cel_runtime::Leaf<i32>>(1);
561    /// assert!(!owner.as_ptr().is_null());
562    /// ```
563    pub fn as_ptr(&self) -> *const u8 {
564        debug_assert!(self.live);
565        self.allocation.pointer.as_ptr()
566    }
567
568    /// Compares values whose complete identity has already been bound by a host.
569    ///
570    /// # Safety
571    /// Both owners must have exactly the same complete shape, and this owner's
572    /// descriptor must support equality.
573    /// - Complexity: O(v) for v contained values, excluding leaf comparison costs.
574    ///
575    /// # Examples
576    /// Use [`Self::try_eq`] at checked host boundaries.
577    pub unsafe fn eq_bound(&self, other: &Self) -> bool {
578        unsafe { equal_raw(&self.value_type, self.as_ptr(), other.as_ptr()) }
579    }
580
581    /// Transfers the representation without any runtime identity checks.
582    ///
583    /// - Postcondition: This owner no longer destroys the transferred value.
584    /// - Complexity: O(s) bytes copied for representation size s.
585    ///
586    /// # Safety
587    ///
588    /// The caller must have prevalidated the full identity and layout. The
589    /// destination must be aligned, disjoint uninitialized storage for exactly
590    /// this representation, and must assume unique destruction responsibility.
591    pub(crate) unsafe fn move_into(&mut self, destination: *mut u8) {
592        debug_assert!(self.live);
593        // SAFETY: The caller guarantees disjoint storage of the validated size.
594        // Byte copying preserves potentially uninitialized padding without reading it.
595        unsafe {
596            ptr::copy_nonoverlapping(self.as_ptr(), destination, self.value_type.layout().size());
597        }
598        self.live = false;
599    }
600}
601
602impl Drop for OwnedValue {
603    /// Destroys the live value before its allocation is automatically released.
604    fn drop(&mut self) {
605        if self.live {
606            self.live = false;
607            // SAFETY: This owner uniquely owns one initialized value of this plan.
608            unsafe { drop_raw(&self.value_type, self.allocation.pointer.as_ptr()) };
609        }
610    }
611}
612
613/// Describes initialized children without per-element descriptors or allocations.
614pub(crate) enum Children<'a> {
615    /// Source-ordered tuple fields at canonical offsets.
616    Fields(&'a [AssociatedType]),
617    /// Homogeneous elements at their concrete stride.
618    Elements(&'a ValueType),
619}
620
621impl Children<'_> {
622    /// Locates one initialized child relative to the aggregate base.
623    fn child(&self, index: usize) -> (&ValueType, usize) {
624        match self {
625            Self::Fields(fields) => (&fields[index].value_type, fields[index].offset),
626            Self::Elements(element) => (element, index * element.layout().size()),
627        }
628    }
629}
630
631/// Destroys a live child prefix backwards, including remaining children on unwind.
632pub(crate) struct Prefix<'a> {
633    pub(crate) base: *mut u8,
634    pub(crate) children: Children<'a>,
635    pub(crate) initialized: usize,
636}
637
638impl Prefix<'_> {
639    /// Destroys remaining initialized children in reverse logical order.
640    ///
641    /// Decrements the live prefix before each destructor so this guard's own
642    /// destructor resumes at the next child if the first destructor unwinds.
643    ///
644    /// - Complexity: O(n) child destructions and O(1) local space.
645    ///
646    /// # Safety
647    ///
648    /// The prefix must uniquely own exactly `initialized` live children matching
649    /// `children`, at their validated offsets from `base`.
650    pub(crate) unsafe fn destroy(&mut self) {
651        while self.initialized != 0 {
652            self.initialized -= 1;
653            let (value_type, offset) = self.children.child(self.initialized);
654            unsafe { drop_raw(value_type, self.base.add(offset)) };
655        }
656    }
657}
658
659impl Drop for Prefix<'_> {
660    /// Destroys any initialized children not yet transferred or destroyed.
661    fn drop(&mut self) {
662        // SAFETY: Private construction and prefix updates maintain live ownership.
663        unsafe { self.destroy() };
664    }
665}
666
667/// Returns the exact global-allocation layout for a concrete array capacity.
668///
669/// - Precondition: Capacity comes from a valid array header of this element type.
670fn array_layout(element: &ValueType, capacity: usize) -> Layout {
671    Layout::from_size_align(
672        element
673            .layout()
674            .size()
675            .checked_mul(capacity)
676            .expect("valid array capacity"),
677        element.layout().align(),
678    )
679    .expect("valid array allocation layout")
680}
681
682/// Destroys a prevalidated live representation and all its owned allocations.
683///
684/// - Complexity: O(v) traversal time and O(d) call space for v values and depth d.
685///
686/// # Safety
687///
688/// `pointer` must uniquely own one aligned live value matching `value_type`.
689pub(crate) unsafe fn drop_raw(value_type: &ValueType, pointer: *mut u8) {
690    if let Some(dropper) = value_type.leaf_drop() {
691        unsafe { dropper(pointer) };
692        return;
693    }
694    match value_type.kind() {
695        ValueKind::Tuple(fields) => {
696            let mut prefix = Prefix {
697                base: pointer,
698                children: Children::Fields(fields),
699                initialized: fields.len(),
700            };
701            unsafe { prefix.destroy() };
702        }
703        ValueKind::Array(_) => unsafe { pointer.cast::<RawArray>().drop_in_place() },
704        // No public constructor supplies callable storage before its carrier exists.
705        ValueKind::Leaf(_) | ValueKind::Callable { .. } => unreachable!("missing ownership plan"),
706    }
707}
708
709/// Clones through a frozen, prevalidated plan with panic-safe prefix ownership.
710///
711/// - Complexity: O(v) traversal time, O(d) call space, and cloned value storage.
712///
713/// # Safety
714///
715/// The plan must support cloning. Source must be live and aligned; destination
716/// must be disjoint, aligned uninitialized storage of the same complete shape.
717/// A panic leaves no initialized value in the destination.
718pub(crate) unsafe fn clone_raw(value_type: &ValueType, source: *const u8, destination: *mut u8) {
719    debug_assert!(value_type.can_clone());
720    if let Some(cloner) = value_type.clone_hook() {
721        unsafe { cloner(source, destination) };
722        return;
723    }
724    match value_type.kind() {
725        ValueKind::Tuple(fields) => {
726            let mut prefix = Prefix {
727                base: destination,
728                children: Children::Fields(fields),
729                initialized: 0,
730            };
731            for field in fields {
732                unsafe {
733                    clone_raw(
734                        &field.value_type,
735                        source.add(field.offset),
736                        destination.add(field.offset),
737                    );
738                }
739                prefix.initialized += 1;
740            }
741            prefix.initialized = 0;
742        }
743        ValueKind::Array(element) => {
744            let source = unsafe { &*source.cast::<RawArray>() };
745            let capacity = if element.layout().size() == 0 {
746                usize::MAX
747            } else {
748                source.len
749            };
750            let allocation = Allocation::new(array_layout(element, capacity));
751            let mut prefix = Prefix {
752                base: allocation.pointer.as_ptr(),
753                children: Children::Elements(element),
754                initialized: 0,
755            };
756            for index in 0..source.len {
757                let offset = index * element.layout().size();
758                unsafe {
759                    clone_raw(
760                        element,
761                        source.ptr.as_ptr().cast::<u8>().add(offset),
762                        allocation.pointer.as_ptr().add(offset),
763                    );
764                }
765                prefix.initialized += 1;
766            }
767            unsafe {
768                destination.cast::<RawArray>().write(RawArray::from_erased(
769                    allocation.pointer.cast(),
770                    source.len,
771                    capacity,
772                    element.clone(),
773                ));
774            }
775            prefix.initialized = 0;
776            std::mem::forget(allocation);
777        }
778        ValueKind::Leaf(_) | ValueKind::Callable { .. } => unreachable!("missing clone plan"),
779    }
780}
781
782/// Compares live values using a prevalidated complete equality plan.
783///
784/// - Complexity: O(v) traversal time and O(d) call space, excluding leaf costs.
785///
786/// # Safety
787///
788/// Both live values must match the complete comparable descriptor.
789unsafe fn equal_raw(value_type: &ValueType, left: *const u8, right: *const u8) -> bool {
790    if let Some(equal) = value_type.equality_hook() {
791        return unsafe { equal(left, right) };
792    }
793    match value_type.kind() {
794        ValueKind::Tuple(fields) => fields.iter().all(|field| unsafe {
795            equal_raw(
796                &field.value_type,
797                left.add(field.offset),
798                right.add(field.offset),
799            )
800        }),
801        ValueKind::Array(element) => {
802            let left = unsafe { &*left.cast::<RawArray>() };
803            let right = unsafe { &*right.cast::<RawArray>() };
804            left.len == right.len
805                && (0..left.len).all(|index| unsafe {
806                    let offset = index * element.layout().size();
807                    equal_raw(
808                        element,
809                        left.ptr.as_ptr().cast::<u8>().add(offset),
810                        right.ptr.as_ptr().cast::<u8>().add(offset),
811                    )
812                })
813        }
814        ValueKind::Leaf(_) | ValueKind::Callable { .. } => unreachable!("missing equality plan"),
815    }
816}
817
818/// Formats live values in source order using a prevalidated debug plan.
819///
820/// - Complexity: O(v + s) time and O(d + s) space, excluding leaf costs.
821///
822/// # Safety
823///
824/// The live value must match the complete debug-capable descriptor.
825unsafe fn debug_raw(value_type: &ValueType, pointer: *const u8, output: &mut String) {
826    if let Some(debug) = value_type.debug_hook() {
827        unsafe { debug(pointer, output) };
828        return;
829    }
830    match value_type.kind() {
831        ValueKind::Tuple(fields) => {
832            output.push('(');
833            for (index, field) in fields.iter().enumerate() {
834                if index != 0 {
835                    output.push_str(", ");
836                }
837                unsafe { debug_raw(&field.value_type, pointer.add(field.offset), output) };
838            }
839            if fields.len() == 1 {
840                output.push(',');
841            }
842            output.push(')');
843        }
844        ValueKind::Array(element) => {
845            output.push('[');
846            let raw = unsafe { &*pointer.cast::<RawArray>() };
847            for index in 0..raw.len {
848                if index != 0 {
849                    output.push_str(", ");
850                }
851                unsafe {
852                    debug_raw(
853                        element,
854                        raw.ptr
855                            .as_ptr()
856                            .cast::<u8>()
857                            .add(index * element.layout().size()),
858                        output,
859                    )
860                };
861            }
862            output.push(']');
863        }
864        ValueKind::Leaf(_) | ValueKind::Callable { .. } => unreachable!("missing debug plan"),
865    }
866}