mirror of
https://github.com/holub/mame
synced 2025-06-22 20:38:50 +03:00
194 lines
4.8 KiB
C++
194 lines
4.8 KiB
C++
// PpmdEncoder.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "../../../C/Alloc.h"
|
|
|
|
#include "../Common/StreamUtils.h"
|
|
|
|
#include "PpmdEncoder.h"
|
|
|
|
namespace NCompress {
|
|
namespace NPpmd {
|
|
|
|
static const UInt32 kBufSize = (1 << 20);
|
|
|
|
static const Byte kOrders[10] = { 3, 4, 4, 5, 5, 6, 8, 16, 24, 32 };
|
|
|
|
void CEncProps::Normalize(int level)
|
|
{
|
|
if (level < 0) level = 5;
|
|
if (level > 9) level = 9;
|
|
if (MemSize == (UInt32)(Int32)-1)
|
|
MemSize = (UInt32)1 << (level + 19);
|
|
const unsigned kMult = 16;
|
|
if (MemSize / kMult > ReduceSize)
|
|
{
|
|
for (unsigned i = 16; i < 32; i++)
|
|
{
|
|
UInt32 m = (UInt32)1 << i;
|
|
if (ReduceSize <= m / kMult)
|
|
{
|
|
if (MemSize > m)
|
|
MemSize = m;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (Order == -1) Order = kOrders[(unsigned)level];
|
|
}
|
|
|
|
CEncoder::CEncoder():
|
|
_inBuf(NULL)
|
|
{
|
|
_props.Normalize(-1);
|
|
Ppmd7_Construct(&_ppmd);
|
|
_ppmd.rc.enc.Stream = &_outStream.vt;
|
|
}
|
|
|
|
CEncoder::~CEncoder()
|
|
{
|
|
::MidFree(_inBuf);
|
|
Ppmd7_Free(&_ppmd, &g_BigAlloc);
|
|
}
|
|
|
|
Z7_COM7F_IMF(CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *coderProps, UInt32 numProps))
|
|
{
|
|
int level = -1;
|
|
CEncProps props;
|
|
for (UInt32 i = 0; i < numProps; i++)
|
|
{
|
|
const PROPVARIANT &prop = coderProps[i];
|
|
const PROPID propID = propIDs[i];
|
|
if (propID > NCoderPropID::kReduceSize)
|
|
continue;
|
|
if (propID == NCoderPropID::kReduceSize)
|
|
{
|
|
if (prop.vt == VT_UI8 && prop.uhVal.QuadPart < (UInt32)(Int32)-1)
|
|
props.ReduceSize = (UInt32)prop.uhVal.QuadPart;
|
|
continue;
|
|
}
|
|
|
|
if (propID == NCoderPropID::kUsedMemorySize)
|
|
{
|
|
// here we have selected (4 GiB - 1 KiB) as replacement for (4 GiB) MEM_SIZE.
|
|
const UInt32 kPpmd_Default_4g = (UInt32)0 - ((UInt32)1 << 10);
|
|
UInt32 v;
|
|
if (prop.vt == VT_UI8)
|
|
{
|
|
// 21.03 : we support 64-bit values (for 4 GiB value)
|
|
const UInt64 v64 = prop.uhVal.QuadPart;
|
|
if (v64 > ((UInt64)1 << 32))
|
|
return E_INVALIDARG;
|
|
if (v64 == ((UInt64)1 << 32))
|
|
v = kPpmd_Default_4g;
|
|
else
|
|
v = (UInt32)v64;
|
|
}
|
|
else if (prop.vt == VT_UI4)
|
|
v = (UInt32)prop.ulVal;
|
|
else
|
|
return E_INVALIDARG;
|
|
if (v > PPMD7_MAX_MEM_SIZE)
|
|
v = kPpmd_Default_4g;
|
|
|
|
/* here we restrict MEM_SIZE for Encoder.
|
|
It's for better performance of encoding and decoding.
|
|
The Decoder still supports more MEM_SIZE values. */
|
|
if (v < ((UInt32)1 << 16) || (v & 3) != 0)
|
|
return E_INVALIDARG;
|
|
// if (v < PPMD7_MIN_MEM_SIZE) return E_INVALIDARG; // (1 << 11)
|
|
/*
|
|
Supported MEM_SIZE range :
|
|
[ (1 << 11) , 0xFFFFFFFF - 12 * 3 ] - current 7-Zip's Ppmd7 constants
|
|
[ 1824 , 0xFFFFFFFF ] - real limits of Ppmd7 code
|
|
*/
|
|
props.MemSize = v;
|
|
continue;
|
|
}
|
|
|
|
if (prop.vt != VT_UI4)
|
|
return E_INVALIDARG;
|
|
const UInt32 v = (UInt32)prop.ulVal;
|
|
switch (propID)
|
|
{
|
|
case NCoderPropID::kOrder:
|
|
if (v < 2 || v > 32)
|
|
return E_INVALIDARG;
|
|
props.Order = (Byte)v;
|
|
break;
|
|
case NCoderPropID::kNumThreads: break;
|
|
case NCoderPropID::kLevel: level = (int)v; break;
|
|
default: return E_INVALIDARG;
|
|
}
|
|
}
|
|
props.Normalize(level);
|
|
_props = props;
|
|
return S_OK;
|
|
}
|
|
|
|
Z7_COM7F_IMF(CEncoder::WriteCoderProperties(ISequentialOutStream *outStream))
|
|
{
|
|
const UInt32 kPropSize = 5;
|
|
Byte props[kPropSize];
|
|
props[0] = (Byte)_props.Order;
|
|
SetUi32(props + 1, _props.MemSize)
|
|
return WriteStream(outStream, props, kPropSize);
|
|
}
|
|
|
|
Z7_COM7F_IMF(CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
|
|
const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress))
|
|
{
|
|
if (!_inBuf)
|
|
{
|
|
_inBuf = (Byte *)::MidAlloc(kBufSize);
|
|
if (!_inBuf)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
if (!_outStream.Alloc(1 << 20))
|
|
return E_OUTOFMEMORY;
|
|
if (!Ppmd7_Alloc(&_ppmd, _props.MemSize, &g_BigAlloc))
|
|
return E_OUTOFMEMORY;
|
|
|
|
_outStream.Stream = outStream;
|
|
_outStream.Init();
|
|
|
|
Ppmd7z_Init_RangeEnc(&_ppmd);
|
|
Ppmd7_Init(&_ppmd, (unsigned)_props.Order);
|
|
|
|
UInt64 processed = 0;
|
|
for (;;)
|
|
{
|
|
UInt32 size;
|
|
RINOK(inStream->Read(_inBuf, kBufSize, &size))
|
|
if (size == 0)
|
|
{
|
|
// We don't write EndMark in PPMD-7z.
|
|
// Ppmd7z_EncodeSymbol(&_ppmd, -1);
|
|
Ppmd7z_Flush_RangeEnc(&_ppmd);
|
|
return _outStream.Flush();
|
|
}
|
|
const Byte *buf = _inBuf;
|
|
const Byte *lim = buf + size;
|
|
/*
|
|
for (; buf < lim; buf++)
|
|
{
|
|
Ppmd7z_EncodeSymbol(&_ppmd, *buf);
|
|
RINOK(_outStream.Res);
|
|
}
|
|
*/
|
|
|
|
Ppmd7z_EncodeSymbols(&_ppmd, buf, lim);
|
|
RINOK(_outStream.Res)
|
|
|
|
processed += size;
|
|
if (progress)
|
|
{
|
|
const UInt64 outSize = _outStream.GetProcessed();
|
|
RINOK(progress->SetRatioInfo(&processed, &outSize))
|
|
}
|
|
}
|
|
}
|
|
|
|
}}
|