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