misc/libphysfs/lzma/CPP/7zip/Compress/LZMA/LZMADecoder.cpp
author nemo
Mon, 10 Apr 2017 12:06:43 -0400
changeset 12213 bb5522e88ab2
permissions -rw-r--r--
bulk copy of latest physfs to our misc/libphysfs since this seems to fix an off-by-1 error reliably hit in readln read of 1 byte probably introduced in the addition of the buffered read. Whether this is excessive or whether libphysfs should even be maintained by us is another matter. But at least we shouldn't crash

// LZMADecoder.cpp

#include "StdAfx.h"

#include "LZMADecoder.h"
#include "../../../Common/Defs.h"

namespace NCompress {
namespace NLZMA {

const int kLenIdFinished = -1;
const int kLenIdNeedInit = -2;

void CDecoder::Init()
{
  { 
    for(int i = 0; i < kNumStates; i++)
    {
      for (UInt32 j = 0; j <= _posStateMask; j++)
      {
        _isMatch[i][j].Init();
        _isRep0Long[i][j].Init();
      }
      _isRep[i].Init();
      _isRepG0[i].Init();
      _isRepG1[i].Init();
      _isRepG2[i].Init();
    }
  }
  { 
    for (UInt32 i = 0; i < kNumLenToPosStates; i++)
    _posSlotDecoder[i].Init();
  }
  { 
    for(UInt32 i = 0; i < kNumFullDistances - kEndPosModelIndex; i++)
      _posDecoders[i].Init();
  }
  _posAlignDecoder.Init();
  _lenDecoder.Init(_posStateMask + 1);
  _repMatchLenDecoder.Init(_posStateMask + 1);
  _literalDecoder.Init();

  _state.Init();
  _reps[0] = _reps[1] = _reps[2] = _reps[3] = 0;
}

HRESULT CDecoder::CodeSpec(UInt32 curSize)
{
  if (_outSizeDefined)
  {
    const UInt64 rem = _outSize - _outWindowStream.GetProcessedSize();
    if (curSize > rem)
      curSize = (UInt32)rem;
  }

  if (_remainLen == kLenIdFinished)
    return S_OK;
  if (_remainLen == kLenIdNeedInit)
  {
    _rangeDecoder.Init();
    Init();
    _remainLen = 0;
  }
  if (curSize == 0)
    return S_OK;

  UInt32 rep0 = _reps[0];
  UInt32 rep1 = _reps[1];
  UInt32 rep2 = _reps[2];
  UInt32 rep3 = _reps[3];
  CState state = _state;
  Byte previousByte;

  while(_remainLen > 0 && curSize > 0)
  {
    previousByte = _outWindowStream.GetByte(rep0);
    _outWindowStream.PutByte(previousByte);
    _remainLen--;
    curSize--;
  }
  UInt64 nowPos64 = _outWindowStream.GetProcessedSize();
  if (nowPos64 == 0)
    previousByte = 0;
  else
    previousByte = _outWindowStream.GetByte(0);

  while(curSize > 0)
  {
    {
      #ifdef _NO_EXCEPTIONS
      if (_rangeDecoder.Stream.ErrorCode != S_OK)
        return _rangeDecoder.Stream.ErrorCode;
      #endif
      if (_rangeDecoder.Stream.WasFinished())
        return S_FALSE;
      UInt32 posState = UInt32(nowPos64) & _posStateMask;
      if (_isMatch[state.Index][posState].Decode(&_rangeDecoder) == 0)
      {
        if(!state.IsCharState())
          previousByte = _literalDecoder.DecodeWithMatchByte(&_rangeDecoder, 
              (UInt32)nowPos64, previousByte, _outWindowStream.GetByte(rep0));
        else
          previousByte = _literalDecoder.DecodeNormal(&_rangeDecoder, 
              (UInt32)nowPos64, previousByte);
        _outWindowStream.PutByte(previousByte);
        state.UpdateChar();
        curSize--;
        nowPos64++;
      }
      else             
      {
        UInt32 len;
        if(_isRep[state.Index].Decode(&_rangeDecoder) == 1)
        {
          len = 0;
          if(_isRepG0[state.Index].Decode(&_rangeDecoder) == 0)
          {
            if(_isRep0Long[state.Index][posState].Decode(&_rangeDecoder) == 0)
            {
              state.UpdateShortRep();
              len = 1;
            }
          }
          else
          {
            UInt32 distance;
            if(_isRepG1[state.Index].Decode(&_rangeDecoder) == 0)
              distance = rep1;
            else 
            {
              if (_isRepG2[state.Index].Decode(&_rangeDecoder) == 0)
                distance = rep2;
              else
              {
                distance = rep3;
                rep3 = rep2;
              }
              rep2 = rep1;
            }
            rep1 = rep0;
            rep0 = distance;
          }
          if (len == 0)
          {
            len = _repMatchLenDecoder.Decode(&_rangeDecoder, posState) + kMatchMinLen;
            state.UpdateRep();
          }
        }
        else
        {
          rep3 = rep2;
          rep2 = rep1;
          rep1 = rep0;
          len = kMatchMinLen + _lenDecoder.Decode(&_rangeDecoder, posState);
          state.UpdateMatch();
          UInt32 posSlot = _posSlotDecoder[GetLenToPosState(len)].Decode(&_rangeDecoder);
          if (posSlot >= kStartPosModelIndex)
          {
            UInt32 numDirectBits = (posSlot >> 1) - 1;
            rep0 = ((2 | (posSlot & 1)) << numDirectBits);

            if (posSlot < kEndPosModelIndex)
              rep0 += NRangeCoder::ReverseBitTreeDecode(_posDecoders + 
                  rep0 - posSlot - 1, &_rangeDecoder, numDirectBits);
            else
            {
              rep0 += (_rangeDecoder.DecodeDirectBits(
                  numDirectBits - kNumAlignBits) << kNumAlignBits);
              rep0 += _posAlignDecoder.ReverseDecode(&_rangeDecoder);
              if (rep0 == 0xFFFFFFFF)
              {
                _remainLen = kLenIdFinished;
                return S_OK;
              }
            }
          }
          else
            rep0 = posSlot;
        }
        UInt32 locLen = len;
        if (len > curSize)
          locLen = (UInt32)curSize;
        if (!_outWindowStream.CopyBlock(rep0, locLen))
          return S_FALSE;
        previousByte = _outWindowStream.GetByte(0);
        curSize -= locLen;
        nowPos64 += locLen;
        len -= locLen;
        if (len != 0)
        {
          _remainLen = (Int32)len;
          break;
        }

        #ifdef _NO_EXCEPTIONS
        if (_outWindowStream.ErrorCode != S_OK)
          return _outWindowStream.ErrorCode;
        #endif
      }
    }
  }
  if (_rangeDecoder.Stream.WasFinished())
    return S_FALSE;
  _reps[0] = rep0;
  _reps[1] = rep1;
  _reps[2] = rep2;
  _reps[3] = rep3;
  _state = state;

  return S_OK;
}

STDMETHODIMP CDecoder::CodeReal(ISequentialInStream *inStream,
    ISequentialOutStream *outStream, 
    const UInt64 *, const UInt64 *outSize,
    ICompressProgressInfo *progress)
{
  SetInStream(inStream);
  _outWindowStream.SetStream(outStream);
  SetOutStreamSize(outSize);
  CDecoderFlusher flusher(this);

  for (;;)
  {
    UInt32 curSize = 1 << 18;
    RINOK(CodeSpec(curSize));
    if (_remainLen == kLenIdFinished)
      break;
    if (progress != NULL)
    {
      UInt64 inSize = _rangeDecoder.GetProcessedSize();
      UInt64 nowPos64 = _outWindowStream.GetProcessedSize();
      RINOK(progress->SetRatioInfo(&inSize, &nowPos64));
    }
    if (_outSizeDefined)
      if (_outWindowStream.GetProcessedSize() >= _outSize)
        break;
  } 
  flusher.NeedFlush = false;
  return Flush();
}


#ifdef _NO_EXCEPTIONS

#define LZMA_TRY_BEGIN
#define LZMA_TRY_END

#else

#define LZMA_TRY_BEGIN try { 
#define LZMA_TRY_END } \
  catch(const CInBufferException &e)  { return e.ErrorCode; } \
  catch(const CLZOutWindowException &e)  { return e.ErrorCode; } \
  catch(...) { return S_FALSE; }

#endif


STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream,
      ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
      ICompressProgressInfo *progress)
{
  LZMA_TRY_BEGIN
  return CodeReal(inStream, outStream, inSize, outSize, progress); 
  LZMA_TRY_END
}

STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *properties, UInt32 size)
{
  if (size < 5)
    return E_INVALIDARG;
  int lc = properties[0] % 9;
  Byte remainder = (Byte)(properties[0] / 9);
  int lp = remainder % 5;
  int pb = remainder / 5;
  if (pb > NLength::kNumPosStatesBitsMax)
    return E_INVALIDARG;
  _posStateMask = (1 << pb) - 1;
  UInt32 dictionarySize = 0;
  for (int i = 0; i < 4; i++)
    dictionarySize += ((UInt32)(properties[1 + i])) << (i * 8);
  if (!_outWindowStream.Create(dictionarySize))
    return E_OUTOFMEMORY;
  if (!_literalDecoder.Create(lp, lc))
    return E_OUTOFMEMORY;
  if (!_rangeDecoder.Create(1 << 20))
    return E_OUTOFMEMORY;
  return S_OK;
}

STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value)
{
  *value = _rangeDecoder.GetProcessedSize();
  return S_OK;
}

STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream)
{
  _rangeDecoder.SetStream(inStream);
  return S_OK;
}

STDMETHODIMP CDecoder::ReleaseInStream()
{
  _rangeDecoder.ReleaseStream();
  return S_OK;
}

STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize)
{
  _outSizeDefined = (outSize != NULL);
  if (_outSizeDefined)
    _outSize = *outSize;
  _remainLen = kLenIdNeedInit;
  _outWindowStream.Init();
  return S_OK;
}

#ifndef NO_READ_FROM_CODER

STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)
{
  LZMA_TRY_BEGIN
  if (processedSize)
    *processedSize = 0;
  const UInt64 startPos = _outWindowStream.GetProcessedSize();
  _outWindowStream.SetMemStream((Byte *)data);
  RINOK(CodeSpec(size));
  if (processedSize)
    *processedSize = (UInt32)(_outWindowStream.GetProcessedSize() - startPos);
  return Flush();
  LZMA_TRY_END
}

#endif

}}