LzmaDecoder.cpp (8416B)
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 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 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 48 CDecoder::~CDecoder() 49 { 50 LzmaDec_Free(&_state, &g_AlignedAlloc); // &_alloc.vt 51 MyFree(_inBuf); 52 } 53 54 STDMETHODIMP CDecoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSizeNew = size; return S_OK; } 55 STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outStep = size; return S_OK; } 56 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 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 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 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 102 STDMETHODIMP CDecoder::SetFinishMode(UInt32 finishMode) 103 { 104 FinishStream = (finishMode != 0); 105 return S_OK; 106 } 107 108 109 STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value) 110 { 111 *value = _inProcessed; 112 return S_OK; 113 } 114 115 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 return S_FALSE; 187 188 if (status == LZMA_STATUS_FINISHED_WITH_MARK) 189 { 190 if (FinishStream) 191 if (_outSizeDefined && _outSize != _outProcessed) 192 return S_FALSE; 193 return readRes; 194 } 195 196 if (outFinished && status != LZMA_STATUS_NEEDS_MORE_INPUT) 197 if (!FinishStream || status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) 198 return readRes; 199 200 return S_FALSE; 201 } 202 } 203 204 if (progress) 205 { 206 const UInt64 inSize = _inProcessed - startInProgress; 207 RINOK(progress->SetRatioInfo(&inSize, &_outProcessed)); 208 } 209 } 210 } 211 212 213 STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, 214 const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress) 215 { 216 if (!_inBuf) 217 return E_INVALIDARG; 218 SetOutStreamSize(outSize); 219 HRESULT res = CodeSpec(inStream, outStream, progress); 220 if (res == S_OK) 221 if (FinishStream && inSize && *inSize != _inProcessed) 222 res = S_FALSE; 223 return res; 224 } 225 226 227 #ifndef NO_READ_FROM_CODER 228 229 STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; } 230 STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; } 231 232 233 STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize) 234 { 235 if (processedSize) 236 *processedSize = 0; 237 238 ELzmaFinishMode finishMode = LZMA_FINISH_ANY; 239 if (_outSizeDefined) 240 { 241 const UInt64 rem = _outSize - _outProcessed; 242 if (size >= rem) 243 { 244 size = (UInt32)rem; 245 if (FinishStream) 246 finishMode = LZMA_FINISH_END; 247 } 248 } 249 250 HRESULT readRes = S_OK; 251 252 for (;;) 253 { 254 if (_inPos == _inLim && readRes == S_OK) 255 { 256 _inPos = _inLim = 0; 257 readRes = _inStream->Read(_inBuf, _inBufSize, &_inLim); 258 } 259 260 SizeT inProcessed = _inLim - _inPos; 261 SizeT outProcessed = size; 262 ELzmaStatus status; 263 264 SRes res = LzmaDec_DecodeToBuf(&_state, (Byte *)data, &outProcessed, 265 _inBuf + _inPos, &inProcessed, finishMode, &status); 266 267 _lzmaStatus = status; 268 _inPos += (UInt32)inProcessed; 269 _inProcessed += inProcessed; 270 _outProcessed += outProcessed; 271 size -= (UInt32)outProcessed; 272 data = (Byte *)data + outProcessed; 273 if (processedSize) 274 *processedSize += (UInt32)outProcessed; 275 276 if (res != 0) 277 return S_FALSE; 278 279 /* 280 if (status == LZMA_STATUS_FINISHED_WITH_MARK) 281 return readRes; 282 283 if (size == 0 && status != LZMA_STATUS_NEEDS_MORE_INPUT) 284 { 285 if (FinishStream 286 && _outSizeDefined && _outProcessed >= _outSize 287 && status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) 288 return S_FALSE; 289 return readRes; 290 } 291 */ 292 293 if (inProcessed == 0 && outProcessed == 0) 294 return readRes; 295 } 296 } 297 298 299 HRESULT CDecoder::CodeResume(ISequentialOutStream *outStream, const UInt64 *outSize, ICompressProgressInfo *progress) 300 { 301 SetOutStreamSizeResume(outSize); 302 return CodeSpec(_inStream, outStream, progress); 303 } 304 305 306 HRESULT CDecoder::ReadFromInputStream(void *data, UInt32 size, UInt32 *processedSize) 307 { 308 RINOK(CreateInputBuffer()); 309 310 if (processedSize) 311 *processedSize = 0; 312 313 HRESULT readRes = S_OK; 314 315 while (size != 0) 316 { 317 if (_inPos == _inLim) 318 { 319 _inPos = _inLim = 0; 320 if (readRes == S_OK) 321 readRes = _inStream->Read(_inBuf, _inBufSize, &_inLim); 322 if (_inLim == 0) 323 break; 324 } 325 326 UInt32 cur = _inLim - _inPos; 327 if (cur > size) 328 cur = size; 329 memcpy(data, _inBuf + _inPos, cur); 330 _inPos += cur; 331 _inProcessed += cur; 332 size -= cur; 333 data = (Byte *)data + cur; 334 if (processedSize) 335 *processedSize += cur; 336 } 337 338 return readRes; 339 } 340 341 #endif 342 343 }}