Coverage Report

Created: 2026-07-25 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zlib-ng/chunkset_tpl.h
Line
Count
Source
1
/* chunkset_tpl.h -- inline functions to copy small data chunks.
2
 * For conditions of distribution and use, see copyright notice in zlib.h
3
 */
4
5
#include "zbuild.h"
6
#include <stdlib.h>
7
8
/* Returns the chunk size */
9
0
static inline size_t CHUNKSIZE(void) {
10
0
    return sizeof(chunk_t);
11
0
}
Unexecuted instantiation: chunkset_sse2.c:chunksize_sse2
Unexecuted instantiation: chunkset_ssse3.c:chunksize_ssse3
Unexecuted instantiation: chunkset_avx2.c:chunksize_avx2
Unexecuted instantiation: chunkset_avx512.c:chunksize_avx512
12
13
/* Behave like memcpy, but assume that it's OK to overwrite at least
14
   chunk_t bytes of output even if the length is shorter than this,
15
   that the length is non-zero, and that `from` lags `out` by at least
16
   sizeof chunk_t bytes (or that they don't overlap at all or simply that
17
   the distance is less than the length of the copy).
18
19
   Aside from better memory bus utilization, this means that short copies
20
   (chunk_t bytes or fewer) will fall straight through the loop
21
   without iteration, which will hopefully make the branch prediction more
22
   reliable. */
23
#ifndef HAVE_CHUNKCOPY
24
0
static inline uint8_t* CHUNKCOPY(uint8_t *out, uint8_t const *from, size_t len) {
25
0
    Assert(len > 0, "chunkcopy should never have a length 0");
26
0
    chunk_t chunk;
27
0
    size_t align = ((len - 1) % sizeof(chunk_t)) + 1;
28
0
    loadchunk(from, &chunk);
29
0
    storechunk(out, &chunk);
30
0
    out += align;
31
0
    from += align;
32
0
    len -= align;
33
0
    while (len > 0) {
34
0
        loadchunk(from, &chunk);
35
0
        storechunk(out, &chunk);
36
0
        out += sizeof(chunk_t);
37
0
        from += sizeof(chunk_t);
38
0
        len -= sizeof(chunk_t);
39
0
    }
40
0
    return out;
41
0
}
Unexecuted instantiation: chunkset_sse2.c:chunkcopy_sse2
Unexecuted instantiation: chunkset_ssse3.c:chunkcopy_ssse3
Unexecuted instantiation: chunkset_avx2.c:chunkcopy_avx2
42
#endif
43
44
/* Perform short copies until distance can be rewritten as being at least
45
   sizeof chunk_t.
46
47
   This assumes that it's OK to overwrite at least the first
48
   2*sizeof(chunk_t) bytes of output even if the copy is shorter than this.
49
   This assumption holds because inflate_fast() starts every iteration with at
50
   least 258 bytes of output space available (258 being the maximum length
51
   output from a single token; see inflate_fast()'s assumptions below). */
