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