Skip to main content

orx_concurrent_iter/pullers/
enumerated_item_puller.rs

1use crate::concurrent_iter::ConcurrentIter;
2
3/// A regular [`Iterator`] which is created from and linked to and
4/// pulls its elements from a [`ConcurrentIter`].
5///
6/// It can be created using the [`item_puller_with_idx`] method of a concurrent iterator.
7///
8/// This is similar to [`ItemPuller`] except that this iterator additionally returns the
9/// indices of the elements in the source concurrent iterator.
10///
11/// [`item_puller_with_idx`]: crate::ConcurrentIter::item_puller_with_idx
12/// [`ItemPuller`]: crate::ItemPuller
13///
14/// # Examples
15///
16/// See the [`ItemPuller`] for detailed examples.
17/// The following example only demonstrates the additional index that is returned by the
18/// next method of the `EnumeratedItemPuller`.
19///
20/// ```
21/// use orx_concurrent_iter::*;
22///
23/// let num_threads = 4;
24/// let data: Vec<_> = (0..100).map(|x| x.to_string()).collect();
25/// let con_iter = data.con_iter();
26///
27/// std::thread::scope(|s| {
28///     for _ in 0..num_threads {
29///         s.spawn(|| {
30///             for (idx, value) in con_iter.item_puller_with_idx() {
31///                 assert_eq!(value, &idx.to_string());
32///             }
33///         });
34///     }
35/// });
36/// ```
37pub struct EnumeratedItemPuller<'a, I>
38where
39    I: ConcurrentIter,
40{
41    con_iter: &'a I,
42}
43
44impl<I: ConcurrentIter> EnumeratedItemPuller<'_, I> {
45    /// Behaves exactly as `next` but additionally provides `thread_idx` to the iterator.
46    /// This information might be useful for certain concurrent iterators, such as the
47    /// [recursive concurrent iterator](https://crates.io/crates/orx-concurrent-recursive-iter).
48    ///
49    /// Assuming a program using `n` threads that accesses this iterator, `thread_idx` is
50    /// assumed to be the internal ordering within this pool of threads taking values in
51    /// `0..n`.
52    #[inline(always)]
53    pub fn next_by(&mut self, thread_idx: usize) -> Option<(usize, I::Item)> {
54        self.con_iter.next_with_idx_by(thread_idx)
55    }
56}
57
58impl<'i, I> From<&'i I> for EnumeratedItemPuller<'i, I>
59where
60    I: ConcurrentIter,
61{
62    fn from(con_iter: &'i I) -> Self {
63        Self { con_iter }
64    }
65}
66
67impl<I> Iterator for EnumeratedItemPuller<'_, I>
68where
69    I: ConcurrentIter,
70{
71    type Item = (usize, I::Item);
72
73    #[inline(always)]
74    fn next(&mut self) -> Option<Self::Item> {
75        self.con_iter.next_with_idx()
76    }
77
78    fn size_hint(&self) -> (usize, Option<usize>) {
79        // lb: other threads might pull all of the elements, hence 0
80        // ub: we might pull all elements, hence ub(con_iter)
81        (0, self.con_iter.size_hint().1)
82    }
83
84    fn fold<B, F>(self, init: B, mut f: F) -> B
85    where
86        Self: Sized,
87        F: FnMut(B, Self::Item) -> B,
88    {
89        let mut acc = init;
90
91        while let Some(elem) = self.con_iter.next_with_idx() {
92            acc = f(acc, elem);
93        }
94
95        acc
96    }
97
98    fn count(self) -> usize
99    where
100        Self: Sized,
101    {
102        self.fold(0, |count, _| count + 1)
103    }
104}