1use 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
48pub 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#[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 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 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 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 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 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}