ahash/
specialize.rs

1use core::hash::BuildHasher;
2use core::hash::Hash;
3use core::hash::Hasher;
4
5#[cfg(not(feature = "std"))]
6extern crate alloc;
7#[cfg(feature = "std")]
8extern crate std as alloc;
9
10#[cfg(feature = "specialize")]
11use crate::BuildHasherExt;
12#[cfg(feature = "specialize")]
13use alloc::string::String;
14#[cfg(feature = "specialize")]
15use alloc::vec::Vec;
16
17/// Provides a way to get an optimized hasher for a given data type.
18/// Rather than using a Hasher generically which can hash any value, this provides a way to get a specialized hash
19/// for a specific type. So this may be faster for primitive types.
20/// # Example
21/// ```
22/// use std::hash::BuildHasher;
23/// use ahash::RandomState;
24/// use ahash::CallHasher;
25///
26/// let hash_builder = RandomState::new();
27/// //...
28/// let value: u32 = 17;
29/// let hash = u32::get_hash(&value, &hash_builder);
30/// ```
31/// Note that the type used to invoke `get_hash` must be the same a the type of value passed.
32/// For example get a hasher specialized on `[u8]` can invoke:
33/// ```
34/// /// use std::hash::BuildHasher;
35/// # use ahash::RandomState;
36/// # use ahash::CallHasher;
37/// # let hash_builder = RandomState::new();
38/// let bytes: [u8; 4] = [1, 2, 3, 4];
39/// let hash = <[u8]>::get_hash(&bytes, &hash_builder);
40/// ```
41pub trait CallHasher {
42    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64;
43}
44
45#[cfg(not(feature = "specialize"))]
46impl<T> CallHasher for T
47where
48    T: Hash + ?Sized,
49{
50    #[inline]
51    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
52        let mut hasher = build_hasher.build_hasher();
53        value.hash(&mut hasher);
54        hasher.finish()
55    }
56}
57
58#[cfg(feature = "specialize")]
59impl<T> CallHasher for T
60where
61    T: Hash + ?Sized,
62{
63    #[inline]
64    default fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
65        let mut hasher = build_hasher.build_hasher();
66        value.hash(&mut hasher);
67        hasher.finish()
68    }
69}
70
71macro_rules! call_hasher_impl {
72    ($typ:ty) => {
73        #[cfg(feature = "specialize")]
74        impl CallHasher for $typ {
75            #[inline]
76            fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
77                build_hasher.hash_as_u64(value)
78            }
79        }
80    };
81}
82call_hasher_impl!(u8);
83call_hasher_impl!(u16);
84call_hasher_impl!(u32);
85call_hasher_impl!(u64);
86call_hasher_impl!(i8);
87call_hasher_impl!(i16);
88call_hasher_impl!(i32);
89call_hasher_impl!(i64);
90
91#[cfg(feature = "specialize")]
92impl CallHasher for u128 {
93    #[inline]
94    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
95        build_hasher.hash_as_fixed_length(value)
96    }
97}
98
99#[cfg(feature = "specialize")]
100impl CallHasher for i128 {
101    #[inline]
102    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
103        build_hasher.hash_as_fixed_length(value)
104    }
105}
106
107#[cfg(feature = "specialize")]
108impl CallHasher for usize {
109    #[inline]
110    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
111        build_hasher.hash_as_fixed_length(value)
112    }
113}
114
115#[cfg(feature = "specialize")]
116impl CallHasher for isize {
117    #[inline]
118    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
119        build_hasher.hash_as_fixed_length(value)
120    }
121}
122
123#[cfg(feature = "specialize")]
124impl CallHasher for [u8] {
125    #[inline]
126    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
127        build_hasher.hash_as_str(value)
128    }
129}
130
131#[cfg(feature = "specialize")]
132impl CallHasher for Vec<u8> {
133    #[inline]
134    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
135        build_hasher.hash_as_str(value)
136    }
137}
138
139#[cfg(feature = "specialize")]
140impl CallHasher for str {
141    #[inline]
142    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
143        build_hasher.hash_as_str(value)
144    }
145}
146
147#[cfg(all(feature = "specialize"))]
148impl CallHasher for String {
149    #[inline]
150    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
151        build_hasher.hash_as_str(value)
152    }
153}
154
155#[cfg(test)]
156mod test {
157    use super::*;
158    use crate::*;
159
160    #[test]
161    #[cfg(feature = "specialize")]
162    pub fn test_specialized_invoked() {
163        let build_hasher = RandomState::with_seeds(1, 2, 3, 4);
164        let shortened = u64::get_hash(&0, &build_hasher);
165        let mut hasher = AHasher::new_with_keys(1, 2);
166        0_u64.hash(&mut hasher);
167        assert_ne!(hasher.finish(), shortened);
168    }
169
170    /// Tests that some non-trivial transformation takes place.
171    #[test]
172    pub fn test_input_processed() {
173        let build_hasher = RandomState::with_seeds(2, 2, 2, 2);
174        assert_ne!(0, u64::get_hash(&0, &build_hasher));
175        assert_ne!(1, u64::get_hash(&0, &build_hasher));
176        assert_ne!(2, u64::get_hash(&0, &build_hasher));
177        assert_ne!(3, u64::get_hash(&0, &build_hasher));
178        assert_ne!(4, u64::get_hash(&0, &build_hasher));
179        assert_ne!(5, u64::get_hash(&0, &build_hasher));
180
181        assert_ne!(0, u64::get_hash(&1, &build_hasher));
182        assert_ne!(1, u64::get_hash(&1, &build_hasher));
183        assert_ne!(2, u64::get_hash(&1, &build_hasher));
184        assert_ne!(3, u64::get_hash(&1, &build_hasher));
185        assert_ne!(4, u64::get_hash(&1, &build_hasher));
186        assert_ne!(5, u64::get_hash(&1, &build_hasher));
187
188        let xored = u64::get_hash(&0, &build_hasher) ^ u64::get_hash(&1, &build_hasher);
189        assert_ne!(0, xored);
190        assert_ne!(1, xored);
191        assert_ne!(2, xored);
192        assert_ne!(3, xored);
193        assert_ne!(4, xored);
194        assert_ne!(5, xored);
195    }
196
197    #[test]
198    pub fn test_ref_independent() {
199        let build_hasher = RandomState::with_seeds(1, 2, 3, 4);
200        assert_eq!(u8::get_hash(&&1, &build_hasher), u8::get_hash(&1, &build_hasher));
201        assert_eq!(u16::get_hash(&&2, &build_hasher), u16::get_hash(&2, &build_hasher));
202        assert_eq!(u32::get_hash(&&3, &build_hasher), u32::get_hash(&3, &build_hasher));
203        assert_eq!(u64::get_hash(&&4, &build_hasher), u64::get_hash(&4, &build_hasher));
204        assert_eq!(u128::get_hash(&&5, &build_hasher), u128::get_hash(&5, &build_hasher));
205        assert_eq!(
206            str::get_hash(&"test", &build_hasher),
207            str::get_hash("test", &build_hasher)
208        );
209        assert_eq!(
210            str::get_hash(&"test", &build_hasher),
211            String::get_hash(&"test".to_string(), &build_hasher)
212        );
213        #[cfg(feature = "specialize")]
214        assert_eq!(
215            str::get_hash(&"test", &build_hasher),
216            <[u8]>::get_hash("test".as_bytes(), &build_hasher)
217        );
218
219        let build_hasher = RandomState::with_seeds(10, 20, 30, 40);
220        assert_eq!(u8::get_hash(&&&1, &build_hasher), u8::get_hash(&1, &build_hasher));
221        assert_eq!(u16::get_hash(&&&2, &build_hasher), u16::get_hash(&2, &build_hasher));
222        assert_eq!(u32::get_hash(&&&3, &build_hasher), u32::get_hash(&3, &build_hasher));
223        assert_eq!(u64::get_hash(&&&4, &build_hasher), u64::get_hash(&4, &build_hasher));
224        assert_eq!(u128::get_hash(&&&5, &build_hasher), u128::get_hash(&5, &build_hasher));
225        assert_eq!(
226            str::get_hash(&&"test", &build_hasher),
227            str::get_hash("test", &build_hasher)
228        );
229        assert_eq!(
230            str::get_hash(&&"test", &build_hasher),
231            String::get_hash(&"test".to_string(), &build_hasher)
232        );
233        #[cfg(feature = "specialize")]
234        assert_eq!(
235            str::get_hash(&&"test", &build_hasher),
236            <[u8]>::get_hash(&"test".to_string().into_bytes(), &build_hasher)
237        );
238    }
239}