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