xxhash_rust/
xxh32.rs

1//!32 bit version of xxhash algorithm
2//!
3//!Written using C implementation as reference.
4
5use core::{mem, slice};
6
7use crate::utils::{Buffer, get_unaligned_chunk, get_aligned_chunk};
8use crate::xxh32_common::*;
9
10fn finalize(mut input: u32, mut data: &[u8], is_aligned: bool) -> u32 {
11    while data.len() >= 4 {
12        input = input.wrapping_add(match is_aligned {
13            true => get_aligned_chunk::<u32>(data, 0).to_le().wrapping_mul(PRIME_3),
14            false => get_unaligned_chunk::<u32>(data, 0).to_le().wrapping_mul(PRIME_3),
15        });
16        input = input.rotate_left(17).wrapping_mul(PRIME_4);
17        data = &data[4..];
18    }
19
20    for byte in data.iter() {
21        input = input.wrapping_add((*byte as u32).wrapping_mul(PRIME_5));
22        input = input.rotate_left(11).wrapping_mul(PRIME_1);
23    }
24
25    avalanche(input)
26}
27
28#[inline(always)]
29const fn init_v(seed: u32) -> (u32, u32, u32, u32) {
30    (
31        seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2),
32        seed.wrapping_add(PRIME_2),
33        seed,
34        seed.wrapping_sub(PRIME_1),
35    )
36}
37
38macro_rules! round_loop {
39    ($input:ident => $($v:tt)+) => {
40        $($v)+.0 = round($($v)+.0, get_unaligned_chunk::<u32>($input, 0).to_le());
41        $($v)+.1 = round($($v)+.1, get_unaligned_chunk::<u32>($input, 4).to_le());
42        $($v)+.2 = round($($v)+.2, get_unaligned_chunk::<u32>($input, 8).to_le());
43        $($v)+.3 = round($($v)+.3, get_unaligned_chunk::<u32>($input, 12).to_le());
44        $input = &$input[16..];
45    }
46}
47
48///Returns hash for the provided input
49pub fn xxh32(mut input: &[u8], seed: u32) -> u32 {
50    let mut result = input.len() as u32;
51
52    if input.len() >= CHUNK_SIZE {
53        let mut v = init_v(seed);
54
55        loop {
56            round_loop!(input => v);
57            if input.len() < CHUNK_SIZE {
58                break;
59            }
60        }
61
62        result = result.wrapping_add(
63            v.0.rotate_left(1).wrapping_add(
64                v.1.rotate_left(7).wrapping_add(
65                    v.2.rotate_left(12).wrapping_add(
66                        v.3.rotate_left(18)
67                    )
68                )
69            )
70        );
71    } else {
72        result = result.wrapping_add(seed.wrapping_add(PRIME_5));
73    }
74
75    return finalize(result, input, false);
76}
77
78///XXH32 Streaming algorithm
79#[derive(Clone)]
80pub struct Xxh32 {
81    total_len: u32,
82    is_large_len: bool,
83    v: (u32, u32, u32, u32),
84    mem: [u32; 4],
85    mem_size: u32,
86}
87
88impl Xxh32 {
89    #[inline]
90    ///Creates new hasher with specified seed.
91    pub const fn new(seed: u32) -> Self {
92        Self {
93            total_len: 0,
94            is_large_len: false,
95            v: init_v(seed),
96            mem: [0, 0, 0, 0],
97            mem_size: 0,
98        }
99    }
100
101    ///Hashes provided input.
102    pub fn update(&mut self, mut input: &[u8]) {
103        self.total_len = self.total_len.wrapping_add(input.len() as u32);
104        self.is_large_len |= (input.len() as u32 >= CHUNK_SIZE as u32) | (self.total_len >= CHUNK_SIZE as u32);
105
106        if (self.mem_size + input.len() as u32) < CHUNK_SIZE as u32 {
107            Buffer {
108                ptr: self.mem.as_mut_ptr() as *mut u8,
109                len: mem::size_of_val(&self.mem),
110                offset: self.mem_size as _,
111            }.copy_from_slice(input);
112            self.mem_size += input.len() as u32;
113            return
114        }
115
116        if self.mem_size > 0 {
117            //previous if can fail only when we do not have enough space in buffer for input.
118            //hence fill_len >= input.len()
119            let fill_len = CHUNK_SIZE - self.mem_size as usize;
120
121            Buffer {
122                ptr: self.mem.as_mut_ptr() as *mut u8,
123                len: mem::size_of_val(&self.mem),
124                offset: self.mem_size as _,
125            }.copy_from_slice_by_size(input, fill_len);
126
127            self.v.0 = round(self.v.0, self.mem[0].to_le());
128            self.v.1 = round(self.v.1, self.mem[1].to_le());
129            self.v.2 = round(self.v.2, self.mem[2].to_le());
130            self.v.3 = round(self.v.3, self.mem[3].to_le());
131
132            input = &input[fill_len..];
133            self.mem_size = 0;
134        }
135
136        if input.len() >= CHUNK_SIZE {
137            loop {
138                round_loop!(input => self.v);
139                if input.len() < CHUNK_SIZE {
140                    break;
141                }
142            }
143        }
144
145        if input.len() > 0 {
146            Buffer {
147                ptr: self.mem.as_mut_ptr() as *mut u8,
148                len: mem::size_of_val(&self.mem),
149                offset: 0
150            }.copy_from_slice(input);
151            self.mem_size = input.len() as u32;
152        }
153    }
154
155    ///Finalize hashing.
156    pub fn digest(&self) -> u32 {
157        let mut result = self.total_len;
158
159        if self.is_large_len {
160            result = result.wrapping_add(
161                self.v.0.rotate_left(1).wrapping_add(
162                    self.v.1.rotate_left(7).wrapping_add(
163                        self.v.2.rotate_left(12).wrapping_add(
164                            self.v.3.rotate_left(18)
165                        )
166                    )
167                )
168            );
169        } else {
170            result = result.wrapping_add(self.v.2.wrapping_add(PRIME_5));
171        }
172
173        let input = unsafe {
174            slice::from_raw_parts(self.mem.as_ptr() as *const u8, self.mem_size as usize)
175        };
176
177        return finalize(result, input, true);
178    }
179
180    #[inline]
181    ///Resets the state with specified seed.
182    pub fn reset(&mut self, seed: u32) {
183        self.total_len = 0;
184        self.is_large_len = false;
185        self.v = init_v(seed);
186        self.mem_size = 0;
187    }
188}
189
190impl Default for Xxh32 {
191    #[inline(always)]
192    fn default() -> Self {
193        Self::new(0)
194    }
195}
196
197#[cfg(feature = "std")]
198impl std::io::Write for Xxh32 {
199    #[inline]
200    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
201        self.update(buf);
202        Ok(buf.len())
203    }
204
205    #[inline]
206    fn flush(&mut self) -> std::io::Result<()> {
207        Ok(())
208    }
209}