d074cc7c07
Plugin: ns7zip v2.0.0 Architectures: x86-ansi, x86-unicode, amd64-unicode License: LGPL-2.1-or-later
1518 lines
43 KiB
C++
1518 lines
43 KiB
C++
// LzxDecoder.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include <string.h>
|
|
// #include <stdio.h>
|
|
|
|
// #define SHOW_DEBUG_INFO
|
|
|
|
#ifdef SHOW_DEBUG_INFO
|
|
#include <stdio.h>
|
|
#define PRF(x) x
|
|
#else
|
|
#define PRF(x)
|
|
#endif
|
|
|
|
#include "../../../C/Alloc.h"
|
|
#include "../../../C/RotateDefs.h"
|
|
#include "../../../C/CpuArch.h"
|
|
|
|
#include "LzxDecoder.h"
|
|
|
|
|
|
#ifdef MY_CPU_X86_OR_AMD64
|
|
#if defined(MY_CPU_AMD64) \
|
|
|| defined(__SSE2__) \
|
|
|| defined(_M_IX86_FP) && (_M_IX86_FP >= 2) \
|
|
|| 0 && defined(_MSC_VER) && (_MSC_VER >= 1400) // set (1 &&) for debug
|
|
|
|
#if defined(__clang__) && (__clang_major__ >= 2) \
|
|
|| defined(__GNUC__) && (__GNUC__ >= 4) \
|
|
|| defined(_MSC_VER) && (_MSC_VER >= 1400)
|
|
#define Z7_LZX_X86_FILTER_USE_SSE2
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#ifdef Z7_LZX_X86_FILTER_USE_SSE2
|
|
// #ifdef MY_CPU_X86_OR_AMD64
|
|
#include <emmintrin.h> // SSE2
|
|
// #endif
|
|
#if defined(__clang__) || defined(__GNUC__)
|
|
typedef int ctz_type;
|
|
#define MY_CTZ(dest, mask) dest = __builtin_ctz((UInt32)(mask))
|
|
#else // #if defined(_MSC_VER)
|
|
#if (_MSC_VER >= 1600)
|
|
// #include <intrin.h>
|
|
#endif
|
|
typedef unsigned long ctz_type;
|
|
#define MY_CTZ(dest, mask) _BitScanForward(&dest, (mask));
|
|
#endif // _MSC_VER
|
|
#endif
|
|
|
|
// when window buffer is filled, we must wrap position to zero,
|
|
// and we want to wrap at same points where original-lzx must wrap.
|
|
// But the wrapping is possible in point where chunk is finished.
|
|
// Usually (chunk_size == 32KB), but (chunk_size != 32KB) also is allowed.
|
|
// So we don't use additional buffer space over required (winSize).
|
|
// And we can't use large overwrite after (len) in CopyLzMatch().
|
|
// But we are allowed to write 3 bytes after (len), because
|
|
// (delta <= _winSize - 3).
|
|
|
|
// #define k_Lz_OverwriteSize 0 // for debug : to disable overwrite
|
|
#define k_Lz_OverwriteSize 3 // = kNumReps
|
|
#if k_Lz_OverwriteSize > 0
|
|
// (k_Lz_OutBufSize_Add >= k_Lz_OverwriteSize) is required
|
|
// we use value 4 to simplify memset() code.
|
|
#define k_Lz_OutBufSize_Add (k_Lz_OverwriteSize + 1) // == 4
|
|
#else
|
|
#define k_Lz_OutBufSize_Add 0
|
|
#endif
|
|
|
|
// (len != 0)
|
|
// (0 < delta <= _winSize - 3)
|
|
Z7_FORCE_INLINE
|
|
void CopyLzMatch(Byte *dest, const Byte *src, UInt32 len, UInt32 delta)
|
|
{
|
|
if (delta >= 4)
|
|
{
|
|
#if k_Lz_OverwriteSize >= 3
|
|
// optimized code with overwrite to reduce the number of branches
|
|
#ifdef MY_CPU_LE_UNALIGN
|
|
*(UInt32 *)(void *)(dest) = *(const UInt32 *)(const void *)(src);
|
|
#else
|
|
dest[0] = src[0];
|
|
dest[1] = src[1];
|
|
dest[2] = src[2];
|
|
dest[3] = src[3];
|
|
#endif
|
|
len--;
|
|
src++;
|
|
dest++;
|
|
{
|
|
#else
|
|
// no overwrite in out buffer
|
|
dest[0] = src[0];
|
|
{
|
|
const unsigned m = (unsigned)len & 1;
|
|
src += m;
|
|
dest += m;
|
|
}
|
|
if (len &= ~(unsigned)1)
|
|
{
|
|
dest[0] = src[0];
|
|
dest[1] = src[1];
|
|
#endif
|
|
// len == 0 is allowed here
|
|
{
|
|
const unsigned m = (unsigned)len & 3;
|
|
src += m;
|
|
dest += m;
|
|
}
|
|
if (len &= ~(unsigned)3)
|
|
{
|
|
#ifdef MY_CPU_LE_UNALIGN
|
|
#if 1
|
|
*(UInt32 *)(void *)(dest) = *(const UInt32 *)(const void *)(src);
|
|
{
|
|
const unsigned m = (unsigned)len & 7;
|
|
dest += m;
|
|
src += m;
|
|
}
|
|
if (len &= ~(unsigned)7)
|
|
do
|
|
{
|
|
*(UInt32 *)(void *)(dest ) = *(const UInt32 *)(const void *)(src);
|
|
*(UInt32 *)(void *)(dest + 4) = *(const UInt32 *)(const void *)(src + 4);
|
|
src += 8;
|
|
dest += 8;
|
|
}
|
|
while (len -= 8);
|
|
#else
|
|
// gcc-11 -O3 for x64 generates incorrect code here
|
|
do
|
|
{
|
|
*(UInt32 *)(void *)(dest) = *(const UInt32 *)(const void *)(src);
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
while (len -= 4);
|
|
#endif
|
|
#else
|
|
do
|
|
{
|
|
const Byte b0 = src[0];
|
|
const Byte b1 = src[1];
|
|
dest[0] = b0;
|
|
dest[1] = b1;
|
|
const Byte b2 = src[2];
|
|
const Byte b3 = src[3];
|
|
dest[2] = b2;
|
|
dest[3] = b3;
|
|
src += 4;
|
|
dest += 4;
|
|
}
|
|
while (len -= 4);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else // (delta < 4)
|
|
{
|
|
const unsigned b0 = *src;
|
|
*dest = (Byte)b0;
|
|
if (len >= 2)
|
|
{
|
|
if (delta < 2)
|
|
{
|
|
dest += (unsigned)len & 1;
|
|
dest[0] = (Byte)b0;
|
|
dest[1] = (Byte)b0;
|
|
dest += (unsigned)len & 2;
|
|
if (len &= ~(unsigned)3)
|
|
{
|
|
#ifdef MY_CPU_LE_UNALIGN
|
|
#ifdef MY_CPU_64BIT
|
|
const UInt64 a = (UInt64)b0 * 0x101010101010101;
|
|
*(UInt32 *)(void *)dest = (UInt32)a;
|
|
dest += (unsigned)len & 7;
|
|
if (len &= ~(unsigned)7)
|
|
{
|
|
// *(UInt64 *)(void *)dest = a;
|
|
// dest += 8;
|
|
// len -= 8;
|
|
// if (len)
|
|
{
|
|
// const ptrdiff_t delta = (ptrdiff_t)dest & 7;
|
|
// dest -= delta;
|
|
do
|
|
{
|
|
*(UInt64 *)(void *)dest = a;
|
|
dest += 8;
|
|
}
|
|
while (len -= 8);
|
|
// dest += delta - 8;
|
|
// *(UInt64 *)(void *)dest = a;
|
|
}
|
|
}
|
|
#else
|
|
const UInt32 a = (UInt32)b0 * 0x1010101;
|
|
do
|
|
{
|
|
*(UInt32 *)(void *)dest = a;
|
|
dest += 4;
|
|
}
|
|
while (len -= 4);
|
|
#endif
|
|
#else
|
|
do
|
|
{
|
|
dest[0] = (Byte)b0;
|
|
dest[1] = (Byte)b0;
|
|
dest[2] = (Byte)b0;
|
|
dest[3] = (Byte)b0;
|
|
dest += 4;
|
|
}
|
|
while (len -= 4);
|
|
#endif
|
|
}
|
|
}
|
|
else if (delta == 2)
|
|
{
|
|
const unsigned m = (unsigned)len & 1;
|
|
len &= ~(unsigned)1;
|
|
src += m;
|
|
dest += m;
|
|
{
|
|
const Byte a0 = src[0];
|
|
const Byte a1 = src[1];
|
|
do
|
|
{
|
|
dest[0] = a0;
|
|
dest[1] = a1;
|
|
dest += 2;
|
|
}
|
|
while (len -= 2);
|
|
}
|
|
}
|
|
else /* if (delta == 3) */
|
|
{
|
|
const unsigned b1 = src[1];
|
|
dest[1] = (Byte)b1;
|
|
if (len -= 2)
|
|
{
|
|
const unsigned b2 = src[2];
|
|
dest += 2;
|
|
do
|
|
{
|
|
dest[0] = (Byte)b2; if (--len == 0) break;
|
|
dest[1] = (Byte)b0; if (--len == 0) break;
|
|
dest[2] = (Byte)b1;
|
|
dest += 3;
|
|
}
|
|
while (--len);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// #define Z7_LZX_SHOW_STAT
|
|
#ifdef Z7_LZX_SHOW_STAT
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
namespace NCompress {
|
|
namespace NLzx {
|
|
|
|
// #define Z7_LZX_SHOW_STAT
|
|
#ifdef Z7_LZX_SHOW_STAT
|
|
static UInt32 g_stats_Num_x86[3];
|
|
static UInt32 g_stats_NumTables;
|
|
static UInt32 g_stats_NumLits;
|
|
static UInt32 g_stats_NumAlign;
|
|
static UInt32 g_stats_main[kMainTableSize];
|
|
static UInt32 g_stats_len[kNumLenSymbols];
|
|
static UInt32 g_stats_main_levels[kNumHuffmanBits + 1];
|
|
static UInt32 g_stats_len_levels[kNumHuffmanBits + 1];
|
|
#define UPDATE_STAT(a) a
|
|
static void PrintVal(UInt32 v)
|
|
{
|
|
printf("\n : %9u", v);
|
|
}
|
|
static void PrintStat(const char *name, const UInt32 *a, size_t num)
|
|
{
|
|
printf("\n\n==== %s:", name);
|
|
UInt32 sum = 0;
|
|
size_t i;
|
|
for (i = 0; i < num; i++)
|
|
sum += a[i];
|
|
PrintVal(sum);
|
|
if (sum != 0)
|
|
{
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
if (i % 8 == 0)
|
|
printf("\n");
|
|
printf("\n%3x : %9u : %5.2f", (unsigned)i, (unsigned)a[i], (double)a[i] * 100 / sum);
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static struct CStat
|
|
{
|
|
~CStat()
|
|
{
|
|
PrintStat("x86_filter", g_stats_Num_x86, Z7_ARRAY_SIZE(g_stats_Num_x86));
|
|
printf("\nTables:"); PrintVal(g_stats_NumTables);
|
|
printf("\nLits:"); PrintVal(g_stats_NumLits);
|
|
printf("\nAlign:"); PrintVal(g_stats_NumAlign);
|
|
PrintStat("Main", g_stats_main, Z7_ARRAY_SIZE(g_stats_main));
|
|
PrintStat("Len", g_stats_len, Z7_ARRAY_SIZE(g_stats_len));
|
|
PrintStat("Main Levels", g_stats_main_levels, Z7_ARRAY_SIZE(g_stats_main_levels));
|
|
PrintStat("Len Levels", g_stats_len_levels, Z7_ARRAY_SIZE(g_stats_len_levels));
|
|
}
|
|
} g_stat;
|
|
#else
|
|
#define UPDATE_STAT(a)
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
3 p015 : ivb- : or r32,r32 / add r32,r32
|
|
4 p0156 : hsw+
|
|
5 p0156b: adl+
|
|
2 p0_5 : ivb- : shl r32,i8
|
|
2 p0__6 : hsw+
|
|
1 p5 : ivb- : jb
|
|
2 p0__6 : hsw+
|
|
2 p0_5 : wsm- : SSE2 : pcmpeqb : _mm_cmpeq_epi8
|
|
2 p_15 : snb-bdw
|
|
2 p01 : skl+
|
|
1 p0 : SSE2 : pmovmskb : _mm_movemask_epi8
|
|
*/
|
|
/*
|
|
v24.00: the code was fixed for more compatibility with original-ms-cab-decoder.
|
|
for ((Int32)translationSize >= 0) : LZX specification shows the code with signed Int32.
|
|
for ((Int32)translationSize < 0) : no specification for that case, but we support that case.
|
|
We suppose our code now is compatible with original-ms-cab-decoder.
|
|
|
|
Starting byte of data stream (real_pos == 0) is special corner case,
|
|
where we don't need any conversion (as in original-ms-cab-decoder).
|
|
Our optimization: we use unsigned (UInt32 pos) (pos = -1 - real_pos).
|
|
So (pos) is always negative: ((Int32)pos < 0).
|
|
It allows us to use simple comparison (v > pos) instead of more complex comparisons.
|
|
*/
|
|
// (p) will point 5 bytes after 0xe8 byte:
|
|
// pos == -1 - (p - 5 - data_start) == 4 + data_start - p
|
|
// (FILTER_PROCESSED_SIZE_DELTA == 4) is optimized value for better speed in some compilers:
|
|
#define FILTER_PROCESSED_SIZE_DELTA 4
|
|
|
|
#if defined(MY_CPU_X86_OR_AMD64) || defined(MY_CPU_ARM_OR_ARM64)
|
|
// optimized branch:
|
|
// size_t must be at least 32-bit for this branch.
|
|
#if 1 // use 1 for simpler code
|
|
// use integer (low 32 bits of pointer) instead of pointer
|
|
#define X86_FILTER_PREPARE processedSize4 = (UInt32)(size_t)(ptrdiff_t)data + \
|
|
(UInt32)(4 - FILTER_PROCESSED_SIZE_DELTA) - processedSize4;
|
|
#define X86_FILTER_CALC_pos(p) const UInt32 pos = processedSize4 - (UInt32)(size_t)(ptrdiff_t)p;
|
|
#else
|
|
// note: (dataStart) pointer can point out of array ranges:
|
|
#define X86_FILTER_PREPARE const Byte *dataStart = data + \
|
|
(4 - FILTER_PROCESSED_SIZE_DELTA) - processedSize4;
|
|
#define X86_FILTER_CALC_pos(p) const UInt32 pos = (UInt32)(size_t)(dataStart - p);
|
|
#endif
|
|
#else
|
|
// non-optimized branch for unusual platforms (16-bit size_t or unusual size_t):
|
|
#define X86_FILTER_PREPARE processedSize4 = \
|
|
(UInt32)(4 - FILTER_PROCESSED_SIZE_DELTA) - processedSize4;
|
|
#define X86_FILTER_CALC_pos(p) const UInt32 pos = processedSize4 - (UInt32)(size_t)(p - data);
|
|
#endif
|
|
|
|
#define X86_TRANSLATE_PRE(p) \
|
|
UInt32 v = GetUi32((p) - 4);
|
|
|
|
#define X86_TRANSLATE_POST(p) \
|
|
{ \
|
|
X86_FILTER_CALC_pos(p) \
|
|
if (v < translationSize) { \
|
|
UPDATE_STAT(g_stats_Num_x86[0]++;) \
|
|
v += pos + 1; \
|
|
SetUi32((p) - 4, v) \
|
|
} \
|
|
else if (v > pos) { \
|
|
UPDATE_STAT(g_stats_Num_x86[1]++;) \
|
|
v += translationSize; \
|
|
SetUi32((p) - 4, v) \
|
|
} else { UPDATE_STAT(g_stats_Num_x86[2]++;) } \
|
|
}
|
|
|
|
|
|
/*
|
|
if ( defined(Z7_LZX_X86_FILTER_USE_SSE2)
|
|
&& defined(Z7_LZX_X86_FILTER_USE_SSE2_ALIGNED))
|
|
the function can read up to aligned_for_32_up_from(size) bytes in (data).
|
|
*/
|
|
// processedSize < (1 << 30)
|
|
Z7_NO_INLINE
|
|
static void x86_Filter4(Byte *data, size_t size, UInt32 processedSize4, UInt32 translationSize)
|
|
{
|
|
const size_t kResidue = 10;
|
|
if (size <= kResidue)
|
|
return;
|
|
Byte * const lim = data + size - kResidue + 4;
|
|
const Byte save = lim[0];
|
|
lim[0] = 0xe8;
|
|
X86_FILTER_PREPARE
|
|
Byte *p = data;
|
|
|
|
#define FILTER_RETURN_IF_LIM(_p_) if (_p_ > lim) { lim[0] = save; return; }
|
|
|
|
#ifdef Z7_LZX_X86_FILTER_USE_SSE2
|
|
|
|
// sse2-aligned/sse2-unaligned provide same speed on real data.
|
|
// but the code is smaller for sse2-unaligned version.
|
|
// for debug : define it to get alternative version with aligned 128-bit reads:
|
|
// #define Z7_LZX_X86_FILTER_USE_SSE2_ALIGNED
|
|
|
|
#define FILTER_MASK_INT UInt32
|
|
#define FILTER_NUM_VECTORS_IN_CHUNK 2
|
|
#define FILTER_CHUNK_BYTES_OFFSET (16 * FILTER_NUM_VECTORS_IN_CHUNK - 5)
|
|
|
|
#ifdef Z7_LZX_X86_FILTER_USE_SSE2_ALIGNED
|
|
// aligned version doesn't uses additional space if buf size is aligned for 32
|
|
#define k_Filter_OutBufSize_Add 0
|
|
#define k_Filter_OutBufSize_AlignMask (16 * FILTER_NUM_VECTORS_IN_CHUNK - 1)
|
|
#define FILTER_LOAD_128(p) _mm_load_si128 ((const __m128i *)(const void *)(p))
|
|
#else
|
|
#define k_Filter_OutBufSize_Add (16 * FILTER_NUM_VECTORS_IN_CHUNK)
|
|
#define k_Filter_OutBufSize_AlignMask 0
|
|
#define FILTER_LOAD_128(p) _mm_loadu_si128((const __m128i *)(const void *)(p))
|
|
#endif
|
|
|
|
#define GET_E8_MASK(dest, dest1, p) \
|
|
{ \
|
|
__m128i v0 = FILTER_LOAD_128(p); \
|
|
__m128i v1 = FILTER_LOAD_128(p + 16); \
|
|
p += 16 * FILTER_NUM_VECTORS_IN_CHUNK; \
|
|
v0 = _mm_cmpeq_epi8(v0, k_e8_Vector); \
|
|
v1 = _mm_cmpeq_epi8(v1, k_e8_Vector); \
|
|
dest = (unsigned)_mm_movemask_epi8(v0); \
|
|
dest1 = (unsigned)_mm_movemask_epi8(v1); \
|
|
}
|
|
|
|
const __m128i k_e8_Vector = _mm_set1_epi32((Int32)(UInt32)0xe8e8e8e8);
|
|
for (;;)
|
|
{
|
|
// for debug: define it for smaller code:
|
|
// #define Z7_LZX_X86_FILTER_CALC_IN_LOOP
|
|
// without Z7_LZX_X86_FILTER_CALC_IN_LOOP, we can get faster and simpler loop
|
|
FILTER_MASK_INT mask;
|
|
{
|
|
FILTER_MASK_INT mask1;
|
|
do
|
|
{
|
|
GET_E8_MASK(mask, mask1, p)
|
|
#ifndef Z7_LZX_X86_FILTER_CALC_IN_LOOP
|
|
mask += mask1;
|
|
#else
|
|
mask |= mask1 << 16;
|
|
#endif
|
|
}
|
|
while (!mask);
|
|
|
|
#ifndef Z7_LZX_X86_FILTER_CALC_IN_LOOP
|
|
mask -= mask1;
|
|
mask |= mask1 << 16;
|
|
#endif
|
|
}
|
|
|
|
#ifdef Z7_LZX_X86_FILTER_USE_SSE2_ALIGNED
|
|
for (;;)
|
|
{
|
|
ctz_type index;
|
|
typedef
|
|
#ifdef MY_CPU_64BIT
|
|
UInt64
|
|
#else
|
|
UInt32
|
|
#endif
|
|
SUPER_MASK_INT;
|
|
SUPER_MASK_INT superMask;
|
|
{
|
|
MY_CTZ(index, mask);
|
|
Byte *p2 = p - FILTER_CHUNK_BYTES_OFFSET + (unsigned)index;
|
|
X86_TRANSLATE_PRE(p2)
|
|
superMask = ~(SUPER_MASK_INT)0x1f << index;
|
|
FILTER_RETURN_IF_LIM(p2)
|
|
X86_TRANSLATE_POST(p2)
|
|
mask &= (UInt32)superMask;
|
|
}
|
|
if (mask)
|
|
continue;
|
|
if (index <= FILTER_CHUNK_BYTES_OFFSET)
|
|
break;
|
|
{
|
|
FILTER_MASK_INT mask1;
|
|
GET_E8_MASK(mask, mask1, p)
|
|
mask &=
|
|
#ifdef MY_CPU_64BIT
|
|
(UInt32)(superMask >> 32);
|
|
#else
|
|
((FILTER_MASK_INT)0 - 1) << ((int)index - FILTER_CHUNK_BYTES_OFFSET);
|
|
#endif
|
|
mask |= mask1 << 16;
|
|
}
|
|
if (!mask)
|
|
break;
|
|
}
|
|
#else // ! Z7_LZX_X86_FILTER_USE_SSE2_ALIGNED
|
|
{
|
|
// we use simplest version without loop:
|
|
// for (;;)
|
|
{
|
|
ctz_type index;
|
|
MY_CTZ(index, mask);
|
|
/*
|
|
printf("\np=%p, mask=%8x, index = %2d, p + index = %x\n",
|
|
(p - 16 * FILTER_NUM_VECTORS_IN_CHUNK), (unsigned)mask,
|
|
(unsigned)index, (unsigned)((unsigned)(ptrdiff_t)(p - 16 * FILTER_NUM_VECTORS_IN_CHUNK) + index));
|
|
*/
|
|
p += (size_t)(unsigned)index - FILTER_CHUNK_BYTES_OFFSET;
|
|
FILTER_RETURN_IF_LIM(p)
|
|
// mask &= ~(FILTER_MASK_INT)0x1f << index; mask >>= index;
|
|
X86_TRANSLATE_PRE(p)
|
|
X86_TRANSLATE_POST(p)
|
|
// if (!mask) break; // p += 16 * FILTER_NUM_VECTORS_IN_CHUNK;
|
|
}
|
|
}
|
|
#endif // ! Z7_LZX_X86_FILTER_USE_SSE2_ALIGNED
|
|
}
|
|
|
|
#else // ! Z7_LZX_X86_FILTER_USE_SSE2
|
|
|
|
#define k_Filter_OutBufSize_Add 0
|
|
#define k_Filter_OutBufSize_AlignMask 0
|
|
|
|
|
|
for (;;)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (p[0] == 0xe8) { p += 5; break; }
|
|
if (p[1] == 0xe8) { p += 6; break; }
|
|
if (p[2] == 0xe8) { p += 7; break; }
|
|
p += 4;
|
|
if (p[-1] == 0xe8) { p += 4; break; }
|
|
}
|
|
FILTER_RETURN_IF_LIM(p)
|
|
X86_TRANSLATE_PRE(p)
|
|
X86_TRANSLATE_POST(p)
|
|
}
|
|
|
|
#endif // ! Z7_LZX_X86_FILTER_USE_SSE2
|
|
}
|
|
|
|
|
|
CDecoder::CDecoder() throw():
|
|
_win(NULL),
|
|
_isUncompressedBlock(false),
|
|
_skipByte(false),
|
|
_keepHistory(false),
|
|
_keepHistoryForNext(true),
|
|
_needAlloc(true),
|
|
_wimMode(false),
|
|
_numDictBits(15),
|
|
_unpackBlockSize(0),
|
|
_x86_translationSize(0),
|
|
_x86_buf(NULL),
|
|
_unpackedData(NULL)
|
|
{
|
|
{
|
|
// it's better to get empty virtual entries, if mispredicted value can be used:
|
|
memset(_reps, 0, kPosSlotOffset * sizeof(_reps[0]));
|
|
memset(_extra, 0, kPosSlotOffset);
|
|
#define SET_NUM_BITS(i) i // #define NUM_BITS_DELTA 31
|
|
_extra[kPosSlotOffset + 0] = SET_NUM_BITS(0);
|
|
_extra[kPosSlotOffset + 1] = SET_NUM_BITS(0);
|
|
// reps[0] = 0 - (kNumReps - 1);
|
|
// reps[1] = 1 - (kNumReps - 1);
|
|
UInt32 a = 2 - (kNumReps - 1);
|
|
UInt32 delta = 1;
|
|
unsigned i;
|
|
for (i = 0; i < kNumLinearPosSlotBits; i++)
|
|
{
|
|
_extra[(size_t)i * 2 + 2 + kPosSlotOffset] = (Byte)(SET_NUM_BITS(i));
|
|
_extra[(size_t)i * 2 + 3 + kPosSlotOffset] = (Byte)(SET_NUM_BITS(i));
|
|
_reps [(size_t)i * 2 + 2 + kPosSlotOffset] = a; a += delta;
|
|
_reps [(size_t)i * 2 + 3 + kPosSlotOffset] = a; a += delta;
|
|
delta += delta;
|
|
}
|
|
for (i = kNumLinearPosSlotBits * 2 + 2; i < kNumPosSlots; i++)
|
|
{
|
|
_extra[(size_t)i + kPosSlotOffset] = SET_NUM_BITS(kNumLinearPosSlotBits);
|
|
_reps [(size_t)i + kPosSlotOffset] = a;
|
|
a += (UInt32)1 << kNumLinearPosSlotBits;
|
|
}
|
|
}
|
|
}
|
|
|
|
CDecoder::~CDecoder() throw()
|
|
{
|
|
if (_needAlloc)
|
|
// BigFree
|
|
z7_AlignedFree
|
|
(_win);
|
|
z7_AlignedFree(_x86_buf);
|
|
}
|
|
|
|
HRESULT CDecoder::Flush() throw()
|
|
{
|
|
// UInt32 t = _x86_processedSize; for (int y = 0; y < 50; y++) { _x86_processedSize = t; // benchmark: (branch predicted)
|
|
if (_x86_translationSize != 0)
|
|
{
|
|
Byte *destData = _win + _writePos;
|
|
const UInt32 curSize = _pos - _writePos;
|
|
if (_keepHistoryForNext)
|
|
{
|
|
const size_t kChunkSize = (size_t)1 << 15;
|
|
if (curSize > kChunkSize)
|
|
return E_NOTIMPL;
|
|
if (!_x86_buf)
|
|
{
|
|
// (kChunkSize % 32 == 0) is required in some cases, because
|
|
// the filter can read data by 32-bytes chunks in some cases.
|
|
// if (chunk_size > (1 << 15)) is possible, then we must the code:
|
|
const size_t kAllocSize = kChunkSize + k_Filter_OutBufSize_Add;
|
|
_x86_buf = (Byte *)z7_AlignedAlloc(kAllocSize);
|
|
if (!_x86_buf)
|
|
return E_OUTOFMEMORY;
|
|
#if 0 != k_Filter_OutBufSize_Add || \
|
|
0 != k_Filter_OutBufSize_AlignMask
|
|
// x86_Filter4() can read after curSize.
|
|
// So we set all data to zero to prevent reading of uninitialized data:
|
|
memset(_x86_buf, 0, kAllocSize); // optional
|
|
#endif
|
|
}
|
|
// for (int yy = 0; yy < 1; yy++) // for debug
|
|
memcpy(_x86_buf, destData, curSize);
|
|
_unpackedData = _x86_buf;
|
|
destData = _x86_buf;
|
|
}
|
|
else
|
|
{
|
|
// x86_Filter4() can overread after (curSize),
|
|
// so we can do memset() after (curSize):
|
|
// k_Filter_OutBufSize_AlignMask also can be used
|
|
// if (!_overDict) memset(destData + curSize, 0, k_Filter_OutBufSize_Add);
|
|
}
|
|
x86_Filter4(destData, curSize, _x86_processedSize - FILTER_PROCESSED_SIZE_DELTA, _x86_translationSize);
|
|
_x86_processedSize += (UInt32)curSize;
|
|
if (_x86_processedSize >= ((UInt32)1 << 30))
|
|
_x86_translationSize = 0;
|
|
}
|
|
// }
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
// (NUM_DELTA_BYTES == 2) reduces the code in main loop.
|
|
#if 1
|
|
#define NUM_DELTA_BYTES 2
|
|
#else
|
|
#define NUM_DELTA_BYTES 0
|
|
#endif
|
|
|
|
#define NUM_DELTA_BIT_OFFSET_BITS (NUM_DELTA_BYTES * 8)
|
|
|
|
#if NUM_DELTA_BIT_OFFSET_BITS > 0
|
|
#define DECODE_ERROR_CODE 0
|
|
#define IS_OVERFLOW_bitOffset(bo) ((bo) >= 0)
|
|
// ( >= 0) comparison after bitOffset change gives simpler commands than ( > 0) comparison
|
|
#else
|
|
#define DECODE_ERROR_CODE 1
|
|
#define IS_OVERFLOW_bitOffset(bo) ((bo) > 0)
|
|
#endif
|
|
|
|
// (numBits != 0)
|
|
#define GET_VAL_BASE(numBits) (_value >> (32 - (numBits)))
|
|
|
|
#define Z7_LZX_HUFF_DECODE( sym, huff, kNumTableBits, move_pos_op, check_op, error_op) \
|
|
Z7_HUFF_DECODE_VAL_IN_HIGH32(sym, huff, kNumHuffmanBits, kNumTableBits, \
|
|
_value, check_op, error_op, move_pos_op, NORMALIZE, bs)
|
|
|
|
#define Z7_LZX_HUFF_DECODE_CHECK_YES(sym, huff, kNumTableBits, move_pos_op) \
|
|
Z7_LZX_HUFF_DECODE( sym, huff, kNumTableBits, move_pos_op, \
|
|
Z7_HUFF_DECODE_ERROR_SYM_CHECK_YES, { return DECODE_ERROR_CODE; })
|
|
|
|
#define Z7_LZX_HUFF_DECODE_CHECK_NO( sym, huff, kNumTableBits, move_pos_op) \
|
|
Z7_LZX_HUFF_DECODE( sym, huff, kNumTableBits, move_pos_op, \
|
|
Z7_HUFF_DECODE_ERROR_SYM_CHECK_NO, {})
|
|
|
|
#define NORMALIZE \
|
|
{ \
|
|
const Byte *ptr = _buf + (_bitOffset >> 4) * 2; \
|
|
/* _value = (((UInt32)GetUi16(ptr) << 16) | GetUi16(ptr + 2)) << (_bitOffset & 15); */ \
|
|
const UInt32 v = GetUi32(ptr); \
|
|
_value = rotlFixed (v, ((int)_bitOffset & 15) + 16); \
|
|
}
|
|
|
|
#define MOVE_POS(bs, numBits) \
|
|
{ \
|
|
_bitOffset += numBits; \
|
|
}
|
|
|
|
#define MOVE_POS_STAT(bs, numBits) \
|
|
{ \
|
|
UPDATE_STAT(g_stats_len_levels[numBits]++;) \
|
|
MOVE_POS(bs, numBits); \
|
|
}
|
|
|
|
#define MOVE_POS_CHECK(bs, numBits) \
|
|
{ \
|
|
if (IS_OVERFLOW_bitOffset(_bitOffset += numBits)) return DECODE_ERROR_CODE; \
|
|
}
|
|
|
|
#define MOVE_POS_CHECK_STAT(bs, numBits) \
|
|
{ \
|
|
UPDATE_STAT(g_stats_main_levels[numBits]++;) \
|
|
MOVE_POS_CHECK(bs, numBits) \
|
|
}
|
|
|
|
|
|
// (numBits == 0) is supported
|
|
|
|
#ifdef Z7_HUFF_USE_64BIT_LIMIT
|
|
|
|
#define MACRO_ReadBitsBig_pre(numBits) \
|
|
{ \
|
|
_bitOffset += (numBits); \
|
|
_value >>= 32 - (numBits); \
|
|
}
|
|
|
|
#else
|
|
|
|
#define MACRO_ReadBitsBig_pre(numBits) \
|
|
{ \
|
|
_bitOffset += (numBits); \
|
|
_value = (UInt32)((UInt32)_value >> 1 >> (31 ^ (numBits))); \
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#define MACRO_ReadBitsBig_add(dest) \
|
|
{ dest += (UInt32)_value; }
|
|
|
|
#define MACRO_ReadBitsBig_add3(dest) \
|
|
{ dest += (UInt32)(_value) << 3; }
|
|
|
|
|
|
// (numBits != 0)
|
|
#define MACRO_ReadBits_NonZero(val, numBits) \
|
|
{ \
|
|
val = (UInt32)(_value >> (32 - (numBits))); \
|
|
MOVE_POS(bs, numBits); \
|
|
NORMALIZE \
|
|
}
|
|
|
|
|
|
struct CBitDecoder
|
|
{
|
|
ptrdiff_t _bitOffset;
|
|
const Byte *_buf;
|
|
|
|
Z7_FORCE_INLINE
|
|
UInt32 GetVal() const
|
|
{
|
|
const Byte *ptr = _buf + (_bitOffset >> 4) * 2;
|
|
const UInt32 v = GetUi32(ptr);
|
|
return rotlFixed (v, ((int)_bitOffset & 15) + 16);
|
|
}
|
|
|
|
Z7_FORCE_INLINE
|
|
bool IsOverRead() const
|
|
{
|
|
return _bitOffset > (int)(0 - NUM_DELTA_BIT_OFFSET_BITS);
|
|
}
|
|
|
|
|
|
Z7_FORCE_INLINE
|
|
bool WasBitStreamFinishedOK() const
|
|
{
|
|
// we check that all 0-15 unused bits are zeros:
|
|
if (_bitOffset == 0 - NUM_DELTA_BIT_OFFSET_BITS)
|
|
return true;
|
|
if ((_bitOffset + NUM_DELTA_BIT_OFFSET_BITS + 15) & ~(ptrdiff_t)15)
|
|
return false;
|
|
const Byte *ptr = _buf - NUM_DELTA_BYTES - 2;
|
|
if ((UInt16)(GetUi16(ptr) << (_bitOffset & 15)))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// (numBits != 0)
|
|
Z7_FORCE_INLINE
|
|
UInt32 ReadBits_NonZero(unsigned numBits) throw()
|
|
{
|
|
const UInt32 val = GetVal() >> (32 - numBits);
|
|
_bitOffset += numBits;
|
|
return val;
|
|
}
|
|
};
|
|
|
|
|
|
class CBitByteDecoder: public CBitDecoder
|
|
{
|
|
size_t _size;
|
|
public:
|
|
|
|
Z7_FORCE_INLINE
|
|
void Init_ByteMode(const Byte *data, size_t size)
|
|
{
|
|
_buf = data;
|
|
_size = size;
|
|
}
|
|
|
|
Z7_FORCE_INLINE
|
|
void Init_BitMode(const Byte *data, size_t size)
|
|
{
|
|
_size = size & 1;
|
|
size &= ~(size_t)1;
|
|
_buf = data + size + NUM_DELTA_BYTES;
|
|
_bitOffset = 0 - (ptrdiff_t)(size * 8) - NUM_DELTA_BIT_OFFSET_BITS;
|
|
}
|
|
|
|
Z7_FORCE_INLINE
|
|
void Switch_To_BitMode()
|
|
{
|
|
Init_BitMode(_buf, _size);
|
|
}
|
|
|
|
Z7_FORCE_INLINE
|
|
bool Switch_To_ByteMode()
|
|
{
|
|
/* here we check that unused bits in high 16-bits word are zeros.
|
|
If high word is full (all 16-bits are unused),
|
|
we check that all 16-bits are zeros.
|
|
So we check and skip (1-16 bits) unused bits */
|
|
if ((GetVal() >> (16 + (_bitOffset & 15))) != 0)
|
|
return false;
|
|
_bitOffset += 16;
|
|
_bitOffset &= ~(ptrdiff_t)15;
|
|
if (_bitOffset > 0 - NUM_DELTA_BIT_OFFSET_BITS)
|
|
return false;
|
|
const ptrdiff_t delta = _bitOffset >> 3;
|
|
_size = (size_t)((ptrdiff_t)(_size) - delta - NUM_DELTA_BYTES);
|
|
_buf += delta;
|
|
// _bitOffset = 0; // optional
|
|
return true;
|
|
}
|
|
|
|
Z7_FORCE_INLINE
|
|
size_t GetRem() const { return _size; }
|
|
|
|
Z7_FORCE_INLINE
|
|
UInt32 ReadUInt32()
|
|
{
|
|
const Byte *ptr = _buf;
|
|
const UInt32 v = GetUi32(ptr);
|
|
_buf += 4;
|
|
_size -= 4;
|
|
return v;
|
|
}
|
|
|
|
Z7_FORCE_INLINE
|
|
void CopyTo(Byte *dest, size_t size)
|
|
{
|
|
memcpy(dest, _buf, size);
|
|
_buf += size;
|
|
_size -= size;
|
|
}
|
|
|
|
Z7_FORCE_INLINE
|
|
bool IsOneDirectByteLeft() const
|
|
{
|
|
return GetRem() == 1;
|
|
}
|
|
|
|
Z7_FORCE_INLINE
|
|
Byte DirectReadByte()
|
|
{
|
|
_size--;
|
|
return *_buf++;
|
|
}
|
|
};
|
|
|
|
|
|
// numBits != 0
|
|
// Z7_FORCE_INLINE
|
|
Z7_NO_INLINE
|
|
static
|
|
UInt32 ReadBits(CBitDecoder &_bitStream, unsigned numBits)
|
|
{
|
|
return _bitStream.ReadBits_NonZero(numBits);
|
|
}
|
|
|
|
#define RIF(x) { if (!(x)) return false; }
|
|
|
|
|
|
/*
|
|
MSVC compiler adds extra move operation,
|
|
if we access array with 32-bit index
|
|
array[calc_index_32_bit(32-bit_var)]
|
|
where calc_index_32_bit operations are: ((unsigned)a>>cnt), &, ^, |
|
|
clang is also affected for ((unsigned)a>>cnt) in byte array.
|
|
*/
|
|
|
|
// it can overread input buffer for 7-17 bytes.
|
|
// (levels != levelsEnd)
|
|
Z7_NO_INLINE
|
|
static ptrdiff_t ReadTable(ptrdiff_t _bitOffset, const Byte *_buf, Byte *levels, const Byte *levelsEnd)
|
|
{
|
|
const unsigned kNumTableBits_Level = 7;
|
|
NHuffman::CDecoder256<kNumHuffmanBits, kLevelTableSize, kNumTableBits_Level> _levelDecoder;
|
|
NHuffman::CValueInt _value;
|
|
// optional check to reduce size of overread zone:
|
|
if (_bitOffset > (int)0 - (int)NUM_DELTA_BIT_OFFSET_BITS - (int)(kLevelTableSize * kNumLevelBits))
|
|
return DECODE_ERROR_CODE;
|
|
NORMALIZE
|
|
{
|
|
Byte levels2[kLevelTableSize / 4 * 4];
|
|
for (size_t i = 0; i < kLevelTableSize / 4 * 4; i += 4)
|
|
{
|
|
UInt32 val;
|
|
MACRO_ReadBits_NonZero(val, kNumLevelBits * 4)
|
|
levels2[i + 0] = (Byte)((val >> (3 * kNumLevelBits)));
|
|
levels2[i + 1] = (Byte)((val >> (2 * kNumLevelBits)) & ((1u << kNumLevelBits) - 1));
|
|
levels2[i + 2] = (Byte)((Byte)val >> (1 * kNumLevelBits));
|
|
levels2[i + 3] = (Byte)((val) & ((1u << kNumLevelBits) - 1));
|
|
}
|
|
RIF(_levelDecoder.Build(levels2, NHuffman::k_BuildMode_Full))
|
|
}
|
|
|
|
do
|
|
{
|
|
unsigned sym;
|
|
Z7_LZX_HUFF_DECODE_CHECK_NO(sym, &_levelDecoder, kNumTableBits_Level, MOVE_POS_CHECK)
|
|
// Z7_HUFF_DECODE_CHECK(sym, &_levelDecoder, kNumHuffmanBits, kNumTableBits_Level, &bitStream, return false)
|
|
// sym = _levelDecoder.Decode(&bitStream);
|
|
// if (!_levelDecoder.Decode_SymCheck_MovePosCheck(&bitStream, sym)) return false;
|
|
|
|
if (sym <= kNumHuffmanBits)
|
|
{
|
|
int delta = (int)*levels - (int)sym;
|
|
delta += delta < 0 ? kNumHuffmanBits + 1 : 0;
|
|
*levels++ = (Byte)delta;
|
|
continue;
|
|
}
|
|
|
|
unsigned num;
|
|
int symbol;
|
|
|
|
if (sym < kLevelSym_Same)
|
|
{
|
|
// sym -= kLevelSym_Zero1;
|
|
MACRO_ReadBits_NonZero(num, kLevelSym_Zero1_NumBits + (sym - kLevelSym_Zero1))
|
|
num += (sym << kLevelSym_Zero1_NumBits) - (kLevelSym_Zero1 << kLevelSym_Zero1_NumBits) + kLevelSym_Zero1_Start;
|
|
symbol = 0;
|
|
}
|
|
// else if (sym != kLevelSym_Same) return DECODE_ERROR_CODE;
|
|
else // (sym == kLevelSym_Same)
|
|
{
|
|
MACRO_ReadBits_NonZero(num, kLevelSym_Same_NumBits)
|
|
num += kLevelSym_Same_Start;
|
|
// + (unsigned)bitStream.ReadBitsSmall(kLevelSym_Same_NumBits);
|
|
// Z7_HUFF_DECODE_CHECK(sym, &_levelDecoder, kNumHuffmanBits, kNumTableBits_Level, &bitStream, return DECODE_ERROR_CODE)
|
|
// if (!_levelDecoder.Decode2(&bitStream, sym)) return DECODE_ERROR_CODE;
|
|
// sym = _levelDecoder.Decode(&bitStream);
|
|
|
|
Z7_LZX_HUFF_DECODE_CHECK_NO(sym, &_levelDecoder, kNumTableBits_Level, MOVE_POS)
|
|
|
|
if (sym > kNumHuffmanBits) return DECODE_ERROR_CODE;
|
|
symbol = *levels - (int)sym;
|
|
symbol += symbol < 0 ? kNumHuffmanBits + 1 : 0;
|
|
}
|
|
|
|
if (num > (size_t)(levelsEnd - levels))
|
|
return false;
|
|
const Byte *limit = levels + num;
|
|
do
|
|
*levels++ = (Byte)symbol;
|
|
while (levels != limit);
|
|
}
|
|
while (levels != levelsEnd);
|
|
|
|
return _bitOffset;
|
|
}
|
|
|
|
|
|
static const unsigned kPosSlotDelta = 256 / kNumLenSlots - kPosSlotOffset;
|
|
|
|
|
|
#define READ_TABLE(_bitStream, levels, levelsEnd) \
|
|
{ \
|
|
_bitStream._bitOffset = ReadTable(_bitStream._bitOffset, _bitStream._buf, levels, levelsEnd); \
|
|
if (_bitStream.IsOverRead()) return false; \
|
|
}
|
|
|
|
// can over-read input buffer for less than 32 bytes
|
|
bool CDecoder::ReadTables(CBitByteDecoder &_bitStream) throw()
|
|
{
|
|
UPDATE_STAT(g_stats_NumTables++;)
|
|
{
|
|
const unsigned blockType = (unsigned)ReadBits(_bitStream, kBlockType_NumBits);
|
|
// if (blockType > kBlockType_Uncompressed || blockType == 0)
|
|
if ((unsigned)(blockType - 1) > kBlockType_Uncompressed - 1)
|
|
return false;
|
|
_unpackBlockSize = 1u << 15;
|
|
if (!_wimMode || ReadBits(_bitStream, 1) == 0)
|
|
{
|
|
_unpackBlockSize = ReadBits(_bitStream, 16);
|
|
// wimlib supports chunks larger than 32KB (unsupported my MS wim).
|
|
if (!_wimMode || _numDictBits >= 16)
|
|
{
|
|
_unpackBlockSize <<= 8;
|
|
_unpackBlockSize |= ReadBits(_bitStream, 8);
|
|
}
|
|
}
|
|
|
|
PRF(printf("\nBlockSize = %6d %s ", _unpackBlockSize, (_pos & 1) ? "@@@" : " "));
|
|
|
|
_isUncompressedBlock = (blockType == kBlockType_Uncompressed);
|
|
_skipByte = false;
|
|
|
|
if (_isUncompressedBlock)
|
|
{
|
|
_skipByte = ((_unpackBlockSize & 1) != 0);
|
|
// printf("\n UncompressedBlock %d", _unpackBlockSize);
|
|
PRF(printf(" UncompressedBlock ");)
|
|
// if (_unpackBlockSize & 1) { PRF(printf(" ######### ")); }
|
|
if (!_bitStream.Switch_To_ByteMode())
|
|
return false;
|
|
if (_bitStream.GetRem() < kNumReps * 4)
|
|
return false;
|
|
for (unsigned i = 0; i < kNumReps; i++)
|
|
{
|
|
const UInt32 rep = _bitStream.ReadUInt32();
|
|
// here we allow only such values for (rep) that can be set also by LZ code:
|
|
if (rep == 0 || rep > _winSize - kNumReps)
|
|
return false;
|
|
_reps[(size_t)i + kPosSlotOffset] = rep;
|
|
}
|
|
// printf("\n");
|
|
return true;
|
|
}
|
|
|
|
// _numAlignBits = 64;
|
|
// const UInt32 k_numAlignBits_PosSlots_MAX = 64 + kPosSlotDelta;
|
|
// _numAlignBits_PosSlots = k_numAlignBits_PosSlots_MAX;
|
|
const UInt32 k_numAlignBits_Dist_MAX = (UInt32)(Int32)-1;
|
|
_numAlignBits_Dist = k_numAlignBits_Dist_MAX;
|
|
if (blockType == kBlockType_Aligned)
|
|
{
|
|
Byte levels[kAlignTableSize];
|
|
// unsigned not0 = 0;
|
|
unsigned not3 = 0;
|
|
for (unsigned i = 0; i < kAlignTableSize; i++)
|
|
{
|
|
const unsigned val = ReadBits(_bitStream, kNumAlignLevelBits);
|
|
levels[i] = (Byte)val;
|
|
// not0 |= val;
|
|
not3 |= (val ^ 3);
|
|
}
|
|
// static unsigned number = 0, all = 0; all++;
|
|
// if (!not0) return false; // Build(true) will test this case
|
|
if (not3)
|
|
{
|
|
// _numAlignBits_PosSlots = (kNumAlignBits + 1) * 2 + kPosSlotDelta;
|
|
// _numAlignBits = kNumAlignBits;
|
|
_numAlignBits_Dist = (1u << (kNumAlignBits + 1)) - (kNumReps - 1);
|
|
RIF(_alignDecoder.Build(levels, true)) // full
|
|
}
|
|
// else { number++; if (number % 4 == 0) printf("\nnumber= %u : %u%%", number, number * 100 / all); }
|
|
}
|
|
// if (_numAlignBits_PosSlots == k_numAlignBits_PosSlots_MAX)
|
|
if (_numAlignBits_Dist == k_numAlignBits_Dist_MAX)
|
|
{
|
|
size_t i;
|
|
for (i = 3; i < kNumLinearPosSlotBits; i++)
|
|
{
|
|
_extra[i * 2 + 2 + kPosSlotOffset] = (Byte)(SET_NUM_BITS(i));
|
|
_extra[i * 2 + 3 + kPosSlotOffset] = (Byte)(SET_NUM_BITS(i));
|
|
}
|
|
for (i = kNumLinearPosSlotBits * 2 + 2; i < kNumPosSlots; i++)
|
|
_extra[i + kPosSlotOffset] = (Byte)SET_NUM_BITS(kNumLinearPosSlotBits);
|
|
}
|
|
else
|
|
{
|
|
size_t i;
|
|
for (i = 3; i < kNumLinearPosSlotBits; i++)
|
|
{
|
|
_extra[i * 2 + 2 + kPosSlotOffset] = (Byte)(SET_NUM_BITS(i) - 3);
|
|
_extra[i * 2 + 3 + kPosSlotOffset] = (Byte)(SET_NUM_BITS(i) - 3);
|
|
}
|
|
for (i = kNumLinearPosSlotBits * 2 + 2; i < kNumPosSlots; i++)
|
|
_extra[i + kPosSlotOffset] = (Byte)(SET_NUM_BITS(kNumLinearPosSlotBits) - 3);
|
|
}
|
|
}
|
|
|
|
READ_TABLE(_bitStream, _mainLevels, _mainLevels + 256)
|
|
READ_TABLE(_bitStream, _mainLevels + 256, _mainLevels + 256 + _numPosLenSlots)
|
|
const unsigned end = 256 + _numPosLenSlots;
|
|
memset(_mainLevels + end, 0, kMainTableSize - end);
|
|
// #define NUM_CYC 1
|
|
// unsigned j; for (j = 0; j < NUM_CYC; j++)
|
|
RIF(_mainDecoder.Build(_mainLevels, NHuffman::k_BuildMode_Full))
|
|
// if (kNumLenSymols_Big_Start)
|
|
memset(_lenLevels, 0, kNumLenSymols_Big_Start);
|
|
READ_TABLE(_bitStream,
|
|
_lenLevels + kNumLenSymols_Big_Start,
|
|
_lenLevels + kNumLenSymols_Big_Start + kNumLenSymbols)
|
|
// for (j = 0; j < NUM_CYC; j++)
|
|
RIF(_lenDecoder.Build(_lenLevels, NHuffman::k_BuildMode_Full_or_Empty))
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
static ptrdiff_t CodeLz(CDecoder *dec, size_t next, ptrdiff_t _bitOffset, const Byte *_buf) throw()
|
|
{
|
|
{
|
|
Byte *const win = dec->_win;
|
|
const UInt32 winSize = dec->_winSize;
|
|
Byte *pos = win + dec->_pos;
|
|
const Byte * const posEnd = pos + next;
|
|
NHuffman::CValueInt _value;
|
|
|
|
NORMALIZE
|
|
|
|
#if 1
|
|
#define HUFF_DEC_PREFIX dec->
|
|
#else
|
|
const NHuffman::CDecoder<kNumHuffmanBits, kMainTableSize, kNumTableBits_Main> _mainDecoder = dec->_mainDecoder;
|
|
const NHuffman::CDecoder256<kNumHuffmanBits, kNumLenSymbols, kNumTableBits_Len> _lenDecoder = dec->_lenDecoder;
|
|
const NHuffman::CDecoder7b<kAlignTableSize> _alignDecoder = dec->_alignDecoder;
|
|
#define HUFF_DEC_PREFIX
|
|
#endif
|
|
|
|
do
|
|
{
|
|
unsigned sym;
|
|
// printf("\npos = %6u", pos - win);
|
|
{
|
|
const NHuffman::CDecoder<kNumHuffmanBits, kMainTableSize, kNumTableBits_Main>
|
|
*mainDecoder = & HUFF_DEC_PREFIX _mainDecoder;
|
|
Z7_LZX_HUFF_DECODE_CHECK_NO(sym, mainDecoder, kNumTableBits_Main, MOVE_POS_CHECK_STAT)
|
|
}
|
|
// if (!_mainDecoder.Decode_SymCheck_MovePosCheck(&bitStream, sym)) return DECODE_ERROR_CODE;
|
|
// sym = _mainDecoder.Decode(&bitStream);
|
|
// if (bitStream.WasExtraReadError_Fast()) return DECODE_ERROR_CODE;
|
|
|
|
// printf(" sym = %3x", sym);
|
|
UPDATE_STAT(g_stats_main[sym]++;)
|
|
|
|
if (sym < 256)
|
|
{
|
|
UPDATE_STAT(g_stats_NumLits++;)
|
|
*pos++ = (Byte)sym;
|
|
}
|
|
else
|
|
{
|
|
// sym -= 256;
|
|
// if (sym >= _numPosLenSlots) return DECODE_ERROR_CODE;
|
|
const unsigned posSlot = sym / kNumLenSlots;
|
|
unsigned len = sym % kNumLenSlots + kMatchMinLen;
|
|
if (len == kNumLenSlots - 1 + kMatchMinLen)
|
|
{
|
|
const NHuffman::CDecoder256<kNumHuffmanBits, kNumLenSymbols, kNumTableBits_Len>
|
|
*lenDecoder = & HUFF_DEC_PREFIX _lenDecoder;
|
|
Z7_LZX_HUFF_DECODE_CHECK_YES(len, lenDecoder, kNumTableBits_Len, MOVE_POS_STAT)
|
|
// if (!_lenDecoder.Decode2(&bitStream, len)) return DECODE_ERROR_CODE;
|
|
// len = _lenDecoder.Decode(&bitStream);
|
|
// if (len >= kNumLenSymbols) return DECODE_ERROR_CODE;
|
|
UPDATE_STAT(g_stats_len[len - kNumLenSymols_Big_Start]++;)
|
|
len += kNumLenSlots - 1 + kMatchMinLen - kNumLenSymols_Big_Start;
|
|
}
|
|
/*
|
|
if ((next -= len) < 0)
|
|
return DECODE_ERROR_CODE;
|
|
*/
|
|
UInt32 dist;
|
|
|
|
dist = dec->_reps[(size_t)posSlot - kPosSlotDelta];
|
|
if (posSlot < kNumReps + 256 / kNumLenSlots)
|
|
{
|
|
// if (posSlot != kNumReps + kPosSlotDelta)
|
|
// if (posSlot - (kNumReps + kPosSlotDelta + 1) < 2)
|
|
dec->_reps[(size_t)posSlot - kPosSlotDelta] = dec->_reps[kPosSlotOffset];
|
|
/*
|
|
if (posSlot != kPosSlotDelta)
|
|
{
|
|
UInt32 temp = dist;
|
|
if (posSlot == kPosSlotDelta + 1)
|
|
{
|
|
dist = reps[1];
|
|
reps[1] = temp;
|
|
}
|
|
else
|
|
{
|
|
dist = reps[2];
|
|
reps[2] = temp;
|
|
}
|
|
// dist = reps[(size_t)(posSlot) - kPosSlotDelta];
|
|
// reps[(size_t)(posSlot) - kPosSlotDelta] = reps[0];
|
|
// reps[(size_t)(posSlot) - kPosSlotDelta] = temp;
|
|
}
|
|
*/
|
|
}
|
|
else // if (posSlot != kNumReps + kPosSlotDelta)
|
|
{
|
|
unsigned numDirectBits;
|
|
#if 0
|
|
if (posSlot < kNumPowerPosSlots + kPosSlotDelta)
|
|
{
|
|
numDirectBits = (posSlot - 2 - kPosSlotDelta) >> 1;
|
|
dist = (UInt32)(2 | (posSlot & 1)) << numDirectBits;
|
|
}
|
|
else
|
|
{
|
|
numDirectBits = kNumLinearPosSlotBits;
|
|
dist = (UInt32)(posSlot - 0x22 - kPosSlotDelta) << kNumLinearPosSlotBits;
|
|
}
|
|
dist -= kNumReps - 1;
|
|
#else
|
|
numDirectBits = dec->_extra[(size_t)posSlot - kPosSlotDelta];
|
|
// dist = reps[(size_t)(posSlot) - kPosSlotDelta];
|
|
#endif
|
|
dec->_reps[kPosSlotOffset + 2] =
|
|
dec->_reps[kPosSlotOffset + 1];
|
|
dec->_reps[kPosSlotOffset + 1] =
|
|
dec->_reps[kPosSlotOffset + 0];
|
|
|
|
// dist += val; dist += bitStream.ReadBitsBig(numDirectBits);
|
|
// if (posSlot >= _numAlignBits_PosSlots)
|
|
// if (numDirectBits >= _numAlignBits)
|
|
// if (val >= _numAlignBits_Dist)
|
|
// UInt32 val; MACRO_ReadBitsBig(val , numDirectBits)
|
|
// dist += val;
|
|
// dist += (UInt32)((UInt32)_value >> 1 >> (/* 31 ^ */ (numDirectBits)));
|
|
// MOVE_POS((numDirectBits ^ 31))
|
|
MACRO_ReadBitsBig_pre(numDirectBits)
|
|
// dist += (UInt32)_value;
|
|
if (dist >= dec->_numAlignBits_Dist)
|
|
{
|
|
// if (numDirectBits != _numAlignBits)
|
|
{
|
|
// UInt32 val;
|
|
// dist -= (UInt32)_value;
|
|
MACRO_ReadBitsBig_add3(dist)
|
|
NORMALIZE
|
|
// dist += (val << kNumAlignBits);
|
|
// dist += bitStream.ReadBitsSmall(numDirectBits - kNumAlignBits) << kNumAlignBits;
|
|
}
|
|
{
|
|
// const unsigned alignTemp = _alignDecoder.Decode(&bitStream);
|
|
const NHuffman::CDecoder7b<kAlignTableSize> *alignDecoder = & HUFF_DEC_PREFIX _alignDecoder;
|
|
unsigned alignTemp;
|
|
UPDATE_STAT(g_stats_NumAlign++;)
|
|
Z7_HUFF_DECODER_7B_DECODE(alignTemp, alignDecoder, GET_VAL_BASE, MOVE_POS, bs)
|
|
// NORMALIZE
|
|
// if (alignTemp >= kAlignTableSize) return DECODE_ERROR_CODE;
|
|
dist += alignTemp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
{
|
|
MACRO_ReadBitsBig_add(dist)
|
|
// dist += bitStream.ReadBitsSmall(numDirectBits - kNumAlignBits) << kNumAlignBits;
|
|
}
|
|
}
|
|
NORMALIZE
|
|
/*
|
|
else
|
|
{
|
|
UInt32 val;
|
|
MACRO_ReadBitsBig(val, numDirectBits)
|
|
dist += val;
|
|
// dist += bitStream.ReadBitsBig(numDirectBits);
|
|
}
|
|
*/
|
|
}
|
|
dec->_reps[kPosSlotOffset + 0] = dist;
|
|
|
|
Byte *dest = pos;
|
|
if (len > (size_t)(posEnd - pos))
|
|
return DECODE_ERROR_CODE;
|
|
Int32 srcPos = (Int32)(pos - win);
|
|
pos += len;
|
|
srcPos -= (Int32)dist;
|
|
if (srcPos < 0) // fast version
|
|
{
|
|
if (!dec->_overDict)
|
|
return DECODE_ERROR_CODE;
|
|
srcPos &= winSize - 1;
|
|
UInt32 rem = winSize - (UInt32)srcPos;
|
|
if (len > rem)
|
|
{
|
|
len -= rem;
|
|
const Byte *src = win + (UInt32)srcPos;
|
|
do
|
|
*dest++ = *src++;
|
|
while (--rem);
|
|
srcPos = 0;
|
|
}
|
|
}
|
|
CopyLzMatch(dest, win + (UInt32)srcPos, len, dist);
|
|
}
|
|
}
|
|
while (pos != posEnd);
|
|
|
|
return _bitOffset;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// inSize != 0
|
|
// outSize != 0 ???
|
|
HRESULT CDecoder::CodeSpec(const Byte *inData, size_t inSize, UInt32 outSize) throw()
|
|
{
|
|
// ((inSize & 1) != 0) case is possible, if current call will be finished with Uncompressed Block.
|
|
CBitByteDecoder _bitStream;
|
|
if (_keepHistory && _isUncompressedBlock)
|
|
_bitStream.Init_ByteMode(inData, inSize);
|
|
else
|
|
_bitStream.Init_BitMode(inData, inSize);
|
|
|
|
if (!_keepHistory)
|
|
{
|
|
_isUncompressedBlock = false;
|
|
_skipByte = false;
|
|
_unpackBlockSize = 0;
|
|
memset(_mainLevels, 0, sizeof(_mainLevels));
|
|
memset(_lenLevels, 0, sizeof(_lenLevels));
|
|
{
|
|
_x86_translationSize = 12000000;
|
|
if (!_wimMode)
|
|
{
|
|
_x86_translationSize = 0;
|
|
if (ReadBits(_bitStream, 1) != 0)
|
|
{
|
|
UInt32 v = ReadBits(_bitStream, 16) << 16;
|
|
v |= ReadBits(_bitStream, 16);
|
|
_x86_translationSize = v;
|
|
}
|
|
}
|
|
_x86_processedSize = 0;
|
|
}
|
|
_reps[0 + kPosSlotOffset] = 1;
|
|
_reps[1 + kPosSlotOffset] = 1;
|
|
_reps[2 + kPosSlotOffset] = 1;
|
|
}
|
|
|
|
while (outSize)
|
|
{
|
|
/*
|
|
// check it for bit mode only:
|
|
if (_bitStream.WasExtraReadError_Fast())
|
|
return S_FALSE;
|
|
*/
|
|
if (_unpackBlockSize == 0)
|
|
{
|
|
if (_skipByte)
|
|
{
|
|
if (_bitStream.GetRem() < 1)
|
|
return S_FALSE;
|
|
if (_bitStream.DirectReadByte() != 0)
|
|
return S_FALSE;
|
|
}
|
|
if (_isUncompressedBlock)
|
|
_bitStream.Switch_To_BitMode();
|
|
if (!ReadTables(_bitStream))
|
|
return S_FALSE;
|
|
continue;
|
|
}
|
|
|
|
// _unpackBlockSize != 0
|
|
UInt32 next = _unpackBlockSize;
|
|
if (next > outSize)
|
|
next = outSize;
|
|
// next != 0
|
|
|
|
// PRF(printf("\nnext = %d", (unsigned)next);)
|
|
|
|
if (_isUncompressedBlock)
|
|
{
|
|
if (_bitStream.GetRem() < next)
|
|
return S_FALSE;
|
|
_bitStream.CopyTo(_win + _pos, next);
|
|
_pos += next;
|
|
_unpackBlockSize -= next;
|
|
}
|
|
else
|
|
{
|
|
_unpackBlockSize -= next;
|
|
_bitStream._bitOffset = CodeLz(this, next, _bitStream._bitOffset, _bitStream._buf);
|
|
if (_bitStream.IsOverRead())
|
|
return S_FALSE;
|
|
_pos += next;
|
|
}
|
|
outSize -= next;
|
|
}
|
|
|
|
// outSize == 0
|
|
|
|
if (_isUncompressedBlock)
|
|
{
|
|
/* we don't know where skipByte can be placed, if it's end of chunk:
|
|
1) in current chunk - there are such cab archives, if chunk is last
|
|
2) in next chunk - are there such archives ? */
|
|
if (_unpackBlockSize == 0
|
|
&& _skipByte
|
|
// && outSize == 0
|
|
&& _bitStream.IsOneDirectByteLeft())
|
|
{
|
|
_skipByte = false;
|
|
if (_bitStream.DirectReadByte() != 0)
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
if (_bitStream.GetRem() != 0)
|
|
return S_FALSE;
|
|
if (!_isUncompressedBlock)
|
|
if (!_bitStream.WasBitStreamFinishedOK())
|
|
return S_FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
#if k_Filter_OutBufSize_Add > k_Lz_OutBufSize_Add
|
|
#define k_OutBufSize_Add k_Filter_OutBufSize_Add
|
|
#else
|
|
#define k_OutBufSize_Add k_Lz_OutBufSize_Add
|
|
#endif
|
|
|
|
HRESULT CDecoder::Code_WithExceedReadWrite(const Byte *inData, size_t inSize, UInt32 outSize) throw()
|
|
{
|
|
if (!_keepHistory)
|
|
{
|
|
_pos = 0;
|
|
_overDict = false;
|
|
}
|
|
else if (_pos == _winSize)
|
|
{
|
|
_pos = 0;
|
|
_overDict = true;
|
|
#if k_OutBufSize_Add > 0
|
|
// data after (_winSize) can be used, because we can use overwrite.
|
|
// memset(_win + _winSize, 0, k_OutBufSize_Add);
|
|
#endif
|
|
}
|
|
_writePos = _pos;
|
|
_unpackedData = _win + _pos;
|
|
|
|
if (outSize > _winSize - _pos)
|
|
return S_FALSE;
|
|
|
|
PRF(printf("\ninSize = %d", (unsigned)inSize);)
|
|
PRF(if ((inSize & 1) != 0) printf("---------");)
|
|
|
|
if (inSize == 0)
|
|
return S_FALSE;
|
|
const HRESULT res = CodeSpec(inData, inSize, outSize);
|
|
const HRESULT res2 = Flush();
|
|
return (res == S_OK ? res2 : res);
|
|
}
|
|
|
|
|
|
HRESULT CDecoder::SetParams2(unsigned numDictBits) throw()
|
|
{
|
|
if (numDictBits < kNumDictBits_Min ||
|
|
numDictBits > kNumDictBits_Max)
|
|
return E_INVALIDARG;
|
|
_numDictBits = (Byte)numDictBits;
|
|
const unsigned numPosSlots2 = (numDictBits < 20) ?
|
|
numDictBits : 17 + (1u << (numDictBits - 18));
|
|
_numPosLenSlots = numPosSlots2 * (kNumLenSlots * 2);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CDecoder::Set_DictBits_and_Alloc(unsigned numDictBits) throw()
|
|
{
|
|
RINOK(SetParams2(numDictBits))
|
|
const UInt32 newWinSize = (UInt32)1 << numDictBits;
|
|
if (_needAlloc)
|
|
{
|
|
if (!_win || newWinSize != _winSize)
|
|
{
|
|
// BigFree
|
|
z7_AlignedFree
|
|
(_win);
|
|
_winSize = 0;
|
|
const size_t alloc_size = newWinSize + k_OutBufSize_Add;
|
|
_win = (Byte *)
|
|
// BigAlloc
|
|
z7_AlignedAlloc
|
|
(alloc_size);
|
|
if (!_win)
|
|
return E_OUTOFMEMORY;
|
|
// optional:
|
|
memset(_win, 0, alloc_size);
|
|
}
|
|
}
|
|
_winSize = newWinSize;
|
|
return S_OK;
|
|
}
|
|
|
|
}}
|