1#![cfg_attr(not(feature = "std"), no_std)]
52
53use core::{mem, ops};
54
55use crunchy::unroll;
56use tetsy_fixed_hash::*;
57#[cfg(feature = "codec")]
58use tetsy_impl_codec::impl_fixed_hash_codec;
59#[cfg(feature = "tetsy-rlp")]
60use tetsy_impl_rlp::impl_fixed_hash_rlp;
61#[cfg(feature = "serialize")]
62use tetsy_impl_serde::impl_fixed_hash_serde;
63use tiny_keccak::{Hasher, Keccak};
64
65const BLOOM_BITS: u32 = 3;
67const BLOOM_SIZE: usize = 256;
68
69construct_fixed_hash! {
70 pub struct Bloom(BLOOM_SIZE);
72}
73#[cfg(feature = "tetsy-rlp")]
74impl_fixed_hash_rlp!(Bloom, BLOOM_SIZE);
75#[cfg(feature = "serialize")]
76impl_fixed_hash_serde!(Bloom, BLOOM_SIZE);
77#[cfg(feature = "codec")]
78impl_fixed_hash_codec!(Bloom, BLOOM_SIZE);
79
80fn log2(x: usize) -> u32 {
82 if x <= 1 {
83 return 0;
84 }
85
86 let n = x.leading_zeros();
87 mem::size_of::<usize>() as u32 * 8 - n
88}
89
90pub enum Input<'a> {
91 Raw(&'a [u8]),
92 Hash(&'a [u8; 32]),
93}
94
95enum Hash<'a> {
96 Ref(&'a [u8; 32]),
97 Owned([u8; 32]),
98}
99
100impl<'a> From<Input<'a>> for Hash<'a> {
101 fn from(input: Input<'a>) -> Self {
102 match input {
103 Input::Raw(raw) => {
104 let mut out = [0u8; 32];
105 let mut keccak256 = Keccak::v256();
106 keccak256.update(raw);
107 keccak256.finalize(&mut out);
108 Hash::Owned(out)
109 }
110 Input::Hash(hash) => Hash::Ref(hash),
111 }
112 }
113}
114
115impl<'a> ops::Index<usize> for Hash<'a> {
116 type Output = u8;
117
118 fn index(&self, index: usize) -> &u8 {
119 match *self {
120 Hash::Ref(r) => &r[index],
121 Hash::Owned(ref hash) => &hash[index],
122 }
123 }
124}
125
126impl<'a> Hash<'a> {
127 fn len(&self) -> usize {
128 match *self {
129 Hash::Ref(r) => r.len(),
130 Hash::Owned(ref hash) => hash.len(),
131 }
132 }
133}
134
135impl<'a> PartialEq<BloomRef<'a>> for Bloom {
136 fn eq(&self, other: &BloomRef<'a>) -> bool {
137 let s_ref: &[u8] = &self.0;
138 let o_ref: &[u8] = other.0;
139 s_ref.eq(o_ref)
140 }
141}
142
143impl<'a> From<Input<'a>> for Bloom {
144 fn from(input: Input<'a>) -> Bloom {
145 let mut bloom = Bloom::default();
146 bloom.accrue(input);
147 bloom
148 }
149}
150
151impl Bloom {
152 pub fn is_empty(&self) -> bool {
153 self.0.iter().all(|x| *x == 0)
154 }
155
156 pub fn contains_input(&self, input: Input<'_>) -> bool {
157 let bloom: Bloom = input.into();
158 self.contains_bloom(&bloom)
159 }
160
161 pub fn contains_bloom<'a, B>(&self, bloom: B) -> bool
162 where
163 BloomRef<'a>: From<B>,
164 {
165 let bloom_ref: BloomRef<'_> = bloom.into();
166 self.contains_bloom_ref(bloom_ref)
168 }
169
170 fn contains_bloom_ref(&self, bloom: BloomRef<'_>) -> bool {
171 let self_ref: BloomRef<'_> = self.into();
172 self_ref.contains_bloom(bloom)
173 }
174
175 pub fn accrue(&mut self, input: Input<'_>) {
176 let p = BLOOM_BITS;
177
178 let m = self.0.len();
179 let bloom_bits = m * 8;
180 let mask = bloom_bits - 1;
181 let bloom_bytes = (log2(bloom_bits) + 7) / 8;
182
183 let hash: Hash<'_> = input.into();
184
185 assert_eq!(m & (m - 1), 0);
187 assert!(p * bloom_bytes <= hash.len() as u32);
189
190 let mut ptr = 0;
191
192 assert_eq!(BLOOM_BITS, 3);
193 unroll! {
194 for i in 0..3 {
195 let _ = i;
196 let mut index = 0 as usize;
197 for _ in 0..bloom_bytes {
198 index = (index << 8) | hash[ptr] as usize;
199 ptr += 1;
200 }
201 index &= mask;
202 self.0[m - 1 - index / 8] |= 1 << (index % 8);
203 }
204 }
205 }
206
207 pub fn accrue_bloom<'a, B>(&mut self, bloom: B)
208 where
209 BloomRef<'a>: From<B>,
210 {
211 let bloom_ref: BloomRef<'_> = bloom.into();
212 assert_eq!(self.0.len(), BLOOM_SIZE);
213 assert_eq!(bloom_ref.0.len(), BLOOM_SIZE);
214 for i in 0..BLOOM_SIZE {
215 self.0[i] |= bloom_ref.0[i];
216 }
217 }
218
219 pub fn data(&self) -> &[u8; BLOOM_SIZE] {
220 &self.0
221 }
222}
223
224#[derive(Clone, Copy)]
225pub struct BloomRef<'a>(&'a [u8; BLOOM_SIZE]);
226
227impl<'a> BloomRef<'a> {
228 #[allow(clippy::trivially_copy_pass_by_ref)]
229 pub fn is_empty(&self) -> bool {
230 self.0.iter().all(|x| *x == 0)
231 }
232
233 #[allow(clippy::trivially_copy_pass_by_ref)]
234 pub fn contains_input(&self, input: Input<'_>) -> bool {
235 let bloom: Bloom = input.into();
236 self.contains_bloom(&bloom)
237 }
238
239 #[allow(clippy::trivially_copy_pass_by_ref)]
240 pub fn contains_bloom<'b, B>(&self, bloom: B) -> bool
241 where
242 BloomRef<'b>: From<B>,
243 {
244 let bloom_ref: BloomRef<'_> = bloom.into();
245 assert_eq!(self.0.len(), BLOOM_SIZE);
246 assert_eq!(bloom_ref.0.len(), BLOOM_SIZE);
247 for i in 0..BLOOM_SIZE {
248 let a = self.0[i];
249 let b = bloom_ref.0[i];
250 if (a & b) != b {
251 return false;
252 }
253 }
254 true
255 }
256
257 #[allow(clippy::trivially_copy_pass_by_ref)]
258 pub fn data(&self) -> &'a [u8; BLOOM_SIZE] {
259 self.0
260 }
261}
262
263impl<'a> From<&'a [u8; BLOOM_SIZE]> for BloomRef<'a> {
264 fn from(data: &'a [u8; BLOOM_SIZE]) -> Self {
265 BloomRef(data)
266 }
267}
268
269impl<'a> From<&'a Bloom> for BloomRef<'a> {
270 fn from(bloom: &'a Bloom) -> Self {
271 BloomRef(&bloom.0)
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::{Bloom, Input};
278 use core::str::FromStr;
279 use hex_literal::hex;
280
281 #[test]
282 #[rustfmt::skip]
283 fn it_works() {
284 let bloom = Bloom::from_str(
285 "00000000000000000000000000000000\
286 00000000100000000000000000000000\
287 00000000000000000000000000000000\
288 00000000000000000000000000000000\
289 00000000000000000000000000000000\
290 00000000000000000000000000000000\
291 00000002020000000000000000000000\
292 00000000000000000000000800000000\
293 10000000000000000000000000000000\
294 00000000000000000000001000000000\
295 00000000000000000000000000000000\
296 00000000000000000000000000000000\
297 00000000000000000000000000000000\
298 00000000000000000000000000000000\
299 00000000000000000000000000000000\
300 00000000000000000000000000000000",
301 ).unwrap();
302 let address = hex!("ef2d6d194084c2de36e0dabfce45d046b37d1106");
303 let topic = hex!("02c69be41d0b7e40352fc85be1cd65eb03d40ef8427a0ca4596b1ead9a00e9fc");
304
305 let mut my_bloom = Bloom::default();
306 assert!(!my_bloom.contains_input(Input::Raw(&address)));
307 assert!(!my_bloom.contains_input(Input::Raw(&topic)));
308
309 my_bloom.accrue(Input::Raw(&address));
310 assert!(my_bloom.contains_input(Input::Raw(&address)));
311 assert!(!my_bloom.contains_input(Input::Raw(&topic)));
312
313 my_bloom.accrue(Input::Raw(&topic));
314 assert!(my_bloom.contains_input(Input::Raw(&address)));
315 assert!(my_bloom.contains_input(Input::Raw(&topic)));
316 assert_eq!(my_bloom, bloom);
317 }
318}