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 
SResToHRESULT(SRes res)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 
CDecoder()29 CDecoder::CDecoder(): _inBuf(0), _outSizeDefined(false)
30 {
31   Lzma2Dec_Construct(&_state);
32 }
33 
SzAlloc(void * p,size_t size)34 static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
SzFree(void * p,void * address)35 static void SzFree(void *p, void *address) { p = p; MyFree(address); }
36 static ISzAlloc g_Alloc = { SzAlloc, SzFree };
37 
~CDecoder()38 CDecoder::~CDecoder()
39 {
40   Lzma2Dec_Free(&_state, &g_Alloc);
41   MyFree(_inBuf);
42 }
43 
SetDecoderProperties2(const Byte * prop,UInt32 size)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 
GetInStreamProcessedSize(UInt64 * value)58 STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value) { *value = _inSizeProcessed; return S_OK; }
SetInStream(ISequentialInStream * inStream)59 STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; }
ReleaseInStream()60 STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; }
61 
SetOutStreamSize(const UInt64 * outSize)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 
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 *,const UInt64 * outSize,ICompressProgressInfo * progress)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 
Read(void * data,UInt32 size,UInt32 * processedSize)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