Coverage Report

Created: 2026-11-12 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zlib-ng/deflate_quick.c
Line
Count
Source
1
/*
2
 * The deflate_quick deflate strategy, designed to be used when cycles are
3
 * at a premium.
4
 *
5
 * Copyright (C) 2013 Intel Corporation. All rights reserved.
6
 * Authors:
7
 *  Wajdi Feghali   <wajdi.k.feghali@intel.com>
8
 *  Jim Guilford    <james.guilford@intel.com>
9
 *  Vinodh Gopal    <vinodh.gopal@intel.com>
10
 *     Erdinc Ozturk   <erdinc.ozturk@intel.com>
11
 *  Jim Kukunas     <james.t.kukunas@linux.intel.com>
12
 *
13
 * Portions are Copyright (C) 2016 12Sided Technology, LLC.
14
 * Author:
15
 *  Phil Vachon     <pvachon@12sidedtech.com>
16
 *
17
 * For conditions of distribution and use, see copyright notice in zlib.h
18
 */
19
20
#include "zbuild.h"
21
#include "zmemory.h"
22
#include "deflate.h"
23
#include "deflate_p.h"
24
#include "functable.h"
25
#include "trees_emit.h"
26
#include "insert_string_p.h"
27
28
extern const ct_data static_ltree[L_CODES+2];
29
extern const ct_data static_dtree[D_CODES];
30
31
/* Returns non-zero when the output buffer is full after closing the block. */
32
Z_FORCEINLINE static int quick_end_block(deflate_state *s, uint32_t strstart, int last,
33
0
                                         const int static_emit) {
34
0
    if (!static_emit) {
35
        /* Tallied symbols are coded by the flush, with trees built for the block */
36
0
        if (last || s->sym_next) {
37
0
            FLUSH_BLOCK_ONLY(s, s->window, last);
38
0
            return (s->strm->avail_out == 0);
39
0
        }
40
0
        return 0;
41
0
    }
42
0
    if (s->block_open) {
43
0
        zng_tr_emit_end_block(s, static_ltree, last);
44
0
        s->block_open = 0;
45
0
        s->block_start = (int)strstart;
46
0
        PREFIX(flush_pending)(s->strm);
47
0
        return (s->strm->avail_out == 0);
48
0
    }
49
0
    return 0;
50
0
}
51
52
/* Returns non-zero when the output buffer is full before the block could start. */
53
Z_FORCEINLINE static int quick_start_block(deflate_state *s, uint32_t strstart, uint32_t lookahead, int last,
54
0
                                           const int static_emit) {
55
    /* Tallied blocks get their header from the flush */
56
0
    if (!static_emit)
57
0
        return 0;
58
0
    if (UNLIKELY(last && s->block_open == 1)) {
59
        /* Emit end of previous block so the last block can carry the final bit */
60
0
        if (quick_end_block(s, strstart, 0, static_emit))
61
0
            return 1;
62
0
    }
63
    /* Start new block only when we have lookahead data or it is the last block,
64
       so that if no input data is given an empty block will not be written */
65
0
    if (UNLIKELY(s->block_open == 0 && (last || lookahead > 0))) {
66
0
        zng_tr_emit_tree(s, STATIC_TREES, last);
67
0
        s->block_open = 1 + last;
68
0
        s->block_start = (int)strstart;
69
0
    }
70
0
    return 0;
71
0
}
72
73
/* Direct emission writes into the pending buffer, so room is made before each
74
   symbol. Returns non-zero when the output buffer is full. */
