Skip to main content

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