distant-net 0.20.0

Network library for distant, providing implementations to support client/server architecture
Documentation
use std::collections::VecDeque;

use super::{Frame, OwnedFrame};

/// Maximum size (in bytes) for saved frames (256MiB)
const MAX_BACKUP_SIZE: usize = 256 * 1024 * 1024;

/// Stores [`Frame`]s for reuse later.
///
/// ### Note
///
/// Empty [`Frame`]s are an exception and are not stored within the backup nor
/// are they tracked in terms of sent/received counts.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Backup {
    /// Maximum size (in bytes) to save frames in case we need to backup them
    ///
    /// NOTE: If 0, no frames will be stored.
    max_backup_size: usize,

    /// Tracker for the total size (in bytes) of stored frames
    current_backup_size: usize,

    /// Storage used to hold outgoing frames in case they need to be reused
    frames: VecDeque<OwnedFrame>,

    /// Counter keeping track of total frames sent
    sent_cnt: u64,

    /// Counter keeping track of total frames received
    received_cnt: u64,

    /// Indicates whether the backup is frozen, which indicates that mutations are ignored
    frozen: bool,
}

impl Default for Backup {
    fn default() -> Self {
        Self::new()
    }
}

impl Backup {
    /// Creates a new, unfrozen backup.
    pub fn new() -> Self {
        Self {
            max_backup_size: MAX_BACKUP_SIZE,
            current_backup_size: 0,
            frames: VecDeque::new(),
            sent_cnt: 0,
            received_cnt: 0,
            frozen: false,
        }
    }

    /// Clears the backup of any stored data and resets the state to being new.
    ///
    /// ### Note
    ///
    /// Like all other modifications, this will do nothing if the backup is frozen.
    pub fn clear(&mut self) {
        if !self.frozen {
            self.current_backup_size = 0;
            self.frames.clear();
            self.sent_cnt = 0;
            self.received_cnt = 0;
        }
    }

    /// Returns true if the backup is frozen, meaning that modifications will be ignored.
    #[inline]
    pub fn is_frozen(&self) -> bool {
        self.frozen
    }

    /// Sets the frozen status.
    #[inline]
    pub fn set_frozen(&mut self, frozen: bool) {
        self.frozen = frozen;
    }

    /// Marks the backup as frozen.
    #[inline]
    pub fn freeze(&mut self) {
        self.frozen = true;
    }

    /// Marks the backup as no longer frozen.
    #[inline]
    pub fn unfreeze(&mut self) {
        self.frozen = false;
    }

    /// Sets the maximum size (in bytes) of collective frames stored in case a backup is needed
    /// during reconnection. Setting the `size` to 0 will result in no frames being stored.
    ///
    /// ### Note
    ///
    /// Like all other modifications, this will do nothing if the backup is frozen.
    pub fn set_max_backup_size(&mut self, size: usize) {
        if !self.frozen {
            self.max_backup_size = size;
        }
    }

    /// Returns the maximum size (in bytes) of collective frames stored in case a backup is needed
    /// during reconnection.
    pub fn max_backup_size(&self) -> usize {
        self.max_backup_size
    }

    /// Increments (by 1) the total sent frames.
    ///
    /// ### Note
    ///
    /// Like all other modifications, this will do nothing if the backup is frozen.
    pub(crate) fn increment_sent_cnt(&mut self) {
        if !self.frozen {
            self.sent_cnt += 1;
        }
    }

    /// Returns how many frames have been sent.
    pub(crate) fn sent_cnt(&self) -> u64 {
        self.sent_cnt
    }

    /// Increments (by 1) the total received frames.
    ///
    /// ### Note
    ///
    /// Like all other modifications, this will do nothing if the backup is frozen.
    pub(super) fn increment_received_cnt(&mut self) {
        if !self.frozen {
            self.received_cnt += 1;
        }
    }

    /// Returns how many frames have been received.
    pub(crate) fn received_cnt(&self) -> u64 {
        self.received_cnt
    }

    /// Sets the total received frames to the specified `cnt`.
    ///
    /// ### Note
    ///
    /// Like all other modifications, this will do nothing if the backup is frozen.
    pub(super) fn set_received_cnt(&mut self, cnt: u64) {
        if !self.frozen {
            self.received_cnt = cnt;
        }
    }

    /// Pushes a new frame to the end of the internal queue.
    ///
    /// ### Note
    ///
    /// Like all other modifications, this will do nothing if the backup is frozen.
    pub(crate) fn push_frame(&mut self, frame: Frame) {
        if self.max_backup_size > 0 && !self.frozen {
            self.current_backup_size += frame.len();
            self.frames.push_back(frame.into_owned());
            while self.current_backup_size > self.max_backup_size {
                match self.frames.pop_front() {
                    Some(frame) => {
                        self.current_backup_size -= frame.len();
                    }

                    // If we have exhausted all frames, then we have reached
                    // an internal size of 0 and should exit the loop
                    None => {
                        self.current_backup_size = 0;
                        break;
                    }
                }
            }
        }
    }

    /// Returns the total frames being kept for potential reuse.
    pub(super) fn frame_cnt(&self) -> usize {
        self.frames.len()
    }

    /// Returns an iterator over the frames contained in the backup.
    pub(super) fn frames(&self) -> impl Iterator<Item = &Frame> {
        self.frames.iter()
    }

    /// Truncates the stored frames to be no larger than `size` total frames by popping from the
    /// front rather than the back of the list.
    ///
    /// ### Note
    ///
    /// Like all other modifications, this will do nothing if the backup is frozen.
    pub(super) fn truncate_front(&mut self, size: usize) {
        if !self.frozen {
            while self.frames.len() > size {
                if let Some(frame) = self.frames.pop_front() {
                    self.current_backup_size -=
                        std::cmp::min(frame.len(), self.current_backup_size);
                }
            }
        }
    }
}