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