michael@0: // LZMADecoder.cpp michael@0: michael@0: #include "StdAfx.h" michael@0: michael@0: #include "LZMADecoder.h" michael@0: #include "../../../Common/Defs.h" michael@0: michael@0: namespace NCompress { michael@0: namespace NLZMA { michael@0: michael@0: const int kLenIdFinished = -1; michael@0: const int kLenIdNeedInit = -2; michael@0: michael@0: void CDecoder::Init() michael@0: { michael@0: { michael@0: for(int i = 0; i < kNumStates; i++) michael@0: { michael@0: for (UInt32 j = 0; j <= _posStateMask; j++) michael@0: { michael@0: _isMatch[i][j].Init(); michael@0: _isRep0Long[i][j].Init(); michael@0: } michael@0: _isRep[i].Init(); michael@0: _isRepG0[i].Init(); michael@0: _isRepG1[i].Init(); michael@0: _isRepG2[i].Init(); michael@0: } michael@0: } michael@0: { michael@0: for (UInt32 i = 0; i < kNumLenToPosStates; i++) michael@0: _posSlotDecoder[i].Init(); michael@0: } michael@0: { michael@0: for(UInt32 i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) michael@0: _posDecoders[i].Init(); michael@0: } michael@0: _posAlignDecoder.Init(); michael@0: _lenDecoder.Init(_posStateMask + 1); michael@0: _repMatchLenDecoder.Init(_posStateMask + 1); michael@0: _literalDecoder.Init(); michael@0: michael@0: _state.Init(); michael@0: _reps[0] = _reps[1] = _reps[2] = _reps[3] = 0; michael@0: } michael@0: michael@0: HRESULT CDecoder::CodeSpec(UInt32 curSize) michael@0: { michael@0: if (_outSizeDefined) michael@0: { michael@0: const UInt64 rem = _outSize - _outWindowStream.GetProcessedSize(); michael@0: if (curSize > rem) michael@0: curSize = (UInt32)rem; michael@0: } michael@0: michael@0: if (_remainLen == kLenIdFinished) michael@0: return S_OK; michael@0: if (_remainLen == kLenIdNeedInit) michael@0: { michael@0: _rangeDecoder.Init(); michael@0: Init(); michael@0: _remainLen = 0; michael@0: } michael@0: if (curSize == 0) michael@0: return S_OK; michael@0: michael@0: UInt32 rep0 = _reps[0]; michael@0: UInt32 rep1 = _reps[1]; michael@0: UInt32 rep2 = _reps[2]; michael@0: UInt32 rep3 = _reps[3]; michael@0: CState state = _state; michael@0: Byte previousByte; michael@0: michael@0: while(_remainLen > 0 && curSize > 0) michael@0: { michael@0: previousByte = _outWindowStream.GetByte(rep0); michael@0: _outWindowStream.PutByte(previousByte); michael@0: _remainLen--; michael@0: curSize--; michael@0: } michael@0: UInt64 nowPos64 = _outWindowStream.GetProcessedSize(); michael@0: if (nowPos64 == 0) michael@0: previousByte = 0; michael@0: else michael@0: previousByte = _outWindowStream.GetByte(0); michael@0: michael@0: while(curSize > 0) michael@0: { michael@0: { michael@0: #ifdef _NO_EXCEPTIONS michael@0: if (_rangeDecoder.Stream.ErrorCode != S_OK) michael@0: return _rangeDecoder.Stream.ErrorCode; michael@0: #endif michael@0: if (_rangeDecoder.Stream.WasFinished()) michael@0: return S_FALSE; michael@0: UInt32 posState = UInt32(nowPos64) & _posStateMask; michael@0: if (_isMatch[state.Index][posState].Decode(&_rangeDecoder) == 0) michael@0: { michael@0: if(!state.IsCharState()) michael@0: previousByte = _literalDecoder.DecodeWithMatchByte(&_rangeDecoder, michael@0: (UInt32)nowPos64, previousByte, _outWindowStream.GetByte(rep0)); michael@0: else michael@0: previousByte = _literalDecoder.DecodeNormal(&_rangeDecoder, michael@0: (UInt32)nowPos64, previousByte); michael@0: _outWindowStream.PutByte(previousByte); michael@0: state.UpdateChar(); michael@0: curSize--; michael@0: nowPos64++; michael@0: } michael@0: else michael@0: { michael@0: UInt32 len; michael@0: if(_isRep[state.Index].Decode(&_rangeDecoder) == 1) michael@0: { michael@0: len = 0; michael@0: if(_isRepG0[state.Index].Decode(&_rangeDecoder) == 0) michael@0: { michael@0: if(_isRep0Long[state.Index][posState].Decode(&_rangeDecoder) == 0) michael@0: { michael@0: state.UpdateShortRep(); michael@0: len = 1; michael@0: } michael@0: } michael@0: else michael@0: { michael@0: UInt32 distance; michael@0: if(_isRepG1[state.Index].Decode(&_rangeDecoder) == 0) michael@0: distance = rep1; michael@0: else michael@0: { michael@0: if (_isRepG2[state.Index].Decode(&_rangeDecoder) == 0) michael@0: distance = rep2; michael@0: else michael@0: { michael@0: distance = rep3; michael@0: rep3 = rep2; michael@0: } michael@0: rep2 = rep1; michael@0: } michael@0: rep1 = rep0; michael@0: rep0 = distance; michael@0: } michael@0: if (len == 0) michael@0: { michael@0: len = _repMatchLenDecoder.Decode(&_rangeDecoder, posState) + kMatchMinLen; michael@0: state.UpdateRep(); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: rep3 = rep2; michael@0: rep2 = rep1; michael@0: rep1 = rep0; michael@0: len = kMatchMinLen + _lenDecoder.Decode(&_rangeDecoder, posState); michael@0: state.UpdateMatch(); michael@0: UInt32 posSlot = _posSlotDecoder[GetLenToPosState(len)].Decode(&_rangeDecoder); michael@0: if (posSlot >= kStartPosModelIndex) michael@0: { michael@0: UInt32 numDirectBits = (posSlot >> 1) - 1; michael@0: rep0 = ((2 | (posSlot & 1)) << numDirectBits); michael@0: michael@0: if (posSlot < kEndPosModelIndex) michael@0: rep0 += NRangeCoder::ReverseBitTreeDecode(_posDecoders + michael@0: rep0 - posSlot - 1, &_rangeDecoder, numDirectBits); michael@0: else michael@0: { michael@0: rep0 += (_rangeDecoder.DecodeDirectBits( michael@0: numDirectBits - kNumAlignBits) << kNumAlignBits); michael@0: rep0 += _posAlignDecoder.ReverseDecode(&_rangeDecoder); michael@0: if (rep0 == 0xFFFFFFFF) michael@0: { michael@0: _remainLen = kLenIdFinished; michael@0: return S_OK; michael@0: } michael@0: } michael@0: } michael@0: else michael@0: rep0 = posSlot; michael@0: } michael@0: UInt32 locLen = len; michael@0: if (len > curSize) michael@0: locLen = (UInt32)curSize; michael@0: if (!_outWindowStream.CopyBlock(rep0, locLen)) michael@0: return S_FALSE; michael@0: previousByte = _outWindowStream.GetByte(0); michael@0: curSize -= locLen; michael@0: nowPos64 += locLen; michael@0: len -= locLen; michael@0: if (len != 0) michael@0: { michael@0: _remainLen = (Int32)len; michael@0: break; michael@0: } michael@0: michael@0: #ifdef _NO_EXCEPTIONS michael@0: if (_outWindowStream.ErrorCode != S_OK) michael@0: return _outWindowStream.ErrorCode; michael@0: #endif michael@0: } michael@0: } michael@0: } michael@0: if (_rangeDecoder.Stream.WasFinished()) michael@0: return S_FALSE; michael@0: _reps[0] = rep0; michael@0: _reps[1] = rep1; michael@0: _reps[2] = rep2; michael@0: _reps[3] = rep3; michael@0: _state = state; michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP CDecoder::CodeReal(ISequentialInStream *inStream, michael@0: ISequentialOutStream *outStream, michael@0: const UInt64 *, const UInt64 *outSize, michael@0: ICompressProgressInfo *progress) michael@0: { michael@0: SetInStream(inStream); michael@0: _outWindowStream.SetStream(outStream); michael@0: SetOutStreamSize(outSize); michael@0: CDecoderFlusher flusher(this); michael@0: michael@0: while (true) michael@0: { michael@0: UInt32 curSize = 1 << 18; michael@0: RINOK(CodeSpec(curSize)); michael@0: if (_remainLen == kLenIdFinished) michael@0: break; michael@0: if (progress != NULL) michael@0: { michael@0: UInt64 inSize = _rangeDecoder.GetProcessedSize(); michael@0: UInt64 nowPos64 = _outWindowStream.GetProcessedSize(); michael@0: RINOK(progress->SetRatioInfo(&inSize, &nowPos64)); michael@0: } michael@0: if (_outSizeDefined) michael@0: if (_outWindowStream.GetProcessedSize() >= _outSize) michael@0: break; michael@0: } michael@0: flusher.NeedFlush = false; michael@0: return Flush(); michael@0: } michael@0: michael@0: michael@0: #ifdef _NO_EXCEPTIONS michael@0: michael@0: #define LZMA_TRY_BEGIN michael@0: #define LZMA_TRY_END michael@0: michael@0: #else michael@0: michael@0: #define LZMA_TRY_BEGIN try { michael@0: #define LZMA_TRY_END } \ michael@0: catch(const CInBufferException &e) { return e.ErrorCode; } \ michael@0: catch(const CLZOutWindowException &e) { return e.ErrorCode; } \ michael@0: catch(...) { return S_FALSE; } michael@0: michael@0: #endif michael@0: michael@0: michael@0: STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, michael@0: ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, michael@0: ICompressProgressInfo *progress) michael@0: { michael@0: LZMA_TRY_BEGIN michael@0: return CodeReal(inStream, outStream, inSize, outSize, progress); michael@0: LZMA_TRY_END michael@0: } michael@0: michael@0: STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *properties, UInt32 size) michael@0: { michael@0: if (size < 5) michael@0: return E_INVALIDARG; michael@0: int lc = properties[0] % 9; michael@0: Byte remainder = (Byte)(properties[0] / 9); michael@0: int lp = remainder % 5; michael@0: int pb = remainder / 5; michael@0: if (pb > NLength::kNumPosStatesBitsMax) michael@0: return E_INVALIDARG; michael@0: _posStateMask = (1 << pb) - 1; michael@0: UInt32 dictionarySize = 0; michael@0: for (int i = 0; i < 4; i++) michael@0: dictionarySize += ((UInt32)(properties[1 + i])) << (i * 8); michael@0: if (!_outWindowStream.Create(dictionarySize)) michael@0: return E_OUTOFMEMORY; michael@0: if (!_literalDecoder.Create(lp, lc)) michael@0: return E_OUTOFMEMORY; michael@0: if (!_rangeDecoder.Create(1 << 20)) michael@0: return E_OUTOFMEMORY; michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value) michael@0: { michael@0: *value = _rangeDecoder.GetProcessedSize(); michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) michael@0: { michael@0: _rangeDecoder.SetStream(inStream); michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP CDecoder::ReleaseInStream() michael@0: { michael@0: _rangeDecoder.ReleaseStream(); michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize) michael@0: { michael@0: if (_outSizeDefined = (outSize != NULL)) michael@0: _outSize = *outSize; michael@0: _remainLen = kLenIdNeedInit; michael@0: _outWindowStream.Init(); michael@0: return S_OK; michael@0: } michael@0: michael@0: #ifdef _ST_MODE michael@0: michael@0: STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize) michael@0: { michael@0: LZMA_TRY_BEGIN michael@0: if (processedSize) michael@0: *processedSize = 0; michael@0: const UInt64 startPos = _outWindowStream.GetProcessedSize(); michael@0: _outWindowStream.SetMemStream((Byte *)data); michael@0: RINOK(CodeSpec(size)); michael@0: if (processedSize) michael@0: *processedSize = (UInt32)(_outWindowStream.GetProcessedSize() - startPos); michael@0: return Flush(); michael@0: LZMA_TRY_END michael@0: } michael@0: michael@0: #endif michael@0: michael@0: }}