Skip to content

Commit dae485b

Browse files
committed
Fix algorithm with new proptest
1 parent 9e76cbb commit dae485b

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
@@ -879,7 +879,7 @@ impl<V> IntoIterator for Ranges<V> {
879879
}
880880
}
881881

882-
impl<V: Ord + Debug> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
882+
impl<V: Ord> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
883883
/// Constructor from arbitrary, unsorted and potentially overlapping ranges.
884884
///
885885
/// This is equivalent, but faster, to computing the [`Ranges::union`] of the
@@ -889,6 +889,10 @@ impl<V: Ord + Debug> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
889889
// 1. The segments are sorted, from lowest to highest (through `Ord`): By sorting.
890890
// 2. Each segment contains at least one version (start < end): By `union`.
891891
// 3. There is at least one version between two segments: By `union`.
892+
893+
// For this implementation, we choose to only build a single smallvec and insert or remove
894+
// in it, instead of e.g. collecting the segments into one smallvec, sorting that and then
895+
// construction the second smallvec without shifting.
892896
let mut segments: SmallVec<[Interval<V>; 1]> = SmallVec::new();
893897

894898
for segment in iter {
@@ -908,7 +912,8 @@ impl<V: Ord + Debug> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
908912
&segment.start_bound(),
909913
);
910914

911-
// Is it overlapping with the following segment?
915+
// Is it overlapping with the following segment? We'll check if there's more than one
916+
// overlap later.
912917
let next_overlapping = insertion_point < segments.len()
913918
&& !end_before_start_with_gap(
914919
&segment.end_bound(),
@@ -920,29 +925,112 @@ impl<V: Ord + Debug> FromIterator<(Bound<V>, Bound<V>)> for Ranges<V> {
920925
// previous: |------|
921926
// segment: |------|
922927
// following: |------|
928+
// final: |---------------|
929+
//
930+
// OR
923931
//
932+
// previous: |------|
933+
// segment: |-----------|
934+
// following: |----|
924935
// final: |---------------|
936+
//
937+
// OR
938+
//
939+
// previous: |------|
940+
// segment: |----------------|
941+
// following: |----| |------|
942+
// final: |------------------------|
925943
// We merge all three segments into one, which is effectively removing one of
926944
// two previously inserted and changing the bounds on the other.
927-
let following = segments.remove(insertion_point);
928-
segments[insertion_point - 1].1 = following.1;
945+
946+
// Remove all elements covered by the final element
947+
let mut following = segments.remove(insertion_point);
948+
while insertion_point < segments.len()
949+
&& !end_before_start_with_gap(
950+
&segment.end_bound(),
951+
&segments[insertion_point].start_bound(),
952+
)
953+
{
954+
following = segments.remove(insertion_point);
955+
}
956+
957+
// Set end to max(segment.end, <last overlapping segment>.end)
958+
if cmp_bounds_end(segment.end_bound(), following.end_bound())
959+
.unwrap()
960+
.is_lt()
961+
{
962+
segments[insertion_point - 1].1 = following.1;
963+
} else {
964+
segments[insertion_point - 1].1 = segment.1;
965+
}
929966
}
930967
(true, false) => {
931968
// previous: |------|
932969
// segment: |------|
933970
// following: |------|
934971
//
972+
// OR
973+
//
974+
// previous: |----------|
975+
// segment: |---|
976+
// following: |------|
977+
//
935978
// final: |----------| |------|
936979
// We can reuse the existing element by extending it.
937-
segments[insertion_point - 1].1 = segment.1;
980+
981+
// Set end to max(segment.end, <previous>.end)
982+
if cmp_bounds_end(
983+
segments[insertion_point - 1].end_bound(),
984+
segment.end_bound(),
985+
)
986+
.unwrap()
987+
.is_lt()
988+
{
989+
segments[insertion_point - 1].1 = segment.1;
990+
}
938991
}
939992
(false, true) => {
940993
// previous: |------|
941994
// segment: |------|
942995
// following: |------|
996+
// final: |------| |----------|
997+
//
998+
// OR
999+
//
1000+
// previous: |------|
1001+
// segment: |----------|
1002+
// following: |---|
1003+
// final: |------| |----------|
1004+
//
1005+
// OR
1006+
//
1007+
// previous: |------|
1008+
// segment: |------------|
1009+
// following: |---| |------|
9431010
//
944-
// final: |------| |---------|
1011+
// final: |------| |-----------------|
9451012
// We can reuse the existing element by extending it.
1013+
1014+
// Remove all fully covered segments so the next element is the last one that
1015+
// overlaps.
1016+
while insertion_point + 1 < segments.len()
1017+
&& !end_before_start_with_gap(
1018+
&segment.end_bound(),
1019+
&segments[insertion_point + 1].start_bound(),
1020+
)
1021+
{
1022+
// We know that the one after also overlaps, so we can drop the current
1023+
// following.
1024+
segments.remove(insertion_point);
1025+
}
1026+
1027+
// Set end to max(segment.end, <last overlapping segment>.end)
1028+
if cmp_bounds_end(segments[insertion_point].end_bound(), segment.end_bound())
1029+
.unwrap()
1030+
.is_lt()
1031+
{
1032+
segments[insertion_point].1 = segment.1;
1033+
}
9461034
segments[insertion_point].0 = segment.0;
9471035
}
9481036
(false, false) => {
@@ -1267,8 +1355,12 @@ pub mod tests {
12671355

12681356
#[test]
12691357
fn from_iter_valid(segments in proptest::collection::vec(any::<(Bound<u32>, Bound<u32>)>(), ..30)) {
1270-
// We check invariants in the method.
1271-
Ranges::from_iter(segments.clone());
1358+
let mut expected = Ranges::empty();
1359+
for segment in &segments {
1360+
expected = expected.union(&Ranges::from_range_bounds(*segment));
1361+
}
1362+
let actual = Ranges::from_iter(segments.clone());
1363+
assert_eq!(expected, actual, "{segments:?}");
12721364
}
12731365
}
12741366

@@ -1306,15 +1398,6 @@ pub mod tests {
13061398
);
13071399
}
13081400

1309-
#[test]
1310-
fn from_iter_regression() {
1311-
Ranges::from_iter([
1312-
(Included(0), Included(0)),
1313-
(Excluded(1u32), Unbounded),
1314-
(Included(0), Included(0)),
1315-
]);
1316-
}
1317-
13181401
#[test]
13191402
fn version_ord() {
13201403
let versions: &[Ranges<u32>] = &[

0 commit comments

Comments
 (0)