blob: 636994cbd829914c5976d0c185f9e0d8091605c3 [file] [log] [blame]
// Bcj2Coder.cpp
#include "StdAfx.h"
#include "../../../C/Alloc.h"
#include "Bcj2Coder.h"
namespace NCompress {
namespace NBcj2 {
inline bool IsJcc(Byte b0, Byte b1) { return (b0 == 0x0F && (b1 & 0xF0) == 0x80); }
inline bool IsJ(Byte b0, Byte b1) { return ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)); }
inline unsigned GetIndex(Byte b0, Byte b1) { return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257)); }
#ifndef EXTRACT_ONLY
static const unsigned kBufSize = 1 << 17;
#define NUM_BITS 2
#define SIGN_BIT (1 << NUM_BITS)
#define MASK_HIGH (0x100 - (1 << (NUM_BITS + 1)))
static const UInt32 kDefaultLimit = (1 << (24 + NUM_BITS));
static bool inline Test86MSByte(Byte b)
{
return (((b) + SIGN_BIT) & MASK_HIGH) == 0;
}
CEncoder::~CEncoder()
{
::MidFree(_buf);
}
HRESULT CEncoder::Flush()
{
RINOK(_mainStream.Flush());
RINOK(_callStream.Flush());
RINOK(_jumpStream.Flush());
_rc.FlushData();
return _rc.FlushStream();
}
HRESULT CEncoder::CodeReal(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams,
ISequentialOutStream **outStreams, const UInt64 ** /* outSizes */, UInt32 numOutStreams,
ICompressProgressInfo *progress)
{
if (numInStreams != 1 || numOutStreams != 4)
return E_INVALIDARG;
if (!_mainStream.Create(1 << 18)) return E_OUTOFMEMORY;
if (!_callStream.Create(1 << 18)) return E_OUTOFMEMORY;
if (!_jumpStream.Create(1 << 18)) return E_OUTOFMEMORY;
if (!_rc.Create(1 << 20)) return E_OUTOFMEMORY;
if (_buf == 0)
{
_buf = (Byte *)MidAlloc(kBufSize);
if (_buf == 0)
return E_OUTOFMEMORY;
}
bool sizeIsDefined = false;
UInt64 inSize = 0;
if (inSizes)
if (inSizes[0])
{
inSize = *inSizes[0];
if (inSize <= kDefaultLimit)
sizeIsDefined = true;
}
ISequentialInStream *inStream = inStreams[0];
_mainStream.SetStream(outStreams[0]); _mainStream.Init();
_callStream.SetStream(outStreams[1]); _callStream.Init();
_jumpStream.SetStream(outStreams[2]); _jumpStream.Init();
_rc.SetStream(outStreams[3]); _rc.Init();
for (unsigned i = 0; i < 256 + 2; i++)
_statusEncoder[i].Init();
CMyComPtr<ICompressGetSubStreamSize> getSubStreamSize;
{
inStream->QueryInterface(IID_ICompressGetSubStreamSize, (void **)&getSubStreamSize);
}
UInt32 nowPos = 0;
UInt64 nowPos64 = 0;
UInt32 bufPos = 0;
Byte prevByte = 0;
UInt64 subStreamIndex = 0;
UInt64 subStreamStartPos = 0;
UInt64 subStreamEndPos = 0;
for (;;)
{
UInt32 processedSize = 0;
for (;;)
{
UInt32 size = kBufSize - (bufPos + processedSize);
UInt32 processedSizeLoc;
if (size == 0)
break;
RINOK(inStream->Read(_buf + bufPos + processedSize, size, &processedSizeLoc));
if (processedSizeLoc == 0)
break;
processedSize += processedSizeLoc;
}
UInt32 endPos = bufPos + processedSize;
if (endPos < 5)
{
// change it
for (bufPos = 0; bufPos < endPos; bufPos++)
{
Byte b = _buf[bufPos];
_mainStream.WriteByte(b);
UInt32 index;
if (b == 0xE8)
index = prevByte;
else if (b == 0xE9)
index = 256;
else if (IsJcc(prevByte, b))
index = 257;
else
{
prevByte = b;
continue;
}
_statusEncoder[index].Encode(&_rc, 0);
prevByte = b;
}
return Flush();
}
bufPos = 0;
UInt32 limit = endPos - 5;
while (bufPos <= limit)
{
Byte b = _buf[bufPos];
_mainStream.WriteByte(b);
if (!IsJ(prevByte, b))
{
bufPos++;
prevByte = b;
continue;
}
Byte nextByte = _buf[bufPos + 4];
UInt32 src =
(UInt32(nextByte) << 24) |
(UInt32(_buf[bufPos + 3]) << 16) |
(UInt32(_buf[bufPos + 2]) << 8) |
(_buf[bufPos + 1]);
UInt32 dest = (nowPos + bufPos + 5) + src;
// if (Test86MSByte(nextByte))
bool convert;
if (getSubStreamSize)
{
UInt64 currentPos = (nowPos64 + bufPos);
while (subStreamEndPos < currentPos)
{
UInt64 subStreamSize;
HRESULT result = getSubStreamSize->GetSubStreamSize(subStreamIndex, &subStreamSize);
if (result == S_OK)
{
subStreamStartPos = subStreamEndPos;
subStreamEndPos += subStreamSize;
subStreamIndex++;
}
else if (result == S_FALSE || result == E_NOTIMPL)
{
getSubStreamSize.Release();
subStreamStartPos = 0;
subStreamEndPos = subStreamStartPos - 1;
}
else
return result;
}
if (getSubStreamSize == NULL)
{
if (sizeIsDefined)
convert = (dest < inSize);
else
convert = Test86MSByte(nextByte);
}
else if (subStreamEndPos - subStreamStartPos > kDefaultLimit)
convert = Test86MSByte(nextByte);
else
{
UInt64 dest64 = (currentPos + 5) + Int64(Int32(src));
convert = (dest64 >= subStreamStartPos && dest64 < subStreamEndPos);
}
}
else if (sizeIsDefined)
convert = (dest < inSize);
else
convert = Test86MSByte(nextByte);
unsigned index = GetIndex(prevByte, b);
if (convert)
{
_statusEncoder[index].Encode(&_rc, 1);
bufPos += 5;
COutBuffer &s = (b == 0xE8) ? _callStream : _jumpStream;
for (int i = 24; i >= 0; i -= 8)
s.WriteByte((Byte)(dest >> i));
prevByte = nextByte;
}
else
{
_statusEncoder[index].Encode(&_rc, 0);
bufPos++;
prevByte = b;
}
}
nowPos += bufPos;
nowPos64 += bufPos;
if (progress)
{
/*
const UInt64 compressedSize =
_mainStream.GetProcessedSize() +
_callStream.GetProcessedSize() +
_jumpStream.GetProcessedSize() +
_rc.GetProcessedSize();
*/
RINOK(progress->SetRatioInfo(&nowPos64, NULL));
}
UInt32 i = 0;
while (bufPos < endPos)
_buf[i++] = _buf[bufPos++];
bufPos = i;
}
}
STDMETHODIMP CEncoder::Code(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams,
ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams,
ICompressProgressInfo *progress)
{
try
{
return CodeReal(inStreams, inSizes, numInStreams, outStreams, outSizes,numOutStreams, progress);
}
catch(const COutBufferException &e) { return e.ErrorCode; }
catch(...) { return S_FALSE; }
}
#endif
STDMETHODIMP CDecoder::SetInBufSize(UInt32 streamIndex, UInt32 size) { _inBufSizes[streamIndex] = size; return S_OK; }
STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; }
CDecoder::CDecoder():
_outBufSize(1 << 16)
{
_inBufSizes[0] = 1 << 20;
_inBufSizes[1] = 1 << 20;
_inBufSizes[2] = 1 << 20;
_inBufSizes[3] = 1 << 20;
}
HRESULT CDecoder::CodeReal(ISequentialInStream **inStreams, const UInt64 ** /* inSizes */, UInt32 numInStreams,
ISequentialOutStream **outStreams, const UInt64 ** /* outSizes */, UInt32 numOutStreams,
ICompressProgressInfo *progress)
{
if (numInStreams != 4 || numOutStreams != 1)
return E_INVALIDARG;
if (!_mainStream.Create(_inBufSizes[0])) return E_OUTOFMEMORY;
if (!_callStream.Create(_inBufSizes[1])) return E_OUTOFMEMORY;
if (!_jumpStream.Create(_inBufSizes[2])) return E_OUTOFMEMORY;
if (!_rc.Create(_inBufSizes[3])) return E_OUTOFMEMORY;
if (!_outStream.Create(_outBufSize)) return E_OUTOFMEMORY;
_mainStream.SetStream(inStreams[0]);
_callStream.SetStream(inStreams[1]);
_jumpStream.SetStream(inStreams[2]);
_rc.SetStream(inStreams[3]);
_outStream.SetStream(outStreams[0]);
_mainStream.Init();
_callStream.Init();
_jumpStream.Init();
_rc.Init();
_outStream.Init();
for (unsigned i = 0; i < 256 + 2; i++)
_statusDecoder[i].Init();
Byte prevByte = 0;
UInt32 processedBytes = 0;
for (;;)
{
if (processedBytes >= (1 << 20) && progress)
{
/*
const UInt64 compressedSize =
_mainStream.GetProcessedSize() +
_callStream.GetProcessedSize() +
_jumpStream.GetProcessedSize() +
_rc.GetProcessedSize();
*/
const UInt64 nowPos64 = _outStream.GetProcessedSize();
RINOK(progress->SetRatioInfo(NULL, &nowPos64));
processedBytes = 0;
}
UInt32 i;
Byte b = 0;
const UInt32 kBurstSize = (1 << 18);
for (i = 0; i < kBurstSize; i++)
{
if (!_mainStream.ReadByte(b))
return _outStream.Flush();
_outStream.WriteByte(b);
if (IsJ(prevByte, b))
break;
prevByte = b;
}
processedBytes += i;
if (i == kBurstSize)
continue;
unsigned index = GetIndex(prevByte, b);
if (_statusDecoder[index].Decode(&_rc) == 1)
{
UInt32 src = 0;
CInBuffer &s = (b == 0xE8) ? _callStream : _jumpStream;
for (unsigned i = 0; i < 4; i++)
{
Byte b0;
if (!s.ReadByte(b0))
return S_FALSE;
src <<= 8;
src |= ((UInt32)b0);
}
UInt32 dest = src - (UInt32(_outStream.GetProcessedSize()) + 4) ;
_outStream.WriteByte((Byte)(dest));
_outStream.WriteByte((Byte)(dest >> 8));
_outStream.WriteByte((Byte)(dest >> 16));
_outStream.WriteByte((Byte)(dest >> 24));
prevByte = (Byte)(dest >> 24);
processedBytes += 4;
}
else
prevByte = b;
}
}
STDMETHODIMP CDecoder::Code(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams,
ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams,
ICompressProgressInfo *progress)
{
try
{
return CodeReal(inStreams, inSizes, numInStreams, outStreams, outSizes,numOutStreams, progress);
}
catch(const CInBufferException &e) { return e.ErrorCode; }
catch(const COutBufferException &e) { return e.ErrorCode; }
catch(...) { return S_FALSE; }
}
}}