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 
11 namespace NCompress {
12 namespace NPpmd {
13 
14 static const UInt32 kBufSize = (1 << 20);
15 
16 static const Byte kOrders[10] = { 3, 4, 4, 5, 5, 6, 8, 16, 24, 32 };
17 
Normalize(int level)18 void 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 <= 31; 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 
CEncoder()41 CEncoder::CEncoder():
42   _inBuf(NULL)
43 {
44   _props.Normalize(-1);
45   Ppmd7_Construct(&_ppmd);
46   _ppmd.rc.enc.Stream = &_outStream.vt;
47 }
48 
~CEncoder()49 CEncoder::~CEncoder()
50 {
51   ::MidFree(_inBuf);
52   Ppmd7_Free(&_ppmd, &g_BigAlloc);
53 }
54 
SetCoderProperties(const PROPID * propIDs,const PROPVARIANT * coderProps,UInt32 numProps)55 STDMETHODIMP 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     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 
WriteCoderProperties(ISequentialOutStream * outStream)130 STDMETHODIMP 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 
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 *,const UInt64 *,ICompressProgressInfo * progress)139 HRESULT 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