Skip to main content

rustlavel_http/compression/
checksum.rs

1//! The two checksums the compressed formats carry.
2//!
3//! gzip (RFC 1952 §2.3.1) ends a member with a CRC-32 of the uncompressed
4//! data; zlib (RFC 1950 §2.2) ends its stream with an Adler-32. Neither is a
5//! cryptographic hash — they exist to catch a truncated or corrupted stream
6//! before a caller trusts the bytes that came out of the inflater — so the
7//! "no cryptography from scratch" exception does not apply and both are
8//! written here.
9//!
10//! Both come in an incremental form (`Crc32::new().update(..).finish()`) for
11//! callers that see the data in pieces, and a one-shot function for the
12//! common case.
13
14/// The reflected form of the IEEE 802.3 polynomial 0x04C11DB7. Reflecting it
15/// lets the byte-at-a-time loop shift right and read the input least
16/// significant bit first, which is the bit order gzip, zip and PNG all agree
17/// on.
18const CRC32_POLYNOMIAL: u32 = 0xEDB8_8320;
19
20/// One entry per byte value: the CRC of that byte alone. Built at compile
21/// time, so it costs nothing at start-up and lives in read-only memory.
22const CRC32_TABLE: [u32; 256] = build_crc32_table();
23
24const fn build_crc32_table() -> [u32; 256] {
25    let mut table = [0u32; 256];
26    let mut byte = 0;
27    while byte < 256 {
28        let mut crc = byte as u32;
29        let mut bit = 0;
30        while bit < 8 {
31            crc = if crc & 1 != 0 { CRC32_POLYNOMIAL ^ (crc >> 1) } else { crc >> 1 };
32            bit += 1;
33        }
34        table[byte] = crc;
35        byte += 1;
36    }
37    table
38}
39
40/// The CRC-32 gzip uses (IEEE 802.3, reflected, initial value and final XOR
41/// both all-ones). `crc32(b"123456789")` is `0xCBF43926`, the check value
42/// every CRC catalogue lists for this variant.
43pub fn crc32(bytes: &[u8]) -> u32 {
44    let mut crc = Crc32::new();
45    crc.update(bytes);
46    crc.finish()
47}
48
49/// An incremental CRC-32.
50#[derive(Debug, Clone)]
51pub struct Crc32 {
52    /// The running register, kept inverted so that `update` is a plain table
53    /// loop and the initial value / final XOR of the standard fall out of
54    /// `new` and `finish`.
55    state: u32,
56}
57
58impl Default for Crc32 {
59    fn default() -> Self {
60        Self::new()
61    }
62}
63
64impl Crc32 {
65    pub fn new() -> Self {
66        Self { state: 0xFFFF_FFFF }
67    }
68
69    pub fn update(&mut self, bytes: &[u8]) {
70        let mut crc = self.state;
71        for &byte in bytes {
72            crc = CRC32_TABLE[((crc ^ u32::from(byte)) & 0xFF) as usize] ^ (crc >> 8);
73        }
74        self.state = crc;
75    }
76
77    pub fn finish(&self) -> u32 {
78        !self.state
79    }
80}
81
82/// The largest prime below 2^16, which is what makes Adler-32 slightly better
83/// at catching errors than a plain 16-bit sum (RFC 1950 §8.2).
84const ADLER_MODULUS: u32 = 65_521;
85
86/// How many bytes can be added to the running sums before either of them can
87/// overflow a `u32`, so the modulo only needs taking once per chunk rather
88/// than once per byte. The bound is the one zlib derives: the largest `n`
89/// with `255n(n+1)/2 + (n+1)(65520) < 2^32`.
90const ADLER_CHUNK: usize = 5552;
91
92/// Adler-32, the zlib trailer checksum. `adler32(b"Wikipedia")` is
93/// `0x11E60398`.
94pub fn adler32(bytes: &[u8]) -> u32 {
95    let mut adler = Adler32::new();
96    adler.update(bytes);
97    adler.finish()
98}
99
100/// An incremental Adler-32.
101#[derive(Debug, Clone)]
102pub struct Adler32 {
103    /// The sum of every byte seen so far, starting from one — the "a" of the
104    /// RFC — and the sum of every intermediate value of `a` — the "b".
105    a: u32,
106    b: u32,
107}
108
109impl Default for Adler32 {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115impl Adler32 {
116    pub fn new() -> Self {
117        Self { a: 1, b: 0 }
118    }
119
120    pub fn update(&mut self, bytes: &[u8]) {
121        for chunk in bytes.chunks(ADLER_CHUNK) {
122            for &byte in chunk {
123                self.a += u32::from(byte);
124                self.b += self.a;
125            }
126            self.a %= ADLER_MODULUS;
127            self.b %= ADLER_MODULUS;
128        }
129    }
130
131    pub fn finish(&self) -> u32 {
132        (self.b << 16) | self.a
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn crc32_check_value() {
142        // The reference check value for CRC-32/ISO-HDLC, the variant gzip uses.
143        assert_eq!(crc32(b"123456789"), 0xCBF4_3926);
144    }
145
146    #[test]
147    fn crc32_of_nothing_is_zero() {
148        assert_eq!(crc32(b""), 0);
149    }
150
151    #[test]
152    fn crc32_incremental_matches_one_shot() {
153        let data: Vec<u8> = (0..10_000u32).map(|i| (i * 7 % 251) as u8).collect();
154        let mut crc = Crc32::new();
155        for piece in data.chunks(333) {
156            crc.update(piece);
157        }
158        assert_eq!(crc.finish(), crc32(&data));
159    }
160
161    #[test]
162    fn adler32_check_value() {
163        // The worked example from the Wikipedia article on Adler-32.
164        assert_eq!(adler32(b"Wikipedia"), 0x11E6_0398);
165    }
166
167    #[test]
168    fn adler32_of_nothing_is_one() {
169        // RFC 1950 §8.2: "a" starts at one, so the empty checksum is 1, not 0.
170        assert_eq!(adler32(b""), 1);
171    }
172
173    #[test]
174    fn adler32_incremental_matches_one_shot_across_chunk_boundary() {
175        // Long enough that the deferred modulo has to be taken several times,
176        // and split at odd sizes so chunk boundaries do not line up with it.
177        let data: Vec<u8> = (0..30_000u32).map(|i| (i.wrapping_mul(2654435761) >> 24) as u8).collect();
178        let mut adler = Adler32::new();
179        for piece in data.chunks(1234) {
180            adler.update(piece);
181        }
182        assert_eq!(adler.finish(), adler32(&data));
183        // And a high-valued input, where the sums grow fastest.
184        let ones = vec![0xFFu8; 3 * ADLER_CHUNK + 17];
185        let mut adler = Adler32::new();
186        adler.update(&ones[..100]);
187        adler.update(&ones[100..]);
188        assert_eq!(adler.finish(), adler32(&ones));
189    }
190}