1 // PpmdDecoder.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 "PpmdDecoder.h"
11 
12 namespace NCompress {
13 namespace NPpmd {
14 
15 static const UInt32 kBufSize = (1 << 16);
16 
17 enum
18 {
19   kStatus_NeedInit,
20   kStatus_Normal,
21   kStatus_Finished_With_Mark,
22   kStatus_Error
23 };
24 
~CDecoder()25 CDecoder::~CDecoder()
26 {
27   ::MidFree(_outBuf);
28   Ppmd7_Free(&_ppmd, &g_BigAlloc);
29 }
30 
SetDecoderProperties2(const Byte *props, UInt32 size)31 Z7_COM7F_IMF(CDecoder::SetDecoderProperties2(const Byte *props, UInt32 size))
32 {
33   if (size < 5)
34     return E_INVALIDARG;
35   _order = props[0];
36   const UInt32 memSize = GetUi32(props + 1);
37   if (_order < PPMD7_MIN_ORDER ||
38       _order > PPMD7_MAX_ORDER ||
39       memSize < PPMD7_MIN_MEM_SIZE ||
40       memSize > PPMD7_MAX_MEM_SIZE)
41     return E_NOTIMPL;
42   if (!_inStream.Alloc(1 << 20))
43     return E_OUTOFMEMORY;
44   if (!Ppmd7_Alloc(&_ppmd, memSize, &g_BigAlloc))
45     return E_OUTOFMEMORY;
46   return S_OK;
47 }
48 
49 #define MY_rangeDec  _ppmd.rc.dec
50 
51 #define CHECK_EXTRA_ERROR \
52     if (_inStream.Extra) { \
53       _status = kStatus_Error; \
54       return (_res = (_inStream.Res != SZ_OK ? _inStream.Res: S_FALSE)); }
55 
56 
CodeSpec(Byte *memStream, UInt32 size)57 HRESULT CDecoder::CodeSpec(Byte *memStream, UInt32 size)
58 {
59   if (_res != S_OK)
60     return _res;
61 
62   switch (_status)
63   {
64     case kStatus_Finished_With_Mark: return S_OK;
65     case kStatus_Error: return S_FALSE;
66     case kStatus_NeedInit:
67       _inStream.Init();
68       if (!Ppmd7z_RangeDec_Init(&MY_rangeDec))
69       {
70         _status = kStatus_Error;
71         return (_res = S_FALSE);
72       }
73       CHECK_EXTRA_ERROR
74       _status = kStatus_Normal;
75       Ppmd7_Init(&_ppmd, _order);
76       break;
77   }
78 
79   if (_outSizeDefined)
80   {
81     const UInt64 rem = _outSize - _processedSize;
82     if (size > rem)
83       size = (UInt32)rem;
84   }
85 
86   int sym = 0;
87   {
88     Byte *buf = memStream;
89     const Byte *lim = buf + size;
90     for (; buf != lim; buf++)
91     {
92       sym = Ppmd7z_DecodeSymbol(&_ppmd);
93       if (_inStream.Extra || sym < 0)
94         break;
95       *buf = (Byte)sym;
96     }
97     /*
98     buf = Ppmd7z_DecodeSymbols(&_ppmd, buf, lim);
99     sym = _ppmd.LastSymbol;
100     */
101     _processedSize += (size_t)(buf - memStream);
102   }
103 
104   CHECK_EXTRA_ERROR
105 
106   if (sym >= 0)
107   {
108     if (!FinishStream
109         || !_outSizeDefined
110         || _outSize != _processedSize
111         || MY_rangeDec.Code == 0)
112       return S_OK;
113     /*
114     // We can decode additional End Marker here:
115     sym = Ppmd7z_DecodeSymbol(&_ppmd);
116     CHECK_EXTRA_ERROR
117     */
118   }
119 
120   if (sym != PPMD7_SYM_END || MY_rangeDec.Code != 0)
121   {
122     _status = kStatus_Error;
123     return (_res = S_FALSE);
124   }
125 
126   _status = kStatus_Finished_With_Mark;
127   return S_OK;
128 }
129 
130 
131 
Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)132 Z7_COM7F_IMF(CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
133     const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress))
134 {
135   if (!_outBuf)
136   {
137     _outBuf = (Byte *)::MidAlloc(kBufSize);
138     if (!_outBuf)
139       return E_OUTOFMEMORY;
140   }
141 
142   _inStream.Stream = inStream;
143   SetOutStreamSize(outSize);
144 
145   do
146   {
147     const UInt64 startPos = _processedSize;
148     const HRESULT res = CodeSpec(_outBuf, kBufSize);
149     const size_t processed = (size_t)(_processedSize - startPos);
150     RINOK(WriteStream(outStream, _outBuf, processed))
151     RINOK(res)
152     if (_status == kStatus_Finished_With_Mark)
153       break;
154     if (progress)
155     {
156       const UInt64 inProcessed = _inStream.GetProcessed();
157       RINOK(progress->SetRatioInfo(&inProcessed, &_processedSize))
158     }
159   }
160   while (!_outSizeDefined || _processedSize < _outSize);
161 
162   if (FinishStream && inSize && *inSize != _inStream.GetProcessed())
163     return S_FALSE;
164 
165   return S_OK;
166 }
167 
168 
SetOutStreamSize(const UInt64 *outSize)169 Z7_COM7F_IMF(CDecoder::SetOutStreamSize(const UInt64 *outSize))
170 {
171   _outSizeDefined = (outSize != NULL);
172   if (_outSizeDefined)
173     _outSize = *outSize;
174   _processedSize = 0;
175   _status = kStatus_NeedInit;
176   _res = SZ_OK;
177   return S_OK;
178 }
179 
SetFinishMode(UInt32 finishMode)180 Z7_COM7F_IMF(CDecoder::SetFinishMode(UInt32 finishMode))
181 {
182   FinishStream = (finishMode != 0);
183   return S_OK;
184 }
185 
GetInStreamProcessedSize(UInt64 *value)186 Z7_COM7F_IMF(CDecoder::GetInStreamProcessedSize(UInt64 *value))
187 {
188   *value = _inStream.GetProcessed();
189   return S_OK;
190 }
191 
192 #ifndef Z7_NO_READ_FROM_CODER
193 
SetInStream(ISequentialInStream *inStream)194 Z7_COM7F_IMF(CDecoder::SetInStream(ISequentialInStream *inStream))
195 {
196   InSeqStream = inStream;
197   _inStream.Stream = inStream;
198   return S_OK;
199 }
200 
ReleaseInStream()201 Z7_COM7F_IMF(CDecoder::ReleaseInStream())
202 {
203   InSeqStream.Release();
204   return S_OK;
205 }
206 
Read(void *data, UInt32 size, UInt32 *processedSize)207 Z7_COM7F_IMF(CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize))
208 {
209   const UInt64 startPos = _processedSize;
210   const HRESULT res = CodeSpec((Byte *)data, size);
211   if (processedSize)
212     *processedSize = (UInt32)(_processedSize - startPos);
213   return res;
214 }
215 
216 #endif
217 
218 }}
219