use std::collections::{HashMap, VecDeque};
use std::sync::Mutex;
use std::time::{Duration, Instant};
use weida_protocol::header::Deduplication;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct Identity {
producer: Option<[u8; 32]>,
scope: Box<str>,
sequence: u64,
}
pub(crate) struct DedupWindow {
window: Option<Duration>,
max_entries: usize,
state: Mutex<State>,
}
#[derive(Default)]
struct State {
order: VecDeque<(Instant, Identity)>,
seen: HashMap<Identity, Instant>,
}
impl DedupWindow {
pub(crate) fn new(
level: Deduplication,
window_ms: Option<u64>,
max_entries: usize,
) -> DedupWindow {
let window = match (level, window_ms) {
(Deduplication::Bounded, Some(ms)) => Some(Duration::from_millis(ms)),
_ => None,
};
DedupWindow {
window,
max_entries,
state: Mutex::new(State::default()),
}
}
pub(crate) fn is_duplicate(
&self,
producer: Option<[u8; 32]>,
scope: &str,
sequence: Option<u64>,
) -> bool {
let Some(window) = self.window else {
return false;
};
let Some(sequence) = sequence else {
return false;
};
let identity = Identity {
producer,
scope: scope.into(),
sequence,
};
let now = Instant::now();
let mut state = self.state.lock().expect("dedup window poisoned");
state.expire(now);
if state.seen.contains_key(&identity) {
return true;
}
state.insert(identity, now + window, self.max_entries);
false
}
#[cfg(test)]
fn tracked(&self) -> usize {
self.state.lock().expect("dedup window poisoned").seen.len()
}
#[cfg(test)]
fn allocated(&self) -> bool {
let state = self.state.lock().expect("dedup window poisoned");
state.seen.capacity() > 0 || state.order.capacity() > 0
}
}
impl State {
fn expire(&mut self, now: Instant) {
while let Some((deadline, _)) = self.order.front() {
if *deadline > now {
break;
}
let (_, identity) = self.order.pop_front().expect("checked above");
if self.seen.get(&identity).is_some_and(|d| *d <= now) {
self.seen.remove(&identity);
}
}
}
fn insert(&mut self, identity: Identity, deadline: Instant, max_entries: usize) {
while self.order.len() >= max_entries {
let Some((_, oldest)) = self.order.pop_front() else {
break;
};
self.seen.remove(&oldest);
}
self.order.push_back((deadline, identity.clone()));
self.seen.insert(identity, deadline);
}
}
#[cfg(test)]
mod tests {
use super::*;
const DIGEST: [u8; 32] = [7u8; 32];
#[test]
fn a_disabled_window_suppresses_nothing_and_allocates_nothing() {
for level in [Deduplication::None, Deduplication::Durable] {
let window = DedupWindow::new(level, Some(60_000), 4096);
for seq in 0..1000 {
assert!(!window.is_duplicate(None, "/md", Some(seq % 4)));
}
assert!(
!window.allocated(),
"an off dedup window must not allocate a table"
);
}
let window = DedupWindow::new(Deduplication::Bounded, None, 4096);
assert!(!window.is_duplicate(None, "/md", Some(1)));
assert!(!window.allocated());
}
#[test]
fn an_unnumbered_transfer_is_never_a_duplicate() {
let window = DedupWindow::new(Deduplication::Bounded, Some(60_000), 4096);
for _ in 0..10 {
assert!(!window.is_duplicate(None, "/md", None));
}
assert_eq!(window.tracked(), 0);
}
#[test]
fn a_repeat_inside_the_window_is_suppressed() {
let window = DedupWindow::new(Deduplication::Bounded, Some(60_000), 4096);
assert!(!window.is_duplicate(None, "/md", Some(1)));
assert!(window.is_duplicate(None, "/md", Some(1)));
assert!(window.is_duplicate(None, "/md", Some(1)));
assert!(!window.is_duplicate(None, "/md", Some(2)));
assert!(!window.is_duplicate(None, "/other", Some(1)));
assert!(!window.is_duplicate(Some(DIGEST), "/md", Some(1)));
assert!(window.is_duplicate(Some(DIGEST), "/md", Some(1)));
}
#[test]
fn an_identity_reused_after_its_window_is_not_suppressed() {
let window = DedupWindow::new(Deduplication::Bounded, Some(0), 4096);
assert!(!window.is_duplicate(None, "/md", Some(1)));
assert!(!window.is_duplicate(None, "/md", Some(1)));
assert_eq!(window.tracked(), 1, "the table does not grow with repeats");
}
#[test]
fn the_table_stops_growing_at_its_count_cap() {
let window = DedupWindow::new(Deduplication::Bounded, Some(60_000), 8);
for seq in 0..1000 {
assert!(!window.is_duplicate(None, "/md", Some(seq)));
}
assert_eq!(
window.tracked(),
8,
"a peer must not be able to size this table by sending fast"
);
assert!(!window.is_duplicate(None, "/md", Some(0)));
assert!(window.is_duplicate(None, "/md", Some(999)));
}
}