Home | History | Annotate | Download | only in Common
      1 // FilterCoder.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../Common/Defs.h"
      6 
      7 #include "FilterCoder.h"
      8 #include "StreamUtils.h"
      9 
     10 /*
     11   AES filters need 16-bytes alignment for HARDWARE-AES instructions.
     12   So we call IFilter::Filter(, size), where (size != 16 * N) only for last data block.
     13 
     14   AES-CBC filters need data size aligned for 16-bytes.
     15   So the encoder can add zeros to the end of original stream.
     16 
     17   Some filters (BCJ and others) don't process data at the end of stream in some cases.
     18   So the encoder and decoder write such last bytes without change.
     19 */
     20 
     21 
     22 static const UInt32 kBufSize = 1 << 20;
     23 
     24 STDMETHODIMP CFilterCoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSize = size; return S_OK; }
     25 STDMETHODIMP CFilterCoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; }
     26 
     27 HRESULT CFilterCoder::Alloc()
     28 {
     29   UInt32 size = MyMin(_inBufSize, _outBufSize);
     30   /* minimal bufSize is 16 bytes for AES and IA64 filter.
     31      bufSize for AES must be aligned for 16 bytes.
     32      We use (1 << 12) min size to support future aligned filters. */
     33   const UInt32 kMinSize = 1 << 12;
     34   size &= ~(UInt32)(kMinSize - 1);
     35   if (size < kMinSize)
     36     size = kMinSize;
     37   if (!_buf || _bufSize != size)
     38   {
     39     AllocAlignedMask(size, 16 - 1);
     40     if (!_buf)
     41       return E_OUTOFMEMORY;
     42     _bufSize = size;
     43   }
     44   return S_OK;
     45 }
     46 
     47 HRESULT CFilterCoder::Init_and_Alloc()
     48 {
     49   RINOK(Filter->Init());
     50   return Alloc();
     51 }
     52 
     53 CFilterCoder::CFilterCoder(bool encodeMode):
     54     _bufSize(0),
     55     _inBufSize(kBufSize),
     56     _outBufSize(kBufSize),
     57     _encodeMode(encodeMode),
     58     _outSizeIsDefined(false),
     59     _outSize(0),
     60     _nowPos64(0)
     61   {}
     62 
     63 CFilterCoder::~CFilterCoder()
     64 {
     65 }
     66 
     67 STDMETHODIMP CFilterCoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
     68     const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
     69 {
     70   RINOK(Init_and_Alloc());
     71 
     72   UInt64 nowPos64 = 0;
     73   bool inputFinished = false;
     74   UInt32 pos = 0;
     75 
     76   while (!outSize || nowPos64 < *outSize)
     77   {
     78     UInt32 endPos = pos;
     79 
     80     if (!inputFinished)
     81     {
     82       size_t processedSize = _bufSize - pos;
     83       RINOK(ReadStream(inStream, _buf + pos, &processedSize));
     84       endPos = pos + (UInt32)processedSize;
     85       inputFinished = (endPos != _bufSize);
     86     }
     87 
     88     pos = Filter->Filter(_buf, endPos);
     89 
     90     if (pos > endPos)
     91     {
     92       // AES
     93       if (!inputFinished || pos > _bufSize)
     94         return E_FAIL;
     95       if (!_encodeMode)
     96         return S_FALSE;
     97 
     98       do
     99         _buf[endPos] = 0;
    100       while (++endPos != pos);
    101 
    102       if (pos != Filter->Filter(_buf, pos))
    103         return E_FAIL;
    104     }
    105 
    106     if (endPos == 0)
    107       return S_OK;
    108 
    109     UInt32 size = (pos != 0 ? pos : endPos);
    110     if (outSize)
    111     {
    112       UInt64 remSize = *outSize - nowPos64;
    113       if (size > remSize)
    114         size = (UInt32)remSize;
    115     }
    116 
    117     RINOK(WriteStream(outStream, _buf, size));
    118     nowPos64 += size;
    119 
    120     if (pos == 0)
    121       return S_OK;
    122 
    123     if (progress)
    124       RINOK(progress->SetRatioInfo(&nowPos64, &nowPos64));
    125 
    126     UInt32 i = 0;
    127     while (pos < endPos)
    128       _buf[i++] = _buf[pos++];
    129     pos = i;
    130   }
    131 
    132   return S_OK;
    133 }
    134 
    135 
    136 
    137 // ---------- Write to Filter ----------
    138 
    139 STDMETHODIMP CFilterCoder::SetOutStream(ISequentialOutStream *outStream)
    140 {
    141   _outStream = outStream;
    142   return S_OK;
    143 }
    144 
    145 STDMETHODIMP CFilterCoder::ReleaseOutStream()
    146 {
    147   _outStream.Release();
    148   return S_OK;
    149 }
    150 
    151 HRESULT CFilterCoder::Flush2()
    152 {
    153   while (_convSize != 0)
    154   {
    155     UInt32 num = _convSize;
    156     if (_outSizeIsDefined)
    157     {
    158       UInt64 rem = _outSize - _nowPos64;
    159       if (num > rem)
    160         num = (UInt32)rem;
    161       if (num == 0)
    162         return k_My_HRESULT_WritingWasCut;
    163     }
    164 
    165     UInt32 processed = 0;
    166     HRESULT res = _outStream->Write(_buf + _convPos, num, &processed);
    167     if (processed == 0)
    168       return res != S_OK ? res : E_FAIL;
    169 
    170     _convPos += processed;
    171     _convSize -= processed;
    172     _nowPos64 += processed;
    173     RINOK(res);
    174   }
    175 
    176   if (_convPos != 0)
    177   {
    178     UInt32 num = _bufPos - _convPos;
    179     for (UInt32 i = 0; i < num; i++)
    180       _buf[i] = _buf[_convPos + i];
    181     _bufPos = num;
    182     _convPos = 0;
    183   }
    184 
    185   return S_OK;
    186 }
    187 
    188 STDMETHODIMP CFilterCoder::Write(const void *data, UInt32 size, UInt32 *processedSize)
    189 {
    190   if (processedSize)
    191     *processedSize = 0;
    192 
    193   while (size != 0)
    194   {
    195     RINOK(Flush2());
    196 
    197     // _convSize is 0
    198     // _convPos is 0
    199     // _bufPos is small
    200 
    201     if (_bufPos != _bufSize)
    202     {
    203       UInt32 num = MyMin(size, _bufSize - _bufPos);
    204       memcpy(_buf + _bufPos, data, num);
    205       size -= num;
    206       data = (const Byte *)data + num;
    207       if (processedSize)
    208         *processedSize += num;
    209       _bufPos += num;
    210       if (_bufPos != _bufSize)
    211         continue;
    212     }
    213 
    214     // _bufPos == _bufSize
    215     _convSize = Filter->Filter(_buf, _bufPos);
    216 
    217     if (_convSize == 0)
    218       break;
    219     if (_convSize > _bufPos)
    220     {
    221       // that case is not possible.
    222       _convSize = 0;
    223       return E_FAIL;
    224     }
    225   }
    226 
    227   return S_OK;
    228 }
    229 
    230 STDMETHODIMP CFilterCoder::OutStreamFinish()
    231 {
    232   for (;;)
    233   {
    234     RINOK(Flush2());
    235     if (_bufPos == 0)
    236       break;
    237     _convSize = Filter->Filter(_buf, _bufPos);
    238     if (_convSize == 0)
    239       _convSize = _bufPos;
    240     else if (_convSize > _bufPos)
    241     {
    242       // AES
    243       if (_convSize > _bufSize)
    244       {
    245         _convSize = 0;
    246         return E_FAIL;
    247       }
    248       if (!_encodeMode)
    249       {
    250         _convSize = 0;
    251         return S_FALSE;
    252       }
    253       for (; _bufPos < _convSize; _bufPos++)
    254         _buf[_bufPos] = 0;
    255       _convSize = Filter->Filter(_buf, _bufPos);
    256       if (_convSize != _bufPos)
    257         return E_FAIL;
    258     }
    259   }
    260 
    261   CMyComPtr<IOutStreamFinish> finish;
    262   _outStream.QueryInterface(IID_IOutStreamFinish, &finish);
    263   if (finish)
    264     return finish->OutStreamFinish();
    265   return S_OK;
    266 }
    267 
    268 // ---------- Init functions ----------
    269 
    270 STDMETHODIMP CFilterCoder::InitEncoder()
    271 {
    272   InitSpecVars();
    273   return Init_and_Alloc();
    274 }
    275 
    276 HRESULT CFilterCoder::Init_NoSubFilterInit()
    277 {
    278   InitSpecVars();
    279   return Alloc();
    280 }
    281 
    282 STDMETHODIMP CFilterCoder::SetOutStreamSize(const UInt64 *outSize)
    283 {
    284   InitSpecVars();
    285   if (outSize)
    286   {
    287     _outSize = *outSize;
    288     _outSizeIsDefined = true;
    289   }
    290   return Init_and_Alloc();
    291 }
    292 
    293 // ---------- Read from Filter ----------
    294 
    295 STDMETHODIMP CFilterCoder::SetInStream(ISequentialInStream *inStream)
    296 {
    297   _inStream = inStream;
    298   return S_OK;
    299 }
    300 
    301 STDMETHODIMP CFilterCoder::ReleaseInStream()
    302 {
    303   _inStream.Release();
    304   return S_OK;
    305 }
    306 
    307 
    308 STDMETHODIMP CFilterCoder::Read(void *data, UInt32 size, UInt32 *processedSize)
    309 {
    310   if (processedSize)
    311     *processedSize = 0;
    312 
    313   while (size != 0)
    314   {
    315     if (_convSize != 0)
    316     {
    317       if (size > _convSize)
    318         size = _convSize;
    319       if (_outSizeIsDefined)
    320       {
    321         UInt64 rem = _outSize - _nowPos64;
    322         if (size > rem)
    323           size = (UInt32)rem;
    324       }
    325       memcpy(data, _buf + _convPos, size);
    326       _convPos += size;
    327       _convSize -= size;
    328       _nowPos64 += size;
    329       if (processedSize)
    330         *processedSize = size;
    331       break;
    332     }
    333 
    334     if (_convPos != 0)
    335     {
    336       UInt32 num = _bufPos - _convPos;
    337       for (UInt32 i = 0; i < num; i++)
    338         _buf[i] = _buf[_convPos + i];
    339       _bufPos = num;
    340       _convPos = 0;
    341     }
    342 
    343     {
    344       size_t readSize = _bufSize - _bufPos;
    345       HRESULT res = ReadStream(_inStream, _buf + _bufPos, &readSize);
    346       _bufPos += (UInt32)readSize;
    347       RINOK(res);
    348     }
    349 
    350     _convSize = Filter->Filter(_buf, _bufPos);
    351 
    352     if (_convSize == 0)
    353     {
    354       if (_bufPos == 0)
    355         break;
    356       // BCJ
    357       _convSize = _bufPos;
    358       continue;
    359     }
    360 
    361     if (_convSize > _bufPos)
    362     {
    363       // AES
    364       if (_convSize > _bufSize)
    365         return E_FAIL;
    366       if (!_encodeMode)
    367         return S_FALSE;
    368 
    369       do
    370         _buf[_bufPos] = 0;
    371       while (++_bufPos != _convSize);
    372 
    373       _convSize = Filter->Filter(_buf, _convSize);
    374       if (_convSize != _bufPos)
    375         return E_FAIL;
    376     }
    377   }
    378 
    379   return S_OK;
    380 }
    381 
    382 
    383 #ifndef _NO_CRYPTO
    384 
    385 STDMETHODIMP CFilterCoder::CryptoSetPassword(const Byte *data, UInt32 size)
    386   { return _SetPassword->CryptoSetPassword(data, size); }
    387 
    388 STDMETHODIMP CFilterCoder::SetKey(const Byte *data, UInt32 size)
    389   { return _CryptoProperties->SetKey(data, size); }
    390 
    391 STDMETHODIMP CFilterCoder::SetInitVector(const Byte *data, UInt32 size)
    392   { return _CryptoProperties->SetInitVector(data, size); }
    393 
    394 #endif
    395 
    396 
    397 #ifndef EXTRACT_ONLY
    398 
    399 STDMETHODIMP CFilterCoder::SetCoderProperties(const PROPID *propIDs,
    400     const PROPVARIANT *properties, UInt32 numProperties)
    401   { return _SetCoderProperties->SetCoderProperties(propIDs, properties, numProperties); }
    402 
    403 STDMETHODIMP CFilterCoder::WriteCoderProperties(ISequentialOutStream *outStream)
    404   { return _WriteCoderProperties->WriteCoderProperties(outStream); }
    405 
    406 /*
    407 STDMETHODIMP CFilterCoder::ResetSalt()
    408   { return _CryptoResetSalt->ResetSalt(); }
    409 */
    410 
    411 STDMETHODIMP CFilterCoder::ResetInitVector()
    412   { return _CryptoResetInitVector->ResetInitVector(); }
    413 
    414 #endif
    415 
    416 
    417 STDMETHODIMP CFilterCoder::SetDecoderProperties2(const Byte *data, UInt32 size)
    418   { return _SetDecoderProperties2->SetDecoderProperties2(data, size); }
    419