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  NeedMoreInput(false)
32{
33  _inSizeProcessed = 0;
34  _inPos = _inSize = 0;
35  LzmaDec_Construct(&_state);
36}
37
38static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
39static void SzFree(void *p, void *address) { p = p; MyFree(address); }
40static ISzAlloc g_Alloc = { SzAlloc, SzFree };
41
42CDecoder::~CDecoder()
43{
44  LzmaDec_Free(&_state, &g_Alloc);
45  MyFree(_inBuf);
46}
47
48STDMETHODIMP CDecoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSize = size; return S_OK; }
49STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; }
50
51HRESULT 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
64STDMETHODIMP 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
71void 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
81STDMETHODIMP 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
90HRESULT 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
173STDMETHODIMP 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
184STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; }
185STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; }
186
187STDMETHODIMP 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
228HRESULT CDecoder::CodeResume(ISequentialOutStream *outStream, const UInt64 *outSize, ICompressProgressInfo *progress)
229{
230  SetOutStreamSizeResume(outSize);
231  return CodeSpec(_inStream, outStream, progress);
232}
233
234HRESULT 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