Coverage Report

Created: 2025-10-28 08:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-1.11.0/src/str.rs
Line
Count
Source
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
15
use crate::iter::plumbing::*;
16
use crate::iter::*;
17
use 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]
22
0
fn is_char_boundary(b: u8) -> bool {
23
    // This is bit magic equivalent to: b < 128 || b >= 192
24
0
    (b as i8) >= -0x40
25
0
}
26
27
/// Find the index of a character boundary near the midpoint.
28
#[inline]
29
0
fn find_char_midpoint(chars: &str) -> usize {
30
0
    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
0
    let (left, right) = chars.as_bytes().split_at(mid);
36
0
    match right.iter().copied().position(is_char_boundary) {
37
0
        Some(i) => mid + i,
38
0
        None => left
39
0
            .iter()
40
0
            .copied()
41
0
            .rposition(is_char_boundary)
42
0
            .unwrap_or(0),
43
    }
44
0
}
45
46
/// Try to split a string near the midpoint.
47
#[inline]
48
0
fn split(chars: &str) -> Option<(&str, &str)> {
49
0
    let index = find_char_midpoint(chars);
50
0
    if index > 0 {
51
0
        Some(chars.split_at(index))
52
    } else {
53
0
        None
54
    }
55
0
}
56
57
/// Parallel extensions for strings.
58
pub 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
0
    fn par_chars(&self) -> Chars<'_> {
73
0
        Chars {
74
0
            chars: self.as_parallel_string(),
75
0
        }
76
0
    }
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
0
    fn par_char_indices(&self) -> CharIndices<'_> {
88
0
        CharIndices {
89
0
            chars: self.as_parallel_string(),
90
0
        }
91
0
    }
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
0
    fn par_bytes(&self) -> Bytes<'_> {
108
0
        Bytes {
109
0
            chars: self.as_parallel_string(),
110
0
        }
111
0
    }
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
0
    fn par_encode_utf16(&self) -> EncodeUtf16<'_> {
132
0
        EncodeUtf16 {
133
0
            chars: self.as_parallel_string(),
134
0
        }
135
0
    }
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
0
    fn par_split<P: Pattern>(&self, separator: P) -> Split<'_, P> {
155
0
        Split::new(self.as_parallel_string(), separator)
156
0
    }
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
0
    fn par_split_inclusive<P: Pattern>(&self, separator: P) -> SplitInclusive<'_, P> {
176
0
        SplitInclusive::new(self.as_parallel_string(), separator)
177
0
    }
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
0
    fn par_split_terminator<P: Pattern>(&self, terminator: P) -> SplitTerminator<'_, P> {
198
0
        SplitTerminator::new(self.as_parallel_string(), terminator)
199
0
    }
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
0
    fn par_lines(&self) -> Lines<'_> {
217
0
        Lines(self.as_parallel_string())
218
0
    }
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
0
    fn par_split_whitespace(&self) -> SplitWhitespace<'_> {
256
0
        SplitWhitespace(self.as_parallel_string())
257
0
    }
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
0
    fn par_split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> {
293
0
        SplitAsciiWhitespace(self.as_parallel_string())
294
0
    }
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
0
    fn par_matches<P: Pattern>(&self, pattern: P) -> Matches<'_, P> {
314
0
        Matches {
315
0
            chars: self.as_parallel_string(),
316
0
            pattern,
317
0
        }
318
0
    }
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
0
    fn par_match_indices<P: Pattern>(&self, pattern: P) -> MatchIndices<'_, P> {
337
0
        MatchIndices {
338
0
            chars: self.as_parallel_string(),
339
0
            pattern,
340
0
        }
341
0
    }
