// 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; } | |
} | |
}} |