Skip to main content

smart_string/str_stack/
iter.rs

1use crate::StrStack;
2
3/// Iterator over [`StrStack`] segments.
4pub struct StrStackIter<'a> {
5    stack: &'a StrStack,
6    index: usize,
7    back_index: usize,
8}
9
10impl<'a> StrStackIter<'a> {
11    #[inline]
12    pub fn new(stack: &'a StrStack) -> Self {
13        Self {
14            back_index: stack.ends_as_slice().len(),
15            stack,
16            index: 0,
17        }
18    }
19
20    #[inline]
21    fn bounds(&self, index: usize) -> (usize, usize) {
22        let ends = self.stack.ends_as_slice();
23        let start = if index > 0 {
24            ends[index - 1] as usize
25        } else {
26            0
27        };
28        let end = ends[index] as usize;
29        (start, end)
30    }
31}
32
33impl<'a> Iterator for StrStackIter<'a> {
34    type Item = &'a str;
35
36    #[inline]
37    fn next(&mut self) -> Option<Self::Item> {
38        if self.index >= self.back_index {
39            return None;
40        }
41        let (begin, end) = self.bounds(self.index);
42        self.index += 1;
43        // SAFETY: `StrStackIter` is constructed from a valid `StrStack` and advances using `ends` boundaries.
44        // `StrStack` only stores UTF-8 segments pushed via `push(&str)`, so `[begin..end]` is in-bounds and valid UTF-8.
45        Some(unsafe { self.stack.get_unchecked_internal(begin, end) })
46    }
47
48    #[inline]
49    fn size_hint(&self) -> (usize, Option<usize>) {
50        let len = self.len();
51        (len, Some(len))
52    }
53}
54
55impl<'a> DoubleEndedIterator for StrStackIter<'a> {
56    #[inline]
57    fn next_back(&mut self) -> Option<Self::Item> {
58        if self.back_index <= self.index {
59            return None;
60        }
61        self.back_index -= 1;
62        let (begin, end) = self.bounds(self.back_index);
63        // SAFETY: same as `next()` — bounds from `ends` are valid UTF-8 segment boundaries.
64        Some(unsafe { self.stack.get_unchecked_internal(begin, end) })
65    }
66}
67
68impl<'a> ExactSizeIterator for StrStackIter<'a> {
69    #[inline]
70    fn len(&self) -> usize {
71        self.back_index - self.index
72    }
73}
74
75impl<'a> std::iter::FusedIterator for StrStackIter<'a> {}
76
77impl<'a> IntoIterator for &'a StrStack {
78    type Item = <StrStackIter<'a> as Iterator>::Item;
79    type IntoIter = StrStackIter<'a>;
80
81    #[inline]
82    fn into_iter(self) -> Self::IntoIter {
83        StrStackIter::new(self)
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_iter() {
93        let mut stack = StrStack::new();
94
95        stack.push("123");
96        stack.push("456");
97        stack.push("789");
98
99        let mut iter = StrStackIter::new(&stack);
100        assert_eq!(iter.next(), Some("123"));
101        assert_eq!(iter.next(), Some("456"));
102        assert_eq!(iter.next(), Some("789"));
103        assert_eq!(iter.next(), None);
104        assert_eq!(iter.next(), None);
105    }
106
107    #[test]
108    fn test_iter_empty() {
109        let stack = StrStack::new();
110
111        let mut iter = StrStackIter::new(&stack);
112        assert_eq!(iter.next(), None);
113        assert_eq!(iter.next(), None);
114    }
115
116    #[test]
117    fn test_exact_size_len_and_size_hint_decrease() {
118        let mut stack = StrStack::new();
119        stack.push("a");
120        stack.push("bb");
121        stack.push("ccc");
122
123        let mut it = stack.iter();
124        assert_eq!(it.len(), 3);
125        assert_eq!(it.size_hint(), (3, Some(3)));
126
127        assert_eq!(it.next(), Some("a"));
128        assert_eq!(it.len(), 2);
129        assert_eq!(it.size_hint(), (2, Some(2)));
130
131        assert_eq!(it.next(), Some("bb"));
132        assert_eq!(it.len(), 1);
133        assert_eq!(it.size_hint(), (1, Some(1)));
134
135        assert_eq!(it.next(), Some("ccc"));
136        assert_eq!(it.len(), 0);
137        assert_eq!(it.size_hint(), (0, Some(0)));
138
139        assert_eq!(it.next(), None);
140        assert_eq!(it.len(), 0);
141        assert_eq!(it.size_hint(), (0, Some(0)));
142    }
143
144    #[test]
145    fn test_iter_reverse() {
146        let mut stack = StrStack::new();
147        stack.push("a");
148        stack.push("b");
149        stack.push("c");
150
151        let collected: Vec<&str> = stack.iter().rev().collect();
152        assert_eq!(collected, vec!["c", "b", "a"]);
153    }
154
155    #[test]
156    fn test_iter_double_ended_meet_in_middle() {
157        let mut stack = StrStack::new();
158        stack.push("a");
159        stack.push("b");
160        stack.push("c");
161        stack.push("d");
162
163        let mut it = stack.iter();
164        assert_eq!(it.next(), Some("a"));
165        assert_eq!(it.next_back(), Some("d"));
166        assert_eq!(it.next(), Some("b"));
167        assert_eq!(it.next_back(), Some("c"));
168        assert_eq!(it.next(), None);
169        assert_eq!(it.next_back(), None);
170    }
171
172    #[test]
173    fn test_iter_fused() {
174        let mut stack = StrStack::new();
175        stack.push("x");
176
177        let mut it = stack.iter();
178        assert_eq!(it.next(), Some("x"));
179        assert_eq!(it.next(), None);
180        assert_eq!(it.next(), None);
181        assert_eq!(it.next(), None);
182    }
183}