1// PpmdEncoder.cpp 2 3#include "StdAfx.h" 4 5#include "../../../C/Alloc.h" 6 7#include "../Common/StreamUtils.h" 8 9#include "PpmdEncoder.h" 10 11namespace NCompress { 12namespace NPpmd { 13 14static const UInt32 kBufSize = (1 << 20); 15 16static const Byte kOrders[10] = { 3, 4, 4, 5, 5, 6, 8, 16, 24, 32 }; 17 18void CEncProps::Normalize(int level) 19{ 20 if (level < 0) level = 5; 21 if (level > 9) level = 9; 22 if (MemSize == (UInt32)(Int32)-1) 23 MemSize = (UInt32)1 << (level + 19); 24 const unsigned kMult = 16; 25 if (MemSize / kMult > ReduceSize) 26 { 27 for (unsigned i = 16; i < 32; i++) 28 { 29 UInt32 m = (UInt32)1 << i; 30 if (ReduceSize <= m / kMult) 31 { 32 if (MemSize > m) 33 MemSize = m; 34 break; 35 } 36 } 37 } 38 if (Order == -1) Order = kOrders[(unsigned)level]; 39} 40 41CEncoder::CEncoder(): 42 _inBuf(NULL) 43{ 44 _props.Normalize(-1); 45 Ppmd7_Construct(&_ppmd); 46 _ppmd.rc.enc.Stream = &_outStream.vt; 47} 48 49CEncoder::~CEncoder() 50{ 51 ::MidFree(_inBuf); 52 Ppmd7_Free(&_ppmd, &g_BigAlloc); 53} 54 55Z7_COM7F_IMF(CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *coderProps, UInt32 numProps)) 56{ 57 int level = -1; 58 CEncProps props; 59 for (UInt32 i = 0; i < numProps; i++) 60 { 61 const PROPVARIANT &prop = coderProps[i]; 62 const PROPID propID = propIDs[i]; 63 if (propID > NCoderPropID::kReduceSize) 64 continue; 65 if (propID == NCoderPropID::kReduceSize) 66 { 67 if (prop.vt == VT_UI8 && prop.uhVal.QuadPart < (UInt32)(Int32)-1) 68 props.ReduceSize = (UInt32)prop.uhVal.QuadPart; 69 continue; 70 } 71 72 if (propID == NCoderPropID::kUsedMemorySize) 73 { 74 // here we have selected (4 GiB - 1 KiB) as replacement for (4 GiB) MEM_SIZE. 75 const UInt32 kPpmd_Default_4g = (UInt32)0 - ((UInt32)1 << 10); 76 UInt32 v; 77 if (prop.vt == VT_UI8) 78 { 79 // 21.03 : we support 64-bit values (for 4 GiB value) 80 const UInt64 v64 = prop.uhVal.QuadPart; 81 if (v64 > ((UInt64)1 << 32)) 82 return E_INVALIDARG; 83 if (v64 == ((UInt64)1 << 32)) 84 v = kPpmd_Default_4g; 85 else 86 v = (UInt32)v64; 87 } 88 else if (prop.vt == VT_UI4) 89 v = (UInt32)prop.ulVal; 90 else 91 return E_INVALIDARG; 92 if (v > PPMD7_MAX_MEM_SIZE) 93 v = kPpmd_Default_4g; 94 95 /* here we restrict MEM_SIZE for Encoder. 96 It's for better performance of encoding and decoding. 97 The Decoder still supports more MEM_SIZE values. */ 98 if (v < ((UInt32)1 << 16) || (v & 3) != 0) 99 return E_INVALIDARG; 100 // if (v < PPMD7_MIN_MEM_SIZE) return E_INVALIDARG; // (1 << 11) 101 /* 102 Supported MEM_SIZE range : 103 [ (1 << 11) , 0xFFFFFFFF - 12 * 3 ] - current 7-Zip's Ppmd7 constants 104 [ 1824 , 0xFFFFFFFF ] - real limits of Ppmd7 code 105 */ 106 props.MemSize = v; 107 continue; 108 } 109 110 if (prop.vt != VT_UI4) 111 return E_INVALIDARG; 112 const UInt32 v = (UInt32)prop.ulVal; 113 switch (propID) 114 { 115 case NCoderPropID::kOrder: 116 if (v < 2 || v > 32) 117 return E_INVALIDARG; 118 props.Order = (Byte)v; 119 break; 120 case NCoderPropID::kNumThreads: break; 121 case NCoderPropID::kLevel: level = (int)v; break; 122 default: return E_INVALIDARG; 123 } 124 } 125 props.Normalize(level); 126 _props = props; 127 return S_OK; 128} 129 130Z7_COM7F_IMF(CEncoder::WriteCoderProperties(ISequentialOutStream *outStream)) 131{ 132 const UInt32 kPropSize = 5; 133 Byte props[kPropSize]; 134 props[0] = (Byte)_props.Order; 135 SetUi32(props + 1, _props.MemSize) 136 return WriteStream(outStream, props, kPropSize); 137} 138 139Z7_COM7F_IMF(CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, 140 const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)) 141{ 142 if (!_inBuf) 143 { 144 _inBuf = (Byte *)::MidAlloc(kBufSize); 145 if (!_inBuf) 146 return E_OUTOFMEMORY; 147 } 148 if (!_outStream.Alloc(1 << 20)) 149 return E_OUTOFMEMORY; 150 if (!Ppmd7_Alloc(&_ppmd, _props.MemSize, &g_BigAlloc)) 151 return E_OUTOFMEMORY; 152 153 _outStream.Stream = outStream; 154 _outStream.Init(); 155 156 Ppmd7z_Init_RangeEnc(&_ppmd); 157 Ppmd7_Init(&_ppmd, (unsigned)_props.Order); 158 159 UInt64 processed = 0; 160 for (;;) 161 { 162 UInt32 size; 163 RINOK(inStream->Read(_inBuf, kBufSize, &size)) 164 if (size == 0) 165 { 166 // We don't write EndMark in PPMD-7z. 167 // Ppmd7z_EncodeSymbol(&_ppmd, -1); 168 Ppmd7z_Flush_RangeEnc(&_ppmd); 169 return _outStream.Flush(); 170 } 171 const Byte *buf = _inBuf; 172 const Byte *lim = buf + size; 173 /* 174 for (; buf < lim; buf++) 175 { 176 Ppmd7z_EncodeSymbol(&_ppmd, *buf); 177 RINOK(_outStream.Res); 178 } 179 */ 180 181 Ppmd7z_EncodeSymbols(&_ppmd, buf, lim); 182 RINOK(_outStream.Res) 183 184 processed += size; 185 if (progress) 186 { 187 const UInt64 outSize = _outStream.GetProcessed(); 188 RINOK(progress->SetRatioInfo(&processed, &outSize)) 189 } 190 } 191} 192 193}} 194