diff options
author | cypherpunks <cypherpunks@torproject.org> | 2018-08-09 21:25:18 +0000 |
---|---|---|
committer | cypherpunks <cypherpunks@torproject.org> | 2018-09-14 15:08:54 +0000 |
commit | 578f7326eda7307c420286c01b57f71925901533 (patch) | |
tree | e07bc81ebc4cef377fb7fabaa69eb429fea68b53 | |
parent | 281854bab7001cc838c91b521b41b666140e124f (diff) | |
download | tor-578f7326eda7307c420286c01b57f71925901533.tar.gz tor-578f7326eda7307c420286c01b57f71925901533.zip |
rust/protover: add ProtoSet::and_not_in()
This is a way more efficient version of retain().
-rw-r--r-- | src/rust/protover/protoset.rs | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/src/rust/protover/protoset.rs b/src/rust/protover/protoset.rs index b99e1a99f7..27c16c7005 100644 --- a/src/rust/protover/protoset.rs +++ b/src/rust/protover/protoset.rs @@ -4,6 +4,8 @@ //! Sets for lazily storing ordered, non-overlapping ranges of integers. +use std::cmp; +use std::iter; use std::slice; use std::str::FromStr; use std::u32; @@ -269,6 +271,57 @@ impl ProtoSet { expanded.retain(f); *self = expanded.into(); } + + /// Returns all the `Version`s in `self` which are not also in the `other` + /// `ProtoSet`. + /// + /// # Examples + /// + /// ``` + /// # use protover::errors::ProtoverError; + /// use protover::protoset::ProtoSet; + /// + /// # fn do_test() -> Result<bool, ProtoverError> { + /// let protoset: ProtoSet = "1,3-6,10-12,15-16".parse()?; + /// let other: ProtoSet = "2,5-7,9-11,14-20".parse()?; + /// + /// let subset: ProtoSet = protoset.and_not_in(&other); + /// + /// assert_eq!(subset.expand(), vec![1, 3, 4, 12]); + /// # + /// # Ok(true) + /// # } + /// # fn main() { do_test(); } // wrap the test so we can use the ? operator + /// ``` + pub fn and_not_in(&self, other: &Self) -> Self { + if self.is_empty() || other.is_empty() { + return self.clone(); + } + + let pairs = self.iter().flat_map(|&(lo, hi)| { + let the_end = (hi + 1, hi + 1); // special case to mark the end of the range. + let excluded_ranges = other + .iter() + .cloned() // have to be owned tuples, to match iter::once(the_end). + .skip_while(move|&(_, hi2)| hi2 < lo) // skip the non-overlapping ranges. + .take_while(move|&(lo2, _)| lo2 <= hi) // take all the overlapping ones. + .chain(iter::once(the_end)); + + let mut nextlo = lo; + excluded_ranges.filter_map(move |(excluded_lo, excluded_hi)| { + let pair = if nextlo < excluded_lo { + Some((nextlo, excluded_lo - 1)) + } else { + None + }; + nextlo = cmp::min(excluded_hi, u32::MAX - 1) + 1; + pair + }) + }); + + let pairs = pairs.collect(); + ProtoSet::is_ok(ProtoSet{ pairs }).expect("should be already sorted") + } } impl FromStr for ProtoSet { |