Files
easy7zip/CPP/7zip/Compress/PpmdZip.cpp
Igor Pavlov 993daef9cb 9.11
2016-05-28 00:16:02 +01:00

224 lines
5.1 KiB
C++
Executable File

// PpmdZip.cpp
// 2010-03-11 : Igor Pavlov : Public domain
#include "StdAfx.h"
#include "../../../C/CpuArch.h"
#include "../Common/StreamUtils.h"
#include "PpmdZip.h"
namespace NCompress {
namespace NPpmdZip {
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(bool fullFileMode):
_fullFileMode(fullFileMode)
{
_ppmd.Stream.In = &_inStream.p;
Ppmd8_Construct(&_ppmd);
}
CDecoder::~CDecoder()
{
Ppmd8_Free(&_ppmd, &g_BigAlloc);
}
STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
{
if (!_outStream.Alloc())
return E_OUTOFMEMORY;
if (!_inStream.Alloc(1 << 20))
return E_OUTOFMEMORY;
_inStream.Stream = inStream;
_inStream.Init();
{
Byte buf[2];
for (int i = 0; i < 2; i++)
buf[i] = _inStream.ReadByte();
if (_inStream.Extra)
return S_FALSE;
UInt32 val = GetUi16(buf);
UInt32 order = (val & 0xF) + 1;
UInt32 mem = ((val >> 4) & 0xFF) + 1;
UInt32 restor = (val >> 12);
if (order < 2 || restor > 2)
return S_FALSE;
#ifndef PPMD8_FREEZE_SUPPORT
if (restor == 2)
return E_NOTIMPL;
#endif
if (!Ppmd8_Alloc(&_ppmd, mem << 20, &g_BigAlloc))
return E_OUTOFMEMORY;
if (!Ppmd8_RangeDec_Init(&_ppmd))
return S_FALSE;
Ppmd8_Init(&_ppmd, order, restor);
}
bool wasFinished = false;
UInt64 processedSize = 0;
while (!outSize || processedSize < *outSize)
{
size_t size = kBufSize;
if (outSize != NULL)
{
const UInt64 rem = *outSize - processedSize;
if (size > rem)
size = (size_t)rem;
}
Byte *data = _outStream.Buf;
size_t i = 0;
int sym = 0;
do
{
sym = Ppmd8_DecodeSymbol(&_ppmd);
if (_inStream.Extra || sym < 0)
break;
data[i] = (Byte)sym;
}
while (++i != size);
processedSize += i;
RINOK(WriteStream(outStream, _outStream.Buf, i));
RINOK(_inStream.Res);
if (_inStream.Extra)
return S_FALSE;
if (sym < 0)
{
if (sym != -1)
return S_FALSE;
wasFinished = true;
break;
}
if (progress)
{
UInt64 inSize = _inStream.GetProcessed();
RINOK(progress->SetRatioInfo(&inSize, &processedSize));
}
}
RINOK(_inStream.Res);
if (_fullFileMode)
{
if (!wasFinished)
{
int res = Ppmd8_DecodeSymbol(&_ppmd);
RINOK(_inStream.Res);
if (_inStream.Extra || res != -1)
return S_FALSE;
}
if (!Ppmd8_RangeDec_IsFinishedOK(&_ppmd))
return S_FALSE;
}
return S_OK;
}
// ---------- Encoder ----------
CEncoder::~CEncoder()
{
Ppmd8_Free(&_ppmd, &g_BigAlloc);
}
HRESULT CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps)
{
for (UInt32 i = 0; i < numProps; i++)
{
const PROPVARIANT &prop = props[i];
if (prop.vt != VT_UI4)
return E_INVALIDARG;
UInt32 v = (UInt32)prop.ulVal;
switch(propIDs[i])
{
case NCoderPropID::kAlgorithm:
if (v > 1)
return E_INVALIDARG;
_restor = v;
break;
case NCoderPropID::kUsedMemorySize:
if (v < (1 << 20) || v > (1 << 28))
return E_INVALIDARG;
_usedMemInMB = v >> 20;
break;
case NCoderPropID::kOrder:
if (v < PPMD8_MIN_ORDER || v > PPMD8_MAX_ORDER)
return E_INVALIDARG;
_order = (Byte)v;
break;
default:
return E_INVALIDARG;
}
}
return S_OK;
}
CEncoder::CEncoder():
_usedMemInMB(16),
_order(6),
_restor(PPMD8_RESTORE_METHOD_RESTART)
{
_ppmd.Stream.Out = &_outStream.p;
Ppmd8_Construct(&_ppmd);
}
HRESULT CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)
{
if (!_inStream.Alloc())
return E_OUTOFMEMORY;
if (!_outStream.Alloc(1 << 20))
return E_OUTOFMEMORY;
if (!Ppmd8_Alloc(&_ppmd, _usedMemInMB << 20, &g_BigAlloc))
return E_OUTOFMEMORY;
_outStream.Stream = outStream;
_outStream.Init();
Ppmd8_RangeEnc_Init(&_ppmd);
Ppmd8_Init(&_ppmd, _order, _restor);
UInt32 val = (UInt32)((_order - 1) + ((_usedMemInMB - 1) << 4) + (_restor << 12));
_outStream.WriteByte((Byte)(val & 0xFF));
_outStream.WriteByte((Byte)(val >> 8));
RINOK(_outStream.Res);
UInt64 processed = 0;
for (;;)
{
UInt32 size;
RINOK(inStream->Read(_inStream.Buf, kBufSize, &size));
if (size == 0)
{
Ppmd8_EncodeSymbol(&_ppmd, -1);
Ppmd8_RangeEnc_FlushData(&_ppmd);
return _outStream.Flush();
}
for (UInt32 i = 0; i < size; i++)
{
Ppmd8_EncodeSymbol(&_ppmd, _inStream.Buf[i]);
RINOK(_outStream.Res);
}
processed += size;
if (progress != NULL)
{
UInt64 outSize = _outStream.GetProcessed();
RINOK(progress->SetRatioInfo(&processed, &outSize));
}
}
}
}}