hopr_protocol_hopr/
tbf.rs1use hopr_api::types::{crypto::types::PacketTag, crypto_random::random_bytes};
2
3#[derive(Debug)]
9pub struct TagBloomFilter {
10 bloom: bloomfilter::Bloom<PacketTag>,
11 count: usize,
12 capacity: usize,
13}
14
15impl TagBloomFilter {
16 const DEFAULT_MAX_ITEMS: usize = 10_000_000;
19 const FALSE_POSITIVE_RATE: f64 = 0.00001_f64;
21
22 #[allow(dead_code)]
24 pub fn count(&self) -> usize {
25 self.count
26 }
27
28 pub fn capacity(&self) -> usize {
29 self.capacity
30 }
31
32 pub fn set(&mut self, tag: &PacketTag) {
34 if self.count == self.capacity {
35 tracing::warn!("maximum number of items in the Bloom filter reached!");
36 self.bloom.clear();
37 self.count = 0;
38 }
39
40 self.bloom.set(tag);
41 self.count += 1;
42 }
43
44 pub fn check(&self, tag: &PacketTag) -> bool {
49 self.bloom.check(tag)
50 }
51
52 #[allow(dead_code)]
57 pub fn check_and_set(&mut self, tag: &PacketTag) -> bool {
58 if self.count == self.capacity {
60 let is_present = self.bloom.check(tag);
61 if !is_present {
62 tracing::warn!("maximum number of items in the Bloom filter reached!");
64 self.bloom.clear();
65 self.bloom.set(tag);
66 self.count = 1;
67 }
68 is_present
69 } else {
70 let was_present = self.bloom.check_and_set(tag);
72 if !was_present {
73 self.count += 1;
74 }
75 was_present
76 }
77 }
78
79 fn with_capacity(size: usize) -> Self {
80 Self {
81 bloom: bloomfilter::Bloom::new_for_fp_rate_with_seed(size, Self::FALSE_POSITIVE_RATE, &random_bytes())
82 .expect("bloom filter with the specified capacity is constructible"),
83 count: 0,
84 capacity: size,
85 }
86 }
87}
88
89impl Default for TagBloomFilter {
90 fn default() -> Self {
91 Self::with_capacity(Self::DEFAULT_MAX_ITEMS)
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use hopr_api::types::crypto::types::PACKET_TAG_LENGTH;
98
99 #[allow(unused_imports)]
100 use super::*;
101
102 #[allow(dead_code)]
103 const ZEROS_TAG: [u8; PACKET_TAG_LENGTH] = [0; PACKET_TAG_LENGTH];
104 #[allow(dead_code)]
105 const ONES_TAG: [u8; PACKET_TAG_LENGTH] = [1; PACKET_TAG_LENGTH];
106
107 #[test]
108 fn tag_bloom_filter_count() {
109 let mut filter = TagBloomFilter::default();
110 assert!(!filter.check_and_set(&ZEROS_TAG));
111 assert_eq!(1, filter.count());
112
113 assert!(filter.check_and_set(&ZEROS_TAG));
114 assert_eq!(1, filter.count());
115
116 assert!(!filter.check_and_set(&ONES_TAG));
117 assert_eq!(2, filter.count());
118
119 assert!(filter.check_and_set(&ZEROS_TAG));
120 assert_eq!(2, filter.count());
121 }
122
123 #[test]
124 fn tag_bloom_filter_wrap_around() {
125 let mut filter = TagBloomFilter::with_capacity(1000);
126 for _ in 1..filter.capacity() {
127 let mut tag: PacketTag = hopr_api::types::crypto_random::random_bytes();
128 tag[0] = 0xaa; assert!(!filter.check_and_set(&tag));
130 }
131 assert_eq!(filter.capacity() - 1, filter.count());
133
134 assert!(!filter.check_and_set(&ZEROS_TAG));
136
137 assert_eq!(filter.capacity(), filter.count());
139 assert!(filter.check(&ZEROS_TAG));
140
141 assert!(filter.check_and_set(&ZEROS_TAG));
143 assert_eq!(filter.capacity(), filter.count());
144
145 assert!(!filter.check_and_set(&ONES_TAG));
147 assert_eq!(1, filter.count());
148 assert!(filter.check(&ONES_TAG));
149 }
150
151 #[test]
152 fn tag_bloom_filter_set_method() {
153 let mut filter = TagBloomFilter::with_capacity(2);
154 filter.set(&ZEROS_TAG);
155 assert_eq!(1, filter.count());
156 assert!(filter.check(&ZEROS_TAG));
157
158 filter.set(&ONES_TAG);
159 assert_eq!(2, filter.count());
160 assert!(filter.check(&ONES_TAG));
161
162 let another_tag: PacketTag = [0x42; PACKET_TAG_LENGTH];
164 filter.set(&another_tag);
165 assert_eq!(1, filter.count());
166 assert!(filter.check(&another_tag));
167 }
171}