// SPDX-License-Identifier: Zlib #include "../zlib_deflate/defutil.h" #include "dfltcc_util.h" #include "dfltcc_deflate.h" #include <asm/setup.h> #include <linux/export.h> #include <linux/zutil.h> #define GET_DFLTCC_DEFLATE_STATE(state) ((struct dfltcc_deflate_state *)GET_DFLTCC_STATE(state)) /* * Compress. */ int dfltcc_can_deflate( z_streamp strm ) { deflate_state *state = (deflate_state *)strm->state; struct dfltcc_deflate_state *dfltcc_state = GET_DFLTCC_DEFLATE_STATE(state); /* Check for kernel dfltcc command line parameter */ if (zlib_dfltcc_support == ZLIB_DFLTCC_DISABLED || zlib_dfltcc_support == ZLIB_DFLTCC_INFLATE_ONLY) return 0; /* Unsupported compression settings */ if (!dfltcc_are_params_ok(state->level, state->w_bits, state->strategy, dfltcc_state->level_mask)) return 0; /* Unsupported hardware */ if (!is_bit_set(dfltcc_state->common.af.fns, DFLTCC_GDHT) || !is_bit_set(dfltcc_state->common.af.fns, DFLTCC_CMPR) || !is_bit_set(dfltcc_state->common.af.fmts, DFLTCC_FMT0)) return 0; return 1; } EXPORT_SYMBOL(dfltcc_can_deflate); void dfltcc_reset_deflate_state(z_streamp strm) { deflate_state *state = (deflate_state *)strm->state; struct dfltcc_deflate_state *dfltcc_state = GET_DFLTCC_DEFLATE_STATE(state); dfltcc_reset_state(&dfltcc_state->common); /* Initialize tuning parameters */ if (zlib_dfltcc_support == ZLIB_DFLTCC_FULL_DEBUG) dfltcc_state->level_mask = DFLTCC_LEVEL_MASK_DEBUG; else dfltcc_state->level_mask = DFLTCC_LEVEL_MASK; dfltcc_state->block_size = DFLTCC_BLOCK_SIZE; dfltcc_state->block_threshold = DFLTCC_FIRST_FHT_BLOCK_SIZE; dfltcc_state->dht_threshold = DFLTCC_DHT_MIN_SAMPLE_SIZE; } EXPORT_SYMBOL(dfltcc_reset_deflate_state); static void dfltcc_gdht( z_streamp strm ) { deflate_state *state = (deflate_state *)strm->state; struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param; size_t avail_in = strm->avail_in; dfltcc(DFLTCC_GDHT, param, NULL, NULL, &strm->next_in, &avail_in, NULL); } static dfltcc_cc dfltcc_cmpr( z_streamp strm ) { deflate_state *state = (deflate_state *)strm->state; struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param; size_t avail_in = strm->avail_in; size_t avail_out = strm->avail_out; dfltcc_cc cc; cc = dfltcc(DFLTCC_CMPR | HBT_CIRCULAR, param, &strm->next_out, &avail_out, &strm->next_in, &avail_in, state->window); strm->total_in += (strm->avail_in - avail_in); strm->total_out += (strm->avail_out - avail_out); strm->avail_in = avail_in; strm->avail_out = avail_out; return cc; } static void send_eobs( z_streamp strm, const struct dfltcc_param_v0 *param ) { deflate_state *state = (deflate_state *)strm->state; zlib_tr_send_bits( state, bi_reverse(param->eobs >> (15 - param->eobl), param->eobl), param->eobl); flush_pending(strm); if (state->pending != 0) { /* The remaining data is located in pending_out[0:pending]. If someone * calls put_byte() - this might happen in deflate() - the byte will be * placed into pending_buf[pending], which is incorrect. Move the * remaining data to the beginning of pending_buf so that put_byte() is * usable again. */ memmove(state->pending_buf, state->pending_out, state->pending); state->pending_out = state->pending_buf; } #ifdef ZLIB_DEBUG state->compressed_len += param->eobl; #endif } int dfltcc_deflate( z_streamp strm, int flush, block_state *result ) { deflate_state *state = (deflate_state *)strm->state; struct dfltcc_deflate_state *dfltcc_state = GET_DFLTCC_DEFLATE_STATE(state); struct dfltcc_param_v0 *param = &dfltcc_state->common.param; uInt masked_avail_in; dfltcc_cc cc; int need_empty_block; int soft_bcc; int no_flush; if (!dfltcc_can_deflate(strm)) { /* Clear history. */ if (flush == Z_FULL_FLUSH) param->hl = 0; return 0; } again: masked_avail_in = 0; soft_bcc = 0; no_flush = flush == Z_NO_FLUSH; /* No input data. Return, except when Continuation Flag is set, which means * that DFLTCC has buffered some output in the parameter block and needs to * be called again in order to flush it. */ if (strm->avail_in == 0 && !param->cf) { /* A block is still open, and the hardware does not support closing * blocks without adding data. Thus, close it manually. */ if (!no_flush && param->bcf) { send_eobs(strm, param); param->bcf = 0; } /* Let one of deflate_* functions write a trailing empty block. */ if (flush == Z_FINISH) return 0; /* Clear history. */ if (flush == Z_FULL_FLUSH) param->hl = 0; /* Trigger block post-processing if necessary. */ *result = no_flush ? need_more : block_done; return 1; } /* There is an open non-BFINAL block, we are not going to close it just * yet, we have compressed more than DFLTCC_BLOCK_SIZE bytes and we see * more than DFLTCC_DHT_MIN_SAMPLE_SIZE bytes. Open a new block with a new * DHT in order to adapt to a possibly changed input data distribution. */ if (param->bcf && no_flush && strm->total_in > dfltcc_state->block_threshold && strm->avail_in >= dfltcc_state->dht_threshold) { if (param->cf) { /* We need to flush the DFLTCC buffer before writing the * End-of-block Symbol. Mask the input data and proceed as usual. */ masked_avail_in += strm->avail_in; strm->avail_in = 0; no_flush = 0; } else { /* DFLTCC buffer is empty, so we can manually write the * End-of-block Symbol right away. */ send_eobs(strm, param); param->bcf = 0; dfltcc_state->block_threshold = strm->total_in + dfltcc_state->block_size; } } /* No space for compressed data. If we proceed, dfltcc_cmpr() will return * DFLTCC_CC_OP1_TOO_SHORT without buffering header bits, but we will still * set BCF=1, which is wrong. Avoid complications and return early. */ if (strm->avail_out == 0) { *result = need_more; return 1; } /* The caller gave us too much data. Pass only one block worth of * uncompressed data to DFLTCC and mask the rest, so that on the next * iteration we start a new block. */ if (no_flush && strm->avail_in > dfltcc_state->block_size) { masked_avail_in += (strm->avail_in - dfltcc_state->block_size); strm->avail_in = dfltcc_state->block_size; } /* When we have an open non-BFINAL deflate block and caller indicates that * the stream is ending, we need to close an open deflate block and open a * BFINAL one. */ need_empty_block = flush == Z_FINISH && param->bcf && !param->bhf; /* Translate stream to parameter block */ param->cvt = CVT_ADLER32; if (!no_flush) /* We need to close a block. Always do this in software - when there is * no input data, the hardware will not hohor BCC. */ soft_bcc = 1; if (flush == Z_FINISH && !param->bcf) /* We are about to open a BFINAL block, set Block Header Final bit * until the stream ends. */ param->bhf = 1; /* DFLTCC-CMPR will write to next_out, so make sure that buffers with * higher precedence are empty. */ Assert(state->pending == 0, "There must be no pending bytes"); Assert(state->bi_valid < 8, "There must be less than 8 pending bits"); param->sbb = (unsigned int)state->bi_valid; if (param->sbb > 0) *strm->next_out = (Byte)state->bi_buf; /* Honor history and check value */ param->nt = 0; param->cv = strm->adler; /* When opening a block, choose a Huffman-Table Type */ if (!param->bcf) { if (strm->total_in == 0 && dfltcc_state->block_threshold > 0) { param->htt = HTT_FIXED; } else { param->htt = HTT_DYNAMIC; dfltcc_gdht(strm); } } /* Deflate */ do { cc = dfltcc_cmpr(strm); if (strm->avail_in < 4096 && masked_avail_in > 0) /* We are about to call DFLTCC with a small input buffer, which is * inefficient. Since there is masked data, there will be at least * one more DFLTCC call, so skip the current one and make the next * one handle more data. */ break; } while (cc == DFLTCC_CC_AGAIN); /* Translate parameter block to stream */ strm->msg = oesc_msg(dfltcc_state->common.msg, param->oesc); state->bi_valid = param->sbb; if (state->bi_valid == 0) state->bi_buf = 0; /* Avoid accessing next_out */ else state->bi_buf = *strm->next_out & ((1 << state->bi_valid) - 1); strm->adler = param->cv; /* Unmask the input data */ strm->avail_in += masked_avail_in; masked_avail_in = 0; /* If we encounter an error, it means there is a bug in DFLTCC call */ Assert(cc != DFLTCC_CC_OP2_CORRUPT || param->oesc == 0, "BUG"); /* Update Block-Continuation Flag. It will be used to check whether to call * GDHT the next time. */ if (cc == DFLTCC_CC_OK) { if (soft_bcc) { send_eobs(strm, param); param->bcf = 0; dfltcc_state->block_threshold = strm->total_in + dfltcc_state->block_size; } else param->bcf = 1; if (flush == Z_FINISH) { if (need_empty_block) /* Make the current deflate() call also close the stream */ return 0; else { bi_windup(state); *result = finish_done; } } else { if (flush == Z_FULL_FLUSH) param->hl = 0; /* Clear history */ *result = flush == Z_NO_FLUSH ? need_more : block_done; } } else { param->bcf = 1; *result = need_more; } if (strm->avail_in != 0 && strm->avail_out != 0) goto again; /* deflate() must use all input or all output */ return 1; } EXPORT_SYMBOL(dfltcc_deflate);