///|
/// The exact VRP records added to and removed from a validator snapshot.
///
/// Snapshot comparison uses set semantics: duplicate records in either input
/// are counted once. Trust-anchor labels are part of a record's identity.
pub(all) struct SnapshotDiff {
added : Array[VrpRecord]
removed : Array[VrpRecord]
unchanged : Int
} derive(Eq, Debug)
///|
pub fn SnapshotDiff::is_empty(self : SnapshotDiff) -> Bool {
self.added.is_empty() && self.removed.is_empty()
}
///|
pub fn SnapshotDiff::summary(self : SnapshotDiff) -> String {
"+\{self.added.length()} -\{self.removed.length()} (\{self.unchanged} unchanged)"
}
///|
struct VrpRecordKey {
address : UInt
prefix_length : Int
max_length : Int
asn : UInt
trust_anchor : String
} derive(Eq, Hash)
///|
fn VrpRecord::key(self : VrpRecord) -> VrpRecordKey {
{
address: self.vrp.prefix().address,
prefix_length: self.vrp.prefix().length(),
max_length: self.vrp.max_length(),
asn: self.vrp.asn(),
trust_anchor: self.trust_anchor,
}
}
///|
fn unique_records(records : Array[VrpRecord]) -> Array[VrpRecord] {
let unique : Array[VrpRecord] = []
let seen : Map[VrpRecordKey, Unit] = Map([], capacity=records.length())
for record in records {
let key = record.key()
if !seen.contains(key) {
seen.set(key, ())
unique.push(record)
}
}
unique
}
///|
fn record_keys(records : Array[VrpRecord]) -> Map[VrpRecordKey, Unit] {
let keys : Map[VrpRecordKey, Unit] = Map([], capacity=records.length())
for record in records {
keys.set(record.key(), ())
}
keys
}
///|
/// Compare two complete VRP snapshots.
///
/// Added records retain their order in `current`; removed records retain their
/// order in `previous`. A change to an ASN, prefix, maximum length, or trust
/// anchor is represented by one removal and one addition.
pub fn compare_snapshots(
previous : Array[VrpRecord],
current : Array[VrpRecord],
) -> SnapshotDiff {
let before = unique_records(previous)
let after = unique_records(current)
let before_keys = record_keys(before)
let after_keys = record_keys(after)
let added : Array[VrpRecord] = []
let removed : Array[VrpRecord] = []
let mut unchanged = 0
for record in after {
if before_keys.contains(record.key()) {
unchanged = unchanged + 1
} else {
added.push(record)
}
}
for record in before {
if !after_keys.contains(record.key()) {
removed.push(record)
}
}
{ added, removed, unchanged, }
}