fasthash_fork/
t1ha.rs

1//! Fast Positive Hash, aka "Позитивный Хэш"
2//!
3//! by Positive Technologies.
4//!
5//! https://github.com/leo-yuriev/t1ha
6//!
7//! Briefly, it is a 64-bit Hash Function:
8//!
9//! Created for 64-bit little-endian platforms, in predominantly for `x86_64`,
10//! but without penalties could runs on any 64-bit CPU.
11//! In most cases up to 15% faster than City64, xxHash, mum-hash,
12//! metro-hash and all others which are not use specific hardware tricks.
13//! Not suitable for cryptography.
14//! Please see t1ha.c for implementation details.
15//!
16//! Acknowledgement:
17//!
18//! The t1ha was originally developed by Leonid Yuriev (Леонид Юрьев)
19//! for The 1Hippeus project - zerocopy messaging in the spirit of Sparta!
20//!
21//! Requirements and Portability:
22//!
23//! t1ha designed for modern 64-bit architectures. But on the other hand,
24//! t1ha doesn't uses any one tricks nor instructions specific to any particular architecture:
25//! therefore t1ha could be used on any CPU for which GCC provides support 64-bit arithmetics.
26//! but unfortunately t1ha could be dramatically slowly on architectures
27//! without native 64-bit operations.
28//! This implementation of t1ha requires modern GNU C compatible compiler,
29//! includes Clang/LLVM; or MSVC++ 14.0 (Visual Studio 2015).
30//!
31//! # Example
32//!
33//! ```
34//! use std::hash::{Hash, Hasher};
35//!
36//! use fasthash::{t1ha, T1haHasher};
37//!
38//! let h = t1ha::hash64(b"hello world");
39//!
40//! assert_eq!(h, 17676503408873599861);
41//!
42//! let mut s: T1haHasher = Default::default();
43//! b"hello world".hash(&mut s);
44//! let h = s.finish();
45//!
46//! assert_eq!(h, 14267663792334695945);
47//! ```
48//!
49use crate::hasher::FastHash;
50
51///
52/// t1ha2 = 64 and 128-bit, SLIGHTLY MORE ATTENTION FOR QUALITY AND STRENGTH.
53///
54///    - The recommended version of "Fast Positive Hash" with good quality
55///      for checksum, hash tables and fingerprinting.
56///    - Portable and extremely efficiency on modern 64-bit CPUs.
57///      Designed for 64-bit little-endian platforms,
58///      in other cases will runs slowly.
59///    - Great quality of hashing and still faster than other non-t1ha hashes.
60///      Provides streaming mode and 128-bit result.
61///
62pub mod t1ha2 {
63    use std::hash::Hasher;
64    use std::mem;
65    use std::ptr;
66
67    use crate::hasher::{FastHash, FastHasher, HasherExt, StreamHasher};
68
69    /// The at-once variant with 64-bit result
70    ///
71    /// # Example
72    ///
73    /// ```
74    /// use fasthash::{FastHash, t1ha2::Hash64AtOnce};
75    ///
76    /// assert_eq!(
77    ///     Hash64AtOnce::hash(b"hello"),
78    ///     3053206065578472372
79    /// );
80    /// assert_eq!(
81    ///     Hash64AtOnce::hash_with_seed(b"hello", 123),
82    ///     14202271713409552392
83    /// );
84    /// assert_eq!(
85    ///     Hash64AtOnce::hash(b"helloworld"),
86    ///     15302361616348747620
87    /// );
88    /// ```
89    #[derive(Clone, Default)]
90    pub struct Hash64AtOnce;
91
92    impl FastHash for Hash64AtOnce {
93        type Hash = u64;
94        type Seed = u64;
95
96        #[inline(always)]
97        fn hash_with_seed<T: AsRef<[u8]>>(bytes: T, seed: u64) -> u64 {
98            unsafe {
99                ffi::t1ha2_atonce(
100                    bytes.as_ref().as_ptr() as *const _,
101                    bytes.as_ref().len(),
102                    seed,
103                )
104            }
105        }
106    }
107
108    /// The at-once variant with 64-bit result
109    ///
110    /// # Example
111    ///
112    /// ```
113    /// use fasthash::{FastHash, t1ha2::Hash128AtOnce};
114    ///
115    /// assert_eq!(
116    ///     Hash128AtOnce::hash(b"hello"),
117    ///     181522150951767732353014146495581994137
118    /// );
119    /// assert_eq!(
120    ///     Hash128AtOnce::hash_with_seed(b"hello", 123),
121    ///     116090820602478335969970261629923046941
122    /// );
123    /// assert_eq!(
124    ///     Hash128AtOnce::hash(b"helloworld"),
125    ///     315212713565720527393405448145758944961
126    /// );
127    /// ```
128    #[derive(Clone, Default)]
129    pub struct Hash128AtOnce;
130
131    impl FastHash for Hash128AtOnce {
132        type Hash = u128;
133        type Seed = u64;
134
135        #[inline(always)]
136        fn hash_with_seed<T: AsRef<[u8]>>(bytes: T, seed: u64) -> u128 {
137            let mut hi = 0;
138
139            let lo = unsafe {
140                ffi::t1ha2_atonce128(
141                    &mut hi,
142                    bytes.as_ref().as_ptr() as *const _,
143                    bytes.as_ref().len(),
144                    seed,
145                )
146            };
147
148            u128::from(hi).wrapping_shl(64) + u128::from(lo)
149        }
150    }
151
152    /// An `t1ha2` implementation of `std::hash::Hasher`.
153    ///
154    /// # Note
155    ///
156    /// Due performance reason 64- and 128-bit results are completely different each other,
157    /// i.e. 64-bit result is NOT any part of 128-bit. */
158    ///
159    /// # Example
160    ///
161    /// ```
162    /// use std::hash::Hasher;
163    ///
164    /// use fasthash::{t1ha2::Hasher128, FastHasher, HasherExt};
165    ///
166    /// let mut h = Hasher128::new();
167    ///
168    /// h.write(b"hello");
169    /// assert_eq!(h.finish(), 11611394885310216856);
170    ///
171    /// h.write(b"world");
172    /// assert_eq!(h.finish_ext(), 189154943715293976030023582550666960629);
173    /// ```
174    #[derive(Clone)]
175    pub struct Hasher128(ptr::NonNull<ffi::t1ha_context_t>);
176
177    impl Default for Hasher128 {
178        fn default() -> Self {
179            Self::new()
180        }
181    }
182
183    impl Drop for Hasher128 {
184        fn drop(&mut self) {
185            unsafe { mem::drop(Box::from_raw(self.0.as_ptr())) }
186        }
187    }
188
189    impl Hasher for Hasher128 {
190        #[inline(always)]
191        fn write(&mut self, bytes: &[u8]) {
192            unsafe {
193                ffi::t1ha2_update(self.0.as_ptr(), bytes.as_ptr() as *const _, bytes.len());
194            }
195        }
196
197        #[inline(always)]
198        fn finish(&self) -> u64 {
199            unsafe { ffi::t1ha2_final(self.0.as_ptr(), ptr::null_mut()) }
200        }
201    }
202
203    impl HasherExt for Hasher128 {
204        fn finish_ext(&self) -> u128 {
205            let mut hi = 0;
206            let lo = unsafe { ffi::t1ha2_final(self.0.as_ptr(), &mut hi) };
207
208            (u128::from(hi) << 64) + u128::from(lo)
209        }
210    }
211
212    impl FastHasher for Hasher128 {
213        type Seed = (u64, u64);
214        type Output = u128;
215
216        #[inline(always)]
217        fn with_seed(seed: (u64, u64)) -> Self {
218            unsafe {
219                let ctx: ptr::NonNull<ffi::t1ha_context_t> =
220                    ptr::NonNull::new_unchecked(Box::into_raw(Box::new(mem::zeroed())));
221
222                ffi::t1ha2_init(ctx.as_ptr(), seed.0, seed.1);
223
224                Hasher128(ctx)
225            }
226        }
227    }
228
229    impl StreamHasher for Hasher128 {}
230
231    impl_build_hasher!(Hasher128, Hash64AtOnce);
232    impl_build_hasher!(Hasher128, Hash128AtOnce);
233}
234
235///
236/// t1ha1 = 64-bit, BASELINE FAST PORTABLE HASH:
237///
238///    - Runs faster on 64-bit platforms in other cases may runs slowly.
239///    - Portable and stable, returns same 64-bit result
240///      on all architectures and CPUs.
241///    - Unfortunately it fails the "strict avalanche criteria",
242///      see test results at https://github.com/demerphq/smhasher.
243///
244///      This flaw is insignificant for the t1ha1() purposes and imperceptible
245///      from a practical point of view.
246///      However, nowadays this issue has resolved in the next t1ha2(),
247///      that was initially planned to providing a bit more quality.
248///
249pub mod t1ha1 {
250    use crate::hasher::FastHash;
251
252    cfg_if! {
253        if #[cfg(target_endian = "little")] {
254            pub use self::{Hasher64Le as Hasher64, Hash64Le as Hash64};
255        } else {
256            pub use self::{Hasher64Be as Hasher64, Hash64Be as Hash64};
257        }
258    }
259
260    /// `T1Hash` 64-bit hash functions for 64-bit little-endian platforms.
261    ///
262    /// # Example
263    ///
264    /// ```
265    /// use fasthash::{FastHash, t1ha1::Hash64Le};
266    ///
267    /// assert_eq!(Hash64Le::hash(b"hello"), 12810198970222070563);
268    /// assert_eq!(
269    ///     Hash64Le::hash_with_seed(b"hello", 123),
270    ///     7105133355958514544
271    /// );
272    /// assert_eq!(Hash64Le::hash(b"helloworld"), 16997942636322422782);
273    /// ```
274    #[derive(Clone, Default)]
275    pub struct Hash64Le;
276
277    impl FastHash for Hash64Le {
278        type Hash = u64;
279        type Seed = u64;
280
281        #[inline(always)]
282        fn hash_with_seed<T: AsRef<[u8]>>(bytes: T, seed: u64) -> u64 {
283            unsafe {
284                ffi::t1ha1_le(
285                    bytes.as_ref().as_ptr() as *const _,
286                    bytes.as_ref().len(),
287                    seed,
288                )
289            }
290        }
291    }
292
293    trivial_hasher! {
294        /// # Example
295        ///
296        /// ```
297        /// use std::hash::Hasher;
298        ///
299        /// use fasthash::{t1ha1::Hasher64Le, FastHasher};
300        ///
301        /// let mut h = Hasher64Le::new();
302        ///
303        /// h.write(b"hello");
304        /// assert_eq!(h.finish(), 12810198970222070563);
305        ///
306        /// h.write(b"world");
307        /// assert_eq!(h.finish(), 16997942636322422782);
308        /// ```
309        Hasher64Le(Hash64Le) -> u64
310    }
311
312    /// `T1Hash` 64-bit hash functions for 64-bit big-endian platforms.
313    ///
314    /// # Example
315    ///
316    /// ```
317    /// use fasthash::{FastHash, t1ha1::Hash64Be};
318    ///
319    /// assert_eq!(Hash64Be::hash(b"hello"), 14880640220959195744);
320    /// assert_eq!(
321    ///     Hash64Be::hash_with_seed(b"hello", 123),
322    ///     1421069625385545216
323    /// );
324    /// assert_eq!(Hash64Be::hash(b"helloworld"), 15825971635414726702);
325    /// ```
326    #[derive(Clone, Default)]
327    pub struct Hash64Be;
328
329    impl FastHash for Hash64Be {
330        type Hash = u64;
331        type Seed = u64;
332
333        #[inline(always)]
334        fn hash_with_seed<T: AsRef<[u8]>>(bytes: T, seed: u64) -> u64 {
335            unsafe {
336                ffi::t1ha1_be(
337                    bytes.as_ref().as_ptr() as *const _,
338                    bytes.as_ref().len(),
339                    seed,
340                )
341            }
342        }
343    }
344
345    trivial_hasher! {
346        /// # Example
347        ///
348        /// ```
349        /// use std::hash::Hasher;
350        ///
351        /// use fasthash::{t1ha1::Hasher64Be, FastHasher};
352        ///
353        /// let mut h = Hasher64Be::new();
354        ///
355        /// h.write(b"hello");
356        /// assert_eq!(h.finish(), 14880640220959195744);
357        ///
358        /// h.write(b"world");
359        /// assert_eq!(h.finish(), 15825971635414726702);
360        /// ```
361        Hasher64Be(Hash64Be) -> u64
362    }
363}
364
365///
366/// t1ha0 = 64-bit, JUST ONLY FASTER:
367///
368///    - Provides fast-as-possible hashing for current CPU, including
369///      32-bit systems and engaging the available hardware acceleration.
370///    - It is a facade that selects most quick-and-dirty hash
371///      for the current processor. For instance, on IA32 (x86) actual function
372///      will be selected in runtime, depending on current CPU capabilities
373///
374pub mod t1ha0 {
375    use crate::hasher::FastHash;
376
377    lazy_static::lazy_static! {
378        static ref T1HA0: ffi::t1ha0_function_t = unsafe { ffi::t1ha0_resolve() };
379    }
380
381    /// `T1Hash` 64-bit hash functions.
382    ///
383    /// # Example
384    ///
385    /// ```
386    /// use fasthash::{FastHash, t1ha0::Hash64};
387    ///
388    /// assert_eq!(Hash64::hash(b"hello"), 3053206065578472372);
389    /// assert_eq!(
390    ///     Hash64::hash_with_seed(b"hello", 123),
391    ///     14202271713409552392
392    /// );
393    /// assert_eq!(Hash64::hash(b"helloworld"), 15302361616348747620);
394    /// ```
395    #[derive(Clone, Default)]
396    pub struct Hash64;
397
398    impl FastHash for Hash64 {
399        type Hash = u64;
400        type Seed = u64;
401
402        #[inline(always)]
403        fn hash_with_seed<T: AsRef<[u8]>>(bytes: T, seed: u64) -> u64 {
404            unsafe {
405                T1HA0.unwrap_or(ffi::t1ha0_64)(
406                    bytes.as_ref().as_ptr() as *const _,
407                    bytes.as_ref().len(),
408                    seed,
409                )
410            }
411        }
412    }
413
414    trivial_hasher! {
415        /// # Example
416        ///
417        /// ```
418        /// use std::hash::Hasher;
419        ///
420        /// use fasthash::{t1ha0::Hasher64, FastHasher};
421        ///
422        /// let mut h = Hasher64::new();
423        ///
424        /// h.write(b"hello");
425        /// assert_eq!(h.finish(), 3053206065578472372);
426        ///
427        /// h.write(b"world");
428        /// assert_eq!(h.finish(), 15302361616348747620);
429        /// ```
430        Hasher64(Hash64) -> u64
431    }
432}
433
434/// `T1Hash` 64-bit hash functions for a byte array.
435#[inline(always)]
436pub fn hash64<T: AsRef<[u8]>>(v: T) -> u64 {
437    t1ha2::Hash64AtOnce::hash(v)
438}
439
440/// `T1Hash` 64-bit hash function for a byte array.
441/// For convenience, a 64-bit seed is also hashed into the result.
442#[inline(always)]
443pub fn hash64_with_seed<T: AsRef<[u8]>>(v: T, seed: u64) -> u64 {
444    t1ha2::Hash64AtOnce::hash_with_seed(v, seed)
445}