Skip to main content

axhash_core/hasher/
core.rs

1use crate::constants::SECRET;
2use crate::math::folded_multiply;
3
4#[derive(Clone, Debug)]
5pub struct AxHasher {
6    pub(crate) acc: u64,
7    pub(crate) sponge: u128,
8    pub(crate) sponge_bits: u8,
9}
10
11impl AxHasher {
12    #[inline(always)]
13    pub fn new() -> Self {
14        Self::new_with_seed(0)
15    }
16
17    #[inline(always)]
18    pub fn new_with_seed(seed: u64) -> Self {
19        Self {
20            acc: seed ^ SECRET[0],
21            sponge: 0,
22            sponge_bits: 0,
23        }
24    }
25
26    #[inline(always)]
27    pub(crate) fn flush_sponge(&mut self) {
28        if self.sponge_bits != 0 {
29            self.flush_sponge_slow();
30        }
31    }
32
33    /// Hot-path flush: di-inline. Untuk streaming workload dengan banyak
34    /// write pendek, flush terjadi setiap 16 byte (frekuen, BUKAN cold).
35    #[inline(always)]
36    pub(crate) fn flush_sponge_hot(&mut self) {
37        let lo = self.sponge as u64;
38        let hi = (self.sponge >> 64) as u64;
39        self.acc = folded_multiply(lo ^ self.acc, hi ^ SECRET[1]);
40        self.sponge = 0;
41        self.sponge_bits = 0;
42    }
43
44    /// Cold-path flush: dipanggil saat sponge kebetulan punya data sebelum
45    /// transisi ke jalur write panjang. Tidak diharapkan sering.
46    #[inline(never)]
47    #[cold]
48    pub(crate) fn flush_sponge_slow(&mut self) {
49        self.flush_sponge_hot();
50    }
51
52    #[inline(always)]
53    pub(crate) fn push_num<T: Into<u128>>(&mut self, value: T, bits: u8) {
54        if self.sponge_bits as u16 + bits as u16 > 128 {
55            self.flush_sponge();
56        }
57
58        self.sponge |= value.into() << self.sponge_bits;
59        self.sponge_bits += bits;
60    }
61
62    /// Push up to (16 - sponge_bytes_used) bytes into the sponge. Caller harus
63    /// memastikan ada cukup ruang. Specialize ukuran-ukuran umum untuk
64    /// menghindari 16-byte stack buffer + memcpy untuk read kecil.
65    #[inline(always)]
66    pub(crate) fn push_bytes(&mut self, bytes: &[u8]) {
67        let n = bytes.len();
68        debug_assert!(n + ((self.sponge_bits >> 3) as usize) <= 16);
69        if n == 0 {
70            return;
71        }
72
73        // Common sizes: single load tanpa buffer init.
74        let value: u128 = if n == 8 {
75            let arr: [u8; 8] = bytes.try_into().unwrap();
76            u64::from_le_bytes(arr) as u128
77        } else if n == 4 {
78            let arr: [u8; 4] = bytes.try_into().unwrap();
79            u32::from_le_bytes(arr) as u128
80        } else if n == 16 {
81            let arr: [u8; 16] = bytes.try_into().unwrap();
82            u128::from_le_bytes(arr)
83        } else if n == 1 {
84            bytes[0] as u128
85        } else if n == 2 {
86            let arr: [u8; 2] = bytes.try_into().unwrap();
87            u16::from_le_bytes(arr) as u128
88        } else {
89            // 3, 5, 6, 7, 9..=15: zero-extend ke 16-byte buf
90            let mut buf = [0u8; 16];
91            buf[..n].copy_from_slice(bytes);
92            u128::from_le_bytes(buf)
93        };
94
95        self.sponge |= value << self.sponge_bits;
96        self.sponge_bits += (n << 3) as u8;
97    }
98}
99
100impl Default for AxHasher {
101    #[inline(always)]
102    fn default() -> Self {
103        Self::new()
104    }
105}