Coverage Report

Created: 2026-10-17 07:13

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
230M
#define EARLY_EXIT_TRIGGER_LEVEL 5
14
15
#define GOTO_NEXT_CHAIN \
16
179M
    if (--chain_length && (cur_match = prev[cur_match & wmask]) > limit) \
17
167M
        continue; \
18
11.9M
    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
316M
Z_INTERNAL uint32_t LONGEST_MATCH(deflate_state *const s, uint32_t cur_match) {
29
316M
    const unsigned wmask = W_MASK(s);
30
316M
    unsigned int strstart = s->strstart;
31
316M
    const unsigned char *window = s->window;
32
316M
    const Pos *prev = s->prev;
33
#ifdef LONGEST_MATCH_SLOW
34
    const Pos *head = s->head;
35
#endif
36
316M
    const unsigned char *scan;
37
316M
    const unsigned char *mbase_start = window;
38
316M
    const unsigned char *mbase_end;
39
316M
    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
316M
    uint32_t chain_length = s->max_chain_length;
47
316M
    uint32_t nice_match = (uint32_t)s->nice_match;
48
316M
    uint32_t best_len, offset;
49
316M
    uint32_t lookahead = s->lookahead;
50
316M
    uint32_t match_offset = 0;
51
316M
    uint64_t scan_start;
52
316M
    uint64_t scan_end;
53
54
    /* The code is optimized for STD_MAX_MATCH-2 multiple of 16. */
55
316M
    Assert(STD_MAX_MATCH == 258, "Code too clever");
56
57
316M
    best_len = s->prev_length ? s->prev_length : STD_MIN_MATCH-1;
58
316M
    if (UNLIKELY(best_len >= lookahead))
59
8.33k
        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
32.8M
    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
53.2M
    const int offset_search = (best_len >= STD_MIN_MATCH);
71
#  endif
72
53.2M
#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
316M
    offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
79
80
230M
    scan = window + strstart;
81
230M
    scan_start = zng_memread_8(scan);
82
230M
    scan_end = zng_memread_8(scan+offset);
83
230M
    mbase_end = (mbase_start+offset);
84
85
    /* Do not waste too much time if we already have a good match */
86
316M
    if (UNLIKELY(best_len >= s->good_match))
87
823k
        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
316M
    limit = strstart > MAX_DIST(s) ? (strstart - MAX_DIST(s)) : 0;
93
#ifndef LONGEST_MATCH_SLOW
94
230M
    early_exit = s->level < EARLY_EXIT_TRIGGER_LEVEL;
95
#endif
96
#ifdef LONGEST_MATCH_SLOW
97
    limit_base = limit;
98
86.1M
    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
51.9M
        for (uint32_t i = 3; i <= best_len; i++) {
112
39.7M
            hash = update_hash_roll(hash, scan[i]);
113
39.7M
            pos = head[hash];
114
39.7M
            if (UNLIKELY(pos < cur_match)) {
115
11.7M
                match_offset = i - 2;
116
11.7M
                cur_match = pos;
117
11.7M
            }
118
39.7M
        }
119
#  else /* 4-byte Knuth hash, fresh lookup per offset */
120
20.7M
        for (uint32_t i = 1; i + (WANT_MIN_MATCH - 1) <= best_len; i++) {
121
16.0M
            uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan + i));
122
16.0M
            uint32_t hash;
123
16.0M
            UPDATE_HASH_KNUTH(hash, val);
124
16.0M
            uint32_t pos = head[hash];
125
16.0M
            if (pos < cur_match) {
126
5.13M
                match_offset = i;
127
5.13M
                cur_match = pos;
128
5.13M
            }
129
16.0M
        }
130
#  endif
131
132
        /* Update offset-dependent variables */
133
16.9M
        limit = limit_base+match_offset;
134
16.9M
        if (UNLIKELY(cur_match <= limit))
135
7.54M
            return best_len;
136
9.41M
        mbase_start -= match_offset;
137
9.41M
        mbase_end -= match_offset;
138
9.41M
    }
139
78.5M
#endif
140
78.5M
    Assert((unsigned long)strstart <= s->window_size - MIN_LOOKAHEAD, "need lookahead");
