use vstd::prelude::*;
verus! {
pub struct RateLimit {
pub max_per_window: u64,
pub window_duration: u64,
pub max_clock: u64,
pub count: u64,
pub window_start: u64,
pub clock: u64,
}
impl RateLimit {
pub open spec fn type_invariant(&self) -> bool {
&&& self.count <= self.max_per_window
&&& self.max_per_window >= 1
}
pub open spec fn window_count_bound(&self) -> bool {
self.count <= self.max_per_window
}
pub open spec fn window_start_not_future(&self) -> bool {
self.window_start <= self.clock
}
pub open spec fn window_expired(&self) -> bool {
self.clock as int - self.window_start as int >= self.window_duration as int
}
pub fn new(max_per_window: u64, window_duration: u64, max_clock: u64) -> (r: RateLimit)
requires
max_per_window >= 1, ensures
r.max_per_window == max_per_window,
r.window_duration == window_duration,
r.max_clock == max_clock,
r.count == 0,
r.window_start == 0,
r.clock == 0,
r.type_invariant(),
r.window_count_bound(),
r.window_start_not_future(),
{
RateLimit { max_per_window, window_duration, max_clock, count: 0, window_start: 0, clock: 0 }
}
pub fn try_acquire(&mut self) -> (acquired: bool)
requires
old(self).type_invariant(),
old(self).window_start_not_future(),
ensures
final(self).max_per_window == old(self).max_per_window,
final(self).window_duration == old(self).window_duration,
final(self).max_clock == old(self).max_clock,
final(self).clock == old(self).clock, acquired == (old(self).window_expired() || old(self).count < old(self).max_per_window),
old(self).window_expired() ==> {
&&& final(self).window_start == old(self).clock
&&& final(self).count == 1
},
(!old(self).window_expired() && old(self).count < old(self).max_per_window) ==> {
&&& final(self).window_start == old(self).window_start
&&& final(self).count == old(self).count + 1
},
(!old(self).window_expired() && old(self).count >= old(self).max_per_window) ==> {
&&& final(self).window_start == old(self).window_start
&&& final(self).count == old(self).count
},
final(self).type_invariant(),
final(self).window_count_bound(),
final(self).window_start_not_future(),
{
let elapsed = self.clock - self.window_start;
if elapsed >= self.window_duration {
self.window_start = self.clock;
self.count = 1;
true
} else if self.count < self.max_per_window {
self.count = self.count + 1;
true
} else {
false
}
}
pub fn tick(&mut self)
requires
old(self).type_invariant(),
old(self).window_start_not_future(),
old(self).clock < old(self).max_clock, ensures
final(self).max_per_window == old(self).max_per_window,
final(self).window_duration == old(self).window_duration,
final(self).max_clock == old(self).max_clock,
final(self).count == old(self).count,
final(self).window_start == old(self).window_start,
final(self).clock == old(self).clock + 1,
final(self).type_invariant(),
final(self).window_count_bound(),
final(self).window_start_not_future(),
{
self.clock = self.clock + 1;
}
}
}