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