Skip to content

Commit b2a4269

Browse files
committed
Fix algorithm with new proptest
1 parent ac176a7 commit b2a4269

File tree

1 file changed

+100
-17
lines changed

1 file changed

+100
-17
lines changed

version-ranges/src/lib.rs

Lines changed: 100 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -826,7 +826,7 @@ impl<V: Ord + Clone> Ranges<V> {
826826
}
827827
}
828828

829-
impl<V: Ord + Debug> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
829+
impl<V: Ord> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
830830
/// Constructor from arbitrary, unsorted and potentially overlapping ranges.
831831
///
832832
/// This is equivalent, but faster, to computing the [`Ranges::union`] of the
@@ -836,6 +836,10 @@ impl<V: Ord + Debug> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
836836
// 1. The segments are sorted, from lowest to highest (through `Ord`): By sorting.
837837
// 2. Each segment contains at least one version (start < end): By `union`.
838838
// 3. There is at least one version between two segments: By `union`.
839+
840+
// For this implementation, we choose to only build a single smallvec and insert or remove
841+
// in it, instead of e.g. collecting the segments into one smallvec, sorting that and then
842+
// construction the second smallvec without shifting.
839843
let mut segments: SmallVec<[Interval<V>; 1]> = SmallVec::new();
840844

841845
for segment in iter {
@@ -855,7 +859,8 @@ impl<V: Ord + Debug> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
855859
&segment.start_bound(),
856860
);
857861

858-
// Is it overlapping with the following segment?
862+
// Is it overlapping with the following segment? We'll check if there's more than one
863+
// overlap later.
859864
let next_overlapping = insertion_point < segments.len()
860865
&& !end_before_start_with_gap(
861866
&segment.end_bound(),
@@ -867,29 +872,112 @@ impl<V: Ord + Debug> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
867872
// previous: |------|
868873
// segment: |------|
869874
// following: |------|
875+
// final: |---------------|
876+
//
877+
// OR
870878
//
879+
// previous: |------|
880+
// segment: |-----------|
881+
// following: |----|
871882
// final: |---------------|
883+
//
884+
// OR
885+
//
886+
// previous: |------|
887+
// segment: |----------------|
888+
// following: |----| |------|
889+
// final: |------------------------|
872890
// We merge all three segments into one, which is effectively removing one of
873891
// two previously inserted and changing the bounds on the other.
874-
let following = segments.remove(insertion_point);
875-
segments[insertion_point - 1].1 = following.1;
892+
893+
// Remove all elements covered by the final element
894+
let mut following = segments.remove(insertion_point);
895+
while insertion_point < segments.len()
896+
&& !end_before_start_with_gap(
897+
&segment.end_bound(),
898+
&segments[insertion_point].start_bound(),
899+
)
900+
{
901+
following = segments.remove(insertion_point);
902+
}
903+
904+
// Set end to max(segment.end, <last overlapping segment>.end)
905+
if cmp_bounds_end(segment.end_bound(), following.end_bound())
906+
.unwrap()
907+
.is_lt()
908+
{
909+
segments[insertion_point - 1].1 = following.1;
910+
} else {
911+
segments[insertion_point - 1].1 = segment.1;
912+
}
876913
}
877914
(true, false) => {
878915
// previous: |------|
879916
// segment: |------|
880917
// following: |------|
881918
//
919+
// OR
920+
//
921+
// previous: |----------|
922+
// segment: |---|
923+
// following: |------|
924+
//
882925
// final: |----------| |------|
883926
// We can reuse the existing element by extending it.
884-
segments[insertion_point - 1].1 = segment.1;
927+
928+
// Set end to max(segment.end, <previous>.end)
929+
if cmp_bounds_end(
930+
segments[insertion_point - 1].end_bound(),
931+
segment.end_bound(),
932+
)
933+
.unwrap()
934+
.is_lt()
935+
{
936+
segments[insertion_point - 1].1 = segment.1;
937+
}
885938
}
886939
(false, true) => {
887940
// previous: |------|
888941
// segment: |------|
889942
// following: |------|
943+
// final: |------| |----------|
944+
//
945+
// OR
946+
//
947+
// previous: |------|
948+
// segment: |----------|
949+
// following: |---|
950+
// final: |------| |----------|
951+
//
952+
// OR
953+
//
954+
// previous: |------|
955+
// segment: |------------|
956+
// following: |---| |------|
890957
//
891-
// final: |------| |---------|
958+
// final: |------| |-----------------|
892959
// We can reuse the existing element by extending it.
960+
961+
// Remove all fully covered segments so the next element is the last one that
962+
// overlaps.
963+
while insertion_point + 1 < segments.len()
964+
&& !end_before_start_with_gap(
965+
&segment.end_bound(),
966+
&segments[insertion_point + 1].start_bound(),
967+
)
968+
{
969+
// We know that the one after also overlaps, so we can drop the current
970+
// following.
971+
segments.remove(insertion_point);
972+
}
973+
974+
// Set end to max(segment.end, <last overlapping segment>.end)
975+
if cmp_bounds_end(segments[insertion_point].end_bound(), segment.end_bound())
976+
.unwrap()
977+
.is_lt()
978+
{
979+
segments[insertion_point].1 = segment.1;
980+
}
893981
segments[insertion_point].0 = segment.0;
894982
}
895983
(false, false) => {
@@ -1214,8 +1302,12 @@ pub mod tests {
12141302

12151303
#[test]
12161304
fn from_iter_valid(segments in proptest::collection::vec(any::<(Bound<u32>, Bound<u32>)>(), ..30)) {
1217-
// We check invariants in the method.
1218-
Ranges::from_iter(segments.clone());
1305+
let mut expected = Ranges::empty();
1306+
for segment in &segments {
1307+
expected = expected.union(&Ranges::from_range_bounds(*segment));
1308+
}
1309+
let actual = Ranges::from_iter(segments.clone());
1310+
assert_eq!(expected, actual, "{segments:?}");
12191311
}
12201312
}
12211313

@@ -1253,15 +1345,6 @@ pub mod tests {
12531345
);
12541346
}
12551347

1256-
#[test]
1257-
fn from_iter_regression() {
1258-
Ranges::from_iter([
1259-
(Included(0), Included(0)),
1260-
(Excluded(1u32), Unbounded),
1261-
(Included(0), Included(0)),
1262-
]);
1263-
}
1264-
12651348
#[test]
12661349
fn version_ord() {
12671350
let versions: &[Ranges<u32>] = &[

0 commit comments

Comments
 (0)