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