///|
pub(all) struct BankDelta {
bank : String
change : String
before_capacity : Int64?
after_capacity : Int64?
before_highwater : Int64
after_highwater : Int64
highwater_delta : Int64
before_peak_live_bytes : Int64
after_peak_live_bytes : Int64
peak_live_delta : Int64
growth_exceeded : Bool
candidate_over_budget : Bool
} derive(Eq, Debug, ToJson)
///|
pub(all) struct Comparison {
allowed_growth_bytes : Int64
baseline_valid : Bool
candidate_valid : Bool
gate_passed : Bool
banks : Array[BankDelta]
} derive(Eq, Debug, ToJson)
///|
/// Compare verified source/layout pairs. Growth is evaluated per bank for both
/// highwater and live-byte peak; a candidate capacity violation always fails.
pub fn compare_layouts(
before : Problem,
before_layout : Layout,
after : Problem,
after_layout : Layout,
max_growth_bytes~ : Int64 = 0L,
) -> Comparison raise PlanError {
if max_growth_bytes < 0L || max_growth_bytes > max_bytes {
reject("GrowthLimit", "allowed growth must be 0..2^50 bytes")
}
let baseline = summarize(before, before_layout)
let candidate = summarize(after, after_layout)
let old : Map[String, BankUsage] = Map([])
let next : Map[String, BankUsage] = Map([])
let ids : Map[String, Bool] = Map([])
for u in baseline {
old[u.bank] = u
ids[u.bank] = true
}
for u in candidate {
next[u.bank] = u
ids[u.bank] = true
}
let names = ids.keys().collect()
names.sort_by(id_compare)
let banks : Array[BankDelta] = []
for bank in names {
let prev = old.get(bank)
let now = next.get(bank)
let before_highwater = prev.map(u => u.highwater).unwrap_or(0L)
let after_highwater = now.map(u => u.highwater).unwrap_or(0L)
let before_peak_live_bytes = prev.map(u => u.peak_live_bytes).unwrap_or(0L)
let after_peak_live_bytes = now.map(u => u.peak_live_bytes).unwrap_or(0L)
let highwater_delta = after_highwater - before_highwater
let peak_live_delta = after_peak_live_bytes - before_peak_live_bytes
banks.push({
bank,
change: if prev is None {
"added"
} else if now is None {
"removed"
} else {
"retained"
},
before_capacity: prev.map(u => u.capacity),
after_capacity: now.map(u => u.capacity),
before_highwater,
after_highwater,
highwater_delta,
before_peak_live_bytes,
after_peak_live_bytes,
peak_live_delta,
growth_exceeded: highwater_delta > max_growth_bytes ||
peak_live_delta > max_growth_bytes,
candidate_over_budget: now.map(u => !u.fits).unwrap_or(false),
})
}
let baseline_valid = !baseline.any(u => !u.fits)
let candidate_valid = !candidate.any(u => !u.fits)
{
allowed_growth_bytes: max_growth_bytes,
baseline_valid,
candidate_valid,
gate_passed: candidate_valid && !banks.any(b => b.growth_exceeded),
banks,
}
}
///|
pub extend BankDelta with Eq::{equal, not_equal}
///|
pub extend BankDelta with @debug.Debug::{to_repr}
///|
pub extend BankDelta with ToJson::{to_json}
///|
pub extend Comparison with Eq::{equal, not_equal}
///|
pub extend Comparison with @debug.Debug::{to_repr}
///|
pub extend Comparison with ToJson::{to_json}