Skip to main content

osom_lib_arrays/fixed_array/
const_buffer.rs

1#![allow(
2    clippy::cast_possible_wrap,
3    clippy::cast_sign_loss,
4    clippy::cast_possible_truncation,
5    clippy::needless_return,
6    clippy::new_without_default
7)]
8
9use crate::const_helpers::subslice_const;
10
11use super::ConstFixedArray;
12
13use osom_lib_primitives::length::Length;
14use osom_lib_reprc::traits::ReprC;
15
16/// This struct allows for iterating over a fixed-length blocks of arrays,
17/// given a big chunk of arbitrary length array.
18///
19/// # Examples
20///
21/// Lets say that we have an array of 20 elements:
22///
23/// ```rust
24/// let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
25/// ```
26///
27/// and now we want to process it in a block-by-block fashion, with each block having 7 elements.
28/// We can do this:
29///
30/// ```rust
31/// use osom_lib_arrays::fixed_array::{ConstBuffer, ConstFixedArray};
32///
33/// let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
34///
35/// let mut bufferer = ConstBuffer::<7, _>::new();
36/// let mut iterator = bufferer.buffer_const(&data);
37/// assert_eq!(iterator.next(), Some(&[1, 2, 3, 4, 5, 6, 7]));
38/// assert_eq!(iterator.next(), Some(&[8, 9, 10, 11, 12, 13, 14]));
39/// assert_eq!(iterator.next(), None);
40///
41/// // The iterator is drained, but we still have remaining data:
42/// let remaining: ConstFixedArray<7, _> = bufferer.release_const();
43/// assert_eq!(remaining.as_slice_const(), &[15, 16, 17, 18, 19, 20]);
44/// ```
45///
46/// # Notes
47///
48/// The [`ConstBuffer`] is additionally const friendly, and thus is defined
49/// for `T: Copy` only. In particular it isn't `Drop`.
50#[repr(transparent)]
51#[must_use]
52pub struct ConstBuffer<const TSIZE: usize, T: Sized + Copy> {
53    array: ConstFixedArray<TSIZE, T>,
54}
55
56unsafe impl<const TSIZE: usize, T: ReprC + Sized + Copy> ReprC for ConstBuffer<TSIZE, T> {
57    const CHECK: () = {
58        let () = <ConstFixedArray<TSIZE, T> as ReprC>::CHECK;
59    };
60}
61
62impl<const TSIZE: usize, T: Sized + Copy> ConstBuffer<TSIZE, T> {
63    /// Creates a new, empty [`ConstBufferer`].
64    #[inline(always)]
65    pub const fn new() -> Self {
66        Self {
67            array: ConstFixedArray::new(),
68        }
69    }
70
71    /// Returns a new [`ConstBufferer`] for the given data.
72    ///
73    /// # Panics
74    ///
75    /// The function will panic if the data length exceeds `u32::MAX`.
76    #[inline(always)]
77    pub const fn buffer_const<'a>(&'a mut self, data: &'a [T]) -> ConstBufferer<'a, TSIZE, T> {
78        assert!(data.len() <= u32::MAX as usize, "Data length cannot exceed u32::MAX");
79        ConstBufferer::new(&mut self.array, data)
80    }
81
82    /// Returns the length of the data buffered in the [`ConstBuffer`].
83    #[inline(always)]
84    pub const fn length(&self) -> Length {
85        self.array.length()
86    }
87
88    /// Clones the [`ConstBuffer`].
89    #[inline(always)]
90    pub const fn clone_const(&self) -> Self {
91        Self {
92            array: self.array.clone_const(),
93        }
94    }
95
96    /// Returns a reference to the currently buffered data.
97    #[inline(always)]
98    pub const fn current_state_const(&self) -> &ConstFixedArray<TSIZE, T> {
99        &self.array
100    }
101
102    /// Releases the remaining data from the bufferer.
103    #[inline(always)]
104    pub const fn release_const(self) -> ConstFixedArray<TSIZE, T> {
105        let mut array = self.array;
106        if array.length().as_usize() == TSIZE {
107            array.drain();
108        }
109        array
110    }
111}
112
113/// A helper struct for buffering the data in [`ConstBuffer`].
114#[repr(C)]
115#[must_use]
116pub struct ConstBufferer<'a, const TSIZE: usize, T: Sized + Copy + 'a> {
117    array: &'a mut ConstFixedArray<TSIZE, T>,
118    data: &'a [T],
119    data_offset: i64,
120}
121
122unsafe impl<const TSIZE: usize, T: ReprC + Sized + Copy> ReprC for ConstBufferer<'_, TSIZE, T> {
123    const CHECK: () = ();
124}
125
126impl<'a, const TSIZE: usize, T: Sized + Copy> ConstBufferer<'a, TSIZE, T> {
127    #[inline(always)]
128    const fn new(array: &'a mut ConstFixedArray<TSIZE, T>, data: &'a [T]) -> Self {
129        Self {
130            array,
131            data,
132            data_offset: 0,
133        }
134    }
135
136    /// Returns the next block of data, if available.
137    pub const fn next(&mut self) -> Option<&[T; TSIZE]> {
138        if self.data_offset as usize == self.data.len() {
139            return None;
140        }
141
142        unsafe {
143            let real_slice = subslice_const(self.data, self.data_offset as usize..self.data.len());
144            let real_len = real_slice.len() as u32;
145
146            if self.array.length().as_usize() == TSIZE {
147                self.array.drain();
148            }
149
150            let len = self.array.length().as_usize();
151
152            if (real_len as u64) + len as u64 >= TSIZE as u64 {
153                let missing = TSIZE - len;
154                let subslice = subslice_const(real_slice, 0..missing);
155                self.array.push_slice_const(subslice);
156                self.data_offset += missing as i64;
157                debug_assert!(self.array.length().as_usize() == TSIZE);
158                return Some(self.array.as_raw_slice_const());
159            }
160
161            self.array.push_slice_const(real_slice);
162            self.data_offset += real_len as i64;
163            debug_assert!(self.array.length().as_usize() < TSIZE);
164            return None;
165        }
166    }
167}