Coverage Report

Created: 2026-09-13 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zlib-ng/match_tpl.h
Line
Count
Source
1
/* match_tpl.h -- find longest match template for compare256 variants
2
 *
3
 * Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler
4
 * For conditions of distribution and use, see copyright notice in zlib.h
5
 *
6
 * Portions copyright (C) 2014-2021 Konstantin Nosov
7
 *  Fast-zlib optimized longest_match
8
 *  https://github.com/gildor2/fast_zlib
9
 */
10
11
#include "insert_string_p.h"
12
13
20.7M
#define EARLY_EXIT_TRIGGER_LEVEL 5
14
15
#define GOTO_NEXT_CHAIN \
16
65.4M
    if (--chain_length && (cur_match = prev[cur_match & wmask]) > limit) \
17
62.8M
        continue; \
18
2.56M
    return best_len;
19
20
/* Set match_start to the longest match starting at the given string and
21
 * return its length. Matches shorter or equal to prev_length are discarded,
22
 * in which case the result is equal to prev_length and match_start is garbage.
23
 *
24
 * IN assertions: cur_match is the head of the hash chain for the current
25
 * string (strstart) and its distance is <= MAX_DIST, and prev_length >=1
26
 * OUT assertion: the match length is not greater than s->lookahead
27
 */
28
60.5M
Z_INTERNAL uint32_t LONGEST_MATCH(deflate_state *const s, uint32_t cur_match) {
29
60.5M
    const unsigned wmask = W_MASK(s);
30
60.5M
    unsigned int strstart = s->strstart;
31
60.5M
    const unsigned char *window = s->window;
32
60.5M
    const Pos *prev = s->prev;
33
#ifdef LONGEST_MATCH_SLOW
34
    const Pos *head = s->head;
35
#endif
36
60.5M
    const unsigned char *scan;
37
60.5M
    const unsigned char *mbase_start = window;
38
60.5M
    const unsigned char *mbase_end;
39
60.5M
    uint32_t limit;
40
#ifdef LONGEST_MATCH_SLOW
41
    uint32_t limit_base;
42
#endif
43
#ifndef LONGEST_MATCH_SLOW
44
    int32_t early_exit;
45
#endif
46
60.5M
    uint32_t chain_length = s->max_chain_length;
47
60.5M
    uint32_t nice_match = (uint32_t)s->nice_match;
48
60.5M
    uint32_t best_len, offset;
49
60.5M
    uint32_t lookahead = s->lookahead;
50
60.5M
    uint32_t match_offset = 0;
51
60.5M
    uint64_t scan_start;
52
60.5M
    uint64_t scan_end;
53
54
    /* The code is optimized for STD_MAX_MATCH-2 multiple of 16. */
55
60.5M
    Assert(STD_MAX_MATCH == 258, "Code too clever");
56
57
60.5M
    best_len = s->prev_length ? s->prev_length : STD_MIN_MATCH-1;
58
60.5M
    if (UNLIKELY(best_len >= lookahead))
59
599
        return lookahead;
60
#ifdef LONGEST_MATCH_SLOW
61
#  ifdef LONGEST_MATCH_SLOW_ROLL
62
    /* Rolling-hash variant always runs the post-match offset search; the
63
     * compiler folds the constant away below.
64
     */
65
34.7M
    const int offset_search = 1;
66
#  else
67
    /* The post-match offset search only pays off when we entered with a
68
     * prior best_len from lazy evaluation, so gate on it at function entry.
69
     */
70
5.05M
    const int offset_search = (best_len >= STD_MIN_MATCH);
71
#  endif
72
5.05M
#endif
73
74
    /* Calculate read offset which should only extend an extra byte to find the
75
     * next best match length. When best_len is shorter than the read width, we
76
     * diff the mismatched bytes instead.
77
     */
78
60.5M
    offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
79
80
20.7M
    scan = window + strstart;
81
20.7M
    scan_start = zng_memread_8(scan);
82
20.7M
    scan_end = zng_memread_8(scan+offset);
83
20.7M
    mbase_end = (mbase_start+offset);
84
85
    /* Do not waste too much time if we already have a good match */
86
60.5M
    if (UNLIKELY(best_len >= s->good_match))
87
118k
        chain_length >>= 2;
88
89
    /* Stop when cur_match becomes <= limit. To simplify the code,
90
     * we prevent matches with the string of window index 0
91
     */
92
60.5M
    limit = strstart > MAX_DIST(s) ? (strstart - MAX_DIST(s)) : 0;
93
#ifndef LONGEST_MATCH_SLOW
94
20.7M
    early_exit = s->level < EARLY_EXIT_TRIGGER_LEVEL;
95
#endif
96
#ifdef LONGEST_MATCH_SLOW
97
    limit_base = limit;
98
39.7M
    if (best_len >= STD_MIN_MATCH) {
99
        /* We're continuing search (lazy evaluation). Find a most distant
100
         * chain by hashing substrings within the match area. We cannot use
101
         * s->prev[strstart+1,...] immediately because those strings are not
102
         * yet inserted into the hash table.
103
         */
104
#  ifdef LONGEST_MATCH_SLOW_ROLL
105
        uint32_t hash;
106
        uint32_t pos;
107
108
        hash = update_hash_roll(0, scan[1]);
109
        hash = update_hash_roll(hash, scan[2]);
110
111
25.8M
        for (uint32_t i = 3; i <= best_len; i++) {
112
19.3M
            hash = update_hash_roll(hash, scan[i]);
113
19.3M
            pos = head[hash];
114
19.3M
            if (UNLIKELY(pos < cur_match)) {
115
5.24M
                match_offset = i - 2;
116
5.24M
                cur_match = pos;
117
5.24M
            }
118
19.3M
        }
119
#  else /* 4-byte Knuth hash, fresh lookup per offset */
120
1.93M
        for (uint32_t i = 1; i + (WANT_MIN_MATCH - 1) <= best_len; i++) {
121
1.70M
            uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan + i));
122
1.70M
            uint32_t hash;
123
1.70M
            UPDATE_HASH_KNUTH(hash, val);
124
1.70M
            uint32_t pos = head[hash];
125
1.70M
            if (pos < cur_match) {
126
276k
                match_offset = i;
127
276k
                cur_match = pos;
128
276k
            }
129
1.70M
        }
130
#  endif
131
132
        /* Update offset-dependent variables */
133
6.67M
        limit = limit_base+match_offset;
134
6.67M
        if (UNLIKELY(cur_match <= limit))
135
1.59M
            return best_len;
136
5.07M
        mbase_start -= match_offset;
137
5.07M
        mbase_end -= match_offset;
138
5.07M
    }
139
38.1M
#endif
140
38.1M
    Assert((unsigned long)strstart <= s->window_size - MIN_LOOKAHEAD, "need lookahead");
