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}