• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // LzmaDecoder.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/Alloc.h"
6 
7 #include "../Common/StreamUtils.h"
8 
9 #include "LzmaDecoder.h"
10 
SResToHRESULT(SRes res)11 static HRESULT SResToHRESULT(SRes res)
12 {
13   switch (res)
14   {
15     case SZ_OK: return S_OK;
16     case SZ_ERROR_MEM: return E_OUTOFMEMORY;
17     case SZ_ERROR_PARAM: return E_INVALIDARG;
18     case SZ_ERROR_UNSUPPORTED: return E_NOTIMPL;
19     case SZ_ERROR_DATA: return S_FALSE;
20   }
21   return E_FAIL;
22 }
23 
24 namespace NCompress {
25 namespace NLzma {
26 
CDecoder()27 CDecoder::CDecoder():
28     _inBuf(NULL),
29     _lzmaStatus(LZMA_STATUS_NOT_SPECIFIED),
30     FinishStream(false),
31     _propsWereSet(false),
32     _outSizeDefined(false),
33     _outStep(1 << 20),
34     _inBufSize(0),
35     _inBufSizeNew(1 << 20)
36 {
37   _inProcessed = 0;
38   _inPos = _inLim = 0;
39 
40   /*
41   AlignOffsetAlloc_CreateVTable(&_alloc);
42   _alloc.numAlignBits = 7;
43   _alloc.offset = 0;
44   */
45   LzmaDec_Construct(&_state);
46 }
47 
~CDecoder()48 CDecoder::~CDecoder()
49 {
50   LzmaDec_Free(&_state, &g_AlignedAlloc); // &_alloc.vt
51   MyFree(_inBuf);
52 }
53 
SetInBufSize(UInt32,UInt32 size)54 STDMETHODIMP CDecoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSizeNew = size; return S_OK; }
SetOutBufSize(UInt32,UInt32 size)55 STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outStep = size; return S_OK; }
56 
CreateInputBuffer()57 HRESULT CDecoder::CreateInputBuffer()
58 {
59   if (!_inBuf || _inBufSizeNew != _inBufSize)
60   {
61     MyFree(_inBuf);
62     _inBufSize = 0;
63     _inBuf = (Byte *)MyAlloc(_inBufSizeNew);
64     if (!_inBuf)
65       return E_OUTOFMEMORY;
66     _inBufSize = _inBufSizeNew;
67   }
68   return S_OK;
69 }
70 
71 
SetDecoderProperties2(const Byte * prop,UInt32 size)72 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size)
73 {
74   RINOK(SResToHRESULT(LzmaDec_Allocate(&_state, prop, size, &g_AlignedAlloc))) // &_alloc.vt
75   _propsWereSet = true;
76   return CreateInputBuffer();
77 }
78 
79 
SetOutStreamSizeResume(const UInt64 * outSize)80 void CDecoder::SetOutStreamSizeResume(const UInt64 *outSize)
81 {
82   _outSizeDefined = (outSize != NULL);
83   _outSize = 0;
84   if (_outSizeDefined)
85     _outSize = *outSize;
86   _outProcessed = 0;
87   _lzmaStatus = LZMA_STATUS_NOT_SPECIFIED;
88 
89   LzmaDec_Init(&_state);
90 }
91 
92 
SetOutStreamSize(const UInt64 * outSize)93 STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize)
94 {
95   _inProcessed = 0;
96   _inPos = _inLim = 0;
97   SetOutStreamSizeResume(outSize);
98   return S_OK;
99 }
100 
101 
SetFinishMode(UInt32 finishMode)102 STDMETHODIMP CDecoder::SetFinishMode(UInt32 finishMode)
103 {
104   FinishStream = (finishMode != 0);
105   return S_OK;
106 }
107 
108 
GetInStreamProcessedSize(UInt64 * value)109 STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value)
110 {
111   *value = _inProcessed;
112   return S_OK;
113 }
114 
115 
CodeSpec(ISequentialInStream * inStream,ISequentialOutStream * outStream,ICompressProgressInfo * progress)116 HRESULT CDecoder::CodeSpec(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress)
117 {
118   if (!_inBuf || !_propsWereSet)
119     return S_FALSE;
120 
121   const UInt64 startInProgress = _inProcessed;
122   SizeT wrPos = _state.dicPos;
123   HRESULT readRes = S_OK;
124 
125   for (;;)
126   {
127     if (_inPos == _inLim && readRes == S_OK)
128     {
129       _inPos = _inLim = 0;
130       readRes = inStream->Read(_inBuf, _inBufSize, &_inLim);
131     }
132 
133     const SizeT dicPos = _state.dicPos;
134     SizeT size;
135     {
136       SizeT next = _state.dicBufSize;
137       if (next - wrPos > _outStep)
138         next = wrPos + _outStep;
139       size = next - dicPos;
140     }
141 
142     ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
143     if (_outSizeDefined)
144     {
145       const UInt64 rem = _outSize - _outProcessed;
146       if (size >= rem)
147       {
148         size = (SizeT)rem;
149         if (FinishStream)
150           finishMode = LZMA_FINISH_END;
151       }
152     }
153 
154     SizeT inProcessed = _inLim - _inPos;
155     ELzmaStatus status;
156 
157     SRes res = LzmaDec_DecodeToDic(&_state, dicPos + size, _inBuf + _inPos, &inProcessed, finishMode, &status);
158 
159     _lzmaStatus = status;
160     _inPos += (UInt32)inProcessed;
161     _inProcessed += inProcessed;
162     const SizeT outProcessed = _state.dicPos - dicPos;
163     _outProcessed += outProcessed;
164 
165     // we check for LZMA_STATUS_NEEDS_MORE_INPUT to allow RangeCoder initialization, if (_outSizeDefined && _outSize == 0)
166     bool outFinished = (_outSizeDefined && _outProcessed >= _outSize);
167 
168     bool needStop = (res != 0
169         || (inProcessed == 0 && outProcessed == 0)
170         || status == LZMA_STATUS_FINISHED_WITH_MARK
171         || (outFinished && status != LZMA_STATUS_NEEDS_MORE_INPUT));
172 
173     if (needStop || outProcessed >= size)
174     {
175       HRESULT res2 = WriteStream(outStream, _state.dic + wrPos, _state.dicPos - wrPos);
176 
177       if (_state.dicPos == _state.dicBufSize)
178         _state.dicPos = 0;
179       wrPos = _state.dicPos;
180 
181       RINOK(res2);
182 
183       if (needStop)
184       {
185         if (res != 0)
186         {
187           // return SResToHRESULT(res);
188           return S_FALSE;
189         }
190 
191         if (status == LZMA_STATUS_FINISHED_WITH_MARK)
192         {
193           if (FinishStream)
194             if (_outSizeDefined && _outSize != _outProcessed)
195               return S_FALSE;
196           return readRes;
197         }
198 
199         if (outFinished && status != LZMA_STATUS_NEEDS_MORE_INPUT)
200           if (!FinishStream || status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
201             return readRes;
202 
203         return S_FALSE;
204       }
205     }
206 
207     if (progress)
208     {
209       const UInt64 inSize = _inProcessed - startInProgress;
210       RINOK(progress->SetRatioInfo(&inSize, &_outProcessed));
211     }
212   }
213 }
214 
215 
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 * inSize,const UInt64 * outSize,ICompressProgressInfo * progress)216 STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
217     const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)
218 {
219   if (!_inBuf)
220     return E_INVALIDARG;
221   SetOutStreamSize(outSize);
222   HRESULT res = CodeSpec(inStream, outStream, progress);
223   if (res == S_OK)
224     if (FinishStream && inSize && *inSize != _inProcessed)
225       res = S_FALSE;
226   return res;
227 }
228 
229 
230 #ifndef NO_READ_FROM_CODER
231 
SetInStream(ISequentialInStream * inStream)232 STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; }
ReleaseInStream()233 STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; }
234 
235 
Read(void * data,UInt32 size,UInt32 * processedSize)236 STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)
237 {
238   if (processedSize)
239     *processedSize = 0;
240 
241   ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
242   if (_outSizeDefined)
243   {
244     const UInt64 rem = _outSize - _outProcessed;
245     if (size >= rem)
246     {
247       size = (UInt32)rem;
248       if (FinishStream)
249         finishMode = LZMA_FINISH_END;
250     }
251   }
252 
253   HRESULT readRes = S_OK;
254 
255   for (;;)
256   {
257     if (_inPos == _inLim && readRes == S_OK)
258     {
259       _inPos = _inLim = 0;
260       readRes = _inStream->Read(_inBuf, _inBufSize, &_inLim);
261     }
262 
263     SizeT inProcessed = _inLim - _inPos;
264     SizeT outProcessed = size;
265     ELzmaStatus status;
266 
267     SRes res = LzmaDec_DecodeToBuf(&_state, (Byte *)data, &outProcessed,
268         _inBuf + _inPos, &inProcessed, finishMode, &status);
269 
270     _lzmaStatus = status;
271     _inPos += (UInt32)inProcessed;
272     _inProcessed += inProcessed;
273     _outProcessed += outProcessed;
274     size -= (UInt32)outProcessed;
275     data = (Byte *)data + outProcessed;
276     if (processedSize)
277       *processedSize += (UInt32)outProcessed;
278 
279     if (res != 0)
280       return S_FALSE;
281 
282     /*
283     if (status == LZMA_STATUS_FINISHED_WITH_MARK)
284       return readRes;
285 
286     if (size == 0 && status != LZMA_STATUS_NEEDS_MORE_INPUT)
287     {
288       if (FinishStream
289           && _outSizeDefined && _outProcessed >= _outSize
290           && status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
291         return S_FALSE;
292       return readRes;
293     }
294     */
295 
296     if (inProcessed == 0 && outProcessed == 0)
297       return readRes;
298   }
299 }
300 
301 
CodeResume(ISequentialOutStream * outStream,const UInt64 * outSize,ICompressProgressInfo * progress)302 HRESULT CDecoder::CodeResume(ISequentialOutStream *outStream, const UInt64 *outSize, ICompressProgressInfo *progress)
303 {
304   SetOutStreamSizeResume(outSize);
305   return CodeSpec(_inStream, outStream, progress);
306 }
307 
308 
ReadFromInputStream(void * data,UInt32 size,UInt32 * processedSize)309 HRESULT CDecoder::ReadFromInputStream(void *data, UInt32 size, UInt32 *processedSize)
310 {
311   RINOK(CreateInputBuffer());
312 
313   if (processedSize)
314     *processedSize = 0;
315 
316   HRESULT readRes = S_OK;
317 
318   while (size != 0)
319   {
320     if (_inPos == _inLim)
321     {
322       _inPos = _inLim = 0;
323       if (readRes == S_OK)
324         readRes = _inStream->Read(_inBuf, _inBufSize, &_inLim);
325       if (_inLim == 0)
326         break;
327     }
328 
329     UInt32 cur = _inLim - _inPos;
330     if (cur > size)
331       cur = size;
332     memcpy(data, _inBuf + _inPos, cur);
333     _inPos += cur;
334     _inProcessed += cur;
335     size -= cur;
336     data = (Byte *)data + cur;
337     if (processedSize)
338       *processedSize += cur;
339   }
340 
341   return readRes;
342 }
343 
344 #endif
345 
346 }}
347