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