Commit | Line | Data |
---|---|---|
aa5b395b MZ |
1 | // SPDX-License-Identifier: Zlib |
2 | ||
3 | #include "../zlib_deflate/defutil.h" | |
4 | #include "dfltcc_util.h" | |
9fec9f8e | 5 | #include "dfltcc_deflate.h" |
c65e6815 | 6 | #include <asm/setup.h> |
605cc30d | 7 | #include <linux/export.h> |
aa5b395b MZ |
8 | #include <linux/zutil.h> |
9 | ||
9fec9f8e MZ |
10 | #define GET_DFLTCC_DEFLATE_STATE(state) ((struct dfltcc_deflate_state *)GET_DFLTCC_STATE(state)) |
11 | ||
aa5b395b MZ |
12 | /* |
13 | * Compress. | |
14 | */ | |
15 | int dfltcc_can_deflate( | |
16 | z_streamp strm | |
17 | ) | |
18 | { | |
19 | deflate_state *state = (deflate_state *)strm->state; | |
9fec9f8e | 20 | struct dfltcc_deflate_state *dfltcc_state = GET_DFLTCC_DEFLATE_STATE(state); |
aa5b395b | 21 | |
c65e6815 MZ |
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 | ||
aa5b395b MZ |
27 | /* Unsupported compression settings */ |
28 | if (!dfltcc_are_params_ok(state->level, state->w_bits, state->strategy, | |
29 | dfltcc_state->level_mask)) | |
30 | return 0; | |
31 | ||
32 | /* Unsupported hardware */ | |
9fec9f8e MZ |
33 | if (!is_bit_set(dfltcc_state->common.af.fns, DFLTCC_GDHT) || |
34 | !is_bit_set(dfltcc_state->common.af.fns, DFLTCC_CMPR) || | |
35 | !is_bit_set(dfltcc_state->common.af.fmts, DFLTCC_FMT0)) | |
aa5b395b MZ |
36 | return 0; |
37 | ||
38 | return 1; | |
39 | } | |
605cc30d | 40 | EXPORT_SYMBOL(dfltcc_can_deflate); |
aa5b395b | 41 | |
9fec9f8e MZ |
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->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 | ||
aa5b395b MZ |
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; | |
e89bd9e7 | 65 | size_t avail_in = strm->avail_in; |
aa5b395b MZ |
66 | |
67 | dfltcc(DFLTCC_GDHT, | |
68 | param, NULL, NULL, | |
69 | &strm->next_in, &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, &strm->next_out, &avail_out, | |
84 | &strm->next_in, &avail_in, 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 | state, | |
101 | bi_reverse(param->eobs >> (15 - param->eobl), param->eobl), | |
102 | 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; | |
9fec9f8e MZ |
126 | struct dfltcc_deflate_state *dfltcc_state = GET_DFLTCC_DEFLATE_STATE(state); |
127 | struct dfltcc_param_v0 *param = &dfltcc_state->common.param; | |
aa5b395b MZ |
128 | uInt masked_avail_in; |
129 | dfltcc_cc cc; | |
130 | int need_empty_block; | |
131 | int soft_bcc; | |
132 | int no_flush; | |
133 | ||
9a549338 MZ |
134 | if (!dfltcc_can_deflate(strm)) { |
135 | /* Clear history. */ | |
136 | if (flush == Z_FULL_FLUSH) | |
137 | param->hl = 0; | |
aa5b395b | 138 | return 0; |
9a549338 | 139 | } |
aa5b395b MZ |
140 | |
141 | again: | |
142 | masked_avail_in = 0; | |
143 | soft_bcc = 0; | |
144 | no_flush = flush == Z_NO_FLUSH; | |
145 | ||
9a549338 MZ |
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. | |
aa5b395b | 149 | */ |
9a549338 MZ |
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) { | |
aa5b395b MZ |
155 | send_eobs(strm, param); |
156 | param->bcf = 0; | |
157 | } | |
9a549338 MZ |
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; | |
0dbae465 MZ |
164 | /* Trigger block post-processing if necessary. */ |
165 | *result = no_flush ? need_more : block_done; | |
aa5b395b MZ |
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; | |
aa5b395b MZ |
192 | } |
193 | } | |
194 | ||
cbf12540 MZ |
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 | ||
aa5b395b MZ |
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 | |
9a549338 | 223 | * no input data, the hardware will not hohor BCC. */ |
aa5b395b MZ |
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; | |
9a549338 MZ |
238 | /* Honor history and check value */ |
239 | param->nt = 0; | |
aa5b395b MZ |
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 */ | |
9fec9f8e | 266 | strm->msg = oesc_msg(dfltcc_state->common.msg, param->oesc); |
aa5b395b MZ |
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(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 | } | |
605cc30d | 313 | EXPORT_SYMBOL(dfltcc_deflate); |