zlib_rs/
adler32.rs

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