| // 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)) |
| } |
| } |
| } |
| |
| }} |