Coverage Report

Created: 2026-11-12 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libdeflate/lib/find_min_cost_path_template.h
Line
Count
Source
1
/*
2
 * find_min_cost_path_template.h - template for deflate_find_min_cost_path()
3
 *
4
 * Copyright 2016 Eric Biggers
5
 * Copyright 2026 Google LLC
6
 *
7
 * Permission is hereby granted, free of charge, to any person
8
 * obtaining a copy of this software and associated documentation
9
 * files (the "Software"), to deal in the Software without
10
 * restriction, including without limitation the rights to use,
11
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
12
 * copies of the Software, and to permit persons to whom the
13
 * Software is furnished to do so, subject to the following
14
 * conditions:
15
 *
16
 * The above copyright notice and this permission notice shall be
17
 * included in all copies or substantial portions of the Software.
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26
 * OTHER DEALINGS IN THE SOFTWARE.
27
 */
28
29
/*
30
 * This file is a template parameterized by FUNCNAME, ATTRIBUTES, and
31
 * deflate_min_packed_cost.  It is included multiple times in the same .c file
32
 * with different "arguments", so it intentionally has no include guard.
33
 *
34
 * It implements the core part of deflate_find_min_cost_path(): finding the
35
 * minimum-cost path over the graph of match/literal choices for a block.
36
 *
37
 * For each position 'i' from 'block_length - 1' to 0, it decides which literal
38
 * or match to choose at position 'i' to reach end-of-block in the cheapest way.
39
 * It saves the choice into optimum_items[i] and the corresponding cost to
40
 * end-of-block into cost_to_end[i].
41
 *
42
 * The main bottleneck is considering matches; there can be many match lengths
43
 * at each position that need to be considered.  To minimize the overhead of
44
 * this step and enable fast SIMD implementations, we pack costs and lengths
45
 * together as follows.  First, we preprocess the bare length costs
46
 * 'c->p.n.costs.length' to create an array 'length_costs', containing:
47
 *
48
 *  (c->p.n.costs.length[len] << 9) | len
49
 *
50
 * That is, its high 23 bits contain the length's cost, and its low 9
51
 * (OPTIMUM_OFFSET_SHIFT in the code) bits contain the length.
52
 *
53
 * Then, in 'cost_to_end[i]' we store 'cost[i]' (the minimum cost to reach
54
 * end-of-block from position 'i') shifted left by 9, using the high 23 bits:
55
 *
56
 *  cost[i] << 9
57
 *
58
 * To consider multiple lengths (with the same offset) at position 'i', we just
59
 * sum the corresponding 'length_costs[len]' and 'cost_to_end[i + len]' entries,
60
 * giving:
61
 *
62
 *  ((c->p.n.costs.length[len] + cost[i + len]) << 9) | len
63
 *
64
 * ... and then take the minimum of those to get:
65
 *
66
 *  (best_cost << 9) | best_len
67
 *
68
 * This vectorizes well and results in a 'u32' that has both the best cost and
69
 * the length that gave that cost, without needing to track them separately.
70
 *
71
 * However, this scheme gives only 23 bits for the cost, which isn't enough for
72
 * the entire block.  (In theory, a block's cost can reach 'MAX_BLOCK_LENGTH *
73
 * MAX_LITERAL_COST', which is just over 2^26 and thus needs 27 bits.)
74
 *
75
 * To solve this, we take advantage of the limited local variation in 'cost':
76
 * over a span of at most DEFLATE_MAX_MATCH_LEN bytes, choosing a literal at
77
 * each byte costs at most MAX_MATCH_SPAN_COST, so for
78
 * 'i < j <= MIN(block_length, i + DEFLATE_MAX_MATCH_LEN)',
79
 * 'cost[i] - cost[j] <= MAX_MATCH_SPAN_COST'.
80
 *
81
 * Therefore, we maintain a base cost 'base_cost' (initially 0) and work with
82
 * costs relative to 'base_cost': 'rel[j] = cost[j] - base_cost'
83
 * ('best_cost_to_end' in the code).  To avoid having to update 'cost_to_end[]'
84
 * when 'base_cost' changes, 'cost_to_end[j]' still stores '(cost[j] << 9) mod
85
 * 2^32', and '-base_cost' is folded into 'length_costs[len]' instead:
86
 *
87
 *  (((c->p.n.costs.length[len] - base_cost) << 9) mod 2^32) | len
88
 *
89
 * Summing 'length_costs[len]' and 'cost_to_end[i + len]' mod 2^32 then gives:
90
 *
91
 *  (((rel[i + len] + c->p.n.costs.length[len]) << 9) mod 2^32) | len
92
 *
93
 * As long as every "live" entry 'j' ('i < j <= i + DEFLATE_MAX_MATCH_LEN')
94
 * satisfies:
95
 *
96
 *  0 <= rel[j] < REBASE_THRESHOLD
97
 *
98
 * ... each candidate's 'rel[j] + c->p.n.costs.length[len]' lies in
99
 * '[0, REBASE_THRESHOLD + MAX_LENGTH_COST)', which fits in 23 bits without
100
 * wrapping around.  Thus the unsigned 32-bit minimum finds the exact minimum
101
 * relative cost, breaking ties in favor of the smallest length.
102
 *
103
 * To maintain '0 <= rel[j] < REBASE_THRESHOLD':
104
 *
105
 *   - Initially, 'base_cost = 0', 'cost_to_end[block_length] = 0', and the
106
 *     sentinel entries past 'block_length' contain 'sentinel << 9' where
107
 *     'MAX_MATCH_SPAN_COST <= sentinel < REBASE_THRESHOLD'.  This makes any
108
 *     match extending to a sentinel have a cost to end-of-block greater than
109
 *     the cost of just choosing literals to end-of-block, so it's never chosen.
110
 *
111
 *   - At each position 'i', 'rel[i]' is computed from nonnegative symbol costs
112
 *     and live 'rel[j] >= 0', so 'rel[i] >= 0'.
113
 *
114
 *   - If 'rel[i] >= REBASE_THRESHOLD', we rebase by setting
115
 *     'base_cost = cost[i] - REBASE_MARGIN' (so the new 'rel[i]' is
116
 *     'REBASE_MARGIN < REBASE_THRESHOLD').  Increasing 'base_cost' only
117
 *     decreases 'rel[j]' for other entries, preserving their upper bound.  And
118
 *     because 'MAX_MATCH_SPAN_COST < REBASE_THRESHOLD <= cost[i] <=
119
 *     (block_length - i) * MAX_LITERAL_COST', position 'i' is more than
120
 *     DEFLATE_MAX_MATCH_LEN bytes before 'block_length', so no sentinel entry
121
 *     is live anymore.  Thus every entry 'j > i' still live after position 'i'
122
 *     satisfies 'cost[j] >= cost[i] - MAX_MATCH_SPAN_COST >= cost[i] -
123
 *     REBASE_MARGIN = base_cost', preserving 'rel[j] >= 0'.
124
 *
125
 * For safety, REBASE_MARGIN and REBASE_THRESHOLD are set to non-tight bounds.
126
 */
