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