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