1use crate::iter::plumbing::*;
17use crate::iter::*;
18use crate::split_producer::*;
19
20#[inline]
23fn is_char_boundary(b: u8) -> bool {
24 (b as i8) >= -0x40
26}
27
28#[inline]
30fn find_char_midpoint(chars: &str) -> usize {
31 let mid = chars.len() / 2;
32
33 let (left, right) = chars.as_bytes().split_at(mid);
37 match right.iter().cloned().position(is_char_boundary) {
38 Some(i) => mid + i,
39 None => left
40 .iter()
41 .cloned()
42 .rposition(is_char_boundary)
43 .unwrap_or(0),
44 }
45}
46
47#[inline]
49fn split(chars: &str) -> Option<(&str, &str)> {
50 let index = find_char_midpoint(chars);
51 if index > 0 {
52 Some(chars.split_at(index))
53 } else {
54 None
55 }
56}
57
58pub trait ParallelString {
60 fn as_parallel_string(&self) -> &str;
63
64 fn par_chars(&self) -> Chars<'_> {
74 Chars {
75 chars: self.as_parallel_string(),
76 }
77 }
78
79 fn par_char_indices(&self) -> CharIndices<'_> {
89 CharIndices {
90 chars: self.as_parallel_string(),
91 }
92 }
93
94 fn par_bytes(&self) -> Bytes<'_> {
109 Bytes {
110 chars: self.as_parallel_string(),
111 }
112 }
113
114 fn par_encode_utf16(&self) -> EncodeUtf16<'_> {
133 EncodeUtf16 {
134 chars: self.as_parallel_string(),
135 }
136 }
137
138 fn par_split<P: Pattern>(&self, separator: P) -> Split<'_, P> {
155 Split::new(self.as_parallel_string(), separator)
156 }
157
158 fn par_split_terminator<P: Pattern>(&self, terminator: P) -> SplitTerminator<'_, P> {
176 SplitTerminator::new(self.as_parallel_string(), terminator)
177 }
178
179 fn par_lines(&self) -> Lines<'_> {
195 Lines(self.as_parallel_string())
196 }
197
198 fn par_split_whitespace(&self) -> SplitWhitespace<'_> {
214 SplitWhitespace(self.as_parallel_string())
215 }
216
217 fn par_matches<P: Pattern>(&self, pattern: P) -> Matches<'_, P> {
234 Matches {
235 chars: self.as_parallel_string(),
236 pattern,
237 }
238 }
239
240 fn par_match_indices<P: Pattern>(&self, pattern: P) -> MatchIndices<'_, P> {
256 MatchIndices {
257 chars: self.as_parallel_string(),
258 pattern,
259 }
260 }
261}
262
263impl ParallelString for str {
264 #[inline]
265 fn as_parallel_string(&self) -> &str {
266 self
267 }
268}
269
270mod private {
277 use crate::iter::plumbing::Folder;
278
279 pub trait Pattern: Sized + Sync + Send {
284 private_decl! {}
285 fn find_in(&self, haystack: &str) -> Option<usize>;
286 fn rfind_in(&self, haystack: &str) -> Option<usize>;
287 fn is_suffix_of(&self, haystack: &str) -> bool;
288 fn fold_splits<'ch, F>(&self, haystack: &'ch str, folder: F, skip_last: bool) -> F
289 where
290 F: Folder<&'ch str>;
291 fn fold_matches<'ch, F>(&self, haystack: &'ch str, folder: F) -> F
292 where
293 F: Folder<&'ch str>;
294 fn fold_match_indices<'ch, F>(&self, haystack: &'ch str, folder: F, base: usize) -> F
295 where
296 F: Folder<(usize, &'ch str)>;
297 }
298}
299use self::private::Pattern;
300
301#[inline]
302fn offset<T>(base: usize) -> impl Fn((usize, T)) -> (usize, T) {
303 move |(i, x)| (base + i, x)
304}
305
306impl Pattern for char {
307 private_impl! {}
308
309 #[inline]
310 fn find_in(&self, chars: &str) -> Option<usize> {
311 chars.find(*self)
312 }
313
314 #[inline]
315 fn rfind_in(&self, chars: &str) -> Option<usize> {
316 chars.rfind(*self)
317 }
318
319 #[inline]
320 fn is_suffix_of(&self, chars: &str) -> bool {
321 chars.ends_with(*self)
322 }
323
324 fn fold_splits<'ch, F>(&self, chars: &'ch str, folder: F, skip_last: bool) -> F
325 where
326 F: Folder<&'ch str>,
327 {
328 let mut split = chars.split(*self);
329 if skip_last {
330 split.next_back();
331 }
332 folder.consume_iter(split)
333 }
334
335 fn fold_matches<'ch, F>(&self, chars: &'ch str, folder: F) -> F
336 where
337 F: Folder<&'ch str>,
338 {
339 folder.consume_iter(chars.matches(*self))
340 }
341
342 fn fold_match_indices<'ch, F>(&self, chars: &'ch str, folder: F, base: usize) -> F
343 where
344 F: Folder<(usize, &'ch str)>,
345 {
346 folder.consume_iter(chars.match_indices(*self).map(offset(base)))
347 }
348}
349
350impl<FN: Sync + Send + Fn(char) -> bool> Pattern for FN {
351 private_impl! {}
352
353 fn find_in(&self, chars: &str) -> Option<usize> {
354 chars.find(self)
355 }
356
357 fn rfind_in(&self, chars: &str) -> Option<usize> {
358 chars.rfind(self)
359 }
360
361 fn is_suffix_of(&self, chars: &str) -> bool {
362 chars.ends_with(self)
363 }
364
365 fn fold_splits<'ch, F>(&self, chars: &'ch str, folder: F, skip_last: bool) -> F
366 where
367 F: Folder<&'ch str>,
368 {
369 let mut split = chars.split(self);
370 if skip_last {
371 split.next_back();
372 }
373 folder.consume_iter(split)
374 }
375
376 fn fold_matches<'ch, F>(&self, chars: &'ch str, folder: F) -> F
377 where
378 F: Folder<&'ch str>,
379 {
380 folder.consume_iter(chars.matches(self))
381 }
382
383 fn fold_match_indices<'ch, F>(&self, chars: &'ch str, folder: F, base: usize) -> F
384 where
385 F: Folder<(usize, &'ch str)>,
386 {
387 folder.consume_iter(chars.match_indices(self).map(offset(base)))
388 }
389}
390
391#[derive(Debug, Clone)]
395pub struct Chars<'ch> {
396 chars: &'ch str,
397}
398
399struct CharsProducer<'ch> {
400 chars: &'ch str,
401}
402
403impl<'ch> ParallelIterator for Chars<'ch> {
404 type Item = char;
405
406 fn drive_unindexed<C>(self, consumer: C) -> C::Result
407 where
408 C: UnindexedConsumer<Self::Item>,
409 {
410 bridge_unindexed(CharsProducer { chars: self.chars }, consumer)
411 }
412}
413
414impl<'ch> UnindexedProducer for CharsProducer<'ch> {
415 type Item = char;
416
417 fn split(self) -> (Self, Option<Self>) {
418 match split(self.chars) {
419 Some((left, right)) => (
420 CharsProducer { chars: left },
421 Some(CharsProducer { chars: right }),
422 ),
423 None => (self, None),
424 }
425 }
426
427 fn fold_with<F>(self, folder: F) -> F
428 where
429 F: Folder<Self::Item>,
430 {
431 folder.consume_iter(self.chars.chars())
432 }
433}
434
435#[derive(Debug, Clone)]
439pub struct CharIndices<'ch> {
440 chars: &'ch str,
441}
442
443struct CharIndicesProducer<'ch> {
444 index: usize,
445 chars: &'ch str,
446}
447
448impl<'ch> ParallelIterator for CharIndices<'ch> {
449 type Item = (usize, char);
450
451 fn drive_unindexed<C>(self, consumer: C) -> C::Result
452 where
453 C: UnindexedConsumer<Self::Item>,
454 {
455 let producer = CharIndicesProducer {
456 index: 0,
457 chars: self.chars,
458 };
459 bridge_unindexed(producer, consumer)
460 }
461}
462
463impl<'ch> UnindexedProducer for CharIndicesProducer<'ch> {
464 type Item = (usize, char);
465
466 fn split(self) -> (Self, Option<Self>) {
467 match split(self.chars) {
468 Some((left, right)) => (
469 CharIndicesProducer {
470 chars: left,
471 ..self
472 },
473 Some(CharIndicesProducer {
474 chars: right,
475 index: self.index + left.len(),
476 }),
477 ),
478 None => (self, None),
479 }
480 }
481
482 fn fold_with<F>(self, folder: F) -> F
483 where
484 F: Folder<Self::Item>,
485 {
486 let base = self.index;
487 folder.consume_iter(self.chars.char_indices().map(offset(base)))
488 }
489}
490
491#[derive(Debug, Clone)]
495pub struct Bytes<'ch> {
496 chars: &'ch str,
497}
498
499struct BytesProducer<'ch> {
500 chars: &'ch str,
501}
502
503impl<'ch> ParallelIterator for Bytes<'ch> {
504 type Item = u8;
505
506 fn drive_unindexed<C>(self, consumer: C) -> C::Result
507 where
508 C: UnindexedConsumer<Self::Item>,
509 {
510 bridge_unindexed(BytesProducer { chars: self.chars }, consumer)
511 }
512}
513
514impl<'ch> UnindexedProducer for BytesProducer<'ch> {
515 type Item = u8;
516
517 fn split(self) -> (Self, Option<Self>) {
518 match split(self.chars) {
519 Some((left, right)) => (
520 BytesProducer { chars: left },
521 Some(BytesProducer { chars: right }),
522 ),
523 None => (self, None),
524 }
525 }
526
527 fn fold_with<F>(self, folder: F) -> F
528 where
529 F: Folder<Self::Item>,
530 {
531 folder.consume_iter(self.chars.bytes())
532 }
533}
534
535#[derive(Debug, Clone)]
539pub struct EncodeUtf16<'ch> {
540 chars: &'ch str,
541}
542
543struct EncodeUtf16Producer<'ch> {
544 chars: &'ch str,
545}
546
547impl<'ch> ParallelIterator for EncodeUtf16<'ch> {
548 type Item = u16;
549
550 fn drive_unindexed<C>(self, consumer: C) -> C::Result
551 where
552 C: UnindexedConsumer<Self::Item>,
553 {
554 bridge_unindexed(EncodeUtf16Producer { chars: self.chars }, consumer)
555 }
556}
557
558impl<'ch> UnindexedProducer for EncodeUtf16Producer<'ch> {
559 type Item = u16;
560
561 fn split(self) -> (Self, Option<Self>) {
562 match split(self.chars) {
563 Some((left, right)) => (
564 EncodeUtf16Producer { chars: left },
565 Some(EncodeUtf16Producer { chars: right }),
566 ),
567 None => (self, None),
568 }
569 }
570
571 fn fold_with<F>(self, folder: F) -> F
572 where
573 F: Folder<Self::Item>,
574 {
575 folder.consume_iter(self.chars.encode_utf16())
576 }
577}
578
579#[derive(Debug, Clone)]
583pub struct Split<'ch, P: Pattern> {
584 chars: &'ch str,
585 separator: P,
586}
587
588impl<'ch, P: Pattern> Split<'ch, P> {
589 fn new(chars: &'ch str, separator: P) -> Self {
590 Split { chars, separator }
591 }
592}
593
594impl<'ch, P: Pattern> ParallelIterator for Split<'ch, P> {
595 type Item = &'ch str;
596
597 fn drive_unindexed<C>(self, consumer: C) -> C::Result
598 where
599 C: UnindexedConsumer<Self::Item>,
600 {
601 let producer = SplitProducer::new(self.chars, &self.separator);
602 bridge_unindexed(producer, consumer)
603 }
604}
605
606impl<'ch, P: Pattern> Fissile<P> for &'ch str {
608 fn length(&self) -> usize {
609 self.len()
610 }
611
612 fn midpoint(&self, end: usize) -> usize {
613 find_char_midpoint(&self[..end])
615 }
616
617 fn find(&self, separator: &P, start: usize, end: usize) -> Option<usize> {
618 separator.find_in(&self[start..end])
619 }
620
621 fn rfind(&self, separator: &P, end: usize) -> Option<usize> {
622 separator.rfind_in(&self[..end])
623 }
624
625 fn split_once(self, index: usize) -> (Self, Self) {
626 let (left, right) = self.split_at(index);
627 let mut right_iter = right.chars();
628 right_iter.next(); (left, right_iter.as_str())
630 }
631
632 fn fold_splits<F>(self, separator: &P, folder: F, skip_last: bool) -> F
633 where
634 F: Folder<Self>,
635 {
636 separator.fold_splits(self, folder, skip_last)
637 }
638}
639
640#[derive(Debug, Clone)]
644pub struct SplitTerminator<'ch, P: Pattern> {
645 chars: &'ch str,
646 terminator: P,
647}
648
649struct SplitTerminatorProducer<'ch, 'sep, P: Pattern> {
650 splitter: SplitProducer<'sep, P, &'ch str>,
651 skip_last: bool,
652}
653
654impl<'ch, P: Pattern> SplitTerminator<'ch, P> {
655 fn new(chars: &'ch str, terminator: P) -> Self {
656 SplitTerminator { chars, terminator }
657 }
658}
659
660impl<'ch, 'sep, P: Pattern + 'sep> SplitTerminatorProducer<'ch, 'sep, P> {
661 fn new(chars: &'ch str, terminator: &'sep P) -> Self {
662 SplitTerminatorProducer {
663 splitter: SplitProducer::new(chars, terminator),
664 skip_last: chars.is_empty() || terminator.is_suffix_of(chars),
665 }
666 }
667}
668
669impl<'ch, P: Pattern> ParallelIterator for SplitTerminator<'ch, P> {
670 type Item = &'ch str;
671
672 fn drive_unindexed<C>(self, consumer: C) -> C::Result
673 where
674 C: UnindexedConsumer<Self::Item>,
675 {
676 let producer = SplitTerminatorProducer::new(self.chars, &self.terminator);
677 bridge_unindexed(producer, consumer)
678 }
679}
680
681impl<'ch, 'sep, P: Pattern + 'sep> UnindexedProducer for SplitTerminatorProducer<'ch, 'sep, P> {
682 type Item = &'ch str;
683
684 fn split(mut self) -> (Self, Option<Self>) {
685 let (left, right) = self.splitter.split();
686 self.splitter = left;
687 let right = right.map(|right| {
688 let skip_last = self.skip_last;
689 self.skip_last = false;
690 SplitTerminatorProducer {
691 splitter: right,
692 skip_last,
693 }
694 });
695 (self, right)
696 }
697
698 fn fold_with<F>(self, folder: F) -> F
699 where
700 F: Folder<Self::Item>,
701 {
702 self.splitter.fold_with(folder, self.skip_last)
703 }
704}
705
706#[derive(Debug, Clone)]
710pub struct Lines<'ch>(&'ch str);
711
712#[inline]
713fn no_carriage_return(line: &str) -> &str {
714 if line.ends_with('\r') {
715 &line[..line.len() - 1]
716 } else {
717 line
718 }
719}
720
721impl<'ch> ParallelIterator for Lines<'ch> {
722 type Item = &'ch str;
723
724 fn drive_unindexed<C>(self, consumer: C) -> C::Result
725 where
726 C: UnindexedConsumer<Self::Item>,
727 {
728 self.0
729 .par_split_terminator('\n')
730 .map(no_carriage_return)
731 .drive_unindexed(consumer)
732 }
733}
734
735#[derive(Debug, Clone)]
739pub struct SplitWhitespace<'ch>(&'ch str);
740
741#[inline]
742fn not_empty(s: &&str) -> bool {
743 !s.is_empty()
744}
745
746impl<'ch> ParallelIterator for SplitWhitespace<'ch> {
747 type Item = &'ch str;
748
749 fn drive_unindexed<C>(self, consumer: C) -> C::Result
750 where
751 C: UnindexedConsumer<Self::Item>,
752 {
753 self.0
754 .par_split(char::is_whitespace)
755 .filter(not_empty)
756 .drive_unindexed(consumer)
757 }
758}
759
760#[derive(Debug, Clone)]
764pub struct Matches<'ch, P: Pattern> {
765 chars: &'ch str,
766 pattern: P,
767}
768
769struct MatchesProducer<'ch, 'pat, P: Pattern> {
770 chars: &'ch str,
771 pattern: &'pat P,
772}
773
774impl<'ch, P: Pattern> ParallelIterator for Matches<'ch, P> {
775 type Item = &'ch str;
776
777 fn drive_unindexed<C>(self, consumer: C) -> C::Result
778 where
779 C: UnindexedConsumer<Self::Item>,
780 {
781 let producer = MatchesProducer {
782 chars: self.chars,
783 pattern: &self.pattern,
784 };
785 bridge_unindexed(producer, consumer)
786 }
787}
788
789impl<'ch, 'pat, P: Pattern> UnindexedProducer for MatchesProducer<'ch, 'pat, P> {
790 type Item = &'ch str;
791
792 fn split(self) -> (Self, Option<Self>) {
793 match split(self.chars) {
794 Some((left, right)) => (
795 MatchesProducer {
796 chars: left,
797 ..self
798 },
799 Some(MatchesProducer {
800 chars: right,
801 ..self
802 }),
803 ),
804 None => (self, None),
805 }
806 }
807
808 fn fold_with<F>(self, folder: F) -> F
809 where
810 F: Folder<Self::Item>,
811 {
812 self.pattern.fold_matches(self.chars, folder)
813 }
814}
815
816#[derive(Debug, Clone)]
820pub struct MatchIndices<'ch, P: Pattern> {
821 chars: &'ch str,
822 pattern: P,
823}
824
825struct MatchIndicesProducer<'ch, 'pat, P: Pattern> {
826 index: usize,
827 chars: &'ch str,
828 pattern: &'pat P,
829}
830
831impl<'ch, P: Pattern> ParallelIterator for MatchIndices<'ch, P> {
832 type Item = (usize, &'ch str);
833
834 fn drive_unindexed<C>(self, consumer: C) -> C::Result
835 where
836 C: UnindexedConsumer<Self::Item>,
837 {
838 let producer = MatchIndicesProducer {
839 index: 0,
840 chars: self.chars,
841 pattern: &self.pattern,
842 };
843 bridge_unindexed(producer, consumer)
844 }
845}
846
847impl<'ch, 'pat, P: Pattern> UnindexedProducer for MatchIndicesProducer<'ch, 'pat, P> {
848 type Item = (usize, &'ch str);
849
850 fn split(self) -> (Self, Option<Self>) {
851 match split(self.chars) {
852 Some((left, right)) => (
853 MatchIndicesProducer {
854 chars: left,
855 ..self
856 },
857 Some(MatchIndicesProducer {
858 chars: right,
859 index: self.index + left.len(),
860 ..self
861 }),
862 ),
863 None => (self, None),
864 }
865 }
866
867 fn fold_with<F>(self, folder: F) -> F
868 where
869 F: Folder<Self::Item>,
870 {
871 self.pattern
872 .fold_match_indices(self.chars, folder, self.index)
873 }
874}