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