1// PpmdDecoder.cpp
2// 2009-03-11 : Igor Pavlov : Public domain
3
4#include "StdAfx.h"
5
6#include "../../../C/Alloc.h"
7#include "../../../C/CpuArch.h"
8
9#include "../Common/StreamUtils.h"
10
11#include "PpmdDecoder.h"
12
13namespace NCompress {
14namespace NPpmd {
15
16static const UInt32 kBufSize = (1 << 20);
17
18enum
19{
20  kStatus_NeedInit,
21  kStatus_Normal,
22  kStatus_Finished,
23  kStatus_Error
24};
25
26CDecoder::~CDecoder()
27{
28  ::MidFree(_outBuf);
29  Ppmd7_Free(&_ppmd, &g_BigAlloc);
30}
31
32STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *props, UInt32 size)
33{
34  if (size < 5)
35    return E_INVALIDARG;
36  _order = props[0];
37  UInt32 memSize = GetUi32(props + 1);
38  if (_order < PPMD7_MIN_ORDER ||
39      _order > PPMD7_MAX_ORDER ||
40      memSize < PPMD7_MIN_MEM_SIZE ||
41      memSize > PPMD7_MAX_MEM_SIZE)
42    return E_NOTIMPL;
43  if (!_inStream.Alloc(1 << 20))
44    return E_OUTOFMEMORY;
45  if (!Ppmd7_Alloc(&_ppmd, memSize, &g_BigAlloc))
46    return E_OUTOFMEMORY;
47  return S_OK;
48}
49
50HRESULT CDecoder::CodeSpec(Byte *memStream, UInt32 size)
51{
52  switch (_status)
53  {
54    case kStatus_Finished: return S_OK;
55    case kStatus_Error: return S_FALSE;
56    case kStatus_NeedInit:
57      _inStream.Init();
58      if (!Ppmd7z_RangeDec_Init(&_rangeDec))
59      {
60        _status = kStatus_Error;
61        return S_FALSE;
62      }
63      _status = kStatus_Normal;
64      Ppmd7_Init(&_ppmd, _order);
65      break;
66  }
67  if (_outSizeDefined)
68  {
69    const UInt64 rem = _outSize - _processedSize;
70    if (size > rem)
71      size = (UInt32)rem;
72  }
73
74  UInt32 i;
75  int sym = 0;
76  for (i = 0; i != size; i++)
77  {
78    sym = Ppmd7_DecodeSymbol(&_ppmd, &_rangeDec.p);
79    if (_inStream.Extra || sym < 0)
80      break;
81    memStream[i] = (Byte)sym;
82  }
83
84  _processedSize += i;
85  if (_inStream.Extra)
86  {
87    _status = kStatus_Error;
88    return _inStream.Res;
89  }
90  if (sym < 0)
91    _status = (sym < -1) ? kStatus_Error : kStatus_Finished;
92  return S_OK;
93}
94
95STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
96    const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
97{
98  if (!_outBuf)
99  {
100    _outBuf = (Byte *)::MidAlloc(kBufSize);
101    if (!_outBuf)
102      return E_OUTOFMEMORY;
103  }
104
105  _inStream.Stream = inStream;
106  SetOutStreamSize(outSize);
107
108  do
109  {
110    const UInt64 startPos = _processedSize;
111    HRESULT res = CodeSpec(_outBuf, kBufSize);
112    size_t processed = (size_t)(_processedSize - startPos);
113    RINOK(WriteStream(outStream, _outBuf, processed));
114    RINOK(res);
115    if (_status == kStatus_Finished)
116      break;
117    if (progress)
118    {
119      UInt64 inSize = _inStream.GetProcessed();
120      RINOK(progress->SetRatioInfo(&inSize, &_processedSize));
121    }
122  }
123  while (!_outSizeDefined || _processedSize < _outSize);
124  return S_OK;
125}
126
127STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize)
128{
129  _outSizeDefined = (outSize != NULL);
130  if (_outSizeDefined)
131    _outSize = *outSize;
132  _processedSize = 0;
133  _status = kStatus_NeedInit;
134  return S_OK;
135}
136
137#ifndef NO_READ_FROM_CODER
138
139STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream)
140{
141  InSeqStream = inStream;
142  _inStream.Stream = inStream;
143  return S_OK;
144}
145
146STDMETHODIMP CDecoder::ReleaseInStream()
147{
148  InSeqStream.Release();
149  return S_OK;
150}
151
152STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)
153{
154  const UInt64 startPos = _processedSize;
155  HRESULT res = CodeSpec((Byte *)data, size);
156  if (processedSize)
157    *processedSize = (UInt32)(_processedSize - startPos);
158  return res;
159}
160
161#endif
162
163}}
164