1 // Lzma2Decoder.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/Alloc.h"
6 
7 #include "../Common/StreamUtils.h"
8 
9 #include "Lzma2Decoder.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_PROGRESS: return E_ABORT;
19     case SZ_ERROR_DATA: return S_FALSE;
20   }
21   return E_FAIL;
22 }
23 
24 namespace NCompress {
25 namespace NLzma2 {
26 
CDecoder()27 CDecoder::CDecoder():
28     _inBuf(NULL),
29     _inBufSize(0),
30     _inBufSizeNew(1 << 20),
31     _outStepSize(1 << 22),
32     _outSizeDefined(false),
33     _finishMode(false)
34 {
35   Lzma2Dec_Construct(&_state);
36 }
37 
SetInBufSize(UInt32,UInt32 size)38 STDMETHODIMP CDecoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSizeNew = size; return S_OK; }
SetOutBufSize(UInt32,UInt32 size)39 STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outStepSize = size; return S_OK; }
40 
~CDecoder()41 CDecoder::~CDecoder()
42 {
43   Lzma2Dec_Free(&_state, &g_Alloc);
44   MidFree(_inBuf);
45 }
46 
SetDecoderProperties2(const Byte * prop,UInt32 size)47 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size)
48 {
49   if (size != 1)
50     return E_NOTIMPL;
51 
52   RINOK(SResToHRESULT(Lzma2Dec_Allocate(&_state, prop[0], &g_Alloc)));
53   if (!_inBuf || _inBufSize != _inBufSizeNew)
54   {
55     MidFree(_inBuf);
56     _inBufSize = 0;
57     _inBuf = (Byte *)MidAlloc(_inBufSizeNew);
58     if (!_inBuf)
59       return E_OUTOFMEMORY;
60     _inBufSize = _inBufSizeNew;
61   }
62 
63   return S_OK;
64 }
65 
GetInStreamProcessedSize(UInt64 * value)66 STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value) { *value = _inSizeProcessed; return S_OK; }
SetInStream(ISequentialInStream * inStream)67 STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; }
ReleaseInStream()68 STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; }
69 
SetOutStreamSize(const UInt64 * outSize)70 STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize)
71 {
72   _outSizeDefined = (outSize != NULL);
73   _outSize = 0;
74   if (_outSizeDefined)
75     _outSize = *outSize;
76 
77   Lzma2Dec_Init(&_state);
78 
79   _inPos = _inSize = 0;
80   _inSizeProcessed = _outSizeProcessed = 0;
81   return S_OK;
82 }
83 
SetFinishMode(UInt32 finishMode)84 STDMETHODIMP CDecoder::SetFinishMode(UInt32 finishMode)
85 {
86   _finishMode = (finishMode != 0);
87   return S_OK;
88 }
89 
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 * inSize,const UInt64 * outSize,ICompressProgressInfo * progress)90 STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream,
91     ISequentialOutStream *outStream, const UInt64 *inSize,
92     const UInt64 *outSize, ICompressProgressInfo *progress)
93 {
94   if (!_inBuf)
95     return S_FALSE;
96   SetOutStreamSize(outSize);
97 
98   UInt32 step = _outStepSize;
99   const UInt32 kOutStepSize_Min = 1 << 12;
100   if (step < kOutStepSize_Min)
101     step = kOutStepSize_Min;
102 
103   SizeT wrPos = _state.decoder.dicPos;
104 
105   SizeT next = (_state.decoder.dicBufSize - _state.decoder.dicPos < step) ?
106       _state.decoder.dicBufSize :
107       _state.decoder.dicPos + step;
108 
109   HRESULT hres = S_OK;
110 
111   for (;;)
112   {
113     if (_inPos == _inSize)
114     {
115       _inPos = _inSize = 0;
116       hres = inStream->Read(_inBuf, _inBufSize, &_inSize);
117       if (hres != S_OK)
118         break;
119     }
120 
121     SizeT dicPos = _state.decoder.dicPos;
122     SizeT curSize = next - dicPos;
123 
124     ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
125     if (_outSizeDefined)
126     {
127       const UInt64 rem = _outSize - _outSizeProcessed;
128       if (curSize >= rem)
129       {
130         curSize = (SizeT)rem;
131         if (_finishMode)
132           finishMode = LZMA_FINISH_END;
133       }
134     }
135 
136     SizeT inSizeProcessed = _inSize - _inPos;
137     ELzmaStatus status;
138     SRes res = Lzma2Dec_DecodeToDic(&_state, dicPos + curSize, _inBuf + _inPos, &inSizeProcessed, finishMode, &status);
139 
140     _inPos += (UInt32)inSizeProcessed;
141     _inSizeProcessed += inSizeProcessed;
142     SizeT outSizeProcessed = _state.decoder.dicPos - dicPos;
143     _outSizeProcessed += outSizeProcessed;
144 
145     bool finished = (inSizeProcessed == 0 && outSizeProcessed == 0
146         || status == LZMA_STATUS_FINISHED_WITH_MARK);
147     bool outFinished = (_outSizeDefined && _outSizeProcessed >= _outSize);
148 
149     if (res != 0
150         || _state.decoder.dicPos >= next
151         || finished
152         || outFinished)
153     {
154       HRESULT res2 = WriteStream(outStream, _state.decoder.dic + wrPos, _state.decoder.dicPos - wrPos);
155 
156       if (_state.decoder.dicPos == _state.decoder.dicBufSize)
157         _state.decoder.dicPos = 0;
158 
159       wrPos = _state.decoder.dicPos;
160 
161       next = (_state.decoder.dicBufSize - _state.decoder.dicPos < step) ?
162           _state.decoder.dicBufSize :
163           _state.decoder.dicPos + step;
164 
165       if (res != 0)
166         return S_FALSE;
167       RINOK(res2);
168 
169       if (finished)
170       {
171         if (status == LZMA_STATUS_FINISHED_WITH_MARK)
172         {
173           if (_finishMode && inSize && *inSize != _inSizeProcessed)
174             return S_FALSE;
175           if (finishMode == LZMA_FINISH_END && !outFinished)
176             return S_FALSE;
177           return S_OK;
178         }
179         return (finishMode == LZMA_FINISH_END) ? S_FALSE : S_OK;
180       }
181 
182       if (outFinished && finishMode == LZMA_FINISH_ANY)
183         return S_OK;
184     }
185 
186     if (progress)
187     {
188       RINOK(progress->SetRatioInfo(&_inSizeProcessed, &_outSizeProcessed));
189     }
190   }
191 
192   HRESULT res2 = WriteStream(outStream, _state.decoder.dic + wrPos, _state.decoder.dicPos - wrPos);
193   if (hres != S_OK)
194     return hres;
195   return res2;
196 }
197 
198 #ifndef NO_READ_FROM_CODER
199 
Read(void * data,UInt32 size,UInt32 * processedSize)200 STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)
201 {
202   UInt32 totalProcessed = 0;
203 
204   if (processedSize)
205     *processedSize = 0;
206 
207   for (;;)
208   {
209     if (_inPos == _inSize)
210     {
211       _inPos = _inSize = 0;
212       RINOK(_inStream->Read(_inBuf, _inBufSize, &_inSize));
213     }
214     {
215       ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
216       if (_outSizeDefined)
217       {
218         const UInt64 rem = _outSize - _outSizeProcessed;
219         if (rem <= size)
220         {
221           size = (UInt32)rem;
222           if (_finishMode)
223             finishMode = LZMA_FINISH_END;
224         }
225       }
226 
227       SizeT outProcessed = size;
228       SizeT inProcessed = _inSize - _inPos;
229 
230       ELzmaStatus status;
231       SRes res = Lzma2Dec_DecodeToBuf(&_state, (Byte *)data, &outProcessed,
232           _inBuf + _inPos, &inProcessed, finishMode, &status);
233 
234       _inPos += (UInt32)inProcessed;
235       _inSizeProcessed += inProcessed;
236       _outSizeProcessed += outProcessed;
237       size -= (UInt32)outProcessed;
238       data = (Byte *)data + outProcessed;
239 
240       totalProcessed += (UInt32)outProcessed;
241       if (processedSize)
242         *processedSize = totalProcessed;
243 
244       if (res != SZ_OK)
245       {
246         if (totalProcessed != 0)
247           return S_OK;
248         return SResToHRESULT(res);
249       }
250 
251       if (inProcessed == 0 && outProcessed == 0)
252         return S_OK;
253       if (status == LZMA_STATUS_FINISHED_WITH_MARK)
254         return S_OK;
255       if (outProcessed != 0)
256       {
257         if (finishMode != LZMA_FINISH_END || _outSize != _outSizeProcessed)
258           return S_OK;
259       }
260     }
261   }
262 }
263 
264 #endif
265 
266 }}
267