hdds_micro/transport/mesh/
seen.rs1#[derive(Debug, Clone, Copy, Default)]
8struct SeenEntry {
9 message_id: u32,
11 seen_at: u32,
13 valid: bool,
15}
16
17pub struct SeenCache<const N: usize> {
21 entries: [SeenEntry; N],
23 next: usize,
25 tick: u32,
27 lifetime: u32,
29}
30
31impl<const N: usize> SeenCache<N> {
32 pub fn new(lifetime: u32) -> Self {
37 Self {
38 entries: [SeenEntry::default(); N],
39 next: 0,
40 tick: 0,
41 lifetime,
42 }
43 }
44
45 pub fn tick(&mut self) {
47 self.tick = self.tick.wrapping_add(1);
48 }
49
50 pub fn is_duplicate(&self, message_id: u32) -> bool {
54 for entry in &self.entries {
55 if entry.valid && entry.message_id == message_id {
56 let age = self.tick.wrapping_sub(entry.seen_at);
58 if age <= self.lifetime {
59 return true;
60 }
61 }
62 }
63 false
64 }
65
66 pub fn check_and_mark(&mut self, message_id: u32) -> bool {
71 for entry in &mut self.entries {
73 if entry.valid && entry.message_id == message_id {
74 let age = self.tick.wrapping_sub(entry.seen_at);
75 if age <= self.lifetime {
76 return true; }
78 }
80 }
81
82 self.mark_seen(message_id);
84 false
85 }
86
87 pub fn mark_seen(&mut self, message_id: u32) {
89 for entry in &mut self.entries {
91 if !entry.valid {
92 entry.message_id = message_id;
93 entry.seen_at = self.tick;
94 entry.valid = true;
95 return;
96 }
97
98 let age = self.tick.wrapping_sub(entry.seen_at);
99 if age > self.lifetime {
100 entry.message_id = message_id;
101 entry.seen_at = self.tick;
102 entry.valid = true;
103 return;
104 }
105 }
106
107 self.entries[self.next] = SeenEntry {
109 message_id,
110 seen_at: self.tick,
111 valid: true,
112 };
113 self.next = (self.next + 1) % N;
114 }
115
116 pub fn clear(&mut self) {
118 for entry in &mut self.entries {
119 entry.valid = false;
120 }
121 self.next = 0;
122 }
123
124 pub fn len(&self) -> usize {
126 self.entries
127 .iter()
128 .filter(|e| e.valid && self.tick.wrapping_sub(e.seen_at) <= self.lifetime)
129 .count()
130 }
131
132 pub fn is_empty(&self) -> bool {
134 self.len() == 0
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn test_seen_cache_basic() {
144 let mut cache: SeenCache<8> = SeenCache::new(100);
145
146 assert!(!cache.check_and_mark(0x0001_0001));
148
149 assert!(cache.check_and_mark(0x0001_0001));
151
152 assert!(!cache.check_and_mark(0x0001_0002));
154 }
155
156 #[test]
157 fn test_seen_cache_expiry() {
158 let mut cache: SeenCache<8> = SeenCache::new(10);
159
160 assert!(!cache.check_and_mark(0x0001_0001));
161 assert!(cache.check_and_mark(0x0001_0001));
162
163 for _ in 0..15 {
165 cache.tick();
166 }
167
168 assert!(!cache.check_and_mark(0x0001_0001));
170 }
171
172 #[test]
173 fn test_seen_cache_lru() {
174 let mut cache: SeenCache<4> = SeenCache::new(1000);
175
176 for i in 0..4 {
178 assert!(!cache.check_and_mark(i));
179 }
180
181 for i in 0..4 {
183 assert!(cache.check_and_mark(i));
184 }
185
186 for i in 4..8 {
188 assert!(!cache.check_and_mark(i));
189 }
190
191 assert!(!cache.check_and_mark(0)); }
194
195 #[test]
196 fn test_seen_cache_clear() {
197 let mut cache: SeenCache<8> = SeenCache::new(100);
198
199 cache.mark_seen(0x0001_0001);
200 cache.mark_seen(0x0001_0002);
201 assert_eq!(cache.len(), 2);
202
203 cache.clear();
204 assert_eq!(cache.len(), 0);
205 assert!(cache.is_empty());
206 }
207
208 #[test]
209 fn test_seen_cache_len() {
210 let mut cache: SeenCache<8> = SeenCache::new(5);
211
212 assert_eq!(cache.len(), 0);
213
214 cache.mark_seen(1);
215 cache.mark_seen(2);
216 cache.mark_seen(3);
217 assert_eq!(cache.len(), 3);
218
219 for _ in 0..10 {
221 cache.tick();
222 }
223 assert_eq!(cache.len(), 0);
224 }
225}