byond_crc32/
lib.rs

1//! ## Example
2//!
3//! ```rust
4//! use byond_crc32::Crc32;
5//!
6//! let mut crc32 = Crc32::new();
7//! crc32.update(b"123456789");
8//! let checksum = crc32.as_u32();
9//! ```
10
11#![cfg_attr(not(any(test, feature = "std")), no_std)]
12
13pub mod baseline;
14mod combine;
15pub mod specialized;
16mod tables;
17
18#[cfg(not(feature = "std"))]
19use core::hash::Hasher;
20#[cfg(feature = "std")]
21use std::hash::Hasher;
22
23const DEFAULT_CRC32: u32 = 0xffffffff;
24
25#[derive(Clone, Copy, Debug, Eq, PartialEq)]
26enum State {
27    Baseline(baseline::State),
28    Specialized(specialized::State),
29}
30
31#[derive(Clone, Copy, Debug, Eq, PartialEq)]
32/// Represents an in-progress CRC-32/BYOND computation.
33pub struct Crc32 {
34    len: u64,
35    state: State,
36}
37
38impl Crc32 {
39    /// Creates a new CRC-32/BYOND computation hasher.
40    pub fn new() -> Self {
41        Self::new_with_initial(DEFAULT_CRC32, 0)
42    }
43
44    /// Creates a new CRC-32/BYOND computation hasher with the
45    /// given initial checksum.
46    ///
47    /// The `len` parameter represents the amount of bytes consumed to
48    /// create the existing checksum, and is used when combining checksums.
49    pub fn new_with_initial(crc: u32, len: u64) -> Self {
50        let state = specialized::State::new(crc).map_or_else(
51            || State::Baseline(baseline::State::new(crc)),
52            State::Specialized,
53        );
54        Self { len, state }
55    }
56
57    /// Gets the underlying checksum value.
58    pub fn as_u32(&self) -> u32 {
59        match self.state {
60            State::Baseline(ref state) => state.as_u32(),
61            State::Specialized(ref state) => state.as_u32(),
62        }
63    }
64
65    /// Returns true if no data has been consumed so far.
66    pub fn is_empty(&self) -> bool {
67        self.len == 0
68    }
69
70    /// The length of data consumed to create the current checksum value.
71    pub fn len(&self) -> u64 {
72        self.len
73    }
74
75    /// Resets the CRC-32/BYOND computation hasher to its initial state.
76    pub fn reset(&mut self) {
77        self.len = 0;
78        match self.state {
79            State::Baseline(ref mut state) => state.reset(),
80            State::Specialized(ref mut state) => state.reset(),
81        }
82    }
83
84    /// Updates the CRC-32/BYOND computation with the given `bytes`.
85    pub fn update(&mut self, bytes: &[u8]) {
86        self.len += bytes.len() as u64;
87        match self.state {
88            State::Baseline(ref mut state) => state.update(bytes),
89            State::Specialized(ref mut state) => state.update(bytes),
90        }
91    }
92
93    /// Combines two CRC-32/BYOND checksums.
94    pub fn combine(a: &Self, b: &Self) -> Self {
95        let (crc1, crc2) = (a.as_u32(), b.as_u32());
96        Self::new_with_initial(combine::combine(crc1, crc2, b.len), a.len + b.len)
97    }
98}
99
100impl Default for Crc32 {
101    fn default() -> Self {
102        Self::new()
103    }
104}
105
106impl Hasher for Crc32 {
107    fn finish(&self) -> u64 {
108        self.as_u32() as u64
109    }
110
111    fn write(&mut self, bytes: &[u8]) {
112        self.update(bytes);
113    }
114}
115
116impl PartialEq<u32> for Crc32 {
117    fn eq(&self, &other: &u32) -> bool {
118        self.as_u32() == other
119    }
120}
121
122impl PartialEq<Crc32> for u32 {
123    fn eq(&self, other: &Crc32) -> bool {
124        *self == other.as_u32()
125    }
126}
127
128#[cfg(test)]
129fn golden(crc: u32, bytes: &[u8]) -> u32 {
130    bytes.iter().fold(crc, |mut crc, &byte| {
131        crc ^= u32::from(byte) << 24;
132        for _ in 0..8 {
133            crc = if crc & 0x80000000 != 0 {
134                (crc << 1) ^ 0xaf
135            } else {
136                crc << 1
137            };
138        }
139        crc
140    })
141}
142
143#[cfg(test)]
144mod tests {
145    use quickcheck_macros::quickcheck;
146
147    use crate::golden;
148
149    const CHECK: u32 = 0xa5fd3138;
150
151    #[test]
152    fn golden_is_valid() {
153        assert_eq!(CHECK, golden(crate::DEFAULT_CRC32, b"123456789"));
154    }
155
156    #[quickcheck]
157    fn check(data: Vec<u8>) -> bool {
158        let mut crc32 = super::Crc32::new();
159        crc32.update(data.as_slice());
160        crc32.as_u32() == golden(crate::DEFAULT_CRC32, data.as_slice())
161    }
162
163    #[test]
164    fn check_combine() {
165        let mut crc_a = super::Crc32::new();
166        let mut crc_b = super::Crc32::new();
167        crc_a.update(b"12345");
168        crc_b.update(b"6789");
169        assert_eq!(CHECK, super::Crc32::combine(&crc_a, &crc_b));
170    }
171}