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
12namespace NCompress {
13namespace NPpmd {
14
15static const UInt32 kBufSize = (1 << 16);
16
17enum
18{
19  kStatus_NeedInit,
20  kStatus_Normal,
21  kStatus_Finished_With_Mark,
22  kStatus_Error
23};
24
25CDecoder::~CDecoder()
26{
27  ::MidFree(_outBuf);
28  Ppmd7_Free(&_ppmd, &g_BigAlloc);
29}
30
31Z7_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
57HRESULT 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
132Z7_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
169Z7_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
180Z7_COM7F_IMF(CDecoder::SetFinishMode(UInt32 finishMode))
181{
182  FinishStream = (finishMode != 0);
183  return S_OK;
184}
185
186Z7_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
194Z7_COM7F_IMF(CDecoder::SetInStream(ISequentialInStream *inStream))
195{
196  InSeqStream = inStream;
197  _inStream.Stream = inStream;
198  return S_OK;
199}
200
201Z7_COM7F_IMF(CDecoder::ReleaseInStream())
202{
203  InSeqStream.Release();
204  return S_OK;
205}
206
207Z7_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