141
73.6M
    for (;;) {
142
73.6M
        if (UNLIKELY(cur_match >= strstart))
143
82
            break;
144
145
        /* Skip to next match if the match length cannot increase or if the match length is
146
         * less than 2. Note that the checks below for insufficient lookahead only occur
147
         * occasionally for performance reasons.
148
         * Therefore uninitialized memory will be accessed and conditional jumps will be made
149
         * that depend on those values. However the length of the match is limited to the
150
         * lookahead, so the output of deflate is not affected by the uninitialized values.
151
         */
152
73.6M
        uint32_t len;
153
73.6M
        if (best_len < sizeof(uint64_t)) {
154
69.0M
            uint64_t cand_start = zng_memread_8(mbase_start + cur_match);
155
69.0M
            if (scan_start != cand_start) {
156
                /* Peel the first candidate out of the loop. A full 8-byte match falls straight
157
                 * through to compare256, and single-candidate chains (barely-compressible data)
158
                 * run with no loop overhead. */
159
67.9M
                uint64_t first_mask = zng_first_bytes_mask64(best_len + 1);
160
67.9M
                uint64_t diff = scan_start ^ cand_start;
161
                /* A candidate beats best_len only when its first best_len+1 bytes match, i.e.
162
                 * those bytes of the XOR are zero. The masked test rejects without running ctz. */
163
67.9M
                if (UNLIKELY((diff & first_mask) == 0)) {
164
10.1M
                    len = zng_first_diff_byte64(diff);
165
10.1M
                    goto short_match_accept;
166
10.1M
                }
167
57.8M
                if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
168
30.4M
                    return best_len;
169
27.3M
                cand_start = zng_memread_8(mbase_start + cur_match);
170
27.3M
                if (scan_start != cand_start) {
171
                    /* Walk the remaining candidates with the chain advance kept inline. */
172
156M
                    for (;;) {
173
156M
                        diff = scan_start ^ cand_start;
174
156M
                        if (UNLIKELY((diff & first_mask) == 0)) {
175
3.49M
                            len = zng_first_diff_byte64(diff);
176
3.49M
                            goto short_match_accept;
177
3.49M
                        }
178
152M
                        if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
179
23.7M
                            return best_len;
180
129M
                        cand_start = zng_memread_8(mbase_start + cur_match);
181
129M
                        if (scan_start == cand_start)
182
98.4k
                            break;
183
129M
                    }
184
27.2M
                }
185
27.3M
            }
186
            /* All 8 bytes match, fallthrough to compare256 for the tail. */
187
69.0M
        } else {
188
            /* Pre-filter the candidate on the start and end sentinels before compare256 using
189
             * simple 8-byte comparison since best_len >= 8. */
190
56.1M
            for (;;) {
191
                /* First check the end of the candidate at best_len+1 due to the higher
192
                 * likelihood of a mismatch. */
193
56.1M
                if (zng_memcmp_8(mbase_end+cur_match, &scan_end) == 0 &&
194
5.46M
                    zng_memcmp_8(mbase_start+cur_match, &scan_start) == 0)
195
3.91M
                    break;
196
52.1M
                GOTO_NEXT_CHAIN;
197
52.1M
            }
198
4.55M
        }
199
5.15M
        len = COMPARE256(scan+2, mbase_start+cur_match+2) + 2;
200
5.15M
        Assert(scan+len <= window+(unsigned)(s->window_size-1), "wild scan");
201
202
5.15M
        if (len > best_len)
203
19.6M
short_match_accept:
204
19.6M
        {
205
19.6M
            uint32_t match_start = cur_match - match_offset;
206
19.6M
            s->match_start = match_start;
207
208
            /* Do not look for better matches if the current match reaches
209
             * or exceeds the end of the input.
210
             */
211
19.6M
            if (UNLIKELY(len >= lookahead))
212
1.29k
                return lookahead;
213
16.6M
            if (UNLIKELY(len >= nice_match))
214
441k
                return len;
215
216
16.2M
            best_len = len;
217
218
16.2M
            offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
219
220
16.2M
            scan_end = zng_memread_8(scan+offset);
221
222
#ifdef LONGEST_MATCH_SLOW
223
            /* Look for a better string offset */
224
11.9M
            if (UNLIKELY(offset_search && len > STD_MIN_MATCH && match_start + len < strstart)) {
225
5.14M
                const unsigned char *scan_endstr;
226
5.14M
                uint32_t hash;
227
5.14M
                uint32_t pos, next_pos;
228
229
                /* Go back to offset 0 */
230
                cur_match -= match_offset;
231
                match_offset = 0;
232
                next_pos = cur_match;
233
234
                /* Walk prev[] for positions inside the match. The bound keeps
235
                 * the hash window within the match, so we follow chains that
236
                 * share bytes with the current match, not data past its end.
237
                 */
238
#  ifdef LONGEST_MATCH_SLOW_ROLL
239
80.2M
                for (uint32_t i = 0; i <= len - STD_MIN_MATCH; i++) {
240
#  else /* 4-byte Knuth hash needs len - 4 bound */
241
770k
                for (uint32_t i = 0; i <= len - WANT_MIN_MATCH; i++) {
242
740k
#  endif
243
740k
                    pos = prev[(cur_match + i) & wmask];
244
77.2M
                    if (UNLIKELY(pos < next_pos)) {
245
                        /* Hash chain is more distant, use it */
246
6.92M
                        if (UNLIKELY(pos <= limit_base + i))
247
1.38M
                            return best_len;
248
5.54M
                        next_pos = pos;
249
5.54M
                        match_offset = i;
250
5.54M
                    }
251
740k
                }
252
                /* Switch cur_match to next_pos chain */
253
3.76M
                cur_match = next_pos;
254
255
                /* Try hash head at a window covering the tail of the current
256
                 * match to find chains that extend farther back. The window
257
                 * width differs per variant: 3 bytes for rolling, 4 for Knuth.
258
                 */
259
#  ifdef LONGEST_MATCH_SLOW_ROLL
260
3.73M
                scan_endstr = scan + len - (STD_MIN_MATCH-1);
261
262
                hash = update_hash_roll(0, scan_endstr[0]);
263
                hash = update_hash_roll(hash, scan_endstr[1]);
264
                hash = update_hash_roll(hash, scan_endstr[2]);
265
266
                pos = head[hash];
267
3.73M
                if (UNLIKELY(pos < cur_match)) {
268
800k
                    match_offset = len - (STD_MIN_MATCH-1);
269
800k
                    if (pos <= limit_base + match_offset)
270
370k
                        return best_len;
271
430k
                    cur_match = pos;
272
430k
                }
273
#  else
274
30.1k
                scan_endstr = scan + len - WANT_MIN_MATCH;
275
30.1k
                uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan_endstr));
276
30.1k
                UPDATE_HASH_KNUTH(hash, val);
277
278
                pos = head[hash];
279
30.1k
                if (pos < cur_match) {
280
0
                    match_offset = len - WANT_MIN_MATCH;
281
0
                    if (pos <= limit_base + match_offset)
282
0
                        return best_len;
283
0
                    cur_match = pos;
284
0
                }
285
30.1k
#  endif
286
287
                /* Update offset-dependent variables */
288
3.39M
                limit = limit_base+match_offset;
289
3.39M
                mbase_start = window-match_offset;
290
3.39M
                mbase_end = (mbase_start+offset);
291
3.39M
                continue;
292
3.76M
            }
293
6.79M
#endif
294
6.79M
            mbase_end = (mbase_start+offset);
295
6.79M
        }
