Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | // 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); |