342
}
343
344
impl ParallelString for str {
345
    #[inline]
346
0
    fn as_parallel_string(&self) -> &str {
347
0
        self
348
0
    }
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.
357
mod 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
}
383
use self::private::Pattern;
384
385
#[inline]
386
0
fn offset<T>(base: usize) -> impl Fn((usize, T)) -> (usize, T) {
387
0
    move |(i, x)| (base + i, x)
388
0
}
389
390
macro_rules! impl_pattern {
391
    (&$self:ident => $pattern:expr) => {
392
        private_impl! {}
393
394
        #[inline]
395
0
        fn find_in(&$self, chars: &str) -> Option<usize> {
396
0
            chars.find($pattern)
397
0
        }
Unexecuted instantiation: <[char; _] as rayon::str::private::Pattern>::find_in
Unexecuted instantiation: <&[char; _] as rayon::str::private::Pattern>::find_in
Unexecuted instantiation: <_ as rayon::str::private::Pattern>::find_in
Unexecuted instantiation: <&[char] as rayon::str::private::Pattern>::find_in
Unexecuted instantiation: <char as rayon::str::private::Pattern>::find_in
398
399
        #[inline]
400
0
        fn rfind_in(&$self, chars: &str) -> Option<usize> {
401
0
            chars.rfind($pattern)
402
0
        }
Unexecuted instantiation: <[char; _] as rayon::str::private::Pattern>::rfind_in
Unexecuted instantiation: <&[char; _] as rayon::str::private::Pattern>::rfind_in
Unexecuted instantiation: <_ as rayon::str::private::Pattern>::rfind_in
Unexecuted instantiation: <&[char] as rayon::str::private::Pattern>::rfind_in
Unexecuted instantiation: <char as rayon::str::private::Pattern>::rfind_in
403
404
        #[inline]
405
0
        fn is_suffix_of(&$self, chars: &str) -> bool {
406
0
            chars.ends_with($pattern)
407
0
        }
Unexecuted instantiation: <[char; _] as rayon::str::private::Pattern>::is_suffix_of
Unexecuted instantiation: <&[char; _] as rayon::str::private::Pattern>::is_suffix_of
Unexecuted instantiation: <_ as rayon::str::private::Pattern>::is_suffix_of
Unexecuted instantiation: <&[char] as rayon::str::private::Pattern>::is_suffix_of
Unexecuted instantiation: <char as rayon::str::private::Pattern>::is_suffix_of
408
409
0
        fn fold_splits<'ch, F>(&$self, chars: &'ch str, folder: F, skip_last: bool) -> F
410
0
        where
411
0
            F: Folder<&'ch str>,
412
        {
413
0
            let mut split = chars.split($pattern);
414
0
            if skip_last {
415
0
                split.next_back();
416
0
            }
417
0
            folder.consume_iter(split)
418
0
        }
Unexecuted instantiation: <[char; _] as rayon::str::private::Pattern>::fold_splits::<_>
Unexecuted instantiation: <&[char; _] as rayon::str::private::Pattern>::fold_splits::<_>
Unexecuted instantiation: <_ as rayon::str::private::Pattern>::fold_splits::<_>
Unexecuted instantiation: <&[char] as rayon::str::private::Pattern>::fold_splits::<_>
Unexecuted instantiation: <char as rayon::str::private::Pattern>::fold_splits::<_>
419
420
0
        fn fold_inclusive_splits<'ch, F>(&$self, chars: &'ch str, folder: F) -> F
421
0
        where
422
0
            F: Folder<&'ch str>,
423
        {
424
0
            folder.consume_iter(chars.split_inclusive($pattern))
425
0
        }
Unexecuted instantiation: <[char; _] as rayon::str::private::Pattern>::fold_inclusive_splits::<_>
Unexecuted instantiation: <&[char; _] as rayon::str::private::Pattern>::fold_inclusive_splits::<_>
Unexecuted instantiation: <_ as rayon::str::private::Pattern>::fold_inclusive_splits::<_>
Unexecuted instantiation: <&[char] as rayon::str::private::Pattern>::fold_inclusive_splits::<_>
Unexecuted instantiation: <char as rayon::str::private::Pattern>::fold_inclusive_splits::<_>
426
427
0
        fn fold_matches<'ch, F>(&$self, chars: &'ch str, folder: F) -> F
428
0
        where
429
0
            F: Folder<&'ch str>,
