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