use std::time::{Duration, SystemTime};
pub(crate) type SelfSends = Vec<(u64, u32, SystemTime)>;
pub(crate) const SELF_SEND_TTL: Duration = Duration::from_secs(2);
pub(crate) const MAX_SELF_SEND_ENTRIES: usize = 65536;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum MatchMode {
Ordered,
Degraded,
}
pub(crate) fn record_self_send(tracker: &mut SelfSends, body: &[u8], sent: SystemTime) {
tracker.retain(|(_, _, t)| {
sent
.duration_since(*t)
.is_ok_and(|age| age <= SELF_SEND_TTL)
});
if tracker.len() < MAX_SELF_SEND_ENTRIES {
tracker.push((fnv1a(body), body.len() as u32, sent));
}
}
pub(crate) fn take_self_send(
tracker: &mut SelfSends,
body: &[u8],
reference: SystemTime,
mode: MatchMode,
) -> bool {
let needle = fnv1a(body);
let needle_len = body.len() as u32;
if let Some(pos) = tracker.iter().position(|(h, len, sent)| {
*h == needle && *len == needle_len && matches(reference, *sent, mode)
}) {
tracker.remove(pos);
true
} else {
false
}
}
fn matches(reference: SystemTime, sent: SystemTime, mode: MatchMode) -> bool {
match reference.duration_since(sent) {
Ok(ahead) => ahead <= SELF_SEND_TTL,
Err(behind) => mode == MatchMode::Ordered && behind.duration() <= Duration::from_millis(1),
}
}
fn fnv1a(data: &[u8]) -> u64 {
const OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const PRIME: u64 = 0x0000_0100_0000_01b3;
let mut h = OFFSET;
for &b in data {
h ^= u64::from(b);
h = h.wrapping_mul(PRIME);
}
h
}
#[cfg(test)]
mod tests;