430
        {
431
0
            folder.consume_iter(chars.matches($pattern))
432
0
        }
Unexecuted instantiation: <[char; _] as rayon::str::private::Pattern>::fold_matches::<_>
Unexecuted instantiation: <&[char; _] as rayon::str::private::Pattern>::fold_matches::<_>
Unexecuted instantiation: <_ as rayon::str::private::Pattern>::fold_matches::<_>
Unexecuted instantiation: <&[char] as rayon::str::private::Pattern>::fold_matches::<_>
Unexecuted instantiation: <char as rayon::str::private::Pattern>::fold_matches::<_>
433
434
0
        fn fold_match_indices<'ch, F>(&$self, chars: &'ch str, folder: F, base: usize) -> F
435
0
        where
436
0
            F: Folder<(usize, &'ch str)>,
437
        {
438
0
            folder.consume_iter(chars.match_indices($pattern).map(offset(base)))
439
0
        }
Unexecuted instantiation: <[char; _] as rayon::str::private::Pattern>::fold_match_indices::<_>
Unexecuted instantiation: <&[char; _] as rayon::str::private::Pattern>::fold_match_indices::<_>
Unexecuted instantiation: <_ as rayon::str::private::Pattern>::fold_match_indices::<_>
Unexecuted instantiation: <&[char] as rayon::str::private::Pattern>::fold_match_indices::<_>
Unexecuted instantiation: <char as rayon::str::private::Pattern>::fold_match_indices::<_>
440
    }
441
}
442
443
impl Pattern for char {
444
    impl_pattern!(&self => *self);
445
}
446
447
impl Pattern for &[char] {
448
    impl_pattern!(&self => *self);
449
}
450
451
impl<const N: usize> Pattern for [char; N] {
452
    impl_pattern!(&self => *self);
453
}
454
455
impl<const N: usize> Pattern for &[char; N] {
456
    impl_pattern!(&self => *self);
457
}
458
459
impl<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)]
467
pub struct Chars<'ch> {
468
    chars: &'ch str,
469
}
470
471
struct CharsProducer<'ch> {
472
    chars: &'ch str,
473
}
474
475
impl<'ch> ParallelIterator for Chars<'ch> {
476
    type Item = char;
477
478
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
479
0
    where
480
0
        C: UnindexedConsumer<Self::Item>,
481
    {
482
0
        bridge_unindexed(CharsProducer { chars: self.chars }, consumer)
483
0
    }
484
}
485
486
impl<'ch> UnindexedProducer for CharsProducer<'ch> {
487
    type Item = char;
488
489
0
    fn split(self) -> (Self, Option<Self>) {
490
0
        match split(self.chars) {
491
0
            Some((left, right)) => (
492
0
                CharsProducer { chars: left },
493
0
                Some(CharsProducer { chars: right }),
494
0
            ),
495
0
            None => (self, None),
496
        }
497
0
    }
498
499
0
    fn fold_with<F>(self, folder: F) -> F
500
0
    where
501
0
        F: Folder<Self::Item>,
502
    {
503
0
        folder.consume_iter(self.chars.chars())
504
0
    }
505
}
506
507
// /////////////////////////////////////////////////////////////////////////
508
509
/// Parallel iterator over the characters of a string, with their positions
510
#[derive(Debug, Clone)]
511
pub struct CharIndices<'ch> {
512
    chars: &'ch str,
513
}
514
515
struct CharIndicesProducer<'ch> {
516
    index: usize,
517
    chars: &'ch str,
