xuko_core/
array.rs

1#![expect(
2    unsafe_op_in_unsafe_fn,
3    reason = "Array requires unsafe code in some places"
4)]
5
6//! Variable length array
7
8use std::alloc;
9use std::ops::{Deref, DerefMut, Index, IndexMut};
10
11/// An error that can occur when creating an [`Array`]
12#[derive(Debug, thiserror::Error)]
13pub enum ArrayCreationError {
14    #[error("allocation error: {0}")]
15    AllocationError(String),
16
17    #[error("layout error: {0}")]
18    LayoutError(#[from] alloc::LayoutError),
19}
20
21/// Variable length array.
22///
23/// The purpose of this data structure is to provide an alternative to [`Vec`] with a static length.
24/// Use a normal array if you know the length at compile time.
25///
26/// A use for [`Array`] is where you need to read from a [`std::io::Read`]er to a [`Vec`] using
27/// the [`std::io::Read::read`] function, this wouldn't work because some elements of a [`Vec`] may be
28/// uninitialized whereas [`Array`] functions just like a static compile-time known array.
29///
30/// # Examples
31///
32/// ```
33/// use xuko_core::array::Array;
34///
35/// let mut array = Array::new(5).expect("Failed to create array");
36///
37/// array[0] = 1;
38/// array[1] = 2;
39/// array[2] = 3;
40/// array[3] = 4;
41/// array[4] = 5;
42/// ```
43pub struct Array<T> {
44    ptr: *mut T,
45    size: usize,
46}
47
48impl<T> Array<T> {
49    /// Allocate an [`Array`]
50    ///
51    /// Returns an [`ArrayCreationError`] when the allocation fails.
52    pub fn new(size: usize) -> Result<Self, ArrayCreationError> {
53        unsafe {
54            // TODO: Should this just panic on failed memory allocation?
55            // The chances of that happening are low anyways.
56            // Well if you want robust code it'd be annoying if this has the possibility of panicing.
57            let layout = alloc::Layout::array::<T>(size)?;
58            let ptr = alloc::alloc(layout) as *mut T;
59
60            if ptr.is_null() {
61                return Err(ArrayCreationError::AllocationError(
62                    "null pointer".to_owned(),
63                ));
64            }
65
66            Ok(Self { ptr, size })
67        }
68    }
69
70    /// Get the size of the array
71    #[must_use]
72    pub const fn size(&self) -> usize {
73        self.size
74    }
75
76    /// Return the array as an immutable pointer
77    #[must_use]
78    pub const fn as_ptr(&self) -> *const T {
79        self.ptr
80    }
81
82    /// Return the array as a mutable pointer
83    #[must_use]
84    pub const fn as_mut_ptr(&self) -> *mut T {
85        self.ptr
86    }
87
88    /// Set value at `index` to `value`
89    ///
90    /// # Panics
91    ///
92    /// Panics when `index` is out of bounds
93    pub fn set(&mut self, index: usize, value: T) {
94        if index >= self.size {
95            panic!("index out of bounds");
96        }
97
98        // SAFETY: it is ensured that whatever this pointer points to is inside of the `Array` by
99        // the previous statement
100        unsafe { *(self.ptr.add(index)) = value }
101    }
102
103    /// Get value at index `index`
104    #[must_use]
105    pub const fn get(&self, index: usize) -> Option<&T> {
106        if index >= self.size {
107            return None;
108        }
109
110        // SAFETY: it is ensured that whatever this pointer points to is inside of the `Array` by
111        // the previous statement
112        unsafe { Some(&(*(self.ptr.add(index)))) }
113    }
114
115    /// Get value at index `index`
116    #[must_use]
117    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
118        if index >= self.size {
119            return None;
120        }
121
122        // SAFETY: it is ensured that whatever this pointer points to is inside of the `Array` by
123        // the previous statement
124        unsafe { Some(&mut (*(self.ptr.add(index)))) }
125    }
126
127    /// Returns an immutable pointer to value at `index`.
128    ///
129    /// # Safety
130    ///
131    /// The pointer is obtained using unchecked pointer arithmetic.
132    #[must_use]
133    pub const unsafe fn get_ptr(&self, index: usize) -> *const T {
134        self.ptr.add(index) as *const T
135    }
136
137    /// Returns a mutable pointer to value at `index`.
138    ///
139    /// # Safety
140    ///
141    /// The pointer is obtained using unchecked pointer arithmetic.
142    #[must_use]
143    pub unsafe fn get_ptr_mut(&mut self, index: usize) -> *mut T {
144        self.ptr.add(index)
145    }
146
147    /// Return an iterator over the array
148    #[must_use]
149    pub fn iter(&self) -> iter::Iter<'_, T> {
150        iter::Iter::new(self)
151    }
152
153    /// Return a mutable iterator over the array
154    #[must_use]
155    pub fn iter_mut(&mut self) -> iter::IterMut<'_, T> {
156        iter::IterMut::new(self)
157    }
158}
159
160impl<T> Drop for Array<T> {
161    fn drop(&mut self) {
162        unsafe {
163            self.ptr.drop_in_place();
164        }
165    }
166}
167
168impl<T> Deref for Array<T> {
169    type Target = [T];
170
171    fn deref(&self) -> &Self::Target {
172        // SAFETY: the length is known in the struct so this wont result in UB
173        unsafe { std::slice::from_raw_parts(self.ptr, self.size) }
174    }
175}
176
177impl<T> DerefMut for Array<T> {
178    fn deref_mut(&mut self) -> &mut Self::Target {
179        // SAFETY: the length is known in the struct so this wont result in UB
180        unsafe { std::slice::from_raw_parts_mut(self.ptr, self.size) }
181    }
182}
183
184impl<T> AsRef<[T]> for Array<T> {
185    fn as_ref(&self) -> &[T] {
186        self
187    }
188}
189
190impl<T> AsMut<[T]> for Array<T> {
191    fn as_mut(&mut self) -> &mut [T] {
192        &mut *self
193    }
194}
195
196/// [`std::io::Write`] is implemented for any [`Array<u8>`]
197impl std::io::Write for Array<u8> {
198    // TODO: is this implementation even good?
199    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
200        let mut index = 0;
201        for b in buf {
202            self[index] = *b;
203            index += 1;
204        }
205        Ok(index + 1)
206    }
207
208    fn flush(&mut self) -> std::io::Result<()> {
209        Ok(())
210    }
211}
212
213impl<T> Index<usize> for Array<T> {
214    type Output = T;
215
216    fn index(&self, index: usize) -> &Self::Output {
217        self.get(index).expect("index out of bounds")
218    }
219}
220
221impl<T> IndexMut<usize> for Array<T> {
222    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
223        self.get_mut(index).expect("index out of bounds")
224    }
225}
226
227impl<T: Clone> Clone for Array<T> {
228    fn clone(&self) -> Self {
229        let mut array = Array::new(self.size).expect("allocation failed");
230
231        for (i, v) in self.iter().enumerate() {
232            array[i] = v.clone();
233        }
234
235        array
236    }
237}
238
239impl<T> IntoIterator for Array<T> {
240    type Item = T;
241    type IntoIter = iter::IntoIter<T>;
242
243    fn into_iter(self) -> Self::IntoIter {
244        iter::IntoIter::new(self)
245    }
246}
247
248impl<'a, T> IntoIterator for &'a Array<T> {
249    type Item = &'a T;
250    type IntoIter = iter::Iter<'a, T>;
251
252    fn into_iter(self) -> Self::IntoIter {
253        self.iter()
254    }
255}
256
257impl<'a, T> IntoIterator for &'a mut Array<T> {
258    type Item = &'a mut T;
259    type IntoIter = iter::IterMut<'a, T>;
260
261    fn into_iter(self) -> Self::IntoIter {
262        self.iter_mut()
263    }
264}
265
266pub mod iter {
267    use super::Array;
268    use std::marker::PhantomData;
269
270    /// Immutable [`Array`] iterator.
271    pub struct Iter<'a, T> {
272        _marker: PhantomData<&'a T>,
273        ptr: *const T,
274        end: *const T,
275    }
276
277    impl<'a, T> Iter<'a, T> {
278        pub(crate) fn new(array: &'a Array<T>) -> Self {
279            let ptr = array.ptr;
280            Self {
281                _marker: PhantomData,
282                ptr,
283                end: unsafe { ptr.add(array.size) },
284            }
285        }
286    }
287
288    impl<'a, T> Iterator for Iter<'a, T> {
289        type Item = &'a T;
290
291        fn next(&mut self) -> Option<Self::Item> {
292            if self.ptr == self.end {
293                None
294            } else {
295                unsafe {
296                    let ptr = self.ptr;
297                    self.ptr = self.ptr.add(1);
298                    Some(&*ptr)
299                }
300            }
301        }
302    }
303
304    /// Mutable [`Array`] iterator
305    pub struct IterMut<'a, T> {
306        _marker: PhantomData<&'a T>,
307        ptr: *mut T,
308        end: *mut T,
309    }
310
311    impl<'a, T> IterMut<'a, T> {
312        pub(crate) fn new(array: &'a Array<T>) -> Self {
313            let ptr = array.ptr;
314            Self {
315                _marker: PhantomData,
316                ptr,
317                end: unsafe { ptr.add(array.size) },
318            }
319        }
320    }
321
322    impl<'a, T> Iterator for IterMut<'a, T> {
323        type Item = &'a mut T;
324
325        fn next(&mut self) -> Option<Self::Item> {
326            if self.ptr == self.end {
327                None
328            } else {
329                unsafe {
330                    let ptr = self.ptr;
331                    self.ptr = self.ptr.add(1);
332                    Some(&mut *ptr)
333                }
334            }
335        }
336    }
337
338    /// Owned [`Array`] Iterator
339    pub struct IntoIter<T> {
340        _array: Array<T>,
341        ptr: *const T,
342        end: *const T,
343    }
344
345    impl<T> IntoIter<T> {
346        pub(crate) fn new(array: Array<T>) -> Self {
347            unsafe {
348                let ptr = array.ptr.cast_const();
349                let end = ptr.add(array.size);
350                Self {
351                    _array: array,
352                    ptr,
353                    end,
354                }
355            }
356        }
357    }
358
359    impl<T> Iterator for IntoIter<T> {
360        type Item = T;
361
362        fn next(&mut self) -> Option<Self::Item> {
363            if self.ptr == self.end {
364                None
365            } else {
366                unsafe {
367                    let ptr = self.ptr;
368                    self.ptr = self.ptr.add(1);
369                    Some(ptr.read())
370                }
371            }
372        }
373    }
374}
375
376#[cfg(test)]
377mod tests {
378    use super::*;
379
380    #[test]
381    fn array_basics() {
382        let mut array = Array::new(5).expect("failed to allocate");
383        array[0] = 1;
384        array[1] = 2;
385        array[2] = 3;
386        array[3] = 4;
387        array[4] = 5;
388
389        for (i, v) in array.iter().enumerate() {
390            match i {
391                0 => assert_eq!(*v, 1),
392                1 => assert_eq!(*v, 2),
393                2 => assert_eq!(*v, 3),
394                3 => assert_eq!(*v, 4),
395                4 => assert_eq!(*v, 5),
396                _ => unreachable!(),
397            }
398        }
399    }
400}