light_zero_copy/
cyclic_vec.rs

1use core::{
2    fmt::{self, Debug},
3    marker::PhantomData,
4    mem::size_of,
5    ops::{Index, IndexMut},
6};
7#[cfg(feature = "std")]
8use std::vec::Vec;
9
10use zerocopy::{little_endian::U32, Ref};
11
12use crate::{add_padding, errors::ZeroCopyError, ZeroCopyTraits};
13
14pub type ZeroCopyCyclicVecU32<'a, T> = ZeroCopyCyclicVec<'a, u32, T>;
15pub type ZeroCopyCyclicVecU64<'a, T> = ZeroCopyCyclicVec<'a, u64, T>;
16pub type ZeroCopyCyclicVecU16<'a, T> = ZeroCopyCyclicVec<'a, u16, T>;
17pub type ZeroCopyCyclicVecU8<'a, T> = ZeroCopyCyclicVec<'a, u8, T>;
18pub type ZeroCopyCyclicVecBorsh<'a, T> = ZeroCopyCyclicVec<'a, U32, T>;
19
20pub struct ZeroCopyCyclicVec<'a, L, T, const PAD: bool = true>
21where
22    L: ZeroCopyTraits,
23    T: ZeroCopyTraits,
24    u64: From<L> + TryInto<L>,
25{
26    /// [current_index, length, capacity]
27    metadata: Ref<&'a mut [u8], [L; 3]>,
28    slice: Ref<&'a mut [u8], [T]>,
29}
30
31const CURRENT_INDEX_INDEX: usize = 0;
32const LENGTH_INDEX: usize = 1;
33const CAPACITY_INDEX: usize = 2;
34
35impl<'a, L, T, const PAD: bool> ZeroCopyCyclicVec<'a, L, T, PAD>
36where
37    L: ZeroCopyTraits,
38    T: ZeroCopyTraits,
39    u64: From<L> + TryInto<L>,
40{
41    pub fn new(capacity: L, bytes: &'a mut [u8]) -> Result<Self, ZeroCopyError> {
42        Ok(Self::new_at(capacity, bytes)?.0)
43    }
44
45    pub fn new_at(capacity: L, bytes: &'a mut [u8]) -> Result<(Self, &'a mut [u8]), ZeroCopyError> {
46        if u64::from(capacity) == 0 {
47            return Err(ZeroCopyError::InvalidCapacity);
48        }
49        let metadata_size = Self::metadata_size();
50        if bytes.len() < metadata_size {
51            return Err(ZeroCopyError::InsufficientMemoryAllocated(
52                bytes.len(),
53                metadata_size,
54            ));
55        }
56        let (meta_data, bytes) = bytes.split_at_mut(metadata_size);
57
58        let (mut metadata, _padding) = Ref::<&mut [u8], [L; 3]>::from_prefix(meta_data)?;
59
60        if u64::from(metadata[LENGTH_INDEX]) != 0
61            || u64::from(metadata[CURRENT_INDEX_INDEX]) != 0
62            || u64::from(metadata[CAPACITY_INDEX]) != 0
63        {
64            return Err(ZeroCopyError::MemoryNotZeroed);
65        }
66        metadata[CAPACITY_INDEX] = capacity;
67        let capacity_usize: usize = u64::from(metadata[CAPACITY_INDEX]) as usize;
68
69        let (slice, remaining_bytes) =
70            Ref::<&mut [u8], [T]>::from_prefix_with_elems(bytes, capacity_usize)?;
71        Ok((Self { metadata, slice }, remaining_bytes))
72    }
73
74    pub fn from_bytes(bytes: &'a mut [u8]) -> Result<Self, ZeroCopyError> {
75        Ok(Self::from_bytes_at(bytes)?.0)
76    }
77
78    #[inline]
79    pub fn from_bytes_at(bytes: &'a mut [u8]) -> Result<(Self, &'a mut [u8]), ZeroCopyError> {
80        let metadata_size = Self::metadata_size();
81        if bytes.len() < metadata_size {
82            return Err(ZeroCopyError::InsufficientMemoryAllocated(
83                bytes.len(),
84                metadata_size,
85            ));
86        }
87
88        let (meta_data, bytes) = bytes.split_at_mut(metadata_size);
89        let (metadata, _padding) = Ref::<&mut [u8], [L; 3]>::from_prefix(meta_data)?;
90        let usize_capacity: usize = u64::from(metadata[CAPACITY_INDEX]) as usize;
91        let usize_len: usize = u64::from(metadata[LENGTH_INDEX]) as usize;
92        let usize_current_index: usize = u64::from(metadata[CURRENT_INDEX_INDEX]) as usize;
93
94        if usize_len > usize_capacity {
95            return Err(ZeroCopyError::LengthGreaterThanCapacity);
96        }
97
98        if usize_current_index > usize_len {
99            return Err(ZeroCopyError::CurrentIndexGreaterThanLength);
100        }
101
102        let full_vector_size = Self::data_size(metadata[CAPACITY_INDEX]);
103        if bytes.len() < full_vector_size {
104            return Err(ZeroCopyError::InsufficientMemoryAllocated(
105                bytes.len() + metadata_size,
106                full_vector_size + metadata_size,
107            ));
108        }
109        let (slice, remaining_bytes) =
110            Ref::<&mut [u8], [T]>::from_prefix_with_elems(bytes, usize_capacity)?;
111        Ok((Self { metadata, slice }, remaining_bytes))
112    }
113
114    /// Convenience method to get the current index of the vector.
115    #[inline]
116    fn get_current_index(&self) -> L {
117        self.metadata[CURRENT_INDEX_INDEX]
118    }
119
120    /// Convenience method to get the current index of the vector.
121    #[inline]
122    fn get_current_index_mut(&mut self) -> &mut L {
123        &mut self.metadata[CURRENT_INDEX_INDEX]
124    }
125
126    /// Convenience method to get the length of the vector.
127    #[inline]
128    fn get_len(&self) -> L {
129        self.metadata[LENGTH_INDEX]
130    }
131
132    /// Convenience method to get the length of the vector.
133    #[inline]
134    fn get_len_mut(&mut self) -> &mut L {
135        &mut self.metadata[LENGTH_INDEX]
136    }
137
138    /// Convenience method to get the capacity of the vector.
139    #[inline]
140    fn get_capacity(&self) -> L {
141        self.metadata[CAPACITY_INDEX]
142    }
143
144    #[inline]
145    pub fn push(&mut self, value: T) {
146        if self.len() < self.capacity() {
147            let len = self.len();
148            self.slice[len] = value;
149            *self.get_len_mut() = (len as u64 + 1u64)
150                .try_into()
151                .map_err(|_| ZeroCopyError::InvalidConversion)
152                .unwrap();
153        } else {
154            let current_index = self.current_index();
155            self.slice[current_index] = value;
156        }
157        let new_index = (self.current_index() + 1) % self.capacity();
158        *self.get_current_index_mut() = (new_index as u64)
159            .try_into()
160            .map_err(|_| ZeroCopyError::InvalidConversion)
161            .unwrap();
162    }
163
164    #[inline]
165    pub fn clear(&mut self) {
166        *self.get_current_index_mut() = 0
167            .try_into()
168            .map_err(|_| ZeroCopyError::InvalidConversion)
169            .unwrap();
170        *self.get_len_mut() = self.get_current_index();
171    }
172
173    #[inline]
174    pub fn first(&self) -> Option<&T> {
175        self.get(self.first_index())
176    }
177
178    #[inline]
179    pub fn first_mut(&mut self) -> Option<&mut T> {
180        self.get_mut(self.first_index())
181    }
182
183    #[inline]
184    pub fn last(&self) -> Option<&T> {
185        self.get(self.last_index())
186    }
187
188    #[inline]
189    pub fn last_mut(&mut self) -> Option<&mut T> {
190        self.get_mut(self.last_index())
191    }
192
193    #[inline]
194    fn current_index(&self) -> usize {
195        u64::from(self.get_current_index()) as usize
196    }
197
198    /// First index is the next index after the last index mod capacity.
199    #[inline]
200    pub fn first_index(&self) -> usize {
201        if self.len() < self.capacity() {
202            0
203        } else {
204            self.last_index().saturating_add(1) % (self.capacity())
205        }
206    }
207
208    #[inline]
209    pub fn last_index(&self) -> usize {
210        if self.current_index() == 0 && self.len() == self.capacity() {
211            self.capacity().saturating_sub(1)
212        } else {
213            self.current_index().saturating_sub(1) % self.capacity()
214        }
215    }
216
217    #[inline]
218    pub fn iter(&self) -> ZeroCopyCyclicVecIterator<'_, L, T, PAD> {
219        ZeroCopyCyclicVecIterator {
220            vec: self,
221            current: self.first_index(),
222            is_finished: false,
223            _marker: PhantomData,
224        }
225    }
226
227    #[inline]
228    pub fn iter_from(
229        &self,
230        start: usize,
231    ) -> Result<ZeroCopyCyclicVecIterator<'_, L, T, PAD>, ZeroCopyError> {
232        if start >= self.len() {
233            return Err(ZeroCopyError::IterFromOutOfBounds);
234        }
235        Ok(ZeroCopyCyclicVecIterator {
236            vec: self,
237            current: start,
238            is_finished: false,
239            _marker: PhantomData,
240        })
241    }
242
243    #[inline]
244    pub fn metadata_size() -> usize {
245        let mut size = size_of::<[L; 3]>();
246        if PAD {
247            add_padding::<[L; 3], T>(&mut size);
248        }
249        size
250    }
251
252    #[inline]
253    pub fn data_size(capacity: L) -> usize {
254        let usize_len: usize = u64::from(capacity) as usize;
255        usize_len.saturating_mul(size_of::<T>())
256    }
257
258    pub fn required_size_for_capacity(capacity: L) -> usize {
259        Self::metadata_size().saturating_add(Self::data_size(capacity))
260    }
261
262    #[inline]
263    pub fn len(&self) -> usize {
264        u64::from(self.get_len()) as usize
265    }
266
267    #[inline]
268    pub fn capacity(&self) -> usize {
269        u64::from(self.get_capacity()) as usize
270    }
271
272    #[inline]
273    pub fn is_empty(&self) -> bool {
274        self.len() == 0
275    }
276
277    #[inline]
278    pub fn get(&self, index: usize) -> Option<&T> {
279        if index >= self.len() {
280            return None;
281        }
282        Some(&self.slice[index])
283    }
284
285    #[inline]
286    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
287        if index >= self.len() {
288            return None;
289        }
290        Some(&mut self.slice[index])
291    }
292
293    #[inline]
294    pub fn as_slice(&self) -> &[T] {
295        &self.slice[..self.len()]
296    }
297
298    #[inline]
299    pub fn as_mut_slice(&mut self) -> &mut [T] {
300        let len = self.len();
301        &mut self.slice[..len]
302    }
303
304    #[cfg(feature = "std")]
305    pub fn try_into_array<const N: usize>(&self) -> Result<[T; N], ZeroCopyError> {
306        if self.len() != N {
307            return Err(ZeroCopyError::ArraySize(N, self.len()));
308        }
309        Ok(core::array::from_fn(|i| *self.get(i).unwrap()))
310    }
311
312    #[cfg(feature = "std")]
313    #[inline]
314    pub fn to_vec(&self) -> Vec<T> {
315        self.as_slice().to_vec()
316    }
317}
318
319pub struct ZeroCopyCyclicVecIterator<'a, L, T, const PAD: bool>
320where
321    L: ZeroCopyTraits,
322    T: ZeroCopyTraits,
323    u64: From<L> + TryInto<L>,
324{
325    vec: &'a ZeroCopyCyclicVec<'a, L, T, PAD>,
326    current: usize,
327    is_finished: bool,
328    _marker: PhantomData<T>,
329}
330
331impl<'a, L, T, const PAD: bool> Iterator for ZeroCopyCyclicVecIterator<'a, L, T, PAD>
332where
333    L: ZeroCopyTraits,
334    T: ZeroCopyTraits,
335    u64: From<L> + TryInto<L>,
336{
337    type Item = &'a T;
338
339    #[inline]
340    fn next(&mut self) -> Option<Self::Item> {
341        if self.vec.capacity() == 0 || self.is_finished {
342            None
343        } else {
344            // Perform one more iteration to perform len() iterations.
345            if self.current == self.vec.last_index() {
346                self.is_finished = true;
347            }
348            let new_current = (self.current + 1) % self.vec.capacity();
349            let element = self.vec.get(self.current);
350            self.current = new_current;
351            element
352        }
353    }
354}
355
356impl<L, T, const PAD: bool> IndexMut<usize> for ZeroCopyCyclicVec<'_, L, T, PAD>
357where
358    L: ZeroCopyTraits,
359    T: ZeroCopyTraits,
360    u64: From<L> + TryInto<L>,
361{
362    #[inline]
363    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
364        // Access the underlying mutable slice using as_mut_slice() and index it
365        &mut self.as_mut_slice()[index]
366    }
367}
368
369impl<L, T, const PAD: bool> Index<usize> for ZeroCopyCyclicVec<'_, L, T, PAD>
370where
371    L: ZeroCopyTraits,
372    T: ZeroCopyTraits,
373    u64: From<L> + TryInto<L>,
374{
375    type Output = T;
376
377    #[inline]
378    fn index(&self, index: usize) -> &Self::Output {
379        // Access the underlying slice using as_slice() and index it
380        &self.as_slice()[index]
381    }
382}
383
384impl<L, T, const PAD: bool> PartialEq for ZeroCopyCyclicVec<'_, L, T, PAD>
385where
386    L: ZeroCopyTraits + PartialEq,
387    T: ZeroCopyTraits + PartialEq,
388    u64: From<L> + TryInto<L>,
389{
390    #[inline]
391    fn eq(&self, other: &Self) -> bool {
392        self.as_slice() == other.as_slice() && self.get_current_index() == other.get_current_index()
393    }
394}
395
396impl<L, T, const PAD: bool> fmt::Debug for ZeroCopyCyclicVec<'_, L, T, PAD>
397where
398    L: ZeroCopyTraits,
399    T: ZeroCopyTraits + Debug,
400    u64: From<L> + TryInto<L>,
401{
402    #[inline]
403    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404        write!(f, "{:?}", self.as_slice())
405    }
406}
407
408#[test]
409fn test_private_getters() {
410    let mut backing_store = [0u8; 64];
411    let mut zcv = ZeroCopyCyclicVecU16::<u16>::new(5, &mut backing_store).unwrap();
412    assert_eq!(zcv.get_len(), 0);
413    assert_eq!(zcv.get_capacity(), 5);
414    for i in 0..5 {
415        zcv.push(i);
416        assert_eq!(zcv.get_len(), i + 1);
417        assert_eq!(zcv.get_len_mut(), &mut (i + 1));
418    }
419}