1 | // SPDX-License-Identifier: Zlib |
2 | |
3 | #include "../zlib_deflate/defutil.h" |
4 | #include "dfltcc_util.h" |
5 | #include "dfltcc_deflate.h" |
6 | #include <asm/setup.h> |
7 | #include <linux/export.h> |
8 | #include <linux/zutil.h> |
9 | |
10 | #define GET_DFLTCC_DEFLATE_STATE(state) ((struct dfltcc_deflate_state *)GET_DFLTCC_STATE(state)) |
11 | |
12 | /* |
13 | * Compress. |
14 | */ |
15 | int dfltcc_can_deflate( |
16 | z_streamp strm |
17 | ) |
18 | { |
19 | deflate_state *state = (deflate_state *)strm->state; |
20 | struct dfltcc_deflate_state *dfltcc_state = GET_DFLTCC_DEFLATE_STATE(state); |
21 | |
22 | /* Check for kernel dfltcc command line parameter */ |
23 | if (zlib_dfltcc_support == ZLIB_DFLTCC_DISABLED || |
24 | zlib_dfltcc_support == ZLIB_DFLTCC_INFLATE_ONLY) |
25 | return 0; |
26 | |
27 | /* Unsupported compression settings */ |
28 | if (!dfltcc_are_params_ok(level: state->level, window_bits: state->w_bits, strategy: state->strategy, |
29 | level_mask: dfltcc_state->level_mask)) |
30 | return 0; |
31 | |
32 | /* Unsupported hardware */ |
33 | if (!is_bit_set(bits: dfltcc_state->common.af.fns, DFLTCC_GDHT) || |
34 | !is_bit_set(bits: dfltcc_state->common.af.fns, DFLTCC_CMPR) || |
35 | !is_bit_set(bits: dfltcc_state->common.af.fmts, DFLTCC_FMT0)) |
36 | return 0; |
37 | |
38 | return 1; |
39 | } |
40 | EXPORT_SYMBOL(dfltcc_can_deflate); |
41 | |
42 | void dfltcc_reset_deflate_state(z_streamp strm) { |
43 | deflate_state *state = (deflate_state *)strm->state; |
44 | struct dfltcc_deflate_state *dfltcc_state = GET_DFLTCC_DEFLATE_STATE(state); |
45 | |
46 | dfltcc_reset_state(dfltcc_state: &dfltcc_state->common); |
47 | |
48 | /* Initialize tuning parameters */ |
49 | if (zlib_dfltcc_support == ZLIB_DFLTCC_FULL_DEBUG) |
50 | dfltcc_state->level_mask = DFLTCC_LEVEL_MASK_DEBUG; |
51 | else |
52 | dfltcc_state->level_mask = DFLTCC_LEVEL_MASK; |
53 | dfltcc_state->block_size = DFLTCC_BLOCK_SIZE; |
54 | dfltcc_state->block_threshold = DFLTCC_FIRST_FHT_BLOCK_SIZE; |
55 | dfltcc_state->dht_threshold = DFLTCC_DHT_MIN_SAMPLE_SIZE; |
56 | } |
57 | EXPORT_SYMBOL(dfltcc_reset_deflate_state); |
58 | |
59 | static void dfltcc_gdht( |
60 | z_streamp strm |
61 | ) |
62 | { |
63 | deflate_state *state = (deflate_state *)strm->state; |
64 | struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param; |
65 | size_t avail_in = strm->avail_in; |
66 | |
67 | dfltcc(DFLTCC_GDHT, |
68 | param, NULL, NULL, |
69 | op2: &strm->next_in, len2: &avail_in, NULL); |
70 | } |
71 | |
72 | static dfltcc_cc dfltcc_cmpr( |
73 | z_streamp strm |
74 | ) |
75 | { |
76 | deflate_state *state = (deflate_state *)strm->state; |
77 | struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param; |
78 | size_t avail_in = strm->avail_in; |
79 | size_t avail_out = strm->avail_out; |
80 | dfltcc_cc cc; |
81 | |
82 | cc = dfltcc(DFLTCC_CMPR | HBT_CIRCULAR, |
83 | param, op1: &strm->next_out, len1: &avail_out, |
84 | op2: &strm->next_in, len2: &avail_in, hist: state->window); |
85 | strm->total_in += (strm->avail_in - avail_in); |
86 | strm->total_out += (strm->avail_out - avail_out); |
87 | strm->avail_in = avail_in; |
88 | strm->avail_out = avail_out; |
89 | return cc; |
90 | } |
91 | |
92 | static void send_eobs( |
93 | z_streamp strm, |
94 | const struct dfltcc_param_v0 *param |
95 | ) |
96 | { |
97 | deflate_state *state = (deflate_state *)strm->state; |
98 | |
99 | zlib_tr_send_bits( |
100 | s: state, |
101 | value: bi_reverse(code: param->eobs >> (15 - param->eobl), len: param->eobl), |
102 | length: param->eobl); |
103 | flush_pending(strm); |
104 | if (state->pending != 0) { |
105 | /* The remaining data is located in pending_out[0:pending]. If someone |
106 | * calls put_byte() - this might happen in deflate() - the byte will be |
107 | * placed into pending_buf[pending], which is incorrect. Move the |
108 | * remaining data to the beginning of pending_buf so that put_byte() is |
109 | * usable again. |
110 | */ |
111 | memmove(state->pending_buf, state->pending_out, state->pending); |
112 | state->pending_out = state->pending_buf; |
113 | } |
114 | #ifdef ZLIB_DEBUG |
115 | state->compressed_len += param->eobl; |
116 | #endif |
117 | } |
118 | |
119 | int dfltcc_deflate( |
120 | z_streamp strm, |
121 | int flush, |
122 | block_state *result |
123 | ) |
124 | { |
125 | deflate_state *state = (deflate_state *)strm->state; |
126 | struct dfltcc_deflate_state *dfltcc_state = GET_DFLTCC_DEFLATE_STATE(state); |
127 | struct dfltcc_param_v0 *param = &dfltcc_state->common.param; |
128 | uInt masked_avail_in; |
129 | dfltcc_cc cc; |
130 | int need_empty_block; |
131 | int soft_bcc; |
132 | int no_flush; |
133 | |
134 | if (!dfltcc_can_deflate(strm)) { |
135 | /* Clear history. */ |
136 | if (flush == Z_FULL_FLUSH) |
137 | param->hl = 0; |
138 | return 0; |
139 | } |
140 | |
141 | again: |
142 | masked_avail_in = 0; |
143 | soft_bcc = 0; |
144 | no_flush = flush == Z_NO_FLUSH; |
145 | |
146 | /* No input data. Return, except when Continuation Flag is set, which means |
147 | * that DFLTCC has buffered some output in the parameter block and needs to |
148 | * be called again in order to flush it. |
149 | */ |
150 | if (strm->avail_in == 0 && !param->cf) { |
151 | /* A block is still open, and the hardware does not support closing |
152 | * blocks without adding data. Thus, close it manually. |
153 | */ |
154 | if (!no_flush && param->bcf) { |
155 | send_eobs(strm, param); |
156 | param->bcf = 0; |
157 | } |
158 | /* Let one of deflate_* functions write a trailing empty block. */ |
159 | if (flush == Z_FINISH) |
160 | return 0; |
161 | /* Clear history. */ |
162 | if (flush == Z_FULL_FLUSH) |
163 | param->hl = 0; |
164 | /* Trigger block post-processing if necessary. */ |
165 | *result = no_flush ? need_more : block_done; |
166 | return 1; |
167 | } |
168 | |
169 | /* There is an open non-BFINAL block, we are not going to close it just |
170 | * yet, we have compressed more than DFLTCC_BLOCK_SIZE bytes and we see |
171 | * more than DFLTCC_DHT_MIN_SAMPLE_SIZE bytes. Open a new block with a new |
172 | * DHT in order to adapt to a possibly changed input data distribution. |
173 | */ |
174 | if (param->bcf && no_flush && |
175 | strm->total_in > dfltcc_state->block_threshold && |
176 | strm->avail_in >= dfltcc_state->dht_threshold) { |
177 | if (param->cf) { |
178 | /* We need to flush the DFLTCC buffer before writing the |
179 | * End-of-block Symbol. Mask the input data and proceed as usual. |
180 | */ |
181 | masked_avail_in += strm->avail_in; |
182 | strm->avail_in = 0; |
183 | no_flush = 0; |
184 | } else { |
185 | /* DFLTCC buffer is empty, so we can manually write the |
186 | * End-of-block Symbol right away. |
187 | */ |
188 | send_eobs(strm, param); |
189 | param->bcf = 0; |
190 | dfltcc_state->block_threshold = |
191 | strm->total_in + dfltcc_state->block_size; |
192 | } |
193 | } |
194 | |
195 | /* No space for compressed data. If we proceed, dfltcc_cmpr() will return |
196 | * DFLTCC_CC_OP1_TOO_SHORT without buffering header bits, but we will still |
197 | * set BCF=1, which is wrong. Avoid complications and return early. |
198 | */ |
199 | if (strm->avail_out == 0) { |
200 | *result = need_more; |
201 | return 1; |
202 | } |
203 | |
204 | /* The caller gave us too much data. Pass only one block worth of |
205 | * uncompressed data to DFLTCC and mask the rest, so that on the next |
206 | * iteration we start a new block. |
207 | */ |
208 | if (no_flush && strm->avail_in > dfltcc_state->block_size) { |
209 | masked_avail_in += (strm->avail_in - dfltcc_state->block_size); |
210 | strm->avail_in = dfltcc_state->block_size; |
211 | } |
212 | |
213 | /* When we have an open non-BFINAL deflate block and caller indicates that |
214 | * the stream is ending, we need to close an open deflate block and open a |
215 | * BFINAL one. |
216 | */ |
217 | need_empty_block = flush == Z_FINISH && param->bcf && !param->bhf; |
218 | |
219 | /* Translate stream to parameter block */ |
220 | param->cvt = CVT_ADLER32; |
221 | if (!no_flush) |
222 | /* We need to close a block. Always do this in software - when there is |
223 | * no input data, the hardware will not hohor BCC. */ |
224 | soft_bcc = 1; |
225 | if (flush == Z_FINISH && !param->bcf) |
226 | /* We are about to open a BFINAL block, set Block Header Final bit |
227 | * until the stream ends. |
228 | */ |
229 | param->bhf = 1; |
230 | /* DFLTCC-CMPR will write to next_out, so make sure that buffers with |
231 | * higher precedence are empty. |
232 | */ |
233 | Assert(state->pending == 0, "There must be no pending bytes" ); |
234 | Assert(state->bi_valid < 8, "There must be less than 8 pending bits" ); |
235 | param->sbb = (unsigned int)state->bi_valid; |
236 | if (param->sbb > 0) |
237 | *strm->next_out = (Byte)state->bi_buf; |
238 | /* Honor history and check value */ |
239 | param->nt = 0; |
240 | param->cv = strm->adler; |
241 | |
242 | /* When opening a block, choose a Huffman-Table Type */ |
243 | if (!param->bcf) { |
244 | if (strm->total_in == 0 && dfltcc_state->block_threshold > 0) { |
245 | param->htt = HTT_FIXED; |
246 | } |
247 | else { |
248 | param->htt = HTT_DYNAMIC; |
249 | dfltcc_gdht(strm); |
250 | } |
251 | } |
252 | |
253 | /* Deflate */ |
254 | do { |
255 | cc = dfltcc_cmpr(strm); |
256 | if (strm->avail_in < 4096 && masked_avail_in > 0) |
257 | /* We are about to call DFLTCC with a small input buffer, which is |
258 | * inefficient. Since there is masked data, there will be at least |
259 | * one more DFLTCC call, so skip the current one and make the next |
260 | * one handle more data. |
261 | */ |
262 | break; |
263 | } while (cc == DFLTCC_CC_AGAIN); |
264 | |
265 | /* Translate parameter block to stream */ |
266 | strm->msg = oesc_msg(buf: dfltcc_state->common.msg, oesc: param->oesc); |
267 | state->bi_valid = param->sbb; |
268 | if (state->bi_valid == 0) |
269 | state->bi_buf = 0; /* Avoid accessing next_out */ |
270 | else |
271 | state->bi_buf = *strm->next_out & ((1 << state->bi_valid) - 1); |
272 | strm->adler = param->cv; |
273 | |
274 | /* Unmask the input data */ |
275 | strm->avail_in += masked_avail_in; |
276 | masked_avail_in = 0; |
277 | |
278 | /* If we encounter an error, it means there is a bug in DFLTCC call */ |
279 | Assert(cc != DFLTCC_CC_OP2_CORRUPT || param->oesc == 0, "BUG" ); |
280 | |
281 | /* Update Block-Continuation Flag. It will be used to check whether to call |
282 | * GDHT the next time. |
283 | */ |
284 | if (cc == DFLTCC_CC_OK) { |
285 | if (soft_bcc) { |
286 | send_eobs(strm, param); |
287 | param->bcf = 0; |
288 | dfltcc_state->block_threshold = |
289 | strm->total_in + dfltcc_state->block_size; |
290 | } else |
291 | param->bcf = 1; |
292 | if (flush == Z_FINISH) { |
293 | if (need_empty_block) |
294 | /* Make the current deflate() call also close the stream */ |
295 | return 0; |
296 | else { |
297 | bi_windup(s: state); |
298 | *result = finish_done; |
299 | } |
300 | } else { |
301 | if (flush == Z_FULL_FLUSH) |
302 | param->hl = 0; /* Clear history */ |
303 | *result = flush == Z_NO_FLUSH ? need_more : block_done; |
304 | } |
305 | } else { |
306 | param->bcf = 1; |
307 | *result = need_more; |
308 | } |
309 | if (strm->avail_in != 0 && strm->avail_out != 0) |
310 | goto again; /* deflate() must use all input or all output */ |
311 | return 1; |
312 | } |
313 | EXPORT_SYMBOL(dfltcc_deflate); |
314 | |