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,
Open(CFSTR fileName)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 
SetTime(const FILETIME * cTime,const FILETIME * aTime,const FILETIME * mTime)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)
GetSize()160         num = (UInt32)rem;
CStdOutFileStream()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