75
0
Z_FORCEINLINE static int quick_flush_pending(deflate_state *s, const int static_emit) {
76
    /* Tallied symbols reach the pending buffer only at the block flush */
77
0
    if (!static_emit)
78
0
        return 0;
79
0
    if (UNLIKELY(s->pending + ((BIT_BUF_SIZE + 7) >> 3) >= s->pending_buf_size)) {
80
0
        PREFIX(flush_pending)(s->strm);
81
0
        return (s->strm->avail_out == 0);
82
0
    }
83
0
    return 0;
84
0
}
85
86
/* Returns non-zero when the output buffer is full after a block flush. */
87
Z_FORCEINLINE static int quick_emit_lit(deflate_state *s, uint8_t lit, uint32_t *strstart,
88
0
                                        uint32_t *lookahead, const int static_emit) {
89
0
    if (static_emit) {
90
0
        zng_tr_emit_lit(s, static_ltree, lit);
91
0
        (*strstart)++;
92
0
        (*lookahead)--;
93
0
        return 0;
94
0
    }
95
0
    int bflush = zng_tr_tally_lit(s, lit);
96
0
    (*strstart)++;
97
0
    (*lookahead)--;
98
0
    if (UNLIKELY(bflush)) {
99
0
        s->strstart = *strstart;
100
0
        s->lookahead = *lookahead;
101
0
        return quick_end_block(s, *strstart, 0, static_emit);
102
0
    }
103
0
    return 0;
104
0
}
105
106
/* Returns non-zero when the output buffer is full after a block flush. */
107
Z_FORCEINLINE static int quick_emit_dist(deflate_state *s, uint32_t match_len, uint32_t dist,
108
0
                                         uint32_t *strstart, uint32_t *lookahead, const int static_emit) {
109
0
    if (static_emit) {
110
0
        zng_tr_emit_dist(s, static_ltree, static_dtree, match_len - STD_MIN_MATCH, dist);
111
0
        *lookahead -= match_len;
112
0
        *strstart += match_len;
113
0
        return 0;
114
0
    }
115
0
    int bflush = zng_tr_tally_dist(s, dist, match_len - STD_MIN_MATCH);
116
0
    *lookahead -= match_len;
117
0
    *strstart += match_len;
118
0
    if (UNLIKELY(bflush)) {
119
0
        s->strstart = *strstart;
120
0
        s->lookahead = *lookahead;
121
0
        return quick_end_block(s, *strstart, 0, static_emit);
122
0
    }
123
0
    return 0;
124
0
}
125
126
/* static_emit is a compile-time constant, so each instantiation keeps only its own mode. */
127
0
Z_FORCEINLINE static block_state deflate_quick_impl(deflate_state *s, int flush, const int static_emit) {
128
0
    unsigned char *window;
129
    /* Carrying the scan state in locals keeps it in callee-saved registers
130
       across the compare256 and flush calls, instead of a load and store
131
       pair through the state on every symbol. */
132
0
    uint32_t strstart = s->strstart;
133
0
    uint32_t lookahead = s->lookahead;
134
0
    unsigned last = (flush == Z_FINISH) ? 1 : 0;
135
136
0
    if (quick_start_block(s, strstart, lookahead, last, static_emit))
137
0
        return need_more;
138
139
0
    window = s->window;
140
141
0
    for (;;) {
142
0
        if (quick_flush_pending(s, static_emit)) {
143
0
            s->lookahead = lookahead;
144
0
            s->strstart = strstart;
145
0
            return (last && s->strm->avail_in == 0 && s->bi_valid == 0 && s->block_open == 0) ? finish_started : need_more;
146
0
        }
147
148
0
        if (UNLIKELY(lookahead < MIN_LOOKAHEAD)) {
149
0
            s->lookahead = lookahead;
150
0
            s->strstart = strstart;
151
0
            PREFIX(fill_window)(s);
152
0
            lookahead = s->lookahead;
153
0
            strstart = s->strstart;
154
0
            if (UNLIKELY(lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH))
155
0
                return need_more;
156
0
            if (UNLIKELY(lookahead == 0))
157
0
                break;
158
159
0
            if (UNLIKELY(s->block_open == 0)) {
160
                /* Start new block when we have lookahead data, so that if no
161
                   input data is given an empty block will not be written */
162
0
                quick_start_block(s, strstart, lookahead, last, static_emit);
163
0
            }
164
0
        }
165
166
0
        uint32_t str_val = Z_U32_FROM_LE(zng_memread_4(window + strstart));
167
168
0
        if (LIKELY(lookahead >= WANT_MIN_MATCH)) {
169
0
            uint32_t hash_head = insert_knuth_val_head(s, strstart, str_val);
170
0
            int64_t dist = (int64_t)strstart - hash_head;
171
172
0
            if (dist <= MAX_DIST(s) && dist > 0) {
173
0
                const uint8_t *match_start = window + hash_head;
174
0
                uint32_t match_val = Z_U32_FROM_LE(zng_memread_4(match_start));
175
176
0
                if (str_val == match_val) {
177
0
                    const uint8_t *scan_start = window + strstart;
178
0
                    uint32_t match_len = FUNCTABLE_CALL(compare256)(scan_start+2, match_start+2) + 2;
179
180
0
                    if (match_len >= WANT_MIN_MATCH) {
181
0
                        if (UNLIKELY(match_len > lookahead))
182
0
                            match_len = lookahead;
183
184
0
                        Assert(match_len <= STD_MAX_MATCH, "match too long");
185
0
                        Assert(strstart <= UINT16_MAX, "strstart should fit in uint16_t");
186
0
                        check_match(s, strstart, hash_head, match_len);
187
188
0
                        if (quick_emit_dist(s, match_len, (uint32_t)dist, &strstart, &lookahead, static_emit))
189
0
                            return need_more;
190
191
                        /* A max-length match might continue: keep emitting continuations at the
192
                         * same distance without a hash lookup as long as possible. */
193
0
                        if (UNLIKELY(match_len == STD_MAX_MATCH && lookahead >= MIN_LOOKAHEAD)) {
194
0
                            const uint32_t cont_dist = (uint32_t)dist;
195
0
                            for (;;) {
196
                                /* Direct emission needs room, let the main loop flush pending */
197
0
                                if (static_emit && UNLIKELY(s->pending + ((BIT_BUF_SIZE + 7) >> 3) >= s->pending_buf_size))
198
0
                                    break;
199
200
                                // Check for continuing match
201
0
                                uint32_t cont_len = max_match_continues(window, strstart, cont_dist);
202
0
                                if (UNLIKELY(cont_len < 8))
203
0
                                    break;
204
205
0
                                check_match(s, strstart, strstart - cont_dist, cont_len);
206
207
                                // Emit match
208
0
                                if (quick_emit_dist(s, cont_len, cont_dist, &strstart, &lookahead, static_emit))
209
0
                                    return need_more;
210
211
0
                                if (UNLIKELY(lookahead < MIN_LOOKAHEAD))
212
0
                                    break; /* not enough lookahead, return to the main loop */
213
0
                            }
214
0
                        }
215
216
0
                        continue;
217
0
                    }
218
0
                }
219
0
            }
220
0
        }
221
222
0
        if (quick_emit_lit(s, (uint8_t)str_val, &strstart, &lookahead, static_emit))
223
0
            return need_more;
224
0
    }
225
226
0
    s->lookahead = lookahead;
227
0
    s->strstart = strstart;
228
0
    s->insert = strstart < (STD_MIN_MATCH - 1) ? strstart : (STD_MIN_MATCH - 1);
229
0
    if (UNLIKELY(last)) {
230
0
        if (quick_end_block(s, strstart, 1, static_emit))
231
0
            return finish_started;
232
0
        return finish_done;
233
0
    }
234
235
0
    if (quick_end_block(s, strstart, 0, static_emit))
236
0
        return need_more;
237
0
    return block_done;
238
0
}
239
240
0
Z_INTERNAL block_state deflate_quick(deflate_state *s, int flush) {
241
    /* Z_FIXED path: every symbol goes straight through the static tables. */
242
0
    if (UNLIKELY(s->strategy == Z_FIXED))
243
0
        return deflate_quick_impl(s, flush, 1);
244
    /* Default path: symbols are tallied so the flush builds per-block trees. */
245
0
    return deflate_quick_impl(s, flush, 0);
246
0
}