• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // FilterCoder.cpp
2 
3 #include "StdAfx.h"
4 
5 // #include <stdio.h>
6 
7 #include "../../Common/Defs.h"
8 
9 #include "FilterCoder.h"
10 #include "StreamUtils.h"
11 
12 #ifdef _WIN32
13   #define alignedMidBuffer_Alloc g_MidAlloc
14 #else
15   #define alignedMidBuffer_Alloc g_AlignedAlloc
16 #endif
17 
~CAlignedMidBuffer()18 CAlignedMidBuffer::~CAlignedMidBuffer()
19 {
20   ISzAlloc_Free(&alignedMidBuffer_Alloc, _buf);
21 }
22 
AllocAligned(size_t size)23 void CAlignedMidBuffer::AllocAligned(size_t size)
24 {
25   ISzAlloc_Free(&alignedMidBuffer_Alloc, _buf);
26   _buf = (Byte *)ISzAlloc_Alloc(&alignedMidBuffer_Alloc, size);
27 }
28 
29 /*
30   AES filters need 16-bytes alignment for HARDWARE-AES instructions.
31   So we call IFilter::Filter(, size), where (size != 16 * N) only for last data block.
32 
33   AES-CBC filters need data size aligned for 16-bytes.
34   So the encoder can add zeros to the end of original stream.
35 
36   Some filters (BCJ and others) don't process data at the end of stream in some cases.
37   So the encoder and decoder write such last bytes without change.
38 
39   Most filters process all data, if we send aligned size to filter.
40      But  BCJ filter can process up 4 bytes less than sent size.
41      And ARMT filter can process    2 bytes less than sent size.
42 */
43 
44 
45 static const UInt32 kBufSize = 1 << 21;
46 
Z7_COM7F_IMF(CFilterCoder::SetInBufSize (UInt32,UInt32 size))47 Z7_COM7F_IMF(CFilterCoder::SetInBufSize(UInt32 , UInt32 size)) { _inBufSize = size; return S_OK; }
Z7_COM7F_IMF(CFilterCoder::SetOutBufSize (UInt32,UInt32 size))48 Z7_COM7F_IMF(CFilterCoder::SetOutBufSize(UInt32 , UInt32 size)) { _outBufSize = size; return S_OK; }
49 
Alloc()50 HRESULT CFilterCoder::Alloc()
51 {
52   UInt32 size = MyMin(_inBufSize, _outBufSize);
53   /* minimal bufSize is 16 bytes for AES and IA64 filter.
54      bufSize for AES must be aligned for 16 bytes.
55      We use (1 << 12) min size to support future aligned filters. */
56   const UInt32 kMinSize = 1 << 12;
57   size &= ~(UInt32)(kMinSize - 1);
58   if (size < kMinSize)
59     size = kMinSize;
60   // size = (1 << 12); // + 117; // for debug
61   if (!_buf || _bufSize != size)
62   {
63     AllocAligned(size);
64     if (!_buf)
65       return E_OUTOFMEMORY;
66     _bufSize = size;
67   }
68   return S_OK;
69 }
70 
Init_and_Alloc()71 HRESULT CFilterCoder::Init_and_Alloc()
72 {
73   RINOK(Filter->Init())
74   return Alloc();
75 }
76 
CFilterCoder(bool encodeMode)77 CFilterCoder::CFilterCoder(bool encodeMode):
78     _bufSize(0),
79     _inBufSize(kBufSize),
80     _outBufSize(kBufSize),
81     _encodeMode(encodeMode),
82     _outSize_Defined(false),
83     _outSize(0),
84     _nowPos64(0)
85   {}
86 
87 
Z7_COM7F_IMF(CFilterCoder::Code (ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 *,const UInt64 * outSize,ICompressProgressInfo * progress))88 Z7_COM7F_IMF(CFilterCoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
89     const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress))
90 {
91   RINOK(Init_and_Alloc())
92 
93   /*
94      It's expected that BCJ/ARMT filter can process up to 4 bytes less
95      than sent data size. For such BCJ/ARMT cases with non-filtered data we:
96        - write some filtered data to output stream
97        - move non-written data (filtered and non-filtered data) to start of buffer
98        - read more new data from input stream to position after end of non-filtered data
99        - call Filter() for concatenated data in buffer.
100 
101      For all cases, even for cases with partial filtering (BCJ/ARMT),
102      we try to keep real/virtual alignment for all operations
103        (memmove, Read(), Filter(), Write()).
104      We use (kAlignSize=64) alignmnent that is larger than (16-bytes)
105      required for AES filter alignment.
106 
107      AES-CBC uses 16-bytes blocks, that is simple case for processing here,
108      if we call Filter() for aligned size for all calls except of last call (last block).
109      And now there are no filters that use blocks with non-power2 size,
110      but we try to support such non-power2 filters too here at Code().
111   */
112 
113   UInt64 prev = 0;
114   UInt64 nowPos64 = 0;
115   bool inputFinished = false;
116   UInt32 readPos = 0;
117   UInt32 filterPos = 0;
118 
119   while (!outSize || nowPos64 < *outSize)
120   {
121     HRESULT hres = S_OK;
122     if (!inputFinished)
123     {
124       size_t processedSize = _bufSize - readPos;
125       /* for AES filters we need at least max(16, kAlignSize) bytes in buffer.
126          But we try to read full buffer to reduce the number of Filter() and Write() calls.
127       */
128       hres = ReadStream(inStream, _buf + readPos, &processedSize);
129       readPos += (UInt32)processedSize;
130       inputFinished = (readPos != _bufSize);
131       if (hres != S_OK)
132       {
133         // do we need to stop encoding after reading error?
134         // if (_encodeMode) return hres;
135         inputFinished = true;
136       }
137     }
138 
139     if (readPos == 0)
140       return hres;
141 
142     /* we set (needMoreInput = true), if it's block-filter (like AES-CBC)
143          that needs more data for current block filtering:
144        We read full input buffer with Read(), and _bufSize is aligned,
145        So the possible cases when we set (needMoreInput = true) are:
146          1) decode : filter needs more data after the end of input stream.
147            another cases are possible for non-power2-block-filter,
148            because buffer size is not aligned for filter_non_power2_block_size:
149          2) decode/encode : filter needs more data from non-finished input stream
150          3) encode        : filter needs more space for zeros after the end of input stream
151     */
152     bool needMoreInput = false;
153 
154     while (readPos != filterPos)
155     {
156       /* Filter() is allowed to process part of data.
157          Here we use the loop to filter as max as possible.
158          when we call Filter(data, size):
159          if (size < 16), AES-CTR filter uses internal 16-byte buffer.
160          new (since v23.00) AES-CTR filter allows (size < 16) for non-last block,
161          but it will work less efficiently than calls with aligned (size).
162          We still support old (before v23.00) AES-CTR filters here.
163          We have aligned (size) for AES-CTR, if it's not last block.
164          We have aligned (readPos) for any filter, if (!inputFinished).
165          We also meet the requirements for (data) pointer in Filter() call:
166          {
167            (virtual_stream_offset % aligment_size) == (data_ptr % aligment_size)
168            (aligment_size == 2^N)
169            (aligment_size  >= 16)
170          }
171       */
172       const UInt32 cur = Filter->Filter(_buf + filterPos, readPos - filterPos);
173       if (cur == 0)
174         break;
175       const UInt32 f = filterPos + cur;
176       if (cur > readPos - filterPos)
177       {
178         // AES-CBC
179         if (hres != S_OK)
180           break;
181 
182         if (!_encodeMode
183             || cur > _bufSize - filterPos
184             || !inputFinished)
185         {
186           /* (cur > _bufSize - filterPos) is unexpected for AES filter, if _bufSize is multiply of 16.
187              But we support this case, if some future filter will use block with non-power2-size.
188           */
189           needMoreInput = true;
190           break;
191         }
192 
193         /* (_encodeMode && inputFinished).
194            We add zero bytes as pad in current block after the end of read data. */
195         Byte *buf = _buf;
196         do
197           buf[readPos] = 0;
198         while (++readPos != f);
199         // (readPos) now is (size_of_real_input_data + size_of_zero_pad)
200         if (cur != Filter->Filter(buf + filterPos, cur))
201           return E_FAIL;
202       }
203       filterPos = f;
204     }
205 
206     UInt32 size = filterPos;
207     if (hres == S_OK)
208     {
209       /* If we need more Read() or Filter() calls, then we need to Write()
210          some data and move unwritten data to get additional space in buffer.
211          We try to keep alignment for data moves, Read(), Filter() and Write() calls.
212       */
213       const UInt32 kAlignSize = 1 << 6;
214       const UInt32 alignedFiltered = filterPos & ~(kAlignSize - 1);
215       if (inputFinished)
216       {
217         if (!needMoreInput)
218           size = readPos; // for risc/bcj filters in last block we write data after filterPos.
219         else if (_encodeMode)
220           size = alignedFiltered; // for non-power2-block-encode-filter
221       }
222       else
223         size = alignedFiltered;
224     }
225 
226     {
227       UInt32 writeSize = size;
228       if (outSize)
229       {
230         const UInt64 rem = *outSize - nowPos64;
231         if (writeSize > rem)
232           writeSize = (UInt32)rem;
233       }
234       RINOK(WriteStream(outStream, _buf, writeSize))
235       nowPos64 += writeSize;
236     }
237 
238     if (hres != S_OK)
239       return hres;
240 
241     if (inputFinished)
242     {
243       if (readPos == size)
244         return hres;
245       if (!_encodeMode)
246       {
247         // block-decode-filter (AES-CBS) has non-full last block
248         // we don't want unaligned data move for more iterations with this error case.
249         return S_FALSE;
250       }
251     }
252 
253     if (size == 0)
254     {
255       // it's unexpected that we have no any move in this iteration.
256       return E_FAIL;
257     }
258     // if (size != 0)
259     {
260       if (filterPos < size)
261         return E_FAIL; // filterPos = 0; else
262       filterPos -= size;
263       readPos -= size;
264       if (readPos != 0)
265         memmove(_buf, _buf + size, readPos);
266     }
267     // printf("\nnowPos64=%x, readPos=%x, filterPos=%x\n", (unsigned)nowPos64, (unsigned)readPos, (unsigned)filterPos);
268 
269     if (progress && (nowPos64 - prev) >= (1 << 22))
270     {
271       prev = nowPos64;
272       RINOK(progress->SetRatioInfo(&nowPos64, &nowPos64))
273     }
274   }
275 
276   return S_OK;
277 }
278 
279 
280 
281 // ---------- Write to Filter ----------
282 
Z7_COM7F_IMF(CFilterCoder::SetOutStream (ISequentialOutStream * outStream))283 Z7_COM7F_IMF(CFilterCoder::SetOutStream(ISequentialOutStream *outStream))
284 {
285   _outStream = outStream;
286   return S_OK;
287 }
288 
Z7_COM7F_IMF(CFilterCoder::ReleaseOutStream ())289 Z7_COM7F_IMF(CFilterCoder::ReleaseOutStream())
290 {
291   _outStream.Release();
292   return S_OK;
293 }
294 
Flush2()295 HRESULT CFilterCoder::Flush2()
296 {
297   while (_convSize != 0)
298   {
299     UInt32 num = _convSize;
300     if (_outSize_Defined)
301     {
302       const UInt64 rem = _outSize - _nowPos64;
303       if (num > rem)
304         num = (UInt32)rem;
305       if (num == 0)
306         return k_My_HRESULT_WritingWasCut;
307     }
308 
309     UInt32 processed = 0;
310     const HRESULT res = _outStream->Write(_buf + _convPos, num, &processed);
311     if (processed == 0)
312       return res != S_OK ? res : E_FAIL;
313 
314     _convPos += processed;
315     _convSize -= processed;
316     _nowPos64 += processed;
317     RINOK(res)
318   }
319 
320   const UInt32 convPos = _convPos;
321   if (convPos != 0)
322   {
323     const UInt32 num = _bufPos - convPos;
324     Byte *buf = _buf;
325     for (UInt32 i = 0; i < num; i++)
326       buf[i] = buf[convPos + i];
327     _bufPos = num;
328     _convPos = 0;
329   }
330 
331   return S_OK;
332 }
333 
Z7_COM7F_IMF(CFilterCoder::Write (const void * data,UInt32 size,UInt32 * processedSize))334 Z7_COM7F_IMF(CFilterCoder::Write(const void *data, UInt32 size, UInt32 *processedSize))
335 {
336   if (processedSize)
337     *processedSize = 0;
338 
339   while (size != 0)
340   {
341     RINOK(Flush2())
342 
343     // _convSize is 0
344     // _convPos is 0
345     // _bufPos is small
346 
347     if (_bufPos != _bufSize)
348     {
349       UInt32 num = MyMin(size, _bufSize - _bufPos);
350       memcpy(_buf + _bufPos, data, num);
351       size -= num;
352       data = (const Byte *)data + num;
353       if (processedSize)
354         *processedSize += num;
355       _bufPos += num;
356       if (_bufPos != _bufSize)
357         continue;
358     }
359 
360     // _bufPos == _bufSize
361     _convSize = Filter->Filter(_buf, _bufPos);
362 
363     if (_convSize == 0)
364       break;
365     if (_convSize > _bufPos)
366     {
367       // that case is not possible.
368       _convSize = 0;
369       return E_FAIL;
370     }
371   }
372 
373   return S_OK;
374 }
375 
Z7_COM7F_IMF(CFilterCoder::OutStreamFinish ())376 Z7_COM7F_IMF(CFilterCoder::OutStreamFinish())
377 {
378   for (;;)
379   {
380     RINOK(Flush2())
381     if (_bufPos == 0)
382       break;
383     const UInt32 convSize = Filter->Filter(_buf, _bufPos);
384     _convSize = convSize;
385     UInt32 bufPos = _bufPos;
386     if (convSize == 0)
387       _convSize = bufPos;
388     else if (convSize > bufPos)
389     {
390       // AES
391       if (convSize > _bufSize)
392       {
393         _convSize = 0;
394         return E_FAIL;
395       }
396       if (!_encodeMode)
397       {
398         _convSize = 0;
399         return S_FALSE;
400       }
401       Byte *buf = _buf;
402       for (; bufPos < convSize; bufPos++)
403         buf[bufPos] = 0;
404       _bufPos = bufPos;
405       _convSize = Filter->Filter(_buf, bufPos);
406       if (_convSize != _bufPos)
407         return E_FAIL;
408     }
409   }
410 
411   CMyComPtr<IOutStreamFinish> finish;
412   _outStream.QueryInterface(IID_IOutStreamFinish, &finish);
413   if (finish)
414     return finish->OutStreamFinish();
415   return S_OK;
416 }
417 
418 // ---------- Init functions ----------
419 
Z7_COM7F_IMF(CFilterCoder::InitEncoder ())420 Z7_COM7F_IMF(CFilterCoder::InitEncoder())
421 {
422   InitSpecVars();
423   return Init_and_Alloc();
424 }
425 
Init_NoSubFilterInit()426 HRESULT CFilterCoder::Init_NoSubFilterInit()
427 {
428   InitSpecVars();
429   return Alloc();
430 }
431 
Z7_COM7F_IMF(CFilterCoder::SetOutStreamSize (const UInt64 * outSize))432 Z7_COM7F_IMF(CFilterCoder::SetOutStreamSize(const UInt64 *outSize))
433 {
434   InitSpecVars();
435   if (outSize)
436   {
437     _outSize = *outSize;
438     _outSize_Defined = true;
439   }
440   return Init_and_Alloc();
441 }
442 
443 // ---------- Read from Filter ----------
444 
Z7_COM7F_IMF(CFilterCoder::SetInStream (ISequentialInStream * inStream))445 Z7_COM7F_IMF(CFilterCoder::SetInStream(ISequentialInStream *inStream))
446 {
447   _inStream = inStream;
448   return S_OK;
449 }
450 
Z7_COM7F_IMF(CFilterCoder::ReleaseInStream ())451 Z7_COM7F_IMF(CFilterCoder::ReleaseInStream())
452 {
453   _inStream.Release();
454   return S_OK;
455 }
456 
457 
Z7_COM7F_IMF(CFilterCoder::Read (void * data,UInt32 size,UInt32 * processedSize))458 Z7_COM7F_IMF(CFilterCoder::Read(void *data, UInt32 size, UInt32 *processedSize))
459 {
460   if (processedSize)
461     *processedSize = 0;
462 
463   while (size != 0)
464   {
465     if (_convSize != 0)
466     {
467       if (size > _convSize)
468         size = _convSize;
469       if (_outSize_Defined)
470       {
471         const UInt64 rem = _outSize - _nowPos64;
472         if (size > rem)
473           size = (UInt32)rem;
474       }
475       memcpy(data, _buf + _convPos, size);
476       _convPos += size;
477       _convSize -= size;
478       _nowPos64 += size;
479       if (processedSize)
480         *processedSize = size;
481       break;
482     }
483 
484     const UInt32 convPos = _convPos;
485     if (convPos != 0)
486     {
487       const UInt32 num = _bufPos - convPos;
488       Byte *buf = _buf;
489       for (UInt32 i = 0; i < num; i++)
490         buf[i] = buf[convPos + i];
491       _bufPos = num;
492       _convPos = 0;
493     }
494 
495     {
496       size_t readSize = _bufSize - _bufPos;
497       const HRESULT res = ReadStream(_inStream, _buf + _bufPos, &readSize);
498       _bufPos += (UInt32)readSize;
499       RINOK(res)
500     }
501 
502     const UInt32 convSize = Filter->Filter(_buf, _bufPos);
503     _convSize = convSize;
504 
505     UInt32 bufPos = _bufPos;
506 
507     if (convSize == 0)
508     {
509       if (bufPos == 0)
510         break;
511       // BCJ
512       _convSize = bufPos;
513       continue;
514     }
515 
516     if (convSize > bufPos)
517     {
518       // AES
519       if (convSize > _bufSize)
520         return E_FAIL;
521       if (!_encodeMode)
522         return S_FALSE;
523       Byte *buf = _buf;
524       do
525         buf[bufPos] = 0;
526       while (++bufPos != convSize);
527       _bufPos = bufPos;
528       _convSize = Filter->Filter(_buf, convSize);
529       if (_convSize != _bufPos)
530         return E_FAIL;
531     }
532   }
533 
534   return S_OK;
535 }
536 
537 
538 #ifndef Z7_NO_CRYPTO
539 
Z7_COM7F_IMF(CFilterCoder::CryptoSetPassword (const Byte * data,UInt32 size))540 Z7_COM7F_IMF(CFilterCoder::CryptoSetPassword(const Byte *data, UInt32 size))
541   { return _setPassword->CryptoSetPassword(data, size); }
542 
Z7_COM7F_IMF(CFilterCoder::SetKey (const Byte * data,UInt32 size))543 Z7_COM7F_IMF(CFilterCoder::SetKey(const Byte *data, UInt32 size))
544   { return _cryptoProperties->SetKey(data, size); }
545 
Z7_COM7F_IMF(CFilterCoder::SetInitVector (const Byte * data,UInt32 size))546 Z7_COM7F_IMF(CFilterCoder::SetInitVector(const Byte *data, UInt32 size))
547   { return _cryptoProperties->SetInitVector(data, size); }
548 
549 #endif
550 
551 
552 #ifndef Z7_EXTRACT_ONLY
553 
Z7_COM7F_IMF(CFilterCoder::SetCoderProperties (const PROPID * propIDs,const PROPVARIANT * properties,UInt32 numProperties))554 Z7_COM7F_IMF(CFilterCoder::SetCoderProperties(const PROPID *propIDs,
555     const PROPVARIANT *properties, UInt32 numProperties))
556   { return _setCoderProperties->SetCoderProperties(propIDs, properties, numProperties); }
557 
Z7_COM7F_IMF(CFilterCoder::WriteCoderProperties (ISequentialOutStream * outStream))558 Z7_COM7F_IMF(CFilterCoder::WriteCoderProperties(ISequentialOutStream *outStream))
559   { return _writeCoderProperties->WriteCoderProperties(outStream); }
560 
Z7_COM7F_IMF(CFilterCoder::SetCoderPropertiesOpt (const PROPID * propIDs,const PROPVARIANT * properties,UInt32 numProperties))561 Z7_COM7F_IMF(CFilterCoder::SetCoderPropertiesOpt(const PROPID *propIDs,
562     const PROPVARIANT *properties, UInt32 numProperties))
563   { return _setCoderPropertiesOpt->SetCoderPropertiesOpt(propIDs, properties, numProperties); }
564 
565 /*
566 Z7_COM7F_IMF(CFilterCoder::ResetSalt()
567   { return _cryptoResetSalt->ResetSalt(); }
568 */
569 
Z7_COM7F_IMF(CFilterCoder::ResetInitVector ())570 Z7_COM7F_IMF(CFilterCoder::ResetInitVector())
571   { return _cryptoResetInitVector->ResetInitVector(); }
572 
573 #endif
574 
575 
Z7_COM7F_IMF(CFilterCoder::SetDecoderProperties2 (const Byte * data,UInt32 size))576 Z7_COM7F_IMF(CFilterCoder::SetDecoderProperties2(const Byte *data, UInt32 size))
577   { return _setDecoderProperties2->SetDecoderProperties2(data, size); }
578