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