zlib_rs/
adler32.rs

1#![warn(unsafe_op_in_unsafe_fn)]
2
3#[cfg(target_arch = "x86_64")]
4mod avx2;
5mod generic;
6#[cfg(target_arch = "aarch64")]
7mod neon;
8#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
9mod wasm;
10
11pub fn adler32(start_checksum: u32, data: &[u8]) -> u32 {
12    #[cfg(target_arch = "x86_64")]
13    if crate::cpu_features::is_enabled_avx2() {
14        return avx2::adler32_avx2(start_checksum, data);
15    }
16
17    #[cfg(target_arch = "aarch64")]
18    if crate::cpu_features::is_enabled_neon() {
19        return self::neon::adler32_neon(start_checksum, data);
20    }
21
22    #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
23    if crate::cpu_features::is_enabled_simd128() {
24        return self::wasm::adler32_wasm(start_checksum, data);
25    }
26
27    generic::adler32_rust(start_checksum, data)
28}
29
30pub fn adler32_fold_copy(start_checksum: u32, dst: &mut [u8], src: &[u8]) -> u32 {
31    debug_assert!(dst.len() >= src.len(), "{} < {}", dst.len(), src.len());
32
33    // integrating the memcpy into the adler32 function did not have any benefits, and in fact was
34    // a bit slower for very small chunk sizes.
35    dst[..src.len()].copy_from_slice(src);
36    adler32(start_checksum, src)
37}
38
39pub fn adler32_combine(adler1: u32, adler2: u32, len2: u64) -> u32 {
40    const BASE: u64 = self::BASE as u64;
41
42    let rem = len2 % BASE;
43
44    let adler1 = adler1 as u64;
45    let adler2 = adler2 as u64;
46
47    /* the derivation of this formula is left as an exercise for the reader */
48    let mut sum1 = adler1 & 0xffff;
49    let mut sum2 = rem * sum1;
50    sum2 %= BASE;
51    sum1 += (adler2 & 0xffff) + BASE - 1;
52    sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem;
53
54    if sum1 >= BASE {
55        sum1 -= BASE;
56    }
57    if sum1 >= BASE {
58        sum1 -= BASE;
59    }
60    if sum2 >= (BASE << 1) {
61        sum2 -= BASE << 1;
62    }
63    if sum2 >= BASE {
64        sum2 -= BASE;
65    }
66
67    (sum1 | (sum2 << 16)) as u32
68}
69
70// inefficient but correct, useful for testing
71#[cfg(test)]
72fn naive_adler32(start_checksum: u32, data: &[u8]) -> u32 {
73    const MOD_ADLER: u32 = 65521; // Largest prime smaller than 2^16
74
75    let mut a = start_checksum & 0xFFFF;
76    let mut b = (start_checksum >> 16) & 0xFFFF;
77
78    for &byte in data {
79        a = (a + byte as u32) % MOD_ADLER;
80        b = (b + a) % MOD_ADLER;
81    }
82
83    (b << 16) | a
84}
85
86const BASE: u32 = 65521; /* largest prime smaller than 65536 */
87const NMAX: u32 = 5552;
88
89#[cfg(test)]
90mod test {
91    use super::*;
92
93    #[test]
94    fn naive_is_fancy_small_inputs() {
95        for i in 0..128 {
96            let v = (0u8..i).collect::<Vec<_>>();
97            assert_eq!(naive_adler32(1, &v), generic::adler32_rust(1, &v));
98        }
99    }
100
101    #[test]
102    fn test_adler32_combine() {
103        ::quickcheck::quickcheck(test as fn(_) -> _);
104
105        fn test(data: Vec<u8>) -> bool {
106            let Some(buf_len) = data.first().copied() else {
107                return true;
108            };
109
110            let buf_size = Ord::max(buf_len, 1) as usize;
111
112            let mut adler1 = 1;
113            let mut adler2 = 1;
114
115            for chunk in data.chunks(buf_size) {
116                adler1 = adler32(adler1, chunk);
117            }
118
119            adler2 = adler32(adler2, &data);
120
121            assert_eq!(adler1, adler2);
122
123            let combine1 = adler32_combine(adler1, adler2, data.len() as _);
124            let combine2 = adler32_combine(adler1, adler1, data.len() as _);
125            assert_eq!(combine1, combine2);
126
127            true
128        }
129    }
130}