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