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():
28 _inBuf(NULL),
29 _lzmaStatus(LZMA_STATUS_NOT_SPECIFIED),
30 FinishStream(false),
31 _propsWereSet(false),
32 _outSizeDefined(false),
33 _outStep(1 << 20),
34 _inBufSize(0),
35 _inBufSizeNew(1 << 20)
36 {
37 _inProcessed = 0;
38 _inPos = _inLim = 0;
39
40 /*
41 AlignOffsetAlloc_CreateVTable(&_alloc);
42 _alloc.numAlignBits = 7;
43 _alloc.offset = 0;
44 */
45 LzmaDec_Construct(&_state);
46 }
47
~CDecoder()48 CDecoder::~CDecoder()
49 {
50 LzmaDec_Free(&_state, &g_AlignedAlloc); // &_alloc.vt
51 MyFree(_inBuf);
52 }
53
SetInBufSize(UInt32,UInt32 size)54 STDMETHODIMP CDecoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSizeNew = size; return S_OK; }
SetOutBufSize(UInt32,UInt32 size)55 STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outStep = size; return S_OK; }
56
CreateInputBuffer()57 HRESULT CDecoder::CreateInputBuffer()
58 {
59 if (!_inBuf || _inBufSizeNew != _inBufSize)
60 {
61 MyFree(_inBuf);
62 _inBufSize = 0;
63 _inBuf = (Byte *)MyAlloc(_inBufSizeNew);
64 if (!_inBuf)
65 return E_OUTOFMEMORY;
66 _inBufSize = _inBufSizeNew;
67 }
68 return S_OK;
69 }
70
71
SetDecoderProperties2(const Byte * prop,UInt32 size)72 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size)
73 {
74 RINOK(SResToHRESULT(LzmaDec_Allocate(&_state, prop, size, &g_AlignedAlloc))) // &_alloc.vt
75 _propsWereSet = true;
76 return CreateInputBuffer();
77 }
78
79
SetOutStreamSizeResume(const UInt64 * outSize)80 void CDecoder::SetOutStreamSizeResume(const UInt64 *outSize)
81 {
82 _outSizeDefined = (outSize != NULL);
83 _outSize = 0;
84 if (_outSizeDefined)
85 _outSize = *outSize;
86 _outProcessed = 0;
87 _lzmaStatus = LZMA_STATUS_NOT_SPECIFIED;
88
89 LzmaDec_Init(&_state);
90 }
91
92
SetOutStreamSize(const UInt64 * outSize)93 STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize)
94 {
95 _inProcessed = 0;
96 _inPos = _inLim = 0;
97 SetOutStreamSizeResume(outSize);
98 return S_OK;
99 }
100
101
SetFinishMode(UInt32 finishMode)102 STDMETHODIMP CDecoder::SetFinishMode(UInt32 finishMode)
103 {
104 FinishStream = (finishMode != 0);
105 return S_OK;
106 }
107
108
GetInStreamProcessedSize(UInt64 * value)109 STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value)
110 {
111 *value = _inProcessed;
112 return S_OK;
113 }
114
115
CodeSpec(ISequentialInStream * inStream,ISequentialOutStream * outStream,ICompressProgressInfo * progress)116 HRESULT CDecoder::CodeSpec(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress)
117 {
118 if (!_inBuf || !_propsWereSet)
119 return S_FALSE;
120
121 const UInt64 startInProgress = _inProcessed;
122 SizeT wrPos = _state.dicPos;
123 HRESULT readRes = S_OK;
124
125 for (;;)
126 {
127 if (_inPos == _inLim && readRes == S_OK)
128 {
129 _inPos = _inLim = 0;
130 readRes = inStream->Read(_inBuf, _inBufSize, &_inLim);
131 }
132
133 const SizeT dicPos = _state.dicPos;
134 SizeT size;
135 {
136 SizeT next = _state.dicBufSize;
137 if (next - wrPos > _outStep)
138 next = wrPos + _outStep;
139 size = next - dicPos;
140 }
141
142 ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
143 if (_outSizeDefined)
144 {
145 const UInt64 rem = _outSize - _outProcessed;
146 if (size >= rem)
147 {
148 size = (SizeT)rem;
149 if (FinishStream)
150 finishMode = LZMA_FINISH_END;
151 }
152 }
153
154 SizeT inProcessed = _inLim - _inPos;
155 ELzmaStatus status;
156
157 SRes res = LzmaDec_DecodeToDic(&_state, dicPos + size, _inBuf + _inPos, &inProcessed, finishMode, &status);
158
159 _lzmaStatus = status;
160 _inPos += (UInt32)inProcessed;
161 _inProcessed += inProcessed;
162 const SizeT outProcessed = _state.dicPos - dicPos;
163 _outProcessed += outProcessed;
164
165 // we check for LZMA_STATUS_NEEDS_MORE_INPUT to allow RangeCoder initialization, if (_outSizeDefined && _outSize == 0)
166 bool outFinished = (_outSizeDefined && _outProcessed >= _outSize);
167
168 bool needStop = (res != 0
169 || (inProcessed == 0 && outProcessed == 0)
170 || status == LZMA_STATUS_FINISHED_WITH_MARK
171 || (outFinished && status != LZMA_STATUS_NEEDS_MORE_INPUT));
172
173 if (needStop || outProcessed >= size)
174 {
175 HRESULT res2 = WriteStream(outStream, _state.dic + wrPos, _state.dicPos - wrPos);
176
177 if (_state.dicPos == _state.dicBufSize)
178 _state.dicPos = 0;
179 wrPos = _state.dicPos;
180
181 RINOK(res2);
182
183 if (needStop)
184 {
185 if (res != 0)
186 {
187 // return SResToHRESULT(res);
188 return S_FALSE;
189 }
190
191 if (status == LZMA_STATUS_FINISHED_WITH_MARK)
192 {
193 if (FinishStream)
194 if (_outSizeDefined && _outSize != _outProcessed)
195 return S_FALSE;
196 return readRes;
197 }
198
199 if (outFinished && status != LZMA_STATUS_NEEDS_MORE_INPUT)
200 if (!FinishStream || status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
201 return readRes;
202
203 return S_FALSE;
204 }
205 }
206
207 if (progress)
208 {
209 const UInt64 inSize = _inProcessed - startInProgress;
210 RINOK(progress->SetRatioInfo(&inSize, &_outProcessed));
211 }
212 }
213 }
214
215
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 * inSize,const UInt64 * outSize,ICompressProgressInfo * progress)216 STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
217 const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)
218 {
219 if (!_inBuf)
220 return E_INVALIDARG;
221 SetOutStreamSize(outSize);
222 HRESULT res = CodeSpec(inStream, outStream, progress);
223 if (res == S_OK)
224 if (FinishStream && inSize && *inSize != _inProcessed)
225 res = S_FALSE;
226 return res;
227 }
228
229
230 #ifndef NO_READ_FROM_CODER
231
SetInStream(ISequentialInStream * inStream)232 STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; }
ReleaseInStream()233 STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; }
234
235
Read(void * data,UInt32 size,UInt32 * processedSize)236 STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)
237 {
238 if (processedSize)
239 *processedSize = 0;
240
241 ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
242 if (_outSizeDefined)
243 {
244 const UInt64 rem = _outSize - _outProcessed;
245 if (size >= rem)
246 {
247 size = (UInt32)rem;
248 if (FinishStream)
249 finishMode = LZMA_FINISH_END;
250 }
251 }
252
253 HRESULT readRes = S_OK;
254
255 for (;;)
256 {
257 if (_inPos == _inLim && readRes == S_OK)
258 {
259 _inPos = _inLim = 0;
260 readRes = _inStream->Read(_inBuf, _inBufSize, &_inLim);
261 }
262
263 SizeT inProcessed = _inLim - _inPos;
264 SizeT outProcessed = size;
265 ELzmaStatus status;
266
267 SRes res = LzmaDec_DecodeToBuf(&_state, (Byte *)data, &outProcessed,
268 _inBuf + _inPos, &inProcessed, finishMode, &status);
269
270 _lzmaStatus = status;
271 _inPos += (UInt32)inProcessed;
272 _inProcessed += inProcessed;
273 _outProcessed += outProcessed;
274 size -= (UInt32)outProcessed;
275 data = (Byte *)data + outProcessed;
276 if (processedSize)
277 *processedSize += (UInt32)outProcessed;
278
279 if (res != 0)
280 return S_FALSE;
281
282 /*
283 if (status == LZMA_STATUS_FINISHED_WITH_MARK)
284 return readRes;
285
286 if (size == 0 && status != LZMA_STATUS_NEEDS_MORE_INPUT)
287 {
288 if (FinishStream
289 && _outSizeDefined && _outProcessed >= _outSize
290 && status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
291 return S_FALSE;
292 return readRes;
293 }
294 */
295
296 if (inProcessed == 0 && outProcessed == 0)
297 return readRes;
298 }
299 }
300
301
CodeResume(ISequentialOutStream * outStream,const UInt64 * outSize,ICompressProgressInfo * progress)302 HRESULT CDecoder::CodeResume(ISequentialOutStream *outStream, const UInt64 *outSize, ICompressProgressInfo *progress)
303 {
304 SetOutStreamSizeResume(outSize);
305 return CodeSpec(_inStream, outStream, progress);
306 }
307
308
ReadFromInputStream(void * data,UInt32 size,UInt32 * processedSize)309 HRESULT CDecoder::ReadFromInputStream(void *data, UInt32 size, UInt32 *processedSize)
310 {
311 RINOK(CreateInputBuffer());
312
313 if (processedSize)
314 *processedSize = 0;
315
316 HRESULT readRes = S_OK;
317
318 while (size != 0)
319 {
320 if (_inPos == _inLim)
321 {
322 _inPos = _inLim = 0;
323 if (readRes == S_OK)
324 readRes = _inStream->Read(_inBuf, _inBufSize, &_inLim);
325 if (_inLim == 0)
326 break;
327 }
328
329 UInt32 cur = _inLim - _inPos;
330 if (cur > size)
331 cur = size;
332 memcpy(data, _inBuf + _inPos, cur);
333 _inPos += cur;
334 _inProcessed += cur;
335 size -= cur;
336 data = (Byte *)data + cur;
337 if (processedSize)
338 *processedSize += cur;
339 }
340
341 return readRes;
342 }
343
344 #endif
345
346 }}
347