Skip to main content

crc32fast/
lib.rs

1//! Fast, SIMD-accelerated CRC32 (IEEE) checksum computation.
2//!
3//! ## Usage
4//!
5//! ### Simple usage
6//!
7//! For simple use-cases, you can call the [`hash()`] convenience function to
8//! directly compute the CRC32 checksum for a given byte slice:
9//!
10//! ```rust
11//! let checksum = crc32fast::hash(b"foo bar baz");
12//! ```
13//!
14//! ### Advanced usage
15//!
16//! For use-cases that require more flexibility or performance, for example when
17//! processing large amounts of data, you can create and manipulate a [`Hasher`]:
18//!
19//! ```rust
20//! use crc32fast::Hasher;
21//!
22//! let mut hasher = Hasher::new();
23//! hasher.update(b"foo bar baz");
24//! let checksum = hasher.finalize();
25//! ```
26//!
27//! ## Performance
28//!
29//! This crate contains multiple CRC32 implementations:
30//!
31//! - A fast baseline implementation which processes up to 16 bytes per iteration
32//! - An optimized implementation for modern `x86` using `sse` and `pclmulqdq` instructions
33//! - Wider `x86` implementations using `vpclmulqdq` on 256-bit (`avx2`) and 512-bit (`avx512f`)
34//!   registers (available when built with Rust 1.89 or newer)
35//! - An optimized implementation for `aarch64` using `crc32` instructions, interleaving three
36//!   chains on larger inputs, and from 64 bytes on CPUs with `pmull` (the `aes` feature), one that
37//!   folds 4 to 16 independent streams with carry-less multiplies
38//!
39//! Calling the [`Hasher::new`] constructor at runtime will perform a feature detection to select the most
40//! optimal implementation for the current CPU feature set.
41
42#![cfg_attr(not(feature = "std"), no_std)]
43#![deny(missing_docs)]
44#![deny(unsafe_code)]
45use core::fmt;
46use core::hash;
47
48mod baseline;
49mod combine;
50mod specialized;
51mod table;
52
53/// Computes the CRC32 hash of a byte slice.
54///
55/// Check out [`Hasher`] for more advanced use-cases.
56#[inline]
57pub fn hash(buf: &[u8]) -> u32 {
58    let mut h = Hasher::new();
59    h.update(buf);
60    h.finalize()
61}
62
63#[derive(Clone)]
64/// Represents an in-progress CRC32 computation.
65pub struct Hasher {
66    amount: u64,
67    state: u32,
68    spec: Option<specialized::State>,
69}
70
71const DEFAULT_INIT_STATE: u32 = 0;
72
73impl Hasher {
74    /// Create a new `Hasher`.
75    ///
76    /// This will perform a CPU feature detection at runtime to select the most
77    /// optimal implementation for the current processor architecture.
78    #[inline]
79    pub fn new() -> Self {
80        Self::new_with_initial(DEFAULT_INIT_STATE)
81    }
82
83    /// Create a new `Hasher` with an initial CRC32 state.
84    ///
85    /// This works just like `Hasher::new`, except that it allows for an initial
86    /// CRC32 state to be passed in.
87    #[inline]
88    pub fn new_with_initial(init: u32) -> Self {
89        Self::new_with_initial_len(init, 0)
90    }
91
92    /// Create a new `Hasher` with an initial CRC32 state.
93    ///
94    /// As `new_with_initial`, but also accepts a length (in bytes). The
95    /// resulting object can then be used with `combine` to compute `crc(a ||
96    /// b)` from `crc(a)`, `crc(b)`, and `len(b)`.
97    #[inline]
98    pub fn new_with_initial_len(init: u32, amount: u64) -> Self {
99        Self::internal_new_specialized(init, amount)
100            .unwrap_or_else(|| Self::internal_new_baseline(init, amount))
101    }
102
103    #[doc(hidden)]
104    // Internal-only API. Don't use.
105    #[inline]
106    pub fn internal_new_baseline(init: u32, amount: u64) -> Self {
107        Hasher {
108            amount,
109            state: init,
110            spec: None,
111        }
112    }
113
114    #[doc(hidden)]
115    // Internal-only API. Don't use.
116    #[inline]
117    pub fn internal_new_specialized(init: u32, amount: u64) -> Option<Self> {
118        Some(Hasher {
119            amount,
120            state: init,
121            spec: Some(specialized::State::new()?),
122        })
123    }
124
125    /// Process the given byte slice and update the hash state.
126    #[inline]
127    pub fn update(&mut self, buf: &[u8]) {
128        self.amount += buf.len() as u64;
129        self.state = match &self.spec {
130            None => baseline::update_fast_16(self.state, buf),
131            Some(spec) => spec.update(self.state, buf),
132        };
133    }
134
135    /// Returns the total number of bytes represented by this hash state.
136    ///
137    /// This includes the length passed to [`Hasher::new_with_initial_len`] and
138    /// the lengths of any hash states added with [`Hasher::combine`].
139    /// [`Hasher::reset`] sets the length back to zero.
140    ///
141    /// A state created with [`Hasher::new_with_initial`] starts with a length
142    /// of zero because the length of the initial state is not known.
143    ///
144    /// ```
145    /// use crc32fast::Hasher;
146    ///
147    /// let mut hasher = Hasher::new();
148    /// hasher.update(b"hello");
149    /// hasher.update(b" world");
150    /// assert_eq!(hasher.total_len(), 11);
151    /// ```
152    #[inline]
153    pub fn total_len(&self) -> u64 {
154        self.amount
155    }
156
157    /// Finalize the hash state and return the computed CRC32 value.
158    #[inline]
159    pub fn finalize(self) -> u32 {
160        self.state
161    }
162
163    /// Reset the hash state.
164    #[inline]
165    pub fn reset(&mut self) {
166        self.state = 0;
167        self.amount = 0;
168    }
169
170    /// Combine the hash state with the hash state for the subsequent block of bytes.
171    pub fn combine(&mut self, other: &Self) {
172        self.amount += other.amount;
173        self.state = combine::combine(self.state, other.state, other.amount);
174    }
175}
176
177impl fmt::Debug for Hasher {
178    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179        f.debug_struct("crc32fast::Hasher").finish()
180    }
181}
182
183impl Default for Hasher {
184    fn default() -> Self {
185        Self::new()
186    }
187}
188
189impl hash::Hasher for Hasher {
190    fn write(&mut self, bytes: &[u8]) {
191        self.update(bytes)
192    }
193
194    fn finish(&self) -> u64 {
195        u64::from(self.clone().finalize())
196    }
197}
198
199#[cfg(test)]
200mod test {
201    use super::Hasher;
202
203    #[test]
204    fn total_len_tracks_resumed_and_combined_blocks() {
205        let prefix = b"hello";
206        let suffix = b" world";
207        let mut hasher = Hasher::new_with_initial_len(super::hash(prefix), prefix.len() as u64);
208        assert_eq!(hasher.total_len(), 5);
209        hasher.update(b" ");
210        assert_eq!(hasher.total_len(), 6);
211
212        let mut remaining = Hasher::new();
213        remaining.update(&suffix[1..]);
214        hasher.combine(&remaining);
215        assert_eq!(hasher.total_len(), 11);
216        assert_eq!(hasher.clone().finalize(), super::hash(b"hello world"));
217        assert_eq!(remaining.total_len(), 5);
218
219        hasher.reset();
220        assert_eq!(hasher.total_len(), 0);
221        hasher.update(b"again");
222        assert_eq!(hasher.total_len(), 5);
223        assert_eq!(hasher.finalize(), super::hash(b"again"));
224    }
225
226    #[test]
227    fn total_len_for_empty_and_unknown_initial_length() {
228        let mut hasher = Hasher::new();
229        assert_eq!(hasher.total_len(), 0);
230        hasher.update(b"");
231        hasher.combine(&Hasher::new());
232        assert_eq!(hasher.total_len(), 0);
233        assert_eq!(
234            Hasher::new_with_initial(super::hash(b"prefix")).total_len(),
235            0
236        );
237        assert_eq!(
238            Hasher::new_with_initial_len(0, u64::MAX).total_len(),
239            u64::MAX
240        );
241    }
242
243    quickcheck::quickcheck! {
244        fn combine(bytes_1: Vec<u8>, bytes_2: Vec<u8>) -> bool {
245            let mut hash_a = Hasher::new();
246            hash_a.update(&bytes_1);
247            hash_a.update(&bytes_2);
248            let mut hash_b = Hasher::new();
249            hash_b.update(&bytes_2);
250            let mut hash_c = Hasher::new();
251            hash_c.update(&bytes_1);
252            hash_c.combine(&hash_b);
253
254            hash_a.finalize() == hash_c.finalize()
255        }
256
257        fn combine_from_len(bytes_1: Vec<u8>, bytes_2: Vec<u8>) -> bool {
258            let mut hash_a = Hasher::new();
259            hash_a.update(&bytes_1);
260
261            let mut hash_b = Hasher::new();
262            hash_b.update(&bytes_2);
263
264            let mut hash_ab = Hasher::new();
265            hash_ab.update(&bytes_1);
266            hash_ab.update(&bytes_2);
267            let ab = hash_ab.finalize();
268
269            hash_a.combine(&hash_b);
270            hash_a.finalize() == ab
271        }
272    }
273}