use crate::core::PacketNumber;
use crate::core::{Error, ErrorKind, Result};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DedupDecision {
Accept,
Duplicate,
}
pub trait Dedup {
fn observe(&mut self, packet_number: PacketNumber) -> DedupDecision;
fn is_duplicate(&self, packet_number: PacketNumber) -> bool;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PacketDedup<const N: usize> {
packets: [Option<PacketNumber>; N],
next: usize,
len: usize,
}
impl<const N: usize> PacketDedup<N> {
#[must_use]
pub const fn new() -> Self {
Self {
packets: [None; N],
next: 0,
len: 0,
}
}
pub fn observe_packet(&mut self, packet_number: PacketNumber) -> Result<DedupDecision> {
if N == 0 {
return Err(Error::new(ErrorKind::Reliability));
}
if self.is_duplicate(packet_number) {
return Ok(DedupDecision::Duplicate);
}
self.packets[self.next] = Some(packet_number);
self.next = (self.next + 1) % N;
self.len = core::cmp::min(self.len + 1, N);
Ok(DedupDecision::Accept)
}
#[must_use]
pub const fn len(&self) -> usize {
self.len
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
}
impl<const N: usize> Default for PacketDedup<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> Dedup for PacketDedup<N> {
fn observe(&mut self, packet_number: PacketNumber) -> DedupDecision {
self.observe_packet(packet_number)
.unwrap_or(DedupDecision::Duplicate)
}
fn is_duplicate(&self, packet_number: PacketNumber) -> bool {
self.packets
.iter()
.flatten()
.any(|known| *known == packet_number)
}
}
#[cfg(test)]
mod tests {
use crate::core::PacketNumber;
use super::{Dedup, DedupDecision, PacketDedup};
#[test]
fn packet_dedup_detects_duplicates() {
let mut dedup = PacketDedup::<2>::new();
assert_eq!(dedup.observe(PacketNumber::new(1)), DedupDecision::Accept);
assert_eq!(
dedup.observe(PacketNumber::new(1)),
DedupDecision::Duplicate
);
}
#[test]
fn packet_dedup_forgets_oldest_packet_when_full() {
let mut dedup = PacketDedup::<2>::new();
assert_eq!(dedup.observe(PacketNumber::new(1)), DedupDecision::Accept);
assert_eq!(dedup.observe(PacketNumber::new(2)), DedupDecision::Accept);
assert_eq!(dedup.observe(PacketNumber::new(3)), DedupDecision::Accept);
assert!(!dedup.is_duplicate(PacketNumber::new(1)));
assert!(dedup.is_duplicate(PacketNumber::new(2)));
assert!(dedup.is_duplicate(PacketNumber::new(3)));
}
}