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