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