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
11static 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
24namespace NCompress {
25namespace NLzma {
26
27CDecoder::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
37static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
38static void SzFree(void *p, void *address) { p = p; MyFree(address); }
39static ISzAlloc g_Alloc = { SzAlloc, SzFree };
40
41CDecoder::~CDecoder()
42{
43  LzmaDec_Free(&_state, &g_Alloc);
44  MyFree(_inBuf);
45}
46
47STDMETHODIMP CDecoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSize = size; return S_OK; }
48STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; }
49
50HRESULT 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
63STDMETHODIMP 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
70void 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
80STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize)
81{
82  _inSizeProcessed = 0;
83  _inPos = _inSize = 0;
84  SetOutStreamSizeResume(outSize);
85  return S_OK;
86}
87
88HRESULT 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
159STDMETHODIMP 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
170STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; }
171STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; }
172
173STDMETHODIMP 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
214HRESULT CDecoder::CodeResume(ISequentialOutStream *outStream, const UInt64 *outSize, ICompressProgressInfo *progress)
215{
216  SetOutStreamSizeResume(outSize);
217  return CodeSpec(_inStream, outStream, progress);
218}
219
220HRESULT 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