Skip to main content

cel_runtime/
raw_vec.rs

1use crate::memory::align_index;
2use std::cmp::max;
3use std::mem::MaybeUninit;
4use std::ops::{Index, IndexMut};
5use std::slice::SliceIndex;
6
7/// A vector of bytes aligned to a given value. The alignment can be increase by calling `align`.
8#[derive(Debug)]
9pub struct RawVec {
10    buffer: Vec<MaybeUninit<u8>>,
11    base_alignment: usize,
12    start_offset: usize,
13}
14
15impl<I> Index<I> for RawVec
16where
17    I: SliceIndex<[MaybeUninit<u8>]>,
18{
19    type Output = I::Output;
20
21    fn index(&self, index: I) -> &Self::Output {
22        let slice = &self.buffer[self.start_offset..];
23        &slice[index]
24    }
25}
26
27impl<I> IndexMut<I> for RawVec
28where
29    I: SliceIndex<[MaybeUninit<u8>]>,
30{
31    fn index_mut(&mut self, index: I) -> &mut Self::Output {
32        let slice = &mut self.buffer[self.start_offset..];
33        &mut slice[index]
34    }
35}
36
37impl RawVec {
38    /// Creates a new `RawVec` with base alignment.
39    ///
40    /// # Examples
41    ///
42    /// ```
43    /// use cel_runtime::RawVec;
44    /// use std::mem::align_of;
45    /// let vec = RawVec::with_base_alignment(align_of::<u32>());
46    /// ```
47    #[must_use]
48    pub fn with_base_alignment(base_alignment: usize) -> Self {
49        RawVec {
50            base_alignment,
51            start_offset: 0,
52            buffer: Vec::new(),
53        }
54    }
55
56    /// Creates a new `RawVec` with base alignment and initial capacity.
57    #[must_use]
58    pub fn with_base_alignment_and_capacity(base_alignment: usize, capacity: usize) -> Self {
59        let mut buffer = Vec::with_capacity(capacity + base_alignment - 1);
60        let ptr_as_index = buffer.as_ptr() as usize;
61        let start_offset = align_index(base_alignment, ptr_as_index) - ptr_as_index;
62        unsafe { buffer.set_len(start_offset) };
63        RawVec {
64            buffer,
65            base_alignment,
66            start_offset,
67        }
68    }
69
70    /// Returns the capacity of the vector in bytes.
71    #[must_use]
72    pub fn capacity(&self) -> usize {
73        self.buffer.capacity() - self.start_offset
74    }
75
76    /// Returns the current length of the vector in bytes.
77    #[must_use]
78    pub fn len(&self) -> usize {
79        self.buffer.len() - self.start_offset
80    }
81
82    /// Returns true if the vector contains no bytes.
83    #[must_use]
84    pub fn is_empty(&self) -> bool {
85        self.len() == 0
86    }
87
88    /// Reserves capacity for at least additional more elements. The collection may reserve more
89    /// space to guarantee amortized constant time growth. After calling reserve, capacity will be
90    /// greater than or equal to `self.len() + additional`. Does nothing if capacity is already
91    /// sufficient.
92    ///
93    /// # Panics
94    ///
95    /// Panics if the new capacity exceeds `isize::MAX bytes`.
96    pub fn reserve(&mut self, addition: usize) {
97        let capacity = self.len() + addition;
98        if capacity > self.capacity() {
99            let mut new_buffer = Self::with_base_alignment_and_capacity(
100                self.base_alignment,
101                max(capacity, self.capacity() * 2),
102            );
103            unsafe {
104                std::ptr::copy_nonoverlapping(self.as_ptr(), new_buffer.as_mut_ptr(), self.len());
105            }
106            *self = new_buffer;
107        }
108    }
109
110    /// Sets the length of the vector.
111    ///
112    /// # Panics
113    ///
114    /// - The length must be less than or equal to the capacity.
115    ///
116    /// # Safety
117    ///
118    /// - The elements at `old_len..new_len` must be initialized.
119    pub unsafe fn set_len(&mut self, len: usize) {
120        assert!(len <= self.capacity());
121        unsafe { self.buffer.set_len(self.start_offset + len) };
122    }
123
124    /// Returns a raw mutable pointer to the vector's buffer, or a dangling raw pointer valid for
125    /// zero sized reads if the vector didn't allocate.
126    ///
127    /// This method guarantees that for the purpose of the aliasing model, this method does not
128    /// materialize a reference to the underlying slice, and thus the returned pointer will remain
129    /// valid when mixed with other calls to [`Self::as_ptr`], [`Self::as_mut_ptr`]. Note
130    /// that calling other methods that materialize references to the slice, or references to
131    /// specific elements you are planning on accessing through this pointer, may still invalidate
132    /// this pointer.
133    ///
134    /// # Safety
135    ///
136    /// The pointer is valid until the vector buffer is reallocated or the vector's lifetime ends.
137    pub unsafe fn as_mut_ptr(&mut self) -> *mut MaybeUninit<u8> {
138        unsafe { self.buffer.as_mut_ptr().add(self.start_offset) }
139    }
140
141    /// Returns a raw pointer to the vector's buffer, or a dangling raw pointer valid for zero sized
142    /// reads if the vector didn't allocate.
143    ///
144    /// The caller must also ensure that the memory the pointer (non-transitively) points to is
145    /// never written to (except inside an `UnsafeCell`) using this pointer or any pointer derived
146    /// from it. If you need to mutate the contents of the slice, use [`Self::as_mut_ptr`].
147    ///
148    /// This method guarantees that for the purpose of the aliasing model, this method does not
149    /// materialize a reference to the underlying slice, and thus the returned pointer will remain
150    /// valid when mixed with other calls to [`Self::as_ptr`], [`Self::as_mut_ptr`]. Note
151    /// that calling other methods that materialize mutable references to the slice, or mutable
152    /// references to specific elements you are planning on accessing through this pointer, as well
153    /// as writing to those elements, may still invalidate this pointer.
154    ///
155    /// # Safety
156    ///
157    /// The pointer is valid until the vector buffer is reallocated or the vector's lifetime ends.
158    #[must_use]
159    pub unsafe fn as_ptr(&self) -> *const MaybeUninit<u8> {
160        unsafe { self.buffer.as_ptr().add(self.start_offset) }
161    }
162
163    /// Shortens the vector, keeping the first `len` elements and dropping
164    /// the rest.
165    ///
166    /// If `len` is greater or equal to the vector's current length, this has
167    /// no effect.
168    pub fn truncate(&mut self, len: usize) {
169        self.buffer.truncate(self.start_offset + len);
170    }
171}
172
173/* Test module */
174#[cfg(test)]
175mod tests {
176    use super::*;
177
178    #[test]
179    fn with_base_alignment() {
180        let vec = RawVec::with_base_alignment(align_of::<u32>());
181        assert_eq!(vec.capacity(), 0);
182        assert_eq!(vec.len(), 0);
183    }
184
185    #[test]
186    fn with_base_alignment_and_capacity() {
187        let vec = RawVec::with_base_alignment_and_capacity(align_of::<u32>(), 10);
188        assert!(vec.capacity() >= 10);
189        assert_eq!(vec.len(), 0);
190        assert_eq!(unsafe { vec.as_ptr() as usize } % align_of::<u32>(), 0);
191    }
192
193    #[test]
194    fn reserve() {
195        let mut vec = RawVec::with_base_alignment(align_of::<u32>());
196        vec.reserve(10);
197        assert!(vec.capacity() >= 10);
198        assert_eq!(vec.len(), 0);
199        assert_eq!(unsafe { vec.as_ptr() as usize } % align_of::<u32>(), 0);
200    }
201
202    #[test]
203    fn set_len() {
204        let mut vec = RawVec::with_base_alignment_and_capacity(align_of::<u32>(), 10);
205        unsafe { vec.set_len(10) };
206        assert_eq!(vec.len(), 10);
207        assert_eq!(unsafe { vec.as_ptr() as usize } % align_of::<u32>(), 0);
208    }
209
210    #[test]
211    fn index() {
212        let mut vec = RawVec::with_base_alignment_and_capacity(align_of::<u32>(), 10);
213        unsafe { vec.set_len(1) };
214        vec[0].write(42);
215        assert_eq!(unsafe { vec[0].assume_init() }, 42);
216    }
217}