141
349M
    for (;;) {
142
349M
        if (UNLIKELY(cur_match >= strstart))
143
78
            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
349M
        uint32_t len;
153
349M
        if (best_len < sizeof(uint64_t)) {
154
333M
            uint64_t cand_start = zng_memread_8(mbase_start + cur_match);
155
333M
            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
326M
                uint64_t first_mask = zng_first_bytes_mask64(best_len + 1);
160
326M
                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
326M
                if (UNLIKELY((diff & first_mask) == 0)) {
164
28.5M
                    len = zng_first_diff_byte64(diff);
165
28.5M
                    goto short_match_accept;
166
28.5M
                }
167
297M
                if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
168
210M
                    return best_len;
169
86.5M
                cand_start = zng_memread_8(mbase_start + cur_match);
170
86.5M
                if (scan_start != cand_start) {
171
                    /* Walk the remaining candidates with the chain advance kept inline. */
172
318M
                    for (;;) {
173
318M
                        diff = scan_start ^ cand_start;
174
318M
                        if (UNLIKELY((diff & first_mask) == 0)) {
175
4.59M
                            len = zng_first_diff_byte64(diff);
176
4.59M
                            goto short_match_accept;
177
4.59M
                        }
178
314M
                        if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
179
81.1M
                            return best_len;
180
232M
                        cand_start = zng_memread_8(mbase_start + cur_match);
181
232M
                        if (scan_start == cand_start)
182
600k
                            break;
183
232M
                    }
184
86.2M
                }
185
86.5M
            }
186
            /* All 8 bytes match, fallthrough to compare256 for the tail. */
187
333M
        } 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
148M
            for (;;) {
191
                /* First check the end of the candidate at best_len+1 due to the higher
192
                 * likelihood of a mismatch. */
193
148M
                if (zng_memcmp_8(mbase_end+cur_match, &scan_end) == 0 &&
194
15.0M
                    zng_memcmp_8(mbase_start+cur_match, &scan_start) == 0)
195
12.7M
                    break;
196
135M
                GOTO_NEXT_CHAIN;
197
135M
            }
198
16.0M
        }
199
20.7M
        len = COMPARE256(scan+2, mbase_start+cur_match+2) + 2;
200
20.7M
        Assert(scan+len <= window+(unsigned)(s->window_size-1), "wild scan");
201
202
20.7M
        if (len > best_len)
203
67.8M
short_match_accept:
204
67.8M
        {
205
67.8M
            uint32_t match_start = cur_match - match_offset;
206
67.8M
            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
67.8M
            if (UNLIKELY(len >= lookahead))
212
15.3k
                return lookahead;
213
50.4M
            if (UNLIKELY(len >= nice_match))
214
3.80M
                return len;
215
216
46.6M
            best_len = len;
217
218
46.6M
            offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
219
220
46.6M
            scan_end = zng_memread_8(scan+offset);
221
222
#ifdef LONGEST_MATCH_SLOW
223
            /* Look for a better string offset */
224
17.8M
            if (UNLIKELY(offset_search && len > STD_MIN_MATCH && match_start + len < strstart)) {
225
6.11M
                const unsigned char *scan_endstr;
226
6.11M
                uint32_t hash;
227
6.11M
                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
146M
                for (uint32_t i = 0; i <= len - STD_MIN_MATCH; i++) {
240
#  else /* 4-byte Knuth hash needs len - 4 bound */
241
14.0M
                for (uint32_t i = 0; i <= len - WANT_MIN_MATCH; i++) {
242
13.5M
#  endif
243
13.5M
                    pos = prev[(cur_match + i) & wmask];
244
155M
                    if (UNLIKELY(pos < next_pos)) {
245
                        /* Hash chain is more distant, use it */
246
7.98M
                        if (UNLIKELY(pos <= limit_base + i))
247
1.13M
                            return best_len;
248
6.85M
                        next_pos = pos;
249
6.85M
                        match_offset = i;
250
6.85M
                    }
251
13.5M
                }
252
                /* Switch cur_match to next_pos chain */
253
4.97M
                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
4.45M
                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
4.45M
                if (UNLIKELY(pos < cur_match)) {
268
769k
                    match_offset = len - (STD_MIN_MATCH-1);
269
769k
                    if (pos <= limit_base + match_offset)
270
313k
                        return best_len;
271
456k
                    cur_match = pos;
272
456k
                }
273
#  else
274
527k
                scan_endstr = scan + len - WANT_MIN_MATCH;
275
527k
                uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan_endstr));
276
527k
                UPDATE_HASH_KNUTH(hash, val);
277
278
                pos = head[hash];
279
527k
                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
527k
#  endif
286
287
                /* Update offset-dependent variables */
288
4.66M
                limit = limit_base+match_offset;
289
4.66M
                mbase_start = window-match_offset;
290
4.66M
                mbase_end = (mbase_start+offset);
291
4.66M
                continue;
292
4.97M
            }
