5151//! If we do not see practical bugs, or we get a formal proof that the code cannot lead to error states, then we may remove this warning.
5252
5353use crate :: { internal:: small_vec:: SmallVec , version_set:: VersionSet } ;
54+ use std:: cmp:: Ordering ;
5455use std:: ops:: RangeBounds ;
5556use std:: {
5657 fmt:: { Debug , Display , Formatter } ,
@@ -202,23 +203,46 @@ impl<V: Ord> Range<V> {
202203 /// Returns true if the this Range contains the specified value.
203204 pub fn contains ( & self , v : & V ) -> bool {
204205 for segment in self . segments . iter ( ) {
205- if match segment {
206- ( Unbounded , Unbounded ) => true ,
207- ( Unbounded , Included ( end) ) => v <= end,
208- ( Unbounded , Excluded ( end) ) => v < end,
209- ( Included ( start) , Unbounded ) => v >= start,
210- ( Included ( start) , Included ( end) ) => v >= start && v <= end,
211- ( Included ( start) , Excluded ( end) ) => v >= start && v < end,
212- ( Excluded ( start) , Unbounded ) => v > start,
213- ( Excluded ( start) , Included ( end) ) => v > start && v <= end,
214- ( Excluded ( start) , Excluded ( end) ) => v > start && v < end,
215- } {
216- return true ;
206+ match within_bounds ( v, segment) {
207+ Ordering :: Less => return false ,
208+ Ordering :: Equal => return true ,
209+ Ordering :: Greater => ( ) ,
217210 }
218211 }
219212 false
220213 }
221214
215+ /// Returns true if the this Range contains the specified values.
216+ ///
217+ /// The `versions` iterator must be sorted.
218+ /// Functionally equivalent to `versions.map(|v| self.contains(v))`.
219+ /// Except it runs in `O(size_of_range + len_of_versions)` not `O(size_of_range * len_of_versions)`
220+ pub fn contains_many < ' s , I > ( & ' s self , versions : I ) -> impl Iterator < Item = bool > + ' s
221+ where
222+ I : Iterator < Item = & ' s V > + ' s ,
223+ V : ' s ,
224+ {
225+ self . locate_versions ( versions) . map ( |m| m. is_some ( ) )
226+ }
227+
228+ /// Return the segment index in the range for each version in the range, None otherwise
229+ fn locate_versions < ' s , I > ( & ' s self , versions : I ) -> impl Iterator < Item = Option < usize > > + ' s
230+ where
231+ I : Iterator < Item = & ' s V > + ' s ,
232+ V : ' s ,
233+ {
234+ versions. scan ( 0 , |i, v| {
235+ while let Some ( segment) = self . segments . get ( * i) {
236+ match within_bounds ( v, segment) {
237+ Ordering :: Less => return Some ( None ) ,
238+ Ordering :: Equal => return Some ( Some ( * i) ) ,
239+ Ordering :: Greater => * i += 1 ,
240+ }
241+ }
242+ Some ( None )
243+ } )
244+ }
245+
222246 /// Construct a simple range from anything that impls [RangeBounds] like `v1..v2`.
223247 pub fn from_range_bounds < R , IV > ( bounds : R ) -> Self
224248 where
@@ -264,6 +288,26 @@ impl<V: Ord> Range<V> {
264288 }
265289}
266290
291+ fn within_bounds < V : PartialOrd > ( v : & V , segment : & Interval < V > ) -> Ordering {
292+ let below_lower_bound = match segment {
293+ ( Excluded ( start) , _) => v <= start,
294+ ( Included ( start) , _) => v < start,
295+ ( Unbounded , _) => false ,
296+ } ;
297+ if below_lower_bound {
298+ return Ordering :: Less ;
299+ }
300+ let below_upper_bound = match segment {
301+ ( _, Unbounded ) => true ,
302+ ( _, Included ( end) ) => v <= end,
303+ ( _, Excluded ( end) ) => v < end,
304+ } ;
305+ if below_upper_bound {
306+ return Ordering :: Equal ;
307+ }
308+ Ordering :: Greater
309+ }
310+
267311fn valid_segment < T : PartialOrd > ( start : & Bound < T > , end : & Bound < T > ) -> bool {
268312 match ( start, end) {
269313 ( Included ( s) , Included ( e) ) => s <= e,
@@ -274,6 +318,33 @@ fn valid_segment<T: PartialOrd>(start: &Bound<T>, end: &Bound<T>) -> bool {
274318 }
275319}
276320
321+ /// group adjacent versions locations
322+ /// [None, 3, 6, 7, None] -> [(3, 7)]
323+ /// [3, 6, 7, None] -> [(None, 7)]
324+ /// [3, 6, 7] -> [(None, None)]
325+ /// [None, 1, 4, 7, None, None, None, 8, None, 9] -> [(1, 7), (8, 8), (9, None)]
326+ fn group_adjacent_locations (
327+ mut locations : impl Iterator < Item = Option < usize > > ,
328+ ) -> impl Iterator < Item = ( Option < usize > , Option < usize > ) > {
329+ // If the first version matched, then the lower bound of that segment is not needed
330+ let mut seg = locations. next ( ) . flatten ( ) . map ( |ver| ( None , Some ( ver) ) ) ;
331+ std:: iter:: from_fn ( move || {
332+ while let Some ( ver) = locations. next ( ) {
333+ if let Some ( ver) = ver {
334+ // As long as were still matching versions, we keep merging into the currently matching segment
335+ seg = Some ( ( seg. map_or ( Some ( ver) , |( s, _) | s) , Some ( ver) ) ) ;
336+ } else {
337+ // If we have found a version that doesn't match, then right the merge segment and prepare for a new one.
338+ if seg. is_some ( ) {
339+ return seg. take ( ) ;
340+ }
341+ }
342+ }
343+ // If the last version matched, then write out the merged segment but the upper bound is not needed.
344+ return seg. take ( ) . map ( |( s, _) | ( s, None ) ) ;
345+ } )
346+ }
347+
277348impl < V : Ord + Clone > Range < V > {
278349 /// Computes the union of this `Range` and another.
279350 pub fn union ( & self , other : & Self ) -> Self {
@@ -321,6 +392,38 @@ impl<V: Ord + Clone> Range<V> {
321392
322393 Self { segments } . check_invariants ( )
323394 }
395+
396+ /// Returns a simpler Range that contains the same versions
397+ ///
398+ /// For every one of the Versions provided in versions the existing range and
399+ /// the simplified range will agree on whether it is contained.
400+ /// The simplified version may include or exclude versions that are not in versions as the implementation wishes.
401+ /// For example:
402+ /// - If all the versions are contained in the original than the range will be simplified to `full`.
403+ /// - If none of the versions are contained in the original than the range will be simplified to `empty`.
404+ ///
405+ /// If versions are not sorted the correctness of this function is not guaranteed.
406+ pub fn simplify < ' s , I > ( & ' s self , versions : I ) -> Self
407+ where
408+ I : Iterator < Item = & ' s V > + ' s ,
409+ V : ' s ,
410+ {
411+ let version_locations = self . locate_versions ( versions) ;
412+ let kept_segments = group_adjacent_locations ( version_locations) ;
413+ self . keep_segments ( kept_segments)
414+ }
415+
416+ /// simplify range with segments at given location bounds.
417+ fn keep_segments < ' s > ( & ' s self , kept_segments : impl Iterator < Item = ( Option < usize > , Option < usize > ) > ) -> Range < V > {
418+ let mut segments = SmallVec :: Empty ;
419+ for ( s, e) in kept_segments {
420+ segments. push ( (
421+ s. map_or ( Unbounded , |s| self . segments [ s] . 0 . clone ( ) ) ,
422+ e. map_or ( Unbounded , |e| self . segments [ e] . 1 . clone ( ) ) ,
423+ ) ) ;
424+ }
425+ Self { segments } . check_invariants ( )
426+ }
324427}
325428
326429impl < T : Debug + Display + Clone + Eq + Ord > VersionSet for Range < T > {
@@ -600,5 +703,32 @@ pub mod tests {
600703 let rv2: Range <u32 > = rv. bounding_range( ) . map( Range :: from_range_bounds:: <_, u32 >) . unwrap_or_else( Range :: empty) ;
601704 assert_eq!( rv, rv2) ;
602705 }
706+
707+ #[ test]
708+ fn contains( range in strategy( ) , versions in proptest:: collection:: vec( version_strat( ) , ..30 ) ) {
709+ for v in versions {
710+ assert_eq!( range. contains( & v) , range. segments. iter( ) . any( |s| RangeBounds :: contains( s, & v) ) ) ;
711+ }
712+ }
713+
714+ #[ test]
715+ fn contains_many( range in strategy( ) , mut versions in proptest:: collection:: vec( version_strat( ) , ..30 ) ) {
716+ versions. sort( ) ;
717+ assert_eq!( versions. len( ) , range. contains_many( versions. iter( ) ) . count( ) ) ;
718+ for ( a, b) in versions. iter( ) . zip( range. contains_many( versions. iter( ) ) ) {
719+ assert_eq!( range. contains( a) , b) ;
720+ }
721+ }
722+
723+ #[ test]
724+ fn simplify( range in strategy( ) , mut versions in proptest:: collection:: vec( version_strat( ) , ..30 ) ) {
725+ versions. sort( ) ;
726+ let simp = range. simplify( versions. iter( ) ) ;
727+
728+ for v in versions {
729+ assert_eq!( range. contains( & v) , simp. contains( & v) ) ;
730+ }
731+ assert!( simp. segments. len( ) <= range. segments. len( ) )
732+ }
603733 }
604734}
0 commit comments