Skip to main content

osom_lib_arrays/fixed_array/
const_fixed_array.rs

1#![allow(clippy::cast_possible_truncation, clippy::new_without_default)]
2
3use core::{marker::PhantomData, mem::MaybeUninit};
4
5use osom_lib_primitives::length::Length;
6use osom_lib_reprc::traits::ReprC;
7
8use crate::{
9    const_helpers::{subslice_const, subslice_mut_const},
10    errors::{ArrayError, ArrayIsEmptyError},
11};
12
13/// A fixed-capacity array. This type is a const (as in: compile time) analogue of [`FixedArray`][`super::FixedArray`].
14///
15/// # Notes
16///
17/// For the array to be usable in const context, the `T` type needs to be `Copy` (which implies "no Drop").
18/// And in that case `ConstFixedArray` doesn't need to be (and actually cannot be to work in const context)
19/// `Drop` as well.
20///
21/// Unlike [`FixedArray`][`super::FixedArray`], this type does not implement
22/// any of the convenient traits such as [`ImmutableArray`][`crate::traits::ImmutableArray`]
23/// or `PartialEq`. These are NOT usable in const context anyway.
24#[repr(C)]
25#[must_use]
26pub struct ConstFixedArray<const TSIZE: usize, T: Sized + Copy> {
27    length: Length,
28    inner: [T; TSIZE],
29    _phantom: PhantomData<T>,
30}
31
32unsafe impl<const TSIZE: usize, T: ReprC + Sized + Copy> ReprC for ConstFixedArray<TSIZE, T> {
33    const CHECK: () = {
34        let () = T::CHECK;
35        let () = <PhantomData<T> as ReprC>::CHECK;
36        let () = <Length as ReprC>::CHECK;
37        let () = <[T; TSIZE] as ReprC>::CHECK;
38    };
39}
40
41impl<const TSIZE: usize, T: Sized + Copy> ConstFixedArray<TSIZE, T> {
42    /// Creates a new, empty [`ConstFixedArray`].
43    ///
44    /// # Panics
45    ///
46    /// This function will panic if the `TSIZE` is invalid, i.e.
47    /// when either `TSIZE` is 0 or exceeds [`Length::MAX_LENGTH`].
48    pub const fn new() -> Self {
49        const {
50            assert!(
51                TSIZE <= Length::MAX_LENGTH.as_usize(),
52                "TSIZE cannot exceed Length::MAX_LENGTH"
53            );
54            assert!(TSIZE > 0, "TSIZE cannot be 0");
55        }
56
57        let empty = unsafe { MaybeUninit::<[T; TSIZE]>::zeroed().assume_init() };
58
59        Self {
60            length: Length::ZERO,
61            inner: empty,
62            _phantom: PhantomData,
63        }
64    }
65
66    /// Returns the length of the [`ConstFixedArray`].
67    #[inline(always)]
68    pub const fn length(&self) -> Length {
69        unsafe { core::hint::assert_unchecked(self.length.as_usize() <= TSIZE) };
70        self.length
71    }
72
73    #[inline(always)]
74    pub const fn is_empty(&self) -> bool {
75        self.length.as_u32() == 0
76    }
77
78    /// Returns the capacity of the [`ConstFixedArray`]. This is `TSIZE`
79    /// as [`Length`].
80    #[inline(always)]
81    pub const fn capacity(&self) -> Length {
82        unsafe { Length::new_unchecked(TSIZE as u32) }
83    }
84
85    /// Returns the [`ConstFixedArray`] as immutable slice.
86    #[inline(always)]
87    pub const fn as_slice_const(&self) -> &[T] {
88        unsafe {
89            let result = subslice_const(&self.inner, 0..self.length.as_usize());
90            core::hint::assert_unchecked(result.len() <= TSIZE);
91            result
92        }
93    }
94
95    /// Returns the [`ConstFixedArray`] as mutable slice.
96    #[inline(always)]
97    pub const fn as_slice_mut_const(&mut self) -> &mut [T] {
98        unsafe {
99            let result = subslice_mut_const(&mut self.inner, 0..self.length.as_usize());
100            core::hint::assert_unchecked(result.len() <= TSIZE);
101            result
102        }
103    }
104
105    /// Pushes a raw array to the [`ConstFixedArray`].
106    ///
107    /// # Errors
108    ///
109    /// For errors see [`ArrayError`].
110    #[inline(always)]
111    pub const fn try_push_array_const<const TARRSIZE: usize>(&mut self, arr: [T; TARRSIZE]) -> Result<(), ArrayError> {
112        self.try_push_slice_const(&arr)
113    }
114
115    /// Pushes a raw array to the [`ConstFixedArray`].
116    ///
117    /// # Panics
118    ///
119    /// Panics if the array is full. Should be consistent with [`ConstFixedArray::try_push_array_const`].
120    pub const fn push_array_const<const TARRSIZE: usize>(&mut self, arr: [T; TARRSIZE]) {
121        match self.try_push_array_const(arr) {
122            Ok(()) => (),
123            Err(err) => match err {
124                ArrayError::LengthLimitExceeded => {
125                    panic!("Failed to push array due to length limit exceeded");
126                }
127                ArrayError::AllocationError => {
128                    panic!("Failed to push array due to allocation error");
129                }
130            },
131        }
132    }
133
134    /// Pushes a slice to the [`ConstFixedArray`].
135    ///
136    /// # Errors
137    ///
138    /// For errors see [`ArrayError`].
139    pub const fn try_push_slice_const(&mut self, slice: &[T]) -> Result<(), ArrayError> {
140        let len = self.length.as_usize();
141        let tsize = slice.len();
142        if len + tsize > TSIZE {
143            return Err(ArrayError::LengthLimitExceeded);
144        }
145
146        unsafe {
147            let dst = subslice_mut_const(&mut self.inner, len..len + tsize);
148            dst.copy_from_slice(slice);
149            self.length = Length::new_unchecked((len + tsize) as u32);
150        }
151        Ok(())
152    }
153
154    /// Pushes a slice to the [`ConstFixedArray`].
155    ///
156    /// # Panics
157    ///
158    /// Panics if the array is full. Should be consistent with [`ConstFixedArray::try_push_slice_const`].
159    #[inline(always)]
160    pub const fn push_slice_const(&mut self, slice: &[T]) {
161        match self.try_push_slice_const(slice) {
162            Ok(()) => (),
163            Err(err) => match err {
164                ArrayError::LengthLimitExceeded => {
165                    panic!("Failed to push slice due to length limit exceeded");
166                }
167                ArrayError::AllocationError => {
168                    panic!("Failed to push slice due to allocation error");
169                }
170            },
171        }
172    }
173
174    /// Pushes a single element to the [`ConstFixedArray`].
175    ///
176    /// # Errors
177    ///
178    /// For errors see [`ArrayError`].
179    #[inline(always)]
180    pub const fn try_push_const(&mut self, value: T) -> Result<(), ArrayError> {
181        self.try_push_array_const([value])
182    }
183
184    /// Pushes a single element to the [`ConstFixedArray`].
185    ///
186    /// # Panics
187    ///
188    /// Panics if the array is full. Should be consistent with [`ConstFixedArray::try_push_const`].
189    #[inline(always)]
190    pub const fn push_const(&mut self, value: T) {
191        self.push_array_const([value]);
192    }
193
194    /// Removes an element from the top of the [`ConstFixedArray`].
195    ///
196    /// # Errors
197    ///
198    /// Returns [`ArrayIsEmptyError`] when the array is empty.
199    pub const fn try_pop_const(&mut self) -> Result<T, ArrayIsEmptyError> {
200        let len = self.length.as_u32();
201        if len == 0 {
202            return Err(ArrayIsEmptyError);
203        }
204
205        let item = unsafe {
206            self.length = Length::new_unchecked(len - 1);
207            (&raw const self.inner).cast::<T>().add((len - 1) as usize).read()
208        };
209        Ok(item)
210    }
211
212    /// Removes an element from the top of the [`ConstFixedArray`].
213    ///
214    /// # Panics
215    ///
216    /// Panics if the array is empty. Should be consistent with [`ConstFixedArray::try_pop_const`].
217    #[inline(always)]
218    #[must_use]
219    pub const fn pop_const(&mut self) -> T {
220        match self.try_pop_const() {
221            Ok(item) => item,
222            Err(ArrayIsEmptyError) => panic!("Failed to pop due to array being empty"),
223        }
224    }
225
226    /// Clones the [`ConstFixedArray`].
227    #[inline(always)]
228    pub const fn clone_const(&self) -> Self {
229        Self {
230            length: self.length,
231            inner: self.inner,
232            _phantom: PhantomData,
233        }
234    }
235
236    /// Returns the raw immutable reference to the underlying array.
237    ///
238    /// # Safety
239    ///
240    /// This function is unsafe because only the buffer up to `Length`
241    /// is guaranteed to be filled with a valid data.
242    #[inline(always)]
243    pub const unsafe fn as_raw_slice_const(&self) -> &[T; TSIZE] {
244        &self.inner
245    }
246
247    /// Returns the raw mutable reference to the underlying array.
248    ///
249    /// # Safety
250    ///
251    /// This function is unsafe because only the buffer up to `Length`
252    /// is guaranteed to be filled with a valid data.
253    #[inline(always)]
254    pub const unsafe fn as_raw_slice_mut_const(&mut self) -> &mut [T; TSIZE] {
255        &mut self.inner
256    }
257
258    /// Drains the [`ConstFixedArray`]. This call sets the internal length of [`ConstFixedArray`] to 0,
259    /// making it effectively empty.
260    #[inline(always)]
261    pub const fn drain(&mut self) {
262        self.length = Length::ZERO;
263    }
264}