Skip to main content

osom_lib_arrays/fixed_array/
fixed_array.rs

1//! Holds the definition of [`FixedArray`].
2
3use core::{
4    borrow::{Borrow, BorrowMut},
5    hash::Hash,
6};
7
8use osom_lib_alloc::traits::Allocator;
9use osom_lib_primitives::length::Length;
10use osom_lib_reprc::traits::ReprC;
11use osom_lib_try_clone::TryClone;
12
13use crate::{
14    dynamic_array::internal_array::InternalArray,
15    errors::{ArrayError, ArrayIsEmptyError, ArrayTryCloneError},
16    traits::{ImmutableArray, MutableArray},
17};
18
19/// A fixed-capacity array. This type is similar to [`DynamicArray`][`crate::dynamic_array::DynamicArray`],
20/// except its capacity is fixed at runtime, instead of compile time. Internally it keeps a pointer
21/// to heap allocated data. That heap allocated data is never resized during the array's lifetime.
22///
23/// This type does require an allocator. But if the size exceeds capacity it simply returns an error.
24#[derive(Debug)]
25#[repr(transparent)]
26#[must_use]
27pub struct FixedArray<T, TAllocator>
28where
29    TAllocator: Allocator,
30{
31    inner: InternalArray<T, TAllocator>,
32}
33
34unsafe impl<T, TAllocator> Send for FixedArray<T, TAllocator>
35where
36    T: Sized + Send,
37    TAllocator: Allocator + Send,
38{
39}
40
41unsafe impl<T, TAllocator> Sync for FixedArray<T, TAllocator>
42where
43    T: Sized + Sync,
44    TAllocator: Allocator + Sync,
45{
46}
47
48unsafe impl<T, TAllocator> ReprC for FixedArray<T, TAllocator>
49where
50    T: ReprC,
51    TAllocator: Allocator,
52{
53    const CHECK: () = const {
54        osom_lib_reprc::hidden::is_reprc::<InternalArray<T, TAllocator>>();
55    };
56}
57
58impl<T, TAllocator> FixedArray<T, TAllocator>
59where
60    TAllocator: Allocator,
61{
62    /// Creates a new [`FixedArray`] with capacity and allocator.
63    /// This allocates memory only when `capacity > 0`.
64    ///
65    /// # Errors
66    ///
67    /// For details see [`ArrayError`].
68    #[inline(always)]
69    pub fn with_capacity_and_allocator(capacity: Length, allocator: TAllocator) -> Result<Self, ArrayError> {
70        let inner = InternalArray::<T, TAllocator>::with_capacity(capacity, allocator)?;
71        Ok(Self { inner })
72    }
73
74    /// Creates a new [`FixedArray`] with capacity and the default allocator.
75    /// This allocates memory only when `capacity > 0`.
76    ///
77    /// # Errors
78    ///
79    /// For details see [`ArrayError`].
80    #[inline(always)]
81    pub fn with_capacity(capacity: Length) -> Result<Self, ArrayError>
82    where
83        TAllocator: Default,
84    {
85        Self::with_capacity_and_allocator(capacity, TAllocator::default())
86    }
87
88    /// Creates a new [`FixedArray`] with a given size, generated through a given factory.
89    /// This allocates memory only when `size > 0`.
90    ///
91    /// # Errors
92    ///
93    /// For details see [`ArrayError`].
94    #[inline(always)]
95    pub fn with_factory<Factory: FnMut(usize) -> T>(size: Length, factory: Factory) -> Result<Self, ArrayError>
96    where
97        TAllocator: Default,
98    {
99        Self::with_factory_and_allocator(size, factory, TAllocator::default())
100    }
101
102    /// Creates a new [`FixedArray`] with a given size, generated through a given factory,
103    /// with a custom allocator. This allocates memory only when `size > 0`.
104    ///
105    /// # Errors
106    ///
107    /// For details see [`ArrayError`].
108    pub fn with_factory_and_allocator<Factory: FnMut(usize) -> T>(
109        size: Length,
110        mut factory: Factory,
111        allocator: TAllocator,
112    ) -> Result<Self, ArrayError> {
113        unsafe {
114            let mut array = Self::with_size_and_allocator_uninitialized(size, allocator)?;
115            let slice_mut_ptr = array.as_mut().as_mut_ptr();
116            for idx in 0..size.as_usize() {
117                slice_mut_ptr.add(idx).write(factory(idx));
118            }
119            Ok(array)
120        }
121    }
122
123    /// Creates a new [`FixedArray`] with a given size, but uninitialized.
124    ///
125    /// # Safety
126    ///
127    /// The underlying array is uninitialized and reading the data is UB, unless
128    /// initialized first.
129    ///
130    /// # Errors
131    ///
132    /// For details see [`ArrayError`].
133    #[inline(always)]
134    pub unsafe fn with_size_uninitialized(size: Length) -> Result<Self, ArrayError>
135    where
136        TAllocator: Default,
137    {
138        unsafe { Self::with_size_and_allocator_uninitialized(size, TAllocator::default()) }
139    }
140
141    /// Creates a new [`FixedArray`] with a given size and allocator, but uninitialized.
142    ///
143    /// # Safety
144    ///
145    /// The underlying array is uninitialized and reading the data is UB, unless
146    /// initialized first.
147    ///
148    /// # Errors
149    ///
150    /// For details see [`ArrayError`].
151    pub unsafe fn with_size_and_allocator_uninitialized(
152        size: Length,
153        allocator: TAllocator,
154    ) -> Result<Self, ArrayError> {
155        let inner = unsafe { InternalArray::with_size_uninitialized(size, allocator) }?;
156        Ok(Self { inner })
157    }
158}
159
160impl<T, TAllocator> ImmutableArray<T> for FixedArray<T, TAllocator>
161where
162    TAllocator: Allocator,
163{
164    #[inline(always)]
165    fn length(&self) -> Length {
166        self.inner.length()
167    }
168
169    #[inline(always)]
170    fn capacity(&self) -> Length {
171        self.inner.capacity()
172    }
173
174    #[inline(always)]
175    fn is_empty(&self) -> bool {
176        self.length().as_u32() == 0
177    }
178}
179
180impl<T, TAllocator> MutableArray<T> for FixedArray<T, TAllocator>
181where
182    TAllocator: Allocator,
183{
184    #[inline(always)]
185    fn try_push_array<const TSIZE: usize>(&mut self, arr: [T; TSIZE]) -> Result<(), ArrayError> {
186        if TSIZE > u32::MAX as usize {
187            return Err(ArrayError::LengthLimitExceeded);
188        }
189        if unsafe { self.inner.length().as_usize().unchecked_add(TSIZE) } > self.inner.capacity().as_usize() {
190            return Err(ArrayError::LengthLimitExceeded);
191        }
192        self.inner.try_push_array(arr)
193    }
194
195    #[inline(always)]
196    fn try_push_slice(&mut self, slice: &[T]) -> Result<(), ArrayTryCloneError>
197    where
198        T: TryClone,
199    {
200        let len = slice.len();
201        if len > u32::MAX as usize {
202            return Err(ArrayError::LengthLimitExceeded.into());
203        }
204
205        if unsafe { self.inner.length().as_usize().unchecked_add(len) } > self.inner.capacity().as_usize() {
206            return Err(ArrayError::LengthLimitExceeded.into());
207        }
208        self.inner.try_push_slice(slice)
209    }
210
211    #[inline(always)]
212    fn try_pop(&mut self) -> Result<T, ArrayIsEmptyError> {
213        self.inner.try_pop()
214    }
215}
216
217impl<T, TAllocator> Drop for FixedArray<T, TAllocator>
218where
219    TAllocator: Allocator,
220{
221    fn drop(&mut self) {
222        unsafe { self.inner.deallocate() };
223    }
224}
225
226impl<T, TAllocator> Clone for FixedArray<T, TAllocator>
227where
228    T: TryClone,
229    TAllocator: Allocator + TryClone,
230{
231    fn clone(&self) -> Self {
232        self.try_clone().expect("Failed to clone fixed array")
233    }
234}
235
236impl<T, TAllocator> TryClone for FixedArray<T, TAllocator>
237where
238    T: TryClone,
239    TAllocator: Allocator + TryClone,
240{
241    type Error = ArrayTryCloneError;
242
243    fn try_clone(&self) -> Result<Self, Self::Error> {
244        Ok(Self {
245            inner: self.inner.try_clone_perfect()?,
246        })
247    }
248}
249
250impl<T, TAllocator, Rhs> PartialEq<Rhs> for FixedArray<T, TAllocator>
251where
252    T: PartialEq,
253    TAllocator: Allocator,
254    Rhs: AsRef<[T]>,
255{
256    fn eq(&self, other: &Rhs) -> bool {
257        self.as_ref() == other.as_ref()
258    }
259}
260
261impl<T, TAllocator> Eq for FixedArray<T, TAllocator>
262where
263    T: Eq,
264    TAllocator: Allocator,
265{
266}
267
268impl<T, TAllocator> Hash for FixedArray<T, TAllocator>
269where
270    T: Hash,
271    TAllocator: Allocator,
272{
273    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
274        self.as_ref().hash(state);
275    }
276}
277
278impl<T, TAllocator> AsRef<[T]> for FixedArray<T, TAllocator>
279where
280    TAllocator: Allocator,
281{
282    fn as_ref(&self) -> &[T] {
283        self.inner.as_slice()
284    }
285}
286
287impl<T, TAllocator> AsMut<[T]> for FixedArray<T, TAllocator>
288where
289    TAllocator: Allocator,
290{
291    fn as_mut(&mut self) -> &mut [T] {
292        self.inner.as_slice_mut()
293    }
294}
295
296impl<T, TAllocator> Borrow<[T]> for FixedArray<T, TAllocator>
297where
298    TAllocator: Allocator,
299{
300    fn borrow(&self) -> &[T] {
301        self.as_ref()
302    }
303}
304
305impl<T, TAllocator> BorrowMut<[T]> for FixedArray<T, TAllocator>
306where
307    TAllocator: Allocator,
308{
309    fn borrow_mut(&mut self) -> &mut [T] {
310        self.as_mut()
311    }
312}