• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* dfltcc_deflate.c - IBM Z DEFLATE CONVERSION CALL compression support. */
2 
3 /*
4    Use the following commands to build zlib-ng with DFLTCC compression support:
5 
6         $ ./configure --with-dfltcc-deflate
7    or
8 
9         $ cmake -DWITH_DFLTCC_DEFLATE=1 .
10 
11    and then
12 
13         $ make
14 */
15 
16 #include "../../zbuild.h"
17 #include "../../zutil.h"
18 #include "../../deflate.h"
19 #include "../../trees_emit.h"
20 #include "dfltcc_deflate.h"
21 #include "dfltcc_detail.h"
22 
dfltcc_can_deflate_with_params(PREFIX3 (streamp)strm,int level,uInt window_bits,int strategy,int reproducible)23 static inline int dfltcc_can_deflate_with_params(PREFIX3(streamp) strm, int level, uInt window_bits, int strategy,
24                                        int reproducible) {
25     deflate_state *state = (deflate_state *)strm->state;
26     struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
27 
28     /* Unsupported compression settings */
29     if ((dfltcc_state->level_mask & (1 << level)) == 0)
30         return 0;
31     if (window_bits != HB_BITS)
32         return 0;
33     if (strategy != Z_FIXED && strategy != Z_DEFAULT_STRATEGY)
34         return 0;
35     if (reproducible)
36         return 0;
37 
38     /* Unsupported hardware */
39     if (!is_bit_set(dfltcc_state->af.fns, DFLTCC_GDHT) ||
40             !is_bit_set(dfltcc_state->af.fns, DFLTCC_CMPR) ||
41             !is_bit_set(dfltcc_state->af.fmts, DFLTCC_FMT0))
42         return 0;
43 
44     return 1;
45 }
46 
dfltcc_can_deflate(PREFIX3 (streamp)strm)47 int Z_INTERNAL dfltcc_can_deflate(PREFIX3(streamp) strm) {
48     deflate_state *state = (deflate_state *)strm->state;
49 
50     return dfltcc_can_deflate_with_params(strm, state->level, state->w_bits, state->strategy, state->reproducible);
51 }
52 
dfltcc_gdht(PREFIX3 (streamp)strm)53 static inline void dfltcc_gdht(PREFIX3(streamp) strm) {
54     deflate_state *state = (deflate_state *)strm->state;
55     struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;
56     size_t avail_in = strm->avail_in;
57 
58     dfltcc(DFLTCC_GDHT, param, NULL, NULL, &strm->next_in, &avail_in, NULL);
59 }
60 
dfltcc_cmpr(PREFIX3 (streamp)strm)61 static inline dfltcc_cc dfltcc_cmpr(PREFIX3(streamp) strm) {
62     deflate_state *state = (deflate_state *)strm->state;
63     struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;
64     size_t avail_in = strm->avail_in;
65     size_t avail_out = strm->avail_out;
66     dfltcc_cc cc;
67 
68     cc = dfltcc(DFLTCC_CMPR | HBT_CIRCULAR,
69                 param, &strm->next_out, &avail_out,
70                 &strm->next_in, &avail_in, state->window);
71     strm->total_in += (strm->avail_in - avail_in);
72     strm->total_out += (strm->avail_out - avail_out);
73     strm->avail_in = avail_in;
74     strm->avail_out = avail_out;
75     return cc;
76 }
77 
send_eobs(PREFIX3 (streamp)strm,const struct dfltcc_param_v0 * param)78 static inline void send_eobs(PREFIX3(streamp) strm, const struct dfltcc_param_v0 *param) {
79     deflate_state *state = (deflate_state *)strm->state;
80 
81     send_bits(state, bi_reverse(param->eobs >> (15 - param->eobl), param->eobl), param->eobl, state->bi_buf, state->bi_valid);
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 
dfltcc_deflate(PREFIX3 (streamp)strm,int flush,block_state * result)98 int Z_INTERNAL dfltcc_deflate(PREFIX3(streamp) strm, int flush, block_state *result) {
99     deflate_state *state = (deflate_state *)strm->state;
100     struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
101     struct dfltcc_param_v0 *param = &dfltcc_state->param;
102     uInt masked_avail_in;
103     dfltcc_cc cc;
104     int need_empty_block;
105     int soft_bcc;
106     int no_flush;
107 
108     if (!dfltcc_can_deflate(strm)) {
109         /* Clear history. */
110         if (flush == Z_FULL_FLUSH)
111             param->hl = 0;
112         return 0;
113     }
114 
115 again:
116     masked_avail_in = 0;
117     soft_bcc = 0;
118     no_flush = flush == Z_NO_FLUSH;
119 
120     /* No input data. Return, except when Continuation Flag is set, which means
121      * that DFLTCC has buffered some output in the parameter block and needs to
122      * be called again in order to flush it.
123      */
124     if (strm->avail_in == 0 && !param->cf) {
125         /* A block is still open, and the hardware does not support closing
126          * blocks without adding data. Thus, close it manually.
127          */
128         if (!no_flush && param->bcf) {
129             send_eobs(strm, param);
130             param->bcf = 0;
131         }
132         /* Let one of deflate_* functions write a trailing empty block. */
133         if (flush == Z_FINISH)
134             return 0;
135         /* Clear history. */
136         if (flush == Z_FULL_FLUSH)
137             param->hl = 0;
138         /* Trigger block post-processing if necessary. */
139         *result = no_flush ? need_more : block_done;
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 = strm->total_in + dfltcc_state->block_size;
165         }
166     }
167 
168     /* No space for compressed data. If we proceed, dfltcc_cmpr() will return
169      * DFLTCC_CC_OP1_TOO_SHORT without buffering header bits, but we will still
170      * set BCF=1, which is wrong. Avoid complications and return early.
171      */
172     if (strm->avail_out == 0) {
173         *result = need_more;
174         return 1;
175     }
176 
177     /* The caller gave us too much data. Pass only one block worth of
178      * uncompressed data to DFLTCC and mask the rest, so that on the next
179      * iteration we start a new block.
180      */
181     if (no_flush && strm->avail_in > dfltcc_state->block_size) {
182         masked_avail_in += (strm->avail_in - dfltcc_state->block_size);
183         strm->avail_in = dfltcc_state->block_size;
184     }
185 
186     /* When we have an open non-BFINAL deflate block and caller indicates that
187      * the stream is ending, we need to close an open deflate block and open a
188      * BFINAL one.
189      */
190     need_empty_block = flush == Z_FINISH && param->bcf && !param->bhf;
191 
192     /* Translate stream to parameter block */
193     param->cvt = state->wrap == 2 ? CVT_CRC32 : CVT_ADLER32;
194     if (!no_flush)
195         /* We need to close a block. Always do this in software - when there is
196          * no input data, the hardware will not honor BCC. */
197         soft_bcc = 1;
198     if (flush == Z_FINISH && !param->bcf)
199         /* We are about to open a BFINAL block, set Block Header Final bit
200          * until the stream ends.
201          */
202         param->bhf = 1;
203     /* DFLTCC-CMPR will write to next_out, so make sure that buffers with
204      * higher precedence are empty.
205      */
206     Assert(state->pending == 0, "There must be no pending bytes");
207     Assert(state->bi_valid < 8, "There must be less than 8 pending bits");
208     param->sbb = (unsigned int)state->bi_valid;
209     if (param->sbb > 0)
210         *strm->next_out = (unsigned char)state->bi_buf;
211     /* Honor history and check value */
212     param->nt = 0;
213     param->cv = state->wrap == 2 ? ZSWAP32(strm->adler) : strm->adler;
214 
215     /* When opening a block, choose a Huffman-Table Type */
216     if (!param->bcf) {
217         if (state->strategy == Z_FIXED || (strm->total_in == 0 && dfltcc_state->block_threshold > 0))
218             param->htt = HTT_FIXED;
219         else {
220             param->htt = HTT_DYNAMIC;
221             dfltcc_gdht(strm);
222         }
223     }
224 
225     /* Deflate */
226     do {
227         cc = dfltcc_cmpr(strm);
228         if (strm->avail_in < 4096 && masked_avail_in > 0)
229             /* We are about to call DFLTCC with a small input buffer, which is
230              * inefficient. Since there is masked data, there will be at least
231              * one more DFLTCC call, so skip the current one and make the next
232              * one handle more data.
233              */
234             break;
235     } while (cc == DFLTCC_CC_AGAIN);
236 
237     /* Translate parameter block to stream */
238     strm->msg = oesc_msg(dfltcc_state->msg, param->oesc);
239     state->bi_valid = param->sbb;
240     if (state->bi_valid == 0)
241         state->bi_buf = 0; /* Avoid accessing next_out */
242     else
243         state->bi_buf = *strm->next_out & ((1 << state->bi_valid) - 1);
244     strm->adler = state->wrap == 2 ? ZSWAP32(param->cv) : param->cv;
245 
246     /* Unmask the input data */
247     strm->avail_in += masked_avail_in;
248     masked_avail_in = 0;
249 
250     /* If we encounter an error, it means there is a bug in DFLTCC call */
251     Assert(cc != DFLTCC_CC_OP2_CORRUPT || param->oesc == 0, "BUG");
252 
253     /* Update Block-Continuation Flag. It will be used to check whether to call
254      * GDHT the next time.
255      */
256     if (cc == DFLTCC_CC_OK) {
257         if (soft_bcc) {
258             send_eobs(strm, param);
259             param->bcf = 0;
260             dfltcc_state->block_threshold = strm->total_in + dfltcc_state->block_size;
261         } else
262             param->bcf = 1;
263         if (flush == Z_FINISH) {
264             if (need_empty_block)
265                 /* Make the current deflate() call also close the stream */
266                 return 0;
267             else {
268                 bi_windup(state);
269                 *result = finish_done;
270             }
271         } else {
272             if (flush == Z_FULL_FLUSH)
273                 param->hl = 0; /* Clear history */
274             *result = flush == Z_NO_FLUSH ? need_more : block_done;
275         }
276     } else {
277         param->bcf = 1;
278         *result = need_more;
279     }
280     if (strm->avail_in != 0 && strm->avail_out != 0)
281         goto again; /* deflate() must use all input or all output */
282     return 1;
283 }
284 
285 /*
286    Switching between hardware and software compression.
287 
288    DFLTCC does not support all zlib settings, e.g. generation of non-compressed
289    blocks or alternative window sizes. When such settings are applied on the
290    fly with deflateParams, we need to convert between hardware and software
291    window formats.
292 */
dfltcc_was_deflate_used(PREFIX3 (streamp)strm)293 static int dfltcc_was_deflate_used(PREFIX3(streamp) strm) {
294     deflate_state *state = (deflate_state *)strm->state;
295     struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;
296 
297     return strm->total_in > 0 || param->nt == 0 || param->hl > 0;
298 }
299 
dfltcc_deflate_params(PREFIX3 (streamp)strm,int level,int strategy,int * flush)300 int Z_INTERNAL dfltcc_deflate_params(PREFIX3(streamp) strm, int level, int strategy, int *flush) {
301     deflate_state *state = (deflate_state *)strm->state;
302     int could_deflate = dfltcc_can_deflate(strm);
303     int can_deflate = dfltcc_can_deflate_with_params(strm, level, state->w_bits, strategy, state->reproducible);
304 
305     if (can_deflate == could_deflate)
306         /* We continue to work in the same mode - no changes needed */
307         return Z_OK;
308 
309     if (!dfltcc_was_deflate_used(strm))
310         /* DFLTCC was not used yet - no changes needed */
311         return Z_OK;
312 
313     /* For now, do not convert between window formats - simply get rid of the old data instead */
314     *flush = Z_FULL_FLUSH;
315     return Z_OK;
316 }
317 
dfltcc_deflate_done(PREFIX3 (streamp)strm,int flush)318 int Z_INTERNAL dfltcc_deflate_done(PREFIX3(streamp) strm, int flush) {
319     deflate_state *state = (deflate_state *)strm->state;
320     struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
321     struct dfltcc_param_v0 *param = &dfltcc_state->param;
322 
323     /* When deflate(Z_FULL_FLUSH) is called with small avail_out, it might
324      * close the block without resetting the compression state. Detect this
325      * situation and return that deflation is not done.
326      */
327     if (flush == Z_FULL_FLUSH && strm->avail_out == 0)
328         return 0;
329 
330     /* Return that deflation is not done if DFLTCC is used and either it
331      * buffered some data (Continuation Flag is set), or has not written EOBS
332      * yet (Block-Continuation Flag is set).
333      */
334     return !dfltcc_can_deflate(strm) || (!param->cf && !param->bcf);
335 }
336 
dfltcc_can_set_reproducible(PREFIX3 (streamp)strm,int reproducible)337 int Z_INTERNAL dfltcc_can_set_reproducible(PREFIX3(streamp) strm, int reproducible) {
338     deflate_state *state = (deflate_state *)strm->state;
339 
340     return reproducible != state->reproducible && !dfltcc_was_deflate_used(strm);
341 }
342 
343 /*
344    Preloading history.
345 */
append_history(struct dfltcc_param_v0 * param,unsigned char * history,const unsigned char * buf,uInt count)346 static void append_history(struct dfltcc_param_v0 *param, unsigned char *history, const unsigned char *buf, uInt count) {
347     size_t offset;
348     size_t n;
349 
350     /* Do not use more than 32K */
351     if (count > HB_SIZE) {
352         buf += count - HB_SIZE;
353         count = HB_SIZE;
354     }
355     offset = (param->ho + param->hl) % HB_SIZE;
356     if (offset + count <= HB_SIZE)
357         /* Circular history buffer does not wrap - copy one chunk */
358         memcpy(history + offset, buf, count);
359     else {
360         /* Circular history buffer wraps - copy two chunks */
361         n = HB_SIZE - offset;
362         memcpy(history + offset, buf, n);
363         memcpy(history, buf + n, count - n);
364     }
365     n = param->hl + count;
366     if (n <= HB_SIZE)
367         /* All history fits into buffer - no need to discard anything */
368         param->hl = n;
369     else {
370         /* History does not fit into buffer - discard extra bytes */
371         param->ho = (param->ho + (n - HB_SIZE)) % HB_SIZE;
372         param->hl = HB_SIZE;
373     }
374 }
375 
dfltcc_deflate_set_dictionary(PREFIX3 (streamp)strm,const unsigned char * dictionary,uInt dict_length)376 int Z_INTERNAL dfltcc_deflate_set_dictionary(PREFIX3(streamp) strm,
377                                                 const unsigned char *dictionary, uInt dict_length) {
378     deflate_state *state = (deflate_state *)strm->state;
379     struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
380     struct dfltcc_param_v0 *param = &dfltcc_state->param;
381 
382     append_history(param, state->window, dictionary, dict_length);
383     state->strstart = 1; /* Add FDICT to zlib header */
384     state->block_start = state->strstart; /* Make deflate_stored happy */
385     return Z_OK;
386 }
387 
dfltcc_deflate_get_dictionary(PREFIX3 (streamp)strm,unsigned char * dictionary,uInt * dict_length)388 int Z_INTERNAL dfltcc_deflate_get_dictionary(PREFIX3(streamp) strm, unsigned char *dictionary, uInt *dict_length) {
389     deflate_state *state = (deflate_state *)strm->state;
390     struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
391     struct dfltcc_param_v0 *param = &dfltcc_state->param;
392 
393     if (dictionary) {
394         if (param->ho + param->hl <= HB_SIZE)
395             /* Circular history buffer does not wrap - copy one chunk */
396             memcpy(dictionary, state->window + param->ho, param->hl);
397         else {
398             /* Circular history buffer wraps - copy two chunks */
399             memcpy(dictionary, state->window + param->ho, HB_SIZE - param->ho);
400             memcpy(dictionary + HB_SIZE - param->ho, state->window, param->ho + param->hl - HB_SIZE);
401         }
402     }
403     if (dict_length)
404         *dict_length = param->hl;
405     return Z_OK;
406 }
407