// PpmdZip.cpp #include "StdAfx.h" #include "../../../C/CpuArch.h" #include "../Common/RegisterCodec.h" #include "../Common/StreamUtils.h" #include "PpmdZip.h" namespace NCompress { namespace NPpmdZip { static const UInt32 kBufSize = 1 << 20; bool CBuf::Alloc() { if (!Buf) Buf = (Byte *)::MidAlloc(kBufSize); return (Buf != NULL); } CDecoder::CDecoder(bool fullFileMode): _fullFileMode(fullFileMode) { Ppmd8_Construct(&_ppmd); _ppmd.Stream.In = &_inStream.vt; } CDecoder::~CDecoder() { Ppmd8_Free(&_ppmd, &g_BigAlloc); } Z7_COM7F_IMF(CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)) { // try { 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; const UInt32 val = GetUi16(buf); const unsigned order = (val & 0xF) + 1; const UInt32 mem = ((val >> 4) & 0xFF) + 1; const unsigned 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_Init_RangeDec(&_ppmd)) return S_FALSE; Ppmd8_Init(&_ppmd, order, restor); } bool wasFinished = false; UInt64 processedSize = 0; for (;;) { size_t size = kBufSize; if (outSize) { const UInt64 rem = *outSize - processedSize; if (size > rem) { size = (size_t)rem; if (size == 0) break; } } int sym; Byte *buf = _outStream.Buf; const Byte *lim = buf + size; do { sym = Ppmd8_DecodeSymbol(&_ppmd); if (_inStream.Extra || sym < 0) break; *buf++ = (Byte)sym; } while (buf != lim); const size_t cur = (size_t)(buf - _outStream.Buf); processedSize += cur; RINOK(WriteStream(outStream, _outStream.Buf, cur)) RINOK(_inStream.Res) if (_inStream.Extra) return S_FALSE; if (sym < 0) { if (sym != -1) return S_FALSE; wasFinished = true; break; } if (progress) { const UInt64 inProccessed = _inStream.GetProcessed(); RINOK(progress->SetRatioInfo(&inProccessed, &processedSize)) } } RINOK(_inStream.Res) if (_fullFileMode) { if (!wasFinished) { const int res = Ppmd8_DecodeSymbol(&_ppmd); RINOK(_inStream.Res) if (_inStream.Extra || res != -1) return S_FALSE; } if (!Ppmd8_RangeDec_IsFinishedOK(&_ppmd)) return S_FALSE; if (inSize && *inSize != _inStream.GetProcessed()) return S_FALSE; } return S_OK; // } catch (...) { return E_FAIL; } } Z7_COM7F_IMF(CDecoder::SetFinishMode(UInt32 finishMode)) { _fullFileMode = (finishMode != 0); return S_OK; } Z7_COM7F_IMF(CDecoder::GetInStreamProcessedSize(UInt64 *value)) { *value = _inStream.GetProcessed(); return S_OK; } // ---------- Encoder ---------- void CEncProps::Normalize(int level) { if (level < 0) level = 5; if (level == 0) level = 1; if (level > 9) level = 9; if (MemSizeMB == (UInt32)(Int32)-1) MemSizeMB = 1 << (level - 1); const unsigned kMult = 16; for (UInt32 m = 1; m < MemSizeMB; m <<= 1) if (ReduceSize <= (m << 20) / kMult) { MemSizeMB = m; break; } if (Order == -1) Order = 3 + level; if (Restor == -1) Restor = level < 7 ? PPMD8_RESTORE_METHOD_RESTART : PPMD8_RESTORE_METHOD_CUT_OFF; } CEncoder::~CEncoder() { Ppmd8_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) { props.ReduceSize = (UInt32)(Int32)-1; if (prop.vt == VT_UI8 && prop.uhVal.QuadPart < (UInt32)(Int32)-1) props.ReduceSize = (UInt32)prop.uhVal.QuadPart; continue; } if (prop.vt != VT_UI4) return E_INVALIDARG; const UInt32 v = (UInt32)prop.ulVal; switch (propID) { case NCoderPropID::kUsedMemorySize: if (v < (1 << 20) || v > (1 << 28)) return E_INVALIDARG; props.MemSizeMB = v >> 20; break; case NCoderPropID::kOrder: if (v < PPMD8_MIN_ORDER || v > PPMD8_MAX_ORDER) return E_INVALIDARG; props.Order = (Byte)v; break; case NCoderPropID::kNumThreads: break; case NCoderPropID::kLevel: level = (int)v; break; case NCoderPropID::kAlgorithm: if (v >= PPMD8_RESTORE_METHOD_UNSUPPPORTED) return E_INVALIDARG; props.Restor = (int)v; break; default: return E_INVALIDARG; } } props.Normalize(level); _props = props; return S_OK; } CEncoder::CEncoder() { _props.Normalize(-1); _ppmd.Stream.Out = &_outStream.vt; Ppmd8_Construct(&_ppmd); } Z7_COM7F_IMF(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, _props.MemSizeMB << 20, &g_BigAlloc)) return E_OUTOFMEMORY; _outStream.Stream = outStream; _outStream.Init(); Ppmd8_Init_RangeEnc(&_ppmd) Ppmd8_Init(&_ppmd, (unsigned)_props.Order, (unsigned)_props.Restor); { const unsigned val = ((unsigned)_props.Order - 1) + (((unsigned)_props.MemSizeMB - 1) << 4) + ((unsigned)_props.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_Flush_RangeEnc(&_ppmd); return _outStream.Flush(); } processed += size; const Byte *buf = _inStream.Buf; const Byte *lim = buf + size; do { Ppmd8_EncodeSymbol(&_ppmd, *buf); if (_outStream.Res != S_OK) break; } while (++buf != lim); RINOK(_outStream.Res) if (progress) { const UInt64 outProccessed = _outStream.GetProcessed(); RINOK(progress->SetRatioInfo(&processed, &outProccessed)) } } } }}