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