/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 |