// LzhDecoder.cpp #include "StdAfx.h" #include "LzhDecoder.h" #include "Windows/Defs.h" namespace NCompress{ namespace NLzh { namespace NDecoder { static const UInt32 kHistorySize = (1 << 16); static const int kBlockSizeBits = 16; static const int kNumCBits = 9; static const int kNumLevelBits = 5; // smallest integer such that (1 << kNumLevelBits) > kNumLevelSymbols/ UInt32 CCoder::ReadBits(int numBits) { return m_InBitStream.ReadBits(numBits); } HRESULT CCoder::ReadLevelTable() { int n = ReadBits(kNumLevelBits); if (n == 0) { m_LevelHuffman.Symbol = ReadBits(kNumLevelBits); if (m_LevelHuffman.Symbol >= kNumLevelSymbols) return S_FALSE; } else { if (n > kNumLevelSymbols) return S_FALSE; m_LevelHuffman.Symbol = -1; Byte lens[kNumLevelSymbols]; int i = 0; while (i < n) { int c = m_InBitStream.ReadBits(3); if (c == 7) while (ReadBits(1)) if (c++ > kMaxHuffmanLen) return S_FALSE; lens[i++] = (Byte)c; if (i == kNumSpecLevelSymbols) { c = ReadBits(2); while (--c >= 0) lens[i++] = 0; } } while (i < kNumLevelSymbols) lens[i++] = 0; m_LevelHuffman.SetCodeLengths(lens); } return S_OK; } HRESULT CCoder::ReadPTable(int numBits) { int n = ReadBits(numBits); if (n == 0) { m_PHuffmanDecoder.Symbol = ReadBits(numBits); if (m_PHuffmanDecoder.Symbol >= kNumDistanceSymbols) return S_FALSE; } else { if (n > kNumDistanceSymbols) return S_FALSE; m_PHuffmanDecoder.Symbol = -1; Byte lens[kNumDistanceSymbols]; int i = 0; while (i < n) { int c = m_InBitStream.ReadBits(3); if (c == 7) while (ReadBits(1)) { if (c > kMaxHuffmanLen) return S_FALSE; c++; } lens[i++] = (Byte)c; } while (i < kNumDistanceSymbols) lens[i++] = 0; m_PHuffmanDecoder.SetCodeLengths(lens); } return S_OK; } HRESULT CCoder::ReadCTable() { int n = ReadBits(kNumCBits); if (n == 0) { m_CHuffmanDecoder.Symbol = ReadBits(kNumCBits); if (m_CHuffmanDecoder.Symbol >= kNumCSymbols) return S_FALSE; } else { if (n > kNumCSymbols) return S_FALSE; m_CHuffmanDecoder.Symbol = -1; Byte lens[kNumCSymbols]; int i = 0; while (i < n) { int c = m_LevelHuffman.Decode(&m_InBitStream); if (c < kNumSpecLevelSymbols) { if (c == 0) c = 1; else if (c == 1) c = ReadBits(4) + 3; else c = ReadBits(kNumCBits) + 20; while (--c >= 0) { if (i > kNumCSymbols) return S_FALSE; lens[i++] = 0; } } else lens[i++] = (Byte)(c - 2); } while (i < kNumCSymbols) lens[i++] = 0; m_CHuffmanDecoder.SetCodeLengths(lens); } return S_OK; } STDMETHODIMP CCoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) { if (outSize == NULL) return E_INVALIDARG; if (!m_OutWindowStream.Create(kHistorySize)) return E_OUTOFMEMORY; if (!m_InBitStream.Create(1 << 20)) return E_OUTOFMEMORY; UInt64 pos = 0; m_OutWindowStream.SetStream(outStream); m_OutWindowStream.Init(false); m_InBitStream.SetStream(inStream); m_InBitStream.Init(); CCoderReleaser coderReleaser(this); int pbit; if (m_NumDictBits <= 13) pbit = 4; else pbit = 5; UInt32 blockSize = 0; while(pos < *outSize) { // for (i = 0; i < dictSize; i++) dtext[i] = 0x20; if (blockSize == 0) { if (progress != NULL) { UInt64 packSize = m_InBitStream.GetProcessedSize(); RINOK(progress->SetRatioInfo(&packSize, &pos)); } blockSize = ReadBits(kBlockSizeBits); ReadLevelTable(); ReadCTable(); RINOK(ReadPTable(pbit)); } blockSize--; UInt32 c = m_CHuffmanDecoder.Decode(&m_InBitStream); if (c < 256) { m_OutWindowStream.PutByte((Byte)c); pos++; } else if (c >= kNumCSymbols) return S_FALSE; else { // offset = (interface->method == LARC_METHOD_NUM) ? 0x100 - 2 : 0x100 - 3; UInt32 len = c - 256 + kMinMatch; UInt32 distance = m_PHuffmanDecoder.Decode(&m_InBitStream); if (distance != 0) distance = (1 << (distance - 1)) + ReadBits(distance - 1); if (distance >= pos) return S_FALSE; if (pos + len > *outSize) len = (UInt32)(*outSize - pos); pos += len; m_OutWindowStream.CopyBlock(distance, len); } } coderReleaser.NeedFlush = false; return m_OutWindowStream.Flush(); } STDMETHODIMP CCoder::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 S_FALSE; } } }}}