314 lines · c
1// SPDX-License-Identifier: Zlib2 3#include "../zlib_deflate/defutil.h"4#include "dfltcc_util.h"5#include "dfltcc_deflate.h"6#include <asm/setup.h>7#include <linux/export.h>8#include <linux/zutil.h>9 10#define GET_DFLTCC_DEFLATE_STATE(state) ((struct dfltcc_deflate_state *)GET_DFLTCC_STATE(state))11 12/*13 * Compress.14 */15int dfltcc_can_deflate(16 z_streamp strm17)18{19 deflate_state *state = (deflate_state *)strm->state;20 struct dfltcc_deflate_state *dfltcc_state = GET_DFLTCC_DEFLATE_STATE(state);21 22 /* Check for kernel dfltcc command line parameter */23 if (zlib_dfltcc_support == ZLIB_DFLTCC_DISABLED ||24 zlib_dfltcc_support == ZLIB_DFLTCC_INFLATE_ONLY)25 return 0;26 27 /* Unsupported compression settings */28 if (!dfltcc_are_params_ok(state->level, state->w_bits, state->strategy,29 dfltcc_state->level_mask))30 return 0;31 32 /* Unsupported hardware */33 if (!is_bit_set(dfltcc_state->common.af.fns, DFLTCC_GDHT) ||34 !is_bit_set(dfltcc_state->common.af.fns, DFLTCC_CMPR) ||35 !is_bit_set(dfltcc_state->common.af.fmts, DFLTCC_FMT0))36 return 0;37 38 return 1;39}40EXPORT_SYMBOL(dfltcc_can_deflate);41 42void dfltcc_reset_deflate_state(z_streamp strm) {43 deflate_state *state = (deflate_state *)strm->state;44 struct dfltcc_deflate_state *dfltcc_state = GET_DFLTCC_DEFLATE_STATE(state);45 46 dfltcc_reset_state(&dfltcc_state->common);47 48 /* Initialize tuning parameters */49 if (zlib_dfltcc_support == ZLIB_DFLTCC_FULL_DEBUG)50 dfltcc_state->level_mask = DFLTCC_LEVEL_MASK_DEBUG;51 else52 dfltcc_state->level_mask = DFLTCC_LEVEL_MASK;53 dfltcc_state->block_size = DFLTCC_BLOCK_SIZE;54 dfltcc_state->block_threshold = DFLTCC_FIRST_FHT_BLOCK_SIZE;55 dfltcc_state->dht_threshold = DFLTCC_DHT_MIN_SAMPLE_SIZE;56}57EXPORT_SYMBOL(dfltcc_reset_deflate_state);58 59static void dfltcc_gdht(60 z_streamp strm61)62{63 deflate_state *state = (deflate_state *)strm->state;64 struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;65 size_t avail_in = strm->avail_in;66 67 dfltcc(DFLTCC_GDHT,68 param, NULL, NULL,69 &strm->next_in, &avail_in, NULL);70}71 72static dfltcc_cc dfltcc_cmpr(73 z_streamp strm74)75{76 deflate_state *state = (deflate_state *)strm->state;77 struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;78 size_t avail_in = strm->avail_in;79 size_t avail_out = strm->avail_out;80 dfltcc_cc cc;81 82 cc = dfltcc(DFLTCC_CMPR | HBT_CIRCULAR,83 param, &strm->next_out, &avail_out,84 &strm->next_in, &avail_in, state->window);85 strm->total_in += (strm->avail_in - avail_in);86 strm->total_out += (strm->avail_out - avail_out);87 strm->avail_in = avail_in;88 strm->avail_out = avail_out;89 return cc;90}91 92static void send_eobs(93 z_streamp strm,94 const struct dfltcc_param_v0 *param95)96{97 deflate_state *state = (deflate_state *)strm->state;98 99 zlib_tr_send_bits(100 state,101 bi_reverse(param->eobs >> (15 - param->eobl), param->eobl),102 param->eobl);103 flush_pending(strm);104 if (state->pending != 0) {105 /* The remaining data is located in pending_out[0:pending]. If someone106 * calls put_byte() - this might happen in deflate() - the byte will be107 * placed into pending_buf[pending], which is incorrect. Move the108 * remaining data to the beginning of pending_buf so that put_byte() is109 * usable again.110 */111 memmove(state->pending_buf, state->pending_out, state->pending);112 state->pending_out = state->pending_buf;113 }114#ifdef ZLIB_DEBUG115 state->compressed_len += param->eobl;116#endif117}118 119int dfltcc_deflate(120 z_streamp strm,121 int flush,122 block_state *result123)124{125 deflate_state *state = (deflate_state *)strm->state;126 struct dfltcc_deflate_state *dfltcc_state = GET_DFLTCC_DEFLATE_STATE(state);127 struct dfltcc_param_v0 *param = &dfltcc_state->common.param;128 uInt masked_avail_in;129 dfltcc_cc cc;130 int need_empty_block;131 int soft_bcc;132 int no_flush;133 134 if (!dfltcc_can_deflate(strm)) {135 /* Clear history. */136 if (flush == Z_FULL_FLUSH)137 param->hl = 0;138 return 0;139 }140 141again:142 masked_avail_in = 0;143 soft_bcc = 0;144 no_flush = flush == Z_NO_FLUSH;145 146 /* No input data. Return, except when Continuation Flag is set, which means147 * that DFLTCC has buffered some output in the parameter block and needs to148 * be called again in order to flush it.149 */150 if (strm->avail_in == 0 && !param->cf) {151 /* A block is still open, and the hardware does not support closing152 * blocks without adding data. Thus, close it manually.153 */154 if (!no_flush && param->bcf) {155 send_eobs(strm, param);156 param->bcf = 0;157 }158 /* Let one of deflate_* functions write a trailing empty block. */159 if (flush == Z_FINISH)160 return 0;161 /* Clear history. */162 if (flush == Z_FULL_FLUSH)163 param->hl = 0;164 /* Trigger block post-processing if necessary. */165 *result = no_flush ? need_more : block_done;166 return 1;167 }168 169 /* There is an open non-BFINAL block, we are not going to close it just170 * yet, we have compressed more than DFLTCC_BLOCK_SIZE bytes and we see171 * more than DFLTCC_DHT_MIN_SAMPLE_SIZE bytes. Open a new block with a new172 * DHT in order to adapt to a possibly changed input data distribution.173 */174 if (param->bcf && no_flush &&175 strm->total_in > dfltcc_state->block_threshold &&176 strm->avail_in >= dfltcc_state->dht_threshold) {177 if (param->cf) {178 /* We need to flush the DFLTCC buffer before writing the179 * End-of-block Symbol. Mask the input data and proceed as usual.180 */181 masked_avail_in += strm->avail_in;182 strm->avail_in = 0;183 no_flush = 0;184 } else {185 /* DFLTCC buffer is empty, so we can manually write the186 * End-of-block Symbol right away.187 */188 send_eobs(strm, param);189 param->bcf = 0;190 dfltcc_state->block_threshold =191 strm->total_in + dfltcc_state->block_size;192 }193 }194 195 /* No space for compressed data. If we proceed, dfltcc_cmpr() will return196 * DFLTCC_CC_OP1_TOO_SHORT without buffering header bits, but we will still197 * set BCF=1, which is wrong. Avoid complications and return early.198 */199 if (strm->avail_out == 0) {200 *result = need_more;201 return 1;202 }203 204 /* The caller gave us too much data. Pass only one block worth of205 * uncompressed data to DFLTCC and mask the rest, so that on the next206 * iteration we start a new block.207 */208 if (no_flush && strm->avail_in > dfltcc_state->block_size) {209 masked_avail_in += (strm->avail_in - dfltcc_state->block_size);210 strm->avail_in = dfltcc_state->block_size;211 }212 213 /* When we have an open non-BFINAL deflate block and caller indicates that214 * the stream is ending, we need to close an open deflate block and open a215 * BFINAL one.216 */217 need_empty_block = flush == Z_FINISH && param->bcf && !param->bhf;218 219 /* Translate stream to parameter block */220 param->cvt = CVT_ADLER32;221 if (!no_flush)222 /* We need to close a block. Always do this in software - when there is223 * no input data, the hardware will not hohor BCC. */224 soft_bcc = 1;225 if (flush == Z_FINISH && !param->bcf)226 /* We are about to open a BFINAL block, set Block Header Final bit227 * until the stream ends.228 */229 param->bhf = 1;230 /* DFLTCC-CMPR will write to next_out, so make sure that buffers with231 * higher precedence are empty.232 */233 Assert(state->pending == 0, "There must be no pending bytes");234 Assert(state->bi_valid < 8, "There must be less than 8 pending bits");235 param->sbb = (unsigned int)state->bi_valid;236 if (param->sbb > 0)237 *strm->next_out = (Byte)state->bi_buf;238 /* Honor history and check value */239 param->nt = 0;240 param->cv = strm->adler;241 242 /* When opening a block, choose a Huffman-Table Type */243 if (!param->bcf) {244 if (strm->total_in == 0 && dfltcc_state->block_threshold > 0) {245 param->htt = HTT_FIXED;246 }247 else {248 param->htt = HTT_DYNAMIC;249 dfltcc_gdht(strm);250 }251 }252 253 /* Deflate */254 do {255 cc = dfltcc_cmpr(strm);256 if (strm->avail_in < 4096 && masked_avail_in > 0)257 /* We are about to call DFLTCC with a small input buffer, which is258 * inefficient. Since there is masked data, there will be at least259 * one more DFLTCC call, so skip the current one and make the next260 * one handle more data.261 */262 break;263 } while (cc == DFLTCC_CC_AGAIN);264 265 /* Translate parameter block to stream */266 strm->msg = oesc_msg(dfltcc_state->common.msg, param->oesc);267 state->bi_valid = param->sbb;268 if (state->bi_valid == 0)269 state->bi_buf = 0; /* Avoid accessing next_out */270 else271 state->bi_buf = *strm->next_out & ((1 << state->bi_valid) - 1);272 strm->adler = param->cv;273 274 /* Unmask the input data */275 strm->avail_in += masked_avail_in;276 masked_avail_in = 0;277 278 /* If we encounter an error, it means there is a bug in DFLTCC call */279 Assert(cc != DFLTCC_CC_OP2_CORRUPT || param->oesc == 0, "BUG");280 281 /* Update Block-Continuation Flag. It will be used to check whether to call282 * GDHT the next time.283 */284 if (cc == DFLTCC_CC_OK) {285 if (soft_bcc) {286 send_eobs(strm, param);287 param->bcf = 0;288 dfltcc_state->block_threshold =289 strm->total_in + dfltcc_state->block_size;290 } else291 param->bcf = 1;292 if (flush == Z_FINISH) {293 if (need_empty_block)294 /* Make the current deflate() call also close the stream */295 return 0;296 else {297 bi_windup(state);298 *result = finish_done;299 }300 } else {301 if (flush == Z_FULL_FLUSH)302 param->hl = 0; /* Clear history */303 *result = flush == Z_NO_FLUSH ? need_more : block_done;304 }305 } else {306 param->bcf = 1;307 *result = need_more;308 }309 if (strm->avail_in != 0 && strm->avail_out != 0)310 goto again; /* deflate() must use all input or all output */311 return 1;312}313EXPORT_SYMBOL(dfltcc_deflate);314