summaryrefslogtreecommitdiffstats
path: root/src/isa-l/igzip/igzip.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/isa-l/igzip/igzip.c')
-rw-r--r--src/isa-l/igzip/igzip.c2022
1 files changed, 2022 insertions, 0 deletions
diff --git a/src/isa-l/igzip/igzip.c b/src/isa-l/igzip/igzip.c
new file mode 100644
index 000000000..65b441b52
--- /dev/null
+++ b/src/isa-l/igzip/igzip.c
@@ -0,0 +1,2022 @@
+/**********************************************************************
+ 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>
+#include <wchar.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 "crc.h"
+#include "repeated_char_result.h"
+#include "huff_codes.h"
+#include "encode_df.h"
+#include "igzip_level_buf_structs.h"
+#include "igzip_checksums.h"
+#include "igzip_wrapper.h"
+#include "unaligned.h"
+
+#ifdef __FreeBSD__
+#include <sys/types.h>
+#include <sys/endian.h>
+# define to_be32(x) bswap32(x)
+#elif defined (__APPLE__)
+#include <libkern/OSByteOrder.h>
+# define to_be32(x) OSSwapInt32(x)
+#elif defined (__GNUC__) && !defined (__MINGW32__)
+# include <byteswap.h>
+# define to_be32(x) bswap_32(x)
+#elif defined _WIN64
+# define to_be32(x) _byteswap_ulong(x)
+#endif
+
+extern void isal_deflate_hash_lvl0(uint16_t *, uint32_t, uint32_t, uint8_t *, uint32_t);
+extern void isal_deflate_hash_lvl1(uint16_t *, uint32_t, uint32_t, uint8_t *, uint32_t);
+extern void isal_deflate_hash_lvl2(uint16_t *, uint32_t, uint32_t, uint8_t *, uint32_t);
+extern void isal_deflate_hash_lvl3(uint16_t *, uint32_t, uint32_t, uint8_t *, uint32_t);
+extern const uint8_t gzip_hdr[];
+extern const uint32_t gzip_hdr_bytes;
+extern const uint32_t gzip_trl_bytes;
+extern const uint8_t zlib_hdr[];
+extern const uint32_t zlib_hdr_bytes;
+extern const uint32_t zlib_trl_bytes;
+extern const struct isal_hufftables hufftables_default;
+extern const struct isal_hufftables hufftables_static;
+
+static uint32_t write_stored_block(struct isal_zstream *stream);
+
+static int write_stream_header_stateless(struct isal_zstream *stream);
+static void write_stream_header(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);
+
+#define TYPE0_HDR_LEN 4
+#define TYPE0_BLK_HDR_LEN 5
+#define TYPE0_MAX_BLK_LEN 65535
+
+void isal_deflate_body(struct isal_zstream *stream);
+void isal_deflate_finish(struct isal_zstream *stream);
+
+void isal_deflate_icf_body(struct isal_zstream *stream);
+void isal_deflate_icf_finish_lvl1(struct isal_zstream *stream);
+void isal_deflate_icf_finish_lvl2(struct isal_zstream *stream);
+void isal_deflate_icf_finish_lvl3(struct isal_zstream *stream);
+/*****************************************************************/
+
+/* Forward declarations */
+static inline void reset_match_history(struct isal_zstream *stream);
+static void write_header(struct isal_zstream *stream, uint8_t * deflate_hdr,
+ uint32_t deflate_hdr_count, uint32_t extra_bits_count,
+ uint32_t next_state, uint32_t toggle_end_of_stream);
+static 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_reset_slver_0001008e;
+struct slver isal_deflate_reset_slver = { 0x008e, 0x01, 0x00 };
+
+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 };
+
+struct slver isal_deflate_set_hufftables_slver_0001008b;
+struct slver isal_deflate_set_hufftables_slver = { 0x008b, 0x01, 0x00 };
+
+struct slver isal_deflate_set_dict_slver_0001008c;
+struct slver isal_deflate_set_dict_slver = { 0x008c, 0x01, 0x00 };
+
+/*****************************************************************/
+
+// isal_adler32_bam1 - adler with (B | A minus 1) storage
+
+uint32_t isal_adler32_bam1(uint32_t adler32, const unsigned char *start, uint64_t length)
+{
+ uint64_t a;
+
+ /* Internally the checksum is being stored as B | (A-1) so crc and
+ * addler have same init value */
+ a = adler32 & 0xffff;
+ a = (a == ADLER_MOD - 1) ? 0 : a + 1;
+ adler32 = isal_adler32((adler32 & 0xffff0000) | a, start, length);
+ a = (adler32 & 0xffff);
+ a = (a == 0) ? ADLER_MOD - 1 : a - 1;
+
+ return (adler32 & 0xffff0000) | a;
+}
+
+static void update_checksum(struct isal_zstream *stream, uint8_t * start_in, uint64_t length)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ switch (stream->gzip_flag) {
+ case IGZIP_GZIP:
+ case IGZIP_GZIP_NO_HDR:
+ state->crc = crc32_gzip_refl(state->crc, start_in, length);
+ break;
+ case IGZIP_ZLIB:
+ case IGZIP_ZLIB_NO_HDR:
+ state->crc = isal_adler32_bam1(state->crc, start_in, length);
+ break;
+ }
+}
+
+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 bytes;
+ int flush_size;
+
+ if (stream->avail_out >= 8) {
+ set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
+
+ flush_size = (-(state->bitbuf.m_bit_count + 3)) % 8;
+
+ bits_to_write <<= flush_size + 3;
+ bits_len = 32 + flush_size + 3;
+
+ state->state = ZSTATE_NEW_HDR;
+ state->has_eob = 0;
+
+ 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->has_hist = IGZIP_NO_HIST;
+ }
+ }
+}
+
+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 flush_icf_block(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ struct level_buf *level_buf = (struct level_buf *)stream->level_buf;
+ struct BitBuf2 *write_buf = &state->bitbuf;
+ struct deflate_icf *icf_buf_encoded_next;
+
+ set_buf(write_buf, stream->next_out, stream->avail_out);
+
+ icf_buf_encoded_next = encode_deflate_icf(level_buf->icf_buf_start + state->count,
+ level_buf->icf_buf_next, write_buf,
+ &level_buf->encode_tables);
+
+ state->count = icf_buf_encoded_next - level_buf->icf_buf_start;
+ stream->next_out = buffer_ptr(write_buf);
+ stream->total_out += buffer_used(write_buf);
+ stream->avail_out -= buffer_used(write_buf);
+
+ if (level_buf->icf_buf_next <= icf_buf_encoded_next) {
+ state->count = 0;
+ if (stream->avail_in == 0 && stream->end_of_stream)
+ state->state = ZSTATE_TRL;
+ else if (stream->avail_in == 0 && stream->flush != NO_FLUSH)
+ state->state = ZSTATE_SYNC_FLUSH;
+ else
+ state->state = ZSTATE_NEW_HDR;
+ }
+}
+
+static int check_level_req(struct isal_zstream *stream)
+{
+ if (stream->level == 0)
+ return 0;
+
+ if (stream->level_buf == NULL)
+ return ISAL_INVALID_LEVEL_BUF;
+
+ switch (stream->level) {
+ case 3:
+ if (stream->level_buf_size < ISAL_DEF_LVL3_MIN)
+ return ISAL_INVALID_LEVEL;
+ break;
+
+ case 2:
+ if (stream->level_buf_size < ISAL_DEF_LVL2_MIN)
+ return ISAL_INVALID_LEVEL;
+ break;
+ case 1:
+ if (stream->level_buf_size < ISAL_DEF_LVL1_MIN)
+ return ISAL_INVALID_LEVEL;
+ break;
+ default:
+ return ISAL_INVALID_LEVEL;
+ }
+
+ return 0;
+}
+
+static int init_hash8k_buf(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ struct level_buf *level_buf = (struct level_buf *)stream->level_buf;
+ state->has_level_buf_init = 1;
+ return sizeof(struct level_buf) - MAX_LVL_BUF_SIZE + sizeof(level_buf->hash8k);
+}
+
+static int init_hash_hist_buf(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ struct level_buf *level_buf = (struct level_buf *)stream->level_buf;
+ state->has_level_buf_init = 1;
+ return sizeof(struct level_buf) - MAX_LVL_BUF_SIZE + sizeof(level_buf->hash_hist);
+}
+
+static int init_hash_map_buf(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ struct level_buf *level_buf = (struct level_buf *)stream->level_buf;
+ if (!state->has_level_buf_init) {
+ level_buf->hash_map.matches_next = level_buf->hash_map.matches;
+ level_buf->hash_map.matches_end = level_buf->hash_map.matches;
+ }
+ state->has_level_buf_init = 1;
+
+ return sizeof(struct level_buf) - MAX_LVL_BUF_SIZE + sizeof(level_buf->hash_map);
+
+}
+
+/* returns the size of the level specific buffer */
+static int init_lvlX_buf(struct isal_zstream *stream)
+{
+ switch (stream->level) {
+ case 3:
+ return init_hash_map_buf(stream);
+ case 2:
+ return init_hash_hist_buf(stream);
+ default:
+ return init_hash8k_buf(stream);
+ }
+
+}
+
+static void init_new_icf_block(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ struct level_buf *level_buf = (struct level_buf *)stream->level_buf;
+ int level_struct_size;
+
+ level_struct_size = init_lvlX_buf(stream);
+
+ state->block_next = state->block_end;
+ level_buf->icf_buf_start =
+ (struct deflate_icf *)(stream->level_buf + level_struct_size);
+
+ level_buf->icf_buf_next = level_buf->icf_buf_start;
+ level_buf->icf_buf_avail_out =
+ stream->level_buf_size - level_struct_size - sizeof(struct deflate_icf);
+
+ memset(&level_buf->hist, 0, sizeof(struct isal_mod_hist));
+ state->state = ZSTATE_BODY;
+}
+
+static int are_buffers_empty_hashX(struct isal_zstream *stream)
+{
+ return !stream->avail_in;
+}
+
+static int are_buffers_empty_hash_map(struct isal_zstream *stream)
+{
+ struct level_buf *level_buf = (struct level_buf *)stream->level_buf;
+
+ return (!stream->avail_in
+ && level_buf->hash_map.matches_next >= level_buf->hash_map.matches_end);
+}
+
+static int are_buffers_empty(struct isal_zstream *stream)
+{
+
+ switch (stream->level) {
+ case 3:
+ return are_buffers_empty_hash_map(stream);
+ case 2:
+ return are_buffers_empty_hashX(stream);
+ default:
+ return are_buffers_empty_hashX(stream);
+ }
+}
+
+static void create_icf_block_hdr(struct isal_zstream *stream, uint8_t * start_in)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ struct level_buf *level_buf = (struct level_buf *)stream->level_buf;
+ struct BitBuf2 *write_buf = &state->bitbuf;
+ struct BitBuf2 write_buf_tmp;
+ uint32_t out_size = stream->avail_out;
+ uint32_t avail_output, block_start_offset;
+ uint8_t *end_out = stream->next_out + out_size;
+ uint64_t cur_in_processed;
+ uint64_t bit_count;
+ uint64_t block_in_size = state->block_end - state->block_next;
+ uint64_t block_size;
+ int buffer_header = 0;
+
+ memcpy(&write_buf_tmp, write_buf, sizeof(struct BitBuf2));
+
+ /* Calculate the bytes required to store a type 0 block. Need to account
+ * for bits stored in the bitbuf. Since 3 bits correspond to the deflate
+ * type 0 header, we need to add one byte more when the number of bits
+ * is at least 6 mod 8. */
+ block_size = (TYPE0_BLK_HDR_LEN) * ((block_in_size + TYPE0_MAX_BLK_LEN - 1) /
+ TYPE0_MAX_BLK_LEN) + block_in_size;
+ block_size = block_size ? block_size : TYPE0_BLK_HDR_LEN;
+ block_size += (write_buf->m_bit_count + 2) / 8;
+
+ /* Write EOB in icf_buf */
+ level_buf->hist.ll_hist[256] = 1;
+ level_buf->icf_buf_next->lit_len = 0x100;
+ level_buf->icf_buf_next->lit_dist = NULL_DIST_SYM;
+ level_buf->icf_buf_next->dist_extra = 0;
+ level_buf->icf_buf_next++;
+
+ state->has_eob_hdr = (stream->end_of_stream && are_buffers_empty(stream)) ? 1 : 0;
+
+ if (end_out - stream->next_out >= ISAL_DEF_MAX_HDR_SIZE) {
+ /* Assumes ISAL_DEF_MAX_HDR_SIZE is large enough to contain a
+ * max length header and a gzip header */
+ if (stream->gzip_flag == IGZIP_GZIP || stream->gzip_flag == IGZIP_ZLIB)
+ write_stream_header_stateless(stream);
+ set_buf(write_buf, stream->next_out, stream->avail_out);
+ buffer_header = 0;
+
+ } else {
+ /* Start writing into temporary buffer */
+ set_buf(write_buf, level_buf->deflate_hdr, ISAL_DEF_MAX_HDR_SIZE);
+ buffer_header = 1;
+ }
+
+ bit_count = create_hufftables_icf(write_buf, &level_buf->encode_tables,
+ &level_buf->hist, state->has_eob_hdr);
+
+ /* Assumes that type 0 block has size less than 4G */
+ block_start_offset = (stream->total_in - state->block_next);
+ cur_in_processed = stream->next_in - start_in;
+ avail_output = stream->avail_out + sizeof(state->buffer) -
+ (stream->total_in - state->block_end);
+
+ if (bit_count / 8 >= block_size && cur_in_processed >= block_start_offset
+ && block_size <= avail_output) {
+ /* Reset stream for writing out a type0 block */
+ state->has_eob_hdr = 0;
+ memcpy(write_buf, &write_buf_tmp, sizeof(struct BitBuf2));
+ state->state = ZSTATE_TYPE0_HDR;
+
+ } else if (buffer_header) {
+ /* Setup stream to write out a buffered header */
+ level_buf->deflate_hdr_count = buffer_used(write_buf);
+ level_buf->deflate_hdr_extra_bits = write_buf->m_bit_count;
+ flush(write_buf);
+ memcpy(write_buf, &write_buf_tmp, sizeof(struct BitBuf2));
+ write_buf->m_bits = 0;
+ write_buf->m_bit_count = 0;
+ state->state = ZSTATE_HDR;
+
+ } else {
+ stream->next_out = buffer_ptr(write_buf);
+ stream->total_out += buffer_used(write_buf);
+ stream->avail_out -= buffer_used(write_buf);
+ state->state = ZSTATE_FLUSH_ICF_BUFFER;
+ }
+}
+
+static void isal_deflate_pass(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ struct isal_hufftables *hufftables = stream->hufftables;
+ uint8_t *start_in = stream->next_in;
+
+ if (state->state == ZSTATE_NEW_HDR || state->state == ZSTATE_HDR) {
+ if (state->count == 0)
+ /* Assume the final header is being written since the header
+ * stored in hufftables is the final header. */
+ state->has_eob_hdr = 1;
+ write_header(stream, hufftables->deflate_hdr, hufftables->deflate_hdr_count,
+ hufftables->deflate_hdr_extra_bits, ZSTATE_BODY,
+ !stream->end_of_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)
+ update_checksum(stream, start_in, stream->next_in - start_in);
+
+ if (state->state == ZSTATE_TRL)
+ write_trailer(stream);
+}
+
+static void isal_deflate_icf_finish(struct isal_zstream *stream)
+{
+ switch (stream->level) {
+ case 3:
+ isal_deflate_icf_finish_lvl3(stream);
+ break;
+ case 2:
+ isal_deflate_icf_finish_lvl2(stream);
+ break;
+ default:
+ isal_deflate_icf_finish_lvl1(stream);
+ }
+}
+
+static void isal_deflate_icf_pass(struct isal_zstream *stream, uint8_t * inbuf_start)
+{
+ uint8_t *start_in = stream->next_in;
+ struct isal_zstate *state = &stream->internal_state;
+ struct level_buf *level_buf = (struct level_buf *)stream->level_buf;
+
+ do {
+ if (state->state == ZSTATE_NEW_HDR)
+ init_new_icf_block(stream);
+
+ if (state->state == ZSTATE_BODY)
+ isal_deflate_icf_body(stream);
+
+ if (state->state == ZSTATE_FLUSH_READ_BUFFER)
+ isal_deflate_icf_finish(stream);
+
+ if (state->state == ZSTATE_CREATE_HDR)
+ create_icf_block_hdr(stream, inbuf_start);
+
+ if (state->state == ZSTATE_HDR)
+ /* Note that the header may be prepended by the
+ * remaining bits in the previous block, as such the
+ * toggle header flag cannot be used */
+ write_header(stream, level_buf->deflate_hdr,
+ level_buf->deflate_hdr_count,
+ level_buf->deflate_hdr_extra_bits,
+ ZSTATE_FLUSH_ICF_BUFFER, 0);
+
+ if (state->state == ZSTATE_FLUSH_ICF_BUFFER)
+ flush_icf_block(stream);
+
+ if (state->state == ZSTATE_TYPE0_HDR || state->state == ZSTATE_TYPE0_BODY) {
+ if (stream->gzip_flag == IGZIP_GZIP || stream->gzip_flag == IGZIP_ZLIB)
+ write_stream_header(stream);
+ write_stored_block(stream);
+ }
+
+ }
+ while (state->state == ZSTATE_NEW_HDR);
+
+ if (state->state == ZSTATE_SYNC_FLUSH)
+ sync_flush(stream);
+
+ if (state->state == ZSTATE_FLUSH_WRITE_BUFFER)
+ flush_write_buffer(stream);
+
+ if (stream->gzip_flag)
+ update_checksum(stream, start_in, stream->next_in - start_in);
+
+ if (state->state == ZSTATE_TRL)
+ write_trailer(stream);
+}
+
+static void isal_deflate_int(struct isal_zstream *stream, uint8_t * start_in)
+{
+ 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
+ // or do not write out empty blocks since the outbuffer was processed
+ || (state->state == ZSTATE_NEW_HDR && stream->avail_out == 0))
+ return;
+ }
+ assert(state->tmp_out_start == state->tmp_out_end);
+
+ if (stream->level == 0)
+ isal_deflate_pass(stream);
+ else
+ isal_deflate_icf_pass(stream, start_in);
+
+ /* 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;
+
+ if (stream->level == 0)
+ isal_deflate_pass(stream);
+ else
+ isal_deflate_icf_pass(stream, start_in);
+
+ 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 void write_constant_compressed_stateless(struct isal_zstream *stream,
+ uint32_t repeated_length)
+{
+ /* 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;
+ uint32_t repeated_char = *stream->next_in;
+ 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;
+
+ /* Assumes the repeated char is either 0 or 0xFF. */
+ memcpy(stream->next_out, repeated_char_header[repeated_char & 1], HEADER_LENGTH);
+
+ if (stream->avail_in == repeated_length && stream->end_of_stream > 0) {
+ stream->next_out[0] |= 1;
+ state->has_eob_hdr = 1;
+ state->has_eob = 1;
+ state->state = ZSTATE_TRL;
+ } else {
+ state->state = ZSTATE_NEW_HDR;
+ }
+
+ 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);
+
+ stream->next_in += repeated_length;
+ stream->avail_in -= repeated_length;
+ stream->total_in += repeated_length;
+ state->block_end += 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)
+ update_checksum(stream, start_in, stream->next_in - start_in);
+
+ return;
+}
+
+static 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 repeat_length;
+ struct isal_zstate *state = &stream->internal_state;
+
+ if (stream->gzip_flag == IGZIP_GZIP || stream->gzip_flag == IGZIP_ZLIB)
+ if (write_stream_header_stateless(stream))
+ return STATELESS_OVERFLOW;
+
+ if (stream->avail_in >= 8
+ && (load_u64(stream->next_in) == 0
+ || load_u64(stream->next_in) == ~(uint64_t) 0)) {
+ repeat_length = detect_repeated_char_length(stream->next_in, stream->avail_in);
+
+ if (stream->avail_in == repeat_length || repeat_length >= MIN_REPEAT_LEN)
+ write_constant_compressed_stateless(stream, repeat_length);
+ }
+
+ if (stream->level == 0) {
+ if (state->state == ZSTATE_NEW_HDR || state->state == ZSTATE_HDR) {
+ write_deflate_header_unaligned_stateless(stream);
+ if (state->state == ZSTATE_NEW_HDR || state->state == ZSTATE_HDR)
+ return STATELESS_OVERFLOW;
+
+ reset_match_history(stream);
+ }
+
+ isal_deflate_pass(stream);
+
+ } else if (stream->level <= ISAL_DEF_MAX_LEVEL) {
+ if (state->state == ZSTATE_NEW_HDR || state->state == ZSTATE_HDR)
+ reset_match_history(stream);
+
+ state->count = 0;
+ isal_deflate_icf_pass(stream, stream->next_in);
+
+ }
+
+ if (state->state == ZSTATE_END
+ || (state->state == ZSTATE_NEW_HDR && stream->flush == FULL_FLUSH))
+ return COMP_OK;
+ else
+ return STATELESS_OVERFLOW;
+}
+
+static void write_type0_header(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ uint64_t stored_blk_hdr;
+ uint32_t copy_size;
+ uint32_t memcpy_len, avail_in;
+ uint32_t block_in_size = state->block_end - state->block_next;
+ uint32_t block_next_offset;
+ struct BitBuf2 *bitbuf = &stream->internal_state.bitbuf;
+
+ if (block_in_size > TYPE0_MAX_BLK_LEN) {
+ stored_blk_hdr = 0xFFFF;
+ copy_size = TYPE0_MAX_BLK_LEN;
+ } else {
+ stored_blk_hdr = ~block_in_size;
+ stored_blk_hdr <<= 16;
+ stored_blk_hdr |= (block_in_size & 0xFFFF);
+ copy_size = block_in_size;
+
+ /* Handle BFINAL bit */
+ block_next_offset = stream->total_in - state->block_next;
+ avail_in = stream->avail_in + block_next_offset;
+ if (stream->end_of_stream && avail_in == block_in_size)
+ stream->internal_state.has_eob_hdr = 1;
+ }
+
+ if (bitbuf->m_bit_count == 0 && stream->avail_out >= TYPE0_HDR_LEN + 1) {
+ stored_blk_hdr = stored_blk_hdr << 8;
+ stored_blk_hdr |= stream->internal_state.has_eob_hdr;
+ memcpy_len = TYPE0_HDR_LEN + 1;
+ memcpy(stream->next_out, &stored_blk_hdr, memcpy_len);
+ } else if (stream->avail_out >= 8) {
+ set_buf(bitbuf, stream->next_out, stream->avail_out);
+ write_bits_flush(bitbuf, stream->internal_state.has_eob_hdr, 3);
+ stream->next_out = buffer_ptr(bitbuf);
+ stream->total_out += buffer_used(bitbuf);
+ stream->avail_out -= buffer_used(bitbuf);
+ memcpy_len = TYPE0_HDR_LEN;
+ memcpy(stream->next_out, &stored_blk_hdr, memcpy_len);
+ } else {
+ stream->internal_state.has_eob_hdr = 0;
+ return;
+ }
+
+ stream->next_out += memcpy_len;
+ stream->avail_out -= memcpy_len;
+ stream->total_out += memcpy_len;
+ stream->internal_state.state = ZSTATE_TYPE0_BODY;
+
+ stream->internal_state.count = copy_size;
+}
+
+static uint32_t write_stored_block(struct isal_zstream *stream)
+{
+ uint32_t copy_size, avail_in, block_next_offset;
+ uint8_t *next_in;
+ struct isal_zstate *state = &stream->internal_state;
+
+ do {
+ if (state->state == ZSTATE_TYPE0_HDR) {
+ write_type0_header(stream);
+ if (state->state == ZSTATE_TYPE0_HDR)
+ break;
+ }
+
+ assert(state->count <= state->block_end - state->block_next);
+ copy_size = state->count;
+
+ block_next_offset = stream->total_in - state->block_next;
+ next_in = stream->next_in - block_next_offset;
+ avail_in = stream->avail_in + block_next_offset;
+
+ if (copy_size > stream->avail_out || copy_size > avail_in) {
+ state->count = copy_size;
+ copy_size = (stream->avail_out <= avail_in) ?
+ stream->avail_out : avail_in;
+
+ memcpy(stream->next_out, next_in, copy_size);
+ state->count -= copy_size;
+ } else {
+ memcpy(stream->next_out, next_in, copy_size);
+
+ state->count = 0;
+ state->state = ZSTATE_TYPE0_HDR;
+ }
+
+ state->block_next += copy_size;
+ stream->next_out += copy_size;
+ stream->avail_out -= copy_size;
+ stream->total_out += copy_size;
+
+ if (state->block_next == state->block_end) {
+ state->state = state->has_eob_hdr ? ZSTATE_TRL : ZSTATE_NEW_HDR;
+ if (stream->flush == FULL_FLUSH && state->state == ZSTATE_NEW_HDR
+ && are_buffers_empty(stream)) {
+ /* Clear match history so there are no cross
+ * block length distance pairs */
+ reset_match_history(stream);
+ }
+ }
+ } while (state->state == ZSTATE_TYPE0_HDR);
+
+ return state->block_end - state->block_next;
+}
+
+static inline void reset_match_history(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ struct level_buf *level_buf = (struct level_buf *)stream->level_buf;
+ uint16_t *hash_table;
+ uint32_t hash_table_size;
+
+ hash_table_size = 2 * (state->hash_mask + 1);
+
+ switch (stream->level) {
+ case 3:
+ hash_table = level_buf->lvl3.hash_table;
+ break;
+ case 2:
+ hash_table = level_buf->lvl2.hash_table;
+ break;
+ case 1:
+ hash_table = level_buf->lvl1.hash_table;
+ break;
+ default:
+ hash_table = state->head;
+ }
+
+ state->has_hist = IGZIP_NO_HIST;
+
+ /* There is definitely more than 16 bytes in the hash table. Set this
+ * minimum to avoid a wmemset of size 0 */
+ if (hash_table_size <= sizeof(wchar_t))
+ hash_table_size = sizeof(wchar_t);
+
+ if (sizeof(wchar_t) == 2) {
+ uint16_t hash_init_val;
+
+ hash_init_val = stream->total_in & 0xffff;
+ wmemset((wchar_t *)hash_table, hash_init_val,
+ hash_table_size / sizeof(wchar_t));
+
+ } else if (sizeof(wchar_t) == 4) {
+ uint32_t hash_init_val;
+ int rep_bits;
+
+ hash_init_val = stream->total_in & 0xffff;
+ for (rep_bits = sizeof(uint16_t) * 8; rep_bits < sizeof(wchar_t) * 8;
+ rep_bits *= 2)
+ hash_init_val |= hash_init_val << rep_bits;
+
+ wmemset((wchar_t *)hash_table, hash_init_val,
+ hash_table_size / sizeof(wchar_t));
+ } else {
+ if ((stream->total_in & 0xFFFF) == 0)
+ memset(hash_table, 0, hash_table_size);
+ else {
+ int i;
+ for (i = 0; i < hash_table_size / 2; i++) {
+ hash_table[i] = (uint16_t) (stream->total_in);
+ }
+ }
+ }
+
+}
+
+static void inline set_dist_mask(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ uint32_t hist_size;
+
+ if (stream->hist_bits > ISAL_DEF_MAX_HIST_BITS || stream->hist_bits == 0)
+ stream->hist_bits = ISAL_DEF_MAX_HIST_BITS;
+
+ hist_size = (1 << (stream->hist_bits));
+ state->dist_mask = hist_size - 1;
+
+ if (IGZIP_HIST_SIZE < ISAL_DEF_HIST_SIZE && state->dist_mask > IGZIP_HIST_SIZE - 1)
+ state->dist_mask = IGZIP_HIST_SIZE - 1;
+}
+
+static void inline set_hash_mask(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+
+ switch (stream->level) {
+ case 3:
+ state->hash_mask = LVL3_HASH_MASK;
+ break;
+ case 2:
+ state->hash_mask = LVL2_HASH_MASK;
+ break;
+ case 1:
+ state->hash_mask = LVL1_HASH_MASK;
+ break;
+ case 0:
+ state->hash_mask = LVL0_HASH_MASK;
+ }
+}
+
+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->level = 0;
+ stream->level_buf = NULL;
+ stream->level_buf_size = 0;
+ stream->end_of_stream = 0;
+ stream->flush = NO_FLUSH;
+ stream->gzip_flag = 0;
+ stream->hist_bits = 0;
+
+ state->block_next = 0;
+ state->block_end = 0;
+ state->b_bytes_valid = 0;
+ state->b_bytes_processed = 0;
+ state->total_in_start = 0;
+ state->has_wrap_hdr = 0;
+ state->has_eob = 0;
+ state->has_eob_hdr = 0;
+ state->has_hist = IGZIP_NO_HIST;
+ state->has_level_buf_init = 0;
+ state->state = ZSTATE_NEW_HDR;
+ state->count = 0;
+
+ state->tmp_out_start = 0;
+ state->tmp_out_end = 0;
+
+ init(&state->bitbuf);
+
+ state->crc = 0;
+
+ return;
+}
+
+void isal_deflate_reset(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+
+ stream->total_in = 0;
+ stream->total_out = 0;
+
+ state->block_next = 0;
+ state->block_end = 0;
+ state->b_bytes_valid = 0;
+ state->b_bytes_processed = 0;
+ state->total_in_start = 0;
+ state->has_wrap_hdr = 0;
+ state->has_eob = 0;
+ state->has_level_buf_init = 0;
+ state->has_eob_hdr = 0;
+ state->has_hist = IGZIP_NO_HIST;
+ state->state = ZSTATE_NEW_HDR;
+ state->count = 0;
+
+ state->tmp_out_start = 0;
+ state->tmp_out_end = 0;
+
+ init(&state->bitbuf);
+
+ state->crc = 0;
+
+}
+
+void isal_gzip_header_init(struct isal_gzip_header *gz_hdr)
+{
+ gz_hdr->text = 0;
+ gz_hdr->time = 0;
+ gz_hdr->xflags = 0;
+ gz_hdr->os = 0xff;
+ gz_hdr->extra = NULL;
+ gz_hdr->extra_buf_len = 0;
+ gz_hdr->extra_len = 0;
+ gz_hdr->name = NULL;
+ gz_hdr->name_buf_len = 0;
+ gz_hdr->comment = NULL;
+ gz_hdr->comment_buf_len = 0;
+ gz_hdr->hcrc = 0;
+}
+
+uint32_t isal_write_gzip_header(struct isal_zstream *stream, struct isal_gzip_header *gz_hdr)
+{
+ uint32_t flags = 0, hcrc, hdr_size = GZIP_HDR_BASE;
+ uint8_t *out_buf = stream->next_out, *out_buf_start = stream->next_out;
+ uint32_t name_len = 0, comment_len = 0;
+
+ if (gz_hdr->text)
+ flags |= TEXT_FLAG;
+ if (gz_hdr->extra) {
+ flags |= EXTRA_FLAG;
+ hdr_size += GZIP_EXTRA_LEN + gz_hdr->extra_len;
+ }
+ if (gz_hdr->name) {
+ flags |= NAME_FLAG;
+ name_len = strnlen(gz_hdr->name, gz_hdr->name_buf_len);
+ if (name_len < gz_hdr->name_buf_len)
+ name_len++;
+ hdr_size += name_len;
+ }
+ if (gz_hdr->comment) {
+ flags |= COMMENT_FLAG;
+ comment_len = strnlen(gz_hdr->comment, gz_hdr->comment_buf_len);
+ if (comment_len < gz_hdr->comment_buf_len)
+ comment_len++;
+ hdr_size += comment_len;
+ }
+ if (gz_hdr->hcrc) {
+ flags |= HCRC_FLAG;
+ hdr_size += GZIP_HCRC_LEN;
+ }
+
+ if (stream->avail_out < hdr_size)
+ return hdr_size;
+
+ out_buf[0] = 0x1f;
+ out_buf[1] = 0x8b;
+ out_buf[2] = DEFLATE_METHOD;
+ out_buf[3] = flags;
+ store_u32(out_buf + 4, gz_hdr->time);
+ out_buf[8] = gz_hdr->xflags;
+ out_buf[9] = gz_hdr->os;
+
+ out_buf += GZIP_HDR_BASE;
+ if (flags & EXTRA_FLAG) {
+ store_u16(out_buf, gz_hdr->extra_len);
+ out_buf += GZIP_EXTRA_LEN;
+
+ memcpy(out_buf, gz_hdr->extra, gz_hdr->extra_len);
+ out_buf += gz_hdr->extra_len;
+ }
+
+ if (flags & NAME_FLAG) {
+ memcpy(out_buf, gz_hdr->name, name_len);
+ out_buf += name_len;
+ }
+
+ if (flags & COMMENT_FLAG) {
+ memcpy(out_buf, gz_hdr->comment, comment_len);
+ out_buf += comment_len;
+ }
+
+ if (flags & HCRC_FLAG) {
+ hcrc = crc32_gzip_refl(0, out_buf_start, out_buf - out_buf_start);
+ store_u16(out_buf, hcrc);
+ out_buf += GZIP_HCRC_LEN;
+ }
+
+ stream->next_out += hdr_size;
+ stream->total_out += hdr_size;
+ stream->avail_out -= hdr_size;
+
+ return ISAL_DECOMP_OK;
+}
+
+uint32_t isal_write_zlib_header(struct isal_zstream *stream, struct isal_zlib_header *z_hdr)
+{
+ uint32_t cmf, flg, dict_flag = 0, hdr_size = ZLIB_HDR_BASE;
+ uint8_t *out_buf = stream->next_out;
+
+ if (z_hdr->dict_flag) {
+ dict_flag = ZLIB_DICT_FLAG;
+ hdr_size = ZLIB_HDR_BASE + ZLIB_DICT_LEN;
+ }
+
+ if (stream->avail_out < hdr_size)
+ return hdr_size;
+
+ cmf = DEFLATE_METHOD | (z_hdr->info << 4);
+ flg = (z_hdr->level << 6) | dict_flag;
+
+ flg += 31 - ((256 * cmf + flg) % 31);
+
+ out_buf[0] = cmf;
+ out_buf[1] = flg;
+
+ if (dict_flag)
+ store_u32(out_buf + 2, z_hdr->dict_id);
+
+ stream->next_out += hdr_size;
+ stream->total_out += hdr_size;
+ stream->avail_out -= hdr_size;
+
+ return ISAL_DECOMP_OK;
+}
+
+int isal_deflate_set_hufftables(struct isal_zstream *stream,
+ struct isal_hufftables *hufftables, int type)
+{
+ if (stream->internal_state.state != ZSTATE_NEW_HDR)
+ return ISAL_INVALID_OPERATION;
+
+ switch (type) {
+ case IGZIP_HUFFTABLE_DEFAULT:
+ stream->hufftables = (struct isal_hufftables *)&hufftables_default;
+ break;
+ case IGZIP_HUFFTABLE_STATIC:
+ stream->hufftables = (struct isal_hufftables *)&hufftables_static;
+ break;
+ case IGZIP_HUFFTABLE_CUSTOM:
+ if (hufftables != NULL) {
+ stream->hufftables = hufftables;
+ break;
+ }
+ default:
+ return ISAL_INVALID_OPERATION;
+ }
+
+ return COMP_OK;
+}
+
+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->level = 0;
+ stream->level_buf = NULL;
+ stream->level_buf_size = 0;
+ stream->end_of_stream = 0;
+ stream->flush = NO_FLUSH;
+ stream->gzip_flag = 0;
+ stream->internal_state.has_wrap_hdr = 0;
+ stream->internal_state.state = ZSTATE_NEW_HDR;
+ return;
+}
+
+void isal_deflate_hash(struct isal_zstream *stream, uint8_t * dict, uint32_t dict_len)
+{
+ /* Reset history to prevent out of bounds matches this works because
+ * dictionary must set at least 1 element in the history */
+ struct level_buf *level_buf = (struct level_buf *)stream->level_buf;
+ uint32_t hash_mask = stream->internal_state.hash_mask;
+
+ switch (stream->level) {
+ case 3:
+ memset(level_buf->lvl3.hash_table, -1, sizeof(level_buf->lvl3.hash_table));
+ isal_deflate_hash_lvl3(level_buf->lvl3.hash_table, hash_mask,
+ stream->total_in, dict, dict_len);
+ break;
+
+ case 2:
+ memset(level_buf->lvl2.hash_table, -1, sizeof(level_buf->lvl2.hash_table));
+ isal_deflate_hash_lvl2(level_buf->lvl2.hash_table, hash_mask,
+ stream->total_in, dict, dict_len);
+ break;
+ case 1:
+ memset(level_buf->lvl1.hash_table, -1, sizeof(level_buf->lvl1.hash_table));
+ isal_deflate_hash_lvl1(level_buf->lvl1.hash_table, hash_mask,
+ stream->total_in, dict, dict_len);
+ break;
+ default:
+ memset(stream->internal_state.head, -1, sizeof(stream->internal_state.head));
+ isal_deflate_hash_lvl0(stream->internal_state.head, hash_mask,
+ stream->total_in, dict, dict_len);
+ }
+
+ stream->internal_state.has_hist = IGZIP_HIST;
+}
+
+int isal_deflate_process_dict(struct isal_zstream *stream, struct isal_dict *dict,
+ uint8_t * dict_data, uint32_t dict_len)
+{
+ if ((dict == NULL)
+ || (dict_len == 0)
+ || (dict->level > ISAL_DEF_MAX_LEVEL))
+ return ISAL_INVALID_STATE;
+
+ if (dict_len > IGZIP_HIST_SIZE) {
+ dict_data = dict_data + dict_len - IGZIP_HIST_SIZE;
+ dict_len = IGZIP_HIST_SIZE;
+ }
+
+ dict->level = stream->level;
+ dict->hist_size = dict_len;
+ memcpy(dict->history, dict_data, dict_len);
+ memset(dict->hashtable, -1, sizeof(dict->hashtable));
+
+ switch (stream->level) {
+ case 3:
+ dict->hash_size = IGZIP_LVL3_HASH_SIZE;
+ isal_deflate_hash_lvl3(dict->hashtable, LVL3_HASH_MASK,
+ 0, dict_data, dict_len);
+ break;
+
+ case 2:
+ dict->hash_size = IGZIP_LVL2_HASH_SIZE;
+ isal_deflate_hash_lvl2(dict->hashtable, LVL2_HASH_MASK,
+ 0, dict_data, dict_len);
+ break;
+ case 1:
+ dict->hash_size = IGZIP_LVL1_HASH_SIZE;
+ isal_deflate_hash_lvl1(dict->hashtable, LVL1_HASH_MASK,
+ 0, dict_data, dict_len);
+ break;
+ default:
+ dict->hash_size = IGZIP_LVL0_HASH_SIZE;
+ isal_deflate_hash_lvl0(dict->hashtable, LVL0_HASH_MASK,
+ 0, dict_data, dict_len);
+ }
+ return COMP_OK;
+}
+
+int isal_deflate_reset_dict(struct isal_zstream *stream, struct isal_dict *dict)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ struct level_buf *level_buf = (struct level_buf *)stream->level_buf;
+ int ret;
+
+ if ((state->state != ZSTATE_NEW_HDR)
+ || (state->b_bytes_processed != state->b_bytes_valid)
+ || (dict->level != stream->level)
+ || (dict->hist_size == 0)
+ || (dict->hist_size > IGZIP_HIST_SIZE)
+ || (dict->hash_size > IGZIP_LVL3_HASH_SIZE))
+ return ISAL_INVALID_STATE;
+
+ ret = check_level_req(stream);
+ if (ret)
+ return ret;
+
+ memcpy(state->buffer, dict->history, dict->hist_size);
+ state->b_bytes_processed = dict->hist_size;
+ state->b_bytes_valid = dict->hist_size;
+ state->has_hist = IGZIP_DICT_HASH_SET;
+
+ switch (stream->level) {
+ case 3:
+ memcpy(level_buf->lvl3.hash_table, dict->hashtable,
+ sizeof(level_buf->lvl3.hash_table));
+ break;
+
+ case 2:
+ memcpy(level_buf->lvl2.hash_table, dict->hashtable,
+ sizeof(level_buf->lvl2.hash_table));
+ break;
+ case 1:
+ memcpy(level_buf->lvl1.hash_table, dict->hashtable,
+ sizeof(level_buf->lvl1.hash_table));
+ break;
+ default:
+ memcpy(stream->internal_state.head, dict->hashtable,
+ sizeof(stream->internal_state.head));
+ }
+
+ return COMP_OK;
+}
+
+int isal_deflate_set_dict(struct isal_zstream *stream, uint8_t * dict, uint32_t dict_len)
+{
+ struct isal_zstate *state = &stream->internal_state;
+
+ if (state->state != ZSTATE_NEW_HDR || state->b_bytes_processed != state->b_bytes_valid)
+ return ISAL_INVALID_STATE;
+
+ if (dict_len <= 0)
+ return COMP_OK;
+
+ if (dict_len > IGZIP_HIST_SIZE) {
+ dict = dict + dict_len - IGZIP_HIST_SIZE;
+ dict_len = IGZIP_HIST_SIZE;
+ }
+
+ memcpy(state->buffer, dict, dict_len);
+ state->b_bytes_processed = dict_len;
+ state->b_bytes_valid = dict_len;
+
+ state->has_hist = IGZIP_DICT_HIST;
+
+ return COMP_OK;
+}
+
+int isal_deflate_stateless(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ 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;
+ const uint32_t has_wrap_hdr = state->has_wrap_hdr;
+
+ int level_check;
+ uint64_t stored_len;
+
+ /* Final block has already been written */
+ state->block_next = stream->total_in;
+ state->block_end = stream->total_in;
+ state->has_eob_hdr = 0;
+ init(&state->bitbuf);
+ state->state = ZSTATE_NEW_HDR;
+ state->crc = 0;
+ state->has_level_buf_init = 0;
+ set_dist_mask(stream);
+
+ if (stream->flush == NO_FLUSH)
+ stream->end_of_stream = 1;
+
+ if (stream->flush != NO_FLUSH && stream->flush != FULL_FLUSH)
+ return INVALID_FLUSH;
+
+ level_check = check_level_req(stream);
+ if (level_check) {
+ if (stream->level == 1 && stream->level_buf == NULL) {
+ /* Default to internal buffer if invalid size is supplied */
+ stream->level_buf = state->buffer;
+ stream->level_buf_size = sizeof(state->buffer) + sizeof(state->head);
+ } else
+ return level_check;
+ }
+
+ set_hash_mask(stream);
+
+ if (state->hash_mask > 2 * avail_in)
+ state->hash_mask = (1 << bsr(avail_in)) - 1;
+
+ if (avail_in == 0)
+ stored_len = TYPE0_BLK_HDR_LEN;
+ else {
+ stored_len = TYPE0_BLK_HDR_LEN * ((avail_in + TYPE0_MAX_BLK_LEN - 1) /
+ TYPE0_MAX_BLK_LEN);
+ stored_len += avail_in;
+ }
+
+ /*
+ at least 1 byte compressed data in the case of empty dynamic block which only
+ contains the EOB
+ */
+ if (stream->gzip_flag == IGZIP_GZIP)
+ stored_len += gzip_hdr_bytes + gzip_trl_bytes;
+ else if (stream->gzip_flag == IGZIP_GZIP_NO_HDR)
+ stored_len += gzip_trl_bytes;
+
+ else if (stream->gzip_flag == IGZIP_ZLIB)
+ stored_len += zlib_hdr_bytes + zlib_trl_bytes;
+
+ else if (stream->gzip_flag == IGZIP_ZLIB_NO_HDR)
+ stored_len += zlib_trl_bytes;
+
+ if (avail_out >= stored_len)
+ stream->avail_out = stored_len;
+
+ if (isal_deflate_int_stateless(stream) == COMP_OK) {
+ if (avail_out >= stored_len)
+ stream->avail_out += avail_out - stored_len;
+ return COMP_OK;
+ } else {
+ if (avail_out >= stored_len)
+ stream->avail_out += avail_out - stored_len;
+ if (stream->flush == FULL_FLUSH) {
+ reset_match_history(stream);
+ }
+ stream->internal_state.has_eob_hdr = 0;
+ }
+
+ if (avail_out < stored_len)
+ return STATELESS_OVERFLOW;
+
+ stream->next_in = next_in + avail_in;
+ stream->avail_in = 0;
+ stream->total_in = avail_in;
+
+ state->block_next = stream->total_in - avail_in;
+ state->block_end = stream->total_in;
+
+ stream->next_out = next_out;
+ stream->avail_out = avail_out;
+ stream->total_out = total_out;
+
+ stream->gzip_flag = gzip_flag;
+ state->has_wrap_hdr = has_wrap_hdr;
+ init(&stream->internal_state.bitbuf);
+ stream->internal_state.count = 0;
+
+ if (stream->gzip_flag == IGZIP_GZIP || stream->gzip_flag == IGZIP_ZLIB)
+ write_stream_header_stateless(stream);
+
+ stream->internal_state.state = ZSTATE_TYPE0_HDR;
+
+ write_stored_block(stream);
+
+ stream->total_in = total_in + avail_in;
+
+ if (stream->gzip_flag) {
+ stream->internal_state.crc = 0;
+ update_checksum(stream, next_in, avail_in);
+ }
+
+ if (stream->end_of_stream)
+ write_trailer(stream);
+
+ return COMP_OK;
+
+}
+
+static inline uint32_t get_hist_size(struct isal_zstream *stream, uint8_t * start_in,
+ int32_t buf_hist_start)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ uint32_t history_size;
+ uint32_t buffered_history;
+ uint32_t buffered_size = state->b_bytes_valid - state->b_bytes_processed;
+ uint32_t input_history;
+
+ buffered_history = (state->has_hist) ? state->b_bytes_processed - buf_hist_start : 0;
+ input_history = stream->next_in - start_in;
+
+ /* Calculate history required for deflate window */
+ history_size = (buffered_history >= input_history) ? buffered_history : input_history;
+ if (history_size > IGZIP_HIST_SIZE)
+ history_size = IGZIP_HIST_SIZE;
+
+ /* Calculate history required based on internal state */
+ if (state->state == ZSTATE_TYPE0_HDR
+ || state->state == ZSTATE_TYPE0_BODY
+ || state->state == ZSTATE_TMP_TYPE0_HDR || state->state == ZSTATE_TMP_TYPE0_BODY) {
+ if (stream->total_in - state->block_next > history_size) {
+ history_size = (stream->total_in - state->block_next);
+ }
+ } else if (stream->avail_in + buffered_size == 0
+ && (stream->end_of_stream || stream->flush == FULL_FLUSH)) {
+ history_size = 0;
+ }
+ return history_size;
+}
+
+int isal_deflate(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ int ret = COMP_OK;
+ uint8_t *next_in, *start_in, *buf_start_in, *next_in_pre;
+ uint32_t avail_in, total_start, hist_size, future_size;
+ uint32_t in_size, in_size_initial, out_size, out_size_initial;
+ uint32_t processed, buffered_size = state->b_bytes_valid - state->b_bytes_processed;
+ uint32_t flush_type = stream->flush;
+ uint32_t end_of_stream = stream->end_of_stream;
+ uint32_t size = 0;
+ int32_t buf_hist_start = 0;
+ uint8_t *copy_down_src = NULL;
+ uint64_t copy_down_size = 0, copy_start_offset;
+ int internal;
+
+ if (stream->flush >= 3)
+ return INVALID_FLUSH;
+
+ ret = check_level_req(stream);
+ if (ret)
+ return ret;
+
+ start_in = stream->next_in;
+ total_start = stream->total_in;
+
+ hist_size = get_hist_size(stream, start_in, buf_hist_start);
+
+ if (state->has_hist == IGZIP_NO_HIST) {
+ set_dist_mask(stream);
+ set_hash_mask(stream);
+ if (state->hash_mask > 2 * stream->avail_in
+ && (stream->flush == FULL_FLUSH || stream->end_of_stream))
+ state->hash_mask = (1 << bsr(2 * stream->avail_in)) - 1;
+ stream->total_in -= buffered_size;
+ reset_match_history(stream);
+ stream->total_in += buffered_size;
+ buf_hist_start = state->b_bytes_processed;
+
+ } else if (state->has_hist == IGZIP_DICT_HIST) {
+ set_dist_mask(stream);
+ set_hash_mask(stream);
+ isal_deflate_hash(stream, state->buffer, state->b_bytes_processed);
+ } else if (state->has_hist == IGZIP_DICT_HASH_SET) {
+ set_dist_mask(stream);
+ set_hash_mask(stream);
+ }
+
+ in_size = stream->avail_in + buffered_size;
+ out_size = stream->total_out;
+ do {
+ in_size_initial = in_size;
+ out_size_initial = out_size;
+ buf_start_in = start_in;
+ internal = 0;
+
+ /* Setup to compress from internal buffer if insufficient history */
+ if (stream->total_in - total_start < hist_size + buffered_size) {
+ /* On entry there should always be sufficient history bufferd */
+ /* assert(state->b_bytes_processed >= hist_size); */
+
+ internal = 1;
+ /* Shift down internal buffer if it contains more data
+ * than required */
+ if (state->b_bytes_processed > hist_size) {
+ copy_start_offset = state->b_bytes_processed - hist_size;
+
+ copy_down_src = &state->buffer[copy_start_offset];
+ copy_down_size = state->b_bytes_valid - copy_start_offset;
+ 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;
+ buf_hist_start -= copy_down_src - state->buffer;
+ if (buf_hist_start < 0)
+ buf_hist_start = 0;
+ }
+
+ size = stream->avail_in;
+ if (size > sizeof(state->buffer) - state->b_bytes_valid)
+ size = sizeof(state->buffer) - state->b_bytes_valid;
+
+ memcpy(&state->buffer[state->b_bytes_valid], stream->next_in, size);
+
+ stream->next_in += size;
+ stream->avail_in -= size;
+ stream->total_in += size;
+ state->b_bytes_valid += size;
+ buffered_size += size;
+
+ /* Save off next_in and avail_in if compression is
+ * performed in internal buffer, total_in can be
+ * recovered from knowledge of the size of the buffered
+ * input */
+ next_in = stream->next_in;
+ avail_in = stream->avail_in;
+
+ /* If not much data is buffered and there is no need to
+ * flush the buffer, just continue rather than attempt
+ * to compress */
+ if (avail_in == 0 && buffered_size <= IGZIP_HIST_SIZE
+ && stream->total_in - buffered_size - state->block_next <=
+ IGZIP_HIST_SIZE && !stream->end_of_stream
+ && stream->flush == NO_FLUSH)
+ continue;
+
+ if (avail_in) {
+ stream->flush = NO_FLUSH;
+ stream->end_of_stream = 0;
+ }
+
+ stream->next_in = &state->buffer[state->b_bytes_processed];
+ stream->avail_in = buffered_size;
+ stream->total_in -= buffered_size;
+
+ buf_start_in = state->buffer;
+
+ } else if (buffered_size) {
+ /* The user provided buffer has sufficient data, reset
+ * the user supplied buffer to included any data already
+ * buffered */
+ stream->next_in -= buffered_size;
+ stream->avail_in += buffered_size;
+ stream->total_in -= buffered_size;
+ state->b_bytes_valid = 0;
+ state->b_bytes_processed = 0;
+ buffered_size = 0;
+ }
+
+ next_in_pre = stream->next_in;
+ isal_deflate_int(stream, buf_start_in);
+ processed = stream->next_in - next_in_pre;
+ hist_size = get_hist_size(stream, buf_start_in, buf_hist_start);
+
+ /* Restore compression to unbuffered input when compressing to internal buffer */
+ if (internal) {
+ state->b_bytes_processed += processed;
+ buffered_size -= processed;
+
+ stream->flush = flush_type;
+ stream->end_of_stream = end_of_stream;
+ stream->total_in += buffered_size;
+
+ stream->next_in = next_in;
+ stream->avail_in = avail_in;
+ }
+
+ in_size = stream->avail_in + buffered_size;
+ out_size = stream->total_out;
+
+ } while (internal && stream->avail_in > 0 && stream->avail_out > 0
+ && (in_size_initial != in_size || out_size_initial != out_size));
+
+ /* Buffer history if data was pulled from the external buffer and future
+ * calls to deflate will be required */
+ if (!internal && (state->state != ZSTATE_END && state->state != ZSTATE_TRL)) {
+ /* If the external buffer was used, sufficient history must
+ * exist in the user input buffer */
+ /* assert(stream->total_in - total_start >= */
+ /* hist_size + buffered_size); */
+
+ stream->next_in -= buffered_size;
+ stream->avail_in += buffered_size;
+ stream->total_in -= buffered_size;
+
+ memmove(state->buffer, stream->next_in - hist_size, hist_size);
+ state->b_bytes_processed = hist_size;
+ state->b_bytes_valid = hist_size;
+ buffered_size = 0;
+ }
+
+ /* Buffer input data if it is necessary for continued execution */
+ if (stream->avail_in > 0 && (stream->avail_out > 0 || stream->level == 3)) {
+ /* Determine how much data to buffer */
+ future_size = sizeof(state->buffer) - state->b_bytes_valid;
+ if (stream->avail_in < future_size)
+ /* Buffer all data if it fits as it will need to be buffered
+ * on the next call anyways*/
+ future_size = stream->avail_in;
+ else if (ISAL_LOOK_AHEAD < future_size)
+ /* Buffer a minimum look ahead required for level 3 */
+ future_size = ISAL_LOOK_AHEAD;
+
+ memcpy(&state->buffer[state->b_bytes_valid], stream->next_in, future_size);
+
+ state->b_bytes_valid += future_size;
+ buffered_size += future_size;
+ stream->next_in += future_size;
+ stream->total_in += future_size;
+ stream->avail_in -= future_size;
+
+ }
+
+ return ret;
+}
+
+static int write_stream_header_stateless(struct isal_zstream *stream)
+{
+ uint32_t hdr_bytes;
+ const uint8_t *hdr;
+ uint32_t next_flag;
+
+ if (stream->internal_state.has_wrap_hdr)
+ return COMP_OK;
+
+ if (stream->gzip_flag == IGZIP_ZLIB) {
+ hdr_bytes = zlib_hdr_bytes;
+ hdr = zlib_hdr;
+ next_flag = IGZIP_ZLIB_NO_HDR;
+
+ } else {
+ hdr_bytes = gzip_hdr_bytes;
+ hdr = gzip_hdr;
+ next_flag = IGZIP_GZIP_NO_HDR;
+ }
+
+ if (hdr_bytes >= stream->avail_out)
+ return STATELESS_OVERFLOW;
+
+ stream->avail_out -= hdr_bytes;
+ stream->total_out += hdr_bytes;
+
+ memcpy(stream->next_out, hdr, hdr_bytes);
+
+ stream->next_out += hdr_bytes;
+ stream->internal_state.has_wrap_hdr = 1;
+ stream->gzip_flag = next_flag;
+
+ return COMP_OK;
+}
+
+static void write_stream_header(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ int bytes_to_write;
+ uint32_t hdr_bytes;
+ const uint8_t *hdr;
+
+ if (stream->internal_state.has_wrap_hdr)
+ return;
+
+ if (stream->gzip_flag == IGZIP_ZLIB) {
+ hdr_bytes = zlib_hdr_bytes;
+ hdr = zlib_hdr;
+ } else {
+ hdr_bytes = gzip_hdr_bytes;
+ hdr = gzip_hdr;
+ }
+
+ bytes_to_write = hdr_bytes;
+ bytes_to_write -= state->count;
+
+ if (bytes_to_write > stream->avail_out)
+ bytes_to_write = stream->avail_out;
+
+ memcpy(stream->next_out, hdr + state->count, bytes_to_write);
+ state->count += bytes_to_write;
+
+ if (state->count == hdr_bytes) {
+ state->count = 0;
+ state->has_wrap_hdr = 1;
+ }
+
+ 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;
+ uint64_t hdr_extra_bits = hufftables->deflate_hdr[hufftables->deflate_hdr_count];
+ 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) {
+ if (hufftables->deflate_hdr_count > 0)
+ *stream->next_out -= 1;
+ else
+ hdr_extra_bits -= 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, hdr_extra_bits, 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;
+ uint8_t *header_next;
+ uint8_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 = hufftables->deflate_hdr;
+ header_end = header_next +
+ (hufftables->deflate_hdr_count / sizeof(header_bits)) * sizeof(header_bits);
+
+ header_bits = load_u64(header_next);
+
+ if (stream->end_of_stream == 0)
+ header_bits--;
+ else
+ state->has_eob_hdr = 1;
+
+ header_next += sizeof(header_bits);
+
+ /* Write out Complete Header bits */
+ for (; header_next <= header_end; header_next += sizeof(header_bits)) {
+ write_bits(&state->bitbuf, header_bits, 32);
+ header_bits >>= 32;
+ write_bits(&state->bitbuf, header_bits, 32);
+ header_bits = load_u64(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;
+}
+
+/* Toggle end of stream only works when deflate header is aligned */
+static void write_header(struct isal_zstream *stream, uint8_t * deflate_hdr,
+ uint32_t deflate_hdr_count, uint32_t extra_bits_count,
+ uint32_t next_state, uint32_t toggle_end_of_stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ uint32_t hdr_extra_bits = deflate_hdr[deflate_hdr_count];
+ 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 || stream->gzip_flag == IGZIP_ZLIB)
+ write_stream_header(stream);
+
+ count = deflate_hdr_count - state->count;
+
+ if (count != 0) {
+ if (count > stream->avail_out)
+ count = stream->avail_out;
+
+ memcpy(stream->next_out, deflate_hdr + state->count, count);
+
+ if (toggle_end_of_stream && state->count == 0 && count > 0) {
+ /* Assumes the final block bit is the first bit */
+ *stream->next_out ^= 1;
+ state->has_eob_hdr = !state->has_eob_hdr;
+ }
+
+ stream->next_out += count;
+ stream->avail_out -= count;
+ stream->total_out += count;
+ state->count += count;
+
+ count = deflate_hdr_count - state->count;
+ } else if (toggle_end_of_stream && deflate_hdr_count == 0) {
+ /* Assumes the final block bit is the first bit */
+ hdr_extra_bits ^= 1;
+ state->has_eob_hdr = !state->has_eob_hdr;
+ }
+
+ if ((count == 0) && (stream->avail_out >= 8)) {
+
+ set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
+
+ write_bits(&state->bitbuf, hdr_extra_bits, extra_bits_count);
+
+ state->state = next_state;
+ state->count = 0;
+
+ count = buffer_used(&state->bitbuf);
+ stream->next_out = buffer_ptr(&state->bitbuf);
+ stream->avail_out -= count;
+ stream->total_out += count;
+ }
+
+}
+
+static void write_trailer(struct isal_zstream *stream)
+{
+ struct isal_zstate *state = &stream->internal_state;
+ unsigned int bytes = 0;
+ uint32_t crc = state->crc;
+
+ set_buf(&state->bitbuf, stream->next_out, stream->avail_out);
+
+ 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 */
+ if (stream->avail_out < 8)
+ return;
+
+ 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;
+ }
+ }
+
+ if (state->bitbuf.m_bit_count) {
+ /* the flush() will pad to the next byte and write up to 8 bytes
+ * to the output stream/buffer.
+ */
+ if (stream->avail_out < 8)
+ return;
+
+ flush(&state->bitbuf);
+ }
+
+ stream->next_out = buffer_ptr(&state->bitbuf);
+ bytes = buffer_used(&state->bitbuf);
+
+ switch (stream->gzip_flag) {
+ case IGZIP_GZIP:
+ case IGZIP_GZIP_NO_HDR:
+ if (stream->avail_out - bytes >= gzip_trl_bytes) {
+ store_u64(stream->next_out, ((uint64_t) stream->total_in << 32) | crc);
+ stream->next_out += gzip_trl_bytes;
+ bytes += gzip_trl_bytes;
+ state->state = ZSTATE_END;
+ }
+ break;
+
+ case IGZIP_ZLIB:
+ case IGZIP_ZLIB_NO_HDR:
+ if (stream->avail_out - bytes >= zlib_trl_bytes) {
+ store_u32(stream->next_out,
+ to_be32((crc & 0xFFFF0000) | ((crc & 0xFFFF) + 1) %
+ ADLER_MOD));
+ stream->next_out += zlib_trl_bytes;
+ bytes += zlib_trl_bytes;
+ state->state = ZSTATE_END;
+ }
+ break;
+
+ default:
+ state->state = ZSTATE_END;
+ }
+
+ stream->avail_out -= bytes;
+ stream->total_out += bytes;
+}