164 lines
3.4 KiB
C++
164 lines
3.4 KiB
C++
// 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
|
|
};
|
|
|
|
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
|
|
|
|
}}
|