xref: /linux/lib/zlib_dfltcc/dfltcc_deflate.c (revision e89bd9e7)
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  */
dfltcc_can_deflate(z_streamp strm)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(state->level, state->w_bits, state->strategy,
29                               dfltcc_state->level_mask))
30         return 0;
31 
32     /* Unsupported hardware */
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))
36         return 0;
37 
38     return 1;
39 }
40 EXPORT_SYMBOL(dfltcc_can_deflate);
41 
dfltcc_reset_deflate_state(z_streamp strm)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 
dfltcc_gdht(z_streamp strm)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            &strm->next_in, &avail_in, NULL);
70 }
71 
dfltcc_cmpr(z_streamp strm)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 
send_eobs(z_streamp strm,const struct dfltcc_param_v0 * param)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 
dfltcc_deflate(z_streamp strm,int flush,block_state * result)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(dfltcc_state->common.msg, 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(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