Coverage Report

Created: 2026-07-30 06:46

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