1use expandable_cuckoo_filter::ExpandableCuckooFilter;
8use std::sync::atomic::{AtomicBool, Ordering};
9use tracing::{debug, info, warn};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum FilterTrust {
14 Untrusted,
16 Trusted,
18}
19
20pub struct FilterManager {
36 filter: ExpandableCuckooFilter,
38 trust_state: AtomicBool,
40}
41
42impl FilterManager {
43 #[must_use]
49 pub fn new(node_id: impl Into<String>, initial_capacity: usize) -> Self {
50 let node_id = node_id.into();
51 info!(
52 node_id = %node_id,
53 initial_capacity,
54 "Creating expandable cuckoo filter manager"
55 );
56
57 let filter = ExpandableCuckooFilter::new(node_id, initial_capacity);
58
59 Self {
60 filter,
61 trust_state: AtomicBool::new(false),
62 }
63 }
64
65 #[must_use]
67 #[inline]
68 pub fn trust_state(&self) -> FilterTrust {
69 if self.trust_state.load(Ordering::Acquire) {
70 FilterTrust::Trusted
71 } else {
72 FilterTrust::Untrusted
73 }
74 }
75
76 #[must_use]
78 #[inline]
79 pub fn is_trusted(&self) -> bool {
80 self.trust_state.load(Ordering::Acquire)
81 }
82
83 pub fn mark_trusted(&self) {
85 info!(
86 entries = self.filter.len(),
87 "Marking cuckoo filter as trusted"
88 );
89 self.trust_state.store(true, Ordering::Release);
90 }
91
92 pub fn mark_untrusted(&self) {
94 warn!("Marking cuckoo filter as untrusted");
95 self.trust_state.store(false, Ordering::Release);
96 }
97
98 #[must_use]
105 pub fn might_exist(&self, key: &str) -> Option<bool> {
106 if !self.trust_state.load(Ordering::Acquire) {
107 return None;
109 }
110
111 Some(self.filter.contains(key))
112 }
113
114 #[must_use]
116 #[inline]
117 pub fn should_check_l3(&self, key: &str) -> bool {
118 match self.might_exist(key) {
119 None => true, Some(true) => true, Some(false) => false, }
123 }
124
125 pub fn insert(&self, key: &str) {
127 self.filter.insert(key);
128 }
129
130 pub fn remove(&self, key: &str) -> bool {
132 self.filter.remove(key)
133 }
134
135 pub fn bulk_insert(&self, keys: &[String]) {
139 for key in keys {
140 self.filter.insert(key.as_str());
141 }
142 debug!(
143 added = keys.len(),
144 total = self.filter.len(),
145 "Bulk inserted keys into cuckoo filter"
146 );
147 }
148
149 #[must_use]
153 pub fn export(&self) -> Option<Vec<u8>> {
154 self.filter.export().ok()
155 }
156
157 pub fn import(&self, data: &[u8]) -> Result<(), String> {
161 self.filter.import(data).map_err(|e| e.to_string())
162 }
163
164 #[must_use]
166 pub fn len(&self) -> usize {
167 self.filter.len()
168 }
169
170 #[must_use]
172 pub fn is_empty(&self) -> bool {
173 self.filter.is_empty()
174 }
175
176 #[must_use]
178 pub fn stats(&self) -> (usize, usize, FilterTrust) {
179 (
180 self.filter.len(),
181 self.filter.capacity(),
182 self.trust_state(),
183 )
184 }
185
186 #[must_use]
188 pub fn memory_usage_bytes(&self) -> usize {
189 self.filter.capacity().saturating_mul(8).max(1024)
191 }
192
193 #[must_use]
195 pub fn node_id(&self) -> &str {
196 self.filter.node_id()
197 }
198
199 #[must_use]
201 pub fn seed(&self) -> u64 {
202 self.filter.seed()
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_new_filter_is_untrusted() {
212 let fm = FilterManager::new("test-node", 1000);
213 assert_eq!(fm.trust_state(), FilterTrust::Untrusted);
214 assert!(fm.is_empty());
215 assert_eq!(fm.len(), 0);
216 }
217
218 #[test]
219 fn test_mark_trusted_and_untrusted() {
220 let fm = FilterManager::new("test-node", 1000);
221
222 assert_eq!(fm.trust_state(), FilterTrust::Untrusted);
223
224 fm.mark_trusted();
225 assert_eq!(fm.trust_state(), FilterTrust::Trusted);
226
227 fm.mark_untrusted();
228 assert_eq!(fm.trust_state(), FilterTrust::Untrusted);
229 }
230
231 #[test]
232 fn test_insert_and_contains() {
233 let fm = FilterManager::new("test-node", 1000);
234 fm.mark_trusted();
235
236 fm.insert("key-1");
238 assert_eq!(fm.len(), 1);
239
240 assert_eq!(fm.might_exist("key-1"), Some(true));
242
243 assert_eq!(fm.might_exist("key-2"), Some(false));
245 }
246
247 #[test]
248 fn test_might_exist_returns_none_when_untrusted() {
249 let fm = FilterManager::new("test-node", 1000);
250
251 fm.insert("key-1");
252
253 assert_eq!(fm.might_exist("key-1"), None);
255 assert_eq!(fm.might_exist("key-2"), None);
256 }
257
258 #[test]
259 fn test_should_check_l3_when_untrusted() {
260 let fm = FilterManager::new("test-node", 1000);
261
262 assert!(fm.should_check_l3("any-key"));
264 assert!(fm.should_check_l3("another-key"));
265 }
266
267 #[test]
268 fn test_should_check_l3_when_trusted() {
269 let fm = FilterManager::new("test-node", 1000);
270 fm.mark_trusted();
271
272 fm.insert("exists");
273
274 assert!(fm.should_check_l3("exists"));
276
277 assert!(!fm.should_check_l3("nonexistent"));
279 }
280
281 #[test]
282 fn test_remove() {
283 let fm = FilterManager::new("test-node", 1000);
284 fm.mark_trusted();
285
286 fm.insert("to-remove");
287 assert_eq!(fm.might_exist("to-remove"), Some(true));
288
289 let removed = fm.remove("to-remove");
290 assert!(removed || fm.might_exist("to-remove") == Some(false));
293 }
294
295 #[test]
296 fn test_bulk_insert() {
297 let fm = FilterManager::new("test-node", 1000);
298 fm.mark_trusted();
299
300 let keys: Vec<String> = (0..100).map(|i| format!("bulk-key-{}", i)).collect();
301 fm.bulk_insert(&keys);
302
303 assert_eq!(fm.len(), 100);
304
305 assert_eq!(fm.might_exist("bulk-key-0"), Some(true));
307 assert_eq!(fm.might_exist("bulk-key-50"), Some(true));
308 assert_eq!(fm.might_exist("bulk-key-99"), Some(true));
309 assert_eq!(fm.might_exist("not-inserted"), Some(false));
310 }
311
312 #[test]
313 fn test_export_import() {
314 let fm1 = FilterManager::new("test-node", 1000);
315
316 fm1.insert("key-a");
318 fm1.insert("key-b");
319 fm1.insert("key-c");
320
321 let exported = fm1.export();
323 assert!(exported.is_some());
324
325 let fm2 = FilterManager::new("test-node", 1000);
327 let import_result = fm2.import(&exported.unwrap());
328 assert!(import_result.is_ok());
329
330 assert_eq!(fm2.len(), 3);
332
333 fm2.mark_trusted();
335 assert_eq!(fm2.might_exist("key-a"), Some(true));
336 assert_eq!(fm2.might_exist("key-b"), Some(true));
337 assert_eq!(fm2.might_exist("key-c"), Some(true));
338 }
339
340 #[test]
341 fn test_stats() {
342 let fm = FilterManager::new("test-node", 1000);
343
344 let (count, capacity, trust) = fm.stats();
345 assert_eq!(count, 0);
346 assert!(capacity > 0);
347 assert_eq!(trust, FilterTrust::Untrusted);
348
349 fm.insert("key");
350 fm.mark_trusted();
351
352 let (count, _, trust) = fm.stats();
353 assert_eq!(count, 1);
354 assert_eq!(trust, FilterTrust::Trusted);
355 }
356
357 #[test]
358 fn test_memory_usage_bytes() {
359 let fm = FilterManager::new("test-node", 10000);
360
361 let mem = fm.memory_usage_bytes();
362 assert!(mem >= 1024, "Should report at least 1KB");
363 assert!(mem < 1024 * 1024, "Should be less than 1MB for 10k capacity");
364 }
365
366 #[test]
367 fn test_node_id_and_seed() {
368 let fm = FilterManager::new("my-unique-node", 1000);
369
370 assert_eq!(fm.node_id(), "my-unique-node");
371 assert!(fm.seed() > 0);
372 }
373
374 #[test]
375 fn test_deterministic_seed_same_node() {
376 let fm1 = FilterManager::new("same-node", 1000);
377 let fm2 = FilterManager::new("same-node", 1000);
378
379 assert_eq!(fm1.seed(), fm2.seed());
380 }
381
382 #[test]
383 fn test_different_seeds_different_nodes() {
384 let fm1 = FilterManager::new("node-a", 1000);
385 let fm2 = FilterManager::new("node-b", 1000);
386
387 assert_ne!(fm1.seed(), fm2.seed());
388 }
389
390 #[test]
391 fn test_filter_grows_beyond_initial_capacity() {
392 let fm = FilterManager::new("test-node", 10); for i in 0..100 {
396 fm.insert(&format!("key-{}", i));
397 }
398
399 assert_eq!(fm.len(), 100);
401
402 let (count, capacity, _) = fm.stats();
404 assert!(capacity >= count);
405 }
406
407 #[test]
408 fn test_is_empty() {
409 let fm = FilterManager::new("test-node", 1000);
410
411 assert!(fm.is_empty());
412
413 fm.insert("key");
414 assert!(!fm.is_empty());
415 }
416
417 #[test]
418 fn test_filter_trust_display() {
419 assert_eq!(format!("{:?}", FilterTrust::Untrusted), "Untrusted");
420 assert_eq!(format!("{:?}", FilterTrust::Trusted), "Trusted");
421 }
422
423 #[test]
424 fn test_multiple_inserts_same_key() {
425 let fm = FilterManager::new("test-node", 1000);
426
427 fm.insert("same-key");
428 fm.insert("same-key");
429 fm.insert("same-key");
430
431 fm.mark_trusted();
434 assert_eq!(fm.might_exist("same-key"), Some(true));
435 }
436
437 #[test]
438 fn test_false_positive_rate_reasonable() {
439 let fm = FilterManager::new("test-node", 10000);
440 fm.mark_trusted();
441
442 for i in 0..1000 {
444 fm.insert(&format!("inserted-{}", i));
445 }
446
447 let mut false_positives = 0;
449 for i in 0..1000 {
450 if fm.might_exist(&format!("not-inserted-{}", i)) == Some(true) {
451 false_positives += 1;
452 }
453 }
454
455 let fp_rate = false_positives as f64 / 1000.0;
457 assert!(fp_rate < 0.05, "False positive rate {} is too high", fp_rate);
458 }
459}