| // PpmdDecoder.cpp |
| |
| #include "StdAfx.h" |
| |
| #include "../../../C/Alloc.h" |
| #include "../../../C/CpuArch.h" |
| |
| #include "../Common/StreamUtils.h" |
| |
| #include "PpmdDecoder.h" |
| |
| namespace NCompress { |
| namespace NPpmd { |
| |
| static const UInt32 kBufSize = (1 << 16); |
| |
| enum |
| { |
| kStatus_NeedInit, |
| kStatus_Normal, |
| kStatus_Finished_With_Mark, |
| kStatus_Error |
| }; |
| |
| CDecoder::~CDecoder() |
| { |
| ::MidFree(_outBuf); |
| Ppmd7_Free(&_ppmd, &g_BigAlloc); |
| } |
| |
| Z7_COM7F_IMF(CDecoder::SetDecoderProperties2(const Byte *props, UInt32 size)) |
| { |
| if (size < 5) |
| return E_INVALIDARG; |
| _order = props[0]; |
| const UInt32 memSize = GetUi32(props + 1); |
| if (_order < PPMD7_MIN_ORDER || |
| _order > PPMD7_MAX_ORDER || |
| memSize < PPMD7_MIN_MEM_SIZE || |
| memSize > PPMD7_MAX_MEM_SIZE) |
| return E_NOTIMPL; |
| if (!_inStream.Alloc(1 << 20)) |
| return E_OUTOFMEMORY; |
| if (!Ppmd7_Alloc(&_ppmd, memSize, &g_BigAlloc)) |
| return E_OUTOFMEMORY; |
| return S_OK; |
| } |
| |
| #define MY_rangeDec _ppmd.rc.dec |
| |
| #define CHECK_EXTRA_ERROR \ |
| if (_inStream.Extra) { \ |
| _status = kStatus_Error; \ |
| return (_res = (_inStream.Res != SZ_OK ? _inStream.Res: S_FALSE)); } |
| |
| |
| HRESULT CDecoder::CodeSpec(Byte *memStream, UInt32 size) |
| { |
| if (_res != S_OK) |
| return _res; |
| |
| switch (_status) |
| { |
| case kStatus_Finished_With_Mark: return S_OK; |
| case kStatus_Error: return S_FALSE; |
| case kStatus_NeedInit: |
| _inStream.Init(); |
| if (!Ppmd7z_RangeDec_Init(&MY_rangeDec)) |
| { |
| _status = kStatus_Error; |
| return (_res = S_FALSE); |
| } |
| CHECK_EXTRA_ERROR |
| _status = kStatus_Normal; |
| Ppmd7_Init(&_ppmd, _order); |
| break; |
| } |
| |
| if (_outSizeDefined) |
| { |
| const UInt64 rem = _outSize - _processedSize; |
| if (size > rem) |
| size = (UInt32)rem; |
| } |
| |
| int sym = 0; |
| { |
| Byte *buf = memStream; |
| const Byte *lim = buf + size; |
| for (; buf != lim; buf++) |
| { |
| sym = Ppmd7z_DecodeSymbol(&_ppmd); |
| if (_inStream.Extra || sym < 0) |
| break; |
| *buf = (Byte)sym; |
| } |
| /* |
| buf = Ppmd7z_DecodeSymbols(&_ppmd, buf, lim); |
| sym = _ppmd.LastSymbol; |
| */ |
| _processedSize += (size_t)(buf - memStream); |
| } |
| |
| CHECK_EXTRA_ERROR |
| |
| if (sym >= 0) |
| { |
| if (!FinishStream |
| || !_outSizeDefined |
| || _outSize != _processedSize |
| || MY_rangeDec.Code == 0) |
| return S_OK; |
| /* |
| // We can decode additional End Marker here: |
| sym = Ppmd7z_DecodeSymbol(&_ppmd); |
| CHECK_EXTRA_ERROR |
| */ |
| } |
| |
| if (sym != PPMD7_SYM_END || MY_rangeDec.Code != 0) |
| { |
| _status = kStatus_Error; |
| return (_res = S_FALSE); |
| } |
| |
| _status = kStatus_Finished_With_Mark; |
| return S_OK; |
| } |
| |
| |
| |
| Z7_COM7F_IMF(CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, |
| const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)) |
| { |
| if (!_outBuf) |
| { |
| _outBuf = (Byte *)::MidAlloc(kBufSize); |
| if (!_outBuf) |
| return E_OUTOFMEMORY; |
| } |
| |
| _inStream.Stream = inStream; |
| SetOutStreamSize(outSize); |
| |
| do |
| { |
| const UInt64 startPos = _processedSize; |
| const HRESULT res = CodeSpec(_outBuf, kBufSize); |
| const size_t processed = (size_t)(_processedSize - startPos); |
| RINOK(WriteStream(outStream, _outBuf, processed)) |
| RINOK(res) |
| if (_status == kStatus_Finished_With_Mark) |
| break; |
| if (progress) |
| { |
| const UInt64 inProcessed = _inStream.GetProcessed(); |
| RINOK(progress->SetRatioInfo(&inProcessed, &_processedSize)) |
| } |
| } |
| while (!_outSizeDefined || _processedSize < _outSize); |
| |
| if (FinishStream && inSize && *inSize != _inStream.GetProcessed()) |
| return S_FALSE; |
| |
| return S_OK; |
| } |
| |
| |
| Z7_COM7F_IMF(CDecoder::SetOutStreamSize(const UInt64 *outSize)) |
| { |
| _outSizeDefined = (outSize != NULL); |
| if (_outSizeDefined) |
| _outSize = *outSize; |
| _processedSize = 0; |
| _status = kStatus_NeedInit; |
| _res = SZ_OK; |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CDecoder::SetFinishMode(UInt32 finishMode)) |
| { |
| FinishStream = (finishMode != 0); |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CDecoder::GetInStreamProcessedSize(UInt64 *value)) |
| { |
| *value = _inStream.GetProcessed(); |
| return S_OK; |
| } |
| |
| #ifndef Z7_NO_READ_FROM_CODER |
| |
| Z7_COM7F_IMF(CDecoder::SetInStream(ISequentialInStream *inStream)) |
| { |
| InSeqStream = inStream; |
| _inStream.Stream = inStream; |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CDecoder::ReleaseInStream()) |
| { |
| InSeqStream.Release(); |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)) |
| { |
| const UInt64 startPos = _processedSize; |
| const HRESULT res = CodeSpec((Byte *)data, size); |
| if (processedSize) |
| *processedSize = (UInt32)(_processedSize - startPos); |
| return res; |
| } |
| |
| #endif |
| |
| }} |