use crate::config::CreditMode;
#[derive(Debug, Clone, Copy)]
pub struct LinkCredit {
pub delivery_count: u32,
pub link_credit: u32,
pub available: u32,
pub drain: bool,
mode: CreditMode,
}
impl LinkCredit {
pub fn new(initial_delivery_count: u32, mode: CreditMode) -> Self {
LinkCredit {
delivery_count: initial_delivery_count,
link_credit: 0,
available: 0,
drain: false,
mode,
}
}
pub fn can_send(&self) -> bool {
self.link_credit > 0
}
pub fn record_sent(&mut self) {
self.delivery_count = self.delivery_count.wrapping_add(1);
self.link_credit = self.link_credit.saturating_sub(1);
}
pub fn apply_flow_as_sender(
&mut self,
flow_delivery_count: Option<u32>,
flow_link_credit: Option<u32>,
drain: bool,
) {
let peer_dc = flow_delivery_count.unwrap_or(self.delivery_count);
if let Some(lc) = flow_link_credit {
self.link_credit = peer_dc.wrapping_add(lc).wrapping_sub(self.delivery_count);
}
self.drain = drain;
}
pub fn grant(&mut self, credit: u32) {
self.link_credit = self.link_credit.saturating_add(credit);
self.drain = false;
}
pub fn set_credit(&mut self, credit: u32) {
self.link_credit = credit;
self.drain = false;
}
pub fn record_received(&mut self) {
self.delivery_count = self.delivery_count.wrapping_add(1);
self.link_credit = self.link_credit.saturating_sub(1);
}
pub fn auto_refill(&self) -> Option<u32> {
match self.mode {
CreditMode::Auto {
initial,
refill_threshold,
} if self.link_credit <= refill_threshold => {
Some(initial.saturating_sub(self.link_credit))
}
_ => None,
}
}
pub fn mode(&self) -> CreditMode {
self.mode
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sender_consumes_credit() {
let mut c = LinkCredit::new(0, CreditMode::Manual);
assert!(!c.can_send());
c.apply_flow_as_sender(Some(0), Some(2), false);
assert_eq!(c.link_credit, 2);
c.record_sent();
assert_eq!(c.delivery_count, 1);
assert_eq!(c.link_credit, 1);
c.record_sent();
assert!(!c.can_send());
}
#[test]
fn receiver_auto_refill() {
let mode = CreditMode::Auto {
initial: 100,
refill_threshold: 50,
};
let mut c = LinkCredit::new(0, mode);
c.set_credit(100);
assert_eq!(c.auto_refill(), None);
for _ in 0..50 {
c.record_received();
}
assert_eq!(c.auto_refill(), Some(50));
}
}