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(): _inBuf(0), _propsWereSet(false), _outSizeDefined(false),
28   _inBufSize(1 << 20),
29   _outBufSize(1 << 22),
30   FinishStream(false),
31   NeedMoreInput(false)
32 {
33   _inSizeProcessed = 0;
34   _inPos = _inSize = 0;
35   LzmaDec_Construct(&_state);
36 }
37 
SzAlloc(void * p,size_t size)38 static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
SzFree(void * p,void * address)39 static void SzFree(void *p, void *address) { p = p; MyFree(address); }
40 static ISzAlloc g_Alloc = { SzAlloc, SzFree };
41 
~CDecoder()42 CDecoder::~CDecoder()
43 {
44   LzmaDec_Free(&_state, &g_Alloc);
45   MyFree(_inBuf);
46 }
47 
SetInBufSize(UInt32,UInt32 size)48 STDMETHODIMP CDecoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSize = size; return S_OK; }
SetOutBufSize(UInt32,UInt32 size)49 STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; }
50 
CreateInputBuffer()51 HRESULT CDecoder::CreateInputBuffer()
52 {
53   if (_inBuf == 0 || _inBufSize != _inBufSizeAllocated)
54   {
55     MyFree(_inBuf);
56     _inBuf = (Byte *)MyAlloc(_inBufSize);
57     if (_inBuf == 0)
58       return E_OUTOFMEMORY;
59     _inBufSizeAllocated = _inBufSize;
60   }
61   return S_OK;
62 }
63 
SetDecoderProperties2(const Byte * prop,UInt32 size)64 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size)
65 {
66   RINOK(SResToHRESULT(LzmaDec_Allocate(&_state, prop, size, &g_Alloc)));
67   _propsWereSet = true;
68   return CreateInputBuffer();
69 }
70 
SetOutStreamSizeResume(const UInt64 * outSize)71 void CDecoder::SetOutStreamSizeResume(const UInt64 *outSize)
72 {
73   _outSizeDefined = (outSize != NULL);
74   if (_outSizeDefined)
75     _outSize = *outSize;
76   _outSizeProcessed = 0;
77   _wrPos = 0;
78   LzmaDec_Init(&_state);
79 }
80 
SetOutStreamSize(const UInt64 * outSize)81 STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize)
82 {
83   _inSizeProcessed = 0;
84   _inPos = _inSize = 0;
85   NeedMoreInput = false;
86   SetOutStreamSizeResume(outSize);
87   return S_OK;
88 }
89 
CodeSpec(ISequentialInStream * inStream,ISequentialOutStream * outStream,ICompressProgressInfo * progress)90 HRESULT CDecoder::CodeSpec(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress)
91 {
92   if (_inBuf == 0 || !_propsWereSet)
93     return S_FALSE;
94 
95   UInt64 startInProgress = _inSizeProcessed;
96 
97   SizeT next = (_state.dicBufSize - _state.dicPos < _outBufSize) ? _state.dicBufSize : (_state.dicPos + _outBufSize);
98   for (;;)
99   {
100     if (_inPos == _inSize)
101     {
102       _inPos = _inSize = 0;
103       RINOK(inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize));
104     }
105 
106     SizeT dicPos = _state.dicPos;
107     SizeT curSize = next - dicPos;
108 
109     ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
110     if (_outSizeDefined)
111     {
112       const UInt64 rem = _outSize - _outSizeProcessed;
113       if (rem <= curSize)
114       {
115         curSize = (SizeT)rem;
116         if (FinishStream)
117           finishMode = LZMA_FINISH_END;
118       }
119     }
120 
121     SizeT inSizeProcessed = _inSize - _inPos;
122     ELzmaStatus status;
123     SRes res = LzmaDec_DecodeToDic(&_state, dicPos + curSize, _inBuf + _inPos, &inSizeProcessed, finishMode, &status);
124 
125     _inPos += (UInt32)inSizeProcessed;
126     _inSizeProcessed += inSizeProcessed;
127     SizeT outSizeProcessed = _state.dicPos - dicPos;
128     _outSizeProcessed += outSizeProcessed;
129 
130     bool finished = (inSizeProcessed == 0 && outSizeProcessed == 0);
131     bool stopDecoding = (_outSizeDefined && _outSizeProcessed >= _outSize);
132 
133     if (res != 0 || _state.dicPos == next || finished || stopDecoding)
134     {
135       HRESULT res2 = WriteStream(outStream, _state.dic + _wrPos, _state.dicPos - _wrPos);
136 
137       _wrPos = _state.dicPos;
138       if (_state.dicPos == _state.dicBufSize)
139       {
140         _state.dicPos = 0;
141         _wrPos = 0;
142       }
143       next = (_state.dicBufSize - _state.dicPos < _outBufSize) ? _state.dicBufSize : (_state.dicPos + _outBufSize);
144 
145       if (res != 0)
146         return S_FALSE;
147       RINOK(res2);
148       if (stopDecoding)
149       {
150         if (status == LZMA_STATUS_NEEDS_MORE_INPUT)
151           NeedMoreInput = true;
152         if (FinishStream &&
153               status != LZMA_STATUS_FINISHED_WITH_MARK &&
154               status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
155           return S_FALSE;
156         return S_OK;
157       }
158       if (finished)
159       {
160         if (status == LZMA_STATUS_NEEDS_MORE_INPUT)
161           NeedMoreInput = true;
162         return (status == LZMA_STATUS_FINISHED_WITH_MARK ? S_OK : S_FALSE);
163       }
164     }
165     if (progress)
166     {
167       UInt64 inSize = _inSizeProcessed - startInProgress;
168       RINOK(progress->SetRatioInfo(&inSize, &_outSizeProcessed));
169     }
170   }
171 }
172 
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 *,const UInt64 * outSize,ICompressProgressInfo * progress)173 STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
174     const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
175 {
176   if (_inBuf == 0)
177     return E_INVALIDARG;
178   SetOutStreamSize(outSize);
179   return CodeSpec(inStream, outStream, progress);
180 }
181 
182 #ifndef NO_READ_FROM_CODER
183 
SetInStream(ISequentialInStream * inStream)184 STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; }
ReleaseInStream()185 STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; }
186 
Read(void * data,UInt32 size,UInt32 * processedSize)187 STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)
188 {
189   if (processedSize)
190     *processedSize = 0;
191   do
192   {
193     if (_inPos == _inSize)
194     {
195       _inPos = _inSize = 0;
196       RINOK(_inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize));
197     }
198     {
199       SizeT inProcessed = _inSize - _inPos;
200 
201       if (_outSizeDefined)
202       {
203         const UInt64 rem = _outSize - _outSizeProcessed;
204         if (rem < size)
205           size = (UInt32)rem;
206       }
207 
208       SizeT outProcessed = size;
209       ELzmaStatus status;
210       SRes res = LzmaDec_DecodeToBuf(&_state, (Byte *)data, &outProcessed,
211           _inBuf + _inPos, &inProcessed, LZMA_FINISH_ANY, &status);
212       _inPos += (UInt32)inProcessed;
213       _inSizeProcessed += inProcessed;
214       _outSizeProcessed += outProcessed;
215       size -= (UInt32)outProcessed;
216       data = (Byte *)data + outProcessed;
217       if (processedSize)
218         *processedSize += (UInt32)outProcessed;
219       RINOK(SResToHRESULT(res));
220       if (inProcessed == 0 && outProcessed == 0)
221         return S_OK;
222     }
223   }
224   while (size != 0);
225   return S_OK;
226 }
227 
CodeResume(ISequentialOutStream * outStream,const UInt64 * outSize,ICompressProgressInfo * progress)228 HRESULT CDecoder::CodeResume(ISequentialOutStream *outStream, const UInt64 *outSize, ICompressProgressInfo *progress)
229 {
230   SetOutStreamSizeResume(outSize);
231   return CodeSpec(_inStream, outStream, progress);
232 }
233 
ReadFromInputStream(void * data,UInt32 size,UInt32 * processedSize)234 HRESULT CDecoder::ReadFromInputStream(void *data, UInt32 size, UInt32 *processedSize)
235 {
236   RINOK(CreateInputBuffer());
237   if (processedSize)
238     *processedSize = 0;
239   while (size > 0)
240   {
241     if (_inPos == _inSize)
242     {
243       _inPos = _inSize = 0;
244       RINOK(_inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize));
245       if (_inSize == 0)
246         break;
247     }
248     {
249       UInt32 curSize = _inSize - _inPos;
250       if (curSize > size)
251         curSize = size;
252       memcpy(data, _inBuf + _inPos, curSize);
253       _inPos += curSize;
254       _inSizeProcessed += curSize;
255       size -= curSize;
256       data = (Byte *)data + curSize;
257       if (processedSize)
258         *processedSize += curSize;
259     }
260   }
261   return S_OK;
262 }
263 
264 #endif
265 
266 }}
267