mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 15:14:59 -06:00
943 lines
23 KiB
C++
Executable File
943 lines
23 KiB
C++
Executable File
// LzfseDecoder.cpp
|
|
|
|
/*
|
|
This code implements LZFSE data decompressing.
|
|
The code from "LZFSE compression library" was used.
|
|
|
|
2018 : Igor Pavlov : BSD 3-clause License : the code in this file
|
|
2015-2017 : Apple Inc : BSD 3-clause License : original "LZFSE compression library" code
|
|
|
|
The code in the "LZFSE compression library" is licensed under the "BSD 3-clause License":
|
|
----
|
|
Copyright (c) 2015-2016, Apple Inc. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
|
|
2. 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.
|
|
|
|
3. Neither the name of the copyright holder(s) nor the names of any 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.
|
|
----
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
|
|
// #define SHOW_DEBUG_INFO
|
|
|
|
#ifdef SHOW_DEBUG_INFO
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
#ifdef SHOW_DEBUG_INFO
|
|
#define PRF(x) x
|
|
#else
|
|
#define PRF(x)
|
|
#endif
|
|
|
|
#include "../../../C/CpuArch.h"
|
|
|
|
#include "LzfseDecoder.h"
|
|
|
|
namespace NCompress {
|
|
namespace NLzfse {
|
|
|
|
static const Byte kSignature_LZFSE_V1 = 0x31; // '1'
|
|
static const Byte kSignature_LZFSE_V2 = 0x32; // '2'
|
|
|
|
|
|
HRESULT CDecoder::GetUInt32(UInt32 &val)
|
|
{
|
|
Byte b[4];
|
|
for (unsigned i = 0; i < 4; i++)
|
|
if (!m_InStream.ReadByte(b[i]))
|
|
return S_FALSE;
|
|
val = GetUi32(b);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CDecoder::DecodeUncompressed(UInt32 unpackSize)
|
|
{
|
|
PRF(printf("\nUncompressed %7u\n", unpackSize));
|
|
|
|
const unsigned kBufSize = 1 << 8;
|
|
Byte buf[kBufSize];
|
|
for (;;)
|
|
{
|
|
if (unpackSize == 0)
|
|
return S_OK;
|
|
UInt32 cur = unpackSize;
|
|
if (cur > kBufSize)
|
|
cur = kBufSize;
|
|
UInt32 cur2 = (UInt32)m_InStream.ReadBytes(buf, cur);
|
|
m_OutWindowStream.PutBytes(buf, cur2);
|
|
if (cur != cur2)
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
HRESULT CDecoder::DecodeLzvn(UInt32 unpackSize, UInt32 packSize)
|
|
{
|
|
PRF(printf("\nLZVN %7u %7u", unpackSize, packSize));
|
|
|
|
UInt32 D = 0;
|
|
|
|
for (;;)
|
|
{
|
|
if (packSize == 0)
|
|
return S_FALSE;
|
|
Byte b;
|
|
if (!m_InStream.ReadByte(b))
|
|
return S_FALSE;
|
|
packSize--;
|
|
|
|
UInt32 M;
|
|
UInt32 L;
|
|
|
|
if (b >= 0xE0)
|
|
{
|
|
/*
|
|
large L - 11100000 LLLLLLLL <LITERALS>
|
|
small L - 1110LLLL <LITERALS>
|
|
|
|
large Rep - 11110000 MMMMMMMM
|
|
small Rep - 1111MMMM
|
|
*/
|
|
|
|
M = b & 0xF;
|
|
if (M == 0)
|
|
{
|
|
if (packSize == 0)
|
|
return S_FALSE;
|
|
Byte b1;
|
|
if (!m_InStream.ReadByte(b1))
|
|
return S_FALSE;
|
|
packSize--;
|
|
M = (UInt32)b1 + 16;
|
|
}
|
|
L = 0;
|
|
if ((b & 0x10) == 0)
|
|
{
|
|
// Literals only
|
|
L = M;
|
|
M = 0;
|
|
}
|
|
}
|
|
|
|
// ERROR codes
|
|
else if ((b & 0xF0) == 0x70) // 0111xxxx
|
|
return S_FALSE;
|
|
else if ((b & 0xF0) == 0xD0) // 1101xxxx
|
|
return S_FALSE;
|
|
|
|
else
|
|
{
|
|
if ((b & 0xE0) == 0xA0)
|
|
{
|
|
// medium - 101LLMMM DDDDDDMM DDDDDDDD <LITERALS>
|
|
if (packSize < 2)
|
|
return S_FALSE;
|
|
Byte b1;
|
|
if (!m_InStream.ReadByte(b1))
|
|
return S_FALSE;
|
|
packSize--;
|
|
|
|
Byte b2;
|
|
if (!m_InStream.ReadByte(b2))
|
|
return S_FALSE;
|
|
packSize--;
|
|
L = (((UInt32)b >> 3) & 3);
|
|
M = (((UInt32)b & 7) << 2) + (b1 & 3);
|
|
D = ((UInt32)b1 >> 2) + ((UInt32)b2 << 6);
|
|
}
|
|
else
|
|
{
|
|
L = (UInt32)b >> 6;
|
|
M = ((UInt32)b >> 3) & 7;
|
|
if ((b & 0x7) == 6)
|
|
{
|
|
// REP - LLMMM110 <LITERALS>
|
|
if (L == 0)
|
|
{
|
|
// spec
|
|
if (M == 0)
|
|
break; // EOS
|
|
if (M <= 2)
|
|
continue; // NOP
|
|
return S_FALSE; // UNDEFINED
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (packSize == 0)
|
|
return S_FALSE;
|
|
Byte b1;
|
|
if (!m_InStream.ReadByte(b1))
|
|
return S_FALSE;
|
|
packSize--;
|
|
|
|
// large - LLMMM111 DDDDDDDD DDDDDDDD <LITERALS>
|
|
// small - LLMMMDDD DDDDDDDD <LITERALS>
|
|
D = ((UInt32)b & 7);
|
|
if (D == 7)
|
|
{
|
|
if (packSize == 0)
|
|
return S_FALSE;
|
|
Byte b2;
|
|
if (!m_InStream.ReadByte(b2))
|
|
return S_FALSE;
|
|
packSize--;
|
|
D = b2;
|
|
}
|
|
D = (D << 8) + b1;
|
|
}
|
|
}
|
|
|
|
M += 3;
|
|
}
|
|
{
|
|
for (unsigned i = 0; i < L; i++)
|
|
{
|
|
if (packSize == 0 || unpackSize == 0)
|
|
return S_FALSE;
|
|
Byte b1;
|
|
if (!m_InStream.ReadByte(b1))
|
|
return S_FALSE;
|
|
packSize--;
|
|
m_OutWindowStream.PutByte(b1);
|
|
unpackSize--;
|
|
}
|
|
}
|
|
|
|
if (M != 0)
|
|
{
|
|
if (unpackSize == 0 || D == 0)
|
|
return S_FALSE;
|
|
unsigned cur = M;
|
|
if (cur > unpackSize)
|
|
cur = (unsigned)unpackSize;
|
|
if (!m_OutWindowStream.CopyBlock(D - 1, cur))
|
|
return S_FALSE;
|
|
unpackSize -= cur;
|
|
if (cur != M)
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
if (unpackSize != 0)
|
|
return S_FALSE;
|
|
|
|
// LZVN encoder writes 7 additional zero bytes
|
|
if (packSize != 7)
|
|
return S_FALSE;
|
|
do
|
|
{
|
|
Byte b;
|
|
if (!m_InStream.ReadByte(b))
|
|
return S_FALSE;
|
|
packSize--;
|
|
if (b != 0)
|
|
return S_FALSE;
|
|
}
|
|
while (packSize != 0);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
// ---------- LZFSE ----------
|
|
|
|
#define MATCHES_PER_BLOCK 10000
|
|
#define LITERALS_PER_BLOCK (4 * MATCHES_PER_BLOCK)
|
|
|
|
#define NUM_L_SYMBOLS 20
|
|
#define NUM_M_SYMBOLS 20
|
|
#define NUM_D_SYMBOLS 64
|
|
#define NUM_LIT_SYMBOLS 256
|
|
|
|
#define NUM_SYMBOLS ( \
|
|
NUM_L_SYMBOLS + \
|
|
NUM_M_SYMBOLS + \
|
|
NUM_D_SYMBOLS + \
|
|
NUM_LIT_SYMBOLS)
|
|
|
|
#define NUM_L_STATES (1 << 6)
|
|
#define NUM_M_STATES (1 << 6)
|
|
#define NUM_D_STATES (1 << 8)
|
|
#define NUM_LIT_STATES (1 << 10)
|
|
|
|
|
|
typedef UInt32 CFseState;
|
|
|
|
|
|
static UInt32 SumFreqs(const UInt16 *freqs, unsigned num)
|
|
{
|
|
UInt32 sum = 0;
|
|
for (unsigned i = 0; i < num; i++)
|
|
sum += (UInt32)freqs[i];
|
|
return sum;
|
|
}
|
|
|
|
|
|
static Z7_FORCE_INLINE unsigned CountZeroBits(UInt32 val, UInt32 mask)
|
|
{
|
|
for (unsigned i = 0;;)
|
|
{
|
|
if (val & mask)
|
|
return i;
|
|
i++;
|
|
mask >>= 1;
|
|
}
|
|
}
|
|
|
|
|
|
static Z7_FORCE_INLINE void InitLitTable(const UInt16 *freqs, UInt32 *table)
|
|
{
|
|
for (unsigned i = 0; i < NUM_LIT_SYMBOLS; i++)
|
|
{
|
|
unsigned f = freqs[i];
|
|
if (f == 0)
|
|
continue;
|
|
|
|
// 0 < f <= numStates
|
|
// 0 <= k <= numStatesLog
|
|
// numStates <= (f<<k) < numStates * 2
|
|
// 0 < j0 <= f
|
|
// (f + j0) = next_power_of_2 for f
|
|
unsigned k = CountZeroBits(f, NUM_LIT_STATES);
|
|
unsigned j0 = (((unsigned)NUM_LIT_STATES * 2) >> k) - f;
|
|
|
|
/*
|
|
CEntry
|
|
{
|
|
Byte k;
|
|
Byte symbol;
|
|
UInt16 delta;
|
|
};
|
|
*/
|
|
|
|
UInt32 e = ((UInt32)i << 8) + k;
|
|
k += 16;
|
|
UInt32 d = e + ((UInt32)f << k) - ((UInt32)NUM_LIT_STATES << 16);
|
|
UInt32 step = (UInt32)1 << k;
|
|
|
|
unsigned j = 0;
|
|
do
|
|
{
|
|
*table++ = d;
|
|
d += step;
|
|
}
|
|
while (++j < j0);
|
|
|
|
e--;
|
|
step >>= 1;
|
|
|
|
for (j = j0; j < f; j++)
|
|
{
|
|
*table++ = e;
|
|
e += step;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
typedef struct
|
|
{
|
|
Byte totalBits;
|
|
Byte extraBits;
|
|
UInt16 delta;
|
|
UInt32 vbase;
|
|
} CExtraEntry;
|
|
|
|
|
|
static void InitExtraDecoderTable(unsigned numStates,
|
|
unsigned numSymbols,
|
|
const UInt16 *freqs,
|
|
const Byte *vbits,
|
|
CExtraEntry *table)
|
|
{
|
|
UInt32 vbase = 0;
|
|
|
|
for (unsigned i = 0; i < numSymbols; i++)
|
|
{
|
|
unsigned f = freqs[i];
|
|
unsigned extraBits = vbits[i];
|
|
|
|
if (f != 0)
|
|
{
|
|
unsigned k = CountZeroBits(f, numStates);
|
|
unsigned j0 = ((2 * numStates) >> k) - f;
|
|
|
|
unsigned j = 0;
|
|
do
|
|
{
|
|
CExtraEntry *e = table++;
|
|
e->totalBits = (Byte)(k + extraBits);
|
|
e->extraBits = (Byte)extraBits;
|
|
e->delta = (UInt16)(((f + j) << k) - numStates);
|
|
e->vbase = vbase;
|
|
}
|
|
while (++j < j0);
|
|
|
|
f -= j0;
|
|
k--;
|
|
|
|
for (j = 0; j < f; j++)
|
|
{
|
|
CExtraEntry *e = table++;
|
|
e->totalBits = (Byte)(k + extraBits);
|
|
e->extraBits = (Byte)extraBits;
|
|
e->delta = (UInt16)(j << k);
|
|
e->vbase = vbase;
|
|
}
|
|
}
|
|
|
|
vbase += ((UInt32)1 << extraBits);
|
|
}
|
|
}
|
|
|
|
|
|
static const Byte k_L_extra[NUM_L_SYMBOLS] =
|
|
{
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 8
|
|
};
|
|
|
|
static const Byte k_M_extra[NUM_M_SYMBOLS] =
|
|
{
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11
|
|
};
|
|
|
|
static const Byte k_D_extra[NUM_D_SYMBOLS] =
|
|
{
|
|
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
|
|
4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
|
|
8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11,
|
|
12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15
|
|
};
|
|
|
|
|
|
|
|
// ---------- CBitStream ----------
|
|
|
|
typedef struct
|
|
{
|
|
UInt32 accum;
|
|
unsigned numBits; // [0, 31] - Number of valid bits in (accum), other bits are 0
|
|
} CBitStream;
|
|
|
|
|
|
static Z7_FORCE_INLINE int FseInStream_Init(CBitStream *s,
|
|
int n, // [-7, 0], (-n == number_of_unused_bits) in last byte
|
|
const Byte **pbuf)
|
|
{
|
|
*pbuf -= 4;
|
|
s->accum = GetUi32(*pbuf);
|
|
if (n)
|
|
{
|
|
s->numBits = (unsigned)(n + 32);
|
|
if ((s->accum >> s->numBits) != 0)
|
|
return -1; // ERROR, encoder should have zeroed the upper bits
|
|
}
|
|
else
|
|
{
|
|
*pbuf += 1;
|
|
s->accum >>= 8;
|
|
s->numBits = 24;
|
|
}
|
|
return 0; // OK
|
|
}
|
|
|
|
|
|
// 0 <= numBits < 32
|
|
#define mask31(x, numBits) ((x) & (((UInt32)1 << (numBits)) - 1))
|
|
|
|
#define FseInStream_FLUSH \
|
|
{ const unsigned nbits = (31 - in.numBits) & (unsigned)-8; \
|
|
if (nbits) { \
|
|
buf -= (nbits >> 3); \
|
|
if (buf < buf_check) return S_FALSE; \
|
|
UInt32 v = GetUi32(buf); \
|
|
in.accum = (in.accum << nbits) | mask31(v, nbits); \
|
|
in.numBits += nbits; }}
|
|
|
|
|
|
|
|
static Z7_FORCE_INLINE UInt32 BitStream_Pull(CBitStream *s, unsigned numBits)
|
|
{
|
|
s->numBits -= numBits;
|
|
UInt32 v = s->accum >> s->numBits;
|
|
s->accum = mask31(s->accum, s->numBits);
|
|
return v;
|
|
}
|
|
|
|
|
|
#define DECODE_LIT(dest, pstate) { \
|
|
UInt32 e = lit_decoder[pstate]; \
|
|
pstate = (CFseState)((e >> 16) + BitStream_Pull(&in, e & 0xff)); \
|
|
dest = (Byte)(e >> 8); }
|
|
|
|
|
|
static Z7_FORCE_INLINE UInt32 FseDecodeExtra(CFseState *pstate,
|
|
const CExtraEntry *table,
|
|
CBitStream *s)
|
|
{
|
|
const CExtraEntry *e = &table[*pstate];
|
|
UInt32 v = BitStream_Pull(s, e->totalBits);
|
|
unsigned extraBits = e->extraBits;
|
|
*pstate = (CFseState)(e->delta + (v >> extraBits));
|
|
return e->vbase + mask31(v, extraBits);
|
|
}
|
|
|
|
|
|
#define freqs_L (freqs)
|
|
#define freqs_M (freqs_L + NUM_L_SYMBOLS)
|
|
#define freqs_D (freqs_M + NUM_M_SYMBOLS)
|
|
#define freqs_LIT (freqs_D + NUM_D_SYMBOLS)
|
|
|
|
#define GET_BITS_64(v, offset, num, dest) dest = (UInt32) ((v >> (offset)) & ((1 << (num)) - 1));
|
|
#define GET_BITS_64_Int32(v, offset, num, dest) dest = (Int32)((v >> (offset)) & ((1 << (num)) - 1));
|
|
#define GET_BITS_32(v, offset, num, dest) dest = (CFseState)((v >> (offset)) & ((1 << (num)) - 1));
|
|
|
|
|
|
HRESULT CDecoder::DecodeLzfse(UInt32 unpackSize, Byte version)
|
|
{
|
|
PRF(printf("\nLZFSE-%d %7u", version - '0', unpackSize));
|
|
|
|
UInt32 numLiterals;
|
|
UInt32 litPayloadSize;
|
|
Int32 literal_bits;
|
|
|
|
UInt32 lit_state_0;
|
|
UInt32 lit_state_1;
|
|
UInt32 lit_state_2;
|
|
UInt32 lit_state_3;
|
|
|
|
UInt32 numMatches;
|
|
UInt32 lmdPayloadSize;
|
|
Int32 lmd_bits;
|
|
|
|
CFseState l_state;
|
|
CFseState m_state;
|
|
CFseState d_state;
|
|
|
|
UInt16 freqs[NUM_SYMBOLS];
|
|
|
|
if (version == kSignature_LZFSE_V1)
|
|
{
|
|
return E_NOTIMPL;
|
|
// we need examples to test LZFSE-V1 code
|
|
/*
|
|
const unsigned k_v1_SubHeaderSize = 7 * 4 + 7 * 2;
|
|
const unsigned k_v1_HeaderSize = k_v1_SubHeaderSize + NUM_SYMBOLS * 2;
|
|
_buffer.AllocAtLeast(k_v1_HeaderSize);
|
|
if (m_InStream.ReadBytes(_buffer, k_v1_HeaderSize) != k_v1_HeaderSize)
|
|
return S_FALSE;
|
|
|
|
const Byte *buf = _buffer;
|
|
#define GET_32(offs, dest) dest = GetUi32(buf + offs)
|
|
#define GET_16(offs, dest) dest = GetUi16(buf + offs)
|
|
|
|
UInt32 payload_bytes;
|
|
GET_32(0, payload_bytes);
|
|
GET_32(4, numLiterals);
|
|
GET_32(8, numMatches);
|
|
GET_32(12, litPayloadSize);
|
|
GET_32(16, lmdPayloadSize);
|
|
if (litPayloadSize > (1 << 20) || lmdPayloadSize > (1 << 20))
|
|
return S_FALSE;
|
|
GET_32(20, literal_bits);
|
|
if (literal_bits < -7 || literal_bits > 0)
|
|
return S_FALSE;
|
|
|
|
GET_16(24, lit_state_0);
|
|
GET_16(26, lit_state_1);
|
|
GET_16(28, lit_state_2);
|
|
GET_16(30, lit_state_3);
|
|
|
|
GET_32(32, lmd_bits);
|
|
if (lmd_bits < -7 || lmd_bits > 0)
|
|
return S_FALSE;
|
|
|
|
GET_16(36, l_state);
|
|
GET_16(38, m_state);
|
|
GET_16(40, d_state);
|
|
|
|
for (unsigned i = 0; i < NUM_SYMBOLS; i++)
|
|
freqs[i] = GetUi16(buf + k_v1_SubHeaderSize + i * 2);
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
UInt32 headerSize;
|
|
{
|
|
const unsigned kPreHeaderSize = 4 * 2; // signature and upackSize
|
|
const unsigned kHeaderSize = 8 * 3;
|
|
Byte temp[kHeaderSize];
|
|
if (m_InStream.ReadBytes(temp, kHeaderSize) != kHeaderSize)
|
|
return S_FALSE;
|
|
|
|
UInt64 v;
|
|
|
|
v = GetUi64(temp);
|
|
GET_BITS_64(v, 0, 20, numLiterals)
|
|
GET_BITS_64(v, 20, 20, litPayloadSize)
|
|
GET_BITS_64(v, 40, 20, numMatches)
|
|
GET_BITS_64_Int32(v, 60, 3 + 1, literal_bits) // (NumberOfUsedBits - 1)
|
|
literal_bits -= 7; // (-NumberOfUnusedBits)
|
|
if (literal_bits > 0)
|
|
return S_FALSE;
|
|
// GET_BITS_64(v, 63, 1, unused);
|
|
|
|
v = GetUi64(temp + 8);
|
|
GET_BITS_64(v, 0, 10, lit_state_0)
|
|
GET_BITS_64(v, 10, 10, lit_state_1)
|
|
GET_BITS_64(v, 20, 10, lit_state_2)
|
|
GET_BITS_64(v, 30, 10, lit_state_3)
|
|
GET_BITS_64(v, 40, 20, lmdPayloadSize)
|
|
GET_BITS_64_Int32(v, 60, 3 + 1, lmd_bits)
|
|
lmd_bits -= 7;
|
|
if (lmd_bits > 0)
|
|
return S_FALSE;
|
|
// GET_BITS_64(v, 63, 1, unused)
|
|
|
|
UInt32 v32 = GetUi32(temp + 20);
|
|
// (total header size in bytes; this does not
|
|
// correspond to a field in the uncompressed header version,
|
|
// but is required; we wouldn't know the size of the
|
|
// compresssed header otherwise.
|
|
GET_BITS_32(v32, 0, 10, l_state)
|
|
GET_BITS_32(v32, 10, 10, m_state)
|
|
GET_BITS_32(v32, 20, 10 + 2, d_state)
|
|
// GET_BITS_64(v, 62, 2, unused)
|
|
|
|
headerSize = GetUi32(temp + 16);
|
|
if (headerSize <= kPreHeaderSize + kHeaderSize)
|
|
return S_FALSE;
|
|
headerSize -= kPreHeaderSize + kHeaderSize;
|
|
}
|
|
|
|
// no freqs case is not allowed ?
|
|
// memset(freqs, 0, sizeof(freqs));
|
|
// if (headerSize != 0)
|
|
{
|
|
static const Byte numBitsTable[32] =
|
|
{
|
|
2, 3, 2, 5, 2, 3, 2, 8, 2, 3, 2, 5, 2, 3, 2, 14,
|
|
2, 3, 2, 5, 2, 3, 2, 8, 2, 3, 2, 5, 2, 3, 2, 14
|
|
};
|
|
|
|
static const Byte valueTable[32] =
|
|
{
|
|
0, 2, 1, 4, 0, 3, 1, 8, 0, 2, 1, 5, 0, 3, 1, 24,
|
|
0, 2, 1, 6, 0, 3, 1, 8, 0, 2, 1, 7, 0, 3, 1, 24
|
|
};
|
|
|
|
UInt32 accum = 0;
|
|
unsigned numBits = 0;
|
|
|
|
for (unsigned i = 0; i < NUM_SYMBOLS; i++)
|
|
{
|
|
while (numBits <= 14 && headerSize != 0)
|
|
{
|
|
Byte b;
|
|
if (!m_InStream.ReadByte(b))
|
|
return S_FALSE;
|
|
accum |= (UInt32)b << numBits;
|
|
numBits += 8;
|
|
headerSize--;
|
|
}
|
|
|
|
unsigned b = (unsigned)accum & 31;
|
|
unsigned n = numBitsTable[b];
|
|
if (numBits < n)
|
|
return S_FALSE;
|
|
numBits -= n;
|
|
UInt32 f = valueTable[b];
|
|
if (n >= 8)
|
|
f += ((accum >> 4) & (0x3ff >> (14 - n)));
|
|
accum >>= n;
|
|
freqs[i] = (UInt16)f;
|
|
}
|
|
|
|
if (numBits >= 8 || headerSize != 0)
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
PRF(printf(" Literals=%6u Matches=%6u", numLiterals, numMatches));
|
|
|
|
if (numLiterals > LITERALS_PER_BLOCK
|
|
|| (numLiterals & 3) != 0
|
|
|| numMatches > MATCHES_PER_BLOCK
|
|
|| lit_state_0 >= NUM_LIT_STATES
|
|
|| lit_state_1 >= NUM_LIT_STATES
|
|
|| lit_state_2 >= NUM_LIT_STATES
|
|
|| lit_state_3 >= NUM_LIT_STATES
|
|
|| l_state >= NUM_L_STATES
|
|
|| m_state >= NUM_M_STATES
|
|
|| d_state >= NUM_D_STATES)
|
|
return S_FALSE;
|
|
|
|
// only full table is allowed ?
|
|
if ( SumFreqs(freqs_L, NUM_L_SYMBOLS) != NUM_L_STATES
|
|
|| SumFreqs(freqs_M, NUM_M_SYMBOLS) != NUM_M_STATES
|
|
|| SumFreqs(freqs_D, NUM_D_SYMBOLS) != NUM_D_STATES
|
|
|| SumFreqs(freqs_LIT, NUM_LIT_SYMBOLS) != NUM_LIT_STATES)
|
|
return S_FALSE;
|
|
|
|
|
|
const unsigned kPad = 16;
|
|
|
|
// ---------- Decode literals ----------
|
|
|
|
{
|
|
_literals.AllocAtLeast(LITERALS_PER_BLOCK + 16);
|
|
_buffer.AllocAtLeast(kPad + litPayloadSize);
|
|
memset(_buffer, 0, kPad);
|
|
|
|
if (m_InStream.ReadBytes(_buffer + kPad, litPayloadSize) != litPayloadSize)
|
|
return S_FALSE;
|
|
|
|
UInt32 lit_decoder[NUM_LIT_STATES];
|
|
InitLitTable(freqs_LIT, lit_decoder);
|
|
|
|
const Byte *buf_start = _buffer + kPad;
|
|
const Byte *buf_check = buf_start - 4;
|
|
const Byte *buf = buf_start + litPayloadSize;
|
|
CBitStream in;
|
|
if (FseInStream_Init(&in, literal_bits, &buf) != 0)
|
|
return S_FALSE;
|
|
|
|
Byte *lit = _literals;
|
|
const Byte *lit_limit = lit + numLiterals;
|
|
for (; lit < lit_limit; lit += 4)
|
|
{
|
|
FseInStream_FLUSH
|
|
DECODE_LIT (lit[0], lit_state_0)
|
|
DECODE_LIT (lit[1], lit_state_1)
|
|
FseInStream_FLUSH
|
|
DECODE_LIT (lit[2], lit_state_2)
|
|
DECODE_LIT (lit[3], lit_state_3)
|
|
}
|
|
|
|
if ((buf_start - buf) * 8 != (int)in.numBits)
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
// ---------- Decode LMD ----------
|
|
|
|
_buffer.AllocAtLeast(kPad + lmdPayloadSize);
|
|
memset(_buffer, 0, kPad);
|
|
if (m_InStream.ReadBytes(_buffer + kPad, lmdPayloadSize) != lmdPayloadSize)
|
|
return S_FALSE;
|
|
|
|
CExtraEntry l_decoder[NUM_L_STATES];
|
|
CExtraEntry m_decoder[NUM_M_STATES];
|
|
CExtraEntry d_decoder[NUM_D_STATES];
|
|
|
|
InitExtraDecoderTable(NUM_L_STATES, NUM_L_SYMBOLS, freqs_L, k_L_extra, l_decoder);
|
|
InitExtraDecoderTable(NUM_M_STATES, NUM_M_SYMBOLS, freqs_M, k_M_extra, m_decoder);
|
|
InitExtraDecoderTable(NUM_D_STATES, NUM_D_SYMBOLS, freqs_D, k_D_extra, d_decoder);
|
|
|
|
const Byte *buf_start = _buffer + kPad;
|
|
const Byte *buf_check = buf_start - 4;
|
|
const Byte *buf = buf_start + lmdPayloadSize;
|
|
CBitStream in;
|
|
if (FseInStream_Init(&in, lmd_bits, &buf))
|
|
return S_FALSE;
|
|
|
|
const Byte *lit = _literals;
|
|
const Byte *lit_limit = lit + numLiterals;
|
|
|
|
UInt32 D = 0;
|
|
|
|
for (;;)
|
|
{
|
|
if (numMatches == 0)
|
|
break;
|
|
numMatches--;
|
|
|
|
FseInStream_FLUSH
|
|
|
|
unsigned L = (unsigned)FseDecodeExtra(&l_state, l_decoder, &in);
|
|
|
|
FseInStream_FLUSH
|
|
|
|
unsigned M = (unsigned)FseDecodeExtra(&m_state, m_decoder, &in);
|
|
|
|
FseInStream_FLUSH
|
|
|
|
{
|
|
UInt32 new_D = FseDecodeExtra(&d_state, d_decoder, &in);
|
|
if (new_D)
|
|
D = new_D;
|
|
}
|
|
|
|
if (L != 0)
|
|
{
|
|
if (L > (size_t)(lit_limit - lit))
|
|
return S_FALSE;
|
|
unsigned cur = L;
|
|
if (cur > unpackSize)
|
|
cur = (unsigned)unpackSize;
|
|
m_OutWindowStream.PutBytes(lit, cur);
|
|
unpackSize -= cur;
|
|
lit += cur;
|
|
if (cur != L)
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (M != 0)
|
|
{
|
|
if (unpackSize == 0 || D == 0)
|
|
return S_FALSE;
|
|
unsigned cur = M;
|
|
if (cur > unpackSize)
|
|
cur = (unsigned)unpackSize;
|
|
if (!m_OutWindowStream.CopyBlock(D - 1, cur))
|
|
return S_FALSE;
|
|
unpackSize -= cur;
|
|
if (cur != M)
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
if (unpackSize != 0)
|
|
return S_FALSE;
|
|
|
|
// LZFSE encoder writes 8 additional zero bytes before LMD payload
|
|
// We test it:
|
|
if ((size_t)(buf - buf_start) * 8 + in.numBits != 64)
|
|
return S_FALSE;
|
|
if (GetUi64(buf_start) != 0)
|
|
return S_FALSE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CDecoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream,
|
|
const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)
|
|
{
|
|
PRF(printf("\n\nLzfseDecoder %7u %7u\n", (unsigned)*outSize, (unsigned)*inSize));
|
|
|
|
const UInt32 kLzfseDictSize = 1 << 18;
|
|
if (!m_OutWindowStream.Create(kLzfseDictSize))
|
|
return E_OUTOFMEMORY;
|
|
if (!m_InStream.Create(1 << 18))
|
|
return E_OUTOFMEMORY;
|
|
|
|
m_OutWindowStream.SetStream(outStream);
|
|
m_OutWindowStream.Init(false);
|
|
m_InStream.SetStream(inStream);
|
|
m_InStream.Init();
|
|
|
|
CCoderReleaser coderReleaser(this);
|
|
|
|
UInt64 prevOut = 0;
|
|
UInt64 prevIn = 0;
|
|
|
|
if (LzvnMode)
|
|
{
|
|
if (!outSize || !inSize)
|
|
return E_NOTIMPL;
|
|
const UInt64 unpackSize = *outSize;
|
|
const UInt64 packSize = *inSize;
|
|
if (unpackSize > (UInt32)(Int32)-1
|
|
|| packSize > (UInt32)(Int32)-1)
|
|
return S_FALSE;
|
|
RINOK(DecodeLzvn((UInt32)unpackSize, (UInt32)packSize))
|
|
}
|
|
else
|
|
for (;;)
|
|
{
|
|
const UInt64 pos = m_OutWindowStream.GetProcessedSize();
|
|
const UInt64 packPos = m_InStream.GetProcessedSize();
|
|
|
|
if (progress && ((pos - prevOut) >= (1 << 22) || (packPos - prevIn) >= (1 << 22)))
|
|
{
|
|
RINOK(progress->SetRatioInfo(&packPos, &pos))
|
|
prevIn = packPos;
|
|
prevOut = pos;
|
|
}
|
|
|
|
UInt32 v;
|
|
RINOK(GetUInt32(v))
|
|
if ((v & 0xFFFFFF) != 0x787662) // bvx
|
|
return S_FALSE;
|
|
v >>= 24;
|
|
|
|
if (v == 0x24) // '$', end of stream
|
|
break;
|
|
|
|
UInt32 unpackSize;
|
|
RINOK(GetUInt32(unpackSize))
|
|
|
|
UInt32 cur = unpackSize;
|
|
if (outSize)
|
|
{
|
|
const UInt64 rem = *outSize - pos;
|
|
if (cur > rem)
|
|
cur = (UInt32)rem;
|
|
}
|
|
unpackSize -= cur;
|
|
|
|
HRESULT res;
|
|
if (v == kSignature_LZFSE_V1 || v == kSignature_LZFSE_V2)
|
|
res = DecodeLzfse(cur, (Byte)v);
|
|
else if (v == 0x6E) // 'n'
|
|
{
|
|
UInt32 packSize;
|
|
res = GetUInt32(packSize);
|
|
if (res == S_OK)
|
|
res = DecodeLzvn(cur, packSize);
|
|
}
|
|
else if (v == 0x2D) // '-'
|
|
res = DecodeUncompressed(cur);
|
|
else
|
|
return E_NOTIMPL;
|
|
|
|
if (res != S_OK)
|
|
return res;
|
|
|
|
if (unpackSize != 0)
|
|
return S_FALSE;
|
|
}
|
|
|
|
coderReleaser.NeedFlush = false;
|
|
HRESULT res = m_OutWindowStream.Flush();
|
|
if (res == S_OK)
|
|
if ((inSize && *inSize != m_InStream.GetProcessedSize())
|
|
|| (outSize && *outSize != m_OutWindowStream.GetProcessedSize()))
|
|
res = S_FALSE;
|
|
return res;
|
|
}
|
|
|
|
|
|
Z7_COM7F_IMF(CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
|
|
const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress))
|
|
{
|
|
try { return CodeReal(inStream, outStream, inSize, outSize, progress); }
|
|
catch(const CInBufferException &e) { return e.ErrorCode; }
|
|
catch(const CLzOutWindowException &e) { return e.ErrorCode; }
|
|
catch(...) { return E_OUTOFMEMORY; }
|
|
// catch(...) { return S_FALSE; }
|
|
}
|
|
|
|
}}
|