| /// Represents a set of `Size` values as a sorted list of ranges. | |
| /// | |
| /// These are (offset, length) pairs, and they are sorted and mutually disjoint, | |
| /// and never adjacent (i.e. there's always a gap between two of them). | |
| #[derive(Debug, Clone)] | |
| pub struct RangeSet<T>(pub Vec<(T, T)>); | |
| impl<T> RangeSet<T> | |
| where | |
| T: Copy + Ord + Default, | |
| T: core::ops::Add<Output = T>, | |
| T: core::ops::Sub<Output = T>, | |
| { | |
| pub fn new() -> Self { | |
| Self(Vec::new()) | |
| } | |
| pub fn add_range(&mut self, offset: T, size: T) { | |
| if size == T::default() { | |
| // No need to track empty ranges. | |
| return; | |
| } | |
| let v = &mut self.0; | |
| // We scan for a partition point where the left partition is all the elements that end | |
| // strictly before we start. Those are elements that are too "low" to merge with us. | |
| let idx = | |
| v.partition_point(|&(other_offset, other_size)| other_offset + other_size < offset); | |
| // Now we want to either merge with the first element of the second partition, or insert ourselves before that. | |
| if let Some(&(other_offset, other_size)) = v.get(idx) | |
| && offset + size >= other_offset | |
| { | |
| // Their end is >= our start (otherwise it would not be in the 2nd partition) and | |
| // our end is >= their start. This means we can merge the ranges. | |
| let new_start = other_offset.min(offset); | |
| let mut new_end = (other_offset + other_size).max(offset + size); | |
| // We grew to the right, so merge with overlapping/adjacent elements. | |
| // (We also may have grown to the left, but that can never make us adjacent with | |
| // anything there since we selected the first such candidate via `partition_point`.) | |
| let mut scan_right = 1; | |
| while let Some(&(next_offset, next_size)) = v.get(idx + scan_right) | |
| && new_end >= next_offset | |
| { | |
| // Increase our size to absorb the next element. | |
| new_end = new_end.max(next_offset + next_size); | |
| // Look at the next element. | |
| scan_right += 1; | |
| } | |
| // Update the element we grew. | |
| v[idx] = (new_start, new_end - new_start); | |
| // Remove the elements we absorbed (if any). | |
| if scan_right > 1 { | |
| drop(v.drain((idx + 1)..(idx + scan_right))); | |
| } | |
| } else { | |
| // Insert new element. | |
| v.insert(idx, (offset, size)); | |
| } | |
| } | |
| } |