// PpmdDecoder.cpp | |
// 2009-03-11 : Igor Pavlov : Public domain | |
#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 << 20); | |
enum | |
{ | |
kStatus_NeedInit, | |
kStatus_Normal, | |
kStatus_Finished, | |
kStatus_Error | |
}; | |
static void *SzBigAlloc(void *, size_t size) { return BigAlloc(size); } | |
static void SzBigFree(void *, void *address) { BigFree(address); } | |
static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; | |
CDecoder::~CDecoder() | |
{ | |
::MidFree(_outBuf); | |
Ppmd7_Free(&_ppmd, &g_BigAlloc); | |
} | |
STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *props, UInt32 size) | |
{ | |
if (size < 5) | |
return E_INVALIDARG; | |
_order = props[0]; | |
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; | |
} | |
HRESULT CDecoder::CodeSpec(Byte *memStream, UInt32 size) | |
{ | |
switch(_status) | |
{ | |
case kStatus_Finished: return S_OK; | |
case kStatus_Error: return S_FALSE; | |
case kStatus_NeedInit: | |
_inStream.Init(); | |
if (!Ppmd7z_RangeDec_Init(&_rangeDec)) | |
{ | |
_status = kStatus_Error; | |
return S_FALSE; | |
} | |
_status = kStatus_Normal; | |
Ppmd7_Init(&_ppmd, _order); | |
break; | |
} | |
if (_outSizeDefined) | |
{ | |
const UInt64 rem = _outSize - _processedSize; | |
if (size > rem) | |
size = (UInt32)rem; | |
} | |
UInt32 i; | |
int sym = 0; | |
for (i = 0; i != size; i++) | |
{ | |
sym = Ppmd7_DecodeSymbol(&_ppmd, &_rangeDec.p); | |
if (_inStream.Extra || sym < 0) | |
break; | |
memStream[i] = (Byte)sym; | |
} | |
_processedSize += i; | |
if (_inStream.Extra) | |
{ | |
_status = kStatus_Error; | |
return _inStream.Res; | |
} | |
if (sym < 0) | |
_status = (sym < -1) ? kStatus_Error : kStatus_Finished; | |
return S_OK; | |
} | |
STDMETHODIMP 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; | |
HRESULT res = CodeSpec(_outBuf, kBufSize); | |
size_t processed = (size_t)(_processedSize - startPos); | |
RINOK(WriteStream(outStream, _outBuf, processed)); | |
RINOK(res); | |
if (_status == kStatus_Finished) | |
break; | |
if (progress) | |
{ | |
UInt64 inSize = _inStream.GetProcessed(); | |
RINOK(progress->SetRatioInfo(&inSize, &_processedSize)); | |
} | |
} | |
while (!_outSizeDefined || _processedSize < _outSize); | |
return S_OK; | |
} | |
STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize) | |
{ | |
_outSizeDefined = (outSize != NULL); | |
if (_outSizeDefined) | |
_outSize = *outSize; | |
_processedSize = 0; | |
_status = kStatus_NeedInit; | |
return S_OK; | |
} | |
#ifndef NO_READ_FROM_CODER | |
STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) | |
{ | |
InSeqStream = inStream; | |
_inStream.Stream = inStream; | |
return S_OK; | |
} | |
STDMETHODIMP CDecoder::ReleaseInStream() | |
{ | |
InSeqStream.Release(); | |
return S_OK; | |
} | |
STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize) | |
{ | |
const UInt64 startPos = _processedSize; | |
HRESULT res = CodeSpec((Byte *)data, size); | |
if (processedSize) | |
*processedSize = (UInt32)(_processedSize - startPos); | |
return res; | |
} | |
#endif | |
}} |