1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
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);
                }
            }
        }
    }
}