ruknet 0.1.2

The perfect reimplementation of the RakNet protocol in Rust
Documentation
use std::collections::VecDeque;

use crate::{error::BufferOverflowError, proto::datagram::Fragment};

const PACKET_HISTORY_SIZE: usize = 128;

#[derive(Debug, PartialEq)]
pub enum PacketHistory {
    Valid {
        fragments: Vec<Fragment>,
        time_sent: u64,
    },
    Invalid,
    Used,
}

#[derive(Debug)]
pub struct RetransmitCache {
    histories: [PacketHistory; PACKET_HISTORY_SIZE],
    start_seq: u32,
    retransmit_fragments: VecDeque<Fragment>,
}

impl RetransmitCache {
    fn relative_index(seq: u32) -> usize {
        seq as usize & (PACKET_HISTORY_SIZE - 1)
    }

    pub fn new() -> Self {
        Self {
            histories: [const { PacketHistory::Invalid }; PACKET_HISTORY_SIZE],
            start_seq: 0,
            retransmit_fragments: VecDeque::with_capacity(16),
        }
    }

    pub fn is_empty(&self) -> bool {
        let relative_index = Self::relative_index(self.start_seq);
        self.histories[relative_index] == PacketHistory::Invalid
            && self.retransmit_fragments.is_empty()
    }

    pub fn insert(
        &mut self,
        seq: u32,
        fragments: Vec<Fragment>,
        time_sent: u64,
    ) -> Result<(), BufferOverflowError> {
        if self.start_seq + PACKET_HISTORY_SIZE as u32 <= seq {
            return Err(BufferOverflowError);
        }

        let relative_index = Self::relative_index(seq);

        if let PacketHistory::Invalid = self.histories[relative_index] {
            self.histories[relative_index] = PacketHistory::Valid {
                fragments,
                time_sent,
            };
        }

        Ok(())
    }

    pub fn mark_as_used(&mut self, seq: u32) -> Option<(Vec<Fragment>, u64)> {
        let relative_index = Self::relative_index(seq);

        match &mut self.histories[relative_index] {
            PacketHistory::Valid {
                fragments,
                time_sent,
            } => {
                let fragments = std::mem::take(fragments);
                let time_sent = *time_sent;
                self.histories[relative_index] = PacketHistory::Used;
                Some((fragments, time_sent))
            }
            _ => None,
        }
    }

    pub fn shift(&mut self) -> usize {
        let mut count = 0;

        for i in 0..PACKET_HISTORY_SIZE {
            let relative_index = Self::relative_index(self.start_seq + i as u32);

            if let PacketHistory::Used = self.histories[relative_index] {
                self.histories[relative_index] = PacketHistory::Invalid;
                count += 1;
            } else {
                break;
            }
        }

        self.start_seq += count as u32;

        count
    }

    pub fn get_retransmit_fragments(&mut self, now: u64, rto: u64) -> &mut VecDeque<Fragment> {
        if self.is_empty() {
            return &mut self.retransmit_fragments;
        }

        let mut count = 0;

        for i in 0..PACKET_HISTORY_SIZE {
            let relative_index = Self::relative_index(self.start_seq + i as u32);

            match &mut self.histories[relative_index] {
                PacketHistory::Valid {
                    time_sent,
                    fragments,
                } => {
                    if now - *time_sent >= rto {
                        self.retransmit_fragments.extend(std::mem::take(fragments));
                        self.histories[relative_index] = PacketHistory::Invalid;
                        count += 1;
                    } else {
                        break;
                    }
                }
                PacketHistory::Invalid => break,
                PacketHistory::Used => {
                    self.histories[relative_index] = PacketHistory::Invalid;
                    count += 1;
                }
            }
        }

        self.start_seq += count as u32;

        &mut self.retransmit_fragments
    }

    pub fn extend_retransmit_fragments(&mut self, fragments: Vec<Fragment>) {
        self.retransmit_fragments.extend(fragments);
    }
}