Skip to main content

smart_string/str_stack/
iter.rs

1use crate::StrStack;
2
3#[derive(Clone, Copy, Debug)]
4struct Cursor {
5    index: usize,
6    begin: usize,
7    end: usize,
8}
9
10impl Cursor {
11    #[inline]
12    pub fn new(index: usize, begin: usize, end: usize) -> Self {
13        Self { index, begin, end }
14    }
15}
16
17pub struct StrStackIter<'a> {
18    stack: &'a StrStack,
19    next: Option<Cursor>,
20}
21
22impl<'a> StrStackIter<'a> {
23    #[inline]
24    pub fn new(stack: &'a StrStack) -> Self {
25        let next = stack
26            .ends
27            .first()
28            .copied()
29            .map(|end| Cursor::new(0, 0, end));
30        Self { stack, next }
31    }
32}
33
34impl<'a> Iterator for StrStackIter<'a> {
35    type Item = &'a str;
36
37    #[inline]
38    fn next(&mut self) -> Option<Self::Item> {
39        let Cursor { index, begin, end } = self.next?;
40        let next_index = index + 1;
41        self.next = self
42            .stack
43            .ends
44            .get(next_index)
45            .copied()
46            .map(|next_end| Cursor::new(next_index, end, next_end));
47        // SAFETY: `StrStackIter` is constructed from a valid `StrStack` and advances using `ends` boundaries.
48        // `StrStack` only stores UTF-8 segments pushed via `push(&str)`, so `[begin..end]` is in-bounds and valid UTF-8.
49        Some(unsafe { self.stack.get_unchecked_internal(begin, end) })
50    }
51
52    #[inline]
53    fn size_hint(&self) -> (usize, Option<usize>) {
54        let len = self.len();
55        (len, Some(len))
56    }
57}
58
59impl<'a> ExactSizeIterator for StrStackIter<'a> {
60    #[inline]
61    fn len(&self) -> usize {
62        self.next
63            .map(|c| self.stack.ends.len() - c.index)
64            .unwrap_or(0)
65    }
66}
67
68impl<'a> IntoIterator for &'a StrStack {
69    type Item = <StrStackIter<'a> as Iterator>::Item;
70    type IntoIter = StrStackIter<'a>;
71
72    #[inline]
73    fn into_iter(self) -> Self::IntoIter {
74        StrStackIter::new(self)
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_iter() {
84        let mut stack = StrStack::new();
85
86        stack.push("123");
87        stack.push("456");
88        stack.push("789");
89
90        let mut iter = StrStackIter::new(&stack);
91        assert_eq!(iter.next(), Some("123"));
92        assert_eq!(iter.next(), Some("456"));
93        assert_eq!(iter.next(), Some("789"));
94        assert_eq!(iter.next(), None);
95        assert_eq!(iter.next(), None);
96    }
97
98    #[test]
99    fn test_iter_empty() {
100        let stack = StrStack::new();
101
102        let mut iter = StrStackIter::new(&stack);
103        assert_eq!(iter.next(), None);
104        assert_eq!(iter.next(), None);
105    }
106
107    #[test]
108    fn test_exact_size_len_and_size_hint_decrease() {
109        let mut stack = StrStack::new();
110        stack.push("a");
111        stack.push("bb");
112        stack.push("ccc");
113
114        let mut it = stack.iter();
115        assert_eq!(it.len(), 3);
116        assert_eq!(it.size_hint(), (3, Some(3)));
117
118        assert_eq!(it.next(), Some("a"));
119        assert_eq!(it.len(), 2);
120        assert_eq!(it.size_hint(), (2, Some(2)));
121
122        assert_eq!(it.next(), Some("bb"));
123        assert_eq!(it.len(), 1);
124        assert_eq!(it.size_hint(), (1, Some(1)));
125
126        assert_eq!(it.next(), Some("ccc"));
127        assert_eq!(it.len(), 0);
128        assert_eq!(it.size_hint(), (0, Some(0)));
129
130        assert_eq!(it.next(), None);
131        assert_eq!(it.len(), 0);
132        assert_eq!(it.size_hint(), (0, Some(0)));
133    }
134}