some tests for RangeMap
This commit is contained in:
parent
65a31317a4
commit
de80bcbdbf
@ -16,7 +16,7 @@ script:
|
||||
- |
|
||||
# Test plain miri
|
||||
cargo build --release --features "cargo_miri" &&
|
||||
cargo test --release &&
|
||||
cargo test --release --all &&
|
||||
cargo install --features "cargo_miri"
|
||||
- |
|
||||
# Test cargo miri
|
||||
|
@ -8,7 +8,6 @@ version = "0.1.0"
|
||||
workspace = "../.."
|
||||
|
||||
[lib]
|
||||
test = false
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
|
@ -1,4 +1,9 @@
|
||||
//! Implements a map from disjoint non-empty integer ranges to data associated with those ranges
|
||||
//! Implements a map from integer indices to data.
|
||||
//! Rather than storing data for every index, internally, this maps entire ranges to the data.
|
||||
//! To this end, the APIs all work on ranges, not on individual integers. Ranges are split as
|
||||
//! necessary (e.g. when [0,5) is first associated with X, and then [1,2) is mutated).
|
||||
//! Users must not depend on whether a range is coalesced or not, even though this is observable
|
||||
//! via the iteration APIs.
|
||||
use std::collections::{BTreeMap};
|
||||
use std::ops;
|
||||
|
||||
@ -21,6 +26,7 @@ struct Range {
|
||||
|
||||
impl Range {
|
||||
fn range(offset: u64, len: u64) -> ops::Range<Range> {
|
||||
assert!(len > 0);
|
||||
// We select all elements that are within
|
||||
// the range given by the offset into the allocation and the length.
|
||||
// This is sound if all ranges that intersect with the argument range, are in the
|
||||
@ -36,6 +42,7 @@ impl Range {
|
||||
left..right
|
||||
}
|
||||
|
||||
/// Tests if all of [offset, offset+len) are contained in this range.
|
||||
fn overlaps(&self, offset: u64, len: u64) -> bool {
|
||||
assert!(len > 0);
|
||||
offset < self.end && offset+len >= self.start
|
||||
@ -48,6 +55,7 @@ impl<T> RangeMap<T> {
|
||||
}
|
||||
|
||||
fn iter_with_range<'a>(&'a self, offset: u64, len: u64) -> impl Iterator<Item=(&'a Range, &'a T)> + 'a {
|
||||
assert!(len > 0);
|
||||
self.map.range(Range::range(offset, len))
|
||||
.filter_map(move |(range, data)| {
|
||||
if range.overlaps(offset, len) {
|
||||
@ -63,7 +71,7 @@ impl<T> RangeMap<T> {
|
||||
}
|
||||
|
||||
fn split_entry_at(&mut self, offset: u64) where T: Clone {
|
||||
let range = match self.iter_with_range(offset, 0).next() {
|
||||
let range = match self.iter_with_range(offset, 1).next() {
|
||||
Some((&range, _)) => range,
|
||||
None => return,
|
||||
};
|
||||
@ -88,6 +96,7 @@ impl<T> RangeMap<T> {
|
||||
pub fn iter_mut_with_gaps<'a>(&'a mut self, offset: u64, len: u64) -> impl Iterator<Item=&'a mut T> + 'a
|
||||
where T: Clone
|
||||
{
|
||||
assert!(len > 0);
|
||||
// Preparation: Split first and last entry as needed.
|
||||
self.split_entry_at(offset);
|
||||
self.split_entry_at(offset+len);
|
||||
@ -112,14 +121,15 @@ impl<T> RangeMap<T> {
|
||||
{
|
||||
// Do a first iteration to collect the gaps
|
||||
let mut gaps = Vec::new();
|
||||
let mut last_end = None;
|
||||
let mut last_end = offset;
|
||||
for (range, _) in self.iter_with_range(offset, len) {
|
||||
if let Some(last_end) = last_end {
|
||||
if last_end < range.start {
|
||||
gaps.push(Range { start: last_end, end: range.start });
|
||||
}
|
||||
if last_end < range.start {
|
||||
gaps.push(Range { start: last_end, end: range.start });
|
||||
}
|
||||
last_end = Some(range.end);
|
||||
last_end = range.end;
|
||||
}
|
||||
if last_end < offset+len {
|
||||
gaps.push(Range { start: last_end, end: offset+len });
|
||||
}
|
||||
|
||||
// Add default for all gaps
|
||||
@ -147,3 +157,43 @@ impl<T> RangeMap<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Query the map at every offset in the range and collect the results.
|
||||
fn to_vec<T: Copy>(map: &RangeMap<T>, offset: u64, len: u64) -> Vec<T> {
|
||||
(offset..offset+len).into_iter().map(|i| *map.iter(i, 1).next().unwrap()).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_insert() {
|
||||
let mut map = RangeMap::<i32>::new();
|
||||
// Insert
|
||||
for x in map.iter_mut(10, 1) {
|
||||
*x = 42;
|
||||
}
|
||||
// Check
|
||||
assert_eq!(to_vec(&map, 10, 1), vec![42]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gaps() {
|
||||
let mut map = RangeMap::<i32>::new();
|
||||
for x in map.iter_mut(11, 1) {
|
||||
*x = 42;
|
||||
}
|
||||
for x in map.iter_mut(15, 1) {
|
||||
*x = 42;
|
||||
}
|
||||
|
||||
// Now request a range that needs three gaps filled
|
||||
for x in map.iter_mut(10, 10) {
|
||||
if *x != 42 { *x = 23; }
|
||||
}
|
||||
|
||||
assert_eq!(to_vec(&map, 10, 10), vec![23, 42, 23, 23, 23, 42, 23, 23, 23, 23]);
|
||||
assert_eq!(to_vec(&map, 13, 5), vec![23, 23, 42, 23, 23]);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user