Coverage Report

Created: 2026-07-14 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zlib-rs-0.6.6/src/adler32/avx2.rs
Line
Count
Source
1
//! # Safety
2
//!
3
//! The functions in this module should only be executed on x86 machines with the AVX2 extension.
4
use core::arch::x86_64::{
5
    __m256i, _mm256_add_epi32, _mm256_castsi256_si128, _mm256_extracti128_si256, _mm256_madd_epi16,
6
    _mm256_maddubs_epi16, _mm256_permutevar8x32_epi32, _mm256_sad_epu8, _mm256_slli_epi32,
7
    _mm256_zextsi128_si256, _mm_add_epi32, _mm_cvtsi128_si32, _mm_cvtsi32_si128, _mm_shuffle_epi32,
8
    _mm_unpackhi_epi64,
9
};
10
11
use crate::adler32::{
12
    generic::{adler32_len_16, adler32_len_64},
13
    BASE, NMAX,
14
};
15
16
0
const fn __m256i_literal(bytes: [u8; 32]) -> __m256i {
17
    // SAFETY: any valid [u8; 32] represents a valid __m256i
18
0
    unsafe { core::mem::transmute(bytes) }
19
0
}
20
21
const DOT2V: __m256i = __m256i_literal([
22
    32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9,
23
    8, 7, 6, 5, 4, 3, 2, 1,
24
]);
25
26
const DOT3V: __m256i = __m256i_literal([
27
    1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
28
]);
29
30
const ZERO: __m256i = __m256i_literal([0u8; 32]);
31
32
/// 32 bit horizontal sum, adapted from Agner Fog's vector library.
33
#[target_feature(enable = "avx2")]
34
97.5k
unsafe fn hsum256(x: __m256i) -> u32 {
35
    #[allow(unused_unsafe)] // because target features 1.1
36
    unsafe {
37
97.5k
        let sum1 = _mm_add_epi32(_mm256_extracti128_si256(x, 1), _mm256_castsi256_si128(x));
38
97.5k
        let sum2 = _mm_add_epi32(sum1, _mm_unpackhi_epi64(sum1, sum1));
39
97.5k
        let sum3 = _mm_add_epi32(sum2, _mm_shuffle_epi32(sum2, 1));
40
97.5k
        _mm_cvtsi128_si32(sum3) as u32
41
    }
42
97.5k
}
43
44
#[target_feature(enable = "avx2")]
45
97.5k
unsafe fn partial_hsum256(x: __m256i) -> u32 {
46
    const PERM_VEC: __m256i = __m256i_literal([
47
        0, 0, 0, 0, //
48
        2, 0, 0, 0, //
49
        4, 0, 0, 0, //
50
        6, 0, 0, 0, //
51
        1, 0, 0, 0, //
52
        1, 0, 0, 0, //
53
        1, 0, 0, 0, //
54
        1, 0, 0, 0, //
55
    ]);
56
57
    #[allow(unused_unsafe)] // because target features 1.1
58
    unsafe {
59
97.5k
        let non_zero = _mm256_permutevar8x32_epi32(x, PERM_VEC);
60
97.5k
        let non_zero_sse = _mm256_castsi256_si128(non_zero);
61
97.5k
        let sum2 = _mm_add_epi32(non_zero_sse, _mm_unpackhi_epi64(non_zero_sse, non_zero_sse));
62
97.5k
        let sum3 = _mm_add_epi32(sum2, _mm_shuffle_epi32(sum2, 1));
63
97.5k
        _mm_cvtsi128_si32(sum3) as u32
64
    }
65
97.5k
}
66
67
4.36k
pub fn adler32_avx2(adler: u32, src: &[u8]) -> u32 {
68
4.36k
    assert!(crate::cpu_features::is_enabled_avx2_and_bmi2());
69
    // SAFETY: the assertion above ensures this code is not executed unless the CPU has AVX2.
70
4.36k
    unsafe { adler32_avx2_help(adler, src) }
71
4.36k
}
72
73
#[target_feature(enable = "avx2")]
74
#[target_feature(enable = "bmi2")]
75
#[target_feature(enable = "bmi1")]
76
4.36k
unsafe fn adler32_avx2_help(adler: u32, src: &[u8]) -> u32 {
77
4.36k
    if src.is_empty() {
78
1.37k
        return adler;
79
2.99k
    }
80
81
    // SAFETY: [u8; 32] safely transmutes into __m256i.
82
2.99k
    let (before, middle, after) = unsafe { src.align_to::<__m256i>() };
83
84
2.99k
    let mut adler1 = (adler >> 16) & 0xffff;
85
2.99k
    let mut adler0 = adler & 0xffff;
86
87
2.99k
    let adler = if before.len() < 16 {
88
2.84k
        adler32_len_16(adler0, before, adler1)
89
156
    } else if before.len() < 32 {
90
156
        adler32_len_64(adler0, before, adler1)
91
    } else {
92
0
        adler
93
    };
94
95
2.99k
    adler1 = (adler >> 16) & 0xffff;
96
2.99k
    adler0 = adler & 0xffff;
97
98
    // use largest step possible (without causing overflow)
99
97.5k
    for chunk in middle.chunks(NMAX as usize / 32) {
100
97.5k
        (adler0, adler1) = unsafe { helper_32_bytes(adler0, adler1, chunk) };
101
97.5k
    }
102
103
2.99k
    if !after.is_empty() {
104
2.45k
        if after.len() < 16 {
105
1.28k
            return adler32_len_16(adler0, after, adler1);
106
1.17k
        } else if after.len() < 32 {
107
1.17k
            return adler32_len_64(adler0, after, adler1);
108
        } else {
109
0
            unreachable!()
110
        }
111
539
    }
112
113
539
    adler0 | (adler1 << 16)
114
4.36k
}
115
116
#[target_feature(enable = "avx2")]
117
97.5k
unsafe fn helper_32_bytes(mut adler0: u32, mut adler1: u32, src: &[__m256i]) -> (u32, u32) {
118
    unsafe {
119
97.5k
        let mut vs1 = _mm256_zextsi128_si256(_mm_cvtsi32_si128(adler0 as i32));
120
97.5k
        let mut vs2 = _mm256_zextsi128_si256(_mm_cvtsi32_si128(adler1 as i32));
121
122
97.5k
        let mut vs1_0 = vs1;
123
97.5k
        let mut vs3 = ZERO;
124
125
16.6M
        for vbuf in src.iter().copied() {
126
16.6M
            let vs1_sad = _mm256_sad_epu8(vbuf, ZERO); // Sum of abs diff, resulting in 2 x int32's
127
16.6M
128
16.6M
            vs1 = _mm256_add_epi32(vs1, vs1_sad);
129
16.6M
            vs3 = _mm256_add_epi32(vs3, vs1_0);
130
16.6M
            let v_short_sum2 = _mm256_maddubs_epi16(vbuf, DOT2V); // sum 32 uint8s to 16 shorts
131
16.6M
            let vsum2 = _mm256_madd_epi16(v_short_sum2, DOT3V); // sum 16 shorts to 8 uint32s
132
16.6M
            vs2 = _mm256_add_epi32(vsum2, vs2);
133
16.6M
            vs1_0 = vs1;
134
16.6M
        }
135
136
        /* Defer the multiplication with 32 to outside of the loop */
137
97.5k
        vs3 = _mm256_slli_epi32(vs3, 5);
138
97.5k
        vs2 = _mm256_add_epi32(vs2, vs3);
139
140
97.5k
        adler0 = partial_hsum256(vs1) % BASE;
141
97.5k
        adler1 = hsum256(vs2) % BASE;
142
143
97.5k
        (adler0, adler1)
144
    }
145
97.5k
}
146
147
#[cfg(test)]
148
#[cfg(target_feature = "avx2")]
149
mod test {
150
    use super::*;
151
152
    #[test]
153
    fn empty_input() {
154
        let avx2 = adler32_avx2(0, &[]);
155
        let rust = crate::adler32::generic::adler32_rust(0, &[]);
156
157
        assert_eq!(rust, avx2);
158
    }
159
160
    quickcheck::quickcheck! {
161
        fn adler32_avx2_is_adler32_rust(v: Vec<u8>, start: u32) -> bool {
162
            let avx2 = adler32_avx2(start, &v);
163
            let rust = crate::adler32::generic::adler32_rust(start, &v);
164
165
            rust == avx2
166
        }
167
    }
168
169
    const INPUT: [u8; 1024] = {
170
        let mut array = [0; 1024];
171
        let mut i = 0;
172
        while i < array.len() {
173
            array[i] = i as u8;
174
            i += 1;
175
        }
176
177
        array
178
    };
179
180
    #[test]
181
    fn start_alignment() {
182
        // SIMD algorithm is sensitive to alignment;
183
        for i in 0..16 {
184
            for start in [crate::ADLER32_INITIAL_VALUE as u32, 42] {
185
                let avx2 = adler32_avx2(start, &INPUT[i..]);
186
                let rust = crate::adler32::generic::adler32_rust(start, &INPUT[i..]);
187
188
                assert_eq!(avx2, rust, "offset = {i}, start = {start}");
189
            }
190
        }
191
    }
192
193
    #[test]
194
    #[cfg_attr(miri, ignore)]
195
    fn large_input() {
196
        const DEFAULT: &[u8] = include_bytes!("../deflate/test-data/paper-100k.pdf");
197
198
        let avx2 = adler32_avx2(42, DEFAULT);
199
        let rust = crate::adler32::generic::adler32_rust(42, DEFAULT);
200
201
        assert_eq!(avx2, rust);
202
    }
203
}