linux/lib/zlib_dfltcc/dfltcc_deflate.c
<<
>>
Prefs
   1// SPDX-License-Identifier: Zlib
   2
   3#include "../zlib_deflate/defutil.h"
   4#include "dfltcc_util.h"
   5#include "dfltcc.h"
   6#include <asm/setup.h>
   7#include <linux/export.h>
   8#include <linux/zutil.h>
   9
  10/*
  11 * Compress.
  12 */
  13int dfltcc_can_deflate(
  14    z_streamp strm
  15)
  16{
  17    deflate_state *state = (deflate_state *)strm->state;
  18    struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
  19
  20    /* Check for kernel dfltcc command line parameter */
  21    if (zlib_dfltcc_support == ZLIB_DFLTCC_DISABLED ||
  22            zlib_dfltcc_support == ZLIB_DFLTCC_INFLATE_ONLY)
  23        return 0;
  24
  25    /* Unsupported compression settings */
  26    if (!dfltcc_are_params_ok(state->level, state->w_bits, state->strategy,
  27                              dfltcc_state->level_mask))
  28        return 0;
  29
  30    /* Unsupported hardware */
  31    if (!is_bit_set(dfltcc_state->af.fns, DFLTCC_GDHT) ||
  32            !is_bit_set(dfltcc_state->af.fns, DFLTCC_CMPR) ||
  33            !is_bit_set(dfltcc_state->af.fmts, DFLTCC_FMT0))
  34        return 0;
  35
  36    return 1;
  37}
  38EXPORT_SYMBOL(dfltcc_can_deflate);
  39
  40static void dfltcc_gdht(
  41    z_streamp strm
  42)
  43{
  44    deflate_state *state = (deflate_state *)strm->state;
  45    struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;
  46    size_t avail_in = avail_in = strm->avail_in;
  47
  48    dfltcc(DFLTCC_GDHT,
  49           param, NULL, NULL,
  50           &strm->next_in, &avail_in, NULL);
  51}
  52
  53static dfltcc_cc dfltcc_cmpr(
  54    z_streamp strm
  55)
  56{
  57    deflate_state *state = (deflate_state *)strm->state;
  58    struct dfltcc_param_v0 *param = &GET_DFLTCC_STATE(state)->param;
  59    size_t avail_in = strm->avail_in;
  60    size_t avail_out = strm->avail_out;
  61    dfltcc_cc cc;
  62
  63    cc = dfltcc(DFLTCC_CMPR | HBT_CIRCULAR,
  64                param, &strm->next_out, &avail_out,
  65                &strm->next_in, &avail_in, state->window);
  66    strm->total_in += (strm->avail_in - avail_in);
  67    strm->total_out += (strm->avail_out - avail_out);
  68    strm->avail_in = avail_in;
  69    strm->avail_out = avail_out;
  70    return cc;
  71}
  72
  73static void send_eobs(
  74    z_streamp strm,
  75    const struct dfltcc_param_v0 *param
  76)
  77{
  78    deflate_state *state = (deflate_state *)strm->state;
  79
  80    zlib_tr_send_bits(
  81          state,
  82          bi_reverse(param->eobs >> (15 - param->eobl), param->eobl),
  83          param->eobl);
  84    flush_pending(strm);
  85    if (state->pending != 0) {
  86        /* The remaining data is located in pending_out[0:pending]. If someone
  87         * calls put_byte() - this might happen in deflate() - the byte will be
  88         * placed into pending_buf[pending], which is incorrect. Move the
  89         * remaining data to the beginning of pending_buf so that put_byte() is
  90         * usable again.
  91         */
  92        memmove(state->pending_buf, state->pending_out, state->pending);
  93        state->pending_out = state->pending_buf;
  94    }
  95#ifdef ZLIB_DEBUG
  96    state->compressed_len += param->eobl;
  97#endif
  98}
  99
 100int dfltcc_deflate(
 101    z_streamp strm,
 102    int flush,
 103    block_state *result
 104)
 105{
 106    deflate_state *state = (deflate_state *)strm->state;
 107    struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
 108    struct dfltcc_param_v0 *param = &dfltcc_state->param;
 109    uInt masked_avail_in;
 110    dfltcc_cc cc;
 111    int need_empty_block;
 112    int soft_bcc;
 113    int no_flush;
 114
 115    if (!dfltcc_can_deflate(strm))
 116        return 0;
 117
 118again:
 119    masked_avail_in = 0;
 120    soft_bcc = 0;
 121    no_flush = flush == Z_NO_FLUSH;
 122
 123    /* Trailing empty block. Switch to software, except when Continuation Flag
 124     * is set, which means that DFLTCC has buffered some output in the
 125     * parameter block and needs to be called again in order to flush it.
 126     */
 127    if (flush == Z_FINISH && strm->avail_in == 0 && !param->cf) {
 128        if (param->bcf) {
 129            /* A block is still open, and the hardware does not support closing
 130             * blocks without adding data. Thus, close it manually.
 131             */
 132            send_eobs(strm, param);
 133            param->bcf = 0;
 134        }
 135        return 0;
 136    }
 137
 138    if (strm->avail_in == 0 && !param->cf) {
 139        *result = need_more;
 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 =
 165                strm->total_in + dfltcc_state->block_size;
 166            if (strm->avail_out == 0) {
 167                *result = need_more;
 168                return 1;
 169            }
 170        }
 171    }
 172
 173    /* The caller gave us too much data. Pass only one block worth of
 174     * uncompressed data to DFLTCC and mask the rest, so that on the next
 175     * iteration we start a new block.
 176     */
 177    if (no_flush && strm->avail_in > dfltcc_state->block_size) {
 178        masked_avail_in += (strm->avail_in - dfltcc_state->block_size);
 179        strm->avail_in = dfltcc_state->block_size;
 180    }
 181
 182    /* When we have an open non-BFINAL deflate block and caller indicates that
 183     * the stream is ending, we need to close an open deflate block and open a
 184     * BFINAL one.
 185     */
 186    need_empty_block = flush == Z_FINISH && param->bcf && !param->bhf;
 187
 188    /* Translate stream to parameter block */
 189    param->cvt = CVT_ADLER32;
 190    if (!no_flush)
 191        /* We need to close a block. Always do this in software - when there is
 192         * no input data, the hardware will not nohor BCC. */
 193        soft_bcc = 1;
 194    if (flush == Z_FINISH && !param->bcf)
 195        /* We are about to open a BFINAL block, set Block Header Final bit
 196         * until the stream ends.
 197         */
 198        param->bhf = 1;
 199    /* DFLTCC-CMPR will write to next_out, so make sure that buffers with
 200     * higher precedence are empty.
 201     */
 202    Assert(state->pending == 0, "There must be no pending bytes");
 203    Assert(state->bi_valid < 8, "There must be less than 8 pending bits");
 204    param->sbb = (unsigned int)state->bi_valid;
 205    if (param->sbb > 0)
 206        *strm->next_out = (Byte)state->bi_buf;
 207    if (param->hl)
 208        param->nt = 0; /* Honor history */
 209    param->cv = strm->adler;
 210
 211    /* When opening a block, choose a Huffman-Table Type */
 212    if (!param->bcf) {
 213        if (strm->total_in == 0 && dfltcc_state->block_threshold > 0) {
 214            param->htt = HTT_FIXED;
 215        }
 216        else {
 217            param->htt = HTT_DYNAMIC;
 218            dfltcc_gdht(strm);
 219        }
 220    }
 221
 222    /* Deflate */
 223    do {
 224        cc = dfltcc_cmpr(strm);
 225        if (strm->avail_in < 4096 && masked_avail_in > 0)
 226            /* We are about to call DFLTCC with a small input buffer, which is
 227             * inefficient. Since there is masked data, there will be at least
 228             * one more DFLTCC call, so skip the current one and make the next
 229             * one handle more data.
 230             */
 231            break;
 232    } while (cc == DFLTCC_CC_AGAIN);
 233
 234    /* Translate parameter block to stream */
 235    strm->msg = oesc_msg(dfltcc_state->msg, param->oesc);
 236    state->bi_valid = param->sbb;
 237    if (state->bi_valid == 0)
 238        state->bi_buf = 0; /* Avoid accessing next_out */
 239    else
 240        state->bi_buf = *strm->next_out & ((1 << state->bi_valid) - 1);
 241    strm->adler = param->cv;
 242
 243    /* Unmask the input data */
 244    strm->avail_in += masked_avail_in;
 245    masked_avail_in = 0;
 246
 247    /* If we encounter an error, it means there is a bug in DFLTCC call */
 248    Assert(cc != DFLTCC_CC_OP2_CORRUPT || param->oesc == 0, "BUG");
 249
 250    /* Update Block-Continuation Flag. It will be used to check whether to call
 251     * GDHT the next time.
 252     */
 253    if (cc == DFLTCC_CC_OK) {
 254        if (soft_bcc) {
 255            send_eobs(strm, param);
 256            param->bcf = 0;
 257            dfltcc_state->block_threshold =
 258                strm->total_in + dfltcc_state->block_size;
 259        } else
 260            param->bcf = 1;
 261        if (flush == Z_FINISH) {
 262            if (need_empty_block)
 263                /* Make the current deflate() call also close the stream */
 264                return 0;
 265            else {
 266                bi_windup(state);
 267                *result = finish_done;
 268            }
 269        } else {
 270            if (flush == Z_FULL_FLUSH)
 271                param->hl = 0; /* Clear history */
 272            *result = flush == Z_NO_FLUSH ? need_more : block_done;
 273        }
 274    } else {
 275        param->bcf = 1;
 276        *result = need_more;
 277    }
 278    if (strm->avail_in != 0 && strm->avail_out != 0)
 279        goto again; /* deflate() must use all input or all output */
 280    return 1;
 281}
 282EXPORT_SYMBOL(dfltcc_deflate);
 283