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
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_PROGRESS: return E_ABORT;
19    case SZ_ERROR_DATA: return S_FALSE;
20  }
21  return E_FAIL;
22}
23
24namespace NCompress {
25namespace NLzma2 {
26
27static const UInt32 kInBufSize = 1 << 20;
28
29CDecoder::CDecoder(): _inBuf(0), _outSizeDefined(false)
30{
31  Lzma2Dec_Construct(&_state);
32}
33
34static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
35static void SzFree(void *p, void *address) { p = p; MyFree(address); }
36static ISzAlloc g_Alloc = { SzAlloc, SzFree };
37
38CDecoder::~CDecoder()
39{
40  Lzma2Dec_Free(&_state, &g_Alloc);
41  MyFree(_inBuf);
42}
43
44STDMETHODIMP 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
58STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value) { *value = _inSizeProcessed; return S_OK; }
59STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; }
60STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; }
61
62STDMETHODIMP 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
75STDMETHODIMP 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
146STDMETHODIMP 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