518
}
519
520
impl<'ch> ParallelIterator for CharIndices<'ch> {
521
    type Item = (usize, char);
522
523
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
524
0
    where
525
0
        C: UnindexedConsumer<Self::Item>,
526
    {
527
0
        let producer = CharIndicesProducer {
528
0
            index: 0,
529
0
            chars: self.chars,
530
0
        };
531
0
        bridge_unindexed(producer, consumer)
532
0
    }
533
}
534
535
impl<'ch> UnindexedProducer for CharIndicesProducer<'ch> {
536
    type Item = (usize, char);
537
538
0
    fn split(self) -> (Self, Option<Self>) {
539
0
        match split(self.chars) {
540
0
            Some((left, right)) => (
541
0
                CharIndicesProducer {
542
0
                    chars: left,
543
0
                    ..self
544
0
                },
545
0
                Some(CharIndicesProducer {
546
0
                    chars: right,
547
0
                    index: self.index + left.len(),
548
0
                }),
549
0
            ),
550
0
            None => (self, None),
551
        }
552
0
    }
553
554
0
    fn fold_with<F>(self, folder: F) -> F
555
0
    where
556
0
        F: Folder<Self::Item>,
557
    {
558
0
        let base = self.index;
559
0
        folder.consume_iter(self.chars.char_indices().map(offset(base)))
560
0
    }
561
}
562
563
// /////////////////////////////////////////////////////////////////////////
564
565
/// Parallel iterator over the bytes of a string
566
#[derive(Debug, Clone)]
567
pub struct Bytes<'ch> {
568
    chars: &'ch str,
569
}
570
571
struct BytesProducer<'ch> {
572
    chars: &'ch str,
573
}
574
575
impl<'ch> ParallelIterator for Bytes<'ch> {
576
    type Item = u8;
577
578
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
579
0
    where
580
0
        C: UnindexedConsumer<Self::Item>,
581
    {
582
0
        bridge_unindexed(BytesProducer { chars: self.chars }, consumer)
583
0
    }
584
}
585
586
impl<'ch> UnindexedProducer for BytesProducer<'ch> {
587
    type Item = u8;
588
589
0
    fn split(self) -> (Self, Option<Self>) {
590
0
        match split(self.chars) {
591
0
            Some((left, right)) => (
592
0
                BytesProducer { chars: left },
593
0
                Some(BytesProducer { chars: right }),
594
0
            ),
595
0
            None => (self, None),
596
        }
597
0
    }
598
599
0
    fn fold_with<F>(self, folder: F) -> F
600
0
    where
601
0
        F: Folder<Self::Item>,
602
    {
603
0
        folder.consume_iter(self.chars.bytes())
604
0
    }
605
}
606
607
// /////////////////////////////////////////////////////////////////////////
608
609
/// Parallel iterator over a string encoded as UTF-16
610
#[derive(Debug, Clone)]
611
pub struct EncodeUtf16<'ch> {
612
    chars: &'ch str,
613
}
614
615
struct EncodeUtf16Producer<'ch> {
616
    chars: &'ch str,
617
}
618
619
impl<'ch> ParallelIterator for EncodeUtf16<'ch> {
620
    type Item = u16;
621
622
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
623
0
    where
624
0
        C: UnindexedConsumer<Self::Item>,
625
    {
626
0
        bridge_unindexed(EncodeUtf16Producer { chars: self.chars }, consumer)
627
0
    }
628
}
629
630
impl<'ch> UnindexedProducer for EncodeUtf16Producer<'ch> {
631
    type Item = u16;
632
633
0
    fn split(self) -> (Self, Option<Self>) {
634
0
        match split(self.chars) {
635
0
            Some((left, right)) => (
636
0
                EncodeUtf16Producer { chars: left },
637
0
                Some(EncodeUtf16Producer { chars: right }),
638
0
            ),
639
0
            None => (self, None),
640
        }
641
0
    }
642
643
0
    fn fold_with<F>(self, folder: F) -> F
644
0
    where
645
0
        F: Folder<Self::Item>,
646
    {
647
0
        folder.consume_iter(self.chars.encode_utf16())
648
0
    }
649
}
650
651
// /////////////////////////////////////////////////////////////////////////
652
653
/// Parallel iterator over substrings separated by a pattern
654
#[derive(Debug, Clone)]
655
pub struct Split<'ch, P: Pattern> {
656
    chars: &'ch str,
657
    separator: P,
