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