1
Fork 0

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,30 +78,28 @@ 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(last) = next.checked_sub(1) { if let Some(right) = next.checked_sub(1) {
let (prev_start, prev_end) = &mut self.map[last]; let (prev_start, prev_end) = self.map[right];
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 {
// Our range starts before the one we found. We'll need // The first range which ends *non-adjacently* to our start.
// to *remove* it, and then try again. // And we can ensure that left <= right.
// let left = self.map.partition_point(|l| l.1 + 1 < start);
// FIXME: This is not so efficient; we may need to let min = std::cmp::min(self.map[left].0, start);
// recurse a bunch of times here. Instead, it's probably let max = std::cmp::max(prev_end, end);
// better to do something like drain_filter(...) on the self.map[right] = (min, max);
// map to be able to delete or modify all the ranges in if left != right {
// start..=end and then potentially re-insert a new self.map.drain(left..right);
// range. }
end = std::cmp::max(end, *prev_end); return true;
self.map.remove(last);
} else { } else {
// We overlap with the previous range, increase it to // We overlap with the previous range, increase it to
// include us. // include us.
@ -109,8 +107,8 @@ impl<I: Idx> IntervalSet<I> {
// Make sure we're actually going to *increase* it though -- // Make sure we're actually going to *increase* it though --
// it may be that end is just inside the previously existing // it may be that end is just inside the previously existing
// set. // set.
return if end > *prev_end { return if end > prev_end {
*prev_end = end; self.map[right].1 = end;
true true
} else { } else {
false false
@ -118,7 +116,7 @@ impl<I: Idx> IntervalSet<I> {
} }
} else { } else {
// Otherwise, we don't overlap, so just insert // Otherwise, we don't overlap, so just insert
self.map.insert(last + 1, (start, end)); self.map.insert(right + 1, (start, end));
return true; return true;
} }
} else { } else {
@ -132,7 +130,6 @@ impl<I: Idx> IntervalSet<I> {
return true; return true;
} }
} }
}
pub fn contains(&self, needle: I) -> bool { pub fn contains(&self, needle: I) -> bool {
let needle = needle.index() as u32; let needle = needle.index() as u32;