127
static ATTRIBUTES u32
128
FUNCNAME(struct libdeflate_compressor *c, const u32 block_length,
129
   const struct lz_match *cache_ptr)
130
0
{
131
0
  u32 best_cost_to_end = 0; /* relative to base_cost */
132
0
  u32 base_cost = 0;
133
0
  u32 length_costs[DEFLATE_MAX_MATCH_LEN + 1 + COST_SIMD_PADDING];
134
0
  size_t i;
135
136
  /*
137
   * Initialize length_costs[] from c->p.n.costs.length.  Disable loop
138
   * unrolling on clang to avoid excessive code size.
139
   */
140
0
#ifdef __clang__
141
0
#  pragma clang loop unroll(disable)
142
0
#endif
143
0
  for (i = DEFLATE_MIN_MATCH_LEN; i <= DEFLATE_MAX_MATCH_LEN; i++)
144
0
    length_costs[i] = (u32)((c->p.n.costs.length[i] <<
145
0
           OPTIMUM_OFFSET_SHIFT) | i);
146
147
  /*
148
   * Initialize padding entries to accommodate SIMD loads.  The value
149
   * doesn't matter, since the SIMD code masks out these lanes.
150
   */
151
0
  for (; i < ARRAY_LEN(length_costs); i++)
152
0
    length_costs[i] = 0;
153
154
  /*
155
   * For each position 'i' from 'block_length - 1' to 0, find the cheapest
156
   * way to reach end-of-block from position 'i', and set cost_to_end[i]
157
   * and optimum_items[i] accordingly.
158
   */
159
0
  i = block_length;
160
0
  do {
161
0
    unsigned num_matches;
162
0
    u32 literal;
163
0
    u32 best_item;
164
165
0
    cache_ptr--;
166
167
0
    num_matches = cache_ptr->length;
168
0
    literal = cache_ptr->offset;
169
170
    /* It's always possible to choose a literal. */
171
0
    best_cost_to_end += c->p.n.costs.literal[literal];
172
0
    best_item = (literal << OPTIMUM_OFFSET_SHIFT) | 1;
173
174
0
    i--;
175
176
    /*
177
     * Also consider matches, if there are any.  They are available
178
     * at cache_ptr - num_matches, sorted in order of strictly
179
     * increasing length.
180
     *
181
     * We consider all possible match lengths that are available,
182
     * including ones generated by truncating a longer match.
183
     * However, we consider only the smallest offset for which each
184
     * length is available, since it's very rare for an offset that
185
     * isn't the smallest to be best.
186
     */
187
0
    if (num_matches) {
188
0
      const struct lz_match *match = cache_ptr - num_matches;
189
0
      size_t len = DEFLATE_MIN_MATCH_LEN;
190
191
0
      do {
192
0
        size_t match_len = match->length;
193
0
        u32 offset = match->offset;
194
0
        u8 offset_slot;
195
0
        u32 packed_best;
196
0
        u32 match_cost; /* relative to base_cost */
197
198
        /*
199
         * Consider matches at 'offset' with lengths
200
         * 'len' through 'match_len'.
201
         */
202
0
        ASSERT(offset >= 1 &&
203
0
               offset <= DEFLATE_MAX_MATCH_OFFSET);
204
0
        ASSERT(len >= DEFLATE_MIN_MATCH_LEN &&
205
0
               len <= match_len &&
206
0
               match_len <= DEFLATE_MAX_MATCH_LEN &&
207
0
               i + match_len + COST_SIMD_PADDING <
208
0
                 ARRAY_LEN(c->p.n.cost_to_end));
209
0
        offset_slot = c->p.n.offset_slot_full[offset];
210
0
        packed_best = deflate_min_packed_cost(
211
0
            length_costs,
212
0
            &c->p.n.cost_to_end[i],
213
0
            len, match_len);
214
0
        match_cost = c->p.n.costs.offset_slot[offset_slot] +
215
0
               (packed_best >> OPTIMUM_OFFSET_SHIFT);
216
0
        if (match_cost < best_cost_to_end) {
217
0
          best_cost_to_end = match_cost;
218
0
          best_item = (packed_best & OPTIMUM_LEN_MASK) |
219
0
                (offset << OPTIMUM_OFFSET_SHIFT);
220
0
        }
221
0
        len = match_len + 1;
222
0
      } while (++match != cache_ptr);
223
0
      cache_ptr -= num_matches;
224
0
    }
225
    /*
226
     * Store the absolute cost shifted left by OPTIMUM_OFFSET_SHIFT.
227
     * This truncates the cost to its low 23 bits, which is safe, as
228
     * explained in this function's comment.
229
     */
230
0
    c->p.n.cost_to_end[i] =
231
0
      (best_cost_to_end + base_cost) << OPTIMUM_OFFSET_SHIFT;
232
0
    c->p.n.optimum_items[i] = best_item;
233
234
    /*
235
     * Increment base_cost if best_cost_to_end has reached
236
     * REBASE_THRESHOLD.  See this function's comment.
237
     */
238
0
    if (unlikely(best_cost_to_end >= REBASE_THRESHOLD)) {
239
0
      u32 delta = best_cost_to_end - REBASE_MARGIN;
240
0
      size_t len;
241
242
0
      best_cost_to_end = REBASE_MARGIN;
243
0
      base_cost += delta;
244
0
      delta <<= OPTIMUM_OFFSET_SHIFT;
245
      /* Disable unrolling on clang to avoid code bloat. */
246
0
#ifdef __clang__
247
0
#  pragma clang loop unroll(disable)
248
0
#endif
249
0
      for (len = DEFLATE_MIN_MATCH_LEN;
250
0
           len <= DEFLATE_MAX_MATCH_LEN; len++)
251
0
        length_costs[len] -= delta;
252
0
    }
253
0
  } while (i != 0);
254
255
  /* Return the absolute cost of the entire block. */
256
0
  return best_cost_to_end + base_cost;
257
0
}
Unexecuted instantiation: deflate_compress.c:deflate_find_min_cost_path_simd
Unexecuted instantiation: deflate_compress.c:deflate_find_min_cost_path_default
258
259
#undef FUNCNAME
260
#undef ATTRIBUTES
261
#undef deflate_min_packed_cost