osom_lib_arrays/
array.rs

1#![allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
2
3use core::{alloc::Layout, marker::PhantomData, ops::Deref};
4
5use osom_lib_alloc::{AllocatedMemory as _, Allocator};
6
7#[cfg(feature = "std_alloc")]
8use osom_lib_alloc::StdAllocator;
9
10use osom_lib_primitives::Length;
11
12use crate::errors::ArrayConstructionError;
13
14/// Represents a fixed-size array but allocated on the heap.
15///
16/// In its essence very similar to [`DynamicArray`][`crate::DynamicArray`]
17/// except it cannot grow or shrink in size. The content is still mutable though.
18///
19/// Cloning of [`Array`] will allocate new memory and copy the data into it.
20#[must_use]
21pub struct Array<
22    T,
23    #[cfg(feature = "std_alloc")] TAllocator = StdAllocator,
24    #[cfg(not(feature = "std_alloc"))] TAllocator,
25> where
26    TAllocator: Allocator,
27{
28    pub(crate) data: TAllocator::TAllocatedMemory,
29    pub(crate) len: Length,
30    pub(crate) allocator: TAllocator,
31    pub(crate) phantom: PhantomData<T>,
32}
33
34impl<T, TAllocator> Array<T, TAllocator>
35where
36    TAllocator: Allocator,
37{
38    pub const MAX_LENGTH: usize = Length::MAX;
39
40    #[inline(always)]
41    const fn layout(len: usize) -> Layout {
42        let byte_size = len * size_of::<T>();
43        let alignment = align_of::<T>();
44        unsafe { Layout::from_size_align_unchecked(byte_size, alignment) }
45    }
46
47    /// Creates a new empty [`Array`].
48    #[inline(always)]
49    pub fn empty() -> Self {
50        Self::empty_with_allocator(TAllocator::default())
51    }
52
53    /// Creates a new empty [`Array`] with the given allocator.
54    ///
55    ///
56    /// # Notes
57    /// The allocator won't matter in the case of empty array.
58    /// Neither allocation nor deallocation will happen in this case.
59    /// We keep this method for consistency only.
60    #[inline(always)]
61    pub fn empty_with_allocator(allocator: TAllocator) -> Self {
62        Self {
63            data: unsafe { allocator.dangling::<T>() },
64            len: Length::ZERO,
65            allocator: allocator,
66            phantom: PhantomData,
67        }
68    }
69
70    /// Creates a new [`Array`] from a fixed-size array and allocator.
71    ///
72    /// # Notes
73    ///
74    /// This will allocate memory if `N > 0` and will copy the data
75    /// into it. It will move ownership as well.
76    ///
77    /// # Errors
78    ///
79    /// For details see [`ArrayConstructionError`].
80    pub fn from_array_with_allocator<const N: usize>(
81        data: [T; N],
82        allocator: TAllocator,
83    ) -> Result<Self, ArrayConstructionError> {
84        if N == 0 {
85            return Ok(Self::empty_with_allocator(allocator));
86        }
87
88        if N > Length::MAX {
89            return Err(ArrayConstructionError::ArrayTooLong);
90        }
91
92        let layout = Self::layout(N);
93        let memory = allocator.allocate(layout)?;
94
95        let array = Self {
96            data: memory,
97            len: unsafe { Length::new_unchecked(N as i32) },
98            allocator: allocator,
99            phantom: PhantomData,
100        };
101
102        let slice_ptr = data.as_ptr();
103        unsafe {
104            array.ptr().copy_from_nonoverlapping(slice_ptr, N);
105        }
106
107        core::mem::forget(data);
108
109        Ok(array)
110    }
111
112    /// Creates a new [`Array`] from a fixed-size array.
113    ///
114    /// # Notes
115    ///
116    /// This will allocate memory if `N > 0` and will copy the data
117    /// into it. It will move ownership as well.
118    ///
119    /// # Errors
120    ///
121    /// For details see [`ArrayConstructionError`].
122    pub fn from_array<const N: usize>(data: [T; N]) -> Result<Self, ArrayConstructionError> {
123        Self::from_array_with_allocator(data, TAllocator::default())
124    }
125
126    /// Returns the length of the [`Array`].
127    #[inline(always)]
128    pub const fn len(&self) -> Length {
129        self.len
130    }
131
132    /// Returns `true` if the [`Array`] is empty, `false` otherwise.
133    #[inline(always)]
134    pub const fn is_empty(&self) -> bool {
135        self.len.value() == 0
136    }
137
138    /// Converts the [`Array`] into a slice.
139    #[inline(always)]
140    pub fn as_slice(&self) -> &[T] {
141        unsafe { core::slice::from_raw_parts(self.ptr(), self.len.into()) }
142    }
143
144    /// Converts the [`Array`] into a mutable slice.
145    #[inline(always)]
146    pub fn as_slice_mut(&mut self) -> &mut [T] {
147        unsafe { core::slice::from_raw_parts_mut(self.ptr(), self.len.into()) }
148    }
149
150    #[inline(always)]
151    fn ptr(&self) -> *mut T {
152        unsafe { self.data.as_ptr() }
153    }
154}
155
156impl<T, TAllocator> Array<T, TAllocator>
157where
158    T: Clone,
159    TAllocator: Allocator,
160{
161    /// Creates a new [`Array`] from a slice and allocator.
162    ///
163    /// # Notes
164    ///
165    /// This will allocate memory if `len > 0` and will copy the data
166    /// into it. It will move ownership as well.
167    ///
168    /// # Errors
169    ///
170    /// For details see [`ArrayConstructionError`].
171    pub fn from_slice_and_allocator(slice: &[T], allocator: TAllocator) -> Result<Self, ArrayConstructionError> {
172        let len = slice.len();
173        if len == 0 {
174            return Ok(Self::empty_with_allocator(allocator));
175        }
176
177        if len > Length::MAX {
178            return Err(ArrayConstructionError::ArrayTooLong);
179        }
180
181        let layout = Self::layout(len);
182        let memory = allocator.allocate(layout)?;
183
184        let array = Self {
185            data: memory,
186            len: unsafe { Length::new_unchecked(len as i32) },
187            allocator: allocator,
188            phantom: PhantomData,
189        };
190
191        unsafe {
192            let mut target = array.ptr();
193            for item in slice {
194                target.write(item.clone());
195                target = target.add(1);
196            }
197        }
198
199        Ok(array)
200    }
201
202    /// Creates a new [`Array`] from a slice.
203    ///
204    /// # Notes
205    ///
206    /// This will allocate memory if `len > 0` and will copy the data
207    /// into it. It will move ownership as well.
208    ///
209    /// # Errors
210    ///
211    /// For details see [`ArrayConstructionError`].
212    pub fn from_slice(slice: &[T]) -> Result<Self, ArrayConstructionError> {
213        Self::from_slice_and_allocator(slice, TAllocator::default())
214    }
215}
216
217impl<T, TAllocator> Drop for Array<T, TAllocator>
218where
219    TAllocator: Allocator,
220{
221    fn drop(&mut self) {
222        let len: usize = self.len.into();
223        if len == 0 {
224            return;
225        }
226
227        unsafe {
228            if core::mem::needs_drop::<T>() {
229                let mut ptr = self.ptr();
230                let end = ptr.add(len);
231                while ptr < end {
232                    core::ptr::drop_in_place(ptr);
233                    ptr = ptr.add(1);
234                }
235            }
236
237            let layout = Self::layout(len);
238            let data = core::ptr::read(&self.data);
239            data.deallocate(layout);
240        }
241    }
242}
243
244impl<T, TAllocator> Clone for Array<T, TAllocator>
245where
246    T: Clone,
247    TAllocator: Allocator,
248{
249    fn clone(&self) -> Self {
250        Self::from_slice_and_allocator(self.as_slice(), self.allocator.clone()).unwrap()
251    }
252}
253
254impl<T, TAllocator> Default for Array<T, TAllocator>
255where
256    TAllocator: Allocator,
257{
258    fn default() -> Self {
259        Self::empty()
260    }
261}
262
263impl<T, TAllocator> Deref for Array<T, TAllocator>
264where
265    TAllocator: Allocator,
266{
267    type Target = [T];
268
269    fn deref(&self) -> &Self::Target {
270        self.as_slice()
271    }
272}
273
274impl<T, TAllocator> core::ops::DerefMut for Array<T, TAllocator>
275where
276    TAllocator: Allocator,
277{
278    fn deref_mut(&mut self) -> &mut Self::Target {
279        self.as_slice_mut()
280    }
281}
282
283impl<T, TAllocator> AsRef<[T]> for Array<T, TAllocator>
284where
285    TAllocator: Allocator,
286{
287    fn as_ref(&self) -> &[T] {
288        self.as_slice()
289    }
290}
291
292impl<T, TAllocator> AsMut<[T]> for Array<T, TAllocator>
293where
294    TAllocator: Allocator,
295{
296    fn as_mut(&mut self) -> &mut [T] {
297        self.as_slice_mut()
298    }
299}