#[derive(Debug)]
pub(crate) struct GlobalWeight {
max: usize,
current: usize,
}
impl GlobalWeight {
pub(crate) fn new(max: usize) -> Self {
Self { max, current: 0 }
}
#[inline]
pub(crate) fn max(&self) -> usize {
self.max
}
#[inline]
pub(crate) fn current(&self) -> usize {
self.current
}
#[inline]
pub(crate) fn has_space_for(&self, weight: usize) -> bool {
let weight = weight.min(self.max);
self.current <= self.max - weight
}
pub(crate) fn add_weight(&mut self, weight: usize) {
let weight = weight.min(self.max);
self.current = self.current.checked_add(weight).unwrap_or_else(|| {
panic!(
"future_queue_grouped: added weight {} to current {}, overflowed",
weight, self.current,
)
});
}
pub(crate) fn sub_weight(&mut self, weight: usize) {
let weight = weight.min(self.max);
self.current = self.current.checked_sub(weight).unwrap_or_else(|| {
panic!(
"future_queue_grouped: subtracted weight {} from current {}, overflowed",
weight, self.current,
)
});
}
}
#[cfg(kani)]
mod kani_proofs {
use super::*;
#[kani::proof]
#[kani::unwind(1)]
fn proof_add_weight_respects_capacity() {
let max: usize = kani::any();
kani::assume(max <= 1024);
let mut gw = GlobalWeight::new(max);
let current: usize = kani::any();
kani::assume(current <= max);
gw.current = current;
let weight: usize = kani::any();
if gw.has_space_for(weight) {
gw.add_weight(weight);
assert!(gw.current <= gw.max, "current <= max after add_weight");
}
}
#[kani::proof]
#[kani::unwind(1)]
fn proof_add_sub_symmetry() {
let max: usize = kani::any();
kani::assume(max <= 1024);
let mut gw = GlobalWeight::new(max);
let current: usize = kani::any();
kani::assume(current <= max);
gw.current = current;
let weight: usize = kani::any();
if gw.has_space_for(weight) {
let original = gw.current;
gw.add_weight(weight);
gw.sub_weight(weight);
assert!(gw.current == original, "add then sub restores original");
}
}
#[kani::proof]
#[kani::unwind(1)]
fn proof_weight_clamping() {
let max: usize = kani::any();
kani::assume(max > 0 && max <= 1024);
let mut gw1 = GlobalWeight::new(max);
let mut gw2 = GlobalWeight::new(max);
let current: usize = kani::any();
kani::assume(current <= max);
gw1.current = current;
gw2.current = current;
let large_weight: usize = kani::any();
kani::assume(large_weight > max);
if gw1.has_space_for(large_weight) {
gw1.add_weight(large_weight);
gw2.add_weight(max);
assert!(gw1.current == gw2.current, "weight > max clamped to max");
}
}
#[kani::proof]
#[kani::unwind(1)]
fn proof_no_space_when_full() {
let max: usize = kani::any();
kani::assume(max > 0 && max <= 1024);
let mut gw = GlobalWeight::new(max);
gw.current = max;
let weight: usize = kani::any();
kani::assume(weight > 0);
assert!(!gw.has_space_for(weight), "no space when current == max");
}
#[kani::proof]
#[kani::unwind(1)]
fn proof_has_space_monotonic() {
let max: usize = kani::any();
kani::assume(max <= 1024);
let mut gw = GlobalWeight::new(max);
let current: usize = kani::any();
kani::assume(current <= max);
gw.current = current;
let weight: usize = kani::any();
let smaller_weight: usize = kani::any();
kani::assume(smaller_weight <= weight);
if gw.has_space_for(weight) {
assert!(
gw.has_space_for(smaller_weight),
"space for w implies space for v <= w"
);
}
}
}