Skip to main content

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