293
11.7M
#endif
294
11.7M
            mbase_end = (mbase_start+offset);
295
11.7M
        }
296
#ifndef LONGEST_MATCH_SLOW
297
673k
        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
1.33k
            break;
302
1.33k
        }
303
29.4M
#endif
304
43.9M
        GOTO_NEXT_CHAIN;
305
43.9M
    }
306
1.41k
    return best_len;
307
78.5M
}
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
230M
Z_INTERNAL uint32_t LONGEST_MATCH(deflate_state *const s, uint32_t cur_match) {
29
230M
    const unsigned wmask = W_MASK(s);
30
230M
    unsigned int strstart = s->strstart;
31
230M
    const unsigned char *window = s->window;
32
230M
    const Pos *prev = s->prev;
33
#ifdef LONGEST_MATCH_SLOW
34
    const Pos *head = s->head;
35
#endif
36
230M
    const unsigned char *scan;
37
230M
    const unsigned char *mbase_start = window;
38
230M
    const unsigned char *mbase_end;
39
230M
    uint32_t limit;
40
#ifdef LONGEST_MATCH_SLOW
41
    uint32_t limit_base;
42
#endif
43
230M
#ifndef LONGEST_MATCH_SLOW
44
230M
    int32_t early_exit;
45
230M
#endif
46
230M
    uint32_t chain_length = s->max_chain_length;
47
230M
    uint32_t nice_match = (uint32_t)s->nice_match;
48
230M
    uint32_t best_len, offset;
49
230M
    uint32_t lookahead = s->lookahead;
50
230M
    uint32_t match_offset = 0;
51
230M
    uint64_t scan_start;
52
230M
    uint64_t scan_end;
53
54
    /* The code is optimized for STD_MAX_MATCH-2 multiple of 16. */
55
230M
    Assert(STD_MAX_MATCH == 258, "Code too clever");
56
57
230M
    best_len = s->prev_length ? s->prev_length : STD_MIN_MATCH-1;
58
230M
    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
230M
    offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
79
80
230M
    scan = window + strstart;
81
230M
    scan_start = zng_memread_8(scan);
82
230M
    scan_end = zng_memread_8(scan+offset);
83
230M
    mbase_end = (mbase_start+offset);
84
85
    /* Do not waste too much time if we already have a good match */
86
230M
    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
230M
    limit = strstart > MAX_DIST(s) ? (strstart - MAX_DIST(s)) : 0;
93
230M
#ifndef LONGEST_MATCH_SLOW
94
230M
    early_exit = s->level < EARLY_EXIT_TRIGGER_LEVEL;
95
230M
#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
230M
    Assert((unsigned long)strstart <= s->window_size - MIN_LOOKAHEAD, "need lookahead");
141
253M
    for (;;) {
142
253M
        if (UNLIKELY(cur_match >= strstart))
143
78
            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
253M
        uint32_t len;
153
253M
        if (best_len < sizeof(uint64_t)) {
154
246M
            uint64_t cand_start = zng_memread_8(mbase_start + cur_match);
155
246M
            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
242M
                uint64_t first_mask = zng_first_bytes_mask64(best_len + 1);
160
242M
                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
242M
                if (UNLIKELY((diff & first_mask) == 0)) {
164
19.1M
                    len = zng_first_diff_byte64(diff);
165
19.1M
                    goto short_match_accept;
166
19.1M
                }
167
222M
                if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
168
165M
                    return best_len;
169
57.1M
                cand_start = zng_memread_8(mbase_start + cur_match);
170
57.1M
                if (scan_start != cand_start) {
171
                    /* Walk the remaining candidates with the chain advance kept inline. */
172
142M
                    for (;;) {
173
142M
                        diff = scan_start ^ cand_start;
174
142M
                        if (UNLIKELY((diff & first_mask) == 0)) {
175
1.77M
                            len = zng_first_diff_byte64(diff);
176
1.77M
                            goto short_match_accept;
177
1.77M
                        }
178
140M
                        if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
179
54.8M
                            return best_len;
180
85.7M
                        cand_start = zng_memread_8(mbase_start + cur_match);
181
85.7M
                        if (scan_start == cand_start)
182
358k
                            break;
183
85.7M
                    }
184
57.0M
                }
185
57.1M
            }
186
            /* All 8 bytes match, fallthrough to compare256 for the tail. */
187
246M
        } 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
49.0M
            for (;;) {
191
                /* First check the end of the candidate at best_len+1 due to the higher
192
                 * likelihood of a mismatch. */
193
49.0M
                if (zng_memcmp_8(mbase_end+cur_match, &scan_end) == 0 &&
194
5.97M
                    zng_memcmp_8(mbase_start+cur_match, &scan_start) == 0)
195
5.65M
                    break;
196
43.3M
                GOTO_NEXT_CHAIN;
197
43.3M
            }
198
7.51M
        }
