1 // Lzma2Decoder.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../C/Alloc.h" 6 7 #include "../Common/StreamUtils.h" 8 9 #include "Lzma2Decoder.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_PROGRESS: return E_ABORT; 19 case SZ_ERROR_DATA: return S_FALSE; 20 } 21 return E_FAIL; 22 } 23 24 namespace NCompress { 25 namespace NLzma2 { 26 27 static const UInt32 kInBufSize = 1 << 20; 28 29 CDecoder::CDecoder(): _inBuf(0), _outSizeDefined(false) 30 { 31 Lzma2Dec_Construct(&_state); 32 } 33 34 static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } 35 static void SzFree(void *p, void *address) { p = p; MyFree(address); } 36 static ISzAlloc g_Alloc = { SzAlloc, SzFree }; 37 38 CDecoder::~CDecoder() 39 { 40 Lzma2Dec_Free(&_state, &g_Alloc); 41 MyFree(_inBuf); 42 } 43 44 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size) 45 { 46 if (size != 1) return SZ_ERROR_UNSUPPORTED; 47 RINOK(SResToHRESULT(Lzma2Dec_Allocate(&_state, prop[0], &g_Alloc))); 48 if (_inBuf == 0) 49 { 50 _inBuf = (Byte *)MyAlloc(kInBufSize); 51 if (_inBuf == 0) 52 return E_OUTOFMEMORY; 53 } 54 55 return S_OK; 56 } 57 58 STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value) { *value = _inSizeProcessed; return S_OK; } 59 STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; } 60 STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; } 61 62 STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize) 63 { 64 _outSizeDefined = (outSize != NULL); 65 if (_outSizeDefined) 66 _outSize = *outSize; 67 68 Lzma2Dec_Init(&_state); 69 70 _inPos = _inSize = 0; 71 _inSizeProcessed = _outSizeProcessed = 0; 72 return S_OK; 73 } 74 75 STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, 76 ISequentialOutStream *outStream, const UInt64 * /* inSize */, 77 const UInt64 *outSize, ICompressProgressInfo *progress) 78 { 79 if (_inBuf == 0) 80 return S_FALSE; 81 SetOutStreamSize(outSize); 82 83 for (;;) 84 { 85 if (_inPos == _inSize) 86 { 87 _inPos = _inSize = 0; 88 RINOK(inStream->Read(_inBuf, kInBufSize, &_inSize)); 89 } 90 91 SizeT dicPos = _state.decoder.dicPos; 92 SizeT curSize = _state.decoder.dicBufSize - dicPos; 93 const UInt32 kStepSize = ((UInt32)1 << 22); 94 if (curSize > kStepSize) 95 curSize = (SizeT)kStepSize; 96 97 ELzmaFinishMode finishMode = LZMA_FINISH_ANY; 98 if (_outSizeDefined) 99 { 100 const UInt64 rem = _outSize - _outSizeProcessed; 101 if (rem < curSize) 102 { 103 curSize = (SizeT)rem; 104 /* 105 // finishMode = LZMA_FINISH_END; 106 we can't use LZMA_FINISH_END here to allow partial decoding 107 */ 108 } 109 } 110 111 SizeT inSizeProcessed = _inSize - _inPos; 112 ELzmaStatus status; 113 SRes res = Lzma2Dec_DecodeToDic(&_state, dicPos + curSize, _inBuf + _inPos, &inSizeProcessed, finishMode, &status); 114 115 _inPos += (UInt32)inSizeProcessed; 116 _inSizeProcessed += inSizeProcessed; 117 SizeT outSizeProcessed = _state.decoder.dicPos - dicPos; 118 _outSizeProcessed += outSizeProcessed; 119 120 bool finished = (inSizeProcessed == 0 && outSizeProcessed == 0); 121 bool stopDecoding = (_outSizeDefined && _outSizeProcessed >= _outSize); 122 123 if (res != 0 || _state.decoder.dicPos == _state.decoder.dicBufSize || finished || stopDecoding) 124 { 125 HRESULT res2 = WriteStream(outStream, _state.decoder.dic, _state.decoder.dicPos); 126 if (res != 0) 127 return S_FALSE; 128 RINOK(res2); 129 if (stopDecoding) 130 return S_OK; 131 if (finished) 132 return (status == LZMA_STATUS_FINISHED_WITH_MARK ? S_OK : S_FALSE); 133 } 134 if (_state.decoder.dicPos == _state.decoder.dicBufSize) 135 _state.decoder.dicPos = 0; 136 137 if (progress != NULL) 138 { 139 RINOK(progress->SetRatioInfo(&_inSizeProcessed, &_outSizeProcessed)); 140 } 141 } 142 } 143 144 #ifndef NO_READ_FROM_CODER 145 146 STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize) 147 { 148 if (processedSize) 149 *processedSize = 0; 150 do 151 { 152 if (_inPos == _inSize) 153 { 154 _inPos = _inSize = 0; 155 RINOK(_inStream->Read(_inBuf, kInBufSize, &_inSize)); 156 } 157 { 158 SizeT inProcessed = _inSize - _inPos; 159 160 if (_outSizeDefined) 161 { 162 const UInt64 rem = _outSize - _outSizeProcessed; 163 if (rem < size) 164 size = (UInt32)rem; 165 } 166 167 SizeT outProcessed = size; 168 ELzmaStatus status; 169 SRes res = Lzma2Dec_DecodeToBuf(&_state, (Byte *)data, &outProcessed, 170 _inBuf + _inPos, &inProcessed, LZMA_FINISH_ANY, &status); 171 _inPos += (UInt32)inProcessed; 172 _inSizeProcessed += inProcessed; 173 _outSizeProcessed += outProcessed; 174 size -= (UInt32)outProcessed; 175 data = (Byte *)data + outProcessed; 176 if (processedSize) 177 *processedSize += (UInt32)outProcessed; 178 RINOK(SResToHRESULT(res)); 179 if (inProcessed == 0 && outProcessed == 0) 180 return S_OK; 181 } 182 } 183 while (size != 0); 184 return S_OK; 185 } 186 187 #endif 188 189 }} 190