par_iter/slice/
chunk_by.rs

1use std::{fmt, marker::PhantomData, mem};
2
3use crate::iter::{plumbing::*, *};
4
5trait ChunkBySlice<T>: AsRef<[T]> + Default + Send {
6    fn split(self, index: usize) -> (Self, Self);
7
8    fn find(&self, pred: &impl Fn(&T, &T) -> bool, start: usize, end: usize) -> Option<usize> {
9        self.as_ref()[start..end]
10            .windows(2)
11            .position(move |w| !pred(&w[0], &w[1]))
12            .map(|i| i + 1)
13    }
14
15    fn rfind(&self, pred: &impl Fn(&T, &T) -> bool, end: usize) -> Option<usize> {
16        self.as_ref()[..end]
17            .windows(2)
18            .rposition(move |w| !pred(&w[0], &w[1]))
19            .map(|i| i + 1)
20    }
21}
22
23impl<T: Sync> ChunkBySlice<T> for &[T] {
24    fn split(self, index: usize) -> (Self, Self) {
25        self.split_at(index)
26    }
27}
28
29impl<T: Send> ChunkBySlice<T> for &mut [T] {
30    fn split(self, index: usize) -> (Self, Self) {
31        self.split_at_mut(index)
32    }
33}
34
35struct ChunkByProducer<'p, T, Slice, Pred> {
36    slice: Slice,
37    pred: &'p Pred,
38    tail: usize,
39    marker: PhantomData<fn(&T)>,
40}
41
42// Note: this implementation is very similar to `SplitProducer`.
43impl<T, Slice, Pred> UnindexedProducer for ChunkByProducer<'_, T, Slice, Pred>
44where
45    Slice: ChunkBySlice<T>,
46    Pred: Fn(&T, &T) -> bool + Send + Sync,
47{
48    type Item = Slice;
49
50    fn split(self) -> (Self, Option<Self>) {
51        if self.tail < 2 {
52            return (Self { tail: 0, ..self }, None);
53        }
54
55        // Look forward for the separator, and failing that look backward.
56        let mid = self.tail / 2;
57        let index = match self.slice.find(self.pred, mid, self.tail) {
58            Some(i) => Some(mid + i),
59            None => self.slice.rfind(self.pred, mid + 1),
60        };
61
62        if let Some(index) = index {
63            let (left, right) = self.slice.split(index);
64
65            let (left_tail, right_tail) = if index <= mid {
66                // If we scanned backwards to find the separator, everything in
67                // the right side is exhausted, with no separators left to find.
68                (index, 0)
69            } else {
70                (mid + 1, self.tail - index)
71            };
72
73            // Create the left split before the separator.
74            let left = Self {
75                slice: left,
76                tail: left_tail,
77                ..self
78            };
79
80            // Create the right split following the separator.
81            let right = Self {
82                slice: right,
83                tail: right_tail,
84                ..self
85            };
86
87            (left, Some(right))
88        } else {
89            // The search is exhausted, no more separators...
90            (Self { tail: 0, ..self }, None)
91        }
92    }
93
94    fn fold_with<F>(self, mut folder: F) -> F
95    where
96        F: Folder<Self::Item>,
97    {
98        let Self {
99            slice, pred, tail, ..
100        } = self;
101
102        let (slice, tail) = if tail == slice.as_ref().len() {
103            // No tail section, so just let `consume_iter` do it all.
104            (Some(slice), None)
105        } else if let Some(index) = slice.rfind(pred, tail) {
106            // We found the last separator to complete the tail, so
107            // end with that slice after `consume_iter` finds the rest.
108            let (left, right) = slice.split(index);
109            (Some(left), Some(right))
110        } else {
111            // We know there are no separators at all, so it's all "tail".
112            (None, Some(slice))
113        };
114
115        if let Some(mut slice) = slice {
116            // TODO (MSRV 1.77) use either:
117            // folder.consume_iter(slice.chunk_by(pred))
118            // folder.consume_iter(slice.chunk_by_mut(pred))
119
120            folder = folder.consume_iter(std::iter::from_fn(move || {
121                let len = slice.as_ref().len();
122                if len > 0 {
123                    let i = slice.find(pred, 0, len).unwrap_or(len);
124                    let (head, tail) = mem::take(&mut slice).split(i);
125                    slice = tail;
126                    Some(head)
127                } else {
128                    None
129                }
130            }));
131        }
132
133        if let Some(tail) = tail {
134            folder = folder.consume(tail);
135        }
136
137        folder
138    }
139}
140
141/// Parallel iterator over slice in (non-overlapping) chunks separated by a
142/// predicate.
143///
144/// This struct is created by the [`par_chunk_by`] method on `&[T]`.
145///
146/// [`par_chunk_by`]: trait.ParallelSlice.html#method.par_chunk_by
147pub struct ChunkBy<'data, T, P> {
148    pred: P,
149    slice: &'data [T],
150}
151
152impl<'data, T, P: Clone> Clone for ChunkBy<'data, T, P> {
153    fn clone(&self) -> Self {
154        ChunkBy {
155            pred: self.pred.clone(),
156            slice: self.slice,
157        }
158    }
159}
160
161impl<'data, T: fmt::Debug, P> fmt::Debug for ChunkBy<'data, T, P> {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        f.debug_struct("ChunkBy")
164            .field("slice", &self.slice)
165            .finish()
166    }
167}
168
169impl<'data, T, P> ChunkBy<'data, T, P> {
170    pub(super) fn new(slice: &'data [T], pred: P) -> Self {
171        Self { pred, slice }
172    }
173}
174
175impl<'data, T, P> ParallelIterator for ChunkBy<'data, T, P>
176where
177    T: Sync,
178    P: Fn(&T, &T) -> bool + Send + Sync,
179{
180    type Item = &'data [T];
181
182    fn drive_unindexed<C>(self, consumer: C) -> C::Result
183    where
184        C: UnindexedConsumer<Self::Item>,
185    {
186        bridge_unindexed(
187            ChunkByProducer {
188                tail: self.slice.len(),
189                slice: self.slice,
190                pred: &self.pred,
191                marker: PhantomData,
192            },
193            consumer,
194        )
195    }
196}
197
198/// Parallel iterator over slice in (non-overlapping) mutable chunks
199/// separated by a predicate.
200///
201/// This struct is created by the [`par_chunk_by_mut`] method on `&mut [T]`.
202///
203/// [`par_chunk_by_mut`]: trait.ParallelSliceMut.html#method.par_chunk_by_mut
204pub struct ChunkByMut<'data, T, P> {
205    pred: P,
206    slice: &'data mut [T],
207}
208
209impl<'data, T: fmt::Debug, P> fmt::Debug for ChunkByMut<'data, T, P> {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        f.debug_struct("ChunkByMut")
212            .field("slice", &self.slice)
213            .finish()
214    }
215}
216
217impl<'data, T, P> ChunkByMut<'data, T, P> {
218    pub(super) fn new(slice: &'data mut [T], pred: P) -> Self {
219        Self { pred, slice }
220    }
221}
222
223impl<'data, T, P> ParallelIterator for ChunkByMut<'data, T, P>
224where
225    T: Send,
226    P: Fn(&T, &T) -> bool + Send + Sync,
227{
228    type Item = &'data mut [T];
229
230    fn drive_unindexed<C>(self, consumer: C) -> C::Result
231    where
232        C: UnindexedConsumer<Self::Item>,
233    {
234        bridge_unindexed(
235            ChunkByProducer {
236                tail: self.slice.len(),
237                slice: self.slice,
238                pred: &self.pred,
239                marker: PhantomData,
240            },
241            consumer,
242        )
243    }
244}