51
51
//! 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.
52
52
53
53
use crate :: { internal:: small_vec:: SmallVec , version_set:: VersionSet } ;
54
+ use std:: cmp:: Ordering ;
54
55
use std:: ops:: RangeBounds ;
55
56
use std:: {
56
57
fmt:: { Debug , Display , Formatter } ,
@@ -202,23 +203,46 @@ impl<V: Ord> Range<V> {
202
203
/// Returns true if the this Range contains the specified value.
203
204
pub fn contains ( & self , v : & V ) -> bool {
204
205
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 => ( ) ,
217
210
}
218
211
}
219
212
false
220
213
}
221
214
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
+
222
246
/// Construct a simple range from anything that impls [RangeBounds] like `v1..v2`.
223
247
pub fn from_range_bounds < R , IV > ( bounds : R ) -> Self
224
248
where
@@ -264,6 +288,26 @@ impl<V: Ord> Range<V> {
264
288
}
265
289
}
266
290
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
+
267
311
fn valid_segment < T : PartialOrd > ( start : & Bound < T > , end : & Bound < T > ) -> bool {
268
312
match ( start, end) {
269
313
( Included ( s) , Included ( e) ) => s <= e,
@@ -274,6 +318,33 @@ fn valid_segment<T: PartialOrd>(start: &Bound<T>, end: &Bound<T>) -> bool {
274
318
}
275
319
}
276
320
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
+ for ver in locations. by_ref ( ) {
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
+ seg. take ( ) . map ( |( s, _) | ( s, None ) )
345
+ } )
346
+ }
347
+
277
348
impl < V : Ord + Clone > Range < V > {
278
349
/// Computes the union of this `Range` and another.
279
350
pub fn union ( & self , other : & Self ) -> Self {
@@ -321,6 +392,41 @@ impl<V: Ord + Clone> Range<V> {
321
392
322
393
Self { segments } . check_invariants ( )
323
394
}
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 (
418
+ & self ,
419
+ kept_segments : impl Iterator < Item = ( Option < usize > , Option < usize > ) > ,
420
+ ) -> Range < V > {
421
+ let mut segments = SmallVec :: Empty ;
422
+ for ( s, e) in kept_segments {
423
+ segments. push ( (
424
+ s. map_or ( Unbounded , |s| self . segments [ s] . 0 . clone ( ) ) ,
425
+ e. map_or ( Unbounded , |e| self . segments [ e] . 1 . clone ( ) ) ,
426
+ ) ) ;
427
+ }
428
+ Self { segments } . check_invariants ( )
429
+ }
324
430
}
325
431
326
432
impl < T : Debug + Display + Clone + Eq + Ord > VersionSet for Range < T > {
@@ -600,5 +706,32 @@ pub mod tests {
600
706
let rv2: Range <u32 > = rv. bounding_range( ) . map( Range :: from_range_bounds:: <_, u32 >) . unwrap_or_else( Range :: empty) ;
601
707
assert_eq!( rv, rv2) ;
602
708
}
709
+
710
+ #[ test]
711
+ fn contains( range in strategy( ) , versions in proptest:: collection:: vec( version_strat( ) , ..30 ) ) {
712
+ for v in versions {
713
+ assert_eq!( range. contains( & v) , range. segments. iter( ) . any( |s| RangeBounds :: contains( s, & v) ) ) ;
714
+ }
715
+ }
716
+
717
+ #[ test]
718
+ fn contains_many( range in strategy( ) , mut versions in proptest:: collection:: vec( version_strat( ) , ..30 ) ) {
719
+ versions. sort( ) ;
720
+ assert_eq!( versions. len( ) , range. contains_many( versions. iter( ) ) . count( ) ) ;
721
+ for ( a, b) in versions. iter( ) . zip( range. contains_many( versions. iter( ) ) ) {
722
+ assert_eq!( range. contains( a) , b) ;
723
+ }
724
+ }
725
+
726
+ #[ test]
727
+ fn simplify( range in strategy( ) , mut versions in proptest:: collection:: vec( version_strat( ) , ..30 ) ) {
728
+ versions. sort( ) ;
729
+ let simp = range. simplify( versions. iter( ) ) ;
730
+
731
+ for v in versions {
732
+ assert_eq!( range. contains( & v) , simp. contains( & v) ) ;
733
+ }
734
+ assert!( simp. segments. len( ) <= range. segments. len( ) )
735
+ }
603
736
}
604
737
}
0 commit comments