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