p7zip/CPP/7zip/Compress/LzhDecoder.cpp
2017-10-11 12:35:36 +02:00

251 lines
4.8 KiB
C++

// LzhDecoder.cpp
#include "StdAfx.h"
#include "LzhDecoder.h"
namespace NCompress{
namespace NLzh {
namespace NDecoder {
static const UInt32 kWindowSizeMin = 1 << 16;
static bool CheckCodeLens(const Byte *lens, unsigned num)
{
UInt32 sum = 0;
for (unsigned i = 0; i < num; i++)
{
unsigned len = lens[i];
if (len != 0)
sum += ((UInt32)1 << (NUM_CODE_BITS - len));
}
return sum == ((UInt32)1 << NUM_CODE_BITS);
}
bool CCoder::ReadTP(unsigned num, unsigned numBits, int spec)
{
_symbolT = -1;
UInt32 n = _inBitStream.ReadBits(numBits);
if (n == 0)
{
_symbolT = _inBitStream.ReadBits(numBits);
return ((unsigned)_symbolT < num);
}
if (n > num)
return false;
{
Byte lens[NPT];
unsigned i;
for (i = 0; i < NPT; i++)
lens[i] = 0;
i = 0;
do
{
UInt32 val = _inBitStream.GetValue(16);
unsigned c = val >> 13;
if (c == 7)
{
UInt32 mask = 1 << 12;
while (mask & val)
{
mask >>= 1;
c++;
}
if (c > 16)
return false;
}
_inBitStream.MovePos(c < 7 ? 3 : c - 3);
lens[i++] = (Byte)c;
if (i == (unsigned)spec)
i += _inBitStream.ReadBits(2);
}
while (i < n);
if (!CheckCodeLens(lens, NPT))
return false;
return _decoderT.Build(lens);
}
}
static const unsigned NUM_C_BITS = 9;
bool CCoder::ReadC()
{
_symbolC = -1;
unsigned n = _inBitStream.ReadBits(NUM_C_BITS);
if (n == 0)
{
_symbolC = _inBitStream.ReadBits(NUM_C_BITS);
return ((unsigned)_symbolC < NC);
}
if (n > NC)
return false;
{
Byte lens[NC];
unsigned i = 0;
do
{
UInt32 c = (unsigned)_symbolT;
if (_symbolT < 0)
c = _decoderT.Decode(&_inBitStream);
if (c <= 2)
{
if (c == 0)
c = 1;
else if (c == 1)
c = _inBitStream.ReadBits(4) + 3;
else
c = _inBitStream.ReadBits(NUM_C_BITS) + 20;
if (i + c > n)
return false;
do
lens[i++] = 0;
while (--c);
}
else
lens[i++] = (Byte)(c - 2);
}
while (i < n);
while (i < NC)
lens[i++] = 0;
if (!CheckCodeLens(lens, NC))
return false;
return _decoderC.Build(lens);
}
}
HRESULT CCoder::CodeReal(UInt64 rem, ICompressProgressInfo *progress)
{
unsigned pbit = (DictSize <= (1 << 14) ? 4 : 5);
UInt32 blockSize = 0;
while (rem != 0)
{
if (blockSize == 0)
{
if (_inBitStream.ExtraBitsWereRead())
return S_FALSE;
if (progress)
{
UInt64 packSize = _inBitStream.GetProcessedSize();
UInt64 pos = _outWindow.GetProcessedSize();
RINOK(progress->SetRatioInfo(&packSize, &pos));
}
blockSize = _inBitStream.ReadBits(16);
if (blockSize == 0)
return S_FALSE;
if (!ReadTP(NT, 5, 3))
return S_FALSE;
if (!ReadC())
return S_FALSE;
if (!ReadTP(NP, pbit, -1))
return S_FALSE;
}
blockSize--;
UInt32 number = (unsigned)_symbolC;
if (_symbolC < 0)
number = _decoderC.Decode(&_inBitStream);
if (number < 256)
{
_outWindow.PutByte((Byte)number);
rem--;
}
else
{
UInt32 len = number - 256 + kMatchMinLen;
UInt32 dist = (unsigned)_symbolT;
if (_symbolT < 0)
dist = _decoderT.Decode(&_inBitStream);
if (dist > 1)
{
dist--;
dist = ((UInt32)1 << dist) + _inBitStream.ReadBits((unsigned)dist);
}
if (dist >= DictSize)
return S_FALSE;
if (len > rem)
len = (UInt32)rem;
if (!_outWindow.CopyBlock(dist, len))
return S_FALSE;
rem -= len;
}
}
if (FinishMode)
{
if (blockSize != 0)
return S_FALSE;
if (_inBitStream.ReadAlignBits() != 0)
return S_FALSE;
}
if (_inBitStream.ExtraBitsWereRead())
return S_FALSE;
return S_OK;
}
STDMETHODIMP CCoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
{
try
{
if (!outSize)
return E_INVALIDARG;
if (!_outWindow.Create(DictSize > kWindowSizeMin ? DictSize : kWindowSizeMin))
return E_OUTOFMEMORY;
if (!_inBitStream.Create(1 << 17))
return E_OUTOFMEMORY;
_outWindow.SetStream(outStream);
_outWindow.Init(false);
_inBitStream.SetStream(inStream);
_inBitStream.Init();
CCoderReleaser coderReleaser(this);
RINOK(CodeReal(*outSize, progress));
coderReleaser.Disable();
return _outWindow.Flush();
}
catch(const CInBufferException &e) { return e.ErrorCode; }
catch(const CLzOutWindowException &e) { return e.ErrorCode; }
catch(...) { return S_FALSE; }
}
}}}