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