optimize insert_range method of IntervalSet

This commit is contained in:
SparrowLii 2022-05-10 19:27:40 +08:00
parent 4799baa70d
commit eead168dd7

View File

@ -70,7 +70,7 @@ impl<I: Idx> IntervalSet<I> {
/// Returns true if we increased the number of elements present. /// Returns true if we increased the number of elements present.
pub fn insert_range(&mut self, range: impl RangeBounds<I> + Clone) -> bool { pub fn insert_range(&mut self, range: impl RangeBounds<I> + Clone) -> bool {
let start = inclusive_start(range.clone()); let start = inclusive_start(range.clone());
let Some(mut end) = inclusive_end(self.domain, range) else { let Some(end) = inclusive_end(self.domain, range) else {
// empty range // empty range
return false; return false;
}; };
@ -78,59 +78,56 @@ impl<I: Idx> IntervalSet<I> {
return false; return false;
} }
loop { // This condition looks a bit weird, but actually makes sense.
// This condition looks a bit weird, but actually makes sense. //
// // if r.0 == end + 1, then we're actually adjacent, so we want to
// if r.0 == end + 1, then we're actually adjacent, so we want to // continue to the next range. We're looking here for the first
// continue to the next range. We're looking here for the first // range which starts *non-adjacently* to our end.
// range which starts *non-adjacently* to our end. let next = self.map.partition_point(|r| r.0 <= end + 1);
let next = self.map.partition_point(|r| r.0 <= end + 1); if let Some(right) = next.checked_sub(1) {
if let Some(last) = next.checked_sub(1) { let (prev_start, prev_end) = self.map[right];
let (prev_start, prev_end) = &mut self.map[last]; if prev_end + 1 >= start {
if *prev_end + 1 >= start { // If the start for the inserted range is adjacent to the
// If the start for the inserted range is adjacent to the // end of the previous, we can extend the previous range.
// end of the previous, we can extend the previous range. if start < prev_start {
if start < *prev_start { // The first range which ends *non-adjacently* to our start.
// Our range starts before the one we found. We'll need // And we can ensure that left <= right.
// to *remove* it, and then try again. let left = self.map.partition_point(|l| l.1 + 1 < start);
// let min = std::cmp::min(self.map[left].0, start);
// FIXME: This is not so efficient; we may need to let max = std::cmp::max(prev_end, end);
// recurse a bunch of times here. Instead, it's probably self.map[right] = (min, max);
// better to do something like drain_filter(...) on the if left != right {
// map to be able to delete or modify all the ranges in self.map.drain(left..right);
// start..=end and then potentially re-insert a new
// range.
end = std::cmp::max(end, *prev_end);
self.map.remove(last);
} else {
// We overlap with the previous range, increase it to
// include us.
//
// Make sure we're actually going to *increase* it though --
// it may be that end is just inside the previously existing
// set.
return if end > *prev_end {
*prev_end = end;
true
} else {
false
};
} }
} else {
// Otherwise, we don't overlap, so just insert
self.map.insert(last + 1, (start, end));
return true; return true;
} else {
// We overlap with the previous range, increase it to
// include us.
//
// Make sure we're actually going to *increase* it though --
// it may be that end is just inside the previously existing
// set.
return if end > prev_end {
self.map[right].1 = end;
true
} else {
false
};
} }
} else { } else {
if self.map.is_empty() { // Otherwise, we don't overlap, so just insert
// Quite common in practice, and expensive to call memcpy self.map.insert(right + 1, (start, end));
// with length zero.
self.map.push((start, end));
} else {
self.map.insert(next, (start, end));
}
return true; return true;
} }
} else {
if self.map.is_empty() {
// Quite common in practice, and expensive to call memcpy
// with length zero.
self.map.push((start, end));
} else {
self.map.insert(next, (start, end));
}
return true;
} }
} }