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