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