// 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(¤tInPos, &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) | |
{ | |
UInt64 outSize2 = outSizeProcessed + (dec.dest - _bufs[BCJ2_NUM_STREAMS]); | |
if (outSize2 - prevProgress >= (1 << 22)) | |
{ | |
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; | |
} | |
}} |