@@ -350,41 +350,69 @@ impl<V: Ord + Clone> Range<V> {
350
350
351
351
/// Computes the intersection of two sets of versions.
352
352
pub fn intersection ( & self , other : & Self ) -> Self {
353
- let mut segments : SmallVec < Interval < V > > = SmallVec :: empty ( ) ;
353
+ let mut output : SmallVec < Interval < V > > = SmallVec :: empty ( ) ;
354
354
let mut left_iter = self . segments . iter ( ) . peekable ( ) ;
355
355
let mut right_iter = other. segments . iter ( ) . peekable ( ) ;
356
-
357
- while let ( Some ( ( left_start, left_end) ) , Some ( ( right_start, right_end) ) ) =
358
- ( left_iter. peek ( ) , right_iter. peek ( ) )
356
+ // By the definition of intersection any point that is matched by the output
357
+ // must have a segment in each of the inputs that it matches.
358
+ // Therefore, every segment in the output must be the intersection of a segment from each of the inputs.
359
+ // It would be correct to do the "O(n^2)" thing, by computing the intersection of every segment from one input
360
+ // with every segment of the other input, and sorting the result.
361
+ // We can avoid the sorting by generating our candidate segments with an increasing `end` value.
362
+ while let Some ( ( ( left_start, left_end) , ( right_start, right_end) ) ) =
363
+ left_iter. peek ( ) . zip ( right_iter. peek ( ) )
359
364
{
365
+ // The next smallest `end` value is going to come from one of the inputs.
366
+ let left_end_is_smaller = match ( left_end, right_end) {
367
+ ( Included ( l) , Included ( r) )
368
+ | ( Excluded ( l) , Excluded ( r) )
369
+ | ( Excluded ( l) , Included ( r) ) => l <= r,
370
+
371
+ ( Included ( l) , Excluded ( r) ) => l < r,
372
+ ( _, Unbounded ) => true ,
373
+ ( Unbounded , _) => false ,
374
+ } ;
375
+ // Now that we are processing `end` we will never have to process any segment smaller than that.
376
+ // We can ensure that the input that `end` came from is larger than `end` by advancing it one step.
377
+ // `end` is the smaller available input, so we know the other input is already larger than `end`.
378
+ // Note: We can call `other_iter.next_if( == end)`, but the ends lining up is rare enough that
379
+ // it does not end up being faster in practice.
380
+ let ( other_start, end) = if left_end_is_smaller {
381
+ left_iter. next ( ) ;
382
+ ( right_start, left_end)
383
+ } else {
384
+ right_iter. next ( ) ;
385
+ ( left_start, right_end)
386
+ } ;
387
+ // `start` will either come from the input `end` came from or the other input, whichever one is larger.
388
+ // The intersection is invalid if `start` > `end`.
389
+ // But, we already know that the segments in our input are valid.
390
+ // So we do not need to check if the `start` from the input `end` came from is smaller then `end`.
391
+ // If the `other_start` is larger than end, then the intersection will be invalid.
392
+ if !valid_segment ( other_start, end) {
393
+ // Note: We can call `this_iter.next_if(!valid_segment(other_start, this_end))` in a loop.
394
+ // But the checks make it slower for the benchmarked inputs.
395
+ continue ;
396
+ }
360
397
let start = match ( left_start, right_start) {
361
398
( Included ( l) , Included ( r) ) => Included ( std:: cmp:: max ( l, r) ) ,
362
399
( Excluded ( l) , Excluded ( r) ) => Excluded ( std:: cmp:: max ( l, r) ) ,
363
400
364
- ( Included ( i) , Excluded ( e) ) | ( Excluded ( e) , Included ( i) ) if i <= e => Excluded ( e) ,
365
- ( Included ( i) , Excluded ( e) ) | ( Excluded ( e) , Included ( i) ) if e < i => Included ( i) ,
366
- ( s, Unbounded ) | ( Unbounded , s) => s. as_ref ( ) ,
367
- _ => unreachable ! ( ) ,
368
- }
369
- . cloned ( ) ;
370
- let end = match ( left_end, right_end) {
371
- ( Included ( l) , Included ( r) ) => Included ( std:: cmp:: min ( l, r) ) ,
372
- ( Excluded ( l) , Excluded ( r) ) => Excluded ( std:: cmp:: min ( l, r) ) ,
373
-
374
- ( Included ( i) , Excluded ( e) ) | ( Excluded ( e) , Included ( i) ) if i >= e => Excluded ( e) ,
375
- ( Included ( i) , Excluded ( e) ) | ( Excluded ( e) , Included ( i) ) if e > i => Included ( i) ,
401
+ ( Included ( i) , Excluded ( e) ) | ( Excluded ( e) , Included ( i) ) => {
402
+ if i <= e {
403
+ Excluded ( e)
404
+ } else {
405
+ Included ( i)
406
+ }
407
+ }
376
408
( s, Unbounded ) | ( Unbounded , s) => s. as_ref ( ) ,
377
- _ => unreachable ! ( ) ,
378
- }
379
- . cloned ( ) ;
380
- left_iter. next_if ( |( _, e) | e == & end) ;
381
- right_iter. next_if ( |( _, e) | e == & end) ;
382
- if valid_segment ( & start, & end) {
383
- segments. push ( ( start, end) )
384
- }
409
+ } ;
410
+ // Now we clone and push a new segment.
411
+ // By dealing with references until now we ensure that NO cloning happens when we reject the segment.
412
+ output. push ( ( start. cloned ( ) , end. clone ( ) ) )
385
413
}
386
414
387
- Self { segments } . check_invariants ( )
415
+ Self { segments : output } . check_invariants ( )
388
416
}
389
417
390
418
/// Returns a simpler Range that contains the same versions
0 commit comments