199
10.2M
        len = COMPARE256(scan+2, mbase_start+cur_match+2) + 2;
200
10.2M
        Assert(scan+len <= window+(unsigned)(s->window_size-1), "wild scan");
201
202
10.2M
        if (len > best_len)
203
39.9M
short_match_accept:
204
39.9M
        {
205
39.9M
            uint32_t match_start = cur_match - match_offset;
206
39.9M
            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
39.9M
            if (UNLIKELY(len >= lookahead))
212
7.83k
                return lookahead;
213
30.4M
            if (UNLIKELY(len >= nice_match))
214
1.66M
                return len;
215
216
28.7M
            best_len = len;
217
218
28.7M
            offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
219
220
28.7M
            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
28.7M
            mbase_end = (mbase_start+offset);
295
28.7M
        }
296
673k
#ifndef LONGEST_MATCH_SLOW
297
673k
        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
1.33k
            break;
302
1.33k
        }
303
29.4M
#endif
304
29.4M
        GOTO_NEXT_CHAIN;
305
29.4M
    }
306
1.41k
    return best_len;
307
230M
}
longest_match_slow_knuth_avx2
Line
Count
Source
28
53.2M
Z_INTERNAL uint32_t LONGEST_MATCH(deflate_state *const s, uint32_t cur_match) {
29
53.2M
    const unsigned wmask = W_MASK(s);
30
53.2M
    unsigned int strstart = s->strstart;
31
53.2M
    const unsigned char *window = s->window;
32
53.2M
    const Pos *prev = s->prev;
33
53.2M
#ifdef LONGEST_MATCH_SLOW
34
53.2M
    const Pos *head = s->head;
35
53.2M
#endif
36
53.2M
    const unsigned char *scan;
37
53.2M
    const unsigned char *mbase_start = window;
38
53.2M
    const unsigned char *mbase_end;
39
53.2M
    uint32_t limit;
40
53.2M
#ifdef LONGEST_MATCH_SLOW
41
53.2M
    uint32_t limit_base;
42
53.2M
#endif
43
#ifndef LONGEST_MATCH_SLOW
44
    int32_t early_exit;
45
#endif
46
53.2M
    uint32_t chain_length = s->max_chain_length;
47
53.2M
    uint32_t nice_match = (uint32_t)s->nice_match;
48
53.2M
    uint32_t best_len, offset;
49
53.2M
    uint32_t lookahead = s->lookahead;
50
53.2M
    uint32_t match_offset = 0;
51
53.2M
    uint64_t scan_start;
52
53.2M
    uint64_t scan_end;
53
54
    /* The code is optimized for STD_MAX_MATCH-2 multiple of 16. */
55
53.2M
    Assert(STD_MAX_MATCH == 258, "Code too clever");
56
57
53.2M
    best_len = s->prev_length ? s->prev_length : STD_MIN_MATCH-1;
58
53.2M
    if (UNLIKELY(best_len >= lookahead))
59
3.16k
        return lookahead;
60
53.2M
#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
53.2M
    const int offset_search = (best_len >= STD_MIN_MATCH);
71
53.2M
#  endif
72
53.2M
#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
53.2M
    offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
79
80
53.2M
    scan = window + strstart;
81
53.2M
    scan_start = zng_memread_8(scan);
82
53.2M
    scan_end = zng_memread_8(scan+offset);
83
53.2M
    mbase_end = (mbase_start+offset);
84
85
    /* Do not waste too much time if we already have a good match */
86
53.2M
    if (UNLIKELY(best_len >= s->good_match))
87
749k
        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
53.2M
    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
53.2M
#ifdef LONGEST_MATCH_SLOW
97
53.2M
    limit_base = limit;
98
53.2M
    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
20.7M
        for (uint32_t i = 1; i + (WANT_MIN_MATCH - 1) <= best_len; i++) {
121
16.0M
            uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan + i));
