simd_adler32/lib.rs
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(
81 all(feature = "nightly", any(target_arch = "x86", target_arch = "x86_64")),
82 feature(stdarch_x86_avx512, avx512_target_feature)
83)]
84#![cfg_attr(
85 all(
86 feature = "nightly",
87 target_arch = "wasm64",
88 target_feature = "simd128"
89 ),
90 feature(simd_wasm64)
91)]
92
93#[doc(hidden)]
94pub mod hash;
95#[doc(hidden)]
96pub mod imp;
97
98pub use hash::*;
99use imp::{get_imp, Adler32Imp};
100
101/// An adler32 hash generator type.
102#[derive(Clone)]
103pub struct Adler32 {
104 a: u16,
105 b: u16,
106 update: Adler32Imp,
107}
108
109impl Adler32 {
110 /// Constructs a new `Adler32`.
111 ///
112 /// Potential overhead here due to runtime feature detection although in testing on 100k
113 /// and 10k random byte arrays it was not really noticeable.
114 ///
115 /// # Examples
116 /// ```rust
117 /// use simd_adler32::Adler32;
118 ///
119 /// let mut adler = Adler32::new();
120 /// ```
121 pub fn new() -> Self {
122 Default::default()
123 }
124
125 /// Constructs a new `Adler32` using existing checksum.
126 ///
127 /// Potential overhead here due to runtime feature detection although in testing on 100k
128 /// and 10k random byte arrays it was not really noticeable.
129 ///
130 /// # Examples
131 /// ```rust
132 /// use simd_adler32::Adler32;
133 ///
134 /// let mut adler = Adler32::from_checksum(0xdeadbeaf);
135 /// ```
136 pub fn from_checksum(checksum: u32) -> Self {
137 Self {
138 a: checksum as u16,
139 b: (checksum >> 16) as u16,
140 update: get_imp(),
141 }
142 }
143
144 /// Computes hash for supplied data and stores results in internal state.
145 pub fn write(&mut self, data: &[u8]) {
146 let (a, b) = (self.update)(self.a, self.b, data);
147
148 self.a = a;
149 self.b = b;
150 }
151
152 /// Returns the hash value for the values written so far.
153 ///
154 /// Despite its name, the method does not reset the hasher’s internal state. Additional
155 /// writes will continue from the current value. If you need to start a fresh hash
156 /// value, you will have to use `reset`.
157 pub fn finish(&self) -> u32 {
158 (u32::from(self.b) << 16) | u32::from(self.a)
159 }
160
161 /// Resets the internal state.
162 pub fn reset(&mut self) {
163 self.a = 1;
164 self.b = 0;
165 }
166}
167
168/// Compute Adler-32 hash on `Adler32Hash` type.
169///
170/// # Arguments
171/// * `hash` - A Adler-32 hash-able type.
172///
173/// # Examples
174/// ```rust
175/// use simd_adler32::adler32;
176///
177/// let hash = adler32(b"Adler-32");
178/// println!("{}", hash); // 800813569
179/// ```
180pub fn adler32<H: Adler32Hash>(hash: &H) -> u32 {
181 hash.hash()
182}
183
184/// A Adler-32 hash-able type.
185pub trait Adler32Hash {
186 /// Feeds this value into `Adler32`.
187 fn hash(&self) -> u32;
188}
189
190impl Default for Adler32 {
191 fn default() -> Self {
192 Self {
193 a: 1,
194 b: 0,
195 update: get_imp(),
196 }
197 }
198}
199
200#[cfg(feature = "std")]
201pub mod read {
202 //! Reader-based hashing.
203 //!
204 //! # Example
205 //! ```rust
206 //! use std::io::Cursor;
207 //! use simd_adler32::read::adler32;
208 //!
209 //! let mut reader = Cursor::new(b"Hello there");
210 //! let hash = adler32(&mut reader).unwrap();
211 //!
212 //! println!("{}", hash) // 800813569
213 //! ```
214 use crate::Adler32;
215 use std::io::{Read, Result};
216
217 /// Compute Adler-32 hash on reader until EOF.
218 ///
219 /// # Example
220 /// ```rust
221 /// use std::io::Cursor;
222 /// use simd_adler32::read::adler32;
223 ///
224 /// let mut reader = Cursor::new(b"Hello there");
225 /// let hash = adler32(&mut reader).unwrap();
226 ///
227 /// println!("{}", hash) // 800813569
228 /// ```
229 pub fn adler32<R: Read>(reader: &mut R) -> Result<u32> {
230 let mut hash = Adler32::new();
231 let mut buf = [0; 4096];
232
233 loop {
234 match reader.read(&mut buf) {
235 Ok(0) => return Ok(hash.finish()),
236 Ok(n) => {
237 hash.write(&buf[..n]);
238 }
239 Err(err) => return Err(err),
240 }
241 }
242 }
243}
244
245#[cfg(feature = "std")]
246pub mod bufread {
247 //! BufRead-based hashing.
248 //!
249 //! Separate `BufRead` trait implemented to allow for custom buffer size optimization.
250 //!
251 //! # Example
252 //! ```rust
253 //! use std::io::{Cursor, BufReader};
254 //! use simd_adler32::bufread::adler32;
255 //!
256 //! let mut reader = Cursor::new(b"Hello there");
257 //! let mut reader = BufReader::new(reader);
258 //! let hash = adler32(&mut reader).unwrap();
259 //!
260 //! println!("{}", hash) // 800813569
261 //! ```
262 use crate::Adler32;
263 use std::io::{BufRead, ErrorKind, Result};
264
265 /// Compute Adler-32 hash on buf reader until EOF.
266 ///
267 /// # Example
268 /// ```rust
269 /// use std::io::{Cursor, BufReader};
270 /// use simd_adler32::bufread::adler32;
271 ///
272 /// let mut reader = Cursor::new(b"Hello there");
273 /// let mut reader = BufReader::new(reader);
274 /// let hash = adler32(&mut reader).unwrap();
275 ///
276 /// println!("{}", hash) // 800813569
277 /// ```
278 pub fn adler32<R: BufRead>(reader: &mut R) -> Result<u32> {
279 let mut hash = Adler32::new();
280
281 loop {
282 let consumed = match reader.fill_buf() {
283 Ok(buf) => {
284 if buf.is_empty() {
285 return Ok(hash.finish());
286 }
287
288 hash.write(buf);
289 buf.len()
290 }
291 Err(err) => match err.kind() {
292 ErrorKind::Interrupted => continue,
293 ErrorKind::UnexpectedEof => return Ok(hash.finish()),
294 _ => return Err(err),
295 },
296 };
297
298 reader.consume(consumed);
299 }
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 #[test]
306 fn test_from_checksum() {
307 let buf = b"rust is pretty cool man";
308 let sum = 0xdeadbeaf;
309
310 let mut simd = super::Adler32::from_checksum(sum);
311 let mut adler = adler::Adler32::from_checksum(sum);
312
313 simd.write(buf);
314 adler.write_slice(buf);
315
316 let simd = simd.finish();
317 let scalar = adler.checksum();
318
319 assert_eq!(simd, scalar);
320 }
321}