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