122
16.0M
            uint32_t hash;
123
16.0M
            UPDATE_HASH_KNUTH(hash, val);
124
16.0M
            uint32_t pos = head[hash];
125
16.0M
            if (pos < cur_match) {
126
5.13M
                match_offset = i;
127
5.13M
                cur_match = pos;
128
5.13M
            }
129
16.0M
        }
130
4.69M
#  endif
131
132
        /* Update offset-dependent variables */
133
4.69M
        limit = limit_base+match_offset;
134
4.69M
        if (UNLIKELY(cur_match <= limit))
135
2.88M
            return best_len;
136
1.80M
        mbase_start -= match_offset;
137
1.80M
        mbase_end -= match_offset;
138
1.80M
    }
139
50.3M
#endif
140
50.3M
    Assert((unsigned long)strstart <= s->window_size - MIN_LOOKAHEAD, "need lookahead");
141
58.1M
    for (;;) {
142
58.1M
        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
58.1M
        uint32_t len;
153
58.1M
        if (best_len < sizeof(uint64_t)) {
154
53.5M
            uint64_t cand_start = zng_memread_8(mbase_start + cur_match);
155
53.5M
            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
52.4M
                uint64_t first_mask = zng_first_bytes_mask64(best_len + 1);
160
52.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
52.4M
                if (UNLIKELY((diff & first_mask) == 0)) {
164
4.29M
                    len = zng_first_diff_byte64(diff);
165
4.29M
                    goto short_match_accept;
166
4.29M
                }
167
48.1M
                if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
168
34.6M
                    return best_len;
169
13.5M
                cand_start = zng_memread_8(mbase_start + cur_match);
170
13.5M
                if (scan_start != cand_start) {
171
                    /* Walk the remaining candidates with the chain advance kept inline. */
172
55.1M
                    for (;;) {
173
55.1M
                        diff = scan_start ^ cand_start;
174
55.1M
                        if (UNLIKELY((diff & first_mask) == 0)) {
175
507k
                            len = zng_first_diff_byte64(diff);
176
507k
                            goto short_match_accept;
177
507k
                        }
178
54.6M
                        if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
179
12.7M
                            return best_len;
180
41.8M
                        cand_start = zng_memread_8(mbase_start + cur_match);
181
41.8M
                        if (scan_start == cand_start)
182
170k
                            break;
183
41.8M
                    }
184
13.4M
                }
185
13.5M
            }
186
            /* All 8 bytes match, fallthrough to compare256 for the tail. */
187
53.5M
        } 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
