Source code

Revision control

Copy as Markdown

Other Tools

// Bcj2Coder.cpp↩
#include "StdAfx.h"
#include "../../../C/Alloc.h"
#include "../Common/StreamUtils.h"
#include "Bcj2Coder.h"
namespace NCompress {↩
namespace NBcj2 {↩
CBaseCoder::CBaseCoder()↩
{↩
for (int i = 0; i < BCJ2_NUM_STREAMS + 1; i++)↩
{↩
_bufs[i] = NULL;↩
_bufsCurSizes[i] = 0;↩
_bufsNewSizes[i] = (1 << 18);↩
}↩
}↩
CBaseCoder::~CBaseCoder()↩
{↩
for (int i = 0; i < BCJ2_NUM_STREAMS + 1; i++)↩
::MidFree(_bufs[i]);↩
}↩
HRESULT CBaseCoder::Alloc(bool allocForOrig)↩
{↩
unsigned num = allocForOrig ? BCJ2_NUM_STREAMS + 1 : BCJ2_NUM_STREAMS;↩
for (unsigned i = 0; i < num; i++)↩
{↩
UInt32 newSize = _bufsNewSizes[i];↩
const UInt32 kMinBufSize = 1;↩
if (newSize < kMinBufSize)↩
newSize = kMinBufSize;↩
if (!_bufs[i] || newSize != _bufsCurSizes[i])↩
{↩
if (_bufs[i])↩
{↩
::MidFree(_bufs[i]);↩
_bufs[i] = 0;↩
}↩
_bufsCurSizes[i] = 0;↩
Byte *buf = (Byte *)::MidAlloc(newSize);↩
_bufs[i] = buf;↩
if (!buf)↩
return E_OUTOFMEMORY;↩
_bufsCurSizes[i] = newSize;↩
}↩
}↩
return S_OK;↩
}↩
#ifndef EXTRACT_ONLY↩
CEncoder::CEncoder(): _relatLim(BCJ2_RELAT_LIMIT) {}↩
CEncoder::~CEncoder() {}↩
STDMETHODIMP CEncoder::SetInBufSize(UInt32, UInt32 size) { _bufsNewSizes[BCJ2_NUM_STREAMS] = size; return S_OK; }↩
STDMETHODIMP CEncoder::SetOutBufSize(UInt32 streamIndex, UInt32 size) { _bufsNewSizes[streamIndex] = size; return S_OK; }↩
STDMETHODIMP CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps)↩
{↩
UInt32 relatLim = BCJ2_RELAT_LIMIT;↩
for (UInt32 i = 0; i < numProps; i++)↩
{↩
const PROPVARIANT &prop = props[i];↩
PROPID propID = propIDs[i];↩
if (propID >= NCoderPropID::kReduceSize)↩
continue;↩
switch (propID)↩
{↩
/*↩
case NCoderPropID::kDefaultProp:↩
{↩
if (prop.vt != VT_UI4)↩
return E_INVALIDARG;↩
UInt32 v = prop.ulVal;↩
if (v > 31)↩
return E_INVALIDARG;↩
relatLim = (UInt32)1 << v;↩
break;↩
}↩
*/
case NCoderPropID::kDictionarySize:↩
{↩
if (prop.vt != VT_UI4)↩
return E_INVALIDARG;↩
relatLim = prop.ulVal;↩
if (relatLim > ((UInt32)1 << 31))↩
return E_INVALIDARG;↩
break;↩
}↩
case NCoderPropID::kNumThreads:↩
continue;↩
case NCoderPropID::kLevel:↩
continue;↩
default: return E_INVALIDARG;↩
}↩
}↩
_relatLim = relatLim;↩
return S_OK;↩
}↩
HRESULT CEncoder::CodeReal(ISequentialInStream * const *inStreams, const UInt64 * const *inSizes, UInt32 numInStreams,↩
ISequentialOutStream * const *outStreams, const UInt64 * const * /* outSizes */, UInt32 numOutStreams,↩
ICompressProgressInfo *progress)↩
{↩
if (numInStreams != 1 || numOutStreams != BCJ2_NUM_STREAMS)↩
return E_INVALIDARG;↩
RINOK(Alloc());↩
UInt32 fileSize_for_Conv = 0;↩
if (inSizes && inSizes[0])↩
{↩
UInt64 inSize = *inSizes[0];↩
if (inSize <= BCJ2_FileSize_MAX)↩
fileSize_for_Conv = (UInt32)inSize;↩
}↩
CMyComPtr<ICompressGetSubStreamSize> getSubStreamSize;↩
inStreams[0]->QueryInterface(IID_ICompressGetSubStreamSize, (void **)&getSubStreamSize);↩
CBcj2Enc enc;↩
enc.src = _bufs[BCJ2_NUM_STREAMS];↩
enc.srcLim = enc.src;↩
{↩
for (int i = 0; i < BCJ2_NUM_STREAMS; i++)↩
{↩
enc.bufs[i] = _bufs[i];↩
enc.lims[i] = _bufs[i] + _bufsCurSizes[i];↩
}↩
}↩
size_t numBytes_in_ReadBuf = 0;↩
UInt64 prevProgress = 0;↩
UInt64 totalStreamRead = 0; // size read from InputStream↩
UInt64 currentInPos = 0; // data that was processed, it doesn't include data in input buffer and data in enc.temp↩
UInt64 outSizeRc = 0;↩
Bcj2Enc_Init(&enc);↩
enc.fileIp = 0;↩
enc.fileSize = fileSize_for_Conv;↩
enc.relatLimit = _relatLim;↩
enc.finishMode = BCJ2_ENC_FINISH_MODE_CONTINUE;↩
bool needSubSize = false;↩
UInt64 subStreamIndex = 0;↩
UInt64 subStreamStartPos = 0;↩
bool readWasFinished = false;↩
for (;;)↩
{↩
if (needSubSize && getSubStreamSize)↩
{↩
enc.fileIp = 0;↩
enc.fileSize = fileSize_for_Conv;↩
enc.finishMode = BCJ2_ENC_FINISH_MODE_CONTINUE;↩
for (;;)↩
{↩
UInt64 subStreamSize = 0;↩
HRESULT result = getSubStreamSize->GetSubStreamSize(subStreamIndex, &subStreamSize);↩
needSubSize = false;↩
if (result == S_OK)↩
{↩
UInt64 newEndPos = subStreamStartPos + subStreamSize;↩
bool isAccurateEnd = (newEndPos < totalStreamRead ||↩
(newEndPos <= totalStreamRead && readWasFinished));↩
if (newEndPos <= currentInPos && isAccurateEnd)↩
{↩
subStreamStartPos = newEndPos;↩
subStreamIndex++;↩
continue;↩
}↩
enc.srcLim = _bufs[BCJ2_NUM_STREAMS] + numBytes_in_ReadBuf;↩
if (isAccurateEnd)↩
{↩
// data in enc.temp is possible here↩
size_t rem = (size_t)(totalStreamRead - newEndPos);↩
/* Pos_of(enc.src) <= old newEndPos <= newEndPos↩
in another case, it's fail in some code */
if ((size_t)(enc.srcLim - enc.src) < rem)↩
return E_FAIL;↩
enc.srcLim -= rem;↩
enc.finishMode = BCJ2_ENC_FINISH_MODE_END_BLOCK;↩
}↩
if (subStreamSize <= BCJ2_FileSize_MAX)↩
{↩
enc.fileIp = enc.ip + (UInt32)(subStreamStartPos - currentInPos);↩
enc.fileSize = (UInt32)subStreamSize;↩
}↩
break;↩
}↩
if (result == S_FALSE)↩
break;↩
if (result == E_NOTIMPL)↩
{↩
getSubStreamSize.Release();↩
break;↩
}↩
return result;↩
}↩
}↩
if (readWasFinished && totalStreamRead - currentInPos == Bcj2Enc_Get_InputData_Size(&enc))↩
enc.finishMode = BCJ2_ENC_FINISH_MODE_END_STREAM;↩
Bcj2Enc_Encode(&enc);↩
currentInPos = totalStreamRead - numBytes_in_ReadBuf + (enc.src - _bufs[BCJ2_NUM_STREAMS]) - enc.tempPos;↩
if (Bcj2Enc_IsFinished(&enc))↩
break;↩
if (enc.state < BCJ2_NUM_STREAMS)↩
{↩
size_t curSize = enc.bufs[enc.state] - _bufs[enc.state];↩
// printf("Write stream = %2d %6d\n", enc.state, curSize);↩
RINOK(WriteStream(outStreams[enc.state], _bufs[enc.state], curSize));↩
if (enc.state == BCJ2_STREAM_RC)↩
outSizeRc += curSize;↩
enc.bufs[enc.state] = _bufs[enc.state];↩
enc.lims[enc.state] = _bufs[enc.state] + _bufsCurSizes[enc.state];↩
}↩
else if (enc.state != BCJ2_ENC_STATE_ORIG)↩
return E_FAIL;↩
else
{↩
needSubSize = true;↩
if (numBytes_in_ReadBuf != (size_t)(enc.src - _bufs[BCJ2_NUM_STREAMS]))↩
{↩
enc.srcLim = _bufs[BCJ2_NUM_STREAMS] + numBytes_in_ReadBuf;↩
continue;↩
}↩
if (readWasFinished)↩
continue;↩
numBytes_in_ReadBuf = 0;↩
enc.src = _bufs[BCJ2_NUM_STREAMS];↩
enc.srcLim = _bufs[BCJ2_NUM_STREAMS];↩
UInt32 curSize = _bufsCurSizes[BCJ2_NUM_STREAMS];↩
RINOK(inStreams[0]->Read(_bufs[BCJ2_NUM_STREAMS], curSize, &curSize));↩
// printf("Read %6d bytes\n", curSize);↩
if (curSize == 0)↩
{↩
readWasFinished = true;↩
continue;↩
}↩
numBytes_in_ReadBuf = curSize;↩
totalStreamRead += numBytes_in_ReadBuf;↩
enc.srcLim = _bufs[BCJ2_NUM_STREAMS] + numBytes_in_ReadBuf;↩
}↩
if (progress && currentInPos - prevProgress >= (1 << 20))↩
{↩
UInt64 outSize2 = currentInPos + outSizeRc + enc.bufs[BCJ2_STREAM_RC] - enc.bufs[BCJ2_STREAM_RC];↩
prevProgress = currentInPos;↩
// printf("progress %8d, %8d\n", (int)inSize2, (int)outSize2);↩
RINOK(progress->SetRatioInfo(&currentInPos, &outSize2));↩
}↩
}↩
for (int i = 0; i < BCJ2_NUM_STREAMS; i++)↩
{↩
RINOK(WriteStream(outStreams[i], _bufs[i], enc.bufs[i] - _bufs[i]));↩
}↩
// if (currentInPos != subStreamStartPos + subStreamSize) return E_FAIL;↩
return S_OK;↩
}↩
STDMETHODIMP CEncoder::Code(ISequentialInStream * const *inStreams, const UInt64 * const *inSizes, UInt32 numInStreams,↩
ISequentialOutStream * const *outStreams, const UInt64 * const *outSizes, UInt32 numOutStreams,↩
ICompressProgressInfo *progress)↩
{↩
try
{↩
return CodeReal(inStreams, inSizes, numInStreams, outStreams, outSizes,numOutStreams, progress);↩
}↩
catch(...) { return E_FAIL; }↩
}↩
#endif
STDMETHODIMP CDecoder::SetInBufSize(UInt32 streamIndex, UInt32 size) { _bufsNewSizes[streamIndex] = size; return S_OK; }↩
STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _bufsNewSizes[BCJ2_NUM_STREAMS] = size; return S_OK; }↩
CDecoder::CDecoder(): _finishMode(false), _outSizeDefined(false), _outSize(0)↩
{}↩
STDMETHODIMP CDecoder::SetFinishMode(UInt32 finishMode)↩
{↩
_finishMode = (finishMode != 0);↩
return S_OK;↩
}↩
void CDecoder::InitCommon()↩
{↩
{↩
for (int i = 0; i < BCJ2_NUM_STREAMS; i++)↩
dec.lims[i] = dec.bufs[i] = _bufs[i];↩
}↩
{↩
for (int i = 0; i < BCJ2_NUM_STREAMS; i++)↩
{↩
_extraReadSizes[i] = 0;↩
_inStreamsProcessed[i] = 0;↩
_readRes[i] = S_OK;↩
}↩
}↩
Bcj2Dec_Init(&dec);↩
}↩
HRESULT CDecoder::Code(ISequentialInStream * const *inStreams, const UInt64 * const *inSizes, UInt32 numInStreams,↩
ISequentialOutStream * const *outStreams, const UInt64 * const *outSizes, UInt32 numOutStreams,↩
ICompressProgressInfo *progress)↩
{↩
if (numInStreams != BCJ2_NUM_STREAMS || numOutStreams != 1)↩
return E_INVALIDARG;↩
RINOK(Alloc());↩
InitCommon();↩
dec.destLim = dec.dest = _bufs[BCJ2_NUM_STREAMS];↩
UInt64 outSizeProcessed = 0;↩
UInt64 prevProgress = 0;↩
HRESULT res = S_OK;↩
for (;;)↩
{↩
if (Bcj2Dec_Decode(&dec) != SZ_OK)↩
return S_FALSE;↩
if (dec.state < BCJ2_NUM_STREAMS)↩
{↩
size_t totalRead = _extraReadSizes[dec.state];↩
{↩
Byte *buf = _bufs[dec.state];↩
for (size_t i = 0; i < totalRead; i++)↩
buf[i] = dec.bufs[dec.state][i];↩
dec.lims[dec.state] =↩
dec.bufs[dec.state] = buf;↩
}↩
if (_readRes[dec.state] != S_OK)↩
{↩
res = _readRes[dec.state];↩
break;↩
}↩
do
{↩
UInt32 curSize = _bufsCurSizes[dec.state] - (UInt32)totalRead;↩
/*↩
we want to call Read even even if size is 0↩
if (inSizes && inSizes[dec.state])↩
{↩
UInt64 rem = *inSizes[dec.state] - _inStreamsProcessed[dec.state];↩
if (curSize > rem)↩
curSize = (UInt32)rem;↩
}↩
*/
HRESULT res2 = inStreams[dec.state]->Read(_bufs[dec.state] + totalRead, curSize, &curSize);↩
_readRes[dec.state] = res2;↩
if (curSize == 0)↩
break;↩
_inStreamsProcessed[dec.state] += curSize;↩
totalRead += curSize;↩
if (res2 != S_OK)↩
break;↩
}↩
while (totalRead < 4 && BCJ2_IS_32BIT_STREAM(dec.state));↩
if (_readRes[dec.state] != S_OK)↩
res = _readRes[dec.state];↩
if (totalRead == 0)↩
break;↩
// res == S_OK;↩
if (BCJ2_IS_32BIT_STREAM(dec.state))↩
{↩
unsigned extraSize = ((unsigned)totalRead & 3);↩
_extraReadSizes[dec.state] = extraSize;↩
if (totalRead < 4)↩
{↩
res = (_readRes[dec.state] != S_OK) ? _readRes[dec.state] : S_FALSE;↩
break;↩
}↩
totalRead -= extraSize;↩
}↩
dec.lims[dec.state] = _bufs[dec.state] + totalRead;↩
}↩
else // if (dec.state <= BCJ2_STATE_ORIG)↩
{↩
size_t curSize = dec.dest - _bufs[BCJ2_NUM_STREAMS];↩
if (curSize != 0)↩
{↩
outSizeProcessed += curSize;↩
RINOK(WriteStream(outStreams[0], _bufs[BCJ2_NUM_STREAMS], curSize));↩
}↩
dec.dest = _bufs[BCJ2_NUM_STREAMS];↩
{↩
size_t rem = _bufsCurSizes[BCJ2_NUM_STREAMS];↩
if (outSizes && outSizes[0])↩
{↩
UInt64 outSize = *outSizes[0] - outSizeProcessed;↩
if (rem > outSize)↩
rem = (size_t)outSize;↩
}↩
dec.destLim = dec.dest + rem;↩
if (rem == 0)↩
break;↩
}↩
}↩
if (progress)↩
{↩
const UInt64 outSize2 = outSizeProcessed + (dec.dest - _bufs[BCJ2_NUM_STREAMS]);↩
if (outSize2 - prevProgress >= (1 << 22))↩
{↩
const UInt64 inSize2 = outSize2 + _inStreamsProcessed[BCJ2_STREAM_RC] - (dec.lims[BCJ2_STREAM_RC] - dec.bufs[BCJ2_STREAM_RC]);↩
RINOK(progress->SetRatioInfo(&inSize2, &outSize2));↩
prevProgress = outSize2;↩
}↩
}↩
}↩
size_t curSize = dec.dest - _bufs[BCJ2_NUM_STREAMS];↩
if (curSize != 0)↩
{↩
outSizeProcessed += curSize;↩
RINOK(WriteStream(outStreams[0], _bufs[BCJ2_NUM_STREAMS], curSize));↩
}↩
if (res != S_OK)↩
return res;↩
if (_finishMode)↩
{↩
if (!Bcj2Dec_IsFinished(&dec))↩
return S_FALSE;↩
// we still allow the cases when input streams are larger than required for decoding.↩
// so the case (dec.state == BCJ2_STATE_ORIG) is also allowed, if MAIN stream is larger than required.↩
if (dec.state != BCJ2_STREAM_MAIN &&↩
dec.state != BCJ2_DEC_STATE_ORIG)↩
return S_FALSE;↩
if (inSizes)↩
{↩
for (int i = 0; i < BCJ2_NUM_STREAMS; i++)↩
{↩
size_t rem = dec.lims[i] - dec.bufs[i] + _extraReadSizes[i];↩
/*↩
if (rem != 0)↩
return S_FALSE;↩
*/
if (inSizes[i] && *inSizes[i] != _inStreamsProcessed[i] - rem)↩
return S_FALSE;↩
}↩
}↩
}↩
return S_OK;↩
}↩
STDMETHODIMP CDecoder::SetInStream2(UInt32 streamIndex, ISequentialInStream *inStream)↩
{↩
_inStreams[streamIndex] = inStream;↩
return S_OK;↩
}↩
STDMETHODIMP CDecoder::ReleaseInStream2(UInt32 streamIndex)↩
{↩
_inStreams[streamIndex].Release();↩
return S_OK;↩
}↩
STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize)↩
{↩
_outSizeDefined = (outSize != NULL);↩
_outSize = 0;↩
if (_outSizeDefined)↩
_outSize = *outSize;↩
_outSize_Processed = 0;↩
HRESULT res = Alloc(false);↩
InitCommon();↩
dec.destLim = dec.dest = NULL;↩
return res;↩
}↩
STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)↩
{↩
if (processedSize)↩
*processedSize = 0;↩
if (size == 0)↩
return S_OK;↩
UInt32 totalProcessed = 0;↩
if (_outSizeDefined)↩
{↩
UInt64 rem = _outSize - _outSize_Processed;↩
if (size > rem)↩
size = (UInt32)rem;↩
}↩
dec.dest = (Byte *)data;↩
dec.destLim = (const Byte *)data + size;↩
HRESULT res = S_OK;↩
for (;;)↩
{↩
SRes sres = Bcj2Dec_Decode(&dec);↩
if (sres != SZ_OK)↩
return S_FALSE;↩
{↩
UInt32 curSize = (UInt32)(dec.dest - (Byte *)data);↩
if (curSize != 0)↩
{↩
totalProcessed += curSize;↩
if (processedSize)↩
*processedSize = totalProcessed;↩
data = (void *)((Byte *)data + curSize);↩
size -= curSize;↩
_outSize_Processed += curSize;↩
}↩
}↩
if (dec.state >= BCJ2_NUM_STREAMS)↩
break;↩
{↩
size_t totalRead = _extraReadSizes[dec.state];↩
{↩
Byte *buf = _bufs[dec.state];↩
for (size_t i = 0; i < totalRead; i++)↩
buf[i] = dec.bufs[dec.state][i];↩
dec.lims[dec.state] =↩
dec.bufs[dec.state] = buf;↩
}↩
if (_readRes[dec.state] != S_OK)↩
return _readRes[dec.state];↩
do
{↩
UInt32 curSize = _bufsCurSizes[dec.state] - (UInt32)totalRead;↩
HRESULT res2 = _inStreams[dec.state]->Read(_bufs[dec.state] + totalRead, curSize, &curSize);↩
_readRes[dec.state] = res2;↩
if (curSize == 0)↩
break;↩
_inStreamsProcessed[dec.state] += curSize;↩
totalRead += curSize;↩
if (res2 != S_OK)↩
break;↩
}↩
while (totalRead < 4 && BCJ2_IS_32BIT_STREAM(dec.state));↩
if (totalRead == 0)↩
{↩
if (totalProcessed == 0)↩
res = _readRes[dec.state];↩
break;↩
}↩
if (BCJ2_IS_32BIT_STREAM(dec.state))↩
{↩
unsigned extraSize = ((unsigned)totalRead & 3);↩
_extraReadSizes[dec.state] = extraSize;↩
if (totalRead < 4)↩
{↩
if (totalProcessed != 0)↩
return S_OK;↩
return (_readRes[dec.state] != S_OK) ? _readRes[dec.state] : S_FALSE;↩
}↩
totalRead -= extraSize;↩
}↩
dec.lims[dec.state] = _bufs[dec.state] + totalRead;↩
}↩
}↩
if (_finishMode && _outSizeDefined && _outSize == _outSize_Processed)↩
{↩
if (!Bcj2Dec_IsFinished(&dec))↩
return S_FALSE;↩
if (dec.state != BCJ2_STREAM_MAIN &&↩
dec.state != BCJ2_DEC_STATE_ORIG)↩
return S_FALSE;↩
/*↩
for (int i = 0; i < BCJ2_NUM_STREAMS; i++)↩
if (dec.bufs[i] != dec.lims[i] || _extraReadSizes[i] != 0)↩
return S_FALSE;↩
*/
}↩
return res;↩
}↩
STDMETHODIMP CDecoder::GetInStreamProcessedSize2(UInt32 streamIndex, UInt64 *value)↩
{↩
const size_t rem = dec.lims[streamIndex] - dec.bufs[streamIndex] + _extraReadSizes[streamIndex];↩
*value = _inStreamsProcessed[streamIndex] - rem;↩
return S_OK;↩
}↩
}}↩