• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Common splitter for strings and slices
2 //!
3 //! This module is private, so these items are effectively `pub(super)`
4 
5 use crate::iter::plumbing::{Folder, UnindexedProducer};
6 
7 /// Common producer for splitting on a predicate.
8 pub(super) struct SplitProducer<'p, P, V, const INCL: bool = false> {
9     data: V,
10     separator: &'p P,
11 
12     /// Marks the endpoint beyond which we've already found no separators.
13     tail: usize,
14 }
15 
16 pub(super) type SplitInclusiveProducer<'p, P, V> = SplitProducer<'p, P, V, true>;
17 
18 /// Helper trait so `&str`, `&[T]`, and `&mut [T]` can share `SplitProducer`.
19 pub(super) trait Fissile<P>: Sized {
length(&self) -> usize20     fn length(&self) -> usize;
midpoint(&self, end: usize) -> usize21     fn midpoint(&self, end: usize) -> usize;
find(&self, separator: &P, start: usize, end: usize) -> Option<usize>22     fn find(&self, separator: &P, start: usize, end: usize) -> Option<usize>;
rfind(&self, separator: &P, end: usize) -> Option<usize>23     fn rfind(&self, separator: &P, end: usize) -> Option<usize>;
split_once<const INCL: bool>(self, index: usize) -> (Self, Self)24     fn split_once<const INCL: bool>(self, index: usize) -> (Self, Self);
fold_splits<F, const INCL: bool>(self, separator: &P, folder: F, skip_last: bool) -> F where F: Folder<Self>, Self: Send25     fn fold_splits<F, const INCL: bool>(self, separator: &P, folder: F, skip_last: bool) -> F
26     where
27         F: Folder<Self>,
28         Self: Send;
29 }
30 
31 impl<'p, P, V> SplitProducer<'p, P, V>
32 where
33     V: Fissile<P> + Send,
34 {
new(data: V, separator: &'p P) -> Self35     pub(super) fn new(data: V, separator: &'p P) -> Self {
36         SplitProducer {
37             tail: data.length(),
38             data,
39             separator,
40         }
41     }
42 }
43 
44 impl<'p, P, V> SplitInclusiveProducer<'p, P, V>
45 where
46     V: Fissile<P> + Send,
47 {
new_incl(data: V, separator: &'p P) -> Self48     pub(super) fn new_incl(data: V, separator: &'p P) -> Self {
49         SplitProducer {
50             tail: data.length(),
51             data,
52             separator,
53         }
54     }
55 }
56 
57 impl<'p, P, V, const INCL: bool> SplitProducer<'p, P, V, INCL>
58 where
59     V: Fissile<P> + Send,
60 {
61     /// Common `fold_with` implementation, integrating `SplitTerminator`'s
62     /// need to sometimes skip its final empty item.
fold_with<F>(self, folder: F, skip_last: bool) -> F where F: Folder<V>,63     pub(super) fn fold_with<F>(self, folder: F, skip_last: bool) -> F
64     where
65         F: Folder<V>,
66     {
67         let SplitProducer {
68             data,
69             separator,
70             tail,
71         } = self;
72 
73         if tail == data.length() {
74             // No tail section, so just let `fold_splits` handle it.
75             data.fold_splits::<F, INCL>(separator, folder, skip_last)
76         } else if let Some(index) = data.rfind(separator, tail) {
77             // We found the last separator to complete the tail, so
78             // end with that slice after `fold_splits` finds the rest.
79             let (left, right) = data.split_once::<INCL>(index);
80             let folder = left.fold_splits::<F, INCL>(separator, folder, false);
81             if skip_last || folder.full() {
82                 folder
83             } else {
84                 folder.consume(right)
85             }
86         } else {
87             // We know there are no separators at all.  Return our whole data.
88             if skip_last {
89                 folder
90             } else {
91                 folder.consume(data)
92             }
93         }
94     }
95 }
96 
97 impl<'p, P, V, const INCL: bool> UnindexedProducer for SplitProducer<'p, P, V, INCL>
98 where
99     V: Fissile<P> + Send,
100     P: Sync,
101 {
102     type Item = V;
103 
split(self) -> (Self, Option<Self>)104     fn split(self) -> (Self, Option<Self>) {
105         // Look forward for the separator, and failing that look backward.
106         let mid = self.data.midpoint(self.tail);
107         let index = match self.data.find(self.separator, mid, self.tail) {
108             Some(i) => Some(mid + i),
109             None => self.data.rfind(self.separator, mid),
110         };
111 
112         if let Some(index) = index {
113             let len = self.data.length();
114             let (left, right) = self.data.split_once::<INCL>(index);
115 
116             let (left_tail, right_tail) = if index < mid {
117                 // If we scanned backwards to find the separator, everything in
118                 // the right side is exhausted, with no separators left to find.
119                 (index, 0)
120             } else {
121                 let right_index = len - right.length();
122                 (mid, self.tail - right_index)
123             };
124 
125             // Create the left split before the separator.
126             let left = SplitProducer {
127                 data: left,
128                 tail: left_tail,
129                 ..self
130             };
131 
132             // Create the right split following the separator.
133             let right = SplitProducer {
134                 data: right,
135                 tail: right_tail,
136                 ..self
137             };
138 
139             (left, Some(right))
140         } else {
141             // The search is exhausted, no more separators...
142             (SplitProducer { tail: 0, ..self }, None)
143         }
144     }
145 
fold_with<F>(self, folder: F) -> F where F: Folder<Self::Item>,146     fn fold_with<F>(self, folder: F) -> F
147     where
148         F: Folder<Self::Item>,
149     {
150         self.fold_with(folder, false)
151     }
152 }
153