658
}
659
660
impl<'ch, P: Pattern> Split<'ch, P> {
661
0
    fn new(chars: &'ch str, separator: P) -> Self {
662
0
        Split { chars, separator }
663
0
    }
664
}
665
666
impl<'ch, P: Pattern> ParallelIterator for Split<'ch, P> {
667
    type Item = &'ch str;
668
669
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
670
0
    where
671
0
        C: UnindexedConsumer<Self::Item>,
672
    {
673
0
        let producer = SplitProducer::new(self.chars, &self.separator);
674
0
        bridge_unindexed(producer, consumer)
675
0
    }
676
}
677
678
/// Implement support for `SplitProducer`.
679
impl<P: Pattern> Fissile<P> for &str {
680
0
    fn length(&self) -> usize {
681
0
        self.len()
682
0
    }
683
684
0
    fn midpoint(&self, end: usize) -> usize {
685
        // First find a suitable UTF-8 boundary.
686
0
        find_char_midpoint(&self[..end])
687
0
    }
688
689
0
    fn find(&self, separator: &P, start: usize, end: usize) -> Option<usize> {
690
0
        separator.find_in(&self[start..end])
691
0
    }
692
693
0
    fn rfind(&self, separator: &P, end: usize) -> Option<usize> {
694
0
        separator.rfind_in(&self[..end])
695
0
    }
696
697
0
    fn split_once<const INCL: bool>(self, index: usize) -> (Self, Self) {
698
0
        if INCL {
699
            // include the separator in the left side
700
0
            let separator = self[index..].chars().next().unwrap();
701
0
            self.split_at(index + separator.len_utf8())
702
        } else {
703
0
            let (left, right) = self.split_at(index);
704
0
            let mut right_iter = right.chars();
705
0
            right_iter.next(); // skip the separator
706
0
            (left, right_iter.as_str())
707
        }
708
0
    }
709
710
0
    fn fold_splits<F, const INCL: bool>(self, separator: &P, folder: F, skip_last: bool) -> F
711
0
    where
712
0
        F: Folder<Self>,
713
    {
714
0
        if INCL {
715
0
            debug_assert!(!skip_last);
716
0
            separator.fold_inclusive_splits(self, folder)
717
        } else {
718
0
            separator.fold_splits(self, folder, skip_last)
719
        }
720
0
    }
721
}
722
723
// /////////////////////////////////////////////////////////////////////////
724
725
/// Parallel iterator over substrings separated by a pattern
726
#[derive(Debug, Clone)]
727
pub struct SplitInclusive<'ch, P: Pattern> {
728
    chars: &'ch str,
729
    separator: P,
730
}
731
732
impl<'ch, P: Pattern> SplitInclusive<'ch, P> {
733
0
    fn new(chars: &'ch str, separator: P) -> Self {
734
0
        SplitInclusive { chars, separator }
735
0
    }
736
}
737
738
impl<'ch, P: Pattern> ParallelIterator for SplitInclusive<'ch, P> {
739
    type Item = &'ch str;
740
741
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
742
0
    where
743
0
        C: UnindexedConsumer<Self::Item>,
744
    {
745
0
        let producer = SplitInclusiveProducer::new_incl(self.chars, &self.separator);
746
0
        bridge_unindexed(producer, consumer)
747
0
    }
748
}
749
750
// /////////////////////////////////////////////////////////////////////////
751
752
/// Parallel iterator over substrings separated by a terminator pattern
753
#[derive(Debug, Clone)]
754
pub struct SplitTerminator<'ch, P: Pattern> {
755
    chars: &'ch str,
756
    terminator: P,
757
}
758
759
struct SplitTerminatorProducer<'ch, 'sep, P: Pattern> {
760
    splitter: SplitProducer<'sep, P, &'ch str>,
761
    skip_last: bool,
762
}
763
764
impl<'ch, P: Pattern> SplitTerminator<'ch, P> {
765
0
    fn new(chars: &'ch str, terminator: P) -> Self {
766
0
        SplitTerminator { chars, terminator }
767
0
    }
768
}
769
770
impl<'ch, 'sep, P: Pattern + 'sep> SplitTerminatorProducer<'ch, 'sep, P> {
771
0
    fn new(chars: &'ch str, terminator: &'sep P) -> Self {
772
        SplitTerminatorProducer {
773
0
            splitter: SplitProducer::new(chars, terminator),
774
0
            skip_last: chars.is_empty() || terminator.is_suffix_of(chars),
775
        }
776
0
    }
