deb-libisal/igzip/igzip.c
Roy Oursler 4809b9ba83 igzip: Create flag signifying existence of buffer history
Create a flag signifying the existence of buffer history so a literal does not
have to be encoded on every call of isal_deflate_body and isal_deflate_finish.

Change-Id: I3750cac7e5f19fbaa190e19fb929172c16289723
Signed-off-by: Roy Oursler <roy.j.oursler@intel.com>
2016-12-06 11:22:22 -07:00

1077 lines
30 KiB
C

/**********************************************************************
Copyright(c) 2011-2016 Intel Corporation All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**********************************************************************/
#define ASM
#include <assert.h>
#include <string.h>
#ifdef _WIN32
# include <intrin.h>
#endif
#define MAX_WRITE_BITS_SIZE 8
#define FORCE_FLUSH 64
#define MIN_OBUF_SIZE 224
#define NON_EMPTY_BLOCK_SIZE 6
#define MAX_SYNC_FLUSH_SIZE NON_EMPTY_BLOCK_SIZE + MAX_WRITE_BITS_SIZE
#include "huffman.h"
#include "bitbuf2.h"
#include "igzip_lib.h"
#include "repeated_char_result.h"
extern const uint8_t gzip_hdr[];
extern const uint32_t gzip_hdr_bytes;
extern const uint32_t gzip_trl_bytes;
extern const struct isal_hufftables hufftables_default;
extern uint32_t CrcTable[256];
extern uint32_t crc32_gzip(uint32_t init_crc, const unsigned char *buf, uint64_t len);
static int write_stored_block_stateless(struct isal_zstream *stream, uint32_t stored_len,
uint32_t crc32);
static int write_gzip_header_stateless(struct isal_zstream *stream);
static int write_deflate_header_stateless(struct isal_zstream *stream);
static int write_deflate_header_unaligned_stateless(struct isal_zstream *stream);
static int write_trailer_stateless(struct isal_zstream *stream);
unsigned int detect_repeated_char(uint8_t * buf, uint32_t size);
#define STORED_BLK_HDR_BZ 5
#define STORED_BLK_MAX_BZ 65535
void isal_deflate_body(struct isal_zstream *stream);
void isal_deflate_finish(struct isal_zstream *stream);
/*****************************************************************/
/* Forward declarations */
static inline void reset_match_history(struct isal_zstream *stream);
void write_header(struct isal_zstream *stream);
void write_deflate_header(struct isal_zstream *stream);
void write_trailer(struct isal_zstream *stream);
struct slver {
uint16_t snum;
uint8_t ver;
uint8_t core;
};
/* Version info */
struct slver isal_deflate_init_slver_01030081;
struct slver isal_deflate_init_slver = { 0x0081, 0x03, 0x01 };
struct slver isal_deflate_stateless_init_slver_00010084;
struct slver isal_deflate_stateless_init_slver = { 0x0084, 0x01, 0x00 };
struct slver isal_deflate_slver_01030082;
struct slver isal_deflate_slver = { 0x0082, 0x03, 0x01 };
struct slver isal_deflate_stateless_slver_01010083;
struct slver isal_deflate_stateless_slver = { 0x0083, 0x01, 0x01 };
/*****************************************************************/
static
void sync_flush(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
uint64_t bits_to_write = 0xFFFF0000, bits_len;
uint64_t code = 0, len = 0, bytes;
int flush_size;
if (stream->avail_out >= 8) {
set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
if (!state->has_eob)
get_lit_code(stream->hufftables, 256, &code, &len);
flush_size = (-(state->bitbuf.m_bit_count + len + 3)) % 8;
bits_to_write <<= flush_size + 3;
bits_len = 32 + len + flush_size + 3;
#ifdef USE_BITBUFB /* Write Bits Always */
state->state = ZSTATE_NEW_HDR;
#else /* Not Write Bits Always */
state->state = ZSTATE_FLUSH_WRITE_BUFFER;
#endif
state->has_eob = 0;
if (len > 0)
bits_to_write = (bits_to_write << len) | code;
write_bits(&state->bitbuf, bits_to_write, bits_len);
bytes = buffer_used(&state->bitbuf);
stream->next_out = buffer_ptr(&state->bitbuf);
stream->avail_out -= bytes;
stream->total_out += bytes;
if (stream->flush == FULL_FLUSH) {
/* Clear match history so there are no cross
* block length distance pairs */
state->file_start -= state->b_bytes_processed;
state->b_bytes_valid -= state->b_bytes_processed;
state->b_bytes_processed = 0;
reset_match_history(stream);
}
}
}
static
void sync_flush_stateless(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
uint64_t bits_to_write = 0xFFFF0000, bits_len;
uint64_t code = 0, len = 0, bytes;
int flush_size;
if (stream->avail_out >= 8) {
set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
if (!state->has_eob)
get_lit_code(stream->hufftables, 256, &code, &len);
flush_size = (-(state->bitbuf.m_bit_count + len + 3)) % 8;
bits_to_write <<= flush_size + 3;
bits_len = 32 + len + flush_size + 3;
#ifdef USE_BITBUFB /* Write Bits Always */
state->state = ZSTATE_NEW_HDR;
#else /* Not Write Bits Always */
state->state = ZSTATE_FLUSH_WRITE_BUFFER;
#endif
state->has_eob = 0;
if (len > 0)
bits_to_write = (bits_to_write << len) | code;
write_bits(&state->bitbuf, bits_to_write, bits_len);
bytes = buffer_used(&state->bitbuf);
stream->next_out = buffer_ptr(&state->bitbuf);
stream->avail_out -= bytes;
stream->total_out += bytes;
}
}
static void flush_write_buffer(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
int bytes = 0;
if (stream->avail_out >= 8) {
set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
flush(&state->bitbuf);
stream->next_out = buffer_ptr(&state->bitbuf);
bytes = buffer_used(&state->bitbuf);
stream->avail_out -= bytes;
stream->total_out += bytes;
state->state = ZSTATE_NEW_HDR;
}
}
static void isal_deflate_pass(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
uint8_t *start_in = stream->next_in;
if (state->state == ZSTATE_NEW_HDR || state->state == ZSTATE_HDR)
write_header(stream);
if (state->state == ZSTATE_BODY)
isal_deflate_body(stream);
if (state->state == ZSTATE_FLUSH_READ_BUFFER)
isal_deflate_finish(stream);
if (state->state == ZSTATE_SYNC_FLUSH)
sync_flush(stream);
if (state->state == ZSTATE_FLUSH_WRITE_BUFFER)
flush_write_buffer(stream);
if (stream->gzip_flag)
state->crc = crc32_gzip(state->crc, start_in, stream->next_in - start_in);
if (state->state == ZSTATE_TRL)
write_trailer(stream);
}
static void isal_deflate_int(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
uint32_t size;
/* Move data from temporary output buffer to output buffer */
if (state->state >= ZSTATE_TMP_OFFSET) {
size = state->tmp_out_end - state->tmp_out_start;
if (size > stream->avail_out)
size = stream->avail_out;
memcpy(stream->next_out, state->tmp_out_buff + state->tmp_out_start, size);
stream->next_out += size;
stream->avail_out -= size;
stream->total_out += size;
state->tmp_out_start += size;
if (state->tmp_out_start == state->tmp_out_end)
state->state -= ZSTATE_TMP_OFFSET;
if (stream->avail_out == 0 || state->state == ZSTATE_END
|| state->state == ZSTATE_NEW_HDR)
return;
}
assert(state->tmp_out_start == state->tmp_out_end);
isal_deflate_pass(stream);
/* Fill temporary output buffer then complete filling output buffer */
if (stream->avail_out > 0 && stream->avail_out < 8 && state->state != ZSTATE_NEW_HDR) {
uint8_t *next_out;
uint32_t avail_out;
uint32_t total_out;
next_out = stream->next_out;
avail_out = stream->avail_out;
total_out = stream->total_out;
stream->next_out = state->tmp_out_buff;
stream->avail_out = sizeof(state->tmp_out_buff);
stream->total_out = 0;
isal_deflate_pass(stream);
state->tmp_out_start = 0;
state->tmp_out_end = stream->total_out;
stream->next_out = next_out;
stream->avail_out = avail_out;
stream->total_out = total_out;
if (state->tmp_out_end) {
size = state->tmp_out_end;
if (size > stream->avail_out)
size = stream->avail_out;
memcpy(stream->next_out, state->tmp_out_buff, size);
stream->next_out += size;
stream->avail_out -= size;
stream->total_out += size;
state->tmp_out_start += size;
if (state->tmp_out_start != state->tmp_out_end)
state->state += ZSTATE_TMP_OFFSET;
}
}
}
static uint32_t write_constant_compressed_stateless(struct isal_zstream *stream,
uint32_t repeated_char,
uint32_t repeated_length,
uint32_t end_of_stream)
{
/* Assumes repeated_length is at least 1.
* Assumes the input end_of_stream is either 0 or 1. */
struct isal_zstate *state = &stream->internal_state;
uint32_t rep_bits = ((repeated_length - 1) / 258) * 2;
uint32_t rep_bytes = rep_bits / 8;
uint32_t rep_extra = (repeated_length - 1) % 258;
uint32_t bytes;
uint8_t *start_in = stream->next_in;
/* Guarantee there is enough space for the header even in the worst case */
if (stream->avail_out < HEADER_LENGTH + MAX_FIXUP_CODE_LENGTH + rep_bytes + 8)
return STATELESS_OVERFLOW;
/* Assumes the repeated char is either 0 or 0xFF. */
memcpy(stream->next_out, repeated_char_header[repeated_char & 1], HEADER_LENGTH);
if (end_of_stream > 0) {
stream->next_out[0] |= 1;
state->has_eob_hdr = 1;
}
memset(stream->next_out + HEADER_LENGTH, 0, rep_bytes);
stream->avail_out -= HEADER_LENGTH + rep_bytes;
stream->next_out += HEADER_LENGTH + rep_bytes;
stream->total_out += HEADER_LENGTH + rep_bytes;
set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
/* These two lines are basically a modified version of init. */
state->bitbuf.m_bits = 0;
state->bitbuf.m_bit_count = rep_bits % 8;
/* Add smaller repeat codes as necessary. Code280 can describe repeat
* lengths of 115-130 bits. Code10 can describe repeat lengths of 10
* bits. If more than 230 bits, fill code with two code280s. Else if
* more than 115 repeates, fill with code10s until one code280 can
* finish the rest of the repeats. Else, fill with code10s and
* literals */
if (rep_extra > 115) {
while (rep_extra > 130 && rep_extra < 230) {
write_bits(&state->bitbuf, CODE_10, CODE_10_LENGTH);
rep_extra -= 10;
}
if (rep_extra >= 230) {
write_bits(&state->bitbuf,
CODE_280 | ((rep_extra / 2 - 115) << CODE_280_LENGTH),
CODE_280_TOTAL_LENGTH);
rep_extra -= rep_extra / 2;
}
write_bits(&state->bitbuf,
CODE_280 | ((rep_extra - 115) << CODE_280_LENGTH),
CODE_280_TOTAL_LENGTH);
} else {
while (rep_extra >= 10) {
write_bits(&state->bitbuf, CODE_10, CODE_10_LENGTH);
rep_extra -= 10;
}
for (; rep_extra > 0; rep_extra--)
write_bits(&state->bitbuf, CODE_LIT, CODE_LIT_LENGTH);
}
write_bits(&state->bitbuf, END_OF_BLOCK, END_OF_BLOCK_LEN);
state->has_eob = 1;
stream->next_in += repeated_length;
stream->avail_in -= repeated_length;
stream->total_in += repeated_length;
bytes = buffer_used(&state->bitbuf);
stream->next_out = buffer_ptr(&state->bitbuf);
stream->avail_out -= bytes;
stream->total_out += bytes;
if (stream->gzip_flag)
state->crc = crc32_gzip(state->crc, start_in, stream->next_in - start_in);
return COMP_OK;
}
int detect_repeated_char_length(uint8_t * in, uint32_t length)
{
/* This currently assumes the first 8 bytes are the same character.
* This won't work effectively if the input stream isn't aligned well. */
uint8_t *p_8, *end = in + length;
uint64_t *p_64 = (uint64_t *) in;
uint64_t w = *p_64;
uint8_t c = (uint8_t) w;
for (; (p_64 <= (uint64_t *) (end - 8)) && (w == *p_64); p_64++) ;
p_8 = (uint8_t *) p_64;
for (; (p_8 < end) && (c == *p_8); p_8++) ;
return p_8 - in;
}
static int isal_deflate_int_stateless(struct isal_zstream *stream)
{
uint32_t repeated_char_length;
struct isal_zstate *state = &stream->internal_state;
if (stream->gzip_flag == IGZIP_GZIP)
if (write_gzip_header_stateless(stream))
return STATELESS_OVERFLOW;
if (stream->avail_in >= 8
&& (*(uint64_t *) stream->next_in == 0
|| *(uint64_t *) stream->next_in == ~(uint64_t) 0))
repeated_char_length =
detect_repeated_char_length(stream->next_in, stream->avail_in);
else
repeated_char_length = 0;
if (stream->avail_in == repeated_char_length) {
if (write_constant_compressed_stateless(stream,
stream->next_in[0],
repeated_char_length,
stream->end_of_stream) != COMP_OK)
return STATELESS_OVERFLOW;
if (state->has_eob_hdr) {
if (write_trailer_stateless(stream) != COMP_OK)
return STATELESS_OVERFLOW;
} else if (stream->avail_out >= 8) {
sync_flush_stateless(stream);
#ifndef USE_BITBUFB
flush_write_buffer(stream);
if (state->bitbuf.m_bit_count != 0)
return STATELESS_OVERFLOW;
#endif
} else
return STATELESS_OVERFLOW;
return COMP_OK;
} else if (repeated_char_length >= MIN_REPEAT_LEN) {
if (write_constant_compressed_stateless
(stream, stream->next_in[0], repeated_char_length, 0) != COMP_OK)
return STATELESS_OVERFLOW;
state->has_eob = 0;
}
if (write_deflate_header_unaligned_stateless(stream) != COMP_OK)
return STATELESS_OVERFLOW;
reset_match_history(stream);
state->file_start = stream->next_in - stream->total_in;
isal_deflate_pass(stream);
if (state->state == ZSTATE_END
|| (state->state == ZSTATE_NEW_HDR && stream->flush == FULL_FLUSH))
return COMP_OK;
else
return STATELESS_OVERFLOW;
}
static int write_stored_block_stateless(struct isal_zstream *stream,
uint32_t stored_len, uint32_t crc32)
{
uint64_t stored_blk_hdr;
uint32_t copy_size;
uint32_t avail_in;
uint64_t gzip_trl;
if (stream->avail_out < stored_len)
return STATELESS_OVERFLOW;
stream->avail_out -= stored_len;
stream->total_out += stored_len;
avail_in = stream->avail_in;
if (stream->gzip_flag == IGZIP_GZIP) {
memcpy(stream->next_out, gzip_hdr, gzip_hdr_bytes);
stream->next_out += gzip_hdr_bytes;
stream->gzip_flag = IGZIP_GZIP_NO_HDR;
}
do {
if (avail_in >= STORED_BLK_MAX_BZ) {
stored_blk_hdr = 0xFFFF00;
copy_size = STORED_BLK_MAX_BZ;
} else {
stored_blk_hdr = ~avail_in;
stored_blk_hdr <<= 24;
stored_blk_hdr |= (avail_in & 0xFFFF) << 8;
copy_size = avail_in;
}
avail_in -= copy_size;
/* Handle BFINAL bit */
if (avail_in == 0) {
if (stream->flush == NO_FLUSH || stream->end_of_stream) {
stored_blk_hdr |= 0x1;
stream->internal_state.has_eob_hdr = 1;
}
}
memcpy(stream->next_out, &stored_blk_hdr, STORED_BLK_HDR_BZ);
stream->next_out += STORED_BLK_HDR_BZ;
memcpy(stream->next_out, stream->next_in, copy_size);
stream->next_out += copy_size;
stream->next_in += copy_size;
stream->total_in += copy_size;
} while (avail_in != 0);
if (stream->gzip_flag && stream->internal_state.has_eob_hdr) {
gzip_trl = stream->avail_in;
gzip_trl <<= 32;
gzip_trl |= crc32 & 0xFFFFFFFF;
memcpy(stream->next_out, &gzip_trl, gzip_trl_bytes);
stream->next_out += gzip_trl_bytes;
}
stream->avail_in = 0;
return COMP_OK;
}
static inline void reset_match_history(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
uint16_t *head = stream->internal_state.head;
int i = 0;
state->has_hist = 0;
if (stream->total_in == 0)
memset(stream->internal_state.head, 0, sizeof(stream->internal_state.head));
else {
for (i = 0; i < sizeof(state->head) / 2; i++) {
head[i] = (uint16_t) (stream->total_in);
}
}
}
void isal_deflate_init(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
stream->total_in = 0;
stream->total_out = 0;
stream->hufftables = (struct isal_hufftables *)&hufftables_default;
stream->flush = 0;
stream->gzip_flag = 0;
state->b_bytes_valid = 0;
state->b_bytes_processed = 0;
state->has_eob = 0;
state->has_eob_hdr = 0;
state->has_hist = 0;
state->state = ZSTATE_NEW_HDR;
state->count = 0;
state->tmp_out_start = 0;
state->tmp_out_end = 0;
state->file_start = stream->next_in;
init(&state->bitbuf);
state->crc = 0;
memset(state->head, 0, sizeof(state->head));
return;
}
void isal_deflate_stateless_init(struct isal_zstream *stream)
{
stream->total_in = 0;
stream->total_out = 0;
stream->hufftables = (struct isal_hufftables *)&hufftables_default;
stream->flush = NO_FLUSH;
stream->end_of_stream = 0;
stream->gzip_flag = 0;
return;
}
uint32_t crc32_gzip_base(uint32_t crc, uint8_t * start, uint32_t length)
{
uint8_t *end = start + length;
crc = ~crc;
while (start < end)
crc = (crc >> 8) ^ CrcTable[(crc & 0x000000FF) ^ *start++];
return ~crc;
}
int isal_deflate_stateless(struct isal_zstream *stream)
{
uint8_t *next_in = stream->next_in;
const uint32_t avail_in = stream->avail_in;
const uint32_t total_in = stream->total_in;
uint8_t *next_out = stream->next_out;
const uint32_t avail_out = stream->avail_out;
const uint32_t total_out = stream->total_out;
const uint32_t gzip_flag = stream->gzip_flag;
uint32_t crc32 = 0;
uint32_t stored_len;
uint32_t dyn_min_len;
uint32_t min_len;
uint32_t select_stored_blk = 0;
/* Final block has already been written */
stream->internal_state.has_eob_hdr = 0;
init(&stream->internal_state.bitbuf);
stream->internal_state.state = ZSTATE_NEW_HDR;
stream->internal_state.crc = 0;
if (stream->flush == NO_FLUSH)
stream->end_of_stream = 1;
if (stream->flush != NO_FLUSH && stream->flush != FULL_FLUSH)
return INVALID_FLUSH;
if (avail_in == 0)
stored_len = STORED_BLK_HDR_BZ;
else
stored_len =
STORED_BLK_HDR_BZ * ((avail_in + STORED_BLK_MAX_BZ - 1) /
STORED_BLK_MAX_BZ) + avail_in;
/*
at least 1 byte compressed data in the case of empty dynamic block which only
contains the EOB
*/
dyn_min_len = stream->hufftables->deflate_hdr_count + 1;
if (stream->gzip_flag == IGZIP_GZIP) {
dyn_min_len += gzip_hdr_bytes + gzip_trl_bytes + 1;
stored_len += gzip_hdr_bytes + gzip_trl_bytes;
} else if (stream->gzip_flag == IGZIP_GZIP_NO_HDR) {
dyn_min_len += gzip_trl_bytes + 1;
stored_len += gzip_trl_bytes;
}
min_len = dyn_min_len;
if (stored_len < dyn_min_len) {
min_len = stored_len;
select_stored_blk = 1;
}
/*
the output buffer should be no less than 8 bytes
while empty stored deflate block is 5 bytes only
*/
if (avail_out < min_len || stream->avail_out < 8)
return STATELESS_OVERFLOW;
if (!select_stored_blk) {
if (isal_deflate_int_stateless(stream) == COMP_OK)
return COMP_OK;
else {
if (stream->flush == FULL_FLUSH) {
stream->internal_state.file_start =
(uint8_t *) & stream->internal_state.buffer;
reset_match_history(stream);
}
stream->internal_state.has_eob_hdr = 0;
}
}
if (avail_out < stored_len)
return STATELESS_OVERFLOW;
stream->next_in = next_in;
stream->avail_in = avail_in;
stream->total_in = total_in;
stream->next_out = next_out;
stream->avail_out = avail_out;
stream->total_out = total_out;
stream->gzip_flag = gzip_flag;
if (stream->gzip_flag)
crc32 = crc32_gzip(0x0, next_in, avail_in);
return write_stored_block_stateless(stream, stored_len, crc32);
}
int isal_deflate(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
int ret = COMP_OK;
uint8_t *next_in;
uint32_t avail_in, avail_in_start;
uint32_t flush_type = stream->flush;
uint32_t end_of_stream = stream->end_of_stream;
int size = 0;
uint8_t *copy_down_src = NULL;
uint64_t copy_down_size = 0;
uint32_t processed = 0;
if (stream->flush >= 3)
return INVALID_FLUSH;
next_in = stream->next_in;
avail_in = stream->avail_in;
stream->total_in -= state->b_bytes_valid - state->b_bytes_processed;
while (processed < IGZIP_HIST_SIZE + ISAL_LOOK_AHEAD) {
size = avail_in;
if (size > sizeof(state->buffer) - state->b_bytes_valid) {
size = sizeof(state->buffer) - state->b_bytes_valid;
stream->flush = NO_FLUSH;
stream->end_of_stream = 0;
}
memcpy(&state->buffer[state->b_bytes_valid], next_in, size);
next_in += size;
avail_in -= size;
state->b_bytes_valid += size;
stream->next_in = &state->buffer[state->b_bytes_processed];
stream->avail_in = state->b_bytes_valid - state->b_bytes_processed;
state->file_start = stream->next_in - stream->total_in;
if (stream->avail_in > IGZIP_HIST_SIZE
|| stream->end_of_stream || stream->flush != NO_FLUSH) {
avail_in_start = stream->avail_in;
isal_deflate_int(stream);
state->b_bytes_processed += avail_in_start - stream->avail_in;
if (state->b_bytes_processed > IGZIP_HIST_SIZE) {
copy_down_src =
&state->buffer[state->b_bytes_processed - IGZIP_HIST_SIZE];
copy_down_size =
state->b_bytes_valid - state->b_bytes_processed +
IGZIP_HIST_SIZE;
memmove(state->buffer, copy_down_src, copy_down_size);
state->b_bytes_valid -= copy_down_src - state->buffer;
state->b_bytes_processed -= copy_down_src - state->buffer;
}
}
stream->flush = flush_type;
stream->end_of_stream = end_of_stream;
if (avail_in <= 0 || stream->avail_out <= 0)
break;
processed += size;
}
if (processed >= IGZIP_HIST_SIZE + ISAL_LOOK_AHEAD) {
stream->next_in = next_in - stream->avail_in;
stream->avail_in = avail_in + stream->avail_in;
state->file_start = stream->next_in - stream->total_in;
if (stream->avail_in > 0 && stream->avail_out > 0)
isal_deflate_int(stream);
size = stream->avail_in;
if (stream->avail_in > IGZIP_HIST_SIZE)
size = 0;
memmove(state->buffer, stream->next_in - IGZIP_HIST_SIZE,
size + IGZIP_HIST_SIZE);
state->b_bytes_processed = IGZIP_HIST_SIZE;
state->b_bytes_valid = size + IGZIP_HIST_SIZE;
stream->next_in += size;
stream->avail_in -= size;
stream->total_in += size;
} else {
stream->total_in += state->b_bytes_valid - state->b_bytes_processed;
stream->next_in = next_in;
stream->avail_in = avail_in;
state->file_start = stream->next_in - stream->total_in;
}
return ret;
}
static int write_gzip_header_stateless(struct isal_zstream *stream)
{
if (gzip_hdr_bytes >= stream->avail_out)
return STATELESS_OVERFLOW;
stream->avail_out -= gzip_hdr_bytes;
stream->total_out += gzip_hdr_bytes;
memcpy(stream->next_out, gzip_hdr, gzip_hdr_bytes);
stream->next_out += gzip_hdr_bytes;
stream->gzip_flag = IGZIP_GZIP_NO_HDR;
return COMP_OK;
}
static void write_gzip_header(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
int bytes_to_write = gzip_hdr_bytes;
bytes_to_write -= state->count;
if (bytes_to_write > stream->avail_out)
bytes_to_write = stream->avail_out;
memcpy(stream->next_out, gzip_hdr + state->count, bytes_to_write);
state->count += bytes_to_write;
if (state->count == gzip_hdr_bytes) {
state->count = 0;
stream->gzip_flag = IGZIP_GZIP_NO_HDR;
}
stream->avail_out -= bytes_to_write;
stream->total_out += bytes_to_write;
stream->next_out += bytes_to_write;
}
static int write_deflate_header_stateless(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
struct isal_hufftables *hufftables = stream->hufftables;
uint32_t count;
if (hufftables->deflate_hdr_count + 8 >= stream->avail_out)
return STATELESS_OVERFLOW;
memcpy(stream->next_out, hufftables->deflate_hdr, hufftables->deflate_hdr_count);
if (stream->end_of_stream == 0)
*stream->next_out -= 1;
else
state->has_eob_hdr = 1;
stream->avail_out -= hufftables->deflate_hdr_count;
stream->total_out += hufftables->deflate_hdr_count;
stream->next_out += hufftables->deflate_hdr_count;
set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
write_bits(&state->bitbuf, hufftables->deflate_hdr[hufftables->deflate_hdr_count],
hufftables->deflate_hdr_extra_bits);
count = buffer_used(&state->bitbuf);
stream->next_out = buffer_ptr(&state->bitbuf);
stream->avail_out -= count;
stream->total_out += count;
state->state = ZSTATE_BODY;
return COMP_OK;
}
static int write_deflate_header_unaligned_stateless(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
struct isal_hufftables *hufftables = stream->hufftables;
unsigned int count;
uint64_t bit_count;
uint64_t *header_next;
uint64_t *header_end;
uint64_t header_bits;
if (state->bitbuf.m_bit_count == 0)
return write_deflate_header_stateless(stream);
if (hufftables->deflate_hdr_count + 16 >= stream->avail_out)
return STATELESS_OVERFLOW;
set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
header_next = (uint64_t *) hufftables->deflate_hdr;
header_end = header_next + hufftables->deflate_hdr_count / 8;
header_bits = *header_next;
if (stream->end_of_stream == 0)
header_bits--;
else
state->has_eob_hdr = 1;
write_bits(&state->bitbuf, header_bits, 32);
header_bits >>= 32;
write_bits(&state->bitbuf, header_bits, 32);
header_next++;
/* Write out Complete Header bits */
for (; header_next < header_end; header_next++) {
header_bits = *header_next;
write_bits(&state->bitbuf, header_bits, 32);
header_bits >>= 32;
write_bits(&state->bitbuf, header_bits, 32);
}
header_bits = *header_next;
bit_count =
(hufftables->deflate_hdr_count & 0x7) * 8 + hufftables->deflate_hdr_extra_bits;
if (bit_count > MAX_BITBUF_BIT_WRITE) {
write_bits(&state->bitbuf, header_bits, MAX_BITBUF_BIT_WRITE);
header_bits >>= MAX_BITBUF_BIT_WRITE;
bit_count -= MAX_BITBUF_BIT_WRITE;
}
write_bits(&state->bitbuf, header_bits, bit_count);
/* check_space flushes extra bytes in bitbuf. Required because
* write_bits_always fails when the next commit makes the buffer
* length exceed 64 bits */
check_space(&state->bitbuf, FORCE_FLUSH);
count = buffer_used(&state->bitbuf);
stream->next_out = buffer_ptr(&state->bitbuf);
stream->avail_out -= count;
stream->total_out += count;
state->state = ZSTATE_BODY;
return COMP_OK;
}
void write_header(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
struct isal_hufftables *hufftables = stream->hufftables;
uint32_t count;
state->state = ZSTATE_HDR;
if (state->bitbuf.m_bit_count != 0) {
if (stream->avail_out < 8)
return;
set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
flush(&state->bitbuf);
count = buffer_used(&state->bitbuf);
stream->next_out = buffer_ptr(&state->bitbuf);
stream->avail_out -= count;
stream->total_out += count;
}
if (stream->gzip_flag == IGZIP_GZIP)
write_gzip_header(stream);
count = hufftables->deflate_hdr_count - state->count;
if (count != 0) {
if (count > stream->avail_out)
count = stream->avail_out;
memcpy(stream->next_out, hufftables->deflate_hdr + state->count, count);
if (state->count == 0 && count > 0) {
if (!stream->end_of_stream)
*stream->next_out -= 1;
else
state->has_eob_hdr = 1;
}
stream->next_out += count;
stream->avail_out -= count;
stream->total_out += count;
state->count += count;
count = hufftables->deflate_hdr_count - state->count;
}
if ((count == 0) && (stream->avail_out >= 8)) {
set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
write_bits(&state->bitbuf,
hufftables->deflate_hdr[hufftables->deflate_hdr_count],
hufftables->deflate_hdr_extra_bits);
state->state = ZSTATE_BODY;
state->count = 0;
count = buffer_used(&state->bitbuf);
stream->next_out = buffer_ptr(&state->bitbuf);
stream->avail_out -= count;
stream->total_out += count;
}
}
void write_trailer(struct isal_zstream *stream)
{
struct isal_zstate *state = &stream->internal_state;
unsigned int bytes;
uint32_t crc = state->crc;
if (stream->avail_out >= 8) {
set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
/* the flush() will pad to the next byte and write up to 8 bytes
* to the output stream/buffer.
*/
if (!state->has_eob_hdr) {
/* If the final header has not been written, write a
* final block. This block is a static huffman block
* which only contains the end of block symbol. The code
* that happens to do this is the fist 10 bits of
* 0x003 */
state->has_eob_hdr = 1;
write_bits(&state->bitbuf, 0x003, 10);
if (is_full(&state->bitbuf)) {
stream->next_out = buffer_ptr(&state->bitbuf);
bytes = buffer_used(&state->bitbuf);
stream->avail_out -= bytes;
stream->total_out += bytes;
return;
}
}
flush(&state->bitbuf);
stream->next_out = buffer_ptr(&state->bitbuf);
bytes = buffer_used(&state->bitbuf);
if (stream->gzip_flag) {
if (!is_full(&state->bitbuf)) {
*(uint64_t *) stream->next_out =
((uint64_t) stream->total_in << 32) | crc;
stream->next_out += 8;
bytes += 8;
state->state = ZSTATE_END;
}
} else
state->state = ZSTATE_END;
stream->avail_out -= bytes;
stream->total_out += bytes;
}
}
static int write_trailer_stateless(struct isal_zstream *stream)
{
int ret = COMP_OK;
struct isal_zstate *state = &stream->internal_state;
unsigned int bytes;
if (stream->avail_out < 8) {
ret = STATELESS_OVERFLOW;
} else {
set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
/* the flush() will pad to the next byte and write up to 8 bytes
* to the output stream/buffer.
*/
flush(&state->bitbuf);
stream->next_out = buffer_ptr(&state->bitbuf);
bytes = buffer_used(&state->bitbuf);
if (stream->gzip_flag) {
if (is_full(&state->bitbuf)) {
ret = STATELESS_OVERFLOW;
} else {
*(uint64_t *) stream->next_out =
((uint64_t) stream->total_in << 32) | state->crc;
stream->next_out += 8;
bytes += 8;
}
}
stream->avail_out -= bytes;
stream->total_out += bytes;
}
return ret;
}