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