296
#ifndef LONGEST_MATCH_SLOW
297
16.6k
        else if (UNLIKELY(early_exit)) {
298
            /* The probability of finding a match later if we here is pretty low, so for
299
             * performance it's best to outright stop here for the lower compression levels
300
             */
301
482
            break;
302
482
        }
303
4.29M
#endif
304
13.2M
        GOTO_NEXT_CHAIN;
305
13.2M
    }
306
564
    return best_len;
307
38.1M
}
Unexecuted instantiation: longest_match_sse2
Unexecuted instantiation: longest_match_slow_knuth_sse2
Unexecuted instantiation: longest_match_slow_roll_sse2
longest_match_avx2
Line
Count
Source
28
20.7M
Z_INTERNAL uint32_t LONGEST_MATCH(deflate_state *const s, uint32_t cur_match) {
29
20.7M
    const unsigned wmask = W_MASK(s);
30
20.7M
    unsigned int strstart = s->strstart;
31
20.7M
    const unsigned char *window = s->window;
32
20.7M
    const Pos *prev = s->prev;
33
#ifdef LONGEST_MATCH_SLOW
34
    const Pos *head = s->head;
35
#endif
36
20.7M
    const unsigned char *scan;
37
20.7M
    const unsigned char *mbase_start = window;
38
20.7M
    const unsigned char *mbase_end;
39
20.7M
    uint32_t limit;
40
#ifdef LONGEST_MATCH_SLOW
41
    uint32_t limit_base;
42
#endif
43
20.7M
#ifndef LONGEST_MATCH_SLOW
44
20.7M
    int32_t early_exit;
45
20.7M
#endif
46
20.7M
    uint32_t chain_length = s->max_chain_length;
47
20.7M
    uint32_t nice_match = (uint32_t)s->nice_match;
48
20.7M
    uint32_t best_len, offset;
49
20.7M
    uint32_t lookahead = s->lookahead;
50
20.7M
    uint32_t match_offset = 0;
51
20.7M
    uint64_t scan_start;
52
20.7M
    uint64_t scan_end;
53
54
    /* The code is optimized for STD_MAX_MATCH-2 multiple of 16. */
55
20.7M
    Assert(STD_MAX_MATCH == 258, "Code too clever");
56
57
20.7M
    best_len = s->prev_length ? s->prev_length : STD_MIN_MATCH-1;
58
20.7M
    if (UNLIKELY(best_len >= lookahead))
59
0
        return lookahead;
60
#ifdef LONGEST_MATCH_SLOW
61
#  ifdef LONGEST_MATCH_SLOW_ROLL
62
    /* Rolling-hash variant always runs the post-match offset search; the
63
     * compiler folds the constant away below.
64
     */
65
    const int offset_search = 1;
66
#  else
67
    /* The post-match offset search only pays off when we entered with a
68
     * prior best_len from lazy evaluation, so gate on it at function entry.
69
     */
70
    const int offset_search = (best_len >= STD_MIN_MATCH);
71
#  endif
72
#endif
73
74
    /* Calculate read offset which should only extend an extra byte to find the
75
     * next best match length. When best_len is shorter than the read width, we
76
     * diff the mismatched bytes instead.
77
     */
78
20.7M
    offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
79
80
20.7M
    scan = window + strstart;
81
20.7M
    scan_start = zng_memread_8(scan);
82
20.7M
    scan_end = zng_memread_8(scan+offset);
83
20.7M
    mbase_end = (mbase_start+offset);
84
85
    /* Do not waste too much time if we already have a good match */
86
20.7M
    if (UNLIKELY(best_len >= s->good_match))
87
0
        chain_length >>= 2;
88
89
    /* Stop when cur_match becomes <= limit. To simplify the code,
90
     * we prevent matches with the string of window index 0
91
     */
92
20.7M
    limit = strstart > MAX_DIST(s) ? (strstart - MAX_DIST(s)) : 0;
93
20.7M
#ifndef LONGEST_MATCH_SLOW
94
20.7M
    early_exit = s->level < EARLY_EXIT_TRIGGER_LEVEL;
95
20.7M
#endif
96
#ifdef LONGEST_MATCH_SLOW
97
    limit_base = limit;
98
    if (best_len >= STD_MIN_MATCH) {
99
        /* We're continuing search (lazy evaluation). Find a most distant
100
         * chain by hashing substrings within the match area. We cannot use
101
         * s->prev[strstart+1,...] immediately because those strings are not
102
         * yet inserted into the hash table.
103
         */
104
#  ifdef LONGEST_MATCH_SLOW_ROLL
105
        uint32_t hash;
106
        uint32_t pos;
107
108
        hash = update_hash_roll(0, scan[1]);
109
        hash = update_hash_roll(hash, scan[2]);
110
111
        for (uint32_t i = 3; i <= best_len; i++) {
112
            hash = update_hash_roll(hash, scan[i]);
113
            pos = head[hash];
114
            if (UNLIKELY(pos < cur_match)) {
115
                match_offset = i - 2;
116
                cur_match = pos;
117
            }
118
        }
119
#  else /* 4-byte Knuth hash, fresh lookup per offset */
120
        for (uint32_t i = 1; i + (WANT_MIN_MATCH - 1) <= best_len; i++) {
121
            uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan + i));
122
            uint32_t hash;
123
            UPDATE_HASH_KNUTH(hash, val);
124
            uint32_t pos = head[hash];
125
            if (pos < cur_match) {
126
                match_offset = i;
127
                cur_match = pos;
128
            }
129
        }
130
#  endif
131
132
        /* Update offset-dependent variables */
133
        limit = limit_base+match_offset;
134
        if (UNLIKELY(cur_match <= limit))
135
            return best_len;
136
        mbase_start -= match_offset;
137
        mbase_end -= match_offset;
138
    }
139
#endif
140
20.7M
    Assert((unsigned long)strstart <= s->window_size - MIN_LOOKAHEAD, "need lookahead");
