astack/
iter.rs

1//! This library contains tools used for iterating over a Stack.
2
3use core::{fmt, iter, mem, ptr};
4
5/// A helper struct used to iterate over the items contained in a [`Stack`][crate::Stack]
6/// from top to bottom with the syntax `for var in expr { /* code */ }`.
7///
8/// Being a LIFO system, the [`next`][StackIntoIter::next] method will start from
9/// the top and move to the bottom. If you want to iterate in the opposite direction,
10/// this struct implements [`DoubleEndedIterator`] as well. Moreover, if you want to
11/// [`collect`][Iterator::collect] a [`StackIntoIter`] into a `Vec`, keep
12/// in mind that the [`StackIntoIter`] advances from top to bottom: therefore, you
13/// will end up with a `Vec` with the items in the inverse order of the original
14/// [`Stack`][crate::Stack].
15///
16/// # Examples
17///
18/// ```rust
19/// use astack::stack;
20///
21/// let items = stack!{ [i32; 5] = [10, 20, 30, 40, 50] };
22///
23/// let mut it = items.into_iter();
24///
25/// assert_eq!(it.next(), Some(50));
26/// assert_eq!(it.next_back(), Some(10));
27/// assert_eq!(it.collect::<Vec<_>>(), vec![40, 30, 20]);
28/// ```
29pub struct StackIntoIter<T, const N: usize> {
30    items: [mem::MaybeUninit<T>; N],
31    top_len: usize,
32    bottom_len: usize,
33}
34
35impl<T, const N: usize> StackIntoIter<T, N> {
36    /// Create a new [`StackIntoIter`].
37    #[inline]
38    pub(super) const unsafe fn new(items: [mem::MaybeUninit<T>; N], top_len: usize) -> Self {
39        // Safety: it is up to the caller (us) to use valid `items` and `top_len`.
40        Self {
41            items,
42            top_len,
43            bottom_len: 0,
44        }
45    }
46
47    /// Implementation of [`Iterator::next`] for [`StackIntoIter`].
48    /// It is the same as `Stack::pop`.
49    #[inline]
50    fn pop_top(&mut self) -> Option<T> {
51        debug_assert!(self.bottom_len <= self.top_len);
52        if self.top_len == self.bottom_len {
53            None
54        } else {
55            self.top_len -= 1;
56            Some(unsafe { ptr::read(self.items.as_ptr().add(self.top_len)).assume_init() })
57        }
58    }
59
60    /// Implementation of [`DoubleEndedIterator::next_back`] for [`StackIntoIter`].
61    #[inline]
62    fn pop_bottom(&mut self) -> Option<T> {
63        debug_assert!(self.bottom_len <= self.top_len);
64
65        if self.bottom_len == self.top_len {
66            None
67        } else {
68            // bottom_len already points to the value, in contrast with
69            // top_len, which points to the next empty slot
70            let result =
71                Some(unsafe { ptr::read(self.items.as_ptr().add(self.bottom_len)).assume_init() });
72            self.bottom_len += 1;
73            result
74        }
75    }
76}
77
78impl<T, const N: usize> Iterator for StackIntoIter<T, N> {
79    type Item = T;
80
81    #[inline]
82    fn next(&mut self) -> Option<Self::Item> {
83        self.pop_top()
84    }
85
86    #[inline]
87    fn size_hint(&self) -> (usize, Option<usize>) {
88        (self.bottom_len, Some(self.top_len))
89    }
90}
91
92impl<T, const N: usize> iter::DoubleEndedIterator for StackIntoIter<T, N> {
93    #[inline]
94    fn next_back(&mut self) -> Option<Self::Item> {
95        self.pop_bottom()
96    }
97}
98
99impl<T, const N: usize> Default for StackIntoIter<T, N> {
100    #[inline]
101    fn default() -> Self {
102        Self {
103            top_len: 0,
104            bottom_len: 0,
105
106            // SAFETY: Same as `Stack::new()`
107            items: unsafe { mem::MaybeUninit::uninit().assume_init() },
108        }
109    }
110}
111
112impl<T, const N: usize> Clone for StackIntoIter<T, N>
113where
114    T: Clone,
115{
116    #[inline]
117    fn clone(&self) -> Self {
118        // The good thing about this method is that we only iterate for
119        // the number of remaining items.
120        //
121        // Safety: this comes from `Stack::clone()`.
122        unsafe {
123            let mut items = mem::MaybeUninit::<[mem::MaybeUninit<T>; N]>::uninit().assume_init();
124            self.items
125                .get_unchecked(self.bottom_len..self.top_len)
126                .iter()
127                .zip(items.get_unchecked_mut(self.bottom_len..self.top_len))
128                .for_each(|(src, dst)| {
129                    dst.write(src.assume_init_ref().clone());
130                });
131            Self {
132                items,
133                top_len: self.top_len,
134                bottom_len: self.bottom_len,
135            }
136        }
137    }
138}
139
140impl<T, const N: usize> Drop for StackIntoIter<T, N> {
141    #[inline]
142    fn drop(&mut self) {
143        debug_assert!(self.bottom_len <= self.top_len);
144
145        // Safety: Same as `Stack::drop()`
146        unsafe {
147            let items = self.items.get_unchecked_mut(self.bottom_len..self.top_len)
148                as *mut [mem::MaybeUninit<T>] as *mut [T];
149            ptr::drop_in_place(items);
150        }
151    }
152}
153
154impl<T, const N: usize> fmt::Debug for StackIntoIter<T, N>
155where
156    T: fmt::Debug,
157{
158    #[inline]
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        let mut list = f.debug_list();
161
162        unsafe {
163            list.entries(
164                self.items
165                    .get_unchecked(self.bottom_len..self.top_len)
166                    .iter()
167                    .map(|item| item.assume_init_ref()),
168            );
169        }
170
171        list.finish()
172    }
173}
174
175impl<T, const N: usize> iter::FusedIterator for StackIntoIter<T, N> {}
176
177impl<T, const N: usize, const M: usize> PartialEq<StackIntoIter<T, M>> for StackIntoIter<T, N>
178where
179    T: PartialEq,
180{
181    #[inline]
182    fn eq(&self, other: &StackIntoIter<T, M>) -> bool {
183        unsafe {
184            let items1 = self.items.get_unchecked(self.bottom_len..self.top_len)
185                as *const [mem::MaybeUninit<T>] as *const [T];
186            let items2 = other.items.get_unchecked(other.bottom_len..other.top_len)
187                as *const [mem::MaybeUninit<T>] as *const [T];
188            *items1 == *items2
189        }
190    }
191}
192
193// impl<I: IntoIterator<Item = T>, T, const N: usize> TryFrom<I> for Stack<T, N> {
194//     type Error = StackError;
195
196//     fn try_from(iter: I) -> Result<Self, Self::Error> {}
197// }