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
//! Fixed-size ring buffer for failure timestamps.
//!
//! Capacity equals `max_retry`. When the buffer is full and the span from
//! oldest to newest is within `find_time`, a ban threshold is reached.
//! Threshold check is O(1).
/// A fixed-capacity ring buffer of `i64` unix timestamps.
#[derive(Debug, Clone)]
pub struct CircularTimestamps {
buf: Vec<i64>,
/// Points to the next write position (also the oldest entry when full).
head: usize,
len: usize,
}
impl CircularTimestamps {
/// Create a new buffer with the given capacity (typically `max_retry`).
pub fn new(capacity: usize) -> Self {
Self {
buf: vec![0; capacity],
head: 0,
len: 0,
}
}
/// Push a timestamp, overwriting the oldest if full.
pub fn push(&mut self, ts: i64) {
let cap = self.buf.len();
if cap == 0 {
return;
}
let write_pos = if self.len < cap {
self.len
} else {
let pos = self.head;
self.head = (self.head + 1) % cap;
pos
};
self.buf[write_pos] = ts;
if self.len < cap {
self.len += 1;
}
}
/// Returns `true` when the buffer has reached its capacity.
pub fn is_full(&self) -> bool {
self.len == self.buf.len()
}
/// Returns the oldest timestamp, or `None` if empty.
pub fn oldest(&self) -> Option<i64> {
if self.len == 0 {
return None;
}
Some(self.buf[self.head])
}
/// Returns the newest timestamp, or `None` if empty.
pub fn newest(&self) -> Option<i64> {
if self.len == 0 {
return None;
}
let cap = self.buf.len();
let idx = if self.len < cap {
self.len - 1
} else {
(self.head + cap - 1) % cap
};
Some(self.buf[idx])
}
/// Check if the failure threshold is reached: buffer is full and the
/// time span from oldest to newest is within `find_time` seconds.
pub fn threshold_reached(&self, find_time: i64) -> bool {
if !self.is_full() {
return false;
}
match (self.oldest(), self.newest()) {
(Some(old), Some(new)) => (new - old) < find_time,
_ => false,
}
}
/// Number of timestamps currently stored.
pub fn len(&self) -> usize {
self.len
}
/// Whether the buffer is empty.
pub fn is_empty(&self) -> bool {
self.len == 0
}
/// The total capacity.
pub fn capacity(&self) -> usize {
self.buf.len()
}
}