1 | //! # simd-adler32 |
2 | //! |
3 | //! A SIMD-accelerated Adler-32 hash algorithm implementation. |
4 | //! |
5 | //! ## Features |
6 | //! |
7 | //! - No dependencies |
8 | //! - Support `no_std` (with `default-features = false`) |
9 | //! - Runtime CPU feature detection (when `std` enabled) |
10 | //! - Blazing fast performance on as many targets as possible (currently only x86 and x86_64) |
11 | //! - Default to scalar implementation when simd not available |
12 | //! |
13 | //! ## Quick start |
14 | //! |
15 | //! > Cargo.toml |
16 | //! |
17 | //! ```toml |
18 | //! [dependencies] |
19 | //! simd-adler32 = "*" |
20 | //! ``` |
21 | //! |
22 | //! > example.rs |
23 | //! |
24 | //! ```rust |
25 | //! use simd_adler32::Adler32; |
26 | //! |
27 | //! let mut adler = Adler32::new(); |
28 | //! adler.write(b"rust is pretty cool, man" ); |
29 | //! let hash = adler.finish(); |
30 | //! |
31 | //! println!("{}" , hash); |
32 | //! // 1921255656 |
33 | //! ``` |
34 | //! |
35 | //! ## Feature flags |
36 | //! |
37 | //! * `std` - Enabled by default |
38 | //! |
39 | //! Enables std support, see [CPU Feature Detection](#cpu-feature-detection) for runtime |
40 | //! detection support. |
41 | //! * `nightly` |
42 | //! |
43 | //! Enables nightly features required for avx512 support. |
44 | //! |
45 | //! * `const-generics` - Enabled by default |
46 | //! |
47 | //! Enables const-generics support allowing for user-defined array hashing by value. See |
48 | //! [`Adler32Hash`] for details. |
49 | //! |
50 | //! ## Support |
51 | //! |
52 | //! **CPU Features** |
53 | //! |
54 | //! | impl | arch | feature | |
55 | //! | ---- | ---------------- | ------- | |
56 | //! | ✅ | `x86`, `x86_64` | avx512 | |
57 | //! | ✅ | `x86`, `x86_64` | avx2 | |
58 | //! | ✅ | `x86`, `x86_64` | ssse3 | |
59 | //! | ✅ | `x86`, `x86_64` | sse2 | |
60 | //! | 🚧 | `arm`, `aarch64` | neon | |
61 | //! | | `wasm32` | simd128 | |
62 | //! |
63 | //! **MSRV** `1.36.0`\*\* |
64 | //! |
65 | //! Minimum supported rust version is tested before a new version is published. [**] Feature |
66 | //! `const-generics` needs to disabled to build on rustc versions `<1.51` which can be done |
67 | //! by updating your dependency definition to the following. |
68 | //! |
69 | //! ## CPU Feature Detection |
70 | //! simd-adler32 supports both runtime and compile time CPU feature detection using the |
71 | //! `std::is_x86_feature_detected` macro when the `Adler32` struct is instantiated with |
72 | //! the `new` fn. |
73 | //! |
74 | //! Without `std` feature enabled simd-adler32 falls back to compile time feature detection |
75 | //! using `target-feature` or `target-cpu` flags supplied to rustc. See [https://rust-lang.github.io/packed_simd/perf-guide/target-feature/rustflags.html](https://rust-lang.github.io/packed_simd/perf-guide/target-feature/rustflags.html) |
76 | //! for more information. |
77 | //! |
78 | //! Feature detection tries to use the fastest supported feature first. |
79 | #![cfg_attr (not(feature = "std" ), no_std)] |
80 | #![cfg_attr (feature = "nightly" , feature(stdsimd, avx512_target_feature))] |
81 | |
82 | #[doc (hidden)] |
83 | pub mod hash; |
84 | #[doc (hidden)] |
85 | pub mod imp; |
86 | |
87 | pub use hash::*; |
88 | use imp::{get_imp, Adler32Imp}; |
89 | |
90 | /// An adler32 hash generator type. |
91 | #[derive (Clone)] |
92 | pub struct Adler32 { |
93 | a: u16, |
94 | b: u16, |
95 | update: Adler32Imp, |
96 | } |
97 | |
98 | impl Adler32 { |
99 | /// Constructs a new `Adler32`. |
100 | /// |
101 | /// Potential overhead here due to runtime feature detection although in testing on 100k |
102 | /// and 10k random byte arrays it was not really noticeable. |
103 | /// |
104 | /// # Examples |
105 | /// ```rust |
106 | /// use simd_adler32::Adler32; |
107 | /// |
108 | /// let mut adler = Adler32::new(); |
109 | /// ``` |
110 | pub fn new() -> Self { |
111 | Default::default() |
112 | } |
113 | |
114 | /// Constructs a new `Adler32` using existing checksum. |
115 | /// |
116 | /// Potential overhead here due to runtime feature detection although in testing on 100k |
117 | /// and 10k random byte arrays it was not really noticeable. |
118 | /// |
119 | /// # Examples |
120 | /// ```rust |
121 | /// use simd_adler32::Adler32; |
122 | /// |
123 | /// let mut adler = Adler32::from_checksum(0xdeadbeaf); |
124 | /// ``` |
125 | pub fn from_checksum(checksum: u32) -> Self { |
126 | Self { |
127 | a: checksum as u16, |
128 | b: (checksum >> 16) as u16, |
129 | update: get_imp(), |
130 | } |
131 | } |
132 | |
133 | /// Computes hash for supplied data and stores results in internal state. |
134 | pub fn write(&mut self, data: &[u8]) { |
135 | let (a, b) = (self.update)(self.a, self.b, data); |
136 | |
137 | self.a = a; |
138 | self.b = b; |
139 | } |
140 | |
141 | /// Returns the hash value for the values written so far. |
142 | /// |
143 | /// Despite its name, the method does not reset the hasher’s internal state. Additional |
144 | /// writes will continue from the current value. If you need to start a fresh hash |
145 | /// value, you will have to use `reset`. |
146 | pub fn finish(&self) -> u32 { |
147 | (u32::from(self.b) << 16) | u32::from(self.a) |
148 | } |
149 | |
150 | /// Resets the internal state. |
151 | pub fn reset(&mut self) { |
152 | self.a = 1; |
153 | self.b = 0; |
154 | } |
155 | } |
156 | |
157 | /// Compute Adler-32 hash on `Adler32Hash` type. |
158 | /// |
159 | /// # Arguments |
160 | /// * `hash` - A Adler-32 hash-able type. |
161 | /// |
162 | /// # Examples |
163 | /// ```rust |
164 | /// use simd_adler32::adler32; |
165 | /// |
166 | /// let hash = adler32(b"Adler-32" ); |
167 | /// println!("{}" , hash); // 800813569 |
168 | /// ``` |
169 | pub fn adler32<H: Adler32Hash>(hash: &H) -> u32 { |
170 | hash.hash() |
171 | } |
172 | |
173 | /// A Adler-32 hash-able type. |
174 | pub trait Adler32Hash { |
175 | /// Feeds this value into `Adler32`. |
176 | fn hash(&self) -> u32; |
177 | } |
178 | |
179 | impl Default for Adler32 { |
180 | fn default() -> Self { |
181 | Self { |
182 | a: 1, |
183 | b: 0, |
184 | update: get_imp(), |
185 | } |
186 | } |
187 | } |
188 | |
189 | #[cfg (feature = "std" )] |
190 | pub mod read { |
191 | //! Reader-based hashing. |
192 | //! |
193 | //! # Example |
194 | //! ```rust |
195 | //! use std::io::Cursor; |
196 | //! use simd_adler32::read::adler32; |
197 | //! |
198 | //! let mut reader = Cursor::new(b"Hello there" ); |
199 | //! let hash = adler32(&mut reader).unwrap(); |
200 | //! |
201 | //! println!("{}" , hash) // 800813569 |
202 | //! ``` |
203 | use crate::Adler32; |
204 | use std::io::{Read, Result}; |
205 | |
206 | /// Compute Adler-32 hash on reader until EOF. |
207 | /// |
208 | /// # Example |
209 | /// ```rust |
210 | /// use std::io::Cursor; |
211 | /// use simd_adler32::read::adler32; |
212 | /// |
213 | /// let mut reader = Cursor::new(b"Hello there" ); |
214 | /// let hash = adler32(&mut reader).unwrap(); |
215 | /// |
216 | /// println!("{}" , hash) // 800813569 |
217 | /// ``` |
218 | pub fn adler32<R: Read>(reader: &mut R) -> Result<u32> { |
219 | let mut hash = Adler32::new(); |
220 | let mut buf = [0; 4096]; |
221 | |
222 | loop { |
223 | match reader.read(&mut buf) { |
224 | Ok(0) => return Ok(hash.finish()), |
225 | Ok(n) => { |
226 | hash.write(&buf[..n]); |
227 | } |
228 | Err(err) => return Err(err), |
229 | } |
230 | } |
231 | } |
232 | } |
233 | |
234 | #[cfg (feature = "std" )] |
235 | pub mod bufread { |
236 | //! BufRead-based hashing. |
237 | //! |
238 | //! Separate `BufRead` trait implemented to allow for custom buffer size optimization. |
239 | //! |
240 | //! # Example |
241 | //! ```rust |
242 | //! use std::io::{Cursor, BufReader}; |
243 | //! use simd_adler32::bufread::adler32; |
244 | //! |
245 | //! let mut reader = Cursor::new(b"Hello there" ); |
246 | //! let mut reader = BufReader::new(reader); |
247 | //! let hash = adler32(&mut reader).unwrap(); |
248 | //! |
249 | //! println!("{}" , hash) // 800813569 |
250 | //! ``` |
251 | use crate::Adler32; |
252 | use std::io::{BufRead, ErrorKind, Result}; |
253 | |
254 | /// Compute Adler-32 hash on buf reader until EOF. |
255 | /// |
256 | /// # Example |
257 | /// ```rust |
258 | /// use std::io::{Cursor, BufReader}; |
259 | /// use simd_adler32::bufread::adler32; |
260 | /// |
261 | /// let mut reader = Cursor::new(b"Hello there" ); |
262 | /// let mut reader = BufReader::new(reader); |
263 | /// let hash = adler32(&mut reader).unwrap(); |
264 | /// |
265 | /// println!("{}" , hash) // 800813569 |
266 | /// ``` |
267 | pub fn adler32<R: BufRead>(reader: &mut R) -> Result<u32> { |
268 | let mut hash = Adler32::new(); |
269 | |
270 | loop { |
271 | let consumed = match reader.fill_buf() { |
272 | Ok(buf) => { |
273 | if buf.is_empty() { |
274 | return Ok(hash.finish()); |
275 | } |
276 | |
277 | hash.write(buf); |
278 | buf.len() |
279 | } |
280 | Err(err) => match err.kind() { |
281 | ErrorKind::Interrupted => continue, |
282 | ErrorKind::UnexpectedEof => return Ok(hash.finish()), |
283 | _ => return Err(err), |
284 | }, |
285 | }; |
286 | |
287 | reader.consume(consumed); |
288 | } |
289 | } |
290 | } |
291 | |
292 | #[cfg (test)] |
293 | mod tests { |
294 | #[test ] |
295 | fn test_from_checksum() { |
296 | let buf = b"rust is pretty cool man" ; |
297 | let sum = 0xdeadbeaf; |
298 | |
299 | let mut simd = super::Adler32::from_checksum(sum); |
300 | let mut adler = adler::Adler32::from_checksum(sum); |
301 | |
302 | simd.write(buf); |
303 | adler.write_slice(buf); |
304 | |
305 | let simd = simd.finish(); |
306 | let scalar = adler.checksum(); |
307 | |
308 | assert_eq!(simd, scalar); |
309 | } |
310 | } |
311 | |