standard_lib/collections/contiguous/array/
array.rs

1use std::alloc::{self, Layout};
2use std::borrow::{Borrow, BorrowMut};
3use std::fmt::{self, Debug, Display, Formatter};
4use std::hash::{Hash, Hasher};
5use std::iter::{self, TrustedLen};
6use std::marker::PhantomData;
7use std::mem::{self, MaybeUninit};
8use std::ops::{Deref, DerefMut};
9use std::ptr::{self, NonNull};
10use std::slice;
11
12use crate::util::error::CapacityOverflow;
13use crate::util::result::ResultExtension;
14
15const MAX_SIZE: usize = isize::MAX as usize;
16
17// TODO: Add try methods.
18
19/// An implementation of an array that has an fixed size at runtime. Similar to a
20/// [`Box<[T]>`](Box<T>) and not intended as an equivalent to Rust's primitive `[T; N]` type,
21/// which is sized at compile time.
22///
23/// # Time Complexity
24/// For this analysis of time complexity, variables are defined as follows:
25/// - `n`: The number of items in the Array.
26/// - `i`: The index of the item in question.
27///
28/// | Method | Complexity |
29/// |-|-|
30/// | `get` | `O(1)` |
31/// | `size` | `O(1)` |
32/// | `realloc` | `O(n)`*, `O(1)` |
33/// | `contains` | `O(n)` |
34///
35/// \* It might be possible to get an `O(1)` reallocation, but I don't believe it is very likely.
36pub struct Array<T> {
37    pub(crate) ptr: NonNull<T>,
38    pub(crate) size: usize,
39    pub(crate) _phantom: PhantomData<T>,
40}
41
42impl<T> Array<T> {
43    /// Creates a new Array with size 0.
44    ///
45    /// This method isn't very helpful in most cases because the size remains zero after
46    /// initialization. See [`Array::new_uninit`] or [`Array::from`] for preferred methods of
47    /// initialization.
48    ///
49    /// # Examples
50    /// ```
51    /// # use standard_lib::collections::contiguous::Array;
52    /// let arr: Array<u8> = Array::new();
53    /// assert_eq!(arr.size(), 0);
54    /// assert_eq!(&*arr, &[]);
55    /// ```
56    pub fn new() -> Array<T> {
57        // SAFETY: There are no values, so they are all initialized.
58        unsafe { Self::new_uninit(0).assume_init() }
59    }
60
61    /// Creates a new Array of [`MaybeUninit<T>`] with the provided `size`. All values are
62    /// uninitialized.
63    ///
64    /// # Panics
65    /// Panics if memory layout size exceeds [`isize::MAX`].
66    ///
67    /// # Examples
68    /// ```
69    /// # use standard_lib::collections::contiguous::Array;
70    /// # use std::mem::MaybeUninit;
71    /// let arr: Array<MaybeUninit<u8>> = Array::new_uninit(5);
72    /// assert_eq!(arr.size(), 5);
73    /// ```
74    pub fn new_uninit(size: usize) -> Array<MaybeUninit<T>> {
75        let layout = Array::<MaybeUninit<T>>::make_layout(size);
76        let ptr = Array::<MaybeUninit<T>>::make_ptr(layout);
77
78        Array {
79            ptr,
80            size,
81            _phantom: PhantomData,
82        }
83    }
84
85    /// Decomposes an `Array<T>` into its raw components, a [`NonNull<T>`] pointer to the contained
86    /// data and a [`usize`] representing the size.
87    ///
88    /// Returns the pointer to the underlying data and the number of elements in the Array.
89    ///
90    /// # Safety
91    /// After calling this function, the caller is responsible for the safety of the allocated data.
92    /// The parts can be used to reconstruct an Array with [`Array::from_parts`], allowing it to be
93    /// used again and dropped normally.
94    ///
95    /// # Examples
96    /// See [`Array::from_parts`].
97    pub const fn into_parts(self) -> (NonNull<T>, usize) {
98        let ret = (self.ptr, self.size);
99        mem::forget(self);
100        ret
101    }
102
103    /// Creates an `Array<T>` from its raw components, a [`NonNull<T>`] pointer to the contained
104    /// data and a [`usize`] representing the size.
105    ///
106    /// # Safety
107    /// This is extremely unsafe, nothing is checked during construction.
108    ///
109    /// For the produced value to be valid:
110    /// - `ptr` needs to be a currently and correctly allocated pointer within the global allocator.
111    /// - `ptr` needs to refer to `size` properly initialized values of `T`.
112    /// - `size` needs to be less than or equal to [`isize::MAX`] / `size_of::<T>()`.
113    ///
114    /// # Examples
115    /// ```
116    /// # use standard_lib::collections::contiguous::Array;
117    /// let arr = Array::from_iter_sized([1_u8, 2, 3].into_iter());
118    /// let (ptr, size) = arr.into_parts();
119    /// assert_eq!(
120    ///     unsafe { Array::from_parts(ptr, size) },
121    ///     Array::from_iter_sized([1, 2, 3].into_iter())
122    /// );
123    /// ```
124    pub const unsafe fn from_parts(ptr: NonNull<T>, size: usize) -> Array<T> {
125        Array {
126            ptr,
127            size,
128            _phantom: PhantomData,
129        }
130    }
131
132    /// Returns the size of the Array.
133    ///
134    /// # Examples
135    /// ```
136    /// # use standard_lib::collections::contiguous::Array;
137    /// let arr = Array::from_iter_sized([1, 2, 3].into_iter());
138    /// assert_eq!(arr.size(), 3);
139    /// ```
140    pub const fn size(&self) -> usize {
141        self.size
142    }
143
144    pub fn as_non_null(&self) -> NonNull<T> {
145        self.ptr
146    }
147
148    /// Interprets self as an `Array<MaybeUninit<T>>`. Although it may not seem very useful by
149    /// itself, this method acts as a counterpart to [`Array::assume_init`] and allows
150    /// [`Array::realloc`] to be called on a previously initialized Array.
151    ///
152    /// # Examples
153    /// ```
154    /// # use standard_lib::collections::contiguous::Array;
155    /// # use std::mem::MaybeUninit;
156    /// let mut arr = Array::from_iter_sized([1_u8, 2, 3].into_iter());
157    /// let mut new_arr = arr.forget_init();
158    ///
159    /// new_arr.realloc(4);
160    /// new_arr[3] = MaybeUninit::new(4);
161    ///
162    /// // SAFETY: All values in new_arr are now initialized.
163    /// arr = unsafe { new_arr.assume_init() };
164    ///
165    /// assert_eq!(&*arr, &[1, 2, 3, 4]);
166    /// ```
167    pub fn forget_init(self) -> Array<MaybeUninit<T>> {
168        // SAFETY: Array<T> has the same layout as Array<MaybeUninit<T>>.
169        unsafe { mem::transmute(self) }
170    }
171
172    /// Interprets &mut self as an `&mut Array<MaybeUninit<T>>`. See [`Array::forget_init`].
173    ///
174    /// # Safety
175    /// When this mutable reference is dropped, self still need to be a valid and initialized
176    /// `Array<T>`. Failing to do so is undefined behavior, as it is effectively the same as calling
177    /// [`Array::assume_init`].
178    pub unsafe fn forget_init_mut(&mut self) -> &mut Array<MaybeUninit<T>> {
179        // SAFETY: &mut Array<T> has the same layout as &mut Array<MaybeUninit<T>>.
180        unsafe { mem::transmute(self) }
181    }
182
183    /// Interprets &self as an `&Array<MaybeUninit<T>>`. See [`Array::forget_init`].
184    pub fn forget_init_ref(&self) -> &Array<MaybeUninit<T>> {
185        // SAFETY: &Array<T> has the same layout as &Array<MaybeUninit<T>>.
186        unsafe { mem::transmute(self) }
187    }
188
189    /// Reallocate self with `new_size`, filling any extra elements with a value produced by
190    /// an invocation of `producer`.
191    ///
192    /// # Panics
193    /// Panics if the memory layout of the new allocation would have a size that exceeds
194    /// [`isize::MAX`]. (`new_size * size_of::<T>() > isize::MAX`)
195    pub fn realloc_with<F: Fn() -> T>(&mut self, producer: F, new_size: usize) {
196        if new_size < self.size {
197            for i in new_size..self.size {
198                // SAFETY: new_size is less than self.size, so i is in range of the Array.
199                // self.size * size_of::<T>() can't have overflown isize::MAX. self is  valid and
200                // fully initialized at this point.
201                unsafe {
202                    // We need to use self here so that we can drop initialized T values in place,
203                    // even though we're invalidating self. Can't drop a *mut MaybeUninit<T> in
204                    // place while assuming that it is initialized.
205                    ptr::drop_in_place(self.ptr.add(i).as_ptr());
206                }
207            }
208
209            // SAFETY: self is actually the one that is unsafe here, because it doesn't know that it
210            // is no longer entirely initialized. Forgetting this false information is the correct
211            // thing to do.
212            let wip_arr = unsafe { self.forget_init_mut() };
213
214            wip_arr.realloc(new_size);
215            // FIXME: What happens if the reallocation fails? Values have already been dropped.
216        } else {
217            let old_size = self.size;
218            // SAFETY: We are lying to self about being initialized, however a panic can't occur
219            // after the allocation so that isn't a problem.
220            let wip_arr = unsafe { self.forget_init_mut() };
221
222            wip_arr.realloc(new_size);
223
224            for i in old_size..new_size {
225                // SAFETY: size > isize::MAX / size_of::<T>() is already guarded against and all
226                // possible values are within the allocated range of the Array.
227                unsafe {
228                    wip_arr.ptr.add(i).write(MaybeUninit::new(producer()))
229                }
230            }
231        }
232
233        // wip_arr is a transmuted mutable reference to self, so we don't need to do anything to
234        // return ownership to self, now that the contents are valid.
235    }
236
237    /// Creates an Array from a type which implements [`IntoIterator`] and creates an
238    /// [`ExactSizeIterator`].
239    ///
240    /// # Panics
241    /// Panics if memory layout size exceeds [`isize::MAX`].
242    ///
243    /// # Examples
244    /// ```
245    /// # use standard_lib::collections::contiguous::Array;
246    /// let arr = Array::from_iter_sized([1, 2, 3].into_iter());
247    /// assert_eq!(&*arr, [1, 2, 3]);
248    /// ```
249    pub fn from_iter_sized<I>(value: I) -> Array<T>
250    where
251        I: Iterator<Item = T> + ExactSizeIterator + TrustedLen,
252    {
253        let size = value.len();
254        let arr = Self::new_uninit(size);
255
256        for (index, item) in value.enumerate() {
257            // SAFETY: size > isize::MAX / size_of::<T>() is already guarded against and all
258            // possible values are within the allocated range of the Array.
259            unsafe {
260                arr.ptr.add(index).write(MaybeUninit::new(item))
261            }
262        }
263
264        // SAFETY: All values are initialized.
265        unsafe { arr.assume_init() }
266    }
267
268    /// A helper function to create a [`Layout`] for use during allocation, containing `size` number
269    /// of elements of type `T`.
270    ///
271    /// # Panics
272    /// Panics if memory layout size exceeds [`isize::MAX`].
273    pub(crate) fn make_layout(size: usize) -> Layout {
274        Layout::array::<T>(size).or(Err(CapacityOverflow)).throw()
275    }
276
277    /// A helper function to create a [`NonNull`] for the provided [`Layout`]. Returns a dangling
278    /// pointer for a zero-sized layout.
279    ///
280    /// # Errors
281    /// In the event of an allocation error, this method calls [`alloc::handle_alloc_error`] as
282    /// recommended, to avoid new allocations rather than panicking.
283    pub(crate) fn make_ptr(layout: Layout) -> NonNull<T> {
284        if layout.size() == 0 {
285            NonNull::dangling()
286        } else {
287            NonNull::new(
288                // SAFETY: Zero-sized layouts have been guarded against.
289                unsafe { alloc::alloc(layout).cast() }
290            ).unwrap_or_else(|| alloc::handle_alloc_error(layout))
291        }
292    }
293}
294
295impl<T: Copy> Array<T> {
296    /// Creates a new `Array<T>` with `count` copies of `item`.
297    ///
298    /// # Panics
299    /// Panics if memory layout size exceeds [`isize::MAX`].
300    ///
301    /// # Examples
302    /// ```
303    /// # use standard_lib::collections::contiguous::Array;
304    /// let arr = Array::repeat_item(5, 3);
305    /// assert_eq!(arr.size(), 3);
306    /// assert_eq!(&*arr, &[5, 5, 5]);
307    /// ```
308    pub fn repeat_item(item: T, count: usize) -> Array<T> {
309        Array::from_iter_sized(iter::repeat_n(item, count))
310    }
311
312    /// Reallocate self with `new_size`, filling any extra elements with a copy of `item`.
313    ///
314    /// # Panics
315    /// Panics if the memory layout of the new allocation would have a size that exceeds
316    /// [`isize::MAX`]. (`new_size * size_of::<T>() > isize::MAX`)
317    pub fn realloc_with_copy(&mut self, item: T, new_size: usize) {
318        self.realloc_with(|| item, new_size);
319    }
320}
321
322impl<T: Default> Array<T> {
323    /// Creates a new `Array<T>` by repeating the default value of `T` `count` times.
324    ///
325    /// # Panics
326    /// Panics if memory layout size exceeds [`isize::MAX`].
327    pub fn repeat_default(count: usize) -> Array<T> {
328        Array::from_iter_sized(iter::repeat_with(|| T::default()).take(count))
329    }
330
331    /// Reallocate self with `new_size`, filling any extra elements with the default value of `T`.
332    ///
333    /// # Panics
334    /// Panics if the memory layout of the new allocation would have a size that exceeds
335    /// [`isize::MAX`]. (`new_size * size_of::<T>() > isize::MAX`)
336    pub fn realloc_with_default(&mut self, new_size: usize) {
337        self.realloc_with(|| T::default(), new_size);
338    }
339}
340
341impl<T> Array<MaybeUninit<T>> {
342    /// Converts a `Array<MaybeUninit<T>>` to `MaybeUninit<Array<T>>`.
343    pub fn transpose(self) -> MaybeUninit<Array<T>> {
344        // SAFETY: Array<MaybeUninit<T>> has the same layout as MaybeUninit<Array<T>>.
345        unsafe { mem::transmute(self) }
346    }
347
348    /// Converts a `&mut Array<MaybeUninit<T>>` to `&mut MaybeUninit<Array<T>>`.
349    pub fn transpose_mut(&mut self) -> &mut MaybeUninit<Array<T>> {
350        // SAFETY: &mut Array<MaybeUninit<T>> has the same layout as &mut MaybeUninit<Array<T>>.
351        unsafe { mem::transmute(self) }
352    }
353
354    /// Converts a `&Array<MaybeUninit<T>>` to `&MaybeUninit<Array<T>>`.
355    pub fn transpose_ref(&self) -> &MaybeUninit<Array<T>> {
356        // SAFETY: &Array<MaybeUninit<T>> has the same layout as &MaybeUninit<Array<T>>.
357        unsafe { mem::transmute(self) }
358    }
359
360    /// Assume that all values of an `Array<MaybeUninit<T>>` are initialized.
361    ///
362    /// # Safety
363    /// It is up to the caller to guarantee that the Array is properly initialized. Failing to do so
364    /// is undefined behavior.
365    ///
366    /// # Examples
367    /// ```
368    /// # use standard_lib::collections::contiguous::Array;
369    /// # use std::mem::MaybeUninit;
370    /// let mut arr = Array::new_uninit(5);
371    /// for i in 0..5 {
372    ///     arr[i] = MaybeUninit::new(i);
373    /// }
374    /// assert_eq!(&*unsafe { arr.assume_init() }, &[0, 1, 2, 3, 4]);
375    /// ```
376    pub unsafe fn assume_init(self) -> Array<T> {
377        // SAFETY: There are no safety guarantees here, responsibility it passed to the caller.
378        unsafe { self.transpose().assume_init() }
379    }
380
381    /// Assume that all values of an `&mut Array<MaybeUninit<T>>` are initialized.
382    ///
383    /// # Safety
384    /// It is up to the caller to guarantee that the Array is properly initialized. Failing to do so
385    /// is undefined behavior.
386    pub unsafe fn assume_init_mut(&mut self) -> &mut Array<T> {
387        // SAFETY: There are no safety guarantees here, responsibility it passed to the caller.
388        unsafe { self.transpose_mut().assume_init_mut() }
389    }
390
391    /// Assume that all values of an `&Array<MaybeUninit<T>>` are initialized.
392    ///
393    /// # Safety
394    /// It is up to the caller to guarantee that the Array is properly initialized. Failing to do so
395    /// is undefined behavior.
396    pub unsafe fn assume_init_ref(&self) -> &Array<T> {
397        // SAFETY: There are no safety guarantees here, responsibility it passed to the caller.
398        unsafe { self.transpose_ref().assume_init_ref() }
399    }
400
401    /// Reallocate the Array to have size equal to new_size, with new locations uninitialized.
402    /// Several checks are performed first to ensure that an allocation is actually required.
403    ///
404    /// # Panics
405    /// Panics if the memory layout of the new allocation would have a size that exceeds
406    /// [`isize::MAX`]. (`new_size * size_of::<T>() > isize::MAX`)
407    pub fn realloc(&mut self, new_size: usize) {
408        let new_ptr = match (self.size, new_size) {
409            (_, _) if size_of::<T>() == 0 => {
410                // I didn't think that handling zero-sized types would be quite so easy. Turns out
411                // the solution is: just don't allocate anything. **tada**
412                // ptr::read (and functions which rely on it) handle zero sized types for us, so as
413                // long as we ensure that alloc and realloc are being used properly, we don't need
414                // to worry about allocations at all.
415
416                // We still need to return the existing dangling pointer so that the Array's size
417                // can be updated.
418                self.ptr
419            },
420            (old, new) if old == new => {
421                // The capacities are equal, do nothing there is no need to reallocate.
422                // SAFETY: Array<T> has the same layout as Array<MaybeUninit<T>>.
423                return;
424            },
425            (_, 0) => {
426                // If the new size is zero, we just need to deallocate it and return a dangling
427                // pointer.
428                let layout = Array::<MaybeUninit<T>>::make_layout(self.size);
429
430                // SAFETY: ptr is always allocated in the global allocator and layout is the same as
431                // when allocated. Zero-sized layouts are guarded against by the first two branches.
432                unsafe { alloc::dealloc(self.ptr.as_ptr().cast(), layout); }
433
434                NonNull::dangling()
435            },
436            (_, new) if new.checked_mul(size_of::<T>())
437                .is_none_or(|size| size > MAX_SIZE) => {
438                // Throw an error if the new size would overflow.
439                Err(CapacityOverflow).throw()
440            },
441            (0, _) => {
442                // If the Array previously had a capacity of zero, we need a new allocation.
443                let layout = Array::<MaybeUninit<T>>::make_layout(new_size);
444
445                // SAFETY: Layout will have non-zero size because both 0 capacity and zero-sized
446                // types are guarded against.
447                let raw_ptr: *mut MaybeUninit<T> = unsafe { alloc::alloc(layout).cast() };
448
449                NonNull::new(raw_ptr).unwrap_or_else(|| alloc::handle_alloc_error(layout))
450            },
451            (_, _) => {
452                // Otherwise, use realloc to handle moving or in-place size changing.
453                let layout = Array::<MaybeUninit<T>>::make_layout(self.size);
454
455                // SAFETY: The same layout and allocator are used for the allocation, and the new
456                // layout size is > 0 and <= isize::MAX.
457                let raw_ptr: *mut MaybeUninit<T> = unsafe {
458                    alloc::realloc(
459                        self.ptr.as_ptr().cast(),
460                        layout,
461                        new_size * size_of::<T>()
462                    ).cast()
463                };
464
465                NonNull::new(raw_ptr).unwrap_or_else(|| alloc::handle_alloc_error(layout))
466            },
467        };
468
469        self.ptr = new_ptr;
470        self.size = new_size;
471    }
472}
473
474impl<T> Default for Array<T> {
475    fn default() -> Self {
476        Self::new()
477    }
478}
479
480impl<T> Drop for Array<T> {
481    fn drop(&mut self) {
482        for i in 0..self.size {
483            // SAFETY: The pointer is nonnull, as well as properly aligned, initialized and
484            // ready to drop.
485            // SAFETY: count > isize::MAX / size_of::<T>() is already guarded against and
486            // all possible values are within the allocated range of the Array.
487            unsafe {
488                ptr::drop_in_place(self.ptr.add(i).as_ptr());
489            }
490        }
491
492        let layout = Array::<T>::make_layout(self.size);
493
494        if layout.size() != 0 {
495            // SAFETY: ptr is always allocated in the global allocator and layout is the same as
496            // when allocated. Zero-sized layouts aren't allocated and are guarded against
497            // deallocation.
498            unsafe {
499                alloc::dealloc(self.ptr.as_ptr().cast(), layout)
500            }
501        }
502    }
503}
504
505impl<T> Deref for Array<T> {
506    type Target = [T];
507
508    fn deref(&self) -> &Self::Target {
509        // SAFETY: The held data uses Layout::array(size) and is therefore valid and properly
510        // aligned for (size * mem::size_of::<T>()) bytes. Data is properly initialized and has a
511        // length no greater than isize::MAX. Array's safe API doesn't provide access to raw
512        // pointers, so the borrow checker prevents mutation throughout 'a.
513        unsafe {
514            slice::from_raw_parts(self.ptr.as_ptr(), self.size)
515        }
516    }
517}
518
519impl<T> DerefMut for Array<T> {
520    fn deref_mut(&mut self) -> &mut Self::Target {
521        // SAFETY: The held data uses Layout::array(size) and is therefore valid and properly
522        // aligned for (size * mem::size_of::<T>()) bytes. Data is properly initialized and has a
523        // length no greater than isize::MAX. Array's safe API doesn't provide access to raw
524        // pointers, so the borrow checker prevents access throughout 'a.
525        unsafe {
526            slice::from_raw_parts_mut(self.ptr.as_ptr(), self.size)
527        }
528    }
529}
530
531impl<T> AsRef<[T]> for Array<T> {
532    fn as_ref(&self) -> &[T] {
533        self.deref()
534    }
535}
536
537impl<T> AsMut<[T]> for Array<T> {
538    fn as_mut(&mut self) -> &mut [T] {
539        self.deref_mut()
540    }
541}
542
543impl<T> Borrow<[T]> for Array<T> {
544    fn borrow(&self) -> &[T] {
545        self.as_ref()
546    }
547}
548
549impl<T> BorrowMut<[T]> for Array<T> {
550    fn borrow_mut(&mut self) -> &mut [T] {
551        self.as_mut()
552    }
553}
554
555// SAFETY: Arrays, when used safely rely on unique pointers and are therefore safe for Send when T:
556// Send.
557unsafe impl<T: Send> Send for Array<T> {}
558// SAFETY: Array's safe API obeys all rules of the borrow checker, so no interior mutability occurs.
559// This means that Array<T> can safely implement Sync when T: Sync.
560unsafe impl<T: Sync> Sync for Array<T> {}
561
562impl<T: Clone> Clone for Array<T> {
563    fn clone(&self) -> Self {
564        Array::from_iter_sized(self.iter().cloned())
565    }
566}
567
568impl<T: PartialEq> PartialEq for Array<T> {
569    fn eq(&self, other: &Self) -> bool {
570        **self == **other
571    }
572}
573
574impl<T: Eq> Eq for Array<T> {}
575
576impl<T: Hash> Hash for Array<T> {
577    fn hash<H: Hasher>(&self, state: &mut H) {
578        (**self).hash(state);
579    }
580}
581
582impl<T: Debug> Debug for Array<T> {
583    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
584        f.debug_struct("Array")
585            .field_with("contents", |f| f.debug_list().entries(self.iter()).finish())
586            .field("size", &self.size)
587            .finish()
588    }
589}
590
591impl<T: Debug> Display for Array<T> {
592    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
593        f.debug_list().entries(self.iter()).finish()
594    }
595}