141
24.1M
    for (;;) {
142
24.1M
        if (UNLIKELY(cur_match >= strstart))
143
82
            break;
144
145
        /* Skip to next match if the match length cannot increase or if the match length is
146
         * less than 2. Note that the checks below for insufficient lookahead only occur
147
         * occasionally for performance reasons.
148
         * Therefore uninitialized memory will be accessed and conditional jumps will be made
149
         * that depend on those values. However the length of the match is limited to the
150
         * lookahead, so the output of deflate is not affected by the uninitialized values.
151
         */
152
24.1M
        uint32_t len;
153
24.1M
        if (best_len < sizeof(uint64_t)) {
154
23.2M
            uint64_t cand_start = zng_memread_8(mbase_start + cur_match);
155
23.2M
            if (scan_start != cand_start) {
156
                /* Peel the first candidate out of the loop. A full 8-byte match falls straight
157
                 * through to compare256, and single-candidate chains (barely-compressible data)
158
                 * run with no loop overhead. */
159
22.5M
                uint64_t first_mask = zng_first_bytes_mask64(best_len + 1);
160
22.5M
                uint64_t diff = scan_start ^ cand_start;
161
                /* A candidate beats best_len only when its first best_len+1 bytes match, i.e.
162
                 * those bytes of the XOR are zero. The masked test rejects without running ctz. */
163
22.5M
                if (UNLIKELY((diff & first_mask) == 0)) {
164
3.04M
                    len = zng_first_diff_byte64(diff);
165
3.04M
                    goto short_match_accept;
166
3.04M
                }
167
19.4M
                if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
168
13.8M
                    return best_len;
169
5.64M
                cand_start = zng_memread_8(mbase_start + cur_match);
170
5.64M
                if (scan_start != cand_start) {
171
                    /* Walk the remaining candidates with the chain advance kept inline. */
172
13.4M
                    for (;;) {
173
13.4M
                        diff = scan_start ^ cand_start;
174
13.4M
                        if (UNLIKELY((diff & first_mask) == 0)) {
175
189k
                            len = zng_first_diff_byte64(diff);
176
189k
                            goto short_match_accept;
177
189k
                        }
178
13.2M
                        if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
179
5.39M
                            return best_len;
180
7.84M
                        cand_start = zng_memread_8(mbase_start + cur_match);
181
7.84M
                        if (scan_start == cand_start)
182
33.5k
                            break;
183
7.84M
                    }
184
5.62M
                }
185
5.64M
            }
186
            /* All 8 bytes match, fallthrough to compare256 for the tail. */
187
23.2M
        } else {
188
            /* Pre-filter the candidate on the start and end sentinels before compare256 using
189
             * simple 8-byte comparison since best_len >= 8. */
190
3.32M
            for (;;) {
191
                /* First check the end of the candidate at best_len+1 due to the higher
192
                 * likelihood of a mismatch. */
193
3.32M
                if (zng_memcmp_8(mbase_end+cur_match, &scan_end) == 0 &&
194
657k
                    zng_memcmp_8(mbase_start+cur_match, &scan_start) == 0)
195
642k
                    break;
196
2.67M
                GOTO_NEXT_CHAIN;
197
2.67M
            }
198
864k
        }
199
1.41M
        len = COMPARE256(scan+2, mbase_start+cur_match+2) + 2;
200
1.41M
        Assert(scan+len <= window+(unsigned)(s->window_size-1), "wild scan");
201
202
1.41M
        if (len > best_len)
203
6.02M
short_match_accept:
204
6.02M
        {
205
6.02M
            uint32_t match_start = cur_match - match_offset;
206
6.02M
            s->match_start = match_start;
207
208
            /* Do not look for better matches if the current match reaches
209
             * or exceeds the end of the input.
210
             */
211
6.02M
            if (UNLIKELY(len >= lookahead))
212
651
                return lookahead;
213
4.62M
            if (UNLIKELY(len >= nice_match))
214
351k
                return len;
215
216
4.27M
            best_len = len;
217
218
4.27M
            offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
219
220
4.27M
            scan_end = zng_memread_8(scan+offset);
221
222
#ifdef LONGEST_MATCH_SLOW
223
            /* Look for a better string offset */
224
            if (UNLIKELY(offset_search && len > STD_MIN_MATCH && match_start + len < strstart)) {
225
                const unsigned char *scan_endstr;
226
                uint32_t hash;
227
                uint32_t pos, next_pos;
228
229
                /* Go back to offset 0 */
230
                cur_match -= match_offset;
231
                match_offset = 0;
232
                next_pos = cur_match;
233
234
                /* Walk prev[] for positions inside the match. The bound keeps
235
                 * the hash window within the match, so we follow chains that
236
                 * share bytes with the current match, not data past its end.
237
                 */
238
#  ifdef LONGEST_MATCH_SLOW_ROLL
239
                for (uint32_t i = 0; i <= len - STD_MIN_MATCH; i++) {
240
#  else /* 4-byte Knuth hash needs len - 4 bound */
241
                for (uint32_t i = 0; i <= len - WANT_MIN_MATCH; i++) {
242
#  endif
243
                    pos = prev[(cur_match + i) & wmask];
244
                    if (UNLIKELY(pos < next_pos)) {
245
                        /* Hash chain is more distant, use it */
246
                        if (UNLIKELY(pos <= limit_base + i))
247
                            return best_len;
248
                        next_pos = pos;
249
                        match_offset = i;
250
                    }
251
                }
252
                /* Switch cur_match to next_pos chain */
253
                cur_match = next_pos;
254
255
                /* Try hash head at a window covering the tail of the current
256
                 * match to find chains that extend farther back. The window
257
                 * width differs per variant: 3 bytes for rolling, 4 for Knuth.
258
                 */
259
#  ifdef LONGEST_MATCH_SLOW_ROLL
260
                scan_endstr = scan + len - (STD_MIN_MATCH-1);
261
262
                hash = update_hash_roll(0, scan_endstr[0]);
263
                hash = update_hash_roll(hash, scan_endstr[1]);
264
                hash = update_hash_roll(hash, scan_endstr[2]);
265
266
                pos = head[hash];
267
                if (UNLIKELY(pos < cur_match)) {
268
                    match_offset = len - (STD_MIN_MATCH-1);
269
                    if (pos <= limit_base + match_offset)
270
                        return best_len;
271
                    cur_match = pos;
272
                }
273
#  else
274
                scan_endstr = scan + len - WANT_MIN_MATCH;
275
                uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan_endstr));
276
                UPDATE_HASH_KNUTH(hash, val);
277
278
                pos = head[hash];
279
                if (pos < cur_match) {
280
                    match_offset = len - WANT_MIN_MATCH;
281
                    if (pos <= limit_base + match_offset)
282
                        return best_len;
283
                    cur_match = pos;
284
                }
285
#  endif
286
287
                /* Update offset-dependent variables */
288
                limit = limit_base+match_offset;
289
                mbase_start = window-match_offset;
290
                mbase_end = (mbase_start+offset);
291
                continue;
292
            }
293
#endif
294
4.27M
            mbase_end = (mbase_start+offset);
295
4.27M
        }
296
16.6k
#ifndef LONGEST_MATCH_SLOW
297
16.6k
        else if (UNLIKELY(early_exit)) {
298
            /* The probability of finding a match later if we here is pretty low, so for
299
             * performance it's best to outright stop here for the lower compression levels
300
             */
301
482
            break;
302
482
        }
