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}