49.0M
            for (;;) {
191
                /* First check the end of the candidate at best_len+1 due to the higher
192
                 * likelihood of a mismatch. */
193
49.0M
                if (zng_memcmp_8(mbase_end+cur_match, &scan_end) == 0 &&
194
4.07M
                    zng_memcmp_8(mbase_start+cur_match, &scan_start) == 0)
195
3.50M
                    break;
196
45.5M
                GOTO_NEXT_CHAIN;
197
45.5M
            }
198
4.57M
        }
199
4.84M
        len = COMPARE256(scan+2, mbase_start+cur_match+2) + 2;
200
4.84M
        Assert(scan+len <= window+(unsigned)(s->window_size-1), "wild scan");
201
202
4.84M
        if (len > best_len)
203
12.8M
short_match_accept:
204
12.8M
        {
205
12.8M
            uint32_t match_start = cur_match - match_offset;
206
12.8M
            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.8M
            if (UNLIKELY(len >= lookahead))
212
3.14k
                return lookahead;
213
8.82M
            if (UNLIKELY(len >= nice_match))
214
363k
                return len;
215
216
8.46M
            best_len = len;
217
218
8.46M
            offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
219
220
8.46M
            scan_end = zng_memread_8(scan+offset);
221
222
8.46M
#ifdef LONGEST_MATCH_SLOW
223
            /* Look for a better string offset */
224
8.46M
            if (UNLIKELY(offset_search && len > STD_MIN_MATCH && match_start + len < strstart)) {
225
667k
                const unsigned char *scan_endstr;
226
667k
                uint32_t hash;
227
667k
                uint32_t pos, next_pos;
228
229
                /* Go back to offset 0 */
230
667k
                cur_match -= match_offset;
231
667k
                match_offset = 0;
232
667k
                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
14.0M
                for (uint32_t i = 0; i <= len - WANT_MIN_MATCH; i++) {
242
13.5M
#  endif
243
13.5M
                    pos = prev[(cur_match + i) & wmask];
244
13.5M
                    if (UNLIKELY(pos < next_pos)) {
245
                        /* Hash chain is more distant, use it */
246
949k
                        if (UNLIKELY(pos <= limit_base + i))
247
139k
                            return best_len;
248
809k
                        next_pos = pos;
249
809k
                        match_offset = i;
250
809k
                    }
251
13.5M
                }
252
                /* Switch cur_match to next_pos chain */
253
527k
                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
527k
                scan_endstr = scan + len - WANT_MIN_MATCH;
275
527k
                uint32_t val = Z_U32_FROM_LE(zng_memread_4(scan_endstr));
276
527k
                UPDATE_HASH_KNUTH(hash, val);
277
278
527k
                pos = head[hash];
279
527k
                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
527k
#  endif
286
287
                /* Update offset-dependent variables */
288
527k
                limit = limit_base+match_offset;
289
527k
                mbase_start = window-match_offset;
290
527k
                mbase_end = (mbase_start+offset);
291
527k
                continue;
292
527k
            }
293
7.79M
#endif
294
7.79M
            mbase_end = (mbase_start+offset);
295
7.79M
        }
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.61M
        GOTO_NEXT_CHAIN;
305
8.61M
    }
306
0
    return best_len;
