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