cel_runtime/array.rs
1//! Concrete, contiguous ownership for homogeneous CEL arrays.
2//!
3//! Transfers to and from vectors preserve their allocations. Nested arrays use
4//! `Array<Array<T>>`, so each outer element is itself a typed array owner.
5//! Its outer allocation transfers to `Vec<Array<T>>`, not `Vec<Vec<T>>`;
6//! explicitly converting each inner owner builds a new outer vector. Borrowing
7//! exposes `&[Array<T>]` without consuming the outer or inner owners.
8//!
9//! ```
10//! use cel_runtime::Array;
11//!
12//! let rows = Array::from_vec(vec![Array::from_vec(vec![1_i32, 2])]);
13//! assert_eq!(rows.as_slice()[0].as_slice(), &[1, 2]);
14//! ```
15
16use std::fmt;
17use std::marker::PhantomData;
18use std::mem::ManuallyDrop;
19use std::ptr::NonNull;
20
21/// Stores an array's pointer, element count, and element capacity, in that order.
22///
23/// The pointer is non-null and aligned for the concrete element type, including empty and
24/// zero-sized arrays. Length and capacity count elements, not bytes; length does
25/// not exceed capacity. The first `len` elements are initialized. For non-zero-sized
26/// elements with nonzero capacity, the pointer uniquely owns a global-allocator
27/// allocation compatible with `Vec<T>` at exactly this capacity and element type.
28///
29/// The header owns its member destruction operation. Erased arrays additionally
30/// own an element descriptor; typed arrays bind concrete drop glue without one.
31/// This layout is independent of Rust's unspecified `Vec<T>` header layout.
32#[repr(C)]
33pub(crate) struct RawArray {
34 pub(crate) ptr: NonNull<()>,
35 pub(crate) len: usize,
36 pub(crate) capacity: usize,
37 drop_members: unsafe fn(&mut RawArray),
38 pub(crate) element_type: Option<crate::ValueType>,
39}
40
41impl RawArray {
42 /// Adopts an erased array allocation with its live element destruction metadata.
43 ///
44 /// # Safety
45 /// The raw parts must uniquely own `len` initialized elements of `element_type`,
46 /// using its layout and the global allocator at exactly `capacity`.
47 pub(crate) unsafe fn from_erased(
48 ptr: NonNull<()>,
49 len: usize,
50 capacity: usize,
51 element_type: crate::ValueType,
52 ) -> Self {
53 Self {
54 ptr,
55 len,
56 capacity,
57 drop_members: drop_erased_members,
58 element_type: Some(element_type),
59 }
60 }
61}
62
63impl Drop for RawArray {
64 /// Destroys live members in reverse order and releases their allocation.
65 ///
66 /// - Complexity: O(n) member destructions for n live members.
67 fn drop(&mut self) {
68 // SAFETY: Each constructor binds destruction to the allocation's exact layout.
69 unsafe { (self.drop_members)(self) };
70 }
71}
72
73/// Destroys concrete members and frees their original vector allocation.
74///
75/// # Safety
76/// The header must uniquely own the raw parts of a `Vec<T>`.
77/// - Complexity: O(n) member destructions for n live members.
78unsafe fn drop_typed_members<T>(raw: &mut RawArray) {
79 let mut guard = ReverseDrop(unsafe {
80 Vec::from_raw_parts(raw.ptr.cast::<T>().as_ptr(), raw.len, raw.capacity)
81 });
82 raw.len = 0;
83 while let Some(value) = guard.0.pop() {
84 drop(value);
85 }
86}
87
88/// Destroys erased members using metadata owned by their array header.
89///
90/// # Safety
91/// The header's descriptor must match every initialized element and its allocation.
92/// - Complexity: O(n) member destructions plus recursive member destruction.
93unsafe fn drop_erased_members(raw: &mut RawArray) {
94 let element = raw
95 .element_type
96 .as_ref()
97 .expect("erased element descriptor");
98 let layout = std::alloc::Layout::from_size_align(
99 element
100 .size()
101 .checked_mul(raw.capacity)
102 .expect("valid array capacity"),
103 element.align(),
104 )
105 .expect("valid array layout");
106 let _allocation = crate::owned_value::Allocation {
107 pointer: raw.ptr.cast(),
108 layout,
109 };
110 let mut prefix = crate::owned_value::Prefix {
111 base: raw.ptr.as_ptr().cast(),
112 children: crate::owned_value::Children::Elements(element),
113 initialized: raw.len,
114 };
115 raw.len = 0;
116 unsafe { prefix.destroy() };
117}
118
119/// Owns homogeneous, contiguous elements without bounds on the element type.
120///
121/// The transparent representation is an owning `RawArray` header, including
122/// member destruction dispatch. Its elements have the concrete layout of `T`.
123/// Nested arrays use `Array<Array<T>>`, not nested vectors.
124///
125/// Dropping an array destroys its elements exactly once in reverse index order,
126/// then releases its allocation. Remaining elements also receive reverse-order
127/// cleanup if a destructor unwinds; a second destructor panic during unwinding
128/// aborts as usual. After [`Self::into_vec`], the vector instead follows Rust's
129/// vector destruction behavior.
130///
131/// - Complexity: Destruction takes O(n) time and O(1) auxiliary space, excluding
132/// element destructor costs.
133///
134/// # Examples
135///
136/// ```
137/// use cel_runtime::Array;
138///
139/// let array = Array::from_vec(vec![String::from("leaf")]);
140/// assert_eq!(array.as_slice(), &["leaf"]);
141/// ```
142///
143/// Elements that are not `Send` cannot become transferable through an array:
144///
145/// ```compile_fail
146/// use cel_runtime::Array;
147/// use std::rc::Rc;
148///
149/// fn require_send<T: Send>() {}
150/// require_send::<Array<Rc<()>>>();
151/// ```
152///
153/// Elements that are not `Sync` cannot become shareable through an array:
154///
155/// ```compile_fail
156/// use cel_runtime::Array;
157/// use std::cell::Cell;
158///
159/// fn require_sync<T: Sync>() {}
160/// require_sync::<Array<Cell<i32>>>();
161/// ```
162#[repr(transparent)]
163pub struct Array<T> {
164 pub(crate) raw: RawArray,
165 marker: PhantomData<T>,
166}
167
168// SAFETY: The header uniquely owns T elements and never exposes untyped access
169// publicly. Moving ownership between threads is valid exactly when T is Send.
170unsafe impl<T: Send> Send for Array<T> {}
171// SAFETY: Shared access exposes only &[T], which is shareable exactly when T is Sync.
172unsafe impl<T: Sync> Sync for Array<T> {}
173
174impl<T> Array<T> {
175 /// Takes ownership of a vector without moving elements or allocating.
176 ///
177 /// - Postcondition: Contents, allocation pointer, length, and capacity are
178 /// unchanged. Empty and zero-sized vectors retain their raw parts.
179 ///
180 /// # Examples
181 ///
182 /// ```
183 /// use cel_runtime::Array;
184 /// let values = vec![3_i32, 5];
185 /// let pointer = values.as_ptr();
186 /// let array = Array::from_vec(values);
187 /// assert_eq!(array.as_slice().as_ptr(), pointer);
188 /// ```
189 #[must_use]
190 pub fn from_vec(values: Vec<T>) -> Self {
191 let (ptr, len, capacity) = values.into_raw_parts();
192 Self {
193 raw: RawArray {
194 // SAFETY: Vec raw parts always provide a non-null aligned pointer.
195 ptr: unsafe { NonNull::new_unchecked(ptr.cast()) },
196 len,
197 capacity,
198 drop_members: drop_typed_members::<T>,
199 element_type: None,
200 },
201 marker: PhantomData,
202 }
203 }
204
205 /// Transfers ownership into a vector without moving elements or allocating.
206 ///
207 /// - Postcondition: Contents, allocation pointer, length, and capacity are
208 /// unchanged; this array runs no element destructors.
209 ///
210 /// # Examples
211 ///
212 /// ```
213 /// use cel_runtime::Array;
214 /// let array = Array::from_vec(vec![3_i32, 5]);
215 /// let pointer = array.as_slice().as_ptr();
216 /// let values = array.into_vec();
217 /// assert_eq!(values.as_ptr(), pointer);
218 /// assert_eq!(values, [3, 5]);
219 /// ```
220 #[must_use]
221 pub fn into_vec(self) -> Vec<T> {
222 let mut owner = ManuallyDrop::new(self);
223 drop(owner.raw.element_type.take());
224 // SAFETY: The header preserves Vec<T>'s raw parts, and ManuallyDrop
225 // prevents the old owner from destroying the transferred allocation.
226 unsafe {
227 Vec::from_raw_parts(
228 owner.raw.ptr.cast::<T>().as_ptr(),
229 owner.raw.len,
230 owner.raw.capacity,
231 )
232 }
233 }
234
235 /// Borrows the contiguous elements without copying or allocating.
236 ///
237 /// # Examples
238 ///
239 /// ```
240 /// use cel_runtime::Array;
241 /// let array = Array::from_vec(vec![3_i32, 5]);
242 /// let values: &[i32] = array.as_slice();
243 /// assert_eq!(values, &[3, 5]);
244 /// ```
245 #[must_use]
246 pub fn as_slice(&self) -> &[T] {
247 // SAFETY: The typed owner guarantees an aligned non-null pointer and
248 // len initialized T elements, alive for the lifetime of this borrow.
249 unsafe { std::slice::from_raw_parts(self.raw.ptr.cast::<T>().as_ptr(), self.raw.len) }
250 }
251
252 /// Returns the number of initialized elements.
253 ///
254 /// # Examples
255 ///
256 /// ```
257 /// use cel_runtime::Array;
258 /// assert_eq!(Array::from_vec(vec![3_i32, 5]).len(), 2);
259 /// ```
260 #[must_use]
261 pub fn len(&self) -> usize {
262 self.raw.len
263 }
264
265 /// Returns the allocation capacity in elements, as reported by the source vector.
266 ///
267 /// Zero-sized elements retain the vector's capacity of `usize::MAX`.
268 ///
269 /// # Examples
270 ///
271 /// ```
272 /// use cel_runtime::Array;
273 /// let values = Vec::<i32>::with_capacity(8);
274 /// let capacity = values.capacity();
275 /// assert_eq!(Array::from_vec(values).capacity(), capacity);
276 /// ```
277 #[must_use]
278 pub fn capacity(&self) -> usize {
279 self.raw.capacity
280 }
281
282 /// Returns whether the array contains no elements.
283 ///
284 /// # Examples
285 ///
286 /// ```
287 /// use cel_runtime::Array;
288 /// assert!(Array::from_vec(Vec::<i32>::new()).is_empty());
289 /// ```
290 #[must_use]
291 pub fn is_empty(&self) -> bool {
292 self.raw.len == 0
293 }
294}
295
296/// Owns remaining elements during cleanup and frees storage even during unwinding.
297struct ReverseDrop<T>(Vec<T>);
298
299impl<T> Drop for ReverseDrop<T> {
300 /// Drops remaining elements in reverse order in O(n) time and O(1) space.
301 fn drop(&mut self) {
302 while let Some(value) = self.0.pop() {
303 drop(value);
304 }
305 }
306}
307
308impl<T: Clone> Clone for Array<T> {
309 /// Clones elements into a new array without sharing owned storage.
310 ///
311 /// - Complexity: O(n) time and space, excluding element clone costs.
312 ///
313 /// # Examples
314 ///
315 /// ```
316 /// use cel_runtime::Array;
317 /// let array = Array::from_vec(vec![String::from("leaf")]);
318 /// assert_eq!(array.clone().as_slice(), array.as_slice());
319 /// ```
320 fn clone(&self) -> Self {
321 Self::from_vec(self.as_slice().to_vec())
322 }
323}
324
325impl<T: PartialEq> PartialEq for Array<T> {
326 /// Compares lengths and corresponding elements using element equality.
327 ///
328 /// - Complexity: O(n) time in the worst case, excluding element comparison
329 /// costs, and O(1) auxiliary space.
330 ///
331 /// # Examples
332 ///
333 /// ```
334 /// use cel_runtime::Array;
335 /// assert_eq!(Array::from_vec(vec![1_i32]), Array::from_vec(vec![1_i32]));
336 /// ```
337 fn eq(&self, other: &Self) -> bool {
338 self.as_slice() == other.as_slice()
339 }
340}
341
342impl<T: fmt::Debug> fmt::Debug for Array<T> {
343 /// Formats the array as `Array([elements])` using element debug formatting.
344 ///
345 /// - Complexity: O(n) element formatting calls, excluding element formatting
346 /// costs and output storage.
347 ///
348 /// # Errors
349 ///
350 /// Returns an error if the formatter fails to write the representation.
351 ///
352 /// # Examples
353 ///
354 /// ```
355 /// use cel_runtime::Array;
356 /// assert_eq!(format!("{:?}", Array::from_vec(vec![1_i32])), "Array([1])");
357 /// ```
358 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
359 formatter
360 .debug_tuple("Array")
361 .field(&self.as_slice())
362 .finish()
363 }
364}
365
366#[cfg(test)]
367mod ownership_tests {
368 use super::*;
369 use std::cell::Cell;
370 use std::rc::Rc;
371
372 /// Releases members when only the erased owning header remains.
373 #[test]
374 fn raw_header_owns_its_members() {
375 /// Records the destruction of one member.
376 struct Tracked(Rc<Cell<usize>>);
377
378 impl Drop for Tracked {
379 /// Records release of this member.
380 fn drop(&mut self) {
381 self.0.set(self.0.get() + 1);
382 }
383 }
384
385 let drops = Rc::new(Cell::new(0));
386 let array = ManuallyDrop::new(Array::from_vec(vec![
387 Tracked(drops.clone()),
388 Tracked(drops.clone()),
389 ]));
390 // SAFETY: The suppressed typed owner transfers its header exactly once.
391 let raw = unsafe { std::ptr::read(&array.raw) };
392 drop(raw);
393 assert_eq!(drops.get(), 2);
394 }
395}