1use super::*;
2use crate::{Address, B256, FixedBytes, Selector, U256};
3use cfg_if::cfg_if;
4use core::{
5 fmt,
6 hash::{BuildHasher, Hasher},
7};
8
9pub type FbMap<const N: usize, V> = HashMap<FixedBytes<N>, V, FbBuildHasher<N>>;
11#[doc(hidden)]
12pub type FbHashMap<const N: usize, V> = FbMap<N, V>;
13pub type FbSet<const N: usize> = HashSet<FixedBytes<N>, FbBuildHasher<N>>;
15#[doc(hidden)]
16pub type FbHashSet<const N: usize> = FbSet<N>;
17
18cfg_if! {
19 if #[cfg(feature = "map-indexmap")] {
20 pub type FbIndexMap<const N: usize, V> =
22 indexmap::IndexMap<FixedBytes<N>, V, FbBuildHasher<N>>;
23 pub type FbIndexSet<const N: usize> =
25 indexmap::IndexSet<FixedBytes<N>, FbBuildHasher<N>>;
26 }
27}
28
29macro_rules! fb_alias_maps {
30 ($($ty:ident < $n:literal >),* $(,)?) => { paste::paste! {
31 $(
32 #[doc = concat!("[`HashMap`] optimized for hashing [`", stringify!($ty), "`].")]
33 pub type [<$ty Map>]<V> = HashMap<$ty, V, FbBuildHasher<$n>>;
34 #[doc(hidden)]
35 pub type [<$ty HashMap>]<V> = [<$ty Map>]<V>;
36 #[doc = concat!("[`HashSet`] optimized for hashing [`", stringify!($ty), "`].")]
37 pub type [<$ty Set>] = HashSet<$ty, FbBuildHasher<$n>>;
38 #[doc(hidden)]
39 pub type [<$ty HashSet>] = [<$ty Set>];
40
41 cfg_if! {
42 if #[cfg(feature = "map-indexmap")] {
43 #[doc = concat!("[`IndexMap`] optimized for hashing [`", stringify!($ty), "`].")]
44 pub type [<$ty IndexMap>]<V> = IndexMap<$ty, V, FbBuildHasher<$n>>;
45 #[doc = concat!("[`IndexSet`] optimized for hashing [`", stringify!($ty), "`].")]
46 pub type [<$ty IndexSet>] = IndexSet<$ty, FbBuildHasher<$n>>;
47 }
48 }
49 )*
50 } };
51}
52
53fb_alias_maps!(Selector<4>, Address<20>, B256<32>, U256<32>);
54
55type FbBuildHasherInner = super::FxBuildHasherInner;
56type FbHasherInner = rustc_hash::FxHasher;
57
58#[derive(Clone, Default)]
62pub struct FbBuildHasher<const N: usize> {
63 inner: FbBuildHasherInner,
64 _marker: core::marker::PhantomData<[(); N]>,
65}
66
67impl<const N: usize> fmt::Debug for FbBuildHasher<N> {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 f.debug_struct("FbBuildHasher").finish_non_exhaustive()
70 }
71}
72
73impl<const N: usize> BuildHasher for FbBuildHasher<N> {
74 type Hasher = FbHasher<N>;
75
76 #[inline]
77 fn build_hasher(&self) -> Self::Hasher {
78 FbHasher { inner: self.inner.build_hasher(), _marker: core::marker::PhantomData }
79 }
80}
81
82#[derive(Clone)]
86pub struct FbHasher<const N: usize> {
87 inner: FbHasherInner,
88 _marker: core::marker::PhantomData<[(); N]>,
89}
90
91impl<const N: usize> Default for FbHasher<N> {
92 #[inline]
93 fn default() -> Self {
94 Self {
95 inner: FbBuildHasherInner::default().build_hasher(),
96 _marker: core::marker::PhantomData,
97 }
98 }
99}
100
101impl<const N: usize> fmt::Debug for FbHasher<N> {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 f.debug_struct("FbHasher").finish_non_exhaustive()
104 }
105}
106
107impl<const N: usize> Hasher for FbHasher<N> {
108 #[inline]
109 fn finish(&self) -> u64 {
110 self.inner.finish()
111 }
112
113 #[inline]
114 fn write(&mut self, bytes: &[u8]) {
115 unsafe { core::hint::assert_unchecked(bytes.len() == N) };
117 if N > 32 {
119 self.inner.write(bytes);
120 } else {
121 write_bytes_unrolled(&mut self.inner, bytes);
122 }
123 }
124
125 #[cfg(not(feature = "nightly"))]
131 #[inline]
132 fn write_usize(&mut self, i: usize) {
133 debug_assert!(i <= N, "{i} > {N}")
134 }
135
136 #[cfg(feature = "nightly")]
137 #[inline]
138 fn write_length_prefix(&mut self, len: usize) {
139 debug_assert!(len <= N, "{len} > {N}")
140 }
141}
142
143#[inline(always)]
144fn write_bytes_unrolled(hasher: &mut FbHasherInner, mut bytes: &[u8]) {
145 while let Some((chunk, rest)) = bytes.split_first_chunk() {
146 hasher.write_usize(usize::from_ne_bytes(*chunk));
147 bytes = rest;
148 }
149 if usize::BITS > 64 {
150 if let Some((chunk, rest)) = bytes.split_first_chunk() {
151 hasher.write_u64(u64::from_ne_bytes(*chunk));
152 bytes = rest;
153 }
154 }
155 if usize::BITS > 32 {
156 if let Some((chunk, rest)) = bytes.split_first_chunk() {
157 hasher.write_u32(u32::from_ne_bytes(*chunk));
158 bytes = rest;
159 }
160 }
161 if usize::BITS > 16 {
162 if let Some((chunk, rest)) = bytes.split_first_chunk() {
163 hasher.write_u16(u16::from_ne_bytes(*chunk));
164 bytes = rest;
165 }
166 }
167 if usize::BITS > 8 {
168 if let Some((chunk, rest)) = bytes.split_first_chunk() {
169 hasher.write_u8(u8::from_ne_bytes(*chunk));
170 bytes = rest;
171 }
172 }
173
174 debug_assert!(bytes.is_empty());
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 fn hash_zero<const N: usize>() -> u64 {
182 FbBuildHasher::<N>::default().hash_one(&FixedBytes::<N>::ZERO)
183 }
184
185 #[test]
186 fn fb_hasher() {
187 ruint::const_for!(N in [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
189 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
190 32, 47, 48, 49, 63, 64, 127, 128, 256, 512, 1024, 2048, 4096] {
191 let _ = hash_zero::<N>();
192 });
193 }
194
195 #[test]
196 fn map() {
197 let mut map = AddressHashMap::<bool>::default();
198 map.insert(Address::ZERO, true);
199 assert_eq!(map.get(&Address::ZERO), Some(&true));
200 assert_eq!(map.get(&Address::with_last_byte(1)), None);
201
202 let map2 = map.clone();
203 assert_eq!(map.len(), map2.len());
204 assert_eq!(map.len(), 1);
205 assert_eq!(map2.get(&Address::ZERO), Some(&true));
206 assert_eq!(map2.get(&Address::with_last_byte(1)), None);
207 }
208
209 #[test]
210 fn u256_map() {
211 let mut map = U256Map::default();
212 map.insert(U256::ZERO, true);
213 assert_eq!(map.get(&U256::ZERO), Some(&true));
214 assert_eq!(map.get(&U256::ONE), None);
215
216 let map2 = map.clone();
217 assert_eq!(map.len(), map2.len());
218 assert_eq!(map.len(), 1);
219 assert_eq!(map2.get(&U256::ZERO), Some(&true));
220 assert_eq!(map2.get(&U256::ONE), None);
221 }
222}