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(())
    }
}