1 // PpmdEncoder.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/Alloc.h"
6 #include "../../../C/CpuArch.h"
7 
8 #include "../Common/StreamUtils.h"
9 
10 #include "PpmdEncoder.h"
11 
12 namespace NCompress {
13 namespace NPpmd {
14 
15 static const UInt32 kBufSize = (1 << 20);
16 
17 static const Byte kOrders[10] = { 3, 4, 4, 5, 5, 6, 8, 16, 24, 32 };
18 
Normalize(int level)19 void CEncProps::Normalize(int level)
20 {
21   if (level < 0) level = 5;
22   if (level > 9) level = 9;
23   if (MemSize == (UInt32)(Int32)-1)
24     MemSize = level >= 9 ? ((UInt32)192 << 20) : ((UInt32)1 << (level + 19));
25   const unsigned kMult = 16;
26   if (MemSize / kMult > ReduceSize)
27   {
28     for (unsigned i = 16; i <= 31; i++)
29     {
30       UInt32 m = (UInt32)1 << i;
31       if (ReduceSize <= m / kMult)
32       {
33         if (MemSize > m)
34           MemSize = m;
35         break;
36       }
37     }
38   }
39   if (Order == -1) Order = kOrders[(unsigned)level];
40 }
41 
CEncoder()42 CEncoder::CEncoder():
43   _inBuf(NULL)
44 {
45   _props.Normalize(-1);
46   _rangeEnc.Stream = &_outStream.vt;
47   Ppmd7_Construct(&_ppmd);
48 }
49 
~CEncoder()50 CEncoder::~CEncoder()
51 {
52   ::MidFree(_inBuf);
53   Ppmd7_Free(&_ppmd, &g_BigAlloc);
54 }
55 
SetCoderProperties(const PROPID * propIDs,const PROPVARIANT * coderProps,UInt32 numProps)56 STDMETHODIMP CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *coderProps, UInt32 numProps)
57 {
58   int level = -1;
59   CEncProps props;
60   for (UInt32 i = 0; i < numProps; i++)
61   {
62     const PROPVARIANT &prop = coderProps[i];
63     PROPID propID = propIDs[i];
64     if (propID > NCoderPropID::kReduceSize)
65       continue;
66     if (propID == NCoderPropID::kReduceSize)
67     {
68       if (prop.vt == VT_UI8 && prop.uhVal.QuadPart < (UInt32)(Int32)-1)
69         props.ReduceSize = (UInt32)prop.uhVal.QuadPart;
70       continue;
71     }
72     if (prop.vt != VT_UI4)
73       return E_INVALIDARG;
74     UInt32 v = (UInt32)prop.ulVal;
75     switch (propID)
76     {
77       case NCoderPropID::kUsedMemorySize:
78         if (v < (1 << 16) || v > PPMD7_MAX_MEM_SIZE || (v & 3) != 0)
79           return E_INVALIDARG;
80         props.MemSize = v;
81         break;
82       case NCoderPropID::kOrder:
83         if (v < 2 || v > 32)
84           return E_INVALIDARG;
85         props.Order = (Byte)v;
86         break;
87       case NCoderPropID::kNumThreads: break;
88       case NCoderPropID::kLevel: level = (int)v; break;
89       default: return E_INVALIDARG;
90     }
91   }
92   props.Normalize(level);
93   _props = props;
94   return S_OK;
95 }
96 
WriteCoderProperties(ISequentialOutStream * outStream)97 STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream)
98 {
99   const UInt32 kPropSize = 5;
100   Byte props[kPropSize];
101   props[0] = (Byte)_props.Order;
102   SetUi32(props + 1, _props.MemSize);
103   return WriteStream(outStream, props, kPropSize);
104 }
105 
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 *,const UInt64 *,ICompressProgressInfo * progress)106 HRESULT CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
107     const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)
108 {
109   if (!_inBuf)
110   {
111     _inBuf = (Byte *)::MidAlloc(kBufSize);
112     if (!_inBuf)
113       return E_OUTOFMEMORY;
114   }
115   if (!_outStream.Alloc(1 << 20))
116     return E_OUTOFMEMORY;
117   if (!Ppmd7_Alloc(&_ppmd, _props.MemSize, &g_BigAlloc))
118     return E_OUTOFMEMORY;
119 
120   _outStream.Stream = outStream;
121   _outStream.Init();
122 
123   Ppmd7z_RangeEnc_Init(&_rangeEnc);
124   Ppmd7_Init(&_ppmd, _props.Order);
125 
126   UInt64 processed = 0;
127   for (;;)
128   {
129     UInt32 size;
130     RINOK(inStream->Read(_inBuf, kBufSize, &size));
131     if (size == 0)
132     {
133       // We don't write EndMark in PPMD-7z.
134       // Ppmd7_EncodeSymbol(&_ppmd, &_rangeEnc, -1);
135       Ppmd7z_RangeEnc_FlushData(&_rangeEnc);
136       return _outStream.Flush();
137     }
138     for (UInt32 i = 0; i < size; i++)
139     {
140       Ppmd7_EncodeSymbol(&_ppmd, &_rangeEnc, _inBuf[i]);
141       RINOK(_outStream.Res);
142     }
143     processed += size;
144     if (progress)
145     {
146       UInt64 outSize = _outStream.GetProcessed();
147       RINOK(progress->SetRatioInfo(&processed, &outSize));
148     }
149   }
150 }
151 
152 }}
153