lib/zlib: add s390 hardware support for kernel zlib_inflate
[linux-2.6-microblaze.git] / lib / zlib_dfltcc / dfltcc_inflate.c
1 // SPDX-License-Identifier: Zlib
2
3 #include "../zlib_inflate/inflate.h"
4 #include "dfltcc_util.h"
5 #include "dfltcc.h"
6 #include <linux/zutil.h>
7
8 /*
9  * Expand.
10  */
11 int dfltcc_can_inflate(
12     z_streamp strm
13 )
14 {
15     struct inflate_state *state = (struct inflate_state *)strm->state;
16     struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
17
18     /* Unsupported compression settings */
19     if (state->wbits != HB_BITS)
20         return 0;
21
22     /* Unsupported hardware */
23     return is_bit_set(dfltcc_state->af.fns, DFLTCC_XPND) &&
24                is_bit_set(dfltcc_state->af.fmts, DFLTCC_FMT0);
25 }
26
27 static int dfltcc_was_inflate_used(
28     z_streamp strm
29 )
30 {
31     struct inflate_state *state = (struct inflate_state *)strm->state;
32     struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;
33
34     return !param->nt;
35 }
36
37 static int dfltcc_inflate_disable(
38     z_streamp strm
39 )
40 {
41     struct inflate_state *state = (struct inflate_state *)strm->state;
42     struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
43
44     if (!dfltcc_can_inflate(strm))
45         return 0;
46     if (dfltcc_was_inflate_used(strm))
47         /* DFLTCC has already decompressed some data. Since there is not
48          * enough information to resume decompression in software, the call
49          * must fail.
50          */
51         return 1;
52     /* DFLTCC was not used yet - decompress in software */
53     memset(&dfltcc_state->af, 0, sizeof(dfltcc_state->af));
54     return 0;
55 }
56
57 static dfltcc_cc dfltcc_xpnd(
58     z_streamp strm
59 )
60 {
61     struct inflate_state *state = (struct inflate_state *)strm->state;
62     struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;
63     size_t avail_in = strm->avail_in;
64     size_t avail_out = strm->avail_out;
65     dfltcc_cc cc;
66
67     cc = dfltcc(DFLTCC_XPND | HBT_CIRCULAR,
68                 param, &strm->next_out, &avail_out,
69                 &strm->next_in, &avail_in, state->window);
70     strm->avail_in = avail_in;
71     strm->avail_out = avail_out;
72     return cc;
73 }
74
75 dfltcc_inflate_action dfltcc_inflate(
76     z_streamp strm,
77     int flush,
78     int *ret
79 )
80 {
81     struct inflate_state *state = (struct inflate_state *)strm->state;
82     struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
83     struct dfltcc_param_v0 *param = &dfltcc_state->param;
84     dfltcc_cc cc;
85
86     if (flush == Z_BLOCK) {
87         /* DFLTCC does not support stopping on block boundaries */
88         if (dfltcc_inflate_disable(strm)) {
89             *ret = Z_STREAM_ERROR;
90             return DFLTCC_INFLATE_BREAK;
91         } else
92             return DFLTCC_INFLATE_SOFTWARE;
93     }
94
95     if (state->last) {
96         if (state->bits != 0) {
97             strm->next_in++;
98             strm->avail_in--;
99             state->bits = 0;
100         }
101         state->mode = CHECK;
102         return DFLTCC_INFLATE_CONTINUE;
103     }
104
105     if (strm->avail_in == 0 && !param->cf)
106         return DFLTCC_INFLATE_BREAK;
107
108     if (!state->window || state->wsize == 0) {
109         state->mode = MEM;
110         return DFLTCC_INFLATE_CONTINUE;
111     }
112
113     /* Translate stream to parameter block */
114     param->cvt = CVT_ADLER32;
115     param->sbb = state->bits;
116     param->hl = state->whave; /* Software and hardware history formats match */
117     param->ho = (state->write - state->whave) & ((1 << HB_BITS) - 1);
118     if (param->hl)
119         param->nt = 0; /* Honor history for the first block */
120     param->cv = state->flags ? REVERSE(state->check) : state->check;
121
122     /* Inflate */
123     do {
124         cc = dfltcc_xpnd(strm);
125     } while (cc == DFLTCC_CC_AGAIN);
126
127     /* Translate parameter block to stream */
128     strm->msg = oesc_msg(dfltcc_state->msg, param->oesc);
129     state->last = cc == DFLTCC_CC_OK;
130     state->bits = param->sbb;
131     state->whave = param->hl;
132     state->write = (param->ho + param->hl) & ((1 << HB_BITS) - 1);
133     state->check = state->flags ? REVERSE(param->cv) : param->cv;
134     if (cc == DFLTCC_CC_OP2_CORRUPT && param->oesc != 0) {
135         /* Report an error if stream is corrupted */
136         state->mode = BAD;
137         return DFLTCC_INFLATE_CONTINUE;
138     }
139     state->mode = TYPEDO;
140     /* Break if operands are exhausted, otherwise continue looping */
141     return (cc == DFLTCC_CC_OP1_TOO_SHORT || cc == DFLTCC_CC_OP2_TOO_SHORT) ?
142         DFLTCC_INFLATE_BREAK : DFLTCC_INFLATE_CONTINUE;
143 }