Home | History | Annotate | Download | only in Compress
      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 
     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_UNSUPPORTED: return E_NOTIMPL;
     19     case SZ_ERROR_DATA: return S_FALSE;
     20   }
     21   return E_FAIL;
     22 }
     23 
     24 namespace NCompress {
     25 namespace NLzma {
     26 
     27 CDecoder::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 
     38 CDecoder::~CDecoder()
     39 {
     40   LzmaDec_Free(&_state, &g_Alloc);
     41   MyFree(_inBuf);
     42 }
     43 
     44 STDMETHODIMP CDecoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSize = size; return S_OK; }
     45 STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; }
     46 
     47 HRESULT CDecoder::CreateInputBuffer()
     48 {
     49   if (_inBuf == 0 || _inBufSize != _inBufSizeAllocated)
     50   {
     51     MyFree(_inBuf);
     52     _inBuf = (Byte *)MyAlloc(_inBufSize);
     53     if (_inBuf == 0)
     54       return E_OUTOFMEMORY;
     55     _inBufSizeAllocated = _inBufSize;
     56   }
     57   return S_OK;
     58 }
     59 
     60 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size)
     61 {
     62   RINOK(SResToHRESULT(LzmaDec_Allocate(&_state, prop, size, &g_Alloc)));
     63   _propsWereSet = true;
     64   return CreateInputBuffer();
     65 }
     66 
     67 void CDecoder::SetOutStreamSizeResume(const UInt64 *outSize)
     68 {
     69   _outSizeDefined = (outSize != NULL);
     70   if (_outSizeDefined)
     71     _outSize = *outSize;
     72   _outSizeProcessed = 0;
     73   _wrPos = 0;
     74   LzmaDec_Init(&_state);
     75 }
     76 
     77 STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize)
     78 {
     79   _inSizeProcessed = 0;
     80   _inPos = _inSize = 0;
     81   NeedMoreInput = false;
     82   SetOutStreamSizeResume(outSize);
     83   return S_OK;
     84 }
     85 
     86 STDMETHODIMP CDecoder::SetFinishMode(UInt32 finishMode)
     87 {
     88   FinishStream = (finishMode != 0);
     89   return S_OK;
     90 }
     91 
     92 HRESULT CDecoder::CodeSpec(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress)
     93 {
     94   if (_inBuf == 0 || !_propsWereSet)
     95     return S_FALSE;
     96 
     97   UInt64 startInProgress = _inSizeProcessed;
     98 
     99   SizeT next = (_state.dicBufSize - _state.dicPos < _outBufSize) ? _state.dicBufSize : (_state.dicPos + _outBufSize);
    100   for (;;)
    101   {
    102     if (_inPos == _inSize)
    103     {
    104       _inPos = _inSize = 0;
    105       RINOK(inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize));
    106     }
    107 
    108     SizeT dicPos = _state.dicPos;
    109     SizeT curSize = next - dicPos;
    110 
    111     ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
    112     if (_outSizeDefined)
    113     {
    114       const UInt64 rem = _outSize - _outSizeProcessed;
    115       if (rem <= curSize)
    116       {
    117         curSize = (SizeT)rem;
    118         if (FinishStream)
    119           finishMode = LZMA_FINISH_END;
    120       }
    121     }
    122 
    123     SizeT inSizeProcessed = _inSize - _inPos;
    124     ELzmaStatus status;
    125     SRes res = LzmaDec_DecodeToDic(&_state, dicPos + curSize, _inBuf + _inPos, &inSizeProcessed, finishMode, &status);
    126 
    127     _inPos += (UInt32)inSizeProcessed;
    128     _inSizeProcessed += inSizeProcessed;
    129     SizeT outSizeProcessed = _state.dicPos - dicPos;
    130     _outSizeProcessed += outSizeProcessed;
    131 
    132     bool finished = (inSizeProcessed == 0 && outSizeProcessed == 0);
    133     bool stopDecoding = (_outSizeDefined && _outSizeProcessed >= _outSize);
    134 
    135     if (res != 0 || _state.dicPos == next || finished || stopDecoding)
    136     {
    137       HRESULT res2 = WriteStream(outStream, _state.dic + _wrPos, _state.dicPos - _wrPos);
    138 
    139       _wrPos = _state.dicPos;
    140       if (_state.dicPos == _state.dicBufSize)
    141       {
    142         _state.dicPos = 0;
    143         _wrPos = 0;
    144       }
    145       next = (_state.dicBufSize - _state.dicPos < _outBufSize) ? _state.dicBufSize : (_state.dicPos + _outBufSize);
    146 
    147       if (res != 0)
    148         return S_FALSE;
    149       RINOK(res2);
    150       if (stopDecoding)
    151       {
    152         if (status == LZMA_STATUS_NEEDS_MORE_INPUT)
    153           NeedMoreInput = true;
    154         if (FinishStream &&
    155               status != LZMA_STATUS_FINISHED_WITH_MARK &&
    156               status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
    157           return S_FALSE;
    158         return S_OK;
    159       }
    160       if (finished)
    161       {
    162         if (status == LZMA_STATUS_NEEDS_MORE_INPUT)
    163           NeedMoreInput = true;
    164         return (status == LZMA_STATUS_FINISHED_WITH_MARK ? S_OK : S_FALSE);
    165       }
    166     }
    167     if (progress)
    168     {
    169       UInt64 inSize = _inSizeProcessed - startInProgress;
    170       RINOK(progress->SetRatioInfo(&inSize, &_outSizeProcessed));
    171     }
    172   }
    173 }
    174 
    175 STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
    176     const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
    177 {
    178   if (_inBuf == 0)
    179     return E_INVALIDARG;
    180   SetOutStreamSize(outSize);
    181   return CodeSpec(inStream, outStream, progress);
    182 }
    183 
    184 #ifndef NO_READ_FROM_CODER
    185 
    186 STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; }
    187 STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; }
    188 
    189 STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)
    190 {
    191   if (processedSize)
    192     *processedSize = 0;
    193   do
    194   {
    195     if (_inPos == _inSize)
    196     {
    197       _inPos = _inSize = 0;
    198       RINOK(_inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize));
    199     }
    200     {
    201       SizeT inProcessed = _inSize - _inPos;
    202 
    203       if (_outSizeDefined)
    204       {
    205         const UInt64 rem = _outSize - _outSizeProcessed;
    206         if (rem < size)
    207           size = (UInt32)rem;
    208       }
    209 
    210       SizeT outProcessed = size;
    211       ELzmaStatus status;
    212       SRes res = LzmaDec_DecodeToBuf(&_state, (Byte *)data, &outProcessed,
    213           _inBuf + _inPos, &inProcessed, LZMA_FINISH_ANY, &status);
    214       _inPos += (UInt32)inProcessed;
    215       _inSizeProcessed += inProcessed;
    216       _outSizeProcessed += outProcessed;
    217       size -= (UInt32)outProcessed;
    218       data = (Byte *)data + outProcessed;
    219       if (processedSize)
    220         *processedSize += (UInt32)outProcessed;
    221       RINOK(SResToHRESULT(res));
    222       if (inProcessed == 0 && outProcessed == 0)
    223         return S_OK;
    224     }
    225   }
    226   while (size != 0);
    227   return S_OK;
    228 }
    229 
    230 HRESULT CDecoder::CodeResume(ISequentialOutStream *outStream, const UInt64 *outSize, ICompressProgressInfo *progress)
    231 {
    232   SetOutStreamSizeResume(outSize);
    233   return CodeSpec(_inStream, outStream, progress);
    234 }
    235 
    236 HRESULT CDecoder::ReadFromInputStream(void *data, UInt32 size, UInt32 *processedSize)
    237 {
    238   RINOK(CreateInputBuffer());
    239   if (processedSize)
    240     *processedSize = 0;
    241   while (size > 0)
    242   {
    243     if (_inPos == _inSize)
    244     {
    245       _inPos = _inSize = 0;
    246       RINOK(_inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize));
    247       if (_inSize == 0)
    248         break;
    249     }
    250     {
    251       UInt32 curSize = _inSize - _inPos;
    252       if (curSize > size)
    253         curSize = size;
    254       memcpy(data, _inBuf + _inPos, curSize);
    255       _inPos += curSize;
    256       _inSizeProcessed += curSize;
    257       size -= curSize;
    258       data = (Byte *)data + curSize;
    259       if (processedSize)
    260         *processedSize += curSize;
    261     }
    262   }
    263   return S_OK;
    264 }
    265 
    266 #endif
    267 
    268 }}
    269