r5sdk/r5dev/thirdparty/lzham/lzhamdecomp/lzham_lzdecomp.cpp
Kawe Mazidjatari 55b90781ee Remove experimental CRC implementation in LZHAM
This implementation was done to calculate buffer CRC on the go, as that was what the VPK file system uses for the file crc's. But it later turned out to be unnecessary, and was never reverted.
2022-12-04 01:33:10 +01:00

1560 lines
61 KiB
C++

// File: lzham_lzdecomp.cpp
// See Copyright Notice and license at the end of include/lzham.h
//
// See "Coroutines in C":
// http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
// Also see "Protothreads - Lightweight, Stackless Threads in C":
// http://www.sics.se/~adam/pt/
#include "../include/lzham_core.h"
#include "lzham_decomp.h"
#include "../include/lzham_symbol_codec.h"
#include "../include/lzham_checksum.h"
#include "lzham_lzdecompbase.h"
using namespace lzham;
namespace lzham
{
static const uint8 s_literal_next_state[24] =
{
0, 0, 0, 0, 1, 2, 3, // 0-6: literal states
4, 5, 6, 4, 5, // 7-11: match states
7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10 // 12-23: unused
};
static const uint s_huge_match_base_len[4] = { CLZDecompBase::cMaxMatchLen + 1, CLZDecompBase::cMaxMatchLen + 1 + 256, CLZDecompBase::cMaxMatchLen + 1 + 256 + 1024, CLZDecompBase::cMaxMatchLen + 1 + 256 + 1024 + 4096 };
static const uint8 s_huge_match_code_len[4] = { 8, 10, 12, 16 };
struct lzham_decompressor
{
void init();
template<bool unbuffered> lzham_decompress_status_t decompress();
void reset_all_tables();
void reset_huffman_table_update_rates();
int m_state;
CLZDecompBase m_lzBase;
symbol_codec m_codec;
uint32 m_raw_decomp_buf_size;
uint8 *m_pRaw_decomp_buf;
uint8 *m_pDecomp_buf;
uint32 m_decomp_adler32;
const uint8 *m_pIn_buf;
size_t *m_pIn_buf_size;
uint8 *m_pOut_buf;
size_t *m_pOut_buf_size;
bool m_no_more_input_bytes_flag;
uint8 *m_pOrig_out_buf;
size_t m_orig_out_buf_size;
lzham_decompress_params m_params;
lzham_decompress_status_t m_status;
#if LZHAM_USE_ALL_ARITHMETIC_CODING
typedef adaptive_arith_data_model sym_data_model;
#else
typedef quasi_adaptive_huffman_data_model sym_data_model;
#endif
sym_data_model m_lit_table[1 << CLZDecompBase::cNumLitPredBits];
sym_data_model m_delta_lit_table[1 << CLZDecompBase::cNumDeltaLitPredBits];
sym_data_model m_main_table;
sym_data_model m_rep_len_table[2];
sym_data_model m_large_len_table[2];
sym_data_model m_dist_lsb_table;
adaptive_bit_model m_is_match_model[CLZDecompBase::cNumStates * (1 << CLZDecompBase::cNumIsMatchContextBits)];
adaptive_bit_model m_is_rep_model[CLZDecompBase::cNumStates];
adaptive_bit_model m_is_rep0_model[CLZDecompBase::cNumStates];
adaptive_bit_model m_is_rep0_single_byte_model[CLZDecompBase::cNumStates];
adaptive_bit_model m_is_rep1_model[CLZDecompBase::cNumStates];
adaptive_bit_model m_is_rep2_model[CLZDecompBase::cNumStates];
uint m_dst_ofs;
uint m_step;
uint m_block_step;
uint m_initial_step;
uint m_block_index;
int m_match_hist0;
int m_match_hist1;
int m_match_hist2;
int m_match_hist3;
uint m_cur_state;
uint m_start_block_dst_ofs;
uint m_prev_char;
uint m_prev_prev_char;
uint m_block_type;
const uint8 *m_pFlush_src;
size_t m_flush_num_bytes_remaining;
size_t m_flush_n;
uint m_seed_bytes_to_ignore_when_flushing;
uint m_file_src_file_adler32;
uint m_rep_lit0;
uint m_match_len;
uint m_match_slot;
uint m_extra_bits;
uint m_num_extra_bits;
uint m_src_ofs;
const uint8* m_pCopy_src;
uint m_num_raw_bytes_remaining;
uint m_debug_is_match;
uint m_debug_match_len;
uint m_debug_match_dist;
uint m_debug_lit;
lzham_decompress_status_t m_z_last_status;
uint m_z_first_call;
uint m_z_has_flushed;
uint m_z_cmf;
uint m_z_flg;
uint m_z_dict_adler32;
uint m_tmp;
};
// Ordinarily I dislike macros like this, but in this case I think using them makes the decompression function easier to follow.
// Coroutine helpers.
#define LZHAM_CR_INITIAL_STATE 0
#define LZHAM_CR_BEGIN(state) switch( state ) { case LZHAM_CR_INITIAL_STATE:
#define LZHAM_CR_RETURN(state, result) do { state = __LINE__; return (result); case __LINE__:; } while (0)
#define LZHAM_CR_FINISH }
// Helpers to save/restore local variables (hopefully CPU registers) to memory.
#define LZHAM_RESTORE_STATE LZHAM_RESTORE_LOCAL_STATE \
match_hist0 = m_match_hist0; match_hist1 = m_match_hist1; match_hist2 = m_match_hist2; match_hist3 = m_match_hist3; \
cur_state = m_cur_state; prev_char = m_prev_char; prev_prev_char = m_prev_prev_char; dst_ofs = m_dst_ofs;
#define LZHAM_SAVE_STATE LZHAM_SAVE_LOCAL_STATE \
m_match_hist0 = match_hist0; m_match_hist1 = match_hist1; m_match_hist2 = match_hist2; m_match_hist3 = match_hist3; \
m_cur_state = cur_state; m_prev_char = prev_char; m_prev_prev_char = prev_prev_char; m_dst_ofs = dst_ofs;
// Helper that coroutine returns to the caller with a request for more input bytes.
#define LZHAM_DECODE_NEEDS_BYTES \
LZHAM_SAVE_STATE \
for ( ; ; ) \
{ \
*m_pIn_buf_size = static_cast<size_t>(m_codec.decode_get_bytes_consumed()); \
*m_pOut_buf_size = 0; \
LZHAM_CR_RETURN(m_state, LZHAM_DECOMP_STATUS_NEEDS_MORE_INPUT); \
m_codec.decode_set_input_buffer(m_pIn_buf, *m_pIn_buf_size, m_pIn_buf, m_no_more_input_bytes_flag); \
if ((m_codec.m_decode_buf_eof) || (m_codec.m_decode_buf_size)) break; \
} \
LZHAM_RESTORE_STATE
#if LZHAM_PLATFORM_X360
#define LZHAM_BULK_MEMCPY XMemCpy
#define LZHAM_MEMCPY memcpy
#else
#define LZHAM_BULK_MEMCPY memcpy
#define LZHAM_MEMCPY memcpy
#endif
// Flush the output buffer/dictionary by doing a coroutine return to the caller.
// The caller must permit the decompressor to flush total_bytes from the dictionary, or (in the
// case of corrupted data, or a bug) we must report a DEST_BUF_TOO_SMALL error.
#define LZHAM_FLUSH_OUTPUT_BUFFER(total_bytes) \
LZHAM_SAVE_STATE \
m_pFlush_src = m_pDecomp_buf + m_seed_bytes_to_ignore_when_flushing; \
m_flush_num_bytes_remaining = total_bytes - m_seed_bytes_to_ignore_when_flushing; \
m_seed_bytes_to_ignore_when_flushing = 0; \
while (m_flush_num_bytes_remaining) \
{ \
m_flush_n = LZHAM_MIN(m_flush_num_bytes_remaining, *m_pOut_buf_size); \
if (0 == (m_params.m_decompress_flags & LZHAM_DECOMP_FLAG_COMPUTE_ADLER32)) \
{ \
LZHAM_BULK_MEMCPY(m_pOut_buf, m_pFlush_src, m_flush_n); \
} \
else \
{ \
size_t copy_ofs = 0; \
while (copy_ofs < m_flush_n) \
{ \
const uint cBytesToMemCpyPerIteration = 8192U; \
size_t bytes_to_copy = LZHAM_MIN((size_t)(m_flush_n - copy_ofs), cBytesToMemCpyPerIteration); \
LZHAM_MEMCPY(m_pOut_buf + copy_ofs, m_pFlush_src + copy_ofs, bytes_to_copy); \
m_decomp_adler32 = adler32(m_pFlush_src + copy_ofs, bytes_to_copy, m_decomp_adler32); \
copy_ofs += bytes_to_copy; \
} \
} \
*m_pIn_buf_size = static_cast<size_t>(m_codec.decode_get_bytes_consumed()); \
*m_pOut_buf_size = m_flush_n; \
LZHAM_CR_RETURN(m_state, m_flush_n ? LZHAM_DECOMP_STATUS_NOT_FINISHED : LZHAM_DECOMP_STATUS_HAS_MORE_OUTPUT); \
m_codec.decode_set_input_buffer(m_pIn_buf, *m_pIn_buf_size, m_pIn_buf, m_no_more_input_bytes_flag); \
m_pFlush_src += m_flush_n; \
m_flush_num_bytes_remaining -= m_flush_n; \
} \
LZHAM_RESTORE_STATE \
#if LZHAM_USE_ALL_ARITHMETIC_CODING
#define LZHAM_DECOMPRESS_DECODE_ADAPTIVE_SYMBOL(codec, result, model) LZHAM_SYMBOL_CODEC_DECODE_ADAPTIVE_ARITHMETIC(codec, result, model)
#else
#define LZHAM_DECOMPRESS_DECODE_ADAPTIVE_SYMBOL(codec, result, model) LZHAM_SYMBOL_CODEC_DECODE_ADAPTIVE_HUFFMAN(codec, result, model)
#endif
//------------------------------------------------------------------------------------------------------------------
void lzham_decompressor::init()
{
m_lzBase.init_position_slots(m_params.m_dict_size_log2);
#ifdef LZHAM_LZDEBUG
if (m_pDecomp_buf)
memset(m_pDecomp_buf, 0xCE, 1U << m_params.m_dict_size_log2);
#endif
m_state = LZHAM_CR_INITIAL_STATE;
m_step = 0;
m_block_step = 0;
m_block_index = 0;
m_initial_step = 0;
m_dst_ofs = 0;
m_pIn_buf = NULL;
m_pIn_buf_size = NULL;
m_pOut_buf = NULL;
m_pOut_buf_size = NULL;
m_no_more_input_bytes_flag = false;
m_status = LZHAM_DECOMP_STATUS_NOT_FINISHED;
m_pOrig_out_buf = NULL;
m_orig_out_buf_size = 0;
m_decomp_adler32 = cInitAdler32;
m_seed_bytes_to_ignore_when_flushing = 0;
m_z_last_status = LZHAM_DECOMP_STATUS_NOT_FINISHED;
m_z_first_call = 1;
m_z_has_flushed = 0;
m_z_cmf = 0;
m_z_flg = 0;
m_z_dict_adler32 = 0;
m_tmp = 0;
}
void lzham_decompressor::reset_all_tables()
{
m_lit_table[0].reset();
for (uint i = 1; i < LZHAM_ARRAY_SIZE(m_lit_table); i++)
m_lit_table[i] = m_lit_table[0];
m_delta_lit_table[0].reset();
for (uint i = 1; i < LZHAM_ARRAY_SIZE(m_delta_lit_table); i++)
m_delta_lit_table[i] = m_delta_lit_table[0];
m_main_table.reset();
for (uint i = 0; i < LZHAM_ARRAY_SIZE(m_rep_len_table); i++)
m_rep_len_table[i].reset();
for (uint i = 0; i < LZHAM_ARRAY_SIZE(m_large_len_table); i++)
m_large_len_table[i].reset();
m_dist_lsb_table.reset();
for (uint i = 0; i < LZHAM_ARRAY_SIZE(m_is_match_model); i++)
m_is_match_model[i].clear();
for (uint i = 0; i < CLZDecompBase::cNumStates; i++)
{
m_is_rep_model[i].clear();
m_is_rep0_model[i].clear();
m_is_rep0_single_byte_model[i].clear();
m_is_rep1_model[i].clear();
m_is_rep2_model[i].clear();
}
}
void lzham_decompressor::reset_huffman_table_update_rates()
{
for (uint i = 0; i < LZHAM_ARRAY_SIZE(m_lit_table); i++)
m_lit_table[i].reset_update_rate();
for (uint i = 0; i < LZHAM_ARRAY_SIZE(m_delta_lit_table); i++)
m_delta_lit_table[i].reset_update_rate();
m_main_table.reset_update_rate();
for (uint i = 0; i < LZHAM_ARRAY_SIZE(m_rep_len_table); i++)
m_rep_len_table[i].reset_update_rate();
for (uint i = 0; i < LZHAM_ARRAY_SIZE(m_large_len_table); i++)
m_large_len_table[i].reset_update_rate();
m_dist_lsb_table.reset_update_rate();
}
//------------------------------------------------------------------------------------------------------------------
// Decompression method. Implemented as a coroutine so it can be paused and resumed to support streaming.
//------------------------------------------------------------------------------------------------------------------
template<bool unbuffered>
lzham_decompress_status_t lzham_decompressor::decompress()
{
// Important: This function is a coroutine. ANY locals variables that need to be preserved across coroutine
// returns must be either be a member variable, or a local which is saved/restored to a member variable at
// the right times. (This makes this function difficult to follow and freaking ugly due to the macros of doom - but hey it works.)
// The most often used variables are in locals so the compiler hopefully puts them into CPU registers.
symbol_codec &codec = m_codec;
const uint dict_size = 1U << m_params.m_dict_size_log2;
const uint dict_size_mask = unbuffered ? UINT_MAX : (dict_size - 1);
int match_hist0 = 0, match_hist1 = 0, match_hist2 = 0, match_hist3 = 0;
uint cur_state = 0, prev_char = 0, prev_prev_char = 0, dst_ofs = 0;
const size_t out_buf_size = *m_pOut_buf_size;
uint8* pDst = unbuffered ? reinterpret_cast<uint8*>(m_pOut_buf) : reinterpret_cast<uint8*>(m_pDecomp_buf);
uint8* pDst_end = unbuffered ? (reinterpret_cast<uint8*>(m_pOut_buf) + out_buf_size) : (reinterpret_cast<uint8*>(m_pDecomp_buf) + dict_size);
LZHAM_SYMBOL_CODEC_DECODE_DECLARE(codec);
#define LZHAM_SAVE_LOCAL_STATE
#define LZHAM_RESTORE_LOCAL_STATE
// Important: Do not use any switch() statements below here.
LZHAM_CR_BEGIN(m_state)
if ((!unbuffered) && (m_params.m_num_seed_bytes))
{
LZHAM_BULK_MEMCPY(pDst, m_params.m_pSeed_bytes, m_params.m_num_seed_bytes);
dst_ofs += m_params.m_num_seed_bytes;
if (dst_ofs >= dict_size)
dst_ofs = 0;
else
m_seed_bytes_to_ignore_when_flushing = dst_ofs;
}
if (!m_codec.start_decoding(m_pIn_buf, *m_pIn_buf_size, m_no_more_input_bytes_flag, NULL, NULL))
return LZHAM_DECOMP_STATUS_FAILED_INITIALIZING;
LZHAM_SYMBOL_CODEC_DECODE_BEGIN(codec);
{
bool fast_table_updating, use_polar_codes;
if (m_params.m_decompress_flags & LZHAM_DECOMP_FLAG_READ_ZLIB_STREAM)
{
uint check;
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, m_z_cmf, 8);
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, m_z_flg, 8);
check = ((m_z_cmf << 8) + m_z_flg) % 31;
if ((check != 0) || ((m_z_cmf & 15) != LZHAM_Z_LZHAM))
return LZHAM_DECOMP_STATUS_FAILED_BAD_ZLIB_HEADER;
if (m_z_flg & 32)
{
if ((!m_params.m_pSeed_bytes) || (unbuffered))
return LZHAM_DECOMP_STATUS_FAILED_NEED_SEED_BYTES;
m_z_dict_adler32 = 0;
for (m_tmp = 0; m_tmp < 4; ++m_tmp)
{
uint n; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, n, 8);
m_z_dict_adler32 = (m_z_dict_adler32 << 8) | n;
}
if (adler32(m_params.m_pSeed_bytes, m_params.m_num_seed_bytes) != m_z_dict_adler32)
return LZHAM_DECOMP_STATUS_FAILED_BAD_SEED_BYTES;
}
}
{
uint tmp;
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, tmp, 2);
fast_table_updating = (tmp & 2) != 0;
use_polar_codes = (tmp & 1) != 0;
}
bool succeeded = m_lit_table[0].init(false, 256, fast_table_updating, use_polar_codes);
for (uint i = 1; i < LZHAM_ARRAY_SIZE(m_lit_table); i++)
succeeded = succeeded && m_lit_table[i].assign(m_lit_table[0]);
succeeded = succeeded && m_delta_lit_table[0].init(false, 256, fast_table_updating, use_polar_codes);
for (uint i = 1; i < LZHAM_ARRAY_SIZE(m_delta_lit_table); i++)
succeeded = succeeded && m_delta_lit_table[i].assign(m_delta_lit_table[0]);
succeeded = succeeded && m_main_table.init(false, CLZDecompBase::cLZXNumSpecialLengths + (m_lzBase.m_num_lzx_slots - CLZDecompBase::cLZXLowestUsableMatchSlot) * 8, fast_table_updating, use_polar_codes);
for (uint i = 0; i < 2; i++)
{
succeeded = succeeded && m_rep_len_table[i].init(false, CLZDecompBase::cNumHugeMatchCodes + (CLZDecompBase::cMaxMatchLen - CLZDecompBase::cMinMatchLen + 1), fast_table_updating, use_polar_codes);
succeeded = succeeded && m_large_len_table[i].init(false, CLZDecompBase::cNumHugeMatchCodes + CLZDecompBase::cLZXNumSecondaryLengths, fast_table_updating, use_polar_codes);
}
succeeded = succeeded && m_dist_lsb_table.init(false, 16, fast_table_updating, use_polar_codes);
if (!succeeded)
return LZHAM_DECOMP_STATUS_FAILED_INITIALIZING;
for (uint i = 0; i < LZHAM_ARRAY_SIZE(m_is_match_model); i++)
m_is_match_model[i].clear();
for (uint i = 0; i < CLZDecompBase::cNumStates; i++)
{
m_is_rep_model[i].clear();
m_is_rep0_model[i].clear();
m_is_rep0_single_byte_model[i].clear();
m_is_rep1_model[i].clear();
m_is_rep2_model[i].clear();
}
}
// Output block loop.
do
{
#ifdef LZHAM_LZDEBUG
uint outer_sync_marker; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, k, 12);
LZHAM_VERIFY(outer_sync_marker == 166);
#endif
// Decode block type.
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, m_block_type, CLZDecompBase::cBlockHeaderBits);
if (m_block_type == CLZDecompBase::cSyncBlock)
{
// Sync block
// Reset either the symbol table update rates, or all statistics, then force a coroutine return to give the caller a chance to handle the output right now.
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, m_tmp, CLZDecompBase::cBlockFlushTypeBits);
if (m_tmp == 1)
reset_huffman_table_update_rates();
else if (m_tmp == 2)
reset_all_tables();
LZHAM_SYMBOL_CODEC_DECODE_ALIGN_TO_BYTE(codec);
uint n; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, n, 16);
if (n != 0)
{
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
*m_pIn_buf_size = static_cast<size_t>(codec.decode_get_bytes_consumed());
*m_pOut_buf_size = 0;
for ( ; ; ) { LZHAM_CR_RETURN(m_state, LZHAM_DECOMP_STATUS_FAILED_BAD_SYNC_BLOCK); }
}
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, n, 16);
if (n != 0xFFFF)
{
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
*m_pIn_buf_size = static_cast<size_t>(codec.decode_get_bytes_consumed());
*m_pOut_buf_size = 0;
for ( ; ; ) { LZHAM_CR_RETURN(m_state, LZHAM_DECOMP_STATUS_FAILED_BAD_SYNC_BLOCK); }
}
if (m_tmp == 2)
{
// It's a full flush, so immediately give caller whatever output we have. Also gives the caller a chance to reposition the input stream ptr somewhere else before continuing.
// It would be nice to do this with partial flushes too, but the current way the output buffer is flushed makes this tricky.
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
if ((!unbuffered) && (dst_ofs))
{
LZHAM_FLUSH_OUTPUT_BUFFER(dst_ofs);
}
else
{
*m_pIn_buf_size = static_cast<size_t>(codec.decode_get_bytes_consumed());
*m_pOut_buf_size = dst_ofs;
LZHAM_SAVE_STATE
LZHAM_CR_RETURN(m_state, LZHAM_DECOMP_STATUS_NOT_FINISHED);
LZHAM_RESTORE_STATE
m_codec.decode_set_input_buffer(m_pIn_buf, *m_pIn_buf_size, m_pIn_buf, m_no_more_input_bytes_flag);
}
LZHAM_SYMBOL_CODEC_DECODE_BEGIN(codec);
dst_ofs = 0;
}
}
else if (m_block_type == CLZDecompBase::cRawBlock)
{
// Raw block handling is complex because we ultimately want to (safely) handle as many bytes as possible using a small number of memcpy()'s.
uint num_raw_bytes_remaining;
num_raw_bytes_remaining = 0;
#undef LZHAM_SAVE_LOCAL_STATE
#undef LZHAM_RESTORE_LOCAL_STATE
#define LZHAM_SAVE_LOCAL_STATE m_num_raw_bytes_remaining = num_raw_bytes_remaining;
#define LZHAM_RESTORE_LOCAL_STATE num_raw_bytes_remaining = m_num_raw_bytes_remaining;
// Determine how large this raw block is.
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, num_raw_bytes_remaining, 24);
// Get and verify raw block length check bits.
uint num_raw_bytes_check_bits; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, num_raw_bytes_check_bits, 8);
uint raw_bytes_remaining0, raw_bytes_remaining1, raw_bytes_remaining2;
raw_bytes_remaining0 = num_raw_bytes_remaining & 0xFF;
raw_bytes_remaining1 = (num_raw_bytes_remaining >> 8) & 0xFF;
raw_bytes_remaining2 = (num_raw_bytes_remaining >> 16) & 0xFF;
if (num_raw_bytes_check_bits != ((raw_bytes_remaining0 ^ raw_bytes_remaining1) ^ raw_bytes_remaining2))
{
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
*m_pIn_buf_size = static_cast<size_t>(codec.decode_get_bytes_consumed());
*m_pOut_buf_size = 0;
for ( ; ; ) { LZHAM_CR_RETURN(m_state, LZHAM_DECOMP_STATUS_FAILED_BAD_RAW_BLOCK); }
}
num_raw_bytes_remaining++;
// Discard any partial bytes from the bit buffer (align up to the next byte).
LZHAM_SYMBOL_CODEC_DECODE_ALIGN_TO_BYTE(codec);
// Flush any full bytes from the bit buffer.
do
{
int b;
LZHAM_SYMBOL_CODEC_DECODE_REMOVE_BYTE_FROM_BIT_BUF(codec, b);
if (b < 0)
break;
if ((unbuffered) && (dst_ofs >= out_buf_size))
{
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
*m_pIn_buf_size = static_cast<size_t>(codec.decode_get_bytes_consumed());
*m_pOut_buf_size = 0;
for ( ; ; ) { LZHAM_CR_RETURN(m_state, LZHAM_DECOMP_STATUS_FAILED_DEST_BUF_TOO_SMALL); }
}
pDst[dst_ofs++] = static_cast<uint8>(b);
if ((!unbuffered) && (dst_ofs > dict_size_mask))
{
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
LZHAM_FLUSH_OUTPUT_BUFFER(dict_size);
LZHAM_SYMBOL_CODEC_DECODE_BEGIN(codec);
dst_ofs = 0;
}
num_raw_bytes_remaining--;
} while (num_raw_bytes_remaining);
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
// Now handle the bulk of the raw data with memcpy().
while (num_raw_bytes_remaining)
{
uint64 in_buf_ofs, in_buf_remaining;
in_buf_ofs = codec.decode_get_bytes_consumed();
in_buf_remaining = *m_pIn_buf_size - in_buf_ofs;
while (!in_buf_remaining)
{
// We need more bytes from the caller.
*m_pIn_buf_size = static_cast<size_t>(in_buf_ofs);
*m_pOut_buf_size = 0;
if (m_no_more_input_bytes_flag)
{
for ( ; ; ) { LZHAM_CR_RETURN(m_state, LZHAM_DECOMP_STATUS_FAILED_EXPECTED_MORE_RAW_BYTES); }
}
LZHAM_SAVE_STATE
LZHAM_CR_RETURN(m_state, LZHAM_DECOMP_STATUS_NEEDS_MORE_INPUT);
LZHAM_RESTORE_STATE
m_codec.decode_set_input_buffer(m_pIn_buf, *m_pIn_buf_size, m_pIn_buf, m_no_more_input_bytes_flag);
in_buf_ofs = 0;
in_buf_remaining = *m_pIn_buf_size;
}
// Determine how many bytes we can safely memcpy() in a single call.
uint num_bytes_to_copy;
num_bytes_to_copy = static_cast<uint>(LZHAM_MIN(num_raw_bytes_remaining, in_buf_remaining));
if (!unbuffered)
num_bytes_to_copy = LZHAM_MIN(num_bytes_to_copy, dict_size - dst_ofs);
if ((unbuffered) && ((dst_ofs + num_bytes_to_copy) > out_buf_size))
{
// Output buffer is not large enough.
*m_pIn_buf_size = static_cast<size_t>(in_buf_ofs);
*m_pOut_buf_size = 0;
for ( ; ; ) { LZHAM_CR_RETURN(m_state, LZHAM_DECOMP_STATUS_FAILED_DEST_BUF_TOO_SMALL); }
}
// Copy the raw bytes.
LZHAM_BULK_MEMCPY(pDst + dst_ofs, m_pIn_buf + in_buf_ofs, num_bytes_to_copy);
in_buf_ofs += num_bytes_to_copy;
num_raw_bytes_remaining -= num_bytes_to_copy;
codec.decode_set_input_buffer(m_pIn_buf, *m_pIn_buf_size, m_pIn_buf + in_buf_ofs, m_no_more_input_bytes_flag);
dst_ofs += num_bytes_to_copy;
if ((!unbuffered) && (dst_ofs > dict_size_mask))
{
LZHAM_ASSERT(dst_ofs == dict_size);
LZHAM_FLUSH_OUTPUT_BUFFER(dict_size);
dst_ofs = 0;
}
}
LZHAM_SYMBOL_CODEC_DECODE_BEGIN(codec);
#undef LZHAM_SAVE_LOCAL_STATE
#undef LZHAM_RESTORE_LOCAL_STATE
#define LZHAM_SAVE_LOCAL_STATE
#define LZHAM_RESTORE_LOCAL_STATE
}
else if (m_block_type == CLZDecompBase::cCompBlock)
{
LZHAM_SYMBOL_CODEC_DECODE_ARITH_START(codec)
match_hist0 = 1;
match_hist1 = 1;
match_hist2 = 1;
match_hist3 = 1;
cur_state = 0;
prev_char = 0;
prev_prev_char = 0;
m_start_block_dst_ofs = dst_ofs;
{
uint block_flush_type; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, block_flush_type, CLZDecompBase::cBlockFlushTypeBits);
if (block_flush_type == 1)
reset_huffman_table_update_rates();
else if (block_flush_type == 2)
reset_all_tables();
}
#ifdef LZHAM_LZDEBUG
m_initial_step = m_step;
m_block_step = 0;
for ( ; ; m_step++, m_block_step++)
#else
for ( ; ; )
#endif
{
#ifdef LZHAM_LZDEBUG
uint sync_marker; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, x, CLZDecompBase::cLZHAMDebugSyncMarkerBits);
LZHAM_VERIFY(sync_marker == CLZDecompBase::cLZHAMDebugSyncMarkerValue);
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, m_debug_is_match, 1);
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, m_debug_match_len, 17);
uint debug_cur_state; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, debug_cur_state, 4);
LZHAM_VERIFY(cur_state == debug_cur_state);
#endif
#ifdef _DEBUG
{
uint total_block_bytes = ((dst_ofs - m_start_block_dst_ofs) & dict_size_mask);
if (total_block_bytes > 0)
{
LZHAM_ASSERT(prev_char == pDst[(dst_ofs - 1) & dict_size_mask]);
}
else
{
LZHAM_ASSERT(prev_char == 0);
}
if (total_block_bytes > 1)
{
LZHAM_ASSERT(prev_prev_char == pDst[(dst_ofs - 2) & dict_size_mask]);
}
else
{
LZHAM_ASSERT(prev_prev_char == 0);
}
}
#endif
// Read "is match" bit.
uint match_model_index;
match_model_index = LZHAM_IS_MATCH_MODEL_INDEX(prev_char, cur_state);
LZHAM_ASSERT(match_model_index < LZHAM_ARRAY_SIZE(m_is_match_model));
uint is_match_bit; LZHAM_SYMBOL_CODEC_DECODE_ARITH_BIT(codec, is_match_bit, m_is_match_model[match_model_index]);
#ifdef LZHAM_LZDEBUG
LZHAM_VERIFY(is_match_bit == m_debug_is_match);
#endif
if (LZHAM_BUILTIN_EXPECT(!is_match_bit, 0))
{
// Handle literal.
#ifdef LZHAM_LZDEBUG
LZHAM_VERIFY(m_debug_match_len == 1);
#endif
#ifdef LZHAM_LZDEBUG
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, m_debug_lit, 8);
#endif
if ((unbuffered) && (LZHAM_BUILTIN_EXPECT(dst_ofs >= out_buf_size, 0)))
{
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
*m_pIn_buf_size = static_cast<size_t>(codec.decode_get_bytes_consumed());
*m_pOut_buf_size = 0;
for ( ; ; ) { LZHAM_CR_RETURN(m_state, LZHAM_DECOMP_STATUS_FAILED_DEST_BUF_TOO_SMALL); }
}
if (LZHAM_BUILTIN_EXPECT(cur_state < CLZDecompBase::cNumLitStates, 1))
{
// Regular literal
uint lit_pred;
lit_pred = (prev_char >> (8 - CLZDecompBase::cNumLitPredBits / 2)) | (prev_prev_char >> (8 - CLZDecompBase::cNumLitPredBits / 2)) << (CLZDecompBase::cNumLitPredBits / 2);
uint r; LZHAM_DECOMPRESS_DECODE_ADAPTIVE_SYMBOL(codec, r, m_lit_table[lit_pred]);
pDst[dst_ofs] = static_cast<uint8>(r);
prev_prev_char = prev_char;
prev_char = r;
#ifdef LZHAM_LZDEBUG
LZHAM_VERIFY(pDst[dst_ofs] == m_debug_lit);
#endif
}
else
{
// Delta literal
uint match_hist0_ofs, rep_lit0, rep_lit1;
// Determine delta literal's partial context.
match_hist0_ofs = dst_ofs - match_hist0;
rep_lit0 = pDst[match_hist0_ofs & dict_size_mask];
rep_lit1 = pDst[(match_hist0_ofs - 1) & dict_size_mask];
uint lit_pred;
lit_pred = (rep_lit0 >> (8 - CLZDecompBase::cNumDeltaLitPredBits / 2)) |
((rep_lit1 >> (8 - CLZDecompBase::cNumDeltaLitPredBits / 2)) << CLZDecompBase::cNumDeltaLitPredBits / 2);
#undef LZHAM_SAVE_LOCAL_STATE
#undef LZHAM_RESTORE_LOCAL_STATE
#define LZHAM_SAVE_LOCAL_STATE m_rep_lit0 = rep_lit0;
#define LZHAM_RESTORE_LOCAL_STATE rep_lit0 = m_rep_lit0;
#ifdef LZHAM_LZDEBUG
uint debug_rep_lit0; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, debug_rep_lit0, 8);
LZHAM_VERIFY(debug_rep_lit0 == rep_lit0);
#endif
uint r; LZHAM_DECOMPRESS_DECODE_ADAPTIVE_SYMBOL(codec, r, m_delta_lit_table[lit_pred]);
r ^= rep_lit0;
pDst[dst_ofs] = static_cast<uint8>(r);
prev_prev_char = prev_char;
prev_char = r;
#ifdef LZHAM_LZDEBUG
LZHAM_VERIFY(pDst[dst_ofs] == m_debug_lit);
#endif
#undef LZHAM_SAVE_LOCAL_STATE
#undef LZHAM_RESTORE_LOCAL_STATE
#define LZHAM_SAVE_LOCAL_STATE
#define LZHAM_RESTORE_LOCAL_STATE
}
cur_state = s_literal_next_state[cur_state];
dst_ofs++;
if ((!unbuffered) && (LZHAM_BUILTIN_EXPECT(dst_ofs > dict_size_mask, 0)))
{
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
LZHAM_FLUSH_OUTPUT_BUFFER(dict_size);
LZHAM_SYMBOL_CODEC_DECODE_BEGIN(codec);
dst_ofs = 0;
}
}
else
{
// Handle match.
uint match_len;
match_len = 1;
#undef LZHAM_SAVE_LOCAL_STATE
#undef LZHAM_RESTORE_LOCAL_STATE
#define LZHAM_SAVE_LOCAL_STATE m_match_len = match_len;
#define LZHAM_RESTORE_LOCAL_STATE match_len = m_match_len;
// Determine if match is a rep_match, and if so what type.
uint is_rep; LZHAM_SYMBOL_CODEC_DECODE_ARITH_BIT(codec, is_rep, m_is_rep_model[cur_state]);
if (LZHAM_BUILTIN_EXPECT(is_rep, 1))
{
uint is_rep0; LZHAM_SYMBOL_CODEC_DECODE_ARITH_BIT(codec, is_rep0, m_is_rep0_model[cur_state]);
if (LZHAM_BUILTIN_EXPECT(is_rep0, 1))
{
uint is_rep0_len1; LZHAM_SYMBOL_CODEC_DECODE_ARITH_BIT(codec, is_rep0_len1, m_is_rep0_single_byte_model[cur_state]);
if (LZHAM_BUILTIN_EXPECT(is_rep0_len1, 1))
{
cur_state = (cur_state < CLZDecompBase::cNumLitStates) ? 9 : 11;
}
else
{
LZHAM_DECOMPRESS_DECODE_ADAPTIVE_SYMBOL(codec, match_len, m_rep_len_table[cur_state >= CLZDecompBase::cNumLitStates]);
match_len += CLZDecompBase::cMinMatchLen;
if (match_len == (CLZDecompBase::cMaxMatchLen + 1))
{
// Decode "huge" match length.
match_len = 0;
do
{
uint b; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, b, 1);
if (!b)
break;
match_len++;
} while (match_len < 3);
uint k; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, k, s_huge_match_code_len[match_len]);
match_len = s_huge_match_base_len[match_len] + k;
}
cur_state = (cur_state < CLZDecompBase::cNumLitStates) ? 8 : 11;
}
}
else
{
LZHAM_DECOMPRESS_DECODE_ADAPTIVE_SYMBOL(codec, match_len, m_rep_len_table[cur_state >= CLZDecompBase::cNumLitStates]);
match_len += CLZDecompBase::cMinMatchLen;
if (match_len == (CLZDecompBase::cMaxMatchLen + 1))
{
// Decode "huge" match length.
match_len = 0;
do
{
uint b; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, b, 1);
if (!b)
break;
match_len++;
} while (match_len < 3);
uint k; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, k, s_huge_match_code_len[match_len]);
match_len = s_huge_match_base_len[match_len] + k;
}
uint is_rep1; LZHAM_SYMBOL_CODEC_DECODE_ARITH_BIT(codec, is_rep1, m_is_rep1_model[cur_state]);
if (LZHAM_BUILTIN_EXPECT(is_rep1, 1))
{
uint temp = match_hist1;
match_hist1 = match_hist0;
match_hist0 = temp;
}
else
{
uint is_rep2; LZHAM_SYMBOL_CODEC_DECODE_ARITH_BIT(codec, is_rep2, m_is_rep2_model[cur_state]);
if (LZHAM_BUILTIN_EXPECT(is_rep2, 1))
{
// rep2
uint temp = match_hist2;
match_hist2 = match_hist1;
match_hist1 = match_hist0;
match_hist0 = temp;
}
else
{
// rep3
uint temp = match_hist3;
match_hist3 = match_hist2;
match_hist2 = match_hist1;
match_hist1 = match_hist0;
match_hist0 = temp;
}
}
cur_state = (cur_state < CLZDecompBase::cNumLitStates) ? 8 : 11;
}
}
else
{
// Handle normal/full match.
uint sym; LZHAM_DECOMPRESS_DECODE_ADAPTIVE_SYMBOL(codec, sym, m_main_table);
sym -= CLZDecompBase::cLZXNumSpecialLengths;
if (LZHAM_BUILTIN_EXPECT(static_cast<int>(sym) < 0, 0))
{
// Handle special symbols.
if (static_cast<int>(sym) == (CLZDecompBase::cLZXSpecialCodeEndOfBlockCode - CLZDecompBase::cLZXNumSpecialLengths))
break;
else
{
// Must be cLZXSpecialCodePartialStateReset.
match_hist0 = 1;
match_hist1 = 1;
match_hist2 = 1;
match_hist3 = 1;
cur_state = 0;
continue;
}
}
// Low 3 bits of symbol = match length category, higher bits = distance category.
match_len = (sym & 7) + 2;
uint match_slot;
match_slot = (sym >> 3) + CLZDecompBase::cLZXLowestUsableMatchSlot;
#undef LZHAM_SAVE_LOCAL_STATE
#undef LZHAM_RESTORE_LOCAL_STATE
#define LZHAM_SAVE_LOCAL_STATE m_match_len = match_len; m_match_slot = match_slot;
#define LZHAM_RESTORE_LOCAL_STATE match_len = m_match_len; match_slot = m_match_slot;
if (LZHAM_BUILTIN_EXPECT(match_len == 9, 0))
{
// Match is >= 9 bytes, decode the actual length.
uint e; LZHAM_DECOMPRESS_DECODE_ADAPTIVE_SYMBOL(codec, e, m_large_len_table[cur_state >= CLZDecompBase::cNumLitStates]);
match_len += e;
if (match_len == (CLZDecompBase::cMaxMatchLen + 1))
{
// Decode "huge" match length.
match_len = 0;
do
{
uint b; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, b, 1);
if (!b)
break;
match_len++;
} while (match_len < 3);
uint k; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, k, s_huge_match_code_len[match_len]);
match_len = s_huge_match_base_len[match_len] + k;
}
}
uint num_extra_bits;
num_extra_bits = m_lzBase.m_lzx_position_extra_bits[match_slot];
uint extra_bits;
#undef LZHAM_SAVE_LOCAL_STATE
#undef LZHAM_RESTORE_LOCAL_STATE
#define LZHAM_SAVE_LOCAL_STATE m_match_len = match_len; m_match_slot = match_slot; m_num_extra_bits = num_extra_bits;
#define LZHAM_RESTORE_LOCAL_STATE match_len = m_match_len; match_slot = m_match_slot; num_extra_bits = m_num_extra_bits;
if (LZHAM_BUILTIN_EXPECT(num_extra_bits < 3, 0))
{
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, extra_bits, num_extra_bits);
}
else
{
extra_bits = 0;
if (LZHAM_BUILTIN_EXPECT(num_extra_bits > 4, 1))
{
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, extra_bits, num_extra_bits - 4);
extra_bits <<= 4;
}
#undef LZHAM_SAVE_LOCAL_STATE
#undef LZHAM_RESTORE_LOCAL_STATE
#define LZHAM_SAVE_LOCAL_STATE m_match_len = match_len; m_match_slot = match_slot; m_extra_bits = extra_bits;
#define LZHAM_RESTORE_LOCAL_STATE match_len = m_match_len; match_slot = m_match_slot; extra_bits = m_extra_bits;
uint j; LZHAM_DECOMPRESS_DECODE_ADAPTIVE_SYMBOL(codec, j, m_dist_lsb_table);
extra_bits += j;
}
match_hist3 = match_hist2;
match_hist2 = match_hist1;
match_hist1 = match_hist0;
match_hist0 = m_lzBase.m_lzx_position_base[match_slot] + extra_bits;
cur_state = (cur_state < CLZDecompBase::cNumLitStates) ? CLZDecompBase::cNumLitStates : CLZDecompBase::cNumLitStates + 3;
#undef LZHAM_SAVE_LOCAL_STATE
#undef LZHAM_RESTORE_LOCAL_STATE
#define LZHAM_SAVE_LOCAL_STATE m_match_len = match_len;
#define LZHAM_RESTORE_LOCAL_STATE match_len = m_match_len;
}
// We have the match's length and distance, now do the copy.
#ifdef LZHAM_LZDEBUG
LZHAM_VERIFY(match_len == m_debug_match_len);
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, m_debug_match_dist, 25);
uint d; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, d, 4);
m_debug_match_dist = (m_debug_match_dist << 4) | d;
LZHAM_VERIFY((uint)match_hist0 == m_debug_match_dist);
#endif
if ( (unbuffered) && LZHAM_BUILTIN_EXPECT((((size_t)match_hist0 > dst_ofs) || ((dst_ofs + match_len) > out_buf_size)), 0) )
{
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
*m_pIn_buf_size = static_cast<size_t>(codec.decode_get_bytes_consumed());
*m_pOut_buf_size = 0;
for ( ; ; ) { LZHAM_CR_RETURN(m_state, LZHAM_DECOMP_STATUS_FAILED_BAD_CODE); }
}
uint src_ofs;
const uint8* pCopy_src;
src_ofs = (dst_ofs - match_hist0) & dict_size_mask;
pCopy_src = pDst + src_ofs;
#undef LZHAM_SAVE_LOCAL_STATE
#undef LZHAM_RESTORE_LOCAL_STATE
#define LZHAM_SAVE_LOCAL_STATE m_match_len = match_len; m_src_ofs = src_ofs; m_pCopy_src = pCopy_src;
#define LZHAM_RESTORE_LOCAL_STATE match_len = m_match_len; src_ofs = m_src_ofs; pCopy_src = m_pCopy_src;
if ( (!unbuffered) && LZHAM_BUILTIN_EXPECT( ((LZHAM_MAX(src_ofs, dst_ofs) + match_len) > dict_size_mask), 0) )
{
// Match source or destination wraps around the end of the dictionary to the beginning, so handle the copy one byte at a time.
do
{
uint8 c;
c = *pCopy_src++;
prev_prev_char = prev_char;
prev_char = c;
pDst[dst_ofs++] = c;
if (LZHAM_BUILTIN_EXPECT(pCopy_src == pDst_end, 0))
pCopy_src = pDst;
if (LZHAM_BUILTIN_EXPECT(dst_ofs > dict_size_mask, 0))
{
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
LZHAM_FLUSH_OUTPUT_BUFFER(dict_size);
LZHAM_SYMBOL_CODEC_DECODE_BEGIN(codec);
dst_ofs = 0;
}
match_len--;
} while (LZHAM_BUILTIN_EXPECT(match_len > 0, 1));
}
else
{
uint8* pCopy_dst = pDst + dst_ofs;
if (LZHAM_BUILTIN_EXPECT(match_hist0 == 1, 0))
{
// Handle byte runs.
uint8 c = *pCopy_src;
if (LZHAM_BUILTIN_EXPECT(match_len < 8, 1))
{
for (int i = match_len; i > 0; i--)
*pCopy_dst++ = c;
if (LZHAM_BUILTIN_EXPECT(match_len == 1, 1))
prev_prev_char = prev_char;
else
prev_prev_char = c;
}
else
{
memset(pCopy_dst, c, match_len);
prev_prev_char = c;
}
prev_char = c;
}
else if (LZHAM_BUILTIN_EXPECT(match_len == 1, 1))
{
// Handle single byte matches.
prev_prev_char = prev_char;
prev_char = *pCopy_src;
*pCopy_dst = static_cast<uint8>(prev_char);
}
else
{
// Handle matches of length 2 or higher.
uint bytes_to_copy = match_len - 2;
if (LZHAM_BUILTIN_EXPECT(((bytes_to_copy < 8) || ((int)bytes_to_copy > match_hist0)), 1))
{
for (int i = bytes_to_copy; i > 0; i--)
*pCopy_dst++ = *pCopy_src++;
}
else
{
LZHAM_MEMCPY(pCopy_dst, pCopy_src, bytes_to_copy);
pCopy_dst += bytes_to_copy;
pCopy_src += bytes_to_copy;
}
// Handle final 2 bytes of match specially, because we always track the last 2 bytes output in
// local variables (needed for computing context) to avoid load hit stores on some CPU's.
prev_prev_char = *pCopy_src++;
*pCopy_dst++ = static_cast<uint8>(prev_prev_char);
prev_char = *pCopy_src++;
*pCopy_dst++ = static_cast<uint8>(prev_char);
}
dst_ofs += match_len;
}
} // lit or match
#undef LZHAM_SAVE_LOCAL_STATE
#undef LZHAM_RESTORE_LOCAL_STATE
#define LZHAM_SAVE_LOCAL_STATE
#define LZHAM_RESTORE_LOCAL_STATE
} // for ( ; ; )
#ifdef LZHAM_LZDEBUG
uint end_sync_marker; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, end_sync_marker, 12);
LZHAM_VERIFY(end_sync_marker == 366);
#endif
LZHAM_SYMBOL_CODEC_DECODE_ALIGN_TO_BYTE(codec);
}
else if (m_block_type == CLZDecompBase::cEOFBlock)
{
// Received EOF.
m_status = LZHAM_DECOMP_STATUS_SUCCESS;
}
else
{
// This block type is currently undefined.
m_status = LZHAM_DECOMP_STATUS_FAILED_BAD_CODE;
}
m_block_index++;
} while (m_status == LZHAM_DECOMP_STATUS_NOT_FINISHED);
if ((!unbuffered) && (dst_ofs))
{
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
LZHAM_FLUSH_OUTPUT_BUFFER(dst_ofs);
LZHAM_SYMBOL_CODEC_DECODE_BEGIN(codec);
}
if (m_status == LZHAM_DECOMP_STATUS_SUCCESS)
{
LZHAM_SYMBOL_CODEC_DECODE_ALIGN_TO_BYTE(codec);
LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, m_file_src_file_adler32, 16);
uint l; LZHAM_SYMBOL_CODEC_DECODE_GET_BITS(codec, l, 16);
m_file_src_file_adler32 = (m_file_src_file_adler32 << 16) | l;
if (m_params.m_decompress_flags & LZHAM_DECOMP_FLAG_COMPUTE_ADLER32)
{
if (unbuffered)
{
m_decomp_adler32 = adler32(pDst, dst_ofs, cInitAdler32);
}
if (m_file_src_file_adler32 != m_decomp_adler32)
{
m_status = LZHAM_DECOMP_STATUS_FAILED_ADLER32;
}
}
else
{
m_decomp_adler32 = m_file_src_file_adler32;
}
}
LZHAM_SYMBOL_CODEC_DECODE_END(codec);
*m_pIn_buf_size = static_cast<size_t>(codec.stop_decoding());
*m_pOut_buf_size = unbuffered ? dst_ofs : 0;
LZHAM_CR_RETURN(m_state, m_status);
for ( ; ; )
{
*m_pIn_buf_size = 0;
*m_pOut_buf_size = 0;
LZHAM_CR_RETURN(m_state, m_status);
}
LZHAM_CR_FINISH
return m_status;
}
static bool check_params(const lzham_decompress_params *pParams)
{
if ((!pParams) || (pParams->m_struct_size != sizeof(lzham_decompress_params)))
return false;
if ((pParams->m_dict_size_log2 < CLZDecompBase::cMinDictSizeLog2) || (pParams->m_dict_size_log2 > CLZDecompBase::cMaxDictSizeLog2))
return false;
if (pParams->m_num_seed_bytes)
{
if (((pParams->m_decompress_flags & LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED) != 0) || (!pParams->m_pSeed_bytes))
return false;
if (pParams->m_num_seed_bytes > (1U << pParams->m_dict_size_log2))
return false;
}
return true;
}
lzham_decompress_state_ptr LZHAM_CDECL lzham_lib_decompress_init(const lzham_decompress_params *pParams)
{
LZHAM_ASSUME(CLZDecompBase::cMinDictSizeLog2 == LZHAM_MIN_DICT_SIZE_LOG2);
LZHAM_ASSUME(CLZDecompBase::cMaxDictSizeLog2 == LZHAM_MAX_DICT_SIZE_LOG2_X64);
if (!check_params(pParams))
return NULL;
lzham_decompressor *pState = lzham_new<lzham_decompressor>();
if (!pState)
return NULL;
pState->m_params = *pParams;
if (pState->m_params.m_decompress_flags & LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED)
{
pState->m_pRaw_decomp_buf = NULL;
pState->m_raw_decomp_buf_size = 0;
pState->m_pDecomp_buf = NULL;
}
else
{
uint32 decomp_buf_size = 1U << pState->m_params.m_dict_size_log2;
pState->m_pRaw_decomp_buf = static_cast<uint8*>(lzham_malloc(decomp_buf_size + 15));
if (!pState->m_pRaw_decomp_buf)
{
lzham_delete(pState);
return NULL;
}
pState->m_raw_decomp_buf_size = decomp_buf_size;
pState->m_pDecomp_buf = math::align_up_pointer(pState->m_pRaw_decomp_buf, 16);
}
pState->init();
return pState;
}
lzham_decompress_state_ptr LZHAM_CDECL lzham_lib_decompress_reinit(lzham_decompress_state_ptr p, const lzham_decompress_params *pParams)
{
if (!p)
return lzham_lib_decompress_init(pParams);
lzham_decompressor *pState = static_cast<lzham_decompressor *>(p);
if (!check_params(pParams))
return NULL;
if (pState->m_params.m_decompress_flags & LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED)
{
lzham_free(pState->m_pRaw_decomp_buf);
pState->m_pRaw_decomp_buf = NULL;
pState->m_raw_decomp_buf_size = 0;
pState->m_pDecomp_buf = NULL;
}
else
{
uint32 new_dict_size = 1U << pState->m_params.m_dict_size_log2;
if ((!pState->m_pRaw_decomp_buf) || (pState->m_raw_decomp_buf_size < new_dict_size))
{
uint8 *pNew_dict = static_cast<uint8*>(lzham_realloc(pState->m_pRaw_decomp_buf, new_dict_size + 15));
if (!pNew_dict)
return NULL;
pState->m_pRaw_decomp_buf = pNew_dict;
pState->m_raw_decomp_buf_size = new_dict_size;
pState->m_pDecomp_buf = math::align_up_pointer(pState->m_pRaw_decomp_buf, 16);
}
}
pState->m_params = *pParams;
pState->init();
return pState;
}
lzham_uint32 LZHAM_CDECL lzham_lib_decompress_deinit(lzham_decompress_state_ptr p)
{
lzham_decompressor *pState = static_cast<lzham_decompressor *>(p);
if (!pState)
return 0;
uint32 adler32 = pState->m_decomp_adler32;
lzham_free(pState->m_pRaw_decomp_buf);
lzham_delete(pState);
return adler32;
}
lzham_decompress_status_t LZHAM_CDECL lzham_lib_decompress(
lzham_decompress_state_ptr p,
const lzham_uint8 *pIn_buf, size_t *pIn_buf_size,
lzham_uint8 *pOut_buf, size_t *pOut_buf_size,
lzham_bool no_more_input_bytes_flag)
{
lzham_decompressor *pState = static_cast<lzham_decompressor *>(p);
if ((!pState) || (!pState->m_params.m_dict_size_log2) || (!pIn_buf_size) || (!pOut_buf_size))
{
return LZHAM_DECOMP_STATUS_INVALID_PARAMETER;
}
if ((*pIn_buf_size) && (!pIn_buf))
{
return LZHAM_DECOMP_STATUS_INVALID_PARAMETER;
}
if ((*pOut_buf_size) && (!pOut_buf))
{
return LZHAM_DECOMP_STATUS_INVALID_PARAMETER;
}
pState->m_pIn_buf = pIn_buf;
pState->m_pIn_buf_size = pIn_buf_size;
pState->m_pOut_buf = pOut_buf;
pState->m_pOut_buf_size = pOut_buf_size;
pState->m_no_more_input_bytes_flag = (no_more_input_bytes_flag != 0);
if (pState->m_params.m_decompress_flags & LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED)
{
if (!pState->m_pOrig_out_buf)
{
pState->m_pOrig_out_buf = pOut_buf;
pState->m_orig_out_buf_size = *pOut_buf_size;
}
else
{
if ((pState->m_pOrig_out_buf != pOut_buf) || (pState->m_orig_out_buf_size != *pOut_buf_size))
{
return LZHAM_DECOMP_STATUS_INVALID_PARAMETER;
}
}
}
lzham_decompress_status_t status;
if (pState->m_params.m_decompress_flags & LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED)
status = pState->decompress<true>();
else
status = pState->decompress<false>();
return status;
}
lzham_decompress_status_t LZHAM_CDECL lzham_lib_decompress_memory(const lzham_decompress_params *pParams, lzham_uint8* pDst_buf, size_t *pDst_len, const lzham_uint8* pSrc_buf, size_t src_len, lzham_uint32 *pAdler32)
{
if (!pParams)
return LZHAM_DECOMP_STATUS_INVALID_PARAMETER;
lzham_decompress_params params(*pParams);
params.m_decompress_flags |= LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED;
lzham_decompress_state_ptr pState = lzham_lib_decompress_init(&params);
if (!pState)
return LZHAM_DECOMP_STATUS_FAILED_INITIALIZING;
lzham_decompress_status_t status = lzham_lib_decompress(pState, pSrc_buf, &src_len, pDst_buf, pDst_len, true);
lzham_uint32 adler32 = lzham_lib_decompress_deinit(pState);
if (pAdler32)
*pAdler32 = adler32;
return status;
}
// ----------------- zlib-style API's
int LZHAM_CDECL lzham_lib_z_inflateInit(lzham_z_streamp pStream)
{
return lzham_lib_z_inflateInit2(pStream, LZHAM_Z_DEFAULT_WINDOW_BITS);
}
int LZHAM_CDECL lzham_lib_z_inflateInit2(lzham_z_streamp pStream, int window_bits)
{
if (!pStream)
return LZHAM_Z_STREAM_ERROR;
#ifdef LZHAM_Z_API_FORCE_WINDOW_BITS
window_bits = LZHAM_Z_API_FORCE_WINDOW_BITS;
#endif
int max_window_bits = LZHAM_64BIT_POINTERS ? LZHAM_MAX_DICT_SIZE_LOG2_X64 : LZHAM_MAX_DICT_SIZE_LOG2_X86;
if (labs(window_bits) > max_window_bits)
return LZHAM_Z_PARAM_ERROR;
if (labs(window_bits) < LZHAM_MIN_DICT_SIZE_LOG2)
window_bits = (window_bits < 0) ? -LZHAM_MIN_DICT_SIZE_LOG2 : LZHAM_MIN_DICT_SIZE_LOG2;
lzham_decompress_params params;
utils::zero_object(params);
params.m_struct_size = sizeof(lzham_decompress_params);
params.m_dict_size_log2 = labs(window_bits);
params.m_decompress_flags = LZHAM_DECOMP_FLAG_COMPUTE_ADLER32;
if (window_bits > 0)
params.m_decompress_flags |= LZHAM_DECOMP_FLAG_READ_ZLIB_STREAM;
lzham_decompress_state_ptr pState = lzham_lib_decompress_init(&params);
if (!pState)
return LZHAM_Z_MEM_ERROR;
pStream->state = static_cast<lzham_z_internal_state *>(pState);
pStream->data_type = 0;
pStream->adler32 = LZHAM_Z_ADLER32_INIT;
pStream->msg = NULL;
pStream->total_in = 0;
pStream->total_out = 0;
pStream->reserved = 0;
return LZHAM_Z_OK;
}
int LZHAM_CDECL lzham_lib_z_inflateReset(lzham_z_streamp pStream)
{
if ((!pStream) || (!pStream->state))
return LZHAM_Z_STREAM_ERROR;
lzham_decompress_state_ptr pState = static_cast<lzham_decompress_state_ptr>(pStream->state);
lzham_decompressor *pDecomp = static_cast<lzham_decompressor *>(pState);
lzham_decompress_params params(pDecomp->m_params);
if (!lzham_lib_decompress_reinit(pState, &params))
return LZHAM_Z_STREAM_ERROR;
return LZHAM_Z_OK;
}
int LZHAM_CDECL lzham_lib_z_inflate(lzham_z_streamp pStream, int flush)
{
if ((!pStream) || (!pStream->state))
return LZHAM_Z_STREAM_ERROR;
if ((flush == LZHAM_Z_PARTIAL_FLUSH) || (flush == LZHAM_Z_FULL_FLUSH))
flush = LZHAM_Z_SYNC_FLUSH;
if (flush)
{
if ((flush != LZHAM_Z_SYNC_FLUSH) && (flush != LZHAM_Z_FINISH))
return LZHAM_Z_STREAM_ERROR;
}
size_t orig_avail_in = pStream->avail_in;
lzham_decompress_state_ptr pState = static_cast<lzham_decompress_state_ptr>(pStream->state);
lzham_decompressor *pDecomp = static_cast<lzham_decompressor *>(pState);
if (pDecomp->m_z_last_status >= LZHAM_DECOMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE)
return LZHAM_Z_DATA_ERROR;
if (pDecomp->m_z_has_flushed && (flush != LZHAM_Z_FINISH))
return LZHAM_Z_STREAM_ERROR;
pDecomp->m_z_has_flushed |= (flush == LZHAM_Z_FINISH);
lzham_decompress_status_t status;
for ( ; ; )
{
size_t in_bytes = pStream->avail_in;
size_t out_bytes = pStream->avail_out;
lzham_bool no_more_input_bytes_flag = (flush == LZHAM_Z_FINISH);
status = lzham_lib_decompress(pState, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, no_more_input_bytes_flag);
pDecomp->m_z_last_status = status;
pStream->next_in += (uint)in_bytes;
pStream->avail_in -= (uint)in_bytes;
pStream->total_in += (uint)in_bytes;
pStream->adler32 = pDecomp->m_decomp_adler32;
pStream->next_out += (uint)out_bytes;
pStream->avail_out -= (uint)out_bytes;
pStream->total_out += (uint)out_bytes;
if (status >= LZHAM_DECOMP_STATUS_FIRST_FAILURE_CODE)
{
if (status == LZHAM_DECOMP_STATUS_FAILED_NEED_SEED_BYTES)
return LZHAM_Z_NEED_DICT;
else
return LZHAM_Z_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well).
}
if ((status == LZHAM_DECOMP_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
return LZHAM_Z_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input, or by setting flush to LZHAM_Z_FINISH.
else if (flush == LZHAM_Z_FINISH)
{
// Caller has indicated that all remaining input was at next_in, and all remaining output will fit entirely in next_out.
// (The output buffer at next_out MUST be large to hold the remaining uncompressed data when flush==LZHAM_Z_FINISH).
if (status == LZHAM_DECOMP_STATUS_SUCCESS)
return LZHAM_Z_STREAM_END;
// If status is LZHAM_DECOMP_STATUS_HAS_MORE_OUTPUT, there must be at least 1 more byte on the way but the caller to lzham_decompress() supplied an empty output buffer.
// Something is wrong because the caller's output buffer should be large enough to hold the entire decompressed stream when flush==LZHAM_Z_FINISH.
else if (status == LZHAM_DECOMP_STATUS_HAS_MORE_OUTPUT)
return LZHAM_Z_BUF_ERROR;
}
else if ((status == LZHAM_DECOMP_STATUS_SUCCESS) || (!pStream->avail_in) || (!pStream->avail_out))
break;
}
return (status == LZHAM_DECOMP_STATUS_SUCCESS) ? LZHAM_Z_STREAM_END : LZHAM_Z_OK;
}
int LZHAM_CDECL lzham_lib_z_inflateEnd(lzham_z_streamp pStream)
{
if (!pStream)
return LZHAM_Z_STREAM_ERROR;
lzham_decompress_state_ptr pState = static_cast<lzham_decompress_state_ptr>(pStream->state);
if (pState)
{
pStream->adler32 = lzham_lib_decompress_deinit(pState);
pStream->state = NULL;
}
return LZHAM_Z_OK;
}
int LZHAM_CDECL lzham_lib_z_uncompress(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len)
{
lzham_z_stream stream;
int status;
memset(&stream, 0, sizeof(stream));
// In case lzham_z_ulong is 64-bits (argh I hate longs).
if ((source_len | *pDest_len) > 0xFFFFFFFFU)
return LZHAM_Z_PARAM_ERROR;
stream.next_in = pSource;
stream.avail_in = (uint)source_len;
stream.next_out = pDest;
stream.avail_out = (uint)*pDest_len;
status = lzham_lib_z_inflateInit(&stream);
if (status != LZHAM_Z_OK)
return status;
status = lzham_lib_z_inflate(&stream, LZHAM_Z_FINISH);
if (status != LZHAM_Z_STREAM_END)
{
lzham_lib_z_inflateEnd(&stream);
return ((status == LZHAM_Z_BUF_ERROR) && (!stream.avail_in)) ? LZHAM_Z_DATA_ERROR : status;
}
*pDest_len = stream.total_out;
return lzham_lib_z_inflateEnd(&stream);
}
const char * LZHAM_CDECL lzham_lib_z_error(int err)
{
static struct
{
int m_err;
const char *m_pDesc;
}
s_error_descs[] =
{
{ LZHAM_Z_OK, "" },
{ LZHAM_Z_STREAM_END, "stream end" },
{ LZHAM_Z_NEED_DICT, "need dictionary" },
{ LZHAM_Z_ERRNO, "file error" },
{ LZHAM_Z_STREAM_ERROR, "stream error" },
{ LZHAM_Z_DATA_ERROR, "data error" },
{ LZHAM_Z_MEM_ERROR, "out of memory" },
{ LZHAM_Z_BUF_ERROR, "buf error" },
{ LZHAM_Z_VERSION_ERROR, "version error" },
{ LZHAM_Z_PARAM_ERROR, "parameter error" }
};
for (uint i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i)
if (s_error_descs[i].m_err == err)
return s_error_descs[i].m_pDesc;
return NULL;
}
lzham_z_ulong LZHAM_CDECL lzham_lib_z_adler32(lzham_z_ulong adler, const lzham_uint8 *ptr, size_t buf_len)
{
return adler32(ptr, buf_len, adler);
}
lzham_z_ulong LZHAM_CDECL lzham_lib_z_crc32(lzham_z_ulong crc, const lzham_uint8 *ptr, size_t buf_len)
{
return crc32(ptr, buf_len, crc);
}
} // namespace lzham