52
0
static inline uint8_t* CHUNKUNROLL(uint8_t *out, unsigned *dist, unsigned *len) {
53
0
    unsigned char const *from = out - *dist;
54
0
    chunk_t chunk;
55
0
    while (*dist < *len && *dist < sizeof(chunk_t)) {
56
0
        loadchunk(from, &chunk);
57
0
        storechunk(out, &chunk);
58
0
        out += *dist;
59
0
        *len -= *dist;
60
0
        *dist += *dist;
61
0
    }
62
0
    return out;
63
0
}
Unexecuted instantiation: chunkset_sse2.c:chunkunroll_sse2
Unexecuted instantiation: chunkset_ssse3.c:chunkunroll_ssse3
Unexecuted instantiation: chunkset_avx2.c:chunkunroll_avx2
Unexecuted instantiation: chunkset_avx512.c:chunkunroll_avx512
64
65
#ifndef HAVE_CHUNK_MAG
66
/* Loads a magazine to feed into memory of the pattern */
67
0
static inline chunk_t GET_CHUNK_MAG(uint8_t *buf, size_t *chunk_rem, size_t dist) {
68
        /* This code takes string of length dist from "from" and repeats
69
         * it for as many times as can fit in a chunk_t (vector register) */
70
0
        size_t cpy_dist;
71
0
        size_t bytes_remaining = sizeof(chunk_t);
72
0
        chunk_t chunk_load;
73
0
        uint8_t *cur_chunk = (uint8_t *)&chunk_load;
74
0
        while (bytes_remaining) {
75
0
            cpy_dist = MIN(dist, bytes_remaining);
76
0
            memcpy(cur_chunk, buf, cpy_dist);
77
0
            bytes_remaining -= cpy_dist;
78
0
            cur_chunk += cpy_dist;
79
            /* This allows us to bypass an expensive integer division since we're effectively
80
             * counting in this loop, anyway */
81
0
            *chunk_rem = cpy_dist;
82
0
        }
83
84
0
        return chunk_load;
85
0
}
86
#endif
87
88
#if defined(HAVE_HALF_CHUNK) && !defined(HAVE_HALFCHUNKCOPY)
89
0
static inline uint8_t* HALFCHUNKCOPY(uint8_t *out, uint8_t const *from, size_t len) {
90
0
    Assert(len > 0, "halfchunkcopy should never have a length 0");
91
0
    halfchunk_t chunk;
92
0
    size_t align = ((len - 1) % sizeof(halfchunk_t)) + 1;
93
0
    loadhalfchunk(from, &chunk);
94
0
    storehalfchunk(out, &chunk);
95
0
    out += align;
96
0
    from += align;
97
0
    len -= align;
98
0
    while (len > 0) {
99
0
        loadhalfchunk(from, &chunk);
100
0
        storehalfchunk(out, &chunk);
101
0
        out += sizeof(halfchunk_t);
102
0
        from += sizeof(halfchunk_t);
103
0
        len -= sizeof(halfchunk_t);
104
0
    }
105
0
    return out;
106
0
}
107
#endif
108
109
/* Copy DIST bytes from OUT - DIST into OUT + DIST * k, for 0 <= k < LEN/DIST.
110
   Return OUT + LEN. */