777
}
778
779
impl<'ch, P: Pattern> ParallelIterator for SplitTerminator<'ch, P> {
780
    type Item = &'ch str;
781
782
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
783
0
    where
784
0
        C: UnindexedConsumer<Self::Item>,
785
    {
786
0
        let producer = SplitTerminatorProducer::new(self.chars, &self.terminator);
787
0
        bridge_unindexed(producer, consumer)
788
0
    }
789
}
790
791
impl<'ch, 'sep, P: Pattern + 'sep> UnindexedProducer for SplitTerminatorProducer<'ch, 'sep, P> {
792
    type Item = &'ch str;
793
794
0
    fn split(mut self) -> (Self, Option<Self>) {
795
0
        let (left, right) = self.splitter.split();
796
0
        self.splitter = left;
797
0
        let right = right.map(|right| {
798
0
            let skip_last = self.skip_last;
799
0
            self.skip_last = false;
800
0
            SplitTerminatorProducer {
801
0
                splitter: right,
802
0
                skip_last,
803
0
            }
804
0
        });
805
0
        (self, right)
806
0
    }
807
808
0
    fn fold_with<F>(self, folder: F) -> F
809
0
    where
810
0
        F: Folder<Self::Item>,
811
    {
812
0
        self.splitter.fold_with(folder, self.skip_last)
813
0
    }
814
}
815
816
// /////////////////////////////////////////////////////////////////////////
817
818
/// Parallel iterator over lines in a string
819
#[derive(Debug, Clone)]
820
pub struct Lines<'ch>(&'ch str);
821
822
#[inline]
823
0
fn no_carriage_return(line: &str) -> &str {
824
0
    line.strip_suffix('\r').unwrap_or(line)
825
0
}
826
827
impl<'ch> ParallelIterator for Lines<'ch> {
828
    type Item = &'ch str;
829
830
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
831
0
    where
832
0
        C: UnindexedConsumer<Self::Item>,
833
    {
834
0
        self.0
835
0
            .par_split_terminator('\n')
836
0
            .map(no_carriage_return)
837
0
            .drive_unindexed(consumer)
838
0
    }
839
}
840
841
// /////////////////////////////////////////////////////////////////////////
842
843
/// Parallel iterator over substrings separated by whitespace
844
#[derive(Debug, Clone)]
845
pub struct SplitWhitespace<'ch>(&'ch str);
846
847
#[inline]
848
0
fn not_empty(s: &&str) -> bool {
849
0
    !s.is_empty()
850
0
}
851
852
impl<'ch> ParallelIterator for SplitWhitespace<'ch> {
853
    type Item = &'ch str;
854
855
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
856
0
    where
857
0
        C: UnindexedConsumer<Self::Item>,
858
    {
859
0
        self.0
860
0
            .par_split(char::is_whitespace)
861
0
            .filter(not_empty)
862
0
            .drive_unindexed(consumer)
863
0
    }
864
}
865
866
// /////////////////////////////////////////////////////////////////////////
867
868
/// Parallel iterator over substrings separated by ASCII whitespace
869
#[derive(Debug, Clone)]
870
pub struct SplitAsciiWhitespace<'ch>(&'ch str);
871
872
#[inline]
873
0
fn is_ascii_whitespace(c: char) -> bool {
874
0
    c.is_ascii_whitespace()
875
0
}
876
877
impl<'ch> ParallelIterator for SplitAsciiWhitespace<'ch> {
878
    type Item = &'ch str;
879
880
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
881
0
    where
882
0
        C: UnindexedConsumer<Self::Item>,
883
    {
884
0
        self.0
885
0
            .par_split(is_ascii_whitespace)
886
0
            .filter(not_empty)
887
0
            .drive_unindexed(consumer)
888
0
    }
889
}
890
891
// /////////////////////////////////////////////////////////////////////////
892
893
/// Parallel iterator over substrings that match a pattern
894
#[derive(Debug, Clone)]
895
pub struct Matches<'ch, P: Pattern> {
896
    chars: &'ch str,
