devela/data/collections/array/d1/uninit/methods.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
// devela::data::collections:array::d1::uninit::methods
use crate::{
iif, ArrayUninit, IndexOutOfBounds, MaybeUninit, Mem,
PartialSpace::{self, NotEnoughSpace, PartiallyAdded},
Storage,
};
// T, S
impl<T, const CAP: usize, S: Storage> ArrayUninit<T, CAP, S> {
/* construct */
/// Returns a new uninitialized empty array.
pub fn new() -> Self {
// WAIT: [MaybeUninit array methods](https://github.com/rust-lang/rust/issues/96097)
// let data = MaybeUninit::uninit_array::<CAP>();
//
#[allow(clippy::uninit_assumed_init)]
// SAFETY: we are ensuring elements are only accessed after initialization.
let data = unsafe { MaybeUninit::uninit().assume_init() };
Self { data, init_len: 0 }
}
/// Initializes the array from an iterator until it's either full or the iterator is exhausted.
///
/// # Returns
/// - Returns a new array initialized with the elements from the `iterator`.
/// - Returns [`PartiallyAdded`] if not all elements could be initialized.
/// - Returns [`NotEnoughSpace`] if the array had no uninitialized elements.
pub fn from_range<I>(iterator: I) -> Result<Self, PartialSpace>
where
I: IntoIterator<Item = T>,
{
let mut array = Self::new();
let _ = array.init_range(iterator)?;
Ok(array)
}
/* query */
/// Returns the count of initialized elements.
#[must_use] #[rustfmt::skip]
pub const fn len(&self) -> usize { self.init_len }
/// Returns `true` if no elements are yet initialized.
#[must_use] #[rustfmt::skip]
pub const fn is_empty(&self) -> bool { self.init_len == 0 }
/// Returns `true` if all the elements are already initialized.
#[must_use] #[rustfmt::skip]
pub const fn is_full(&self) -> bool { self.init_len >= CAP }
/// Returns `index` back if it's within the range already initialized.
///
/// # Errors
/// Returns [`IndexOutOfBounds`] if the index is larger than the initialized length.
#[rustfmt::skip]
pub const fn verify_index(&self, index: usize) -> Result<usize, IndexOutOfBounds> {
iif![index < self.init_len; Ok(index); Err(IndexOutOfBounds(Some(index)))]
}
/* get */
/// Returns a shared reference to an initialized element at a given index.
///
/// # Errors
/// Returns [`IndexOutOfBounds`] if the index is larger than the initialized length.
/// or if the element at that index is not initialized.
pub fn get(&self, index: usize) -> Result<&T, IndexOutOfBounds> {
let _ = self.verify_index(index)?;
// SAFETY: the index is verified
Ok(unsafe { self.data[index].assume_init_ref() })
}
/// Returns an exclusive reference to an initialized element at a given index.
///
/// # Errors
/// Returns [`IndexOutOfBounds`] if the index is larger than the initialized length.
/// or if the element at that index is not initialized.
pub fn get_mut(&mut self, index: usize) -> Result<&mut T, IndexOutOfBounds> {
let _ = self.verify_index(index)?;
// SAFETY: the index is verified
Ok(unsafe { self.data[index].assume_init_mut() })
}
/* push */
/// Initializes the next uninitialized element with the provided value.
///
/// # Errors
/// Returns [`IndexOutOfBounds`] if the index is larger than the initialized length.
pub fn init_next(&mut self, value: T) -> Result<(), IndexOutOfBounds> {
if self.is_full() {
Err(IndexOutOfBounds(None))
} else {
self.data[self.init_len] = MaybeUninit::new(value);
self.init_len += 1;
Ok(())
}
}
/// Initializes elements from an iterator
///
/// Starts at the last initialized element, continues until either the array
/// is full or the iterator is exhausted.
///
/// # Returns
/// - Returns the number of elements initialized.
/// - Returns [`PartiallyAdded`] if not all elements could be initialized.
/// - Returns [`NotEnoughSpace`] if the array had no uninitialized elements.
pub fn init_range<I>(&mut self, values: I) -> Result<usize, PartialSpace>
where
I: IntoIterator<Item = T>,
{
if self.is_full() {
Err(NotEnoughSpace(None))
} else {
let start_len = self.init_len;
for value in values {
if self.is_full() {
return Ok(self.init_len - start_len);
}
self.data[self.init_len] = MaybeUninit::new(value);
self.init_len += 1;
}
Err(PartiallyAdded(Some(self.init_len - start_len)))
}
}
/// Replaces the value at a given index with a new value and returns the old value.
/// # Errors
/// Returns [`IndexOutOfBounds`] if the index is not within the range of initialized elements.
pub fn replace(&mut self, index: usize, value: T) -> Result<T, IndexOutOfBounds> {
let index = self.verify_index(index)?;
// SAFETY: If the index is verified, the value is initialized
let slot = unsafe { self.data[index].assume_init_mut() };
Ok(Mem::replace(slot, value))
}
/// Swaps the values at two indices.
/// # Errors
/// Returns [`IndexOutOfBounds`]
/// if either index is not within the range of initialized elements.
pub fn swap(&mut self, index1: usize, index2: usize) -> Result<(), IndexOutOfBounds> {
let idx1 = self.verify_index(index1)?;
let idx2 = self.verify_index(index2)?;
// SAFETY: If the indices are verified, the values are initialized
unsafe {
core::ptr::swap(self.data[idx1].assume_init_mut(), self.data[idx2].assume_init_mut());
}
Ok(())
}
}