111
0
static inline uint8_t* CHUNKMEMSET(uint8_t *out, uint8_t *from, size_t len) {
112
    /* Debug performance related issues when len < sizeof(uint64_t):
113
       Assert(len >= sizeof(uint64_t), "chunkmemset should be called on larger chunks"); */
114
0
    Assert(from != out, "chunkmemset cannot have a distance 0");
115
116
0
    chunk_t chunk_load;
117
0
    size_t chunk_mod = 0;
118
0
    size_t adv_amount;
119
0
    size_t dist = (size_t)ABS(out - from);
120
121
    /* We are supporting the case for when we are reading bytes from ahead in the buffer.
122
     * We now have to handle this, though it wasn't _quite_ clear if this rare circumstance
123
     * always needed to be handled here or if we're just now seeing it because we are
124
     * dispatching to this function, more */
125
0
    if (out < from && dist < len) {
126
#ifdef HAVE_MASKED_READWRITE
127
0
        if (len <= sizeof(chunk_t)) {
128
0
            loadchunk_masked(from, &chunk_load, len);
129
0
            storechunk_masked(out, &chunk_load, len);
130
0
            return out + len;
131
0
        }
132
0
#endif
133
        /* Here the memmove semantics match perfectly, as when this happens we are
134
         * effectively sliding down the contents of memory by dist bytes */
135
0
        memmove(out, from, len);
136
0
        return out + len;
137
0
    }
138
139
#ifndef HAVE_CHUNKMEMSET_1
140
0
    if (dist == 1) {
141
0
        memset(out, *from, len);
142
0
        return out + len;
143
0
    } else
144
0
#endif
145
0
    if (dist >= len || dist >= sizeof(chunk_t)) {
146
0
        return CHUNKCOPY(out, from, len);
147
0
    }
148
149
    /* Only AVX2+ as there's 128 bit vectors and 256 bit. We allow for shorter vector
150
     * lengths because they serve to allow more cases to fall into chunkcopy, as the
151
     * distance of the shorter length is still deemed a safe distance. We rewrite this
152
     * here rather than calling the ssse3 variant directly now because doing so required
153
     * dispatching to another function and broke inlining for this function entirely. We
154
     * also can merge an assert and some remainder peeling behavior into the same code blocks,
155
     * making the code a little smaller.  */
156
#ifdef HAVE_HALF_CHUNK
157
0
    if (dist > 1 && len <= sizeof(halfchunk_t)) {
158
0
        if (dist >= sizeof(halfchunk_t))
159
0
            return HALFCHUNKCOPY(out, from, len);
160
161
0
        if ((dist % 2) != 0 || dist == 6) {
162
0
            halfchunk_t halfchunk_load = GET_HALFCHUNK_MAG(from, &chunk_mod, dist);
163
164
0
            if (len == sizeof(halfchunk_t)) {
165
0
                storehalfchunk(out, &halfchunk_load);
166
0
                len -= sizeof(halfchunk_t);
167
0
                out += sizeof(halfchunk_t);
168
0
            }
169
170
0
            chunk_load = halfchunk2whole(&halfchunk_load);
171
0
            goto rem_bytes;
172
0
        }
173
0
    }
174
0
#endif
175
176
#ifdef HAVE_CHUNKMEMSET_1
177
    /* Broadcast + chunk store beats memset on NEON/AVX2/AVX-512 */
178
0
    if (dist == 1) {
179
0
        chunkmemset_1(from, &chunk_load);
180
0
    } else
181
0
#endif
182
0
#ifdef HAVE_CHUNKMEMSET_2
183
0
    if (dist == 2) {
184
0
        chunkmemset_2(from, &chunk_load);
185
0
    } else
186
0
#endif
187
0
#ifdef HAVE_CHUNKMEMSET_4
188
0
    if (dist == 4) {
189
0
        chunkmemset_4(from, &chunk_load);
190
0
    } else
191
0
#endif
192
0
#ifdef HAVE_CHUNKMEMSET_8
193
0
    if (dist == 8) {
194
0
        chunkmemset_8(from, &chunk_load);
195
0
    } else
196
0
#endif
197
#ifdef HAVE_CHUNKMEMSET_16
198
0
    if (dist == 16) {
199
0
        chunkmemset_16(from, &chunk_load);
200
0
    } else
201
0
#endif
202
0
    chunk_load = GET_CHUNK_MAG(from, &chunk_mod, dist);
203
204
0
    if (len <= sizeof(chunk_t)) {
205
#ifdef HAVE_MASKED_READWRITE
206
        storechunk_masked(out, &chunk_load, len);
207
#else
208
        storechunk(out, &chunk_load);
209
#endif
210
0
        return out + len;
211
0
    }
212
213
0
    adv_amount = sizeof(chunk_t) - chunk_mod;
214
215
0
    while (len >= (2 * sizeof(chunk_t))) {
216
0
        storechunk(out, &chunk_load);
217
0
        storechunk(out + adv_amount, &chunk_load);
218
0
        out += 2 * adv_amount;
219
0
        len -= 2 * adv_amount;
220
0
    }
221
222
    /* If we don't have a "dist" length that divides evenly into a vector
223
     * register, we can write the whole vector register but we need only
224
     * advance by the amount of the whole string that fits in our chunk_t.
225
     * If we do divide evenly into the vector length, adv_amount = chunk_t size*/
226
0
    while (len >= sizeof(chunk_t)) {
227
0
        storechunk(out, &chunk_load);
228
0
        len -= adv_amount;
229
0
        out += adv_amount;
230
0
    }
231
232
#ifdef HAVE_HALF_CHUNK
233
0
rem_bytes:
234
0
#endif
235
0
    if (len) {
236
#ifdef HAVE_MASKED_READWRITE
237
        storechunk_masked(out, &chunk_load, len);
238
        out += len;
239
#else
240
        uint8_t *chunk_p = (uint8_t *)&chunk_load;
241
0
        if (len & 16) { memcpy(out, chunk_p, 16); out += 16; chunk_p += 16; }
242
0
        if (len & 8) { memcpy(out, chunk_p, 8); out += 8; chunk_p += 8; }
243
0
        if (len & 4) { memcpy(out, chunk_p, 4); out += 4; chunk_p += 4; }
244
0
        if (len & 2) { memcpy(out, chunk_p, 2); out += 2; chunk_p += 2; }
245
0
        if (len & 1) { *out++ = *chunk_p; }
246
#endif
247
0
    }
248
249
0
    return out;
250
0
}
Unexecuted instantiation: chunkset_sse2.c:chunkmemset_sse2
Unexecuted instantiation: chunkset_ssse3.c:chunkmemset_ssse3
Unexecuted instantiation: chunkset_avx2.c:chunkmemset_avx2
Unexecuted instantiation: chunkset_avx512.c:chunkmemset_avx512
251
252
0
Z_INTERNAL uint8_t* CHUNKMEMSET_SAFE(uint8_t *out, uint8_t *from, size_t len, size_t left) {
253
#if OPTIMAL_CMP < 32
254
    static const uintptr_t align_mask = 7;
255
#elif OPTIMAL_CMP == 32
256
    static const uintptr_t align_mask = 3;
257
#endif
258
259
0
    len = MIN(len, left);
260
261
#if OPTIMAL_CMP < 64
262
    while (((uintptr_t)out & align_mask) && (len > 0)) {
263
        *out++ = *from++;
264
        --len;
265
        --left;
266
    }
267
#endif
268
269
#ifndef HAVE_MASKED_READWRITE
270
0
    if (UNLIKELY(left < sizeof(chunk_t))) {
271
0
        while (len > 0) {
272
0
            *out++ = *from++;
273
0
            --len;
274
0
        }
275
276
0
        return out;
277
0
    }
278
0
#endif
279
280
0
    if (len)
281
0
        out = CHUNKMEMSET(out, from, len);
282
283
0
    return out;
284
0
}
Unexecuted instantiation: chunkmemset_safe_sse2
Unexecuted instantiation: chunkmemset_safe_ssse3
Unexecuted instantiation: chunkmemset_safe_avx2
Unexecuted instantiation: chunkmemset_safe_avx512
285
286
static inline uint8_t *CHUNKCOPY_SAFE(uint8_t *out, uint8_t *from, size_t len, uint8_t *safe)
287
0
{
288
0
    if (out == from)
289
0
        return out + len;
290
291
0
    size_t safelen = (safe - out);
292
0
    len = MIN(len, safelen);
293
294
#ifndef HAVE_MASKED_READWRITE
295
0
    size_t from_dist = (size_t)ABS(safe - from);
296
0
    if (UNLIKELY(from_dist < sizeof(chunk_t) || safelen < sizeof(chunk_t))) {
297
0
        while (len--) {
298
0
            *out++ = *from++;
299
0
        }
300
301
0
        return out;
302
0
    }
303
0
#endif
304
305
0
    return CHUNKMEMSET(out, from, len);
306
0
}
Unexecuted instantiation: chunkset_sse2.c:CHUNKCOPY_SAFE
Unexecuted instantiation: chunkset_ssse3.c:CHUNKCOPY_SAFE
Unexecuted instantiation: chunkset_avx2.c:CHUNKCOPY_SAFE
Unexecuted instantiation: chunkset_avx512.c:CHUNKCOPY_SAFE