307
50.3M
}
longest_match_slow_roll_avx2
Line
Count
Source
28
32.8M
Z_INTERNAL uint32_t LONGEST_MATCH(deflate_state *const s, uint32_t cur_match) {
29
32.8M
    const unsigned wmask = W_MASK(s);
30
32.8M
    unsigned int strstart = s->strstart;
31
32.8M
    const unsigned char *window = s->window;
32
32.8M
    const Pos *prev = s->prev;
33
32.8M
#ifdef LONGEST_MATCH_SLOW
34
32.8M
    const Pos *head = s->head;
35
32.8M
#endif
36
32.8M
    const unsigned char *scan;
37
32.8M
    const unsigned char *mbase_start = window;
38
32.8M
    const unsigned char *mbase_end;
39
32.8M
    uint32_t limit;
40
32.8M
#ifdef LONGEST_MATCH_SLOW
41
32.8M
    uint32_t limit_base;
42
32.8M
#endif
43
#ifndef LONGEST_MATCH_SLOW
44
    int32_t early_exit;
45
#endif
46
32.8M
    uint32_t chain_length = s->max_chain_length;
47
32.8M
    uint32_t nice_match = (uint32_t)s->nice_match;
48
32.8M
    uint32_t best_len, offset;
49
32.8M
    uint32_t lookahead = s->lookahead;
50
32.8M
    uint32_t match_offset = 0;
51
32.8M
    uint64_t scan_start;
52
32.8M
    uint64_t scan_end;
53
54
    /* The code is optimized for STD_MAX_MATCH-2 multiple of 16. */
55
32.8M
    Assert(STD_MAX_MATCH == 258, "Code too clever");
56
57
32.8M
    best_len = s->prev_length ? s->prev_length : STD_MIN_MATCH-1;
58
32.8M
    if (UNLIKELY(best_len >= lookahead))
59
5.16k
        return lookahead;
60
32.8M
#ifdef LONGEST_MATCH_SLOW
61
32.8M
#  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
32.8M
    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
32.8M
#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
32.8M
    offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
79
80
32.8M
    scan = window + strstart;
81
32.8M
    scan_start = zng_memread_8(scan);
82
32.8M
    scan_end = zng_memread_8(scan+offset);
83
32.8M
    mbase_end = (mbase_start+offset);
84
85
    /* Do not waste too much time if we already have a good match */
86
32.8M
    if (UNLIKELY(best_len >= s->good_match))
87
74.2k
        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
32.8M
    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
32.8M
#ifdef LONGEST_MATCH_SLOW
97
32.8M
    limit_base = limit;
98
32.8M
    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
12.2M
#  ifdef LONGEST_MATCH_SLOW_ROLL
105
12.2M
        uint32_t hash;
106
12.2M
        uint32_t pos;
107
108
12.2M
        hash = update_hash_roll(0, scan[1]);
109
12.2M
        hash = update_hash_roll(hash, scan[2]);
110
111
51.9M
        for (uint32_t i = 3; i <= best_len; i++) {
112
39.7M
            hash = update_hash_roll(hash, scan[i]);
113
39.7M
            pos = head[hash];
114
39.7M
            if (UNLIKELY(pos < cur_match)) {
115
11.7M
                match_offset = i - 2;
116
11.7M
                cur_match = pos;
117
11.7M
            }
118
39.7M
        }
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
12.2M
        limit = limit_base+match_offset;
134
12.2M
        if (UNLIKELY(cur_match <= limit))
135
4.65M
            return best_len;
136
7.60M
        mbase_start -= match_offset;
137
7.60M
        mbase_end -= match_offset;
138
7.60M
    }
139
28.1M
#endif
140
28.1M
    Assert((unsigned long)strstart <= s->window_size - MIN_LOOKAHEAD, "need lookahead");
141
37.5M
    for (;;) {
142
37.5M
        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
37.5M
        uint32_t len;
153
37.5M
        if (best_len < sizeof(uint64_t)) {
154
33.5M
            uint64_t cand_start = zng_memread_8(mbase_start + cur_match);
155
33.5M
            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
31.5M
                uint64_t first_mask = zng_first_bytes_mask64(best_len + 1);
160
31.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
31.5M
                if (UNLIKELY((diff & first_mask) == 0)) {
164
5.06M
                    len = zng_first_diff_byte64(diff);
165
5.06M
                    goto short_match_accept;
166
5.06M
                }
167
26.4M
                if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
168
10.6M
                    return best_len;
169
15.8M
                cand_start = zng_memread_8(mbase_start + cur_match);
170
15.8M
                if (scan_start != cand_start) {
171
                    /* Walk the remaining candidates with the chain advance kept inline. */
172
121M
                    for (;;) {
173
121M
                        diff = scan_start ^ cand_start;
174
121M
                        if (UNLIKELY((diff & first_mask) == 0)) {
175
2.30M
                            len = zng_first_diff_byte64(diff);
176
2.30M
                            goto short_match_accept;
177
2.30M
                        }
178
118M
                        if (--chain_length == 0 || (cur_match = prev[cur_match & wmask]) <= limit)
179
13.4M
                            return best_len;
180
105M
                        cand_start = zng_memread_8(mbase_start + cur_match);
181
105M
                        if (scan_start == cand_start)
182
70.8k
                            break;
183
105M
                    }
184
15.8M
                }
185
15.8M
            }
186
            /* All 8 bytes match, fallthrough to compare256 for the tail. */
187
33.5M
        } 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