897
    pattern: P,
898
}
899
900
struct MatchesProducer<'ch, 'pat, P: Pattern> {
901
    chars: &'ch str,
902
    pattern: &'pat P,
903
}
904
905
impl<'ch, P: Pattern> ParallelIterator for Matches<'ch, P> {
906
    type Item = &'ch str;
907
908
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
909
0
    where
910
0
        C: UnindexedConsumer<Self::Item>,
911
    {
912
0
        let producer = MatchesProducer {
913
0
            chars: self.chars,
914
0
            pattern: &self.pattern,
915
0
        };
916
0
        bridge_unindexed(producer, consumer)
917
0
    }
918
}
919
920
impl<'ch, 'pat, P: Pattern> UnindexedProducer for MatchesProducer<'ch, 'pat, P> {
921
    type Item = &'ch str;
922
923
0
    fn split(self) -> (Self, Option<Self>) {
924
0
        match split(self.chars) {
925
0
            Some((left, right)) => (
926
0
                MatchesProducer {
927
0
                    chars: left,
928
0
                    ..self
929
0
                },
930
0
                Some(MatchesProducer {
931
0
                    chars: right,
932
0
                    ..self
933
0
                }),
934
0
            ),
935
0
            None => (self, None),
936
        }
937
0
    }
938
939
0
    fn fold_with<F>(self, folder: F) -> F
940
0
    where
941
0
        F: Folder<Self::Item>,
942
    {
943
0
        self.pattern.fold_matches(self.chars, folder)
944
0
    }
945
}
946
947
// /////////////////////////////////////////////////////////////////////////
948
949
/// Parallel iterator over substrings that match a pattern, with their positions
950
#[derive(Debug, Clone)]
951
pub struct MatchIndices<'ch, P: Pattern> {
952
    chars: &'ch str,
953
    pattern: P,
954
}
955
956
struct MatchIndicesProducer<'ch, 'pat, P: Pattern> {
957
    index: usize,
958
    chars: &'ch str,
959
    pattern: &'pat P,
960
}
961
962
impl<'ch, P: Pattern> ParallelIterator for MatchIndices<'ch, P> {
963
    type Item = (usize, &'ch str);
964
965
0
    fn drive_unindexed<C>(self, consumer: C) -> C::Result
966
0
    where
967
0
        C: UnindexedConsumer<Self::Item>,
968
    {
969
0
        let producer = MatchIndicesProducer {
970
0
            index: 0,
971
0
            chars: self.chars,
972
0
            pattern: &self.pattern,
973
0
        };
974
0
        bridge_unindexed(producer, consumer)
975
0
    }
976
}
977
978
impl<'ch, 'pat, P: Pattern> UnindexedProducer for MatchIndicesProducer<'ch, 'pat, P> {
979
    type Item = (usize, &'ch str);
980
981
0
    fn split(self) -> (Self, Option<Self>) {
982
0
        match split(self.chars) {
983
0
            Some((left, right)) => (
984
0
                MatchIndicesProducer {
985
0
                    chars: left,
986
0
                    ..self
987
0
                },
988
0
                Some(MatchIndicesProducer {
989
0
                    chars: right,
990
0
                    index: self.index + left.len(),
991
0
                    ..self
992
0
                }),
993
0
            ),
994
0
            None => (self, None),
995
        }
996
0
    }
997
998
0
    fn fold_with<F>(self, folder: F) -> F
999
0
    where
1000
0
        F: Folder<Self::Item>,
1001
    {
1002
0
        self.pattern
1003
0
            .fold_match_indices(self.chars, folder, self.index)
1004
0
    }
1005
}