pub struct CountingBloomFilter { /* private fields */ }Implementations§
Source§impl CountingBloomFilter
impl CountingBloomFilter
Sourcepub fn new(expected_entries: usize) -> Self
pub fn new(expected_entries: usize) -> Self
Build an empty counting filter sized for expected_entries.
Examples found in repository?
examples/sample_app.rs (line 112)
109fn counting_session_set() {
110 use subms_bloom_filter::CountingBloomFilter;
111 println!("\n== counting: active sessions with logout ==");
112 let mut sessions = CountingBloomFilter::new(1_000);
113 for s in ["sess-alice", "sess-bob", "sess-carol"] {
114 sessions.add(s);
115 }
116 println!(" bob active? {}", sessions.might_contain("sess-bob"));
117 sessions.remove("sess-bob"); // logout
118 println!(" bob after logout? {}", sessions.might_contain("sess-bob"));
119 assert!(
120 sessions.might_contain("sess-alice"),
121 "other sessions untouched"
122 );
123}More examples
examples/perf_features.rs (line 92)
70fn main() -> io::Result<()> {
71 let ks = keys();
72 let canon = SIZES[SIZES.len() - 1];
73
74 let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
75 .join("..")
76 .join(".subms")
77 .join("features")
78 .join("rust.json");
79 let existing = std::fs::read_to_string(&path).unwrap_or_default();
80 let mut manifest = SubMsFeatureManifest::load_str("rust", &existing);
81 // Stamp the box these numbers came from. The bench runs wherever it is
82 // invoked, so an unstamped manifest is indistinguishable from a fleet
83 // capture; the renderer will not publish one it cannot attribute.
84 let (source, instance) = SubMsP99Source::from_env();
85 manifest.set_p99_source(source, instance.as_deref());
86
87 // ---------- counting: adds remove() over 4-bit counters ----------
88 #[cfg(feature = "counting")]
89 {
90 use subms_bloom_filter::CountingBloomFilter;
91 let fill = |n: usize| {
92 let mut c = CountingBloomFilter::new(n);
93 for k in &ks[..n] {
94 c.add(k);
95 }
96 c
97 };
98 let sweep: Vec<(usize, u64)> = SIZES
99 .iter()
100 .map(|&n| {
101 (
102 n,
103 probe_p99(n, || fill(n), |c, k| _ = c.might_contain(k), &ks),
104 )
105 })
106 .collect();
107 let (cat, reason) = classify_feature(&sweep, None, None);
108 let mut p99 = BTreeMap::new();
109 p99.insert("contains".to_string(), sweep.last().unwrap().1);
110 p99.insert(
111 "add".to_string(),
112 mutate_p99(
113 canon,
114 || CountingBloomFilter::new(canon),
115 |c, k| c.add(k),
116 &ks,
117 ),
118 );
119 p99.insert(
120 "remove".to_string(),
121 mutate_p99(canon, || fill(canon), |c, k| c.remove(k), &ks),
122 );
123 manifest.set_feature("counting", cat, &p99, &reason);
124 }
125
126 // ---------- scalable: layered add, hit walks layers ----------
127 #[cfg(feature = "scalable")]
128 {
129 use subms_bloom_filter::ScalableBloomFilter;
130 let fill = |n: usize| {
131 let mut s = ScalableBloomFilter::new(1_000);
132 for k in &ks[..n] {
133 s.add(k);
134 }
135 s
136 };
137 let sweep: Vec<(usize, u64)> = SIZES
138 .iter()
139 .map(|&n| {
140 (
141 n,
142 probe_p99(n, || fill(n), |s, k| _ = s.might_contain(k), &ks),
143 )
144 })
145 .collect();
146 let (cat, reason) = classify_feature(&sweep, None, None);
147 let mut p99 = BTreeMap::new();
148 p99.insert("contains".to_string(), sweep.last().unwrap().1);
149 p99.insert(
150 "add".to_string(),
151 mutate_p99(
152 canon,
153 || ScalableBloomFilter::new(1_000),
154 |s, k| s.add(k),
155 &ks,
156 ),
157 );
158 manifest.set_feature("scalable", cat, &p99, &reason);
159 }
160
161 // ---------- partitioned: k independent slices ----------
162 #[cfg(feature = "partitioned")]
163 {
164 use subms_bloom_filter::PartitionedBloomFilter;
165 let fill = |n: usize| {
166 let mut p = PartitionedBloomFilter::new(n);
167 for k in &ks[..n] {
168 p.add(k);
169 }
170 p
171 };
172 let sweep: Vec<(usize, u64)> = SIZES
173 .iter()
174 .map(|&n| {
175 (
176 n,
177 probe_p99(n, || fill(n), |p, k| _ = p.might_contain(k), &ks),
178 )
179 })
180 .collect();
181 let (cat, reason) = classify_feature(&sweep, None, None);
182 let mut p99 = BTreeMap::new();
183 p99.insert("contains".to_string(), sweep.last().unwrap().1);
184 p99.insert(
185 "add".to_string(),
186 mutate_p99(
187 canon,
188 || PartitionedBloomFilter::new(canon),
189 |p, k| p.add(k),
190 &ks,
191 ),
192 );
193 manifest.set_feature("partitioned", cat, &p99, &reason);
194 }
195
196 // ---------- serde: derive only, no hot-path workload -> auxiliary ----------
197 #[cfg(feature = "serde")]
198 {
199 let (cat, reason) = classify_feature(&[], None, None);
200 manifest.set_feature("serde", cat, &BTreeMap::new(), &reason);
201 }
202
203 std::fs::create_dir_all(path.parent().unwrap())?;
204 std::fs::write(&path, manifest.to_json())?;
205 io::stdout().write_all(manifest.to_json().as_bytes())?;
206 Ok(())
207}pub fn bit_count(&self) -> u32
pub fn k(&self) -> u32
Sourcepub fn add(&mut self, key: &str)
pub fn add(&mut self, key: &str)
Add a key. Increments the per-cell 4-bit counter at each of the
k positions, saturating at 15.
Examples found in repository?
examples/sample_app.rs (line 114)
109fn counting_session_set() {
110 use subms_bloom_filter::CountingBloomFilter;
111 println!("\n== counting: active sessions with logout ==");
112 let mut sessions = CountingBloomFilter::new(1_000);
113 for s in ["sess-alice", "sess-bob", "sess-carol"] {
114 sessions.add(s);
115 }
116 println!(" bob active? {}", sessions.might_contain("sess-bob"));
117 sessions.remove("sess-bob"); // logout
118 println!(" bob after logout? {}", sessions.might_contain("sess-bob"));
119 assert!(
120 sessions.might_contain("sess-alice"),
121 "other sessions untouched"
122 );
123}More examples
examples/perf_features.rs (line 94)
70fn main() -> io::Result<()> {
71 let ks = keys();
72 let canon = SIZES[SIZES.len() - 1];
73
74 let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
75 .join("..")
76 .join(".subms")
77 .join("features")
78 .join("rust.json");
79 let existing = std::fs::read_to_string(&path).unwrap_or_default();
80 let mut manifest = SubMsFeatureManifest::load_str("rust", &existing);
81 // Stamp the box these numbers came from. The bench runs wherever it is
82 // invoked, so an unstamped manifest is indistinguishable from a fleet
83 // capture; the renderer will not publish one it cannot attribute.
84 let (source, instance) = SubMsP99Source::from_env();
85 manifest.set_p99_source(source, instance.as_deref());
86
87 // ---------- counting: adds remove() over 4-bit counters ----------
88 #[cfg(feature = "counting")]
89 {
90 use subms_bloom_filter::CountingBloomFilter;
91 let fill = |n: usize| {
92 let mut c = CountingBloomFilter::new(n);
93 for k in &ks[..n] {
94 c.add(k);
95 }
96 c
97 };
98 let sweep: Vec<(usize, u64)> = SIZES
99 .iter()
100 .map(|&n| {
101 (
102 n,
103 probe_p99(n, || fill(n), |c, k| _ = c.might_contain(k), &ks),
104 )
105 })
106 .collect();
107 let (cat, reason) = classify_feature(&sweep, None, None);
108 let mut p99 = BTreeMap::new();
109 p99.insert("contains".to_string(), sweep.last().unwrap().1);
110 p99.insert(
111 "add".to_string(),
112 mutate_p99(
113 canon,
114 || CountingBloomFilter::new(canon),
115 |c, k| c.add(k),
116 &ks,
117 ),
118 );
119 p99.insert(
120 "remove".to_string(),
121 mutate_p99(canon, || fill(canon), |c, k| c.remove(k), &ks),
122 );
123 manifest.set_feature("counting", cat, &p99, &reason);
124 }
125
126 // ---------- scalable: layered add, hit walks layers ----------
127 #[cfg(feature = "scalable")]
128 {
129 use subms_bloom_filter::ScalableBloomFilter;
130 let fill = |n: usize| {
131 let mut s = ScalableBloomFilter::new(1_000);
132 for k in &ks[..n] {
133 s.add(k);
134 }
135 s
136 };
137 let sweep: Vec<(usize, u64)> = SIZES
138 .iter()
139 .map(|&n| {
140 (
141 n,
142 probe_p99(n, || fill(n), |s, k| _ = s.might_contain(k), &ks),
143 )
144 })
145 .collect();
146 let (cat, reason) = classify_feature(&sweep, None, None);
147 let mut p99 = BTreeMap::new();
148 p99.insert("contains".to_string(), sweep.last().unwrap().1);
149 p99.insert(
150 "add".to_string(),
151 mutate_p99(
152 canon,
153 || ScalableBloomFilter::new(1_000),
154 |s, k| s.add(k),
155 &ks,
156 ),
157 );
158 manifest.set_feature("scalable", cat, &p99, &reason);
159 }
160
161 // ---------- partitioned: k independent slices ----------
162 #[cfg(feature = "partitioned")]
163 {
164 use subms_bloom_filter::PartitionedBloomFilter;
165 let fill = |n: usize| {
166 let mut p = PartitionedBloomFilter::new(n);
167 for k in &ks[..n] {
168 p.add(k);
169 }
170 p
171 };
172 let sweep: Vec<(usize, u64)> = SIZES
173 .iter()
174 .map(|&n| {
175 (
176 n,
177 probe_p99(n, || fill(n), |p, k| _ = p.might_contain(k), &ks),
178 )
179 })
180 .collect();
181 let (cat, reason) = classify_feature(&sweep, None, None);
182 let mut p99 = BTreeMap::new();
183 p99.insert("contains".to_string(), sweep.last().unwrap().1);
184 p99.insert(
185 "add".to_string(),
186 mutate_p99(
187 canon,
188 || PartitionedBloomFilter::new(canon),
189 |p, k| p.add(k),
190 &ks,
191 ),
192 );
193 manifest.set_feature("partitioned", cat, &p99, &reason);
194 }
195
196 // ---------- serde: derive only, no hot-path workload -> auxiliary ----------
197 #[cfg(feature = "serde")]
198 {
199 let (cat, reason) = classify_feature(&[], None, None);
200 manifest.set_feature("serde", cat, &BTreeMap::new(), &reason);
201 }
202
203 std::fs::create_dir_all(path.parent().unwrap())?;
204 std::fs::write(&path, manifest.to_json())?;
205 io::stdout().write_all(manifest.to_json().as_bytes())?;
206 Ok(())
207}Sourcepub fn might_contain(&self, key: &str) -> bool
pub fn might_contain(&self, key: &str) -> bool
Probabilistic membership query. No false negatives - if the
key was added (and never removed enough to clear all k
counters), this returns true. False positives still occur at
the configured rate.
Examples found in repository?
examples/sample_app.rs (line 116)
109fn counting_session_set() {
110 use subms_bloom_filter::CountingBloomFilter;
111 println!("\n== counting: active sessions with logout ==");
112 let mut sessions = CountingBloomFilter::new(1_000);
113 for s in ["sess-alice", "sess-bob", "sess-carol"] {
114 sessions.add(s);
115 }
116 println!(" bob active? {}", sessions.might_contain("sess-bob"));
117 sessions.remove("sess-bob"); // logout
118 println!(" bob after logout? {}", sessions.might_contain("sess-bob"));
119 assert!(
120 sessions.might_contain("sess-alice"),
121 "other sessions untouched"
122 );
123}More examples
examples/perf_features.rs (line 103)
70fn main() -> io::Result<()> {
71 let ks = keys();
72 let canon = SIZES[SIZES.len() - 1];
73
74 let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
75 .join("..")
76 .join(".subms")
77 .join("features")
78 .join("rust.json");
79 let existing = std::fs::read_to_string(&path).unwrap_or_default();
80 let mut manifest = SubMsFeatureManifest::load_str("rust", &existing);
81 // Stamp the box these numbers came from. The bench runs wherever it is
82 // invoked, so an unstamped manifest is indistinguishable from a fleet
83 // capture; the renderer will not publish one it cannot attribute.
84 let (source, instance) = SubMsP99Source::from_env();
85 manifest.set_p99_source(source, instance.as_deref());
86
87 // ---------- counting: adds remove() over 4-bit counters ----------
88 #[cfg(feature = "counting")]
89 {
90 use subms_bloom_filter::CountingBloomFilter;
91 let fill = |n: usize| {
92 let mut c = CountingBloomFilter::new(n);
93 for k in &ks[..n] {
94 c.add(k);
95 }
96 c
97 };
98 let sweep: Vec<(usize, u64)> = SIZES
99 .iter()
100 .map(|&n| {
101 (
102 n,
103 probe_p99(n, || fill(n), |c, k| _ = c.might_contain(k), &ks),
104 )
105 })
106 .collect();
107 let (cat, reason) = classify_feature(&sweep, None, None);
108 let mut p99 = BTreeMap::new();
109 p99.insert("contains".to_string(), sweep.last().unwrap().1);
110 p99.insert(
111 "add".to_string(),
112 mutate_p99(
113 canon,
114 || CountingBloomFilter::new(canon),
115 |c, k| c.add(k),
116 &ks,
117 ),
118 );
119 p99.insert(
120 "remove".to_string(),
121 mutate_p99(canon, || fill(canon), |c, k| c.remove(k), &ks),
122 );
123 manifest.set_feature("counting", cat, &p99, &reason);
124 }
125
126 // ---------- scalable: layered add, hit walks layers ----------
127 #[cfg(feature = "scalable")]
128 {
129 use subms_bloom_filter::ScalableBloomFilter;
130 let fill = |n: usize| {
131 let mut s = ScalableBloomFilter::new(1_000);
132 for k in &ks[..n] {
133 s.add(k);
134 }
135 s
136 };
137 let sweep: Vec<(usize, u64)> = SIZES
138 .iter()
139 .map(|&n| {
140 (
141 n,
142 probe_p99(n, || fill(n), |s, k| _ = s.might_contain(k), &ks),
143 )
144 })
145 .collect();
146 let (cat, reason) = classify_feature(&sweep, None, None);
147 let mut p99 = BTreeMap::new();
148 p99.insert("contains".to_string(), sweep.last().unwrap().1);
149 p99.insert(
150 "add".to_string(),
151 mutate_p99(
152 canon,
153 || ScalableBloomFilter::new(1_000),
154 |s, k| s.add(k),
155 &ks,
156 ),
157 );
158 manifest.set_feature("scalable", cat, &p99, &reason);
159 }
160
161 // ---------- partitioned: k independent slices ----------
162 #[cfg(feature = "partitioned")]
163 {
164 use subms_bloom_filter::PartitionedBloomFilter;
165 let fill = |n: usize| {
166 let mut p = PartitionedBloomFilter::new(n);
167 for k in &ks[..n] {
168 p.add(k);
169 }
170 p
171 };
172 let sweep: Vec<(usize, u64)> = SIZES
173 .iter()
174 .map(|&n| {
175 (
176 n,
177 probe_p99(n, || fill(n), |p, k| _ = p.might_contain(k), &ks),
178 )
179 })
180 .collect();
181 let (cat, reason) = classify_feature(&sweep, None, None);
182 let mut p99 = BTreeMap::new();
183 p99.insert("contains".to_string(), sweep.last().unwrap().1);
184 p99.insert(
185 "add".to_string(),
186 mutate_p99(
187 canon,
188 || PartitionedBloomFilter::new(canon),
189 |p, k| p.add(k),
190 &ks,
191 ),
192 );
193 manifest.set_feature("partitioned", cat, &p99, &reason);
194 }
195
196 // ---------- serde: derive only, no hot-path workload -> auxiliary ----------
197 #[cfg(feature = "serde")]
198 {
199 let (cat, reason) = classify_feature(&[], None, None);
200 manifest.set_feature("serde", cat, &BTreeMap::new(), &reason);
201 }
202
203 std::fs::create_dir_all(path.parent().unwrap())?;
204 std::fs::write(&path, manifest.to_json())?;
205 io::stdout().write_all(manifest.to_json().as_bytes())?;
206 Ok(())
207}Sourcepub fn remove(&mut self, key: &str)
pub fn remove(&mut self, key: &str)
Remove a key. Decrements each of the k counters. Cells that
were saturated (counter == 15) stay at 15 - they cannot be
decremented without risking false negatives for OTHER keys
that incremented them past the saturation point.
Examples found in repository?
examples/sample_app.rs (line 117)
109fn counting_session_set() {
110 use subms_bloom_filter::CountingBloomFilter;
111 println!("\n== counting: active sessions with logout ==");
112 let mut sessions = CountingBloomFilter::new(1_000);
113 for s in ["sess-alice", "sess-bob", "sess-carol"] {
114 sessions.add(s);
115 }
116 println!(" bob active? {}", sessions.might_contain("sess-bob"));
117 sessions.remove("sess-bob"); // logout
118 println!(" bob after logout? {}", sessions.might_contain("sess-bob"));
119 assert!(
120 sessions.might_contain("sess-alice"),
121 "other sessions untouched"
122 );
123}More examples
examples/perf_features.rs (line 121)
70fn main() -> io::Result<()> {
71 let ks = keys();
72 let canon = SIZES[SIZES.len() - 1];
73
74 let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
75 .join("..")
76 .join(".subms")
77 .join("features")
78 .join("rust.json");
79 let existing = std::fs::read_to_string(&path).unwrap_or_default();
80 let mut manifest = SubMsFeatureManifest::load_str("rust", &existing);
81 // Stamp the box these numbers came from. The bench runs wherever it is
82 // invoked, so an unstamped manifest is indistinguishable from a fleet
83 // capture; the renderer will not publish one it cannot attribute.
84 let (source, instance) = SubMsP99Source::from_env();
85 manifest.set_p99_source(source, instance.as_deref());
86
87 // ---------- counting: adds remove() over 4-bit counters ----------
88 #[cfg(feature = "counting")]
89 {
90 use subms_bloom_filter::CountingBloomFilter;
91 let fill = |n: usize| {
92 let mut c = CountingBloomFilter::new(n);
93 for k in &ks[..n] {
94 c.add(k);
95 }
96 c
97 };
98 let sweep: Vec<(usize, u64)> = SIZES
99 .iter()
100 .map(|&n| {
101 (
102 n,
103 probe_p99(n, || fill(n), |c, k| _ = c.might_contain(k), &ks),
104 )
105 })
106 .collect();
107 let (cat, reason) = classify_feature(&sweep, None, None);
108 let mut p99 = BTreeMap::new();
109 p99.insert("contains".to_string(), sweep.last().unwrap().1);
110 p99.insert(
111 "add".to_string(),
112 mutate_p99(
113 canon,
114 || CountingBloomFilter::new(canon),
115 |c, k| c.add(k),
116 &ks,
117 ),
118 );
119 p99.insert(
120 "remove".to_string(),
121 mutate_p99(canon, || fill(canon), |c, k| c.remove(k), &ks),
122 );
123 manifest.set_feature("counting", cat, &p99, &reason);
124 }
125
126 // ---------- scalable: layered add, hit walks layers ----------
127 #[cfg(feature = "scalable")]
128 {
129 use subms_bloom_filter::ScalableBloomFilter;
130 let fill = |n: usize| {
131 let mut s = ScalableBloomFilter::new(1_000);
132 for k in &ks[..n] {
133 s.add(k);
134 }
135 s
136 };
137 let sweep: Vec<(usize, u64)> = SIZES
138 .iter()
139 .map(|&n| {
140 (
141 n,
142 probe_p99(n, || fill(n), |s, k| _ = s.might_contain(k), &ks),
143 )
144 })
145 .collect();
146 let (cat, reason) = classify_feature(&sweep, None, None);
147 let mut p99 = BTreeMap::new();
148 p99.insert("contains".to_string(), sweep.last().unwrap().1);
149 p99.insert(
150 "add".to_string(),
151 mutate_p99(
152 canon,
153 || ScalableBloomFilter::new(1_000),
154 |s, k| s.add(k),
155 &ks,
156 ),
157 );
158 manifest.set_feature("scalable", cat, &p99, &reason);
159 }
160
161 // ---------- partitioned: k independent slices ----------
162 #[cfg(feature = "partitioned")]
163 {
164 use subms_bloom_filter::PartitionedBloomFilter;
165 let fill = |n: usize| {
166 let mut p = PartitionedBloomFilter::new(n);
167 for k in &ks[..n] {
168 p.add(k);
169 }
170 p
171 };
172 let sweep: Vec<(usize, u64)> = SIZES
173 .iter()
174 .map(|&n| {
175 (
176 n,
177 probe_p99(n, || fill(n), |p, k| _ = p.might_contain(k), &ks),
178 )
179 })
180 .collect();
181 let (cat, reason) = classify_feature(&sweep, None, None);
182 let mut p99 = BTreeMap::new();
183 p99.insert("contains".to_string(), sweep.last().unwrap().1);
184 p99.insert(
185 "add".to_string(),
186 mutate_p99(
187 canon,
188 || PartitionedBloomFilter::new(canon),
189 |p, k| p.add(k),
190 &ks,
191 ),
192 );
193 manifest.set_feature("partitioned", cat, &p99, &reason);
194 }
195
196 // ---------- serde: derive only, no hot-path workload -> auxiliary ----------
197 #[cfg(feature = "serde")]
198 {
199 let (cat, reason) = classify_feature(&[], None, None);
200 manifest.set_feature("serde", cat, &BTreeMap::new(), &reason);
201 }
202
203 std::fs::create_dir_all(path.parent().unwrap())?;
204 std::fs::write(&path, manifest.to_json())?;
205 io::stdout().write_all(manifest.to_json().as_bytes())?;
206 Ok(())
207}Trait Implementations§
Source§impl<'de> Deserialize<'de> for CountingBloomFilter
impl<'de> Deserialize<'de> for CountingBloomFilter
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Deserialize this value from the given Serde deserializer. Read more
Auto Trait Implementations§
impl Freeze for CountingBloomFilter
impl RefUnwindSafe for CountingBloomFilter
impl Send for CountingBloomFilter
impl Sync for CountingBloomFilter
impl Unpin for CountingBloomFilter
impl UnsafeUnpin for CountingBloomFilter
impl UnwindSafe for CountingBloomFilter
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more