use std::ops::RangeInclusive;
use super::*;
fn sibling(prefix: &TestPrefix) -> Option<TestPrefix> {
if prefix.prefix_len() == 0 {
return None;
}
let differing_bit = 1u32 << (32 - prefix.prefix_len() as u32);
Some(TestPrefix::from_repr_len(
prefix.repr() ^ differing_bit,
prefix.prefix_len(),
))
}
fn range(prefix: &TestPrefix) -> RangeInclusive<u32> {
let start = prefix.repr();
let end = start + ((1u64 << (32 - prefix.prefix_len() as u32)) - 1) as u32;
start..=end
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Reduced {
No,
Irredundant,
Minimal,
}
fn covered_space(
set: &PrefixSet<TestPrefix>,
reduced: Reduced,
) -> Option<Vec<RangeInclusive<u32>>> {
let mut merged: Vec<RangeInclusive<u32>> = Vec::new();
let mut previous: Option<TestPrefix> = None;
for prefix in set {
let cur = range(&prefix);
if let Some(previous) = previous {
let prev = range(&previous);
let overlaps = cur.start() <= prev.end(); let mergeable = sibling(&previous) == Some(prefix); let violated = match reduced {
Reduced::No => false,
Reduced::Irredundant => overlaps,
Reduced::Minimal => overlaps || mergeable,
};
if violated {
return None;
}
}
previous = Some(prefix);
match merged.last_mut() {
Some(last) if *cur.start() <= last.end().saturating_add(1) => {
let end = *cur.end().max(last.end());
*last = *last.start()..=end;
}
_ => merged.push(cur),
}
}
Some(merged)
}
qc!(aggregate_set, _aggregate_set);
fn _aggregate_set(prefixes: Vec<TestPrefix>) -> bool {
let original = prefixes.iter().copied().collect::<PrefixSet<_>>();
let mut aggregated = original.clone();
aggregated.aggregate();
let mut double_agg = aggregated.clone();
double_agg.aggregate();
let original_space = covered_space(&original, Reduced::No).unwrap();
let Some(aggregated_space) = covered_space(&aggregated, Reduced::Minimal) else {
return false; };
original_space == aggregated_space
&& original.address_count() == aggregated.address_count()
&& aggregated.len() == aggregated.iter().count()
&& aggregated.0.check_memory_alloc()
&& aggregated == double_agg
}
fn lpm_map(
map: &PrefixMap<TestPrefix, u8>,
reduced: Reduced,
) -> Option<rangemap::RangeInclusiveMap<u32, u8>> {
let entries: Vec<(TestPrefix, u8)> = map.iter().map(|(p, v)| (p, *v)).collect();
if reduced != Reduced::No {
for &(p, v) in &entries {
let redundant = entries
.iter()
.filter(|(q, _)| q.prefix_len() < p.prefix_len() && q.contains(&p))
.max_by_key(|(q, _)| q.prefix_len())
.is_some_and(|&(_, ancestor_value)| ancestor_value == v);
let mergeable = sibling(&p).is_some_and(|sib| map.get(&sib) == Some(&v));
let violated = match reduced {
Reduced::No => false,
Reduced::Irredundant => redundant,
Reduced::Minimal => redundant || mergeable,
};
if violated {
return None;
}
}
}
let mut sorted = entries;
sorted.sort_by_key(|(p, _)| p.prefix_len());
let mut lpm = rangemap::RangeInclusiveMap::new();
for (p, v) in sorted {
lpm.insert(range(&p), v);
}
Some(lpm)
}
qc!(aggregate_consistent_map, _aggregate_consistent_map);
fn _aggregate_consistent_map(entries: Vec<(TestPrefix, u8)>) -> bool {
let original: PrefixMap<TestPrefix, u8> = entries.into_iter().collect();
let mut aggregated = original.clone();
aggregated.aggregate_consistent();
let mut twice = aggregated.clone();
twice.aggregate_consistent();
let original_lpm = lpm_map(&original, Reduced::No).unwrap();
let Some(aggregated_lpm) = lpm_map(&aggregated, Reduced::Irredundant) else {
return false; };
let is_subset = aggregated.iter().all(|(p, v)| original.get(&p) == Some(v));
original_lpm == aggregated_lpm
&& original.address_count() == aggregated.address_count()
&& is_subset
&& aggregated.len() == aggregated.iter().count()
&& aggregated.check_memory_alloc()
&& aggregated == twice
}
qc!(aggregate_consistent_set, _aggregate_consistent_set);
fn _aggregate_consistent_set(prefixes: Vec<TestPrefix>) -> bool {
let original = prefixes.iter().copied().collect::<PrefixSet<_>>();
let mut aggregated = original.clone();
aggregated.aggregate_consistent();
let mut twice = aggregated.clone();
twice.aggregate_consistent();
let original_space = covered_space(&original, Reduced::No).unwrap();
let Some(aggregated_space) = covered_space(&aggregated, Reduced::Irredundant) else {
return false; };
let is_subset = aggregated.iter().all(|p| original.contains(&p));
let lpm_presence_preserved = prefixes
.iter()
.all(|p| original.get_lpm(p).is_some() == aggregated.get_lpm(p).is_some());
original_space == aggregated_space
&& original.address_count() == aggregated.address_count()
&& is_subset
&& lpm_presence_preserved
&& aggregated.len() == aggregated.iter().count()
&& aggregated.0.check_memory_alloc()
&& aggregated == twice
}
qc!(is_covered_in_aggregate_set, _is_covered_in_aggregate_set);
fn _is_covered_in_aggregate_set((prefixes, probes): (Vec<TestPrefix>, Vec<TestPrefix>)) -> bool {
let original = prefixes.iter().copied().collect::<PrefixSet<_>>();
let mut aggregated = original.clone();
aggregated.aggregate();
probes
.iter()
.all(|p| original.is_covered_in_aggregate(p) == aggregated.is_covered(p))
}
qc!(aggregate_map, _aggregate_map);
fn _aggregate_map(entries: Vec<(TestPrefix, u8)>) -> bool {
let original: PrefixMap<TestPrefix, u8> = entries.into_iter().collect();
let mut aggregated = original.clone();
aggregated.aggregate();
let mut twice = aggregated.clone();
twice.aggregate();
let original_lpm = lpm_map(&original, Reduced::No).unwrap();
let Some(aggregated_lpm) = lpm_map(&aggregated, Reduced::Minimal) else {
return false; };
original_lpm == aggregated_lpm
&& original.address_count() == aggregated.address_count()
&& aggregated.len() == aggregated.iter().count()
&& aggregated.check_memory_alloc()
&& aggregated == twice
}
qc!(aggregate_fill_map, _aggregate_fill_map);
fn _aggregate_fill_map(entries: Vec<(TestPrefix, u8)>) -> bool {
const DEFAULT: u8 = 0;
let original: PrefixMap<TestPrefix, u8> = entries.into_iter().collect();
let mut aggregated = original.clone();
aggregated.aggregate_fill(|| DEFAULT);
let mut twice = aggregated.clone();
twice.aggregate_fill(|| DEFAULT);
let original_lpm = lpm_map(&original, Reduced::No).unwrap();
let Some(aggregated_lpm) = lpm_map(&aggregated, Reduced::Minimal) else {
return false;
};
let mut expected = rangemap::RangeInclusiveMap::new();
expected.insert(0..=u32::MAX, DEFAULT);
for (r, v) in original_lpm.iter() {
expected.insert(r.clone(), *v);
}
aggregated_lpm == expected
&& aggregated.len() == aggregated.iter().count()
&& aggregated.check_memory_alloc()
&& aggregated == twice
}