misc/libphysfs/lzma/CPP/7zip/Compress/LZMA_Alone/LzmaRam.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

// LzmaRam.cpp

#include "StdAfx.h"
#include "../../../Common/Types.h"
#include "../LZMA/LZMADecoder.h"
#include "../LZMA/LZMAEncoder.h"
#include "LzmaRam.h"

extern "C"
{
  #include "../../../../C/Compress/Branch/BranchX86.h"
}

class CInStreamRam: 
  public ISequentialInStream,
  public CMyUnknownImp
{
  const Byte *Data;
  size_t Size;
  size_t Pos;
public:
  MY_UNKNOWN_IMP
  void Init(const Byte *data, size_t size)
  {
    Data = data;
    Size = size;
    Pos = 0;
  }
  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
};

STDMETHODIMP CInStreamRam::Read(void *data, UInt32 size, UInt32 *processedSize)
{
  if (size > (Size - Pos))
    size = (UInt32)(Size - Pos);
  for (UInt32 i = 0; i < size; i++)
    ((Byte *)data)[i] = Data[Pos + i];
  Pos += size;
  if(processedSize != NULL)
    *processedSize = size;
  return S_OK;
}
  
class COutStreamRam: 
  public ISequentialOutStream,
  public CMyUnknownImp
{
  size_t Size;
public:
  Byte *Data;
  size_t Pos;
  bool Overflow;
  void Init(Byte *data, size_t size)
  {
    Data = data;
    Size = size;
    Pos = 0;
    Overflow = false;
  }
  void SetPos(size_t pos)
  {
    Overflow = false;
    Pos = pos;
  }
  MY_UNKNOWN_IMP
  HRESULT WriteByte(Byte b)
  {
    if (Pos >= Size)
    {
      Overflow = true;
      return E_FAIL;
    }
    Data[Pos++] = b;
    return S_OK;
  }
  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
};

STDMETHODIMP COutStreamRam::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
  UInt32 i;
  for (i = 0; i < size && Pos < Size; i++)
    Data[Pos++] = ((const Byte *)data)[i];
  if(processedSize != NULL)
    *processedSize = i;
  if (i != size)
  {
    Overflow = true;
    return E_FAIL;
  }
  return S_OK;
}
  
#define SZ_RAM_E_FAIL (1)
#define SZ_RAM_E_OUTOFMEMORY (2)
#define SZE_OUT_OVERFLOW (3)

int LzmaRamEncode(
    const Byte *inBuffer, size_t inSize, 
    Byte *outBuffer, size_t outSize, size_t *outSizeProcessed, 
    UInt32 dictionarySize, ESzFilterMode filterMode)
{
  #ifndef _NO_EXCEPTIONS
  try { 
  #endif

  *outSizeProcessed = 0;
  const size_t kIdSize = 1;
  const size_t kLzmaPropsSize = 5;
  const size_t kMinDestSize = kIdSize + kLzmaPropsSize + 8;
  if (outSize < kMinDestSize)
    return SZE_OUT_OVERFLOW;
  NCompress::NLZMA::CEncoder *encoderSpec = new NCompress::NLZMA::CEncoder;
  CMyComPtr<ICompressCoder> encoder = encoderSpec;

  PROPID propIDs[] = 
  { 
    NCoderPropID::kAlgorithm,
    NCoderPropID::kDictionarySize,  
    NCoderPropID::kNumFastBytes,
  };
  const int kNumProps = sizeof(propIDs) / sizeof(propIDs[0]);
  PROPVARIANT properties[kNumProps];
  properties[0].vt = VT_UI4;
  properties[1].vt = VT_UI4;
  properties[2].vt = VT_UI4;
  properties[0].ulVal = (UInt32)2;
  properties[1].ulVal = (UInt32)dictionarySize;
  properties[2].ulVal = (UInt32)64;

  if (encoderSpec->SetCoderProperties(propIDs, properties, kNumProps) != S_OK)
    return 1;
  
  COutStreamRam *outStreamSpec = new COutStreamRam;
  if (outStreamSpec == 0)
    return SZ_RAM_E_OUTOFMEMORY;
  CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
  CInStreamRam *inStreamSpec = new CInStreamRam;
  if (inStreamSpec == 0)
    return SZ_RAM_E_OUTOFMEMORY;
  CMyComPtr<ISequentialInStream> inStream = inStreamSpec;

  outStreamSpec->Init(outBuffer, outSize);
  if (outStreamSpec->WriteByte(0) != S_OK)
    return SZE_OUT_OVERFLOW;

  if (encoderSpec->WriteCoderProperties(outStream) != S_OK)
    return SZE_OUT_OVERFLOW;
  if (outStreamSpec->Pos != kIdSize + kLzmaPropsSize)
    return 1;
  
  int i;
  for (i = 0; i < 8; i++)
  {
    UInt64 t = (UInt64)(inSize);
    if (outStreamSpec->WriteByte((Byte)((t) >> (8 * i))) != S_OK)
      return SZE_OUT_OVERFLOW;
  }

  Byte *filteredStream = 0;

  bool useFilter = (filterMode != SZ_FILTER_NO);
  if (useFilter)
  {
    if (inSize != 0)
    {
      filteredStream = (Byte *)MyAlloc(inSize);
      if (filteredStream == 0)
        return SZ_RAM_E_OUTOFMEMORY;
      memmove(filteredStream, inBuffer, inSize);
    }
    UInt32 x86State;
    x86_Convert_Init(x86State);
    x86_Convert(filteredStream, (SizeT)inSize, 0, &x86State, 1);
  }
  
  size_t minSize = 0;
  int numPasses = (filterMode == SZ_FILTER_AUTO) ? 3 : 1;
  bool bestIsFiltered = false;
  int mainResult = 0;
  size_t startPos = outStreamSpec->Pos;
  for (i = 0; i < numPasses; i++)
  {
    if (numPasses > 1 && i == numPasses - 1 && !bestIsFiltered)
      break;
    outStreamSpec->SetPos(startPos);
    bool curModeIsFiltered = false;
    if (useFilter && i == 0)
      curModeIsFiltered = true;
    if (numPasses > 1 && i == numPasses - 1)
      curModeIsFiltered = true;

    inStreamSpec->Init(curModeIsFiltered ? filteredStream : inBuffer, inSize);
    
    HRESULT lzmaResult = encoder->Code(inStream, outStream, 0, 0, 0);
    
    mainResult = 0;
    if (lzmaResult == E_OUTOFMEMORY)
    {
      mainResult = SZ_RAM_E_OUTOFMEMORY;
      break;
    } 
    if (i == 0 || outStreamSpec->Pos <= minSize)
    {
      minSize = outStreamSpec->Pos;
      bestIsFiltered = curModeIsFiltered;
    }
    if (outStreamSpec->Overflow)
      mainResult = SZE_OUT_OVERFLOW;
    else if (lzmaResult != S_OK)
    {
      mainResult = SZ_RAM_E_FAIL;
      break;
    } 
  }
  *outSizeProcessed = outStreamSpec->Pos;
  if (bestIsFiltered)
    outBuffer[0] = 1;
  if (useFilter)
    MyFree(filteredStream);
  return mainResult;
  
  #ifndef _NO_EXCEPTIONS
  } catch(...) { return SZ_RAM_E_OUTOFMEMORY; }
  #endif
}