blob: 1238df60cb9d39542521dd06af8145b4842210c6 [file] [log] [blame]
// 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
}}