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 
13 namespace NCompress {
14 namespace NPpmd {
15 
16 static const UInt32 kBufSize = (1 << 20);
17 
18 enum
19 {
20   kStatus_NeedInit,
21   kStatus_Normal,
22   kStatus_Finished,
23   kStatus_Error
24 };
25 
~CDecoder()26 CDecoder::~CDecoder()
27 {
28   ::MidFree(_outBuf);
29   Ppmd7_Free(&_ppmd, &g_BigAlloc);
30 }
31 
SetDecoderProperties2(const Byte * props,UInt32 size)32 STDMETHODIMP 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 
CodeSpec(Byte * memStream,UInt32 size)50 HRESULT 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.vt);
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 
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 *,const UInt64 * outSize,ICompressProgressInfo * progress)95 STDMETHODIMP 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 
SetOutStreamSize(const UInt64 * outSize)127 STDMETHODIMP 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 
GetInStreamProcessedSize(UInt64 * value)138 STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value)
139 {
140   *value = _inStream.GetProcessed();
141   return S_OK;
142 }
143 
144 #ifndef NO_READ_FROM_CODER
145 
SetInStream(ISequentialInStream * inStream)146 STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream)
147 {
148   InSeqStream = inStream;
149   _inStream.Stream = inStream;
150   return S_OK;
151 }
152 
ReleaseInStream()153 STDMETHODIMP CDecoder::ReleaseInStream()
154 {
155   InSeqStream.Release();
156   return S_OK;
157 }
158 
Read(void * data,UInt32 size,UInt32 * processedSize)159 STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)
160 {
161   const UInt64 startPos = _processedSize;
162   HRESULT res = CodeSpec((Byte *)data, size);
163   if (processedSize)
164     *processedSize = (UInt32)(_processedSize - startPos);
165   return res;
166 }
167 
168 #endif
169 
170 }}
171