use crate::prelude::*;
use core::cmp::min;
use core::fmt::{self, Debug, Formatter};
#[derive(Clone)]
pub struct VelocityControl {
pub start_sec: u64,
pub bucket_interval: u32,
pub buckets: Vec<u64>,
pub limit: u64,
}
impl Debug for VelocityControl {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("VelocityControl")
.field("start_sec", &self.start_sec)
.field("bucket_interval", &self.bucket_interval)
.field("buckets", &format_args!("{:?}", self.buckets))
.field("limit", &self.limit)
.finish()
}
}
#[derive(Clone, Copy, Debug)]
pub enum VelocityControlIntervalType {
Hourly,
Daily,
Unlimited,
}
#[derive(Clone, Copy, Debug)]
pub struct VelocityControlSpec {
pub limit_msat: u64,
pub interval_type: VelocityControlIntervalType,
}
impl VelocityControlSpec {
pub const UNLIMITED: VelocityControlSpec = VelocityControlSpec {
limit_msat: 0,
interval_type: VelocityControlIntervalType::Unlimited,
};
}
impl VelocityControl {
pub fn new_with_intervals(limit_msat: u64, bucket_interval: u32, num_buckets: usize) -> Self {
assert!(bucket_interval > 0 && num_buckets > 0);
let mut buckets = Vec::new();
buckets.resize(num_buckets, 0);
VelocityControl { start_sec: 0, bucket_interval, buckets, limit: limit_msat }
}
pub fn new_unlimited(bucket_interval: u32, num_buckets: usize) -> Self {
assert!(bucket_interval > 0 && num_buckets > 0);
let mut buckets = Vec::new();
buckets.resize(num_buckets, 0);
VelocityControl { start_sec: 0, bucket_interval, buckets, limit: u64::MAX }
}
pub fn new(spec: VelocityControlSpec) -> Self {
let (limit, bucket_interval, num_buckets) = Self::spec_to_triple(&spec);
Self::new_with_intervals(limit, bucket_interval, num_buckets)
}
pub fn spec_matches(&self, spec: &VelocityControlSpec) -> bool {
let (limit, bucket_interval, num_buckets) = Self::spec_to_triple(spec);
self.limit == limit
&& self.bucket_interval == bucket_interval
&& self.buckets.len() == num_buckets
}
pub fn update_spec(&mut self, spec: &VelocityControlSpec) {
if !self.spec_matches(spec) {
let (limit, bucket_interval, num_buckets) = Self::spec_to_triple(spec);
self.limit = limit;
self.bucket_interval = bucket_interval;
self.buckets = Vec::new();
self.buckets.resize(num_buckets, 0);
self.start_sec = 0;
}
}
fn spec_to_triple(spec: &VelocityControlSpec) -> (u64, u32, usize) {
match spec.interval_type {
VelocityControlIntervalType::Hourly => (spec.limit_msat, 300, 12),
VelocityControlIntervalType::Daily => (spec.limit_msat, 3600, 24),
VelocityControlIntervalType::Unlimited => (u64::MAX, 300, 12),
}
}
pub fn load_from_state(spec: VelocityControlSpec, state: (u64, Vec<u64>)) -> Self {
let control = Self::new(spec);
control.with_state(state)
}
fn with_state(mut self, state: (u64, Vec<u64>)) -> VelocityControl {
self.start_sec = state.0;
self.buckets = state.1;
self
}
pub fn get_state(&self) -> (u64, Vec<u64>) {
(self.start_sec, self.buckets.clone())
}
pub fn is_unlimited(&self) -> bool {
self.limit == u64::MAX
}
pub fn insert(&mut self, current_sec: u64, velocity_msat: u64) -> bool {
let nshift = (current_sec - self.start_sec) / self.bucket_interval as u64;
let len = self.buckets.len();
let nshift = min(len, nshift as usize);
self.buckets.resize(len - nshift, 0);
for _ in 0..nshift {
self.buckets.insert(0, 0);
}
self.start_sec = current_sec - (current_sec % self.bucket_interval as u64);
let current_velocity = self.velocity();
if current_velocity.saturating_add(velocity_msat) > self.limit {
false
} else {
self.buckets[0] = self.buckets[0].saturating_add(velocity_msat);
true
}
}
pub fn clear(&mut self) {
for bucket in self.buckets.iter_mut() {
*bucket = 0;
}
}
pub fn velocity(&self) -> u64 {
let mut sum = 0u64;
for bucket in self.buckets.iter() {
sum = sum.saturating_add(*bucket)
}
sum
}
}
#[cfg(test)]
mod tests {
use crate::util::velocity::VelocityControl;
#[test]
fn test_velocity() {
let mut c = VelocityControl::new_with_intervals(100, 10, 4);
assert_eq!(c.velocity(), 0);
assert!(c.insert(1100, 90));
assert_eq!(c.velocity(), 90);
assert!(!c.insert(1101, 11));
assert_eq!(c.velocity(), 90);
assert!(c.insert(1101, 10));
assert_eq!(c.velocity(), 100);
assert!(!c.insert(1139, 90));
assert_eq!(c.velocity(), 100);
assert!(c.insert(1140, 90));
assert_eq!(c.velocity(), 90);
assert!(c.insert(1150, 5));
assert_eq!(c.velocity(), 95);
assert!(c.insert(1180, 80));
assert_eq!(c.velocity(), 85);
assert!(c.insert(1190, 1));
assert_eq!(c.velocity(), 81);
}
#[test]
fn test_unlimited() {
let mut c = VelocityControl::new_unlimited(10, 4);
assert!(c.insert(0, u64::MAX - 1));
assert_eq!(c.velocity(), u64::MAX - 1);
assert!(c.insert(0, 1));
assert_eq!(c.velocity(), u64::MAX);
assert!(c.insert(0, 1));
assert_eq!(c.velocity(), u64::MAX);
}
}