50.0M
            for (;;) {
191
                /* First check the end of the candidate at best_len+1 due to the higher
192
                 * likelihood of a mismatch. */
193
50.0M
                if (zng_memcmp_8(mbase_end+cur_match, &scan_end) == 0 &&
194
4.96M
                    zng_memcmp_8(mbase_start+cur_match, &scan_start) == 0)
195
3.61M
                    break;
196
46.4M
                GOTO_NEXT_CHAIN;
197
46.4M
            }
198
3.99M
        }
199
5.73M
        len = COMPARE256(scan+2, mbase_start+cur_match+2) + 2;
200
5.73M
        Assert(scan+len <= window+(unsigned)(s->window_size-1), "wild scan");
201
202
5.73M
        if (len > best_len)
203
15.0M
short_match_accept:
204
15.0M
        {
205
15.0M
            uint32_t match_start = cur_match - match_offset;
206
15.0M
            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
15.0M
            if (UNLIKELY(len >= lookahead))
212
4.38k
                return lookahead;
213
11.2M
            if (UNLIKELY(len >= nice_match))
214
1.77M
                return len;
215
216
9.42M
            best_len = len;
217
218
9.42M
            offset = best_len >= sizeof(uint64_t) ? best_len - 7 : 0;
219
220
9.42M
            scan_end = zng_memread_8(scan+offset);
221
222
9.42M
#ifdef LONGEST_MATCH_SLOW
223
            /* Look for a better string offset */
224
9.42M
            if (UNLIKELY(offset_search && len > STD_MIN_MATCH && match_start + len < strstart)) {
225
5.44M
                const unsigned char *scan_endstr;
226
5.44M
                uint32_t hash;
227
5.44M
                uint32_t pos, next_pos;
228
229
                /* Go back to offset 0 */
230
5.44M
                cur_match -= match_offset;
231
5.44M
                match_offset = 0;
232
5.44M
                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.44M
#  ifdef LONGEST_MATCH_SLOW_ROLL
239
146M
                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
141M
                    pos = prev[(cur_match + i) & wmask];
244
141M
                    if (UNLIKELY(pos < next_pos)) {
245
                        /* Hash chain is more distant, use it */
246
7.03M
                        if (UNLIKELY(pos <= limit_base + i))
247
994k
                            return best_len;
248
6.04M
                        next_pos = pos;
249
6.04M
                        match_offset = i;
250
6.04M
                    }
251
141M
                }
252
                /* Switch cur_match to next_pos chain */
253
4.45M
                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
4.45M
#  ifdef LONGEST_MATCH_SLOW_ROLL
260
4.45M
                scan_endstr = scan + len - (STD_MIN_MATCH-1);
261
262
4.45M
                hash = update_hash_roll(0, scan_endstr[0]);
263
4.45M
                hash = update_hash_roll(hash, scan_endstr[1]);
264
4.45M
                hash = update_hash_roll(hash, scan_endstr[2]);
265
266
4.45M
                pos = head[hash];
267
4.45M
                if (UNLIKELY(pos < cur_match)) {
268
769k
                    match_offset = len - (STD_MIN_MATCH-1);
269
769k
                    if (pos <= limit_base + match_offset)
270
313k
                        return best_len;
271
456k
                    cur_match = pos;
272
456k
                }
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
4.13M
                limit = limit_base+match_offset;
289
4.13M
                mbase_start = window-match_offset;
290
4.13M
                mbase_end = (mbase_start+offset);
291
4.13M
                continue;
292
4.45M
            }
293
3.98M
#endif
294
3.98M
            mbase_end = (mbase_start+offset);
295
3.98M
        }
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
5.87M
        GOTO_NEXT_CHAIN;
305
5.87M
    }
306
0
    return best_len;
307
28.1M
}
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