rayon/
str.rs

1//! Parallel iterator types for [strings]
2//!
3//! You will rarely need to interact with this module directly unless you need
4//! to name one of the iterator types.
5//!
6//! Note: [`ParallelString::par_split()`] and [`par_split_terminator()`]
7//! reference a `Pattern` trait which is not visible outside this crate.
8//! This trait is intentionally kept private, for use only by Rayon itself.
9//! It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
10//! and any function or closure `F: Fn(char) -> bool + Sync + Send`.
11//!
12//! [`par_split_terminator()`]: ParallelString::par_split_terminator()
13//! [strings]: std::str
14
15use crate::iter::plumbing::*;
16use crate::iter::*;
17use crate::split_producer::*;
18
19/// Test if a byte is the start of a UTF-8 character.
20/// (extracted from `str::is_char_boundary`)
21#[inline]
22fn is_char_boundary(b: u8) -> bool {
23    // This is bit magic equivalent to: b < 128 || b >= 192
24    (b as i8) >= -0x40
25}
26
27/// Find the index of a character boundary near the midpoint.
28#[inline]
29fn find_char_midpoint(chars: &str) -> usize {
30    let mid = chars.len() / 2;
31
32    // We want to split near the midpoint, but we need to find an actual
33    // character boundary.  So we look at the raw bytes, first scanning
34    // forward from the midpoint for a boundary, then trying backward.
35    let (left, right) = chars.as_bytes().split_at(mid);
36    match right.iter().copied().position(is_char_boundary) {
37        Some(i) => mid + i,
38        None => left
39            .iter()
40            .copied()
41            .rposition(is_char_boundary)
42            .unwrap_or(0),
43    }
44}
45
46/// Try to split a string near the midpoint.
47#[inline]
48fn split(chars: &str) -> Option<(&str, &str)> {
49    let index = find_char_midpoint(chars);
50    if index > 0 {
51        Some(chars.split_at(index))
52    } else {
53        None
54    }
55}
56
57/// Parallel extensions for strings.
58pub trait ParallelString {
59    /// Returns a plain string slice, which is used to implement the rest of
60    /// the parallel methods.
61    fn as_parallel_string(&self) -> &str;
62
63    /// Returns a parallel iterator over the characters of a string.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use rayon::prelude::*;
69    /// let max = "hello".par_chars().max_by_key(|c| *c as i32);
70    /// assert_eq!(Some('o'), max);
71    /// ```
72    fn par_chars(&self) -> Chars<'_> {
73        Chars {
74            chars: self.as_parallel_string(),
75        }
76    }
77
78    /// Returns a parallel iterator over the characters of a string, with their positions.
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// use rayon::prelude::*;
84    /// let min = "hello".par_char_indices().min_by_key(|&(_i, c)| c as i32);
85    /// assert_eq!(Some((1, 'e')), min);
86    /// ```
87    fn par_char_indices(&self) -> CharIndices<'_> {
88        CharIndices {
89            chars: self.as_parallel_string(),
90        }
91    }
92
93    /// Returns a parallel iterator over the bytes of a string.
94    ///
95    /// Note that multi-byte sequences (for code points greater than `U+007F`)
96    /// are produced as separate items, but will not be split across threads.
97    /// If you would prefer an indexed iterator without that guarantee, consider
98    /// `string.as_bytes().par_iter().copied()` instead.
99    ///
100    /// # Examples
101    ///
102    /// ```
103    /// use rayon::prelude::*;
104    /// let max = "hello".par_bytes().max();
105    /// assert_eq!(Some(b'o'), max);
106    /// ```
107    fn par_bytes(&self) -> Bytes<'_> {
108        Bytes {
109            chars: self.as_parallel_string(),
110        }
111    }
112
113    /// Returns a parallel iterator over a string encoded as UTF-16.
114    ///
115    /// Note that surrogate pairs (for code points greater than `U+FFFF`) are
116    /// produced as separate items, but will not be split across threads.
117    ///
118    /// # Examples
119    ///
120    /// ```
121    /// use rayon::prelude::*;
122    ///
123    /// let max = "hello".par_encode_utf16().max();
124    /// assert_eq!(Some(b'o' as u16), max);
125    ///
126    /// let text = "Zażółć gęślą jaźń";
127    /// let utf8_len = text.len();
128    /// let utf16_len = text.par_encode_utf16().count();
129    /// assert!(utf16_len <= utf8_len);
130    /// ```
131    fn par_encode_utf16(&self) -> EncodeUtf16<'_> {
132        EncodeUtf16 {
133            chars: self.as_parallel_string(),
134        }
135    }
136
137    /// Returns a parallel iterator over substrings separated by a
138    /// given character or predicate, similar to `str::split`.
139    ///
140    /// Note: the `Pattern` trait is private, for use only by Rayon itself.
141    /// It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
142    /// and any function or closure `F: Fn(char) -> bool + Sync + Send`.
143    ///
144    /// # Examples
145    ///
146    /// ```
147    /// use rayon::prelude::*;
148    /// let total = "1, 2, buckle, 3, 4, door"
149    ///    .par_split(',')
150    ///    .filter_map(|s| s.trim().parse::<i32>().ok())
151    ///    .sum();
152    /// assert_eq!(10, total);
153    /// ```
154    fn par_split<P: Pattern>(&self, separator: P) -> Split<'_, P> {
155        Split::new(self.as_parallel_string(), separator)
156    }
157
158    /// Returns a parallel iterator over substrings separated by a
159    /// given character or predicate, keeping the matched part as a terminator
160    /// of the substring similar to `str::split_inclusive`.
161    ///
162    /// Note: the `Pattern` trait is private, for use only by Rayon itself.
163    /// It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
164    /// and any function or closure `F: Fn(char) -> bool + Sync + Send`.
165    ///
166    /// # Examples
167    ///
168    /// ```
169    /// use rayon::prelude::*;
170    /// let lines: Vec<_> = "Mary had a little lamb\nlittle lamb\nlittle lamb."
171    ///    .par_split_inclusive('\n')
172    ///    .collect();
173    /// assert_eq!(lines, ["Mary had a little lamb\n", "little lamb\n", "little lamb."]);
174    /// ```
175    fn par_split_inclusive<P: Pattern>(&self, separator: P) -> SplitInclusive<'_, P> {
176        SplitInclusive::new(self.as_parallel_string(), separator)
177    }
178
179    /// Returns a parallel iterator over substrings terminated by a
180    /// given character or predicate, similar to `str::split_terminator`.
181    /// It's equivalent to `par_split`, except it doesn't produce an empty
182    /// substring after a trailing terminator.
183    ///
184    /// Note: the `Pattern` trait is private, for use only by Rayon itself.
185    /// It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
186    /// and any function or closure `F: Fn(char) -> bool + Sync + Send`.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use rayon::prelude::*;
192    /// let parts: Vec<_> = "((1 + 3) * 2)"
193    ///     .par_split_terminator(|c| c == '(' || c == ')')
194    ///     .collect();
195    /// assert_eq!(vec!["", "", "1 + 3", " * 2"], parts);
196    /// ```
197    fn par_split_terminator<P: Pattern>(&self, terminator: P) -> SplitTerminator<'_, P> {
198        SplitTerminator::new(self.as_parallel_string(), terminator)
199    }
200
201    /// Returns a parallel iterator over the lines of a string, ending with an
202    /// optional carriage return and with a newline (`\r\n` or just `\n`).
203    /// The final line ending is optional, and line endings are not included in
204    /// the output strings.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// use rayon::prelude::*;
210    /// let lengths: Vec<_> = "hello world\nfizbuzz"
211    ///     .par_lines()
212    ///     .map(|l| l.len())
213    ///     .collect();
214    /// assert_eq!(vec![11, 7], lengths);
215    /// ```
216    fn par_lines(&self) -> Lines<'_> {
217        Lines(self.as_parallel_string())
218    }
219
220    /// Returns a parallel iterator over the sub-slices of a string that are
221    /// separated by any amount of whitespace.
222    ///
223    /// As with `str::split_whitespace`, 'whitespace' is defined according to
224    /// the terms of the Unicode Derived Core Property `White_Space`.
225    /// If you only want to split on ASCII whitespace instead, use
226    /// [`par_split_ascii_whitespace`][`ParallelString::par_split_ascii_whitespace`].
227    ///
228    /// # Examples
229    ///
230    /// ```
231    /// use rayon::prelude::*;
232    /// let longest = "which is the longest word?"
233    ///     .par_split_whitespace()
234    ///     .max_by_key(|word| word.len());
235    /// assert_eq!(Some("longest"), longest);
236    /// ```
237    ///
238    /// All kinds of whitespace are considered:
239    ///
240    /// ```
241    /// use rayon::prelude::*;
242    /// let words: Vec<&str> = " Mary   had\ta\u{2009}little  \n\t lamb"
243    ///     .par_split_whitespace()
244    ///     .collect();
245    /// assert_eq!(words, ["Mary", "had", "a", "little", "lamb"]);
246    /// ```
247    ///
248    /// If the string is empty or all whitespace, the iterator yields no string slices:
249    ///
250    /// ```
251    /// use rayon::prelude::*;
252    /// assert_eq!("".par_split_whitespace().count(), 0);
253    /// assert_eq!("   ".par_split_whitespace().count(), 0);
254    /// ```
255    fn par_split_whitespace(&self) -> SplitWhitespace<'_> {
256        SplitWhitespace(self.as_parallel_string())
257    }
258
259    /// Returns a parallel iterator over the sub-slices of a string that are
260    /// separated by any amount of ASCII whitespace.
261    ///
262    /// To split by Unicode `White_Space` instead, use
263    /// [`par_split_whitespace`][`ParallelString::par_split_whitespace`].
264    ///
265    /// # Examples
266    ///
267    /// ```
268    /// use rayon::prelude::*;
269    /// let longest = "which is the longest word?"
270    ///     .par_split_ascii_whitespace()
271    ///     .max_by_key(|word| word.len());
272    /// assert_eq!(Some("longest"), longest);
273    /// ```
274    ///
275    /// All kinds of ASCII whitespace are considered, but not Unicode `White_Space`:
276    ///
277    /// ```
278    /// use rayon::prelude::*;
279    /// let words: Vec<&str> = " Mary   had\ta\u{2009}little  \n\t lamb"
280    ///     .par_split_ascii_whitespace()
281    ///     .collect();
282    /// assert_eq!(words, ["Mary", "had", "a\u{2009}little", "lamb"]);
283    /// ```
284    ///
285    /// If the string is empty or all ASCII whitespace, the iterator yields no string slices:
286    ///
287    /// ```
288    /// use rayon::prelude::*;
289    /// assert_eq!("".par_split_whitespace().count(), 0);
290    /// assert_eq!("   ".par_split_whitespace().count(), 0);
291    /// ```
292    fn par_split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> {
293        SplitAsciiWhitespace(self.as_parallel_string())
294    }
295
296    /// Returns a parallel iterator over substrings that match a
297    /// given character or predicate, similar to `str::matches`.
298    ///
299    /// Note: the `Pattern` trait is private, for use only by Rayon itself.
300    /// It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
301    /// and any function or closure `F: Fn(char) -> bool + Sync + Send`.
302    ///
303    /// # Examples
304    ///
305    /// ```
306    /// use rayon::prelude::*;
307    /// let total = "1, 2, buckle, 3, 4, door"
308    ///    .par_matches(char::is_numeric)
309    ///    .map(|s| s.parse::<i32>().expect("digit"))
310    ///    .sum();
311    /// assert_eq!(10, total);
312    /// ```
313    fn par_matches<P: Pattern>(&self, pattern: P) -> Matches<'_, P> {
314        Matches {
315            chars: self.as_parallel_string(),
316            pattern,
317        }
318    }
319
320    /// Returns a parallel iterator over substrings that match a given character
321    /// or predicate, with their positions, similar to `str::match_indices`.
322    ///
323    /// Note: the `Pattern` trait is private, for use only by Rayon itself.
324    /// It is implemented for `char`, `&[char]`, `[char; N]`, `&[char; N]`,
325    /// and any function or closure `F: Fn(char) -> bool + Sync + Send`.
326    ///
327    /// # Examples
328    ///
329    /// ```
330    /// use rayon::prelude::*;
331    /// let digits: Vec<_> = "1, 2, buckle, 3, 4, door"
332    ///    .par_match_indices(char::is_numeric)
333    ///    .collect();
334    /// assert_eq!(digits, vec![(0, "1"), (3, "2"), (14, "3"), (17, "4")]);
335    /// ```
336    fn par_match_indices<P: Pattern>(&self, pattern: P) -> MatchIndices<'_, P> {
337        MatchIndices {
338            chars: self.as_parallel_string(),
339            pattern,
340        }
341    }
342}
343
344impl ParallelString for str {
345    #[inline]
346    fn as_parallel_string(&self) -> &str {
347        self
348    }
349}
350
351// /////////////////////////////////////////////////////////////////////////
352
353/// We hide the `Pattern` trait in a private module, as its API is not meant
354/// for general consumption.  If we could have privacy on trait items, then it
355/// would be nicer to have its basic existence and implementors public while
356/// keeping all of the methods private.
357mod private {
358    use crate::iter::plumbing::Folder;
359
360    /// Pattern-matching trait for `ParallelString`, somewhat like a mix of
361    /// `std::str::pattern::{Pattern, Searcher}`.
362    ///
363    /// Implementing this trait is not permitted outside of `rayon`.
364    pub trait Pattern: Sized + Sync + Send {
365        private_decl! {}
366        fn find_in(&self, haystack: &str) -> Option<usize>;
367        fn rfind_in(&self, haystack: &str) -> Option<usize>;
368        fn is_suffix_of(&self, haystack: &str) -> bool;
369        fn fold_splits<'ch, F>(&self, haystack: &'ch str, folder: F, skip_last: bool) -> F
370        where
371            F: Folder<&'ch str>;
372        fn fold_inclusive_splits<'ch, F>(&self, haystack: &'ch str, folder: F) -> F
373        where
374            F: Folder<&'ch str>;
375        fn fold_matches<'ch, F>(&self, haystack: &'ch str, folder: F) -> F
376        where
377            F: Folder<&'ch str>;
378        fn fold_match_indices<'ch, F>(&self, haystack: &'ch str, folder: F, base: usize) -> F
379        where
380            F: Folder<(usize, &'ch str)>;
381    }
382}
383use self::private::Pattern;
384
385#[inline]
386fn offset<T>(base: usize) -> impl Fn((usize, T)) -> (usize, T) {
387    move |(i, x)| (base + i, x)
388}
389
390macro_rules! impl_pattern {
391    (&$self:ident => $pattern:expr) => {
392        private_impl! {}
393
394        #[inline]
395        fn find_in(&$self, chars: &str) -> Option<usize> {
396            chars.find($pattern)
397        }
398
399        #[inline]
400        fn rfind_in(&$self, chars: &str) -> Option<usize> {
401            chars.rfind($pattern)
402        }
403
404        #[inline]
405        fn is_suffix_of(&$self, chars: &str) -> bool {
406            chars.ends_with($pattern)
407        }
408
409        fn fold_splits<'ch, F>(&$self, chars: &'ch str, folder: F, skip_last: bool) -> F
410        where
411            F: Folder<&'ch str>,
412        {
413            let mut split = chars.split($pattern);
414            if skip_last {
415                split.next_back();
416            }
417            folder.consume_iter(split)
418        }
419
420        fn fold_inclusive_splits<'ch, F>(&$self, chars: &'ch str, folder: F) -> F
421        where
422            F: Folder<&'ch str>,
423        {
424            folder.consume_iter(chars.split_inclusive($pattern))
425        }
426
427        fn fold_matches<'ch, F>(&$self, chars: &'ch str, folder: F) -> F
428        where
429            F: Folder<&'ch str>,
430        {
431            folder.consume_iter(chars.matches($pattern))
432        }
433
434        fn fold_match_indices<'ch, F>(&$self, chars: &'ch str, folder: F, base: usize) -> F
435        where
436            F: Folder<(usize, &'ch str)>,
437        {
438            folder.consume_iter(chars.match_indices($pattern).map(offset(base)))
439        }
440    }
441}
442
443impl Pattern for char {
444    impl_pattern!(&self => *self);
445}
446
447impl Pattern for &[char] {
448    impl_pattern!(&self => *self);
449}
450
451impl<const N: usize> Pattern for [char; N] {
452    impl_pattern!(&self => *self);
453}
454
455impl<const N: usize> Pattern for &[char; N] {
456    impl_pattern!(&self => *self);
457}
458
459impl<FN: Sync + Send + Fn(char) -> bool> Pattern for FN {
460    impl_pattern!(&self => self);
461}
462
463// /////////////////////////////////////////////////////////////////////////
464
465/// Parallel iterator over the characters of a string
466#[derive(Debug, Clone)]
467pub struct Chars<'ch> {
468    chars: &'ch str,
469}
470
471struct CharsProducer<'ch> {
472    chars: &'ch str,
473}
474
475impl<'ch> ParallelIterator for Chars<'ch> {
476    type Item = char;
477
478    fn drive_unindexed<C>(self, consumer: C) -> C::Result
479    where
480        C: UnindexedConsumer<Self::Item>,
481    {
482        bridge_unindexed(CharsProducer { chars: self.chars }, consumer)
483    }
484}
485
486impl<'ch> UnindexedProducer for CharsProducer<'ch> {
487    type Item = char;
488
489    fn split(self) -> (Self, Option<Self>) {
490        match split(self.chars) {
491            Some((left, right)) => (
492                CharsProducer { chars: left },
493                Some(CharsProducer { chars: right }),
494            ),
495            None => (self, None),
496        }
497    }
498
499    fn fold_with<F>(self, folder: F) -> F
500    where
501        F: Folder<Self::Item>,
502    {
503        folder.consume_iter(self.chars.chars())
504    }
505}
506
507// /////////////////////////////////////////////////////////////////////////
508
509/// Parallel iterator over the characters of a string, with their positions
510#[derive(Debug, Clone)]
511pub struct CharIndices<'ch> {
512    chars: &'ch str,
513}
514
515struct CharIndicesProducer<'ch> {
516    index: usize,
517    chars: &'ch str,
518}
519
520impl<'ch> ParallelIterator for CharIndices<'ch> {
521    type Item = (usize, char);
522
523    fn drive_unindexed<C>(self, consumer: C) -> C::Result
524    where
525        C: UnindexedConsumer<Self::Item>,
526    {
527        let producer = CharIndicesProducer {
528            index: 0,
529            chars: self.chars,
530        };
531        bridge_unindexed(producer, consumer)
532    }
533}
534
535impl<'ch> UnindexedProducer for CharIndicesProducer<'ch> {
536    type Item = (usize, char);
537
538    fn split(self) -> (Self, Option<Self>) {
539        match split(self.chars) {
540            Some((left, right)) => (
541                CharIndicesProducer {
542                    chars: left,
543                    ..self
544                },
545                Some(CharIndicesProducer {
546                    chars: right,
547                    index: self.index + left.len(),
548                }),
549            ),
550            None => (self, None),
551        }
552    }
553
554    fn fold_with<F>(self, folder: F) -> F
555    where
556        F: Folder<Self::Item>,
557    {
558        let base = self.index;
559        folder.consume_iter(self.chars.char_indices().map(offset(base)))
560    }
561}
562
563// /////////////////////////////////////////////////////////////////////////
564
565/// Parallel iterator over the bytes of a string
566#[derive(Debug, Clone)]
567pub struct Bytes<'ch> {
568    chars: &'ch str,
569}
570
571struct BytesProducer<'ch> {
572    chars: &'ch str,
573}
574
575impl<'ch> ParallelIterator for Bytes<'ch> {
576    type Item = u8;
577
578    fn drive_unindexed<C>(self, consumer: C) -> C::Result
579    where
580        C: UnindexedConsumer<Self::Item>,
581    {
582        bridge_unindexed(BytesProducer { chars: self.chars }, consumer)
583    }
584}
585
586impl<'ch> UnindexedProducer for BytesProducer<'ch> {
587    type Item = u8;
588
589    fn split(self) -> (Self, Option<Self>) {
590        match split(self.chars) {
591            Some((left, right)) => (
592                BytesProducer { chars: left },
593                Some(BytesProducer { chars: right }),
594            ),
595            None => (self, None),
596        }
597    }
598
599    fn fold_with<F>(self, folder: F) -> F
600    where
601        F: Folder<Self::Item>,
602    {
603        folder.consume_iter(self.chars.bytes())
604    }
605}
606
607// /////////////////////////////////////////////////////////////////////////
608
609/// Parallel iterator over a string encoded as UTF-16
610#[derive(Debug, Clone)]
611pub struct EncodeUtf16<'ch> {
612    chars: &'ch str,
613}
614
615struct EncodeUtf16Producer<'ch> {
616    chars: &'ch str,
617}
618
619impl<'ch> ParallelIterator for EncodeUtf16<'ch> {
620    type Item = u16;
621
622    fn drive_unindexed<C>(self, consumer: C) -> C::Result
623    where
624        C: UnindexedConsumer<Self::Item>,
625    {
626        bridge_unindexed(EncodeUtf16Producer { chars: self.chars }, consumer)
627    }
628}
629
630impl<'ch> UnindexedProducer for EncodeUtf16Producer<'ch> {
631    type Item = u16;
632
633    fn split(self) -> (Self, Option<Self>) {
634        match split(self.chars) {
635            Some((left, right)) => (
636                EncodeUtf16Producer { chars: left },
637                Some(EncodeUtf16Producer { chars: right }),
638            ),
639            None => (self, None),
640        }
641    }
642
643    fn fold_with<F>(self, folder: F) -> F
644    where
645        F: Folder<Self::Item>,
646    {
647        folder.consume_iter(self.chars.encode_utf16())
648    }
649}
650
651// /////////////////////////////////////////////////////////////////////////
652
653/// Parallel iterator over substrings separated by a pattern
654#[derive(Debug, Clone)]
655pub struct Split<'ch, P: Pattern> {
656    chars: &'ch str,
657    separator: P,
658}
659
660impl<'ch, P: Pattern> Split<'ch, P> {
661    fn new(chars: &'ch str, separator: P) -> Self {
662        Split { chars, separator }
663    }
664}
665
666impl<'ch, P: Pattern> ParallelIterator for Split<'ch, P> {
667    type Item = &'ch str;
668
669    fn drive_unindexed<C>(self, consumer: C) -> C::Result
670    where
671        C: UnindexedConsumer<Self::Item>,
672    {
673        let producer = SplitProducer::new(self.chars, &self.separator);
674        bridge_unindexed(producer, consumer)
675    }
676}
677
678/// Implement support for `SplitProducer`.
679impl<P: Pattern> Fissile<P> for &str {
680    fn length(&self) -> usize {
681        self.len()
682    }
683
684    fn midpoint(&self, end: usize) -> usize {
685        // First find a suitable UTF-8 boundary.
686        find_char_midpoint(&self[..end])
687    }
688
689    fn find(&self, separator: &P, start: usize, end: usize) -> Option<usize> {
690        separator.find_in(&self[start..end])
691    }
692
693    fn rfind(&self, separator: &P, end: usize) -> Option<usize> {
694        separator.rfind_in(&self[..end])
695    }
696
697    fn split_once<const INCL: bool>(self, index: usize) -> (Self, Self) {
698        if INCL {
699            // include the separator in the left side
700            let separator = self[index..].chars().next().unwrap();
701            self.split_at(index + separator.len_utf8())
702        } else {
703            let (left, right) = self.split_at(index);
704            let mut right_iter = right.chars();
705            right_iter.next(); // skip the separator
706            (left, right_iter.as_str())
707        }
708    }
709
710    fn fold_splits<F, const INCL: bool>(self, separator: &P, folder: F, skip_last: bool) -> F
711    where
712        F: Folder<Self>,
713    {
714        if INCL {
715            debug_assert!(!skip_last);
716            separator.fold_inclusive_splits(self, folder)
717        } else {
718            separator.fold_splits(self, folder, skip_last)
719        }
720    }
721}
722
723// /////////////////////////////////////////////////////////////////////////
724
725/// Parallel iterator over substrings separated by a pattern
726#[derive(Debug, Clone)]
727pub struct SplitInclusive<'ch, P: Pattern> {
728    chars: &'ch str,
729    separator: P,
730}
731
732impl<'ch, P: Pattern> SplitInclusive<'ch, P> {
733    fn new(chars: &'ch str, separator: P) -> Self {
734        SplitInclusive { chars, separator }
735    }
736}
737
738impl<'ch, P: Pattern> ParallelIterator for SplitInclusive<'ch, P> {
739    type Item = &'ch str;
740
741    fn drive_unindexed<C>(self, consumer: C) -> C::Result
742    where
743        C: UnindexedConsumer<Self::Item>,
744    {
745        let producer = SplitInclusiveProducer::new_incl(self.chars, &self.separator);
746        bridge_unindexed(producer, consumer)
747    }
748}
749
750// /////////////////////////////////////////////////////////////////////////
751
752/// Parallel iterator over substrings separated by a terminator pattern
753#[derive(Debug, Clone)]
754pub struct SplitTerminator<'ch, P: Pattern> {
755    chars: &'ch str,
756    terminator: P,
757}
758
759struct SplitTerminatorProducer<'ch, 'sep, P: Pattern> {
760    splitter: SplitProducer<'sep, P, &'ch str>,
761    skip_last: bool,
762}
763
764impl<'ch, P: Pattern> SplitTerminator<'ch, P> {
765    fn new(chars: &'ch str, terminator: P) -> Self {
766        SplitTerminator { chars, terminator }
767    }
768}
769
770impl<'ch, 'sep, P: Pattern + 'sep> SplitTerminatorProducer<'ch, 'sep, P> {
771    fn new(chars: &'ch str, terminator: &'sep P) -> Self {
772        SplitTerminatorProducer {
773            splitter: SplitProducer::new(chars, terminator),
774            skip_last: chars.is_empty() || terminator.is_suffix_of(chars),
775        }
776    }
777}
778
779impl<'ch, P: Pattern> ParallelIterator for SplitTerminator<'ch, P> {
780    type Item = &'ch str;
781
782    fn drive_unindexed<C>(self, consumer: C) -> C::Result
783    where
784        C: UnindexedConsumer<Self::Item>,
785    {
786        let producer = SplitTerminatorProducer::new(self.chars, &self.terminator);
787        bridge_unindexed(producer, consumer)
788    }
789}
790
791impl<'ch, 'sep, P: Pattern + 'sep> UnindexedProducer for SplitTerminatorProducer<'ch, 'sep, P> {
792    type Item = &'ch str;
793
794    fn split(mut self) -> (Self, Option<Self>) {
795        let (left, right) = self.splitter.split();
796        self.splitter = left;
797        let right = right.map(|right| {
798            let skip_last = self.skip_last;
799            self.skip_last = false;
800            SplitTerminatorProducer {
801                splitter: right,
802                skip_last,
803            }
804        });
805        (self, right)
806    }
807
808    fn fold_with<F>(self, folder: F) -> F
809    where
810        F: Folder<Self::Item>,
811    {
812        self.splitter.fold_with(folder, self.skip_last)
813    }
814}
815
816// /////////////////////////////////////////////////////////////////////////
817
818/// Parallel iterator over lines in a string
819#[derive(Debug, Clone)]
820pub struct Lines<'ch>(&'ch str);
821
822#[inline]
823fn no_carriage_return(line: &str) -> &str {
824    line.strip_suffix('\r').unwrap_or(line)
825}
826
827impl<'ch> ParallelIterator for Lines<'ch> {
828    type Item = &'ch str;
829
830    fn drive_unindexed<C>(self, consumer: C) -> C::Result
831    where
832        C: UnindexedConsumer<Self::Item>,
833    {
834        self.0
835            .par_split_terminator('\n')
836            .map(no_carriage_return)
837            .drive_unindexed(consumer)
838    }
839}
840
841// /////////////////////////////////////////////////////////////////////////
842
843/// Parallel iterator over substrings separated by whitespace
844#[derive(Debug, Clone)]
845pub struct SplitWhitespace<'ch>(&'ch str);
846
847#[inline]
848fn not_empty(s: &&str) -> bool {
849    !s.is_empty()
850}
851
852impl<'ch> ParallelIterator for SplitWhitespace<'ch> {
853    type Item = &'ch str;
854
855    fn drive_unindexed<C>(self, consumer: C) -> C::Result
856    where
857        C: UnindexedConsumer<Self::Item>,
858    {
859        self.0
860            .par_split(char::is_whitespace)
861            .filter(not_empty)
862            .drive_unindexed(consumer)
863    }
864}
865
866// /////////////////////////////////////////////////////////////////////////
867
868/// Parallel iterator over substrings separated by ASCII whitespace
869#[derive(Debug, Clone)]
870pub struct SplitAsciiWhitespace<'ch>(&'ch str);
871
872#[inline]
873fn is_ascii_whitespace(c: char) -> bool {
874    c.is_ascii_whitespace()
875}
876
877impl<'ch> ParallelIterator for SplitAsciiWhitespace<'ch> {
878    type Item = &'ch str;
879
880    fn drive_unindexed<C>(self, consumer: C) -> C::Result
881    where
882        C: UnindexedConsumer<Self::Item>,
883    {
884        self.0
885            .par_split(is_ascii_whitespace)
886            .filter(not_empty)
887            .drive_unindexed(consumer)
888    }
889}
890
891// /////////////////////////////////////////////////////////////////////////
892
893/// Parallel iterator over substrings that match a pattern
894#[derive(Debug, Clone)]
895pub struct Matches<'ch, P: Pattern> {
896    chars: &'ch str,
897    pattern: P,
898}
899
900struct MatchesProducer<'ch, 'pat, P: Pattern> {
901    chars: &'ch str,
902    pattern: &'pat P,
903}
904
905impl<'ch, P: Pattern> ParallelIterator for Matches<'ch, P> {
906    type Item = &'ch str;
907
908    fn drive_unindexed<C>(self, consumer: C) -> C::Result
909    where
910        C: UnindexedConsumer<Self::Item>,
911    {
912        let producer = MatchesProducer {
913            chars: self.chars,
914            pattern: &self.pattern,
915        };
916        bridge_unindexed(producer, consumer)
917    }
918}
919
920impl<'ch, 'pat, P: Pattern> UnindexedProducer for MatchesProducer<'ch, 'pat, P> {
921    type Item = &'ch str;
922
923    fn split(self) -> (Self, Option<Self>) {
924        match split(self.chars) {
925            Some((left, right)) => (
926                MatchesProducer {
927                    chars: left,
928                    ..self
929                },
930                Some(MatchesProducer {
931                    chars: right,
932                    ..self
933                }),
934            ),
935            None => (self, None),
936        }
937    }
938
939    fn fold_with<F>(self, folder: F) -> F
940    where
941        F: Folder<Self::Item>,
942    {
943        self.pattern.fold_matches(self.chars, folder)
944    }
945}
946
947// /////////////////////////////////////////////////////////////////////////
948
949/// Parallel iterator over substrings that match a pattern, with their positions
950#[derive(Debug, Clone)]
951pub struct MatchIndices<'ch, P: Pattern> {
952    chars: &'ch str,
953    pattern: P,
954}
955
956struct MatchIndicesProducer<'ch, 'pat, P: Pattern> {
957    index: usize,
958    chars: &'ch str,
959    pattern: &'pat P,
960}
961
962impl<'ch, P: Pattern> ParallelIterator for MatchIndices<'ch, P> {
963    type Item = (usize, &'ch str);
964
965    fn drive_unindexed<C>(self, consumer: C) -> C::Result
966    where
967        C: UnindexedConsumer<Self::Item>,
968    {
969        let producer = MatchIndicesProducer {
970            index: 0,
971            chars: self.chars,
972            pattern: &self.pattern,
973        };
974        bridge_unindexed(producer, consumer)
975    }
976}
977
978impl<'ch, 'pat, P: Pattern> UnindexedProducer for MatchIndicesProducer<'ch, 'pat, P> {
979    type Item = (usize, &'ch str);
980
981    fn split(self) -> (Self, Option<Self>) {
982        match split(self.chars) {
983            Some((left, right)) => (
984                MatchIndicesProducer {
985                    chars: left,
986                    ..self
987                },
988                Some(MatchIndicesProducer {
989                    chars: right,
990                    index: self.index + left.len(),
991                    ..self
992                }),
993            ),
994            None => (self, None),
995        }
996    }
997
998    fn fold_with<F>(self, folder: F) -> F
999    where
1000        F: Folder<Self::Item>,
1001    {
1002        self.pattern
1003            .fold_match_indices(self.chars, folder, self.index)
1004    }
1005}