1// Lzma2Decoder.cpp
2
3#include "StdAfx.h"
4
5// #include <stdio.h>
6
7#include "../../../C/Alloc.h"
8// #include "../../../C/CpuTicks.h"
9
10#include "../Common/StreamUtils.h"
11
12#include "Lzma2Decoder.h"
13
14namespace NCompress {
15namespace NLzma2 {
16
17CDecoder::CDecoder():
18      _dec(NULL)
19    , _inProcessed(0)
20    , _prop(0xFF)
21    , _finishMode(false)
22    , _inBufSize(1 << 20)
23    , _outStep(1 << 20)
24    #ifndef Z7_ST
25    , _tryMt(1)
26    , _numThreads(1)
27    , _memUsage((UInt64)(sizeof(size_t)) << 28)
28    #endif
29{}
30
31CDecoder::~CDecoder()
32{
33  if (_dec)
34    Lzma2DecMt_Destroy(_dec);
35}
36
37Z7_COM7F_IMF(CDecoder::SetInBufSize(UInt32 , UInt32 size)) { _inBufSize = size; return S_OK; }
38Z7_COM7F_IMF(CDecoder::SetOutBufSize(UInt32 , UInt32 size)) { _outStep = size; return S_OK; }
39
40Z7_COM7F_IMF(CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size))
41{
42  if (size != 1)
43    return E_NOTIMPL;
44  if (prop[0] > 40)
45    return E_NOTIMPL;
46  _prop = prop[0];
47  return S_OK;
48}
49
50
51Z7_COM7F_IMF(CDecoder::SetFinishMode(UInt32 finishMode))
52{
53  _finishMode = (finishMode != 0);
54  return S_OK;
55}
56
57
58
59#ifndef Z7_ST
60
61static UInt64 Get_ExpectedBlockSize_From_Dict(UInt32 dictSize)
62{
63  const UInt32 kMinSize = (UInt32)1 << 20;
64  const UInt32 kMaxSize = (UInt32)1 << 28;
65  UInt64 blockSize = (UInt64)dictSize << 2;
66  if (blockSize < kMinSize) blockSize = kMinSize;
67  if (blockSize > kMaxSize) blockSize = kMaxSize;
68  if (blockSize < dictSize) blockSize = dictSize;
69  blockSize += (kMinSize - 1);
70  blockSize &= ~(UInt64)(kMinSize - 1);
71  return blockSize;
72}
73
74#define LZMA2_DIC_SIZE_FROM_PROP_FULL(p) ((p) == 40 ? 0xFFFFFFFF : (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)))
75
76#endif
77
78#define RET_IF_WRAP_ERROR_CONFIRMED(wrapRes, sRes, sResErrorCode) \
79  if (wrapRes != S_OK && sRes == sResErrorCode) return wrapRes;
80
81#define RET_IF_WRAP_ERROR(wrapRes, sRes, sResErrorCode) \
82  if (wrapRes != S_OK /* && (sRes == SZ_OK || sRes == sResErrorCode) */) return wrapRes;
83
84Z7_COM7F_IMF(CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
85    const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress))
86{
87  _inProcessed = 0;
88
89  if (!_dec)
90  {
91    _dec = Lzma2DecMt_Create(
92      // &g_AlignedAlloc,
93      &g_Alloc,
94      &g_MidAlloc);
95    if (!_dec)
96      return E_OUTOFMEMORY;
97  }
98
99  CLzma2DecMtProps props;
100  Lzma2DecMtProps_Init(&props);
101
102  props.inBufSize_ST = _inBufSize;
103  props.outStep_ST = _outStep;
104
105  #ifndef Z7_ST
106  {
107    props.numThreads = 1;
108    UInt32 numThreads = _numThreads;
109
110    if (_tryMt && numThreads >= 1)
111    {
112      const UInt64 useLimit = _memUsage;
113      const UInt32 dictSize = LZMA2_DIC_SIZE_FROM_PROP_FULL(_prop);
114      const UInt64 expectedBlockSize64 = Get_ExpectedBlockSize_From_Dict(dictSize);
115      const size_t expectedBlockSize = (size_t)expectedBlockSize64;
116      const size_t inBlockMax = expectedBlockSize + expectedBlockSize / 16;
117      if (expectedBlockSize == expectedBlockSize64 && inBlockMax >= expectedBlockSize)
118      {
119        props.outBlockMax = expectedBlockSize;
120        props.inBlockMax = inBlockMax;
121        const size_t kOverheadSize = props.inBufSize_MT + (1 << 16);
122        const UInt64 okThreads = useLimit / (props.outBlockMax + props.inBlockMax + kOverheadSize);
123        if (numThreads > okThreads)
124          numThreads = (UInt32)okThreads;
125        if (numThreads == 0)
126          numThreads = 1;
127        props.numThreads = numThreads;
128      }
129    }
130  }
131  #endif
132
133  CSeqInStreamWrap inWrap;
134  CSeqOutStreamWrap outWrap;
135  CCompressProgressWrap progressWrap;
136
137  inWrap.Init(inStream);
138  outWrap.Init(outStream);
139  progressWrap.Init(progress);
140
141  SRes res;
142
143  UInt64 inProcessed = 0;
144  int isMT = False;
145
146  #ifndef Z7_ST
147  isMT = _tryMt;
148  #endif
149
150  // UInt64 cpuTicks = GetCpuTicks();
151
152  res = Lzma2DecMt_Decode(_dec, _prop, &props,
153      &outWrap.vt, outSize, _finishMode,
154      &inWrap.vt,
155      &inProcessed,
156      &isMT,
157      progress ? &progressWrap.vt : NULL);
158
159  /*
160  cpuTicks = GetCpuTicks() - cpuTicks;
161  printf("\n             ticks = %10I64u\n", cpuTicks / 1000000);
162  */
163
164
165  #ifndef Z7_ST
166  /* we reset _tryMt, only if p->props.numThreads was changed */
167  if (props.numThreads > 1)
168    _tryMt = isMT;
169  #endif
170
171  _inProcessed = inProcessed;
172
173  RET_IF_WRAP_ERROR(progressWrap.Res, res, SZ_ERROR_PROGRESS)
174  RET_IF_WRAP_ERROR(outWrap.Res, res, SZ_ERROR_WRITE)
175  RET_IF_WRAP_ERROR_CONFIRMED(inWrap.Res, res, SZ_ERROR_READ)
176
177  if (res == SZ_OK && _finishMode)
178  {
179    if (inSize && *inSize != inProcessed)
180      res = SZ_ERROR_DATA;
181    if (outSize && *outSize != outWrap.Processed)
182      res = SZ_ERROR_DATA;
183  }
184
185  return SResToHRESULT(res);
186}
187
188
189Z7_COM7F_IMF(CDecoder::GetInStreamProcessedSize(UInt64 *value))
190{
191  *value = _inProcessed;
192  return S_OK;
193}
194
195
196#ifndef Z7_ST
197
198Z7_COM7F_IMF(CDecoder::SetNumberOfThreads(UInt32 numThreads))
199{
200  _numThreads = numThreads;
201  return S_OK;
202}
203
204Z7_COM7F_IMF(CDecoder::SetMemLimit(UInt64 memUsage))
205{
206  _memUsage = memUsage;
207  return S_OK;
208}
209
210#endif
211
212
213#ifndef Z7_NO_READ_FROM_CODER
214
215Z7_COM7F_IMF(CDecoder::SetOutStreamSize(const UInt64 *outSize))
216{
217  CLzma2DecMtProps props;
218  Lzma2DecMtProps_Init(&props);
219  props.inBufSize_ST = _inBufSize;
220  props.outStep_ST = _outStep;
221
222  _inProcessed = 0;
223
224  if (!_dec)
225  {
226    _dec = Lzma2DecMt_Create(&g_AlignedAlloc, &g_MidAlloc);
227    if (!_dec)
228      return E_OUTOFMEMORY;
229  }
230
231  _inWrap.Init(_inStream);
232
233  const SRes res = Lzma2DecMt_Init(_dec, _prop, &props, outSize, _finishMode, &_inWrap.vt);
234
235  if (res != SZ_OK)
236    return SResToHRESULT(res);
237  return S_OK;
238}
239
240
241Z7_COM7F_IMF(CDecoder::SetInStream(ISequentialInStream *inStream))
242  { _inStream = inStream; return S_OK; }
243Z7_COM7F_IMF(CDecoder::ReleaseInStream())
244  { _inStream.Release(); return S_OK; }
245
246
247Z7_COM7F_IMF(CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize))
248{
249  if (processedSize)
250    *processedSize = 0;
251
252  size_t size2 = size;
253  UInt64 inProcessed = 0;
254
255  const SRes res = Lzma2DecMt_Read(_dec, (Byte *)data, &size2, &inProcessed);
256
257  _inProcessed += inProcessed;
258  if (processedSize)
259    *processedSize = (UInt32)size2;
260  if (res != SZ_OK)
261    return SResToHRESULT(res);
262  return S_OK;
263}
264
265#endif
266
267}}
268