From 58719434c7920bc1374eadebbf082b4235d47da1 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sun, 29 Dec 2024 19:03:53 -0500 Subject: [PATCH 1/2] improve gaps() time complexity for inclusive range map I didn't initially realize that the inclusive range map is a completely different implementation. Implement an O(log n) initial seek for the inclusive range map, similar to abf6bec733a5e8939cf0d9a93f0f35ed7cee3de2. --- src/inclusive_map.rs | 83 ++++++++++++++++++++++---------------------- src/map.rs | 7 ++-- 2 files changed, 44 insertions(+), 46 deletions(-) diff --git a/src/inclusive_map.rs b/src/inclusive_map.rs index 6ba7e03..c15902a 100644 --- a/src/inclusive_map.rs +++ b/src/inclusive_map.rs @@ -544,14 +544,15 @@ where /// /// The iterator element type is `RangeInclusive`. pub fn gaps<'a>(&'a self, outer_range: &'a RangeInclusive) -> Gaps<'a, K, V, StepFnsT> { + let overlap_iter = self.overlapping(outer_range); Gaps { - done: false, - outer_range, - keys: self.btm.keys(), + candidate_needs_plus_one: false, + candidate_start: outer_range.start(), + query_end: &outer_range.end(), + btm_range_iter: overlap_iter.btm_range_iter, // We'll start the candidate range at the start of the outer range // without checking what's there. Each time we yield an item, // we'll skip any ranges we find before the next gap. - candidate_start: outer_range.start().clone(), _phantom: PhantomData, } } @@ -801,10 +802,10 @@ pub struct Gaps<'a, K, V, StepFnsT> { /// avoid overflowing when dealing with inclusive ranges. /// /// All other things here are ignored if `done` is `true`. - done: bool, - outer_range: &'a RangeInclusive, - keys: alloc::collections::btree_map::Keys<'a, RangeInclusiveStartWrapper, V>, - candidate_start: K, + candidate_needs_plus_one: bool, + candidate_start: &'a K, + query_end: &'a K, + btm_range_iter: alloc::collections::btree_map::Range<'a, RangeInclusiveStartWrapper, V>, _phantom: PhantomData, } @@ -824,49 +825,47 @@ where type Item = RangeInclusive; fn next(&mut self) -> Option { - if self.done { - // We've already passed the end of the outer range; - // there are no more gaps to find. - return None; - } + while let Some(overlap) = self.btm_range_iter.next() { + let overlap = overlap.0; - for item in &mut self.keys { - let range = &item.range; - if *range.end() < self.candidate_start { - // We're already completely past it; ignore it. - } else if *range.start() <= self.candidate_start { - // We're inside it; move past it. - if *range.end() >= *self.outer_range.end() { - // Special case: it goes all the way to the end so we - // can't safely skip past it. (Might overflow.) - self.done = true; - return None; - } - self.candidate_start = StepFnsT::add_one(range.end()); - } else if *range.start() <= *self.outer_range.end() { - // It starts before the end of the outer range, - // so move past it and then yield a gap. - let gap = self.candidate_start.clone()..=StepFnsT::sub_one(range.start()); - if *range.end() >= *self.outer_range.end() { - // Special case: it goes all the way to the end so we - // can't safely skip past it. (Might overflow.) - self.done = true; - } else { - self.candidate_start = StepFnsT::add_one(range.end()); - } + // If the range in the map has advanced beyond the query range, return + // any tail gap. + if *self.query_end < *overlap.start() { + break; + } + + let candidate_needs_plus_one = + core::mem::replace(&mut self.candidate_needs_plus_one, true); + + let cur_candidate_start = core::mem::replace(&mut self.candidate_start, overlap.end()); + + let cur_candidate_start = if candidate_needs_plus_one { + StepFnsT::add_one(cur_candidate_start) + } else { + cur_candidate_start.clone() + }; + + if cur_candidate_start < *overlap.start() { + let gap = cur_candidate_start..=StepFnsT::sub_one(overlap.start()); return Some(gap); } } // Now that we've run out of items, the only other possible // gap is one at the end of the outer range. - self.done = true; - if self.candidate_start <= *self.outer_range.end() { + let candidate_needs_plus_one = core::mem::replace(&mut self.candidate_needs_plus_one, true); + + let cur_candidate_start = core::mem::replace(&mut self.candidate_start, self.query_end); + if candidate_needs_plus_one { + if *cur_candidate_start < *self.query_end { + return Some(StepFnsT::add_one(cur_candidate_start)..=self.query_end.clone()); + } + } else if *cur_candidate_start <= *self.query_end { // There's a gap at the end! - Some(self.candidate_start.clone()..=self.outer_range.end().clone()) - } else { - None + return Some(cur_candidate_start.clone()..=self.query_end.clone()); } + + None } } diff --git a/src/map.rs b/src/map.rs index d550759..2ed7243 100644 --- a/src/map.rs +++ b/src/map.rs @@ -725,7 +725,7 @@ where { type Item = Range; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { // Keep track of the next range in the map beyond the current returned range. for overlap in self.btm_range_iter.by_ref() { let overlap = &overlap.0.range; @@ -736,13 +736,12 @@ where break; } - let original_start = self.candidate_start; - self.candidate_start = &overlap.end; + let original_start = core::mem::replace(&mut self.candidate_start, &overlap.end); // The query range overhangs to the left, return a gap. if *original_start < overlap.start { let gap = original_start.clone()..overlap.start.clone(); - return Some(gap) + return Some(gap); } // The remaining query range starts within the current From 6a9e6f35f8a42be148bd68a10b9f080c56be2f95 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 30 Dec 2024 11:29:13 -0500 Subject: [PATCH 2/2] fix some clippy issues --- src/inclusive_map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inclusive_map.rs b/src/inclusive_map.rs index c15902a..36ff7da 100644 --- a/src/inclusive_map.rs +++ b/src/inclusive_map.rs @@ -548,7 +548,7 @@ where Gaps { candidate_needs_plus_one: false, candidate_start: outer_range.start(), - query_end: &outer_range.end(), + query_end: outer_range.end(), btm_range_iter: overlap_iter.btm_range_iter, // We'll start the candidate range at the start of the outer range // without checking what's there. Each time we yield an item, @@ -825,7 +825,7 @@ where type Item = RangeInclusive; fn next(&mut self) -> Option { - while let Some(overlap) = self.btm_range_iter.next() { + for overlap in self.btm_range_iter.by_ref() { let overlap = overlap.0; // If the range in the map has advanced beyond the query range, return