axhash_core/hash/core.rs
1use crate::constants::SECRET;
2use crate::math::folded_multiply;
3
4// A streaming hasher that implements [`std::hash::Hasher`].
5//
6// `AxHasher` accumulates typed values (primitives, byte slices) into an
7// internal accumulator. Call [`finish`](core::hash::Hasher::finish) to obtain
8// the final `u64` digest. The hasher can be reused by constructing a new
9// instance; there is no reset method because construction is cheap.
10//
11// # Usage with `HashMap`
12//
13// Use [`AxBuildHasher`](super::build::AxBuildHasher) as the `BuildHasher` for
14// standard collections:
15//
16// ```rust
17// use std::collections::HashMap;
18// use axhash_core::AxBuildHasher;
19//
20// let mut map: HashMap<&str, u32, AxBuildHasher> =
21// HashMap::with_hasher(AxBuildHasher::new());
22// map.insert("hello", 42);
23// ```
24//
25// # Streaming example
26//
27// ```rust
28// use axhash_core::AxHasher;
29// use std::hash::Hasher as _;
30//
31// let mut h = AxHasher::new_with_seed(0x1234);
32// h.write(b"part-1");
33// h.write(b"part-2");
34// let digest = h.finish();
35// assert_ne!(digest, 0);
36// ```
37#[derive(Clone, Debug)]
38pub struct AxHasher {
39 pub(crate) acc: u64,
40 pub(crate) sponge: u128,
41 pub(crate) sponge_bits: u8,
42}
43
44impl AxHasher {
45 // Creates a new `AxHasher` with the default seed (`0`).
46 #[inline(always)]
47 pub fn new() -> Self {
48 Self::new_with_seed(0)
49 }
50
51 // Creates a new `AxHasher` initialised with the given `seed`.
52 //
53 // Different seeds produce independent hash families. This is useful for
54 // hash-flooding resistance or domain separation between hasher instances.
55 #[inline(always)]
56 pub fn new_with_seed(seed: u64) -> Self {
57 Self {
58 acc: seed ^ SECRET[0],
59 sponge: 0,
60 sponge_bits: 0,
61 }
62 }
63
64 #[inline(always)]
65 pub(crate) fn flush_sponge(&mut self) {
66 if self.sponge_bits == 0 {
67 return;
68 }
69
70 let lo = self.sponge as u64;
71 let hi = (self.sponge >> 64) as u64;
72 self.acc = folded_multiply(lo ^ self.acc, hi ^ SECRET[1]);
73 self.sponge = 0;
74 self.sponge_bits = 0;
75 }
76
77 #[inline(always)]
78 pub(crate) fn push_num<T: Into<u128>>(&mut self, value: T, bits: u8) {
79 if self.sponge_bits as u16 + bits as u16 > 128 {
80 self.flush_sponge();
81 }
82
83 self.sponge |= value.into() << self.sponge_bits;
84 self.sponge_bits += bits;
85 }
86}
87
88impl Default for AxHasher {
89 // Returns a default `AxHasher` equivalent to [`AxHasher::new`].
90 #[inline(always)]
91 fn default() -> Self {
92 Self::new()
93 }
94}