Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/kornelski/7z.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'CPP/7zip/Common/FilterCoder.cpp')
-rw-r--r--CPP/7zip/Common/FilterCoder.cpp435
1 files changed, 294 insertions, 141 deletions
diff --git a/CPP/7zip/Common/FilterCoder.cpp b/CPP/7zip/Common/FilterCoder.cpp
index 578a3193..4f3ae4e7 100644
--- a/CPP/7zip/Common/FilterCoder.cpp
+++ b/CPP/7zip/Common/FilterCoder.cpp
@@ -2,90 +2,144 @@
#include "StdAfx.h"
-#include "../../../C/Alloc.h"
-
#include "../../Common/Defs.h"
#include "FilterCoder.h"
#include "StreamUtils.h"
-static const UInt32 kBufferSize = 1 << 17;
+/*
+ AES filters need 16-bytes alignment for HARDWARE-AES instructions.
+ So we call IFilter::Filter(, size), where (size != 16 * N) only for last data block.
+
+ AES-CBC filters need data size aligned for 16-bytes.
+ So the encoder can add zeros to the end of original stream.
+
+ Some filters (BCJ and others) don't process data at the end of stream in some cases.
+ So the encoder and decoder write such last bytes without change.
+*/
+
+
+static const UInt32 kBufSize = 1 << 20;
-CFilterCoder::CFilterCoder()
+STDMETHODIMP CFilterCoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSize = size; return S_OK; }
+STDMETHODIMP CFilterCoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; }
+
+HRESULT CFilterCoder::Alloc()
{
- _buffer = (Byte *)::MidAlloc(kBufferSize);
- if (_buffer == 0)
- throw 1;
+ UInt32 size = MyMin(_inBufSize, _outBufSize);
+ /* minimal bufSize is 16 bytes for AES and IA64 filter.
+ bufSize for AES must be aligned for 16 bytes.
+ We use (1 << 12) min size to support future aligned filters. */
+ const UInt32 kMinSize = 1 << 12;
+ size &= ~(UInt32)(kMinSize - 1);
+ if (size < kMinSize)
+ size = kMinSize;
+ if (!_buf || _bufSize != size)
+ {
+ AllocAlignedMask(size, 16 - 1);
+ if (!_buf)
+ return E_OUTOFMEMORY;
+ _bufSize = size;
+ }
+ return S_OK;
}
-CFilterCoder::~CFilterCoder()
+HRESULT CFilterCoder::Init_and_Alloc()
{
- ::MidFree(_buffer);
+ RINOK(Filter->Init());
+ return Alloc();
}
-HRESULT CFilterCoder::WriteWithLimit(ISequentialOutStream *outStream, UInt32 size)
+CFilterCoder::CFilterCoder(bool encodeMode):
+ _bufSize(0),
+ _inBufSize(kBufSize),
+ _outBufSize(kBufSize),
+ _encodeMode(encodeMode),
+ _outSizeIsDefined(false),
+ _outSize(0),
+ _nowPos64(0)
+ {}
+
+CFilterCoder::~CFilterCoder()
{
- if (_outSizeIsDefined)
- {
- UInt64 remSize = _outSize - _nowPos64;
- if (size > remSize)
- size = (UInt32)remSize;
- }
- RINOK(WriteStream(outStream, _buffer, size));
- _nowPos64 += size;
- return S_OK;
}
STDMETHODIMP CFilterCoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
{
- RINOK(Init());
- UInt32 bufferPos = 0;
- _outSizeIsDefined = (outSize != 0);
- if (_outSizeIsDefined)
- _outSize = *outSize;
+ RINOK(Init_and_Alloc());
+
+ UInt64 nowPos64 = 0;
+ bool inputFinished = false;
+ UInt32 pos = 0;
- while (!_outSizeIsDefined || _nowPos64 < _outSize)
+ while (!outSize || nowPos64 < *outSize)
{
- size_t processedSize = kBufferSize - bufferPos;
-
- // Change it: It can be optimized using ReadPart
- RINOK(ReadStream(inStream, _buffer + bufferPos, &processedSize));
+ UInt32 endPos = pos;
- UInt32 endPos = bufferPos + (UInt32)processedSize;
-
- bufferPos = Filter->Filter(_buffer, endPos);
- if (bufferPos > endPos)
+ if (!inputFinished)
{
- for (; endPos < bufferPos; endPos++)
- _buffer[endPos] = 0;
- bufferPos = Filter->Filter(_buffer, endPos);
+ size_t processedSize = _bufSize - pos;
+ RINOK(ReadStream(inStream, _buf + pos, &processedSize));
+ endPos = pos + (UInt32)processedSize;
+ inputFinished = (endPos != _bufSize);
}
- if (bufferPos == 0)
+ pos = Filter->Filter(_buf, endPos);
+
+ if (pos > endPos)
{
- if (endPos == 0)
- return S_OK;
- return WriteWithLimit(outStream, endPos);
+ // AES
+ if (!inputFinished || pos > _bufSize)
+ return E_FAIL;
+ if (!_encodeMode)
+ return S_FALSE;
+
+ do
+ _buf[endPos] = 0;
+ while (++endPos != pos);
+
+ if (pos != Filter->Filter(_buf, pos))
+ return E_FAIL;
}
- RINOK(WriteWithLimit(outStream, bufferPos));
- if (progress != NULL)
+
+ if (endPos == 0)
+ return S_OK;
+
+ UInt32 size = (pos != 0 ? pos : endPos);
+ if (outSize)
{
- RINOK(progress->SetRatioInfo(&_nowPos64, &_nowPos64));
+ UInt64 remSize = *outSize - nowPos64;
+ if (size > remSize)
+ size = (UInt32)remSize;
}
+
+ RINOK(WriteStream(outStream, _buf, size));
+ nowPos64 += size;
+
+ if (pos == 0)
+ return S_OK;
+
+ if (progress)
+ RINOK(progress->SetRatioInfo(&nowPos64, &nowPos64));
+
UInt32 i = 0;
- while (bufferPos < endPos)
- _buffer[i++] = _buffer[bufferPos++];
- bufferPos = i;
+ while (pos < endPos)
+ _buf[i++] = _buf[pos++];
+ pos = i;
}
+
return S_OK;
}
+
+
+// ---------- Write to Filter ----------
+
STDMETHODIMP CFilterCoder::SetOutStream(ISequentialOutStream *outStream)
{
- _bufferPos = 0;
_outStream = outStream;
- return Init();
+ return S_OK;
}
STDMETHODIMP CFilterCoder::ReleaseOutStream()
@@ -94,76 +148,154 @@ STDMETHODIMP CFilterCoder::ReleaseOutStream()
return S_OK;
}
+HRESULT CFilterCoder::Flush2()
+{
+ while (_convSize != 0)
+ {
+ UInt32 num = _convSize;
+ if (_outSizeIsDefined)
+ {
+ UInt64 rem = _outSize - _nowPos64;
+ if (num > rem)
+ num = (UInt32)rem;
+ if (num == 0)
+ return k_My_HRESULT_WritingWasCut;
+ }
+
+ UInt32 processed = 0;
+ HRESULT res = _outStream->Write(_buf + _convPos, num, &processed);
+ if (processed == 0)
+ return res != S_OK ? res : E_FAIL;
+
+ _convPos += processed;
+ _convSize -= processed;
+ _nowPos64 += processed;
+ RINOK(res);
+ }
+
+ if (_convPos != 0)
+ {
+ UInt32 num = _bufPos - _convPos;
+ for (UInt32 i = 0; i < num; i++)
+ _buf[i] = _buf[_convPos + i];
+ _bufPos = num;
+ _convPos = 0;
+ }
+
+ return S_OK;
+}
STDMETHODIMP CFilterCoder::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
- if (processedSize != NULL)
+ if (processedSize)
*processedSize = 0;
- while (size > 0)
+
+ while (size != 0)
{
- UInt32 sizeTemp = MyMin(size, kBufferSize - _bufferPos);
- memcpy(_buffer + _bufferPos, data, sizeTemp);
- size -= sizeTemp;
- if (processedSize != NULL)
- *processedSize += sizeTemp;
- data = (const Byte *)data + sizeTemp;
- UInt32 endPos = _bufferPos + sizeTemp;
- _bufferPos = Filter->Filter(_buffer, endPos);
- if (_bufferPos == 0)
+ RINOK(Flush2());
+
+ // _convSize is 0
+ // _convPos is 0
+ // _bufPos is small
+
+ if (_bufPos != _bufSize)
{
- _bufferPos = endPos;
- break;
+ UInt32 num = MyMin(size, _bufSize - _bufPos);
+ memcpy(_buf + _bufPos, data, num);
+ size -= num;
+ data = (const Byte *)data + num;
+ if (processedSize)
+ *processedSize += num;
+ _bufPos += num;
+ if (_bufPos != _bufSize)
+ continue;
}
- if (_bufferPos > endPos)
- {
- if (size != 0)
- return E_FAIL;
+
+ // _bufPos == _bufSize
+ _convSize = Filter->Filter(_buf, _bufPos);
+
+ if (_convSize == 0)
break;
+ if (_convSize > _bufPos)
+ {
+ // that case is not possible.
+ _convSize = 0;
+ return E_FAIL;
}
- RINOK(WriteWithLimit(_outStream, _bufferPos));
- UInt32 i = 0;
- while (_bufferPos < endPos)
- _buffer[i++] = _buffer[_bufferPos++];
- _bufferPos = i;
}
+
return S_OK;
}
-STDMETHODIMP CFilterCoder::Flush()
+STDMETHODIMP CFilterCoder::OutStreamFinish()
{
- if (_bufferPos != 0)
+ for (;;)
{
- // _buffer contains only data refused by previous Filter->Filter call.
- UInt32 endPos = Filter->Filter(_buffer, _bufferPos);
- if (endPos > _bufferPos)
+ RINOK(Flush2());
+ if (_bufPos == 0)
+ break;
+ _convSize = Filter->Filter(_buf, _bufPos);
+ if (_convSize == 0)
+ _convSize = _bufPos;
+ else if (_convSize > _bufPos)
{
- for (; _bufferPos < endPos; _bufferPos++)
- _buffer[_bufferPos] = 0;
- if (Filter->Filter(_buffer, endPos) != endPos)
+ // AES
+ if (_convSize > _bufSize)
+ {
+ _convSize = 0;
+ return E_FAIL;
+ }
+ if (!_encodeMode)
+ {
+ _convSize = 0;
+ return S_FALSE;
+ }
+ for (; _bufPos < _convSize; _bufPos++)
+ _buf[_bufPos] = 0;
+ _convSize = Filter->Filter(_buf, _bufPos);
+ if (_convSize != _bufPos)
return E_FAIL;
}
- RINOK(WriteWithLimit(_outStream, _bufferPos));
- _bufferPos = 0;
}
- CMyComPtr<IOutStreamFlush> flush;
- _outStream.QueryInterface(IID_IOutStreamFlush, &flush);
- if (flush)
- return flush->Flush();
+
+ CMyComPtr<IOutStreamFinish> finish;
+ _outStream.QueryInterface(IID_IOutStreamFinish, &finish);
+ if (finish)
+ return finish->OutStreamFinish();
return S_OK;
}
+// ---------- Init functions ----------
-void CFilterCoder::SetInStream_NoSubFilterInit(ISequentialInStream *inStream)
+STDMETHODIMP CFilterCoder::InitEncoder()
{
- _convertedPosBegin = _convertedPosEnd = _bufferPos = 0;
- _inStream = inStream;
- Init2();
+ InitSpecVars();
+ return Init_and_Alloc();
+}
+
+HRESULT CFilterCoder::Init_NoSubFilterInit()
+{
+ InitSpecVars();
+ return Alloc();
+}
+
+STDMETHODIMP CFilterCoder::SetOutStreamSize(const UInt64 *outSize)
+{
+ InitSpecVars();
+ if (outSize)
+ {
+ _outSize = *outSize;
+ _outSizeIsDefined = true;
+ }
+ return Init_and_Alloc();
}
+// ---------- Read from Filter ----------
+
STDMETHODIMP CFilterCoder::SetInStream(ISequentialInStream *inStream)
{
- SetInStream_NoSubFilterInit(inStream);
- return Init();
+ _inStream = inStream;
+ return S_OK;
}
STDMETHODIMP CFilterCoder::ReleaseInStream()
@@ -172,94 +304,115 @@ STDMETHODIMP CFilterCoder::ReleaseInStream()
return S_OK;
}
+
STDMETHODIMP CFilterCoder::Read(void *data, UInt32 size, UInt32 *processedSize)
{
- if (processedSize != NULL)
+ if (processedSize)
*processedSize = 0;
- while (size > 0)
+
+ while (size != 0)
{
- if (_convertedPosBegin != _convertedPosEnd)
+ if (_convSize != 0)
{
- UInt32 sizeTemp = MyMin(size, _convertedPosEnd - _convertedPosBegin);
- memcpy(data, _buffer + _convertedPosBegin, sizeTemp);
- _convertedPosBegin += sizeTemp;
- data = (void *)((Byte *)data + sizeTemp);
- size -= sizeTemp;
- if (processedSize != NULL)
- *processedSize += sizeTemp;
+ if (size > _convSize)
+ size = _convSize;
+ if (_outSizeIsDefined)
+ {
+ UInt64 rem = _outSize - _nowPos64;
+ if (size > rem)
+ size = (UInt32)rem;
+ }
+ memcpy(data, _buf + _convPos, size);
+ _convPos += size;
+ _convSize -= size;
+ _nowPos64 += size;
+ if (processedSize)
+ *processedSize = size;
break;
}
- UInt32 i;
- for (i = 0; _convertedPosEnd + i < _bufferPos; i++)
- _buffer[i] = _buffer[_convertedPosEnd + i];
- _bufferPos = i;
- _convertedPosBegin = _convertedPosEnd = 0;
- size_t processedSizeTemp = kBufferSize - _bufferPos;
- RINOK(ReadStream(_inStream, _buffer + _bufferPos, &processedSizeTemp));
- _bufferPos += (UInt32)processedSizeTemp;
- _convertedPosEnd = Filter->Filter(_buffer, _bufferPos);
- if (_convertedPosEnd == 0)
+
+ if (_convPos != 0)
+ {
+ UInt32 num = _bufPos - _convPos;
+ for (UInt32 i = 0; i < num; i++)
+ _buf[i] = _buf[_convPos + i];
+ _bufPos = num;
+ _convPos = 0;
+ }
+
+ {
+ size_t readSize = _bufSize - _bufPos;
+ HRESULT res = ReadStream(_inStream, _buf + _bufPos, &readSize);
+ _bufPos += (UInt32)readSize;
+ RINOK(res);
+ }
+
+ _convSize = Filter->Filter(_buf, _bufPos);
+
+ if (_convSize == 0)
{
- if (_bufferPos == 0)
+ if (_bufPos == 0)
break;
- _convertedPosEnd = _bufferPos; // check it
+ // BCJ
+ _convSize = _bufPos;
continue;
}
- if (_convertedPosEnd > _bufferPos)
+
+ if (_convSize > _bufPos)
{
- for (; _bufferPos < _convertedPosEnd; _bufferPos++)
- _buffer[_bufferPos] = 0;
- _convertedPosEnd = Filter->Filter(_buffer, _bufferPos);
+ // AES
+ if (_convSize > _bufSize)
+ return E_FAIL;
+ if (!_encodeMode)
+ return S_FALSE;
+
+ do
+ _buf[_bufPos] = 0;
+ while (++_bufPos != _convSize);
+
+ _convSize = Filter->Filter(_buf, _convSize);
+ if (_convSize != _bufPos)
+ return E_FAIL;
}
}
+
return S_OK;
}
+
#ifndef _NO_CRYPTO
STDMETHODIMP CFilterCoder::CryptoSetPassword(const Byte *data, UInt32 size)
-{
- return _setPassword->CryptoSetPassword(data, size);
-}
+ { return _SetPassword->CryptoSetPassword(data, size); }
STDMETHODIMP CFilterCoder::SetKey(const Byte *data, UInt32 size)
-{
- return _cryptoProperties->SetKey(data, size);
-}
+ { return _CryptoProperties->SetKey(data, size); }
STDMETHODIMP CFilterCoder::SetInitVector(const Byte *data, UInt32 size)
-{
- return _cryptoProperties->SetInitVector(data, size);
-}
+ { return _CryptoProperties->SetInitVector(data, size); }
#endif
+
#ifndef EXTRACT_ONLY
+
STDMETHODIMP CFilterCoder::SetCoderProperties(const PROPID *propIDs,
- const PROPVARIANT *properties, UInt32 numProperties)
-{
- return _SetCoderProperties->SetCoderProperties(propIDs, properties, numProperties);
-}
+ const PROPVARIANT *properties, UInt32 numProperties)
+ { return _SetCoderProperties->SetCoderProperties(propIDs, properties, numProperties); }
STDMETHODIMP CFilterCoder::WriteCoderProperties(ISequentialOutStream *outStream)
-{
- return _writeCoderProperties->WriteCoderProperties(outStream);
-}
+ { return _WriteCoderProperties->WriteCoderProperties(outStream); }
/*
STDMETHODIMP CFilterCoder::ResetSalt()
-{
- return _CryptoResetSalt->ResetSalt();
-}
+ { return _CryptoResetSalt->ResetSalt(); }
*/
STDMETHODIMP CFilterCoder::ResetInitVector()
-{
- return _CryptoResetInitVector->ResetInitVector();
-}
+ { return _CryptoResetInitVector->ResetInitVector(); }
+
#endif
+
STDMETHODIMP CFilterCoder::SetDecoderProperties2(const Byte *data, UInt32 size)
-{
- return _setDecoderProperties->SetDecoderProperties2(data, size);
-}
+ { return _SetDecoderProperties2->SetDecoderProperties2(data, size); }