1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 05:12:40 +03:00

add RangeSet type

The idea is to use this to maintain a set of dirty StableRowIndex
values for the mux rpc.
This commit is contained in:
Wez Furlong 2020-01-04 18:10:53 -08:00
parent 400c8aa66d
commit 60e05dbe95
4 changed files with 339 additions and 0 deletions

8
Cargo.lock generated
View File

@ -1892,6 +1892,13 @@ dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rangeset"
version = "0.1.0"
dependencies = [
"num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ratelimit_meter"
version = "5.0.0"
@ -2792,6 +2799,7 @@ dependencies = [
"portable-pty 0.2.0",
"pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"promise 0.2.0",
"rangeset 0.1.0",
"ratelimit_meter 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serial 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -17,6 +17,7 @@ anyhow = "1.0"
thiserror = "1.0"
base64 = "0.10"
base91 = { path = "base91" }
rangeset = { path = "rangeset" }
bitflags = "1.0"
crossbeam-channel = "0.3"
dirs = "1.0"

8
rangeset/Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "rangeset"
version = "0.1.0"
authors = ["Wez Furlong <wez@wezfurlong.org>"]
edition = "2018"
[dependencies]
num = "0.2"

322
rangeset/src/lib.rs Normal file
View File

@ -0,0 +1,322 @@
use num::Integer;
use std::cmp::{max, min};
use std::fmt::Debug;
use std::ops::Range;
/// Track a set of integers, collapsing adjacent integers into ranges.
/// Internally stores the set in an array of ranges.
/// Allows adding and subtracting ranges.
#[derive(Debug, Clone, PartialEq)]
pub struct RangeSet<T: Integer + Copy> {
ranges: Vec<Range<T>>,
}
fn range_is_empty<T: Integer>(range: &Range<T>) -> bool {
range.start == range.end
}
/// Returns true if r1 intersects r2
fn intersects_range<T: Integer + Copy + Debug>(r1: &Range<T>, r2: &Range<T>) -> bool {
let start = max(r1.start, r2.start);
let end = min(r1.end, r2.end);
end > start
}
#[allow(dead_code)]
/// Computes the intersection of r1 and r2
fn range_intersection<T: Integer + Copy + Debug>(r1: &Range<T>, r2: &Range<T>) -> Option<Range<T>> {
let start = max(r1.start, r2.start);
let end = min(r1.end, r2.end);
if end > start {
Some(start..end)
} else {
None
}
}
/// Computes the r1 - r2, which may result in up to two non-overlapping ranges.
fn range_subtract<T: Integer + Copy + Debug>(
r1: &Range<T>,
r2: &Range<T>,
) -> (Option<Range<T>>, Option<Range<T>>) {
let i_start = max(r1.start, r2.start);
let i_end = min(r1.end, r2.end);
if i_end > i_start {
let a = if i_start == r1.start {
// Intersection overlaps with the LHS
None
} else {
// The LHS up to the intersection
Some(r1.start..r1.end.min(i_start))
};
let b = if i_end == r1.end {
// Intersection overlaps with the RHS
None
} else {
// The intersection up to the RHS
Some(r1.end.min(i_end)..r1.end)
};
(a, b)
} else {
// No intersection, so we're left with r1 with nothing removed
(Some(r1.clone()), None)
}
}
/// Merge two ranges to produce their union
fn range_union<T: Integer>(r1: Range<T>, r2: Range<T>) -> Range<T> {
let start = r1.start.min(r2.start);
let end = r1.end.max(r2.end);
start..end
}
impl<T: Integer + Copy + Debug> RangeSet<T> {
/// Create a new set
pub fn new() -> Self {
Self { ranges: vec![] }
}
/// Returns true if this set is empty
pub fn is_empty(&self) -> bool {
self.ranges.is_empty()
}
/// Returns true if this set contains the specified integer
pub fn contains(&self, value: T) -> bool {
for r in &self.ranges {
if r.contains(&value) {
return true;
}
}
false
}
/// Returns a rangeset containing all of the integers that are present
/// in self but not in other.
/// The current implementation is `O(n**2)` but this should be "OK"
/// as the likely scenario is that there will be a large contiguous
/// range for the scrollback, and a smaller contiguous range for changes
/// in the viewport.
/// If that doesn't hold up, we can improve this.
pub fn difference(&self, other: &Self) -> Self {
let mut result = Self::new();
for my_range in &self.ranges {
for other_range in &other.ranges {
match range_subtract(my_range, other_range) {
(Some(a), Some(b)) => {
result.add_range(a);
result.add_range(b);
}
(Some(a), None) | (None, Some(a)) if a != *other_range => {
result.add_range(a);
}
_ => {}
}
}
}
result
}
/// Remove a single integer from the set
pub fn remove(&mut self, value: T) {
self.remove_range(value..value + num::one());
}
/// Remove a range of integers from the set
pub fn remove_range(&mut self, range: Range<T>) {
let mut to_add = vec![];
let mut to_remove = vec![];
for (idx, r) in self.ranges.iter().enumerate() {
match range_subtract(r, &range) {
(None, None) => to_remove.push(idx),
(Some(a), Some(b)) => {
to_remove.push(idx);
to_add.push(a);
to_add.push(b);
}
(Some(a), None) | (None, Some(a)) if a != *r => {
to_remove.push(idx);
to_add.push(a);
}
_ => {}
}
}
for idx in to_remove.into_iter().rev() {
self.ranges.remove(idx);
}
for r in to_add {
self.add_range(r);
}
}
/// Remove a set of ranges from this set
pub fn remove_set(&mut self, set: &Self) {
for r in set.iter() {
self.remove_range(r.clone());
}
}
/// Add a single integer to the set
pub fn add(&mut self, value: T) {
self.add_range(value..value + num::one());
}
/// Add a range of integers to the set
pub fn add_range(&mut self, range: Range<T>) {
if range_is_empty(&range) {
return;
}
if self.ranges.is_empty() {
self.ranges.push(range.clone());
return;
}
match self.intersection_helper(&range) {
(Some(a), Some(b)) if b == a + 1 => {
// This range intersects with two or more adjacent ranges and will
// therefore join them together
let second = self.ranges[b].clone();
let merged = range_union(range, second);
self.ranges.remove(b);
return self.add_range(merged);
}
(Some(a), _) => self.merge_into_range(a, range),
(None, Some(_)) => unreachable!(),
(None, None) => {
// No intersection, so find the insertion point
let idx = self.insertion_point(&range);
self.ranges.insert(idx, range.clone());
}
}
}
/// Add a set of ranges to this set
pub fn add_set(&mut self, set: &Self) {
for r in set.iter() {
self.add_range(r.clone());
}
}
fn merge_into_range(&mut self, idx: usize, range: Range<T>) {
let existing = self.ranges[idx].clone();
self.ranges[idx] = range_union(existing, range);
}
fn intersection_helper(&self, range: &Range<T>) -> (Option<usize>, Option<usize>) {
let mut first = None;
for (idx, r) in self.ranges.iter().enumerate() {
if intersects_range(r, range) || r.end == range.start {
if first.is_some() {
return (first, Some(idx));
}
first = Some(idx);
}
}
(first, None)
}
fn insertion_point(&self, range: &Range<T>) -> usize {
for (idx, r) in self.ranges.iter().enumerate() {
if range.end < r.start {
return idx;
}
}
self.ranges.len()
}
/// Returns an iterator over the ranges that comprise the set
pub fn iter(&self) -> impl Iterator<Item = &Range<T>> {
self.ranges.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn collect<T: Integer + Copy + Debug>(set: &RangeSet<T>) -> Vec<Range<T>> {
set.iter().cloned().collect()
}
#[test]
fn add_range() {
let mut set = RangeSet::new();
set.add(1);
set.add(2);
set.add(4);
assert_eq!(collect(&set), vec![1..3, 4..5]);
set.add_range(2..6);
assert_eq!(collect(&set), vec![1..6]);
}
#[test]
fn remove_range() {
let mut set = RangeSet::new();
set.add_range(1..5);
set.add_range(8..10);
assert_eq!(collect(&set), vec![1..5, 8..10]);
// Middle
set.remove(2);
assert_eq!(collect(&set), vec![1..2, 3..5, 8..10]);
// RHS
set.remove_range(4..7);
assert_eq!(collect(&set), vec![1..2, 3..4, 8..10]);
// Complete overlap of one range, LHS overlap with another
set.remove_range(3..9);
assert_eq!(collect(&set), vec![1..2, 9..10]);
}
#[test]
fn difference() {
let mut set = RangeSet::new();
set.add_range(1..10);
let mut other = RangeSet::new();
other.add_range(1..15);
let diff = other.difference(&set);
assert_eq!(collect(&diff), vec![10..15]);
let diff = set.difference(&other);
assert_eq!(collect(&diff), vec![]);
}
#[test]
fn difference_more() {
let mut set = RangeSet::new();
set.add(1);
set.add(3);
set.add(5);
let mut other = RangeSet::new();
other.add(2);
other.add(4);
other.add(6);
let diff = other.difference(&set);
assert_eq!(collect(&diff), vec![2..3, 4..5, 6..7]);
let diff = set.difference(&other);
assert_eq!(collect(&diff), vec![1..2, 3..4, 5..6]);
}
}