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