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