1 // XzDecoder.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../C/Alloc.h"
6
7 #include "../Common/StreamUtils.h"
8
9 #include "../Archive/IArchive.h"
10
11 #include "XzDecoder.h"
12
13 using namespace NArchive;
14
15 namespace NCompress {
16 namespace NXz {
17
18
CXzUnpackerCPP()19 CXzUnpackerCPP::CXzUnpackerCPP(): InBuf(NULL), OutBuf(NULL)
20 {
21 XzUnpacker_Construct(&p, &g_Alloc);
22 }
23
~CXzUnpackerCPP()24 CXzUnpackerCPP::~CXzUnpackerCPP()
25 {
26 XzUnpacker_Free(&p);
27 MidFree(InBuf);
28 MidFree(OutBuf);
29 }
30
31
Clear()32 void CStatInfo::Clear()
33 {
34 InSize = 0;
35 OutSize = 0;
36 PhySize = 0;
37
38 NumStreams = 0;
39 NumBlocks = 0;
40
41 UnpackSize_Defined = false;
42
43 NumStreams_Defined = false;
44 NumBlocks_Defined = false;
45
46 IsArc = false;
47 UnexpectedEnd = false;
48 DataAfterEnd = false;
49 Unsupported = false;
50 HeadersError = false;
51 DataError = false;
52 CrcError = false;
53 }
54
55
Decode(ISequentialInStream * seqInStream,ISequentialOutStream * outStream,const UInt64 * outSizeLimit,bool finishStream,ICompressProgressInfo * progress)56 HRESULT CDecoder::Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream,
57 const UInt64 *outSizeLimit, bool finishStream, ICompressProgressInfo *progress)
58 {
59 const size_t kInBufSize = (size_t)1 << 20;
60 const size_t kOutBufSize = (size_t)1 << 21;
61
62 Clear();
63 DecodeRes = SZ_OK;
64
65 XzUnpacker_Init(&xzu.p);
66
67 if (!xzu.InBuf)
68 {
69 xzu.InBuf = (Byte *)MidAlloc(kInBufSize);
70 if (!xzu.InBuf)
71 return E_OUTOFMEMORY;
72 }
73 if (!xzu.OutBuf)
74 {
75 xzu.OutBuf = (Byte *)MidAlloc(kOutBufSize);
76 if (!xzu.OutBuf)
77 return E_OUTOFMEMORY;
78 }
79
80 UInt32 inSize = 0;
81 UInt32 inPos = 0;
82 SizeT outPos = 0;
83
84 HRESULT readRes = S_OK;
85
86 for (;;)
87 {
88 if (inPos == inSize && readRes == S_OK)
89 {
90 inPos = inSize = 0;
91 readRes = seqInStream->Read(xzu.InBuf, kInBufSize, &inSize);
92 }
93
94 SizeT inLen = inSize - inPos;
95 SizeT outLen = kOutBufSize - outPos;
96 ECoderFinishMode finishMode = CODER_FINISH_ANY;
97
98 /*
99 // 17.01 : the code was disabled:
100 if (inSize == 0)
101 finishMode = CODER_FINISH_END;
102 */
103
104 if (outSizeLimit)
105 {
106 const UInt64 rem = *outSizeLimit - OutSize;
107 if (outLen >= rem)
108 {
109 outLen = (SizeT)rem;
110 if (finishStream)
111 finishMode = CODER_FINISH_END;
112 }
113 }
114
115 ECoderStatus status;
116
117 const SizeT outLenRequested = outLen;
118
119 SRes res = XzUnpacker_Code(&xzu.p,
120 xzu.OutBuf + outPos, &outLen,
121 xzu.InBuf + inPos, &inLen,
122 finishMode, &status);
123
124 DecodeRes = res;
125
126 inPos += (UInt32)inLen;
127 outPos += outLen;
128
129 InSize += inLen;
130 OutSize += outLen;
131
132 bool finished = ((inLen == 0 && outLen == 0) || res != SZ_OK);
133
134 if (outLen >= outLenRequested || finished)
135 {
136 if (outStream && outPos != 0)
137 {
138 RINOK(WriteStream(outStream, xzu.OutBuf, outPos));
139 }
140 outPos = 0;
141 }
142
143 if (progress)
144 {
145 RINOK(progress->SetRatioInfo(&InSize, &OutSize));
146 }
147
148 if (!finished)
149 continue;
150
151 {
152 PhySize = InSize;
153 NumStreams = xzu.p.numStartedStreams;
154 if (NumStreams > 0)
155 IsArc = true;
156 NumBlocks = xzu.p.numTotalBlocks;
157
158 UnpackSize_Defined = true;
159 NumStreams_Defined = true;
160 NumBlocks_Defined = true;
161
162 UInt64 extraSize = XzUnpacker_GetExtraSize(&xzu.p);
163
164 if (res == SZ_OK)
165 {
166 if (status == CODER_STATUS_NEEDS_MORE_INPUT)
167 {
168 extraSize = 0;
169 if (!XzUnpacker_IsStreamWasFinished(&xzu.p))
170 {
171 // finished at padding bytes, but padding is not aligned for 4
172 UnexpectedEnd = true;
173 res = SZ_ERROR_DATA;
174 }
175 }
176 else // status == CODER_STATUS_NOT_FINISHED
177 res = SZ_ERROR_DATA;
178 }
179 else if (res == SZ_ERROR_NO_ARCHIVE)
180 {
181 if (InSize == extraSize)
182 IsArc = false;
183 else
184 {
185 if (extraSize != 0 || inPos != inSize)
186 {
187 DataAfterEnd = true;
188 res = SZ_OK;
189 }
190 }
191 }
192
193 DecodeRes = res;
194 PhySize -= extraSize;
195
196 switch (res)
197 {
198 case SZ_OK: break;
199 case SZ_ERROR_NO_ARCHIVE: IsArc = false; break;
200 case SZ_ERROR_ARCHIVE: HeadersError = true; break;
201 case SZ_ERROR_UNSUPPORTED: Unsupported = true; break;
202 case SZ_ERROR_CRC: CrcError = true; break;
203 case SZ_ERROR_DATA: DataError = true; break;
204 default: DataError = true; break;
205 }
206
207 return readRes;
208 }
209 }
210 }
211
212
Get_Extract_OperationResult() const213 Int32 CDecoder::Get_Extract_OperationResult() const
214 {
215 Int32 opRes;
216 if (!IsArc)
217 opRes = NExtract::NOperationResult::kIsNotArc;
218 else if (UnexpectedEnd)
219 opRes = NExtract::NOperationResult::kUnexpectedEnd;
220 else if (DataAfterEnd)
221 opRes = NExtract::NOperationResult::kDataAfterEnd;
222 else if (CrcError)
223 opRes = NExtract::NOperationResult::kCRCError;
224 else if (Unsupported)
225 opRes = NExtract::NOperationResult::kUnsupportedMethod;
226 else if (HeadersError)
227 opRes = NExtract::NOperationResult::kDataError;
228 else if (DataError)
229 opRes = NExtract::NOperationResult::kDataError;
230 else if (DecodeRes != SZ_OK)
231 opRes = NExtract::NOperationResult::kDataError;
232 else
233 opRes = NExtract::NOperationResult::kOK;
234 return opRes;
235 }
236
237
238
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 *,const UInt64 * outSize,ICompressProgressInfo * progress)239 HRESULT CComDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
240 const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
241 {
242 RINOK(_decoder.Decode(inStream, outStream, outSize, _finishStream, progress));
243 Int32 opRes = _decoder.Get_Extract_OperationResult();
244 if (opRes == NArchive::NExtract::NOperationResult::kUnsupportedMethod)
245 return E_NOTIMPL;
246 if (opRes != NArchive::NExtract::NOperationResult::kOK)
247 return S_FALSE;
248 return S_OK;
249 }
250
SetFinishMode(UInt32 finishMode)251 STDMETHODIMP CComDecoder::SetFinishMode(UInt32 finishMode)
252 {
253 _finishStream = (finishMode != 0);
254 return S_OK;
255 }
256
GetInStreamProcessedSize(UInt64 * value)257 STDMETHODIMP CComDecoder::GetInStreamProcessedSize(UInt64 *value)
258 {
259 *value = _decoder.InSize;
260 return S_OK;
261 }
262
263 }}
264