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.
20pub(crate) trait CallHasher {
21    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64;
22}
23
24#[cfg(not(feature = "specialize"))]
25impl<T> CallHasher for T
26where
27    T: Hash + ?Sized,
28{
29    #[inline]
30    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
31        let mut hasher = build_hasher.build_hasher();
32        value.hash(&mut hasher);
33        hasher.finish()
34    }
35}
36
37#[cfg(feature = "specialize")]
38impl<T> CallHasher for T
39where
40    T: Hash + ?Sized,
41{
42    #[inline]
43    default fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
44        let mut hasher = build_hasher.build_hasher();
45        value.hash(&mut hasher);
46        hasher.finish()
47    }
48}
49
50macro_rules! call_hasher_impl {
51    ($typ:ty) => {
52        #[cfg(feature = "specialize")]
53        impl CallHasher for $typ {
54            #[inline]
55            fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
56                build_hasher.hash_as_u64(value)
57            }
58        }
59    };
60}
61call_hasher_impl!(u8);
62call_hasher_impl!(u16);
63call_hasher_impl!(u32);
64call_hasher_impl!(u64);
65call_hasher_impl!(i8);
66call_hasher_impl!(i16);
67call_hasher_impl!(i32);
68call_hasher_impl!(i64);
69
70#[cfg(feature = "specialize")]
71impl CallHasher for u128 {
72    #[inline]
73    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
74        build_hasher.hash_as_fixed_length(value)
75    }
76}
77
78#[cfg(feature = "specialize")]
79impl CallHasher for i128 {
80    #[inline]
81    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
82        build_hasher.hash_as_fixed_length(value)
83    }
84}
85
86#[cfg(feature = "specialize")]
87impl CallHasher for usize {
88    #[inline]
89    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
90        build_hasher.hash_as_fixed_length(value)
91    }
92}
93
94#[cfg(feature = "specialize")]
95impl CallHasher for isize {
96    #[inline]
97    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
98        build_hasher.hash_as_fixed_length(value)
99    }
100}
101
102#[cfg(feature = "specialize")]
103impl CallHasher for [u8] {
104    #[inline]
105    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
106        build_hasher.hash_as_str(value)
107    }
108}
109
110#[cfg(feature = "specialize")]
111impl CallHasher for Vec<u8> {
112    #[inline]
113    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
114        build_hasher.hash_as_str(value)
115    }
116}
117
118#[cfg(feature = "specialize")]
119impl CallHasher for str {
120    #[inline]
121    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
122        build_hasher.hash_as_str(value)
123    }
124}
125
126#[cfg(all(feature = "specialize"))]
127impl CallHasher for String {
128    #[inline]
129    fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
130        build_hasher.hash_as_str(value)
131    }
132}
133
134#[cfg(test)]
135mod test {
136    use super::*;
137    use crate::*;
138
139    #[test]
140    #[cfg(feature = "specialize")]
141    pub fn test_specialized_invoked() {
142        let build_hasher = RandomState::with_seeds(1, 2, 3, 4);
143        let shortened = u64::get_hash(&0, &build_hasher);
144        let mut hasher = AHasher::new_with_keys(1, 2);
145        0_u64.hash(&mut hasher);
146        assert_ne!(hasher.finish(), shortened);
147    }
148
149    /// Tests that some non-trivial transformation takes place.
150    #[test]
151    pub fn test_input_processed() {
152        let build_hasher = RandomState::with_seeds(2, 2, 2, 2);
153        assert_ne!(0, u64::get_hash(&0, &build_hasher));
154        assert_ne!(1, u64::get_hash(&0, &build_hasher));
155        assert_ne!(2, u64::get_hash(&0, &build_hasher));
156        assert_ne!(3, u64::get_hash(&0, &build_hasher));
157        assert_ne!(4, u64::get_hash(&0, &build_hasher));
158        assert_ne!(5, u64::get_hash(&0, &build_hasher));
159
160        assert_ne!(0, u64::get_hash(&1, &build_hasher));
161        assert_ne!(1, u64::get_hash(&1, &build_hasher));
162        assert_ne!(2, u64::get_hash(&1, &build_hasher));
163        assert_ne!(3, u64::get_hash(&1, &build_hasher));
164        assert_ne!(4, u64::get_hash(&1, &build_hasher));
165        assert_ne!(5, u64::get_hash(&1, &build_hasher));
166
167        let xored = u64::get_hash(&0, &build_hasher) ^ u64::get_hash(&1, &build_hasher);
168        assert_ne!(0, xored);
169        assert_ne!(1, xored);
170        assert_ne!(2, xored);
171        assert_ne!(3, xored);
172        assert_ne!(4, xored);
173        assert_ne!(5, xored);
174    }
175
176    #[test]
177    pub fn test_ref_independent() {
178        let build_hasher = RandomState::with_seeds(1, 2, 3, 4);
179        assert_eq!(u8::get_hash(&&1, &build_hasher), u8::get_hash(&1, &build_hasher));
180        assert_eq!(u16::get_hash(&&2, &build_hasher), u16::get_hash(&2, &build_hasher));
181        assert_eq!(u32::get_hash(&&3, &build_hasher), u32::get_hash(&3, &build_hasher));
182        assert_eq!(u64::get_hash(&&4, &build_hasher), u64::get_hash(&4, &build_hasher));
183        assert_eq!(u128::get_hash(&&5, &build_hasher), u128::get_hash(&5, &build_hasher));
184        assert_eq!(
185            str::get_hash(&"test", &build_hasher),
186            str::get_hash("test", &build_hasher)
187        );
188        assert_eq!(
189            str::get_hash(&"test", &build_hasher),
190            String::get_hash(&"test".to_string(), &build_hasher)
191        );
192        #[cfg(feature = "specialize")]
193        assert_eq!(
194            str::get_hash(&"test", &build_hasher),
195            <[u8]>::get_hash("test".as_bytes(), &build_hasher)
196        );
197
198        let build_hasher = RandomState::with_seeds(10, 20, 30, 40);
199        assert_eq!(u8::get_hash(&&&1, &build_hasher), u8::get_hash(&1, &build_hasher));
200        assert_eq!(u16::get_hash(&&&2, &build_hasher), u16::get_hash(&2, &build_hasher));
201        assert_eq!(u32::get_hash(&&&3, &build_hasher), u32::get_hash(&3, &build_hasher));
202        assert_eq!(u64::get_hash(&&&4, &build_hasher), u64::get_hash(&4, &build_hasher));
203        assert_eq!(u128::get_hash(&&&5, &build_hasher), u128::get_hash(&5, &build_hasher));
204        assert_eq!(
205            str::get_hash(&&"test", &build_hasher),
206            str::get_hash("test", &build_hasher)
207        );
208        assert_eq!(
209            str::get_hash(&&"test", &build_hasher),
210            String::get_hash(&"test".to_string(), &build_hasher)
211        );
212        #[cfg(feature = "specialize")]
213        assert_eq!(
214            str::get_hash(&&"test", &build_hasher),
215            <[u8]>::get_hash(&"test".to_string().into_bytes(), &build_hasher)
216        );
217    }
218}