// Implode/Decoder.cpp #include "StdAfx.h" #include "ImplodeDecoder.h" #include "Common/Defs.h" namespace NCompress { namespace NImplode { namespace NDecoder { class CException { public: enum ECauseType { kData } m_Cause; CException(ECauseType cause): m_Cause(cause) {} }; static const int kNumDistanceLowDirectBitsForBigDict = 7; static const int kNumDistanceLowDirectBitsForSmallDict = 6; static const int kNumBitsInByte = 8; // static const int kLevelStructuresNumberFieldSize = kNumBitsInByte; static const int kLevelStructuresNumberAdditionalValue = 1; static const int kNumLevelStructureLevelBits = 4; static const int kLevelStructureLevelAdditionalValue = 1; static const int kNumLevelStructureRepNumberBits = 4; static const int kLevelStructureRepNumberAdditionalValue = 1; static const int kLiteralTableSize = (1 << kNumBitsInByte); static const int kDistanceTableSize = 64; static const int kLengthTableSize = 64; static const UInt32 kHistorySize = (1 << MyMax(kNumDistanceLowDirectBitsForBigDict, kNumDistanceLowDirectBitsForSmallDict)) * kDistanceTableSize; // = 8 KB; static const int kNumAdditionalLengthBits = 8; static const UInt32 kMatchMinLenWhenLiteralsOn = 3; static const UInt32 kMatchMinLenWhenLiteralsOff = 2; static const UInt32 kMatchMinLenMax = MyMax(kMatchMinLenWhenLiteralsOn, kMatchMinLenWhenLiteralsOff); // 3 // static const UInt32 kMatchMaxLenMax = kMatchMinLenMax + (kLengthTableSize - 1) + (1 << kNumAdditionalLengthBits) - 1; // or 2 enum { kMatchId = 0, kLiteralId = 1 }; CCoder::CCoder(): m_LiteralDecoder(kLiteralTableSize), m_LengthDecoder(kLengthTableSize), m_DistanceDecoder(kDistanceTableSize) { } void CCoder::ReleaseStreams() { m_OutWindowStream.ReleaseStream(); m_InBitStream.ReleaseStream(); } bool CCoder::ReadLevelItems(NImplode::NHuffman::CDecoder &decoder, Byte *levels, int numLevelItems) { int numCodedStructures = m_InBitStream.ReadBits(kNumBitsInByte) + kLevelStructuresNumberAdditionalValue; int currentIndex = 0; for(int i = 0; i < numCodedStructures; i++) { int level = m_InBitStream.ReadBits(kNumLevelStructureLevelBits) + kLevelStructureLevelAdditionalValue; int rep = m_InBitStream.ReadBits(kNumLevelStructureRepNumberBits) + kLevelStructureRepNumberAdditionalValue; if (currentIndex + rep > numLevelItems) throw CException(CException::kData); for(int j = 0; j < rep; j++) levels[currentIndex++] = (Byte)level; } if (currentIndex != numLevelItems) return false; return decoder.SetCodeLengths(levels); } bool CCoder::ReadTables(void) { if (m_LiteralsOn) { Byte literalLevels[kLiteralTableSize]; if (!ReadLevelItems(m_LiteralDecoder, literalLevels, kLiteralTableSize)) return false; } Byte lengthLevels[kLengthTableSize]; if (!ReadLevelItems(m_LengthDecoder, lengthLevels, kLengthTableSize)) return false; Byte distanceLevels[kDistanceTableSize]; return ReadLevelItems(m_DistanceDecoder, distanceLevels, kDistanceTableSize); } class CCoderReleaser { CCoder *m_Coder; public: CCoderReleaser(CCoder *coder): m_Coder(coder) {} ~CCoderReleaser() { m_Coder->ReleaseStreams(); } }; HRESULT CCoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) { if (!m_InBitStream.Create(1 << 20)) return E_OUTOFMEMORY; if (!m_OutWindowStream.Create(kHistorySize)) return E_OUTOFMEMORY; if (outSize == NULL) return E_INVALIDARG; UInt64 pos = 0, unPackSize = *outSize; m_OutWindowStream.SetStream(outStream); m_OutWindowStream.Init(false); m_InBitStream.SetStream(inStream); m_InBitStream.Init(); CCoderReleaser coderReleaser(this); if (!ReadTables()) return S_FALSE; while(pos < unPackSize) { if (progress != NULL && pos % (1 << 16) == 0) { UInt64 packSize = m_InBitStream.GetProcessedSize(); RINOK(progress->SetRatioInfo(&packSize, &pos)); } if(m_InBitStream.ReadBits(1) == kMatchId) // match { UInt32 lowDistBits = m_InBitStream.ReadBits(m_NumDistanceLowDirectBits); UInt32 distance = m_DistanceDecoder.DecodeSymbol(&m_InBitStream); if (distance >= kDistanceTableSize) return S_FALSE; distance = (distance << m_NumDistanceLowDirectBits) + lowDistBits; UInt32 lengthSymbol = m_LengthDecoder.DecodeSymbol(&m_InBitStream); if (lengthSymbol >= kLengthTableSize) return S_FALSE; UInt32 length = lengthSymbol + m_MinMatchLength; if (lengthSymbol == kLengthTableSize - 1) // special symbol = 63 length += m_InBitStream.ReadBits(kNumAdditionalLengthBits); while(distance >= pos && length > 0) { m_OutWindowStream.PutByte(0); pos++; length--; } if (length > 0) m_OutWindowStream.CopyBlock(distance, length); pos += length; } else { Byte b; if (m_LiteralsOn) { UInt32 temp = m_LiteralDecoder.DecodeSymbol(&m_InBitStream); if (temp >= kLiteralTableSize) return S_FALSE; b = (Byte)temp; } else b = (Byte)m_InBitStream.ReadBits(kNumBitsInByte); m_OutWindowStream.PutByte(b); pos++; } } if (pos > unPackSize) return S_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 CLzOutWindowException &e) { return e.ErrorCode; } catch(...) { return S_FALSE; } } STDMETHODIMP CCoder::SetDecoderProperties2(const Byte *data, UInt32 size) { if (size < 1) return E_INVALIDARG; Byte flag = data[0]; m_BigDictionaryOn = ((flag & 2) != 0); m_NumDistanceLowDirectBits = m_BigDictionaryOn ? kNumDistanceLowDirectBitsForBigDict: kNumDistanceLowDirectBitsForSmallDict; m_LiteralsOn = ((flag & 4) != 0); m_MinMatchLength = m_LiteralsOn ? kMatchMinLenWhenLiteralsOn : kMatchMinLenWhenLiteralsOff; return S_OK; } }}}