303
4.29M
#endif
304
4.29M
        GOTO_NEXT_CHAIN;
305
4.29M
    }
306
564
    return best_len;
307
20.7M
}
longest_match_slow_knuth_avx2
Line
Count
Source
28
5.06M
Z_INTERNAL uint32_t LONGEST_MATCH(deflate_state *const s, uint32_t cur_match) {
29
5.06M
    const unsigned wmask = W_MASK(s);
30
5.06M
    unsigned int strstart = s->strstart;
31
5.06M
    const unsigned char *window = s->window;
32
5.06M
    const Pos *prev = s->prev;
33
5.06M
#ifdef LONGEST_MATCH_SLOW
34
5.06M
    const Pos *head = s->head;
35
5.06M
#endif
36
5.06M
    const unsigned char *scan;
37
5.06M
    const unsigned char *mbase_start = window;
38
5.06M
    const unsigned char *mbase_end;
39
5.06M
    uint32_t limit;
40
5.06M
#ifdef LONGEST_MATCH_SLOW
41
5.06M
    uint32_t limit_base;
42
5.06M
#endif
43
#ifndef LONGEST_MATCH_SLOW
44
    int32_t early_exit;
45
#endif
46
5.06M
    uint32_t chain_length = s->max_chain_length;
47
5.06M
    uint32_t nice_match = (uint32_t)s->nice_match;
48
5.06M
    uint32_t best_len, offset;
49
5.06M
    uint32_t lookahead = s->lookahead;
50
5.06M
    uint32_t match_offset = 0;
51
5.06M
    uint64_t scan_start;
52
5.06M
    uint64_t scan_end;
53
54
    /* The code is optimized for STD_MAX_MATCH-2 multiple of 16. */
55
5.06M
    Assert(STD_MAX_MATCH == 258, "Code too clever");
56
57
5.06M
    best_len = s->prev_length ? s->prev_length : STD_MIN_MATCH-1;
58
5.06M
    if (UNLIKELY(best_len >= lookahead))
59
165
        return lookahead;
60
5.05M
#ifdef LONGEST_MATCH_SLOW
61
#  ifdef LONGEST_MATCH_SLOW_ROLL
62
    /* Rolling-hash variant always runs the post-match offset search; the
63
     * compiler folds the constant away below.
64
     */
65
    const int offset_search = 1;
66
#  else
67
    /* The post-match offset search only pays off when we entered with a
68
     * prior best_len from lazy evaluation, so gate on it at function entry.
69
     */
70
5.05M
    const int offset_search = (best_len >= STD_MIN_MATCH);
71
5.05M
#  endif
72
5.05M
#endif
73
74
    /* Calculate read offset which should only extend an extra byte to find the
75
     * next best match length. When best_len is shorter than the read width, we
76
     * diff the mismatched bytes instead.
77
     */
78
5.05M
    offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
79
80
5.05M
    scan = window + strstart;
81
5.05M
    scan_start = zng_memread_8(scan);
82
5.05M
    scan_end = zng_memread_8(scan+offset);
83
5.05M
    mbase_end = (mbase_start+offset);
84
85
    /* Do not waste too much time if we already have a good match */
86
5.05M
    if (UNLIKELY(best_len >= s->good_match))
87
47.7k
        chain_length >>= 2;
88
89
    /* Stop when cur_match becomes <= limit. To simplify the code,
90
     * we prevent matches with the string of window index 0
91
     */
92
5.05M
    limit = strstart > MAX_DIST(s) ? (strstart - MAX_DIST(s)) : 0;
93
#ifndef LONGEST_MATCH_SLOW
94
    early_exit = s->level < EARLY_EXIT_TRIGGER_LEVEL;
95
#endif
96
5.05M
#ifdef LONGEST_MATCH_SLOW
97
5.05M
    limit_base = limit;
98
5.05M
    if (best_len >= STD_MIN_MATCH) {
99
        /* We're continuing search (lazy evaluation). Find a most distant
100
         * chain by hashing substrings within the match area. We cannot use
101
         * s->prev[strstart+1,...] immediately because those strings are not
102
         * yet inserted into the hash table.
103
         */
104
#  ifdef LONGEST_MATCH_SLOW_ROLL
105
        uint32_t hash;
106
        uint32_t pos;
107
108
        hash = update_hash_roll(0, scan[1]);
109
        hash = update_hash_roll(hash, scan[2]);
110
111
        for (uint32_t i = 3; i <= best_len; i++) {
112
            hash = update_hash_roll(hash, scan[i]);
113
            pos = head[hash];
114
            if (UNLIKELY(pos < cur_match)) {
115
                match_offset = i - 2;
116
                cur_match = pos;
117
            }
118
        }
119
#  else /* 4-byte Knuth hash, fresh lookup per offset */
120
1.93M
        for (uint32_t i = 1; i + (WANT_MIN_MATCH - 1) <= best_len; i++) {
121
1.70M
            uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan + i));
122
1.70M
            uint32_t hash;
123
1.70M
            UPDATE_HASH_KNUTH(hash, val);
124
1.70M
            uint32_t pos = head[hash];
125
1.70M
            if (pos < cur_match) {
126
276k
                match_offset = i;
127
276k
                cur_match = pos;
128
276k
            }
129
1.70M
        }
130
230k
#  endif
131
132
        /* Update offset-dependent variables */
133
230k
        limit = limit_base+match_offset;
134
230k
        if (UNLIKELY(cur_match <= limit))
135
122k
            return best_len;
136
107k
        mbase_start -= match_offset;
137
107k
        mbase_end -= match_offset;
138
107k
    }
139
4.93M
#endif
140
4.93M
    Assert((unsigned long)strstart <= s->window_size - MIN_LOOKAHEAD, "need lookahead");
141
5.51M
    for (;;) {
142
5.51M
        if (UNLIKELY(cur_match >= strstart))
143
0
            break;
144
145
        /* Skip to next match if the match length cannot increase or if the match length is
146
         * less than 2. Note that the checks below for insufficient lookahead only occur
147
         * occasionally for performance reasons.
148
         * Therefore uninitialized memory will be accessed and conditional jumps will be made
149
         * that depend on those values. However the length of the match is limited to the
150
         * lookahead, so the output of deflate is not affected by the uninitialized values.
151
         */
152
5.51M
        uint32_t len;
153
5.51M
        if (best_len < sizeof(uint64_t)) {
154
5.08M
            uint64_t cand_start = zng_memread_8(mbase_start + cur_match);
155
5.08M
            if (scan_start != cand_start) {
156
                /* Peel the first candidate out of the loop. A full 8-byte match falls straight
157
                 * through to compare256, and single-candidate chains (barely-compressible data)
158
                 * run with no loop overhead. */
159
4.97M
                uint64_t first_mask = zng_first_bytes_mask64(best_len + 1);
160
4.97M
                uint64_t diff = scan_start ^ cand_start;
161
                /* A candidate beats best_len only when its first best_len+1 bytes match, i.e.
162
                 * those bytes of the XOR are zero. The masked test rejects without running ctz. */
163
4.97M
                if (UNLIKELY((diff & first_mask) == 0)) {
164
254k
                    len = zng_first_diff_byte64(diff);
165
254k
                    goto short_match_accept;
166
254k
                }
167
4.71M
                if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
168
3.50M
                    return best_len;
169
1.21M
                cand_start = zng_memread_8(mbase_start + cur_match);
170
1.21M
                if (scan_start != cand_start) {
171
                    /* Walk the remaining candidates with the chain advance kept inline. */
172
5.13M
                    for (;;) {
173
5.13M
                        diff = scan_start ^ cand_start;
174
5.13M
                        if (UNLIKELY((diff & first_mask) == 0)) {
175
33.4k
                            len = zng_first_diff_byte64(diff);
176
33.4k
                            goto short_match_accept;
177
33.4k
                        }
178
5.09M
                        if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
179
1.16M
                            return best_len;
180
3.92M
                        cand_start = zng_memread_8(mbase_start + cur_match);
181
3.92M
                        if (scan_start == cand_start)
182
10.2k
                            break;
183
3.92M
                    }
184
1.21M
                }
185
1.21M
            }
186
            /* All 8 bytes match, fallthrough to compare256 for the tail. */
187
5.08M
        } else {
188
            /* Pre-filter the candidate on the start and end sentinels before compare256 using
189
             * simple 8-byte comparison since best_len >= 8. */
190
7.86M
            for (;;) {
191
                /* First check the end of the candidate at best_len+1 due to the higher
192
                 * likelihood of a mismatch. */
193
7.86M
                if (zng_memcmp_8(mbase_end+cur_match, &scan_end) == 0 &&
194
445k
                    zng_memcmp_8(mbase_start+cur_match, &scan_start) == 0)
195
312k
                    break;
196
7.54M
                GOTO_NEXT_CHAIN;
197
7.54M
            }
198
430k
        }
199
441k
        len = COMPARE256(scan+2, mbase_start+cur_match+2) + 2;
200
441k
        Assert(scan+len <= window+(unsigned)(s->window_size-1), "wild scan");
201
202
441k
        if (len > best_len)
203
924k
short_match_accept:
204
924k
        {
205
924k
            uint32_t match_start = cur_match - match_offset;
206
924k
            s->match_start = match_start;
207
208
            /* Do not look for better matches if the current match reaches
209
             * or exceeds the end of the input.
210
             */
211
924k
            if (UNLIKELY(len >= lookahead))
212
205
                return lookahead;
213
606k
            if (UNLIKELY(len >= nice_match))
214
28.7k
                return len;
215
216
577k
            best_len = len;
217
218
577k
            offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
219
220
577k
            scan_end = zng_memread_8(scan+offset);
221
222
577k
#ifdef LONGEST_MATCH_SLOW
223
            /* Look for a better string offset */
224
577k
            if (UNLIKELY(offset_search && len > STD_MIN_MATCH && match_start + len < strstart)) {
225
40.7k
                const unsigned char *scan_endstr;
226
40.7k
                uint32_t hash;
227
40.7k
                uint32_t pos, next_pos;
228
229
                /* Go back to offset 0 */
230
40.7k
                cur_match -= match_offset;
231
40.7k
                match_offset = 0;
232
40.7k
                next_pos = cur_match;
233
234
                /* Walk prev[] for positions inside the match. The bound keeps
235
                 * the hash window within the match, so we follow chains that
236
                 * share bytes with the current match, not data past its end.
237
                 */
238
#  ifdef LONGEST_MATCH_SLOW_ROLL
239
                for (uint32_t i = 0; i <= len - STD_MIN_MATCH; i++) {
240
#  else /* 4-byte Knuth hash needs len - 4 bound */
241
770k
                for (uint32_t i = 0; i <= len - WANT_MIN_MATCH; i++) {
242
740k
#  endif
243
740k
                    pos = prev[(cur_match + i) & wmask];
244
740k
                    if (UNLIKELY(pos < next_pos)) {
245
                        /* Hash chain is more distant, use it */
246
60.0k
                        if (UNLIKELY(pos <= limit_base + i))
247
10.5k
                            return best_len;
248
49.4k
                        next_pos = pos;
249
49.4k
                        match_offset = i;
250
49.4k
                    }
251
740k
                }
252
                /* Switch cur_match to next_pos chain */
253
30.1k
                cur_match = next_pos;
254
255
                /* Try hash head at a window covering the tail of the current
256
                 * match to find chains that extend farther back. The window
257
                 * width differs per variant: 3 bytes for rolling, 4 for Knuth.
258
                 */
259
#  ifdef LONGEST_MATCH_SLOW_ROLL
260
                scan_endstr = scan + len - (STD_MIN_MATCH-1);
261
262
                hash = update_hash_roll(0, scan_endstr[0]);
263
                hash = update_hash_roll(hash, scan_endstr[1]);
264
                hash = update_hash_roll(hash, scan_endstr[2]);
265
266
                pos = head[hash];
267
                if (UNLIKELY(pos < cur_match)) {
268
                    match_offset = len - (STD_MIN_MATCH-1);
269
                    if (pos <= limit_base + match_offset)
270
                        return best_len;
271
                    cur_match = pos;
272
                }
273
#  else
274
30.1k
                scan_endstr = scan + len - WANT_MIN_MATCH;
275
30.1k
                uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan_endstr));
276
30.1k
                UPDATE_HASH_KNUTH(hash, val);
277
278
30.1k
                pos = head[hash];
279
30.1k
                if (pos < cur_match) {
280
0
                    match_offset = len - WANT_MIN_MATCH;
281
0
                    if (pos <= limit_base + match_offset)
282
0
                        return best_len;
283
0
                    cur_match = pos;
284
0
                }
285
30.1k
#  endif
286
287
                /* Update offset-dependent variables */
288
30.1k
                limit = limit_base+match_offset;
289
30.1k
                mbase_start = window-match_offset;
290
30.1k
                mbase_end = (mbase_start+offset);
291
30.1k
                continue;
292
30.1k
            }
293
536k
#endif
294
536k
            mbase_end = (mbase_start+offset);
295
536k
        }
296
#ifndef LONGEST_MATCH_SLOW
297
        else if (UNLIKELY(early_exit)) {
298
            /* The probability of finding a match later if we here is pretty low, so for
299
             * performance it's best to outright stop here for the lower compression levels
300
             */
301
            break;
302
        }
303
#endif
304
660k
        GOTO_NEXT_CHAIN;
305
660k
    }
306
0
    return best_len;
307
4.93M
}
longest_match_slow_roll_avx2
Line
Count
Source
28
34.7M
Z_INTERNAL uint32_t LONGEST_MATCH(deflate_state *const s, uint32_t cur_match) {
29
34.7M
    const unsigned wmask = W_MASK(s);
30
34.7M
    unsigned int strstart = s->strstart;
31
34.7M
    const unsigned char *window = s->window;
32
34.7M
    const Pos *prev = s->prev;
33
34.7M
#ifdef LONGEST_MATCH_SLOW
34
34.7M
    const Pos *head = s->head;
35
34.7M
#endif
36
34.7M
    const unsigned char *scan;
37
34.7M
    const unsigned char *mbase_start = window;
38
34.7M
    const unsigned char *mbase_end;
39
34.7M
    uint32_t limit;
40
34.7M
#ifdef LONGEST_MATCH_SLOW
41
34.7M
    uint32_t limit_base;
42
34.7M
#endif
43
#ifndef LONGEST_MATCH_SLOW
44
    int32_t early_exit;
45
#endif
46
34.7M
    uint32_t chain_length = s->max_chain_length;
47
34.7M
    uint32_t nice_match = (uint32_t)s->nice_match;
48
34.7M
    uint32_t best_len, offset;
49
34.7M
    uint32_t lookahead = s->lookahead;
50
34.7M
    uint32_t match_offset = 0;
51
34.7M
    uint64_t scan_start;
52
34.7M
    uint64_t scan_end;
53
54
    /* The code is optimized for STD_MAX_MATCH-2 multiple of 16. */
55
34.7M
    Assert(STD_MAX_MATCH == 258, "Code too clever");
56
57
34.7M
    best_len = s->prev_length ? s->prev_length : STD_MIN_MATCH-1;
58
34.7M
    if (UNLIKELY(best_len >= lookahead))
59
434
        return lookahead;
60
34.7M
#ifdef LONGEST_MATCH_SLOW
61
34.7M
#  ifdef LONGEST_MATCH_SLOW_ROLL
62
    /* Rolling-hash variant always runs the post-match offset search; the
63
     * compiler folds the constant away below.
64
     */
65
34.7M
    const int offset_search = 1;
66
#  else
67
    /* The post-match offset search only pays off when we entered with a
68
     * prior best_len from lazy evaluation, so gate on it at function entry.
69
     */
70
    const int offset_search = (best_len >= STD_MIN_MATCH);
71
#  endif
72
34.7M
#endif
73
74
    /* Calculate read offset which should only extend an extra byte to find the
75
     * next best match length. When best_len is shorter than the read width, we
76
     * diff the mismatched bytes instead.
77
     */
78
34.7M
    offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
79
80
34.7M
    scan = window + strstart;
81
34.7M
    scan_start = zng_memread_8(scan);
82
34.7M
    scan_end = zng_memread_8(scan+offset);
83
34.7M
    mbase_end = (mbase_start+offset);
84
85
    /* Do not waste too much time if we already have a good match */
86
34.7M
    if (UNLIKELY(best_len >= s->good_match))
87
70.8k
        chain_length >>= 2;
88
89
    /* Stop when cur_match becomes <= limit. To simplify the code,
90
     * we prevent matches with the string of window index 0
91
     */
92
34.7M
    limit = strstart > MAX_DIST(s) ? (strstart - MAX_DIST(s)) : 0;
93
#ifndef LONGEST_MATCH_SLOW
94
    early_exit = s->level < EARLY_EXIT_TRIGGER_LEVEL;
95
#endif
96
34.7M
#ifdef LONGEST_MATCH_SLOW
97
34.7M
    limit_base = limit;
98
34.7M
    if (best_len >= STD_MIN_MATCH) {
99
        /* We're continuing search (lazy evaluation). Find a most distant
100
         * chain by hashing substrings within the match area. We cannot use
101
         * s->prev[strstart+1,...] immediately because those strings are not
102
         * yet inserted into the hash table.
103
         */
104
6.44M
#  ifdef LONGEST_MATCH_SLOW_ROLL
105
6.44M
        uint32_t hash;
106
6.44M
        uint32_t pos;
107
108
6.44M
        hash = update_hash_roll(0, scan[1]);
109
6.44M
        hash = update_hash_roll(hash, scan[2]);
110
111
25.8M
        for (uint32_t i = 3; i <= best_len; i++) {
112
19.3M
            hash = update_hash_roll(hash, scan[i]);
113
19.3M
            pos = head[hash];
114
19.3M
            if (UNLIKELY(pos < cur_match)) {
115
5.24M
                match_offset = i - 2;
116
5.24M
                cur_match = pos;
117
5.24M
            }
118
19.3M
        }
119
#  else /* 4-byte Knuth hash, fresh lookup per offset */
120
        for (uint32_t i = 1; i + (WANT_MIN_MATCH - 1) <= best_len; i++) {
121
            uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan + i));
122
            uint32_t hash;
123
            UPDATE_HASH_KNUTH(hash, val);
124
            uint32_t pos = head[hash];
125
            if (pos < cur_match) {
126
                match_offset = i;
127
                cur_match = pos;
128
            }
129
        }
130
#  endif
131
132
        /* Update offset-dependent variables */
133
6.44M
        limit = limit_base+match_offset;
134
6.44M
        if (UNLIKELY(cur_match <= limit))
135
1.47M
            return best_len;
136
4.96M
        mbase_start -= match_offset;
137
4.96M
        mbase_end -= match_offset;
138
4.96M
    }
139
33.2M
#endif
140
33.2M
    Assert((unsigned long)strstart <= s->window_size - MIN_LOOKAHEAD, "need lookahead");
141
44.0M
    for (;;) {
142
44.0M
        if (UNLIKELY(cur_match >= strstart))
143
0
            break;
144
145
        /* Skip to next match if the match length cannot increase or if the match length is
146
         * less than 2. Note that the checks below for insufficient lookahead only occur
147
         * occasionally for performance reasons.
148
         * Therefore uninitialized memory will be accessed and conditional jumps will be made
149
         * that depend on those values. However the length of the match is limited to the
150
         * lookahead, so the output of deflate is not affected by the uninitialized values.
151
         */
152
44.0M
        uint32_t len;
153
44.0M
        if (best_len < sizeof(uint64_t)) {
154
40.7M
            uint64_t cand_start = zng_memread_8(mbase_start + cur_match);
155
40.7M
            if (scan_start != cand_start) {
156
                /* Peel the first candidate out of the loop. A full 8-byte match falls straight
157
                 * through to compare256, and single-candidate chains (barely-compressible data)
158
                 * run with no loop overhead. */
159
40.4M
                uint64_t first_mask = zng_first_bytes_mask64(best_len + 1);
160
40.4M
                uint64_t diff = scan_start ^ cand_start;
161
                /* A candidate beats best_len only when its first best_len+1 bytes match, i.e.
162
                 * those bytes of the XOR are zero. The masked test rejects without running ctz. */
163
40.4M
                if (UNLIKELY((diff & first_mask) == 0)) {
164
6.87M
                    len = zng_first_diff_byte64(diff);
165
6.87M
                    goto short_match_accept;
166
6.87M
                }
167
33.6M
                if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
168
13.1M
                    return best_len;
169
20.4M
                cand_start = zng_memread_8(mbase_start + cur_match);
170
20.4M
                if (scan_start != cand_start) {
171
                    /* Walk the remaining candidates with the chain advance kept inline. */
172
137M
                    for (;;) {
173
137M
                        diff = scan_start ^ cand_start;
174
137M
                        if (UNLIKELY((diff & first_mask) == 0)) {
175
3.27M
                            len = zng_first_diff_byte64(diff);
176
3.27M
                            goto short_match_accept;
177
3.27M
                        }
178
134M
                        if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
179
17.1M
                            return best_len;
180
117M
                        cand_start = zng_memread_8(mbase_start + cur_match);
181
117M
                        if (scan_start == cand_start)
182
54.6k
                            break;
183
117M
                    }
184
20.4M
                }
185
20.4M
            }
186
            /* All 8 bytes match, fallthrough to compare256 for the tail. */
187
40.7M
        } else {
188
            /* Pre-filter the candidate on the start and end sentinels before compare256 using
189
             * simple 8-byte comparison since best_len >= 8. */
190
44.9M
            for (;;) {
191
                /* First check the end of the candidate at best_len+1 due to the higher
192
                 * likelihood of a mismatch. */
193
44.9M
                if (zng_memcmp_8(mbase_end+cur_match, &scan_end) == 0 &&
194
4.36M
                    zng_memcmp_8(mbase_start+cur_match, &scan_start) == 0)
195
2.96M
                    break;
196
41.9M
                GOTO_NEXT_CHAIN;
197
41.9M
            }
198
3.25M
        }
199
3.29M
        len = COMPARE256(scan+2, mbase_start+cur_match+2) + 2;
200
3.29M
        Assert(scan+len <= window+(unsigned)(s->window_size-1), "wild scan");
201
202
3.29M
        if (len > best_len)
203
12.7M
short_match_accept:
204
12.7M
        {
205
12.7M
            uint32_t match_start = cur_match - match_offset;
206
12.7M
            s->match_start = match_start;
207
208
            /* Do not look for better matches if the current match reaches
209
             * or exceeds the end of the input.
210
             */
211
12.7M
            if (UNLIKELY(len >= lookahead))
212
440
                return lookahead;
213
11.4M
            if (UNLIKELY(len >= nice_match))
214
60.8k
                return len;
215
216
11.3M
            best_len = len;
217
218
11.3M
            offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
219
220
11.3M
            scan_end = zng_memread_8(scan+offset);
221
222
11.3M
#ifdef LONGEST_MATCH_SLOW
223
            /* Look for a better string offset */
224
11.3M
            if (UNLIKELY(offset_search && len > STD_MIN_MATCH && match_start + len < strstart)) {
225
5.10M
                const unsigned char *scan_endstr;
226
5.10M
                uint32_t hash;
227
5.10M
                uint32_t pos, next_pos;
228
229
                /* Go back to offset 0 */
230
5.10M
                cur_match -= match_offset;
231
5.10M
                match_offset = 0;
232
5.10M
                next_pos = cur_match;
233
234
                /* Walk prev[] for positions inside the match. The bound keeps
235
                 * the hash window within the match, so we follow chains that
236
                 * share bytes with the current match, not data past its end.
237
                 */
238
5.10M
#  ifdef LONGEST_MATCH_SLOW_ROLL
239
80.2M
                for (uint32_t i = 0; i <= len - STD_MIN_MATCH; i++) {
240
#  else /* 4-byte Knuth hash needs len - 4 bound */
241
                for (uint32_t i = 0; i <= len - WANT_MIN_MATCH; i++) {
242
#  endif
243
76.5M
                    pos = prev[(cur_match + i) & wmask];
244
76.5M
                    if (UNLIKELY(pos < next_pos)) {
245
                        /* Hash chain is more distant, use it */
246
6.86M
                        if (UNLIKELY(pos <= limit_base + i))
247
1.37M
                            return best_len;
248
5.49M
                        next_pos = pos;
249
5.49M
                        match_offset = i;
250
5.49M
                    }
251
76.5M
                }
252
                /* Switch cur_match to next_pos chain */
253
3.73M
                cur_match = next_pos;
254
255
                /* Try hash head at a window covering the tail of the current
256
                 * match to find chains that extend farther back. The window
257
                 * width differs per variant: 3 bytes for rolling, 4 for Knuth.
258
                 */
259
3.73M
#  ifdef LONGEST_MATCH_SLOW_ROLL
260
3.73M
                scan_endstr = scan + len - (STD_MIN_MATCH-1);
261
262
3.73M
                hash = update_hash_roll(0, scan_endstr[0]);
263
3.73M
                hash = update_hash_roll(hash, scan_endstr[1]);
264
3.73M
                hash = update_hash_roll(hash, scan_endstr[2]);
265
266
3.73M
                pos = head[hash];
267
3.73M
                if (UNLIKELY(pos < cur_match)) {
268
800k
                    match_offset = len - (STD_MIN_MATCH-1);
269
800k
                    if (pos <= limit_base + match_offset)
270
370k
                        return best_len;
271
430k
                    cur_match = pos;
272
430k
                }
273
#  else
274
                scan_endstr = scan + len - WANT_MIN_MATCH;
275
                uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan_endstr));
276
                UPDATE_HASH_KNUTH(hash, val);
277
278
                pos = head[hash];
279
                if (pos < cur_match) {
280
                    match_offset = len - WANT_MIN_MATCH;
281
                    if (pos <= limit_base + match_offset)
282
                        return best_len;
283
                    cur_match = pos;
284
                }
285
#  endif
286
287
                /* Update offset-dependent variables */
288
3.36M
                limit = limit_base+match_offset;
289
3.36M
                mbase_start = window-match_offset;
290
3.36M
                mbase_end = (mbase_start+offset);
291
3.36M
                continue;
292
3.73M
            }
293
6.26M
#endif
294
6.26M
            mbase_end = (mbase_start+offset);
295
6.26M
        }
296
#ifndef LONGEST_MATCH_SLOW
297
        else if (UNLIKELY(early_exit)) {
298
            /* The probability of finding a match later if we here is pretty low, so for
299
             * performance it's best to outright stop here for the lower compression levels
300
             */
301
            break;
302
        }
303
#endif
304
8.28M
        GOTO_NEXT_CHAIN;
305
8.28M
    }
306
0
    return best_len;
307
33.2M
}
Unexecuted instantiation: longest_match_avx512
Unexecuted instantiation: longest_match_slow_knuth_avx512
Unexecuted instantiation: longest_match_slow_roll_avx512
308
309
#undef LONGEST_MATCH_SLOW
310
#undef LONGEST_MATCH_SLOW_ROLL
311
#undef LONGEST_MATCH