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 static const UInt32 kInBufSize = 1 << 20;
     28 
     29 CDecoder::CDecoder(): _inBuf(0), _outSizeDefined(false)
     30 {
     31   Lzma2Dec_Construct(&_state);
     32 }
     33 
     34 static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
     35 static void SzFree(void *p, void *address) { p = p; MyFree(address); }
     36 static ISzAlloc g_Alloc = { SzAlloc, SzFree };
     37 
     38 CDecoder::~CDecoder()
     39 {
     40   Lzma2Dec_Free(&_state, &g_Alloc);
     41   MyFree(_inBuf);
     42 }
     43 
     44 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size)
     45 {
     46   if (size != 1) return SZ_ERROR_UNSUPPORTED;
     47   RINOK(SResToHRESULT(Lzma2Dec_Allocate(&_state, prop[0], &g_Alloc)));
     48   if (_inBuf == 0)
     49   {
     50     _inBuf = (Byte *)MyAlloc(kInBufSize);
     51     if (_inBuf == 0)
     52       return E_OUTOFMEMORY;
     53   }
     54 
     55   return S_OK;
     56 }
     57 
     58 STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value) { *value = _inSizeProcessed; return S_OK; }
     59 STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; }
     60 STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; }
     61 
     62 STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize)
     63 {
     64   _outSizeDefined = (outSize != NULL);
     65   if (_outSizeDefined)
     66     _outSize = *outSize;
     67 
     68   Lzma2Dec_Init(&_state);
     69 
     70   _inPos = _inSize = 0;
     71   _inSizeProcessed = _outSizeProcessed = 0;
     72   return S_OK;
     73 }
     74 
     75 STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream,
     76     ISequentialOutStream *outStream, const UInt64 * /* inSize */,
     77     const UInt64 *outSize, ICompressProgressInfo *progress)
     78 {
     79   if (_inBuf == 0)
     80     return S_FALSE;
     81   SetOutStreamSize(outSize);
     82 
     83   for (;;)
     84   {
     85     if (_inPos == _inSize)
     86     {
     87       _inPos = _inSize = 0;
     88       RINOK(inStream->Read(_inBuf, kInBufSize, &_inSize));
     89     }
     90 
     91     SizeT dicPos = _state.decoder.dicPos;
     92     SizeT curSize = _state.decoder.dicBufSize - dicPos;
     93     const UInt32 kStepSize = ((UInt32)1 << 22);
     94     if (curSize > kStepSize)
     95       curSize = (SizeT)kStepSize;
     96 
     97     ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
     98     if (_outSizeDefined)
     99     {
    100       const UInt64 rem = _outSize - _outSizeProcessed;
    101       if (rem < curSize)
    102       {
    103         curSize = (SizeT)rem;
    104         /*
    105         // finishMode = LZMA_FINISH_END;
    106         we can't use LZMA_FINISH_END here to allow partial decoding
    107         */
    108       }
    109     }
    110 
    111     SizeT inSizeProcessed = _inSize - _inPos;
    112     ELzmaStatus status;
    113     SRes res = Lzma2Dec_DecodeToDic(&_state, dicPos + curSize, _inBuf + _inPos, &inSizeProcessed, finishMode, &status);
    114 
    115     _inPos += (UInt32)inSizeProcessed;
    116     _inSizeProcessed += inSizeProcessed;
    117     SizeT outSizeProcessed = _state.decoder.dicPos - dicPos;
    118     _outSizeProcessed += outSizeProcessed;
    119 
    120     bool finished = (inSizeProcessed == 0 && outSizeProcessed == 0);
    121     bool stopDecoding = (_outSizeDefined && _outSizeProcessed >= _outSize);
    122 
    123     if (res != 0 || _state.decoder.dicPos == _state.decoder.dicBufSize || finished || stopDecoding)
    124     {
    125       HRESULT res2 = WriteStream(outStream, _state.decoder.dic, _state.decoder.dicPos);
    126       if (res != 0)
    127         return S_FALSE;
    128       RINOK(res2);
    129       if (stopDecoding)
    130         return S_OK;
    131       if (finished)
    132         return (status == LZMA_STATUS_FINISHED_WITH_MARK ? S_OK : S_FALSE);
    133     }
    134     if (_state.decoder.dicPos == _state.decoder.dicBufSize)
    135       _state.decoder.dicPos = 0;
    136 
    137     if (progress != NULL)
    138     {
    139       RINOK(progress->SetRatioInfo(&_inSizeProcessed, &_outSizeProcessed));
    140     }
    141   }
    142 }
    143 
    144 #ifndef NO_READ_FROM_CODER
    145 
    146 STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)
    147 {
    148   if (processedSize)
    149     *processedSize = 0;
    150   do
    151   {
    152     if (_inPos == _inSize)
    153     {
    154       _inPos = _inSize = 0;
    155       RINOK(_inStream->Read(_inBuf, kInBufSize, &_inSize));
    156     }
    157     {
    158       SizeT inProcessed = _inSize - _inPos;
    159 
    160       if (_outSizeDefined)
    161       {
    162         const UInt64 rem = _outSize - _outSizeProcessed;
    163         if (rem < size)
    164           size = (UInt32)rem;
    165       }
    166 
    167       SizeT outProcessed = size;
    168       ELzmaStatus status;
    169       SRes res = Lzma2Dec_DecodeToBuf(&_state, (Byte *)data, &outProcessed,
    170           _inBuf + _inPos, &inProcessed, LZMA_FINISH_ANY, &status);
    171       _inPos += (UInt32)inProcessed;
    172       _inSizeProcessed += inProcessed;
    173       _outSizeProcessed += outProcessed;
    174       size -= (UInt32)outProcessed;
    175       data = (Byte *)data + outProcessed;
    176       if (processedSize)
    177         *processedSize += (UInt32)outProcessed;
    178       RINOK(SResToHRESULT(res));
    179       if (inProcessed == 0 && outProcessed == 0)
    180         return S_OK;
    181     }
    182   }
    183   while (size != 0);
    184   return S_OK;
    185 }
    186 
    187 #endif
    188 
    189 }}
    190