blob: 261a626c0cad28e5e8aae64a6e2eb4d1a70cc036 [file] [log] [blame]
// 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)
{
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;
}
}}