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}