Skip to main content

cel_runtime/
raw_sequence.rs

1use crate::memory::align_index;
2use crate::raw_vec::RawVec;
3use std::mem;
4
5/// A sequence that stores heterogeneous values with proper alignment.
6///
7/// The `RawSequence` provides a memory-efficient way to store heterogeneous values
8/// while maintaining proper alignment requirements for each type. It uses an
9/// internal buffer that aligns values according to their type's requirements,
10/// up to a maximum alignment of 4096 bytes.
11pub struct RawSequence {
12    buffer: RawVec,
13}
14
15impl Default for RawSequence {
16    fn default() -> Self {
17        Self::new()
18    }
19}
20
21impl RawSequence {
22    /// Creates a new empty `RawSequence`.
23    /// The sequence is initialized with a 4096-byte aligned buffer.
24    #[must_use]
25    pub fn new() -> Self {
26        RawSequence {
27            buffer: RawVec::with_base_alignment(4096),
28        }
29    }
30
31    /// Pushes a value onto the sequence.
32    ///
33    /// The value is stored at an address that satisfies its alignment requirements.
34    /// Automatically grows the internal buffer if needed.
35    ///
36    /// # Panics
37    /// Panics if the type's alignment requirement exceeds 4096 bytes.
38    pub fn push<T>(&mut self, value: T) {
39        assert!(mem::align_of::<T>() <= 4096);
40        let len = self.buffer.len();
41        let aligned: usize = align_index(mem::align_of::<T>(), len);
42        let new_len = aligned + mem::size_of::<T>();
43
44        self.buffer.reserve(new_len - len);
45        unsafe {
46            self.buffer.set_len(new_len);
47            std::ptr::write(self.buffer.as_mut_ptr().add(aligned).cast::<T>(), value);
48        }
49    }
50
51    /// Drops a value in-place at the specified position.
52    ///
53    /// # Safety
54    /// - The position must point to a valid value of type T
55    /// - The caller must ensure that the value is actually of type T
56    ///
57    /// Returns the position immediately after the dropped value.
58    pub unsafe fn drop_in_place<T>(&mut self, p: usize) -> usize {
59        let aligned: usize = align_index(mem::align_of::<T>(), p);
60        unsafe { std::ptr::drop_in_place(self.buffer.as_ptr().add(aligned) as *mut T) };
61        aligned + mem::size_of::<T>()
62    }
63
64    /// Retrieves a reference to the next value at the specified position.
65    ///
66    /// # Safety
67    /// - The position must point to a valid value of type T
68    /// - The caller must ensure that the value is actually of type T
69    ///
70    /// Returns a tuple containing:
71    /// - A reference to the value
72    /// - The position immediately after the value
73    #[must_use]
74    pub unsafe fn next<T>(&self, p: usize) -> (&T, usize) {
75        let aligned: usize = align_index(mem::align_of::<T>(), p);
76        let ptr = unsafe { self.buffer.as_ptr().add(aligned).cast::<T>() };
77        unsafe { (&*ptr, aligned + mem::size_of::<T>()) }
78    }
79
80    /// Returns the number of bytes in the RawSequence, also referred to as its ‘length’.
81    #[must_use]
82    pub fn len(&self) -> usize {
83        self.buffer.len()
84    }
85
86    /// Returns true if the RawSequence has no elements.
87    #[must_use]
88    pub fn is_empty(&self) -> bool {
89        self.buffer.is_empty()
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    /// Test module for RawSequence.
96    ///
97    /// Contains tests that verify:
98    /// - Pushing different types of values
99    /// - Retrieving values in correct order
100    /// - Proper memory cleanup using drop_in_place
101    use super::*;
102
103    #[test]
104    fn sequence_operations() {
105        let mut seq = RawSequence::new();
106
107        seq.push(100u32);
108        seq.push(200u32);
109        seq.push(42.0f64);
110        seq.push("Hello, world!");
111
112        let (value, p) = unsafe { seq.next::<u32>(0) };
113        assert_eq!(*value, 100);
114        let (value, p) = unsafe { seq.next::<u32>(p) };
115        assert_eq!(*value, 200);
116        let (value, p) = unsafe { seq.next::<f64>(p) };
117        assert_eq!(*value, 42.0);
118        let (value, _) = unsafe { seq.next::<&str>(p) };
119        assert_eq!(*value, "Hello, world!");
120
121        let p = unsafe { seq.drop_in_place::<u32>(0) };
122        let p = unsafe { seq.drop_in_place::<u32>(p) };
123        let p = unsafe { seq.drop_in_place::<f64>(p) };
124        let _ = unsafe { seq.drop_in_place::<&str>(p) };
125    }
126}