use std::ops::{Bound, RangeInclusive};
use itertools::{Itertools, chain};
use rangemap::{RangeInclusiveMap, StepFns, StepLite};
use tor_basic_utils::rangebounds::RangeBoundsExt;
pub fn rangemap_mutate_range<K, V, StepFnsT, E>(
map: &mut RangeInclusiveMap<K, V, StepFnsT>,
range0: &RangeInclusive<K>,
mut update: impl FnMut(&mut Option<V>, &RangeInclusive<K>) -> Result<(), E>,
) -> Result<(), E>
where
K: Ord + Clone + StepLite,
V: PartialEq + Clone,
StepFnsT: StepFns<K>,
{
let relevants = chain!(
map.overlapping(range0).map(|(k, _v)| k.clone()),
map.gaps(range0),
)
.collect_vec();
for relevant in relevants {
let k = {
let k = relevant
.intersect(range0)
.expect("intersection of overlapping ranges was empty");
let fix = |b: Bound<&K>| match b {
Bound::Included(y) => y.clone(),
_other => unreachable!("intersection of closed ranges wasn't closed"),
};
fix(k.0)..=fix(k.1)
};
let v0 = map.get(k.start());
let mut v = v0.cloned();
let r = update(&mut v, &k);
if v.as_ref() != v0 {
if let Some(v) = v {
map.insert(k, v);
} else {
map.remove(k);
}
}
r?;
}
Ok(())
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
#![allow(clippy::string_slice)] use super::*;
use educe::Educe;
use std::fmt::Debug;
use void::{ResultVoidExt as _, Void};
type Range = RangeInclusive<u8>;
const ALL_K: Range = 0..=255;
type Id = u32;
#[derive(Debug, Clone, Educe)]
#[educe(PartialEq)]
struct Value {
v: char,
#[educe(PartialEq(ignore))]
id: Id,
}
#[derive(Debug, Clone, Educe)]
#[educe(Default, PartialEq)]
struct TestState {
map: RangeInclusiveMap<u8, Value>,
#[educe(Default(expression = "[None; _]"))] reference: [Option<char>; 256],
#[educe(PartialEq(ignore))]
ids: IdGenerator,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
struct IdGenerator(Id);
impl IdGenerator {
fn next(&mut self) -> Id {
let r = self.0;
self.0 += 1;
r
}
}
impl TestState {
fn from_iter(elems: impl IntoIterator<Item = (Range, char)>) -> Self {
let mut self_ = TestState::default();
let id = self_.ids.next();
for (range, v) in elems {
self_.map.insert(range.clone(), Value { v, id });
for k in range.clone() {
self_.reference[k as usize] = Some(v);
}
}
self_
}
fn mutate_range<E: Debug>(
&mut self,
range0: Range,
mut real_update: impl FnMut(&Range, &mut Option<Value>) -> Result<(), E>,
) -> Result<(), E> {
let mut updated = [false; 256];
println!("updating range0={range0:?}");
let r = rangemap_mutate_range(
&mut self.map,
&range0,
|value: &mut Option<Value>, range| {
println!("updating range0={range:?} range={range:?}");
assert!(range0.contains(range.start()), "uncontained start");
assert!(range0.contains(range.end()), "uncontained end");
let r = real_update(range, value);
println!("updating range0={range0:?} range={range:?}, to {value:?}, r={r:?}");
for k in range.clone() {
updated[k as usize] = true;
self.reference[k as usize] = value.as_ref().map(|value| value.v);
}
r
},
);
self.check();
if r.is_ok() {
for k in ALL_K {
assert_eq!(
updated[k as usize],
range0.contains(&k),
"updated inconsistency k={k:?}",
);
}
}
r
}
fn set_range(&mut self, range0: Range, val: char, expected_old_values: &str) {
let id = self.ids.next();
self.mutate_range(range0.clone(), |k, vmut| {
assert!(
expected_old_values.contains(vmut.as_ref().map(|v| v.v).unwrap_or('_')),
"{range0:?} {k:?} {val:?} {vmut:?} {expected_old_values:?}"
);
*vmut = Some(Value { v: val, id });
Ok::<_, Void>(())
})
.void_unwrap();
}
fn check(&self) {
for k in ALL_K {
assert_eq!(
self.map.get(&k).map(|v| v.v),
self.reference[k as usize],
"map now implies wrong v at k={k:?}",
);
}
}
}
#[test]
fn mutations() {
let s0 = TestState::from_iter([
(0..=9, 'a'),
(20..=29, 'x'),
]);
{
let mut s = s0.clone();
s.set_range(0..=9, 'b', "a");
}
{
let mut s = s0.clone();
s.mutate_range(10..=19, |_k, v| {
assert_eq!(*v, None);
Ok::<_, Void>(())
})
.void_unwrap();
assert_eq!(s, s0);
s.set_range(10..=19, 'n', "_");
}
{
let mut s = s0.clone();
s.set_range(1..=8, 'b', "a");
s.set_range(7..=9, 'c', "ab");
}
{
let mut s = s0.clone();
s.set_range(5..=25, 'm', "_ax");
}
{
let mut s = s0.clone();
s.mutate_range(5..=25, |k, v| {
*v = Some(Value { v: 'm', id: 1000 });
(*k.start() < 15).then_some(()).ok_or(())
})
.expect_err("terminated early");
}
{
let mut s = s0.clone();
let range = 0..=9;
s.set_range(range.clone(), 'a', "a");
let ent = s.map.get(&1).unwrap();
assert_eq!(ent.id, 0);
let id = s.ids.next();
s.map.insert(range.clone(), Value { v: 'a', id });
let ent = s.map.get(&1).unwrap();
assert_eq!(ent.id, id);
}
}
}