1 // Bcj2Coder.cpp
2
3 #include "StdAfx.h"
4
5 // #include <stdio.h>
6
7 #include "../../../C/Alloc.h"
8
9 #include "../Common/StreamUtils.h"
10
11 #include "Bcj2Coder.h"
12
13 namespace NCompress {
14 namespace NBcj2 {
15
CBaseCoder()16 CBaseCoder::CBaseCoder()
17 {
18 for (unsigned i = 0; i < BCJ2_NUM_STREAMS + 1; i++)
19 {
20 _bufs[i] = NULL;
21 _bufsSizes[i] = 0;
22 _bufsSizes_New[i] = (1 << 18);
23 }
24 }
25
~CBaseCoder()26 CBaseCoder::~CBaseCoder()
27 {
28 for (unsigned i = 0; i < BCJ2_NUM_STREAMS + 1; i++)
29 ::MidFree(_bufs[i]);
30 }
31
Alloc(bool allocForOrig)32 HRESULT CBaseCoder::Alloc(bool allocForOrig)
33 {
34 const unsigned num = allocForOrig ? BCJ2_NUM_STREAMS + 1 : BCJ2_NUM_STREAMS;
35 for (unsigned i = 0; i < num; i++)
36 {
37 UInt32 size = _bufsSizes_New[i];
38 /* buffer sizes for BCJ2_STREAM_CALL and BCJ2_STREAM_JUMP streams
39 must be aligned for 4 */
40 size &= ~(UInt32)3;
41 const UInt32 kMinBufSize = 4;
42 if (size < kMinBufSize)
43 size = kMinBufSize;
44 // size = 4 * 100; // for debug
45 // if (BCJ2_IS_32BIT_STREAM(i) == 1) size = 4 * 1; // for debug
46 if (!_bufs[i] || size != _bufsSizes[i])
47 {
48 if (_bufs[i])
49 {
50 ::MidFree(_bufs[i]);
51 _bufs[i] = NULL;
52 }
53 _bufsSizes[i] = 0;
54 Byte *buf = (Byte *)::MidAlloc(size);
55 if (!buf)
56 return E_OUTOFMEMORY;
57 _bufs[i] = buf;
58 _bufsSizes[i] = size;
59 }
60 }
61 return S_OK;
62 }
63
64
65
66 #ifndef Z7_EXTRACT_ONLY
67
CEncoder()68 CEncoder::CEncoder():
69 _relatLim(BCJ2_ENC_RELAT_LIMIT_DEFAULT)
70 // , _excludeRangeBits(BCJ2_RELAT_EXCLUDE_NUM_BITS)
71 {}
~CEncoder()72 CEncoder::~CEncoder() {}
73
Z7_COM7F_IMF(CEncoder::SetInBufSize (UInt32,UInt32 size))74 Z7_COM7F_IMF(CEncoder::SetInBufSize(UInt32, UInt32 size))
75 { _bufsSizes_New[BCJ2_NUM_STREAMS] = size; return S_OK; }
Z7_COM7F_IMF(CEncoder::SetOutBufSize (UInt32 streamIndex,UInt32 size))76 Z7_COM7F_IMF(CEncoder::SetOutBufSize(UInt32 streamIndex, UInt32 size))
77 { _bufsSizes_New[streamIndex] = size; return S_OK; }
78
Z7_COM7F_IMF(CEncoder::SetCoderProperties (const PROPID * propIDs,const PROPVARIANT * props,UInt32 numProps))79 Z7_COM7F_IMF(CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps))
80 {
81 UInt32 relatLim = BCJ2_ENC_RELAT_LIMIT_DEFAULT;
82 // UInt32 excludeRangeBits = BCJ2_RELAT_EXCLUDE_NUM_BITS;
83 for (UInt32 i = 0; i < numProps; i++)
84 {
85 const PROPVARIANT &prop = props[i];
86 const PROPID propID = propIDs[i];
87 if (propID >= NCoderPropID::kReduceSize
88 // && propID != NCoderPropID::kHashBits
89 )
90 continue;
91 switch (propID)
92 {
93 /*
94 case NCoderPropID::kDefaultProp:
95 {
96 if (prop.vt != VT_UI4)
97 return E_INVALIDARG;
98 UInt32 v = prop.ulVal;
99 if (v > 31)
100 return E_INVALIDARG;
101 relatLim = (UInt32)1 << v;
102 break;
103 }
104 case NCoderPropID::kHashBits:
105 {
106 if (prop.vt != VT_UI4)
107 return E_INVALIDARG;
108 UInt32 v = prop.ulVal;
109 if (v > 31)
110 return E_INVALIDARG;
111 excludeRangeBits = v;
112 break;
113 }
114 */
115 case NCoderPropID::kDictionarySize:
116 {
117 if (prop.vt != VT_UI4)
118 return E_INVALIDARG;
119 relatLim = prop.ulVal;
120 if (relatLim > BCJ2_ENC_RELAT_LIMIT_MAX)
121 return E_INVALIDARG;
122 break;
123 }
124 case NCoderPropID::kNumThreads:
125 case NCoderPropID::kLevel:
126 continue;
127 default: return E_INVALIDARG;
128 }
129 }
130 _relatLim = relatLim;
131 // _excludeRangeBits = excludeRangeBits;
132 return S_OK;
133 }
134
135
CodeReal(ISequentialInStream * const * inStreams,const UInt64 * const * inSizes,UInt32 numInStreams,ISequentialOutStream * const * outStreams,const UInt64 * const *,UInt32 numOutStreams,ICompressProgressInfo * progress)136 HRESULT CEncoder::CodeReal(
137 ISequentialInStream * const *inStreams, const UInt64 * const *inSizes, UInt32 numInStreams,
138 ISequentialOutStream * const *outStreams, const UInt64 * const * /* outSizes */, UInt32 numOutStreams,
139 ICompressProgressInfo *progress)
140 {
141 if (numInStreams != 1 || numOutStreams != BCJ2_NUM_STREAMS)
142 return E_INVALIDARG;
143
144 RINOK(Alloc())
145
146 CBcj2Enc_ip_unsigned fileSize_minus1 = BCJ2_ENC_FileSizeField_UNLIMITED;
147 if (inSizes && inSizes[0])
148 {
149 const UInt64 inSize = *inSizes[0];
150 #ifdef BCJ2_ENC_FileSize_MAX
151 if (inSize <= BCJ2_ENC_FileSize_MAX)
152 #endif
153 fileSize_minus1 = BCJ2_ENC_GET_FileSizeField_VAL_FROM_FileSize(inSize);
154 }
155
156 Z7_DECL_CMyComPtr_QI_FROM(ICompressGetSubStreamSize, getSubStreamSize, inStreams[0])
157
158 CBcj2Enc enc;
159 enc.src = _bufs[BCJ2_NUM_STREAMS];
160 enc.srcLim = enc.src;
161 {
162 for (unsigned i = 0; i < BCJ2_NUM_STREAMS; i++)
163 {
164 enc.bufs[i] = _bufs[i];
165 enc.lims[i] = _bufs[i] + _bufsSizes[i];
166 }
167 }
168 Bcj2Enc_Init(&enc);
169 enc.fileIp64 = 0;
170 enc.fileSize64_minus1 = fileSize_minus1;
171 enc.relatLimit = _relatLim;
172 // enc.relatExcludeBits = _excludeRangeBits;
173 enc.finishMode = BCJ2_ENC_FINISH_MODE_CONTINUE;
174
175 // Varibales that correspond processed data in input stream:
176 UInt64 inPos_without_Temp = 0; // it doesn't include data in enc.temp[]
177 UInt64 inPos_with_Temp = 0; // it includes data in enc.temp[]
178
179 UInt64 prevProgress = 0;
180 UInt64 totalRead = 0; // size read from input stream
181 UInt64 outSizeRc = 0;
182 UInt64 subStream_Index = 0;
183 UInt64 subStream_StartPos = 0; // global start offset of subStreams[subStream_Index]
184 UInt64 subStream_Size = 0;
185 const Byte *srcLim_Read = _bufs[BCJ2_NUM_STREAMS];
186 bool readWasFinished = false;
187 bool isAccurate = false;
188 bool wasUnknownSize = false;
189
190 for (;;)
191 {
192 if (readWasFinished && enc.srcLim == srcLim_Read)
193 enc.finishMode = BCJ2_ENC_FINISH_MODE_END_STREAM;
194
195 // for debug:
196 // for (int y=0;y<100;y++) { CBcj2Enc enc2 = enc; Bcj2Enc_Encode(&enc2); }
197
198 Bcj2Enc_Encode(&enc);
199
200 inPos_with_Temp = totalRead - (size_t)(srcLim_Read - enc.src);
201 inPos_without_Temp = inPos_with_Temp - Bcj2Enc_Get_AvailInputSize_in_Temp(&enc);
202
203 // if (inPos_without_Temp != enc.ip64) return E_FAIL;
204
205 if (Bcj2Enc_IsFinished(&enc))
206 break;
207
208 if (enc.state < BCJ2_NUM_STREAMS)
209 {
210 if (enc.bufs[enc.state] != enc.lims[enc.state])
211 return E_FAIL;
212 const size_t curSize = (size_t)(enc.bufs[enc.state] - _bufs[enc.state]);
213 // printf("Write stream = %2d %6d\n", enc.state, curSize);
214 RINOK(WriteStream(outStreams[enc.state], _bufs[enc.state], curSize))
215 if (enc.state == BCJ2_STREAM_RC)
216 outSizeRc += curSize;
217 enc.bufs[enc.state] = _bufs[enc.state];
218 enc.lims[enc.state] = _bufs[enc.state] + _bufsSizes[enc.state];
219 }
220 else
221 {
222 if (enc.state != BCJ2_ENC_STATE_ORIG)
223 return E_FAIL;
224 // (enc.state == BCJ2_ENC_STATE_ORIG)
225 if (enc.src != enc.srcLim)
226 return E_FAIL;
227 if (enc.finishMode != BCJ2_ENC_FINISH_MODE_CONTINUE
228 && Bcj2Enc_Get_AvailInputSize_in_Temp(&enc) != 0)
229 return E_FAIL;
230
231 if (enc.src == srcLim_Read)
232 {
233 if (readWasFinished)
234 return E_FAIL;
235 UInt32 curSize = _bufsSizes[BCJ2_NUM_STREAMS];
236 RINOK(inStreams[0]->Read(_bufs[BCJ2_NUM_STREAMS], curSize, &curSize))
237 // printf("Read %6u bytes\n", curSize);
238 if (curSize == 0)
239 readWasFinished = true;
240 totalRead += curSize;
241 enc.src = _bufs[BCJ2_NUM_STREAMS];
242 srcLim_Read = _bufs[BCJ2_NUM_STREAMS] + curSize;
243 }
244 enc.srcLim = srcLim_Read;
245
246 if (getSubStreamSize)
247 {
248 /* we set base default conversions options that will be used,
249 if subStream related options will be not OK */
250 enc.fileIp64 = 0;
251 enc.fileSize64_minus1 = fileSize_minus1;
252 for (;;)
253 {
254 UInt64 nextPos;
255 if (isAccurate)
256 nextPos = subStream_StartPos + subStream_Size;
257 else
258 {
259 const HRESULT hres = getSubStreamSize->GetSubStreamSize(subStream_Index, &subStream_Size);
260 if (hres != S_OK)
261 {
262 enc.finishMode = BCJ2_ENC_FINISH_MODE_CONTINUE;
263 /* if sub-stream size is unknown, we use default settings.
264 We still can recover to normal mode for next sub-stream,
265 if GetSubStreamSize() will return S_OK, when current
266 sub-stream will be finished.
267 */
268 if (hres == S_FALSE)
269 {
270 wasUnknownSize = true;
271 break;
272 }
273 if (hres == E_NOTIMPL)
274 {
275 getSubStreamSize.Release();
276 break;
277 }
278 return hres;
279 }
280 // printf("GetSubStreamSize %6u : %6u \n", (unsigned)subStream_Index, (unsigned)subStream_Size);
281 nextPos = subStream_StartPos + subStream_Size;
282 if ((Int64)subStream_Size == -1)
283 {
284 /* it's not expected, but (-1) can mean unknown size. */
285 enc.finishMode = BCJ2_ENC_FINISH_MODE_CONTINUE;
286 wasUnknownSize = true;
287 break;
288 }
289 if (nextPos < subStream_StartPos)
290 return E_FAIL;
291 isAccurate =
292 (nextPos < totalRead
293 || (nextPos <= totalRead && readWasFinished));
294 }
295
296 /* (nextPos) is estimated end position of current sub_stream.
297 But only (totalRead) and (readWasFinished) values
298 can confirm that this estimated end position is accurate.
299 That end position is accurate, if it can't be changed in
300 further calls of GetSubStreamSize() */
301
302 /* (nextPos < inPos_with_Temp) is unexpected case here, that we
303 can get if from some incorrect ICompressGetSubStreamSize object,
304 where new GetSubStreamSize() call returns smaller size than
305 confirmed by Read() size from previous GetSubStreamSize() call.
306 */
307 if (nextPos < inPos_with_Temp)
308 {
309 if (wasUnknownSize)
310 {
311 /* that case can be complicated for recovering.
312 so we disable sub-streams requesting. */
313 enc.finishMode = BCJ2_ENC_FINISH_MODE_CONTINUE;
314 getSubStreamSize.Release();
315 break;
316 }
317 return E_FAIL; // to stop after failure
318 }
319
320 if (nextPos <= inPos_with_Temp)
321 {
322 // (nextPos == inPos_with_Temp)
323 /* CBcj2Enc encoder requires to finish each [non-empty] block (sub-stream)
324 with BCJ2_ENC_FINISH_MODE_END_BLOCK
325 or with BCJ2_ENC_FINISH_MODE_END_STREAM for last block:
326 And we send data of new block to CBcj2Enc, only if previous block was finished.
327 So we switch to next sub-stream if after Bcj2Enc_Encode() call we have
328 && (enc.finishMode != BCJ2_ENC_FINISH_MODE_CONTINUE)
329 && (nextPos == inPos_with_Temp)
330 && (enc.state == BCJ2_ENC_STATE_ORIG)
331 */
332 if (enc.finishMode != BCJ2_ENC_FINISH_MODE_CONTINUE)
333 {
334 /* subStream_StartPos is increased only here.
335 (subStream_StartPos == inPos_with_Temp) : at start
336 (subStream_StartPos <= inPos_with_Temp) : will be later
337 */
338 subStream_StartPos = nextPos;
339 subStream_Size = 0;
340 wasUnknownSize = false;
341 subStream_Index++;
342 isAccurate = false;
343 // we don't change finishMode here
344 continue;
345 }
346 }
347
348 enc.finishMode = BCJ2_ENC_FINISH_MODE_CONTINUE;
349 /* for (!isAccurate) case:
350 (totalRead <= real_end_of_subStream)
351 so we can use BCJ2_ENC_FINISH_MODE_CONTINUE up to (totalRead)
352 // we don't change settings at the end of substream, if settings were unknown,
353 */
354
355 /* if (wasUnknownSize) then we can't trust size of that sub-stream.
356 so we use default settings instead */
357 if (!wasUnknownSize)
358 #ifdef BCJ2_ENC_FileSize_MAX
359 if (subStream_Size <= BCJ2_ENC_FileSize_MAX)
360 #endif
361 {
362 enc.fileIp64 =
363 (CBcj2Enc_ip_unsigned)(
364 (CBcj2Enc_ip_signed)enc.ip64 +
365 (CBcj2Enc_ip_signed)(subStream_StartPos - inPos_without_Temp));
366 Bcj2Enc_SET_FileSize(&enc, subStream_Size)
367 }
368
369 if (isAccurate)
370 {
371 /* (real_end_of_subStream == nextPos <= totalRead)
372 So we can use BCJ2_ENC_FINISH_MODE_END_BLOCK up to (nextPos). */
373 const size_t rem = (size_t)(totalRead - nextPos);
374 if ((size_t)(enc.srcLim - enc.src) < rem)
375 return E_FAIL;
376 enc.srcLim -= rem;
377 enc.finishMode = BCJ2_ENC_FINISH_MODE_END_BLOCK;
378 }
379
380 break;
381 } // for() loop
382 } // getSubStreamSize
383 }
384
385 if (progress && inPos_without_Temp - prevProgress >= (1 << 22))
386 {
387 prevProgress = inPos_without_Temp;
388 const UInt64 outSize2 = inPos_without_Temp + outSizeRc +
389 (size_t)(enc.bufs[BCJ2_STREAM_RC] - _bufs[BCJ2_STREAM_RC]);
390 // printf("progress %8u, %8u\n", (unsigned)inSize2, (unsigned)outSize2);
391 RINOK(progress->SetRatioInfo(&inPos_without_Temp, &outSize2))
392 }
393 }
394
395 for (unsigned i = 0; i < BCJ2_NUM_STREAMS; i++)
396 {
397 RINOK(WriteStream(outStreams[i], _bufs[i], (size_t)(enc.bufs[i] - _bufs[i])))
398 }
399 // if (inPos_without_Temp != subStream_StartPos + subStream_Size) return E_FAIL;
400 return S_OK;
401 }
402
403
Z7_COM7F_IMF(CEncoder::Code (ISequentialInStream * const * inStreams,const UInt64 * const * inSizes,UInt32 numInStreams,ISequentialOutStream * const * outStreams,const UInt64 * const * outSizes,UInt32 numOutStreams,ICompressProgressInfo * progress))404 Z7_COM7F_IMF(CEncoder::Code(
405 ISequentialInStream * const *inStreams, const UInt64 * const *inSizes, UInt32 numInStreams,
406 ISequentialOutStream * const *outStreams, const UInt64 * const *outSizes, UInt32 numOutStreams,
407 ICompressProgressInfo *progress))
408 {
409 try
410 {
411 return CodeReal(inStreams, inSizes, numInStreams, outStreams, outSizes,numOutStreams, progress);
412 }
413 catch(...) { return E_FAIL; }
414 }
415
416 #endif
417
418
419
420
421
422
CDecoder()423 CDecoder::CDecoder():
424 _finishMode(false)
425 #ifndef Z7_NO_READ_FROM_CODER
426 , _outSizeDefined(false)
427 , _outSize(0)
428 , _outSize_Processed(0)
429 #endif
430 {}
431
Z7_COM7F_IMF(CDecoder::SetInBufSize (UInt32 streamIndex,UInt32 size))432 Z7_COM7F_IMF(CDecoder::SetInBufSize(UInt32 streamIndex, UInt32 size))
433 { _bufsSizes_New[streamIndex] = size; return S_OK; }
Z7_COM7F_IMF(CDecoder::SetOutBufSize (UInt32,UInt32 size))434 Z7_COM7F_IMF(CDecoder::SetOutBufSize(UInt32, UInt32 size))
435 { _bufsSizes_New[BCJ2_NUM_STREAMS] = size; return S_OK; }
436
Z7_COM7F_IMF(CDecoder::SetFinishMode (UInt32 finishMode))437 Z7_COM7F_IMF(CDecoder::SetFinishMode(UInt32 finishMode))
438 {
439 _finishMode = (finishMode != 0);
440 return S_OK;
441 }
442
InitCommon()443 void CBaseDecoder::InitCommon()
444 {
445 for (unsigned i = 0; i < BCJ2_NUM_STREAMS; i++)
446 {
447 dec.lims[i] = dec.bufs[i] = _bufs[i];
448 _readRes[i] = S_OK;
449 _extraSizes[i] = 0;
450 _readSizes[i] = 0;
451 }
452 Bcj2Dec_Init(&dec);
453 }
454
455
456 /* call ReadInStream() only after Bcj2Dec_Decode().
457 input requirement:
458 (dec.state < BCJ2_NUM_STREAMS)
459 */
ReadInStream(ISequentialInStream * inStream)460 void CBaseDecoder::ReadInStream(ISequentialInStream *inStream)
461 {
462 const unsigned state = dec.state;
463 UInt32 total;
464 {
465 Byte *buf = _bufs[state];
466 const Byte *cur = dec.bufs[state];
467 // if (cur != dec.lims[state]) throw 1; // unexpected case
468 dec.lims[state] =
469 dec.bufs[state] = buf;
470 total = (UInt32)_extraSizes[state];
471 for (UInt32 i = 0; i < total; i++)
472 buf[i] = cur[i];
473 }
474
475 if (_readRes[state] != S_OK)
476 return;
477
478 do
479 {
480 UInt32 curSize = _bufsSizes[state] - total;
481 // if (state == 0) curSize = 0; // for debug
482 // curSize = 7; // for debug
483 /* even if we have reached provided inSizes[state] limit,
484 we call Read() with (curSize != 0), because
485 we want the called handler of stream->Read() could
486 execute required Init/Flushing code even for empty stream.
487 In another way we could call Read() with (curSize == 0) for
488 finished streams, but some Read() handlers can ignore Read(size=0) calls.
489 */
490 const HRESULT hres = inStream->Read(_bufs[state] + total, curSize, &curSize);
491 _readRes[state] = hres;
492 if (curSize == 0)
493 break;
494 _readSizes[state] += curSize;
495 total += curSize;
496 if (hres != S_OK)
497 break;
498 }
499 while (total < 4 && BCJ2_IS_32BIT_STREAM(state));
500
501 /* we exit from decoding loop here, if we can't
502 provide new data for input stream.
503 Usually it's normal exit after full stream decoding. */
504 if (total == 0)
505 return;
506
507 if (BCJ2_IS_32BIT_STREAM(state))
508 {
509 const unsigned extra = ((unsigned)total & 3);
510 _extraSizes[state] = extra;
511 if (total < 4)
512 {
513 if (_readRes[state] == S_OK)
514 _readRes[state] = S_FALSE; // actually it's stream error. So maybe we need another error code.
515 return;
516 }
517 total -= extra;
518 }
519
520 dec.lims[state] += total; // = _bufs[state] + total;
521 }
522
523
Z7_COM7F_IMF(CDecoder::Code (ISequentialInStream * const * inStreams,const UInt64 * const * inSizes,UInt32 numInStreams,ISequentialOutStream * const * outStreams,const UInt64 * const * outSizes,UInt32 numOutStreams,ICompressProgressInfo * progress))524 Z7_COM7F_IMF(CDecoder::Code(
525 ISequentialInStream * const *inStreams, const UInt64 * const *inSizes, UInt32 numInStreams,
526 ISequentialOutStream * const *outStreams, const UInt64 * const *outSizes, UInt32 numOutStreams,
527 ICompressProgressInfo *progress))
528 {
529 if (numInStreams != BCJ2_NUM_STREAMS || numOutStreams != 1)
530 return E_INVALIDARG;
531
532 RINOK(Alloc())
533 InitCommon();
534
535 dec.destLim = dec.dest = _bufs[BCJ2_NUM_STREAMS];
536
537 UInt64 outSizeWritten = 0;
538 UInt64 prevProgress = 0;
539
540 HRESULT hres_Crit = S_OK; // critical hres status (mostly from input stream reading)
541 HRESULT hres_Weak = S_OK; // first non-critical error code from input stream reading
542
543 for (;;)
544 {
545 if (Bcj2Dec_Decode(&dec) != SZ_OK)
546 {
547 /* it's possible only at start (first 5 bytes in RC stream) */
548 hres_Crit = S_FALSE;
549 break;
550 }
551 if (dec.state < BCJ2_NUM_STREAMS)
552 {
553 ReadInStream(inStreams[dec.state]);
554 const unsigned state = dec.state;
555 const HRESULT hres = _readRes[state];
556 if (dec.lims[state] == _bufs[state])
557 {
558 // we break decoding, if there are no new data in input stream
559 hres_Crit = hres;
560 break;
561 }
562 if (hres != S_OK && hres_Weak == S_OK)
563 hres_Weak = hres;
564 }
565 else // (BCJ2_DEC_STATE_ORIG_0 <= state <= BCJ2_STATE_ORIG)
566 {
567 {
568 const size_t curSize = (size_t)(dec.dest - _bufs[BCJ2_NUM_STREAMS]);
569 if (curSize != 0)
570 {
571 outSizeWritten += curSize;
572 RINOK(WriteStream(outStreams[0], _bufs[BCJ2_NUM_STREAMS], curSize))
573 }
574 }
575 {
576 UInt32 rem = _bufsSizes[BCJ2_NUM_STREAMS];
577 if (outSizes && outSizes[0])
578 {
579 const UInt64 outSize = *outSizes[0] - outSizeWritten;
580 if (rem > outSize)
581 rem = (UInt32)outSize;
582 }
583 dec.dest = _bufs[BCJ2_NUM_STREAMS];
584 dec.destLim = dec.dest + rem;
585 /* we exit from decoding loop here,
586 if (outSizes[0]) limit for output stream was reached */
587 if (rem == 0)
588 break;
589 }
590 }
591
592 if (progress)
593 {
594 // here we don't count additional data in dec.temp (up to 4 bytes for output stream)
595 const UInt64 processed = outSizeWritten + (size_t)(dec.dest - _bufs[BCJ2_NUM_STREAMS]);
596 if (processed - prevProgress >= (1 << 24))
597 {
598 prevProgress = processed;
599 const UInt64 inSize = processed +
600 _readSizes[BCJ2_STREAM_RC] - (size_t)(
601 dec.lims[BCJ2_STREAM_RC] -
602 dec.bufs[BCJ2_STREAM_RC]);
603 RINOK(progress->SetRatioInfo(&inSize, &prevProgress))
604 }
605 }
606 }
607
608 {
609 const size_t curSize = (size_t)(dec.dest - _bufs[BCJ2_NUM_STREAMS]);
610 if (curSize != 0)
611 {
612 outSizeWritten += curSize;
613 RINOK(WriteStream(outStreams[0], _bufs[BCJ2_NUM_STREAMS], curSize))
614 }
615 }
616
617 if (hres_Crit == S_OK) hres_Crit = hres_Weak;
618 if (hres_Crit != S_OK) return hres_Crit;
619
620 if (_finishMode)
621 {
622 if (!Bcj2Dec_IsMaybeFinished_code(&dec))
623 return S_FALSE;
624
625 /* here we support two correct ways to finish full stream decoding
626 with one of the following conditions:
627 - the end of input stream MAIN was reached
628 - the end of output stream ORIG was reached
629 Currently 7-Zip/7z code ends with (state == BCJ2_STREAM_MAIN),
630 because the sizes of MAIN and ORIG streams are known and these
631 sizes are stored in 7z archive headers.
632 And Bcj2Dec_Decode() exits with (state == BCJ2_STREAM_MAIN),
633 if both MAIN and ORIG streams have reached buffers limits.
634 But if the size of MAIN stream is not known or if the
635 size of MAIN stream includes some padding after payload data,
636 then we still can correctly finish decoding with
637 (state == BCJ2_DEC_STATE_ORIG), if we know the exact size
638 of output ORIG stream.
639 */
640 if (dec.state != BCJ2_STREAM_MAIN)
641 if (dec.state != BCJ2_DEC_STATE_ORIG)
642 return S_FALSE;
643
644 /* the caller also will know written size.
645 So the following check is optional: */
646 if (outSizes && outSizes[0] && *outSizes[0] != outSizeWritten)
647 return S_FALSE;
648
649 if (inSizes)
650 {
651 for (unsigned i = 0; i < BCJ2_NUM_STREAMS; i++)
652 {
653 /* if (inSizes[i]) is defined, we do full check for processed stream size. */
654 if (inSizes[i] && *inSizes[i] != GetProcessedSize_ForInStream(i))
655 return S_FALSE;
656 }
657 }
658 }
659
660 return S_OK;
661 }
662
663
Z7_COM7F_IMF(CDecoder::GetInStreamProcessedSize2 (UInt32 streamIndex,UInt64 * value))664 Z7_COM7F_IMF(CDecoder::GetInStreamProcessedSize2(UInt32 streamIndex, UInt64 *value))
665 {
666 *value = GetProcessedSize_ForInStream(streamIndex);
667 return S_OK;
668 }
669
670
671 #ifndef Z7_NO_READ_FROM_CODER
672
Z7_COM7F_IMF(CDecoder::SetInStream2 (UInt32 streamIndex,ISequentialInStream * inStream))673 Z7_COM7F_IMF(CDecoder::SetInStream2(UInt32 streamIndex, ISequentialInStream *inStream))
674 {
675 _inStreams[streamIndex] = inStream;
676 return S_OK;
677 }
678
Z7_COM7F_IMF(CDecoder::ReleaseInStream2 (UInt32 streamIndex))679 Z7_COM7F_IMF(CDecoder::ReleaseInStream2(UInt32 streamIndex))
680 {
681 _inStreams[streamIndex].Release();
682 return S_OK;
683 }
684
Z7_COM7F_IMF(CDecoder::SetOutStreamSize (const UInt64 * outSize))685 Z7_COM7F_IMF(CDecoder::SetOutStreamSize(const UInt64 *outSize))
686 {
687 _outSizeDefined = (outSize != NULL);
688 _outSize = 0;
689 if (_outSizeDefined)
690 _outSize = *outSize;
691 _outSize_Processed = 0;
692
693 const HRESULT res = Alloc(false); // allocForOrig
694 InitCommon();
695 dec.destLim = dec.dest = NULL;
696 return res;
697 }
698
699
Z7_COM7F_IMF(CDecoder::Read (void * data,UInt32 size,UInt32 * processedSize))700 Z7_COM7F_IMF(CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize))
701 {
702 if (processedSize)
703 *processedSize = 0;
704
705 /* Note the case:
706 The output (ORIG) stream can be empty.
707 But BCJ2_STREAM_RC stream always is not empty.
708 And we want to support full data processing for all streams.
709 We disable check (size == 0) here.
710 So if the caller calls this CDecoder::Read() with (size == 0),
711 we execute required Init/Flushing code in this CDecoder object.
712 Also this CDecoder::Read() function will call Read() for input streams.
713 So the handlers of input streams objects also can do Init/Flushing.
714 */
715 // if (size == 0) return S_OK; // disabled to allow (size == 0) processing
716
717 UInt32 totalProcessed = 0;
718
719 if (_outSizeDefined)
720 {
721 const UInt64 rem = _outSize - _outSize_Processed;
722 if (size > rem)
723 size = (UInt32)rem;
724 }
725 dec.dest = (Byte *)data;
726 dec.destLim = (const Byte *)data + size;
727
728 HRESULT res = S_OK;
729
730 for (;;)
731 {
732 if (Bcj2Dec_Decode(&dec) != SZ_OK)
733 return S_FALSE; // this error can be only at start of stream
734 {
735 const UInt32 curSize = (UInt32)(size_t)(dec.dest - (Byte *)data);
736 if (curSize != 0)
737 {
738 data = (void *)((Byte *)data + curSize);
739 size -= curSize;
740 _outSize_Processed += curSize;
741 totalProcessed += curSize;
742 if (processedSize)
743 *processedSize = totalProcessed;
744 }
745 }
746 if (dec.state >= BCJ2_NUM_STREAMS)
747 break;
748 ReadInStream(_inStreams[dec.state]);
749 if (dec.lims[dec.state] == _bufs[dec.state])
750 {
751 /* we break decoding, if there are no new data in input stream.
752 and we ignore error code, if some data were written to output buffer. */
753 if (totalProcessed == 0)
754 res = _readRes[dec.state];
755 break;
756 }
757 }
758
759 if (res == S_OK)
760 if (_finishMode && _outSizeDefined && _outSize == _outSize_Processed)
761 {
762 if (!Bcj2Dec_IsMaybeFinished_code(&dec))
763 return S_FALSE;
764 if (dec.state != BCJ2_STREAM_MAIN)
765 if (dec.state != BCJ2_DEC_STATE_ORIG)
766 return S_FALSE;
767 }
768
769 return res;
770 }
771
772 #endif
773
774 }}
775
776
777 /*
778 extern "C"
779 {
780 extern UInt32 bcj2_stats[256 + 2][2];
781 }
782
783 static class CBcj2Stat
784 {
785 public:
786 ~CBcj2Stat()
787 {
788 printf("\nBCJ2 stat:");
789 unsigned sums[2] = { 0, 0 };
790 int i;
791 for (i = 2; i < 256 + 2; i++)
792 {
793 sums[0] += bcj2_stats[i][0];
794 sums[1] += bcj2_stats[i][1];
795 }
796 const unsigned sums2 = sums[0] + sums[1];
797 for (int vi = 0; vi < 256 + 3; vi++)
798 {
799 printf("\n");
800 UInt32 n0, n1;
801 if (vi < 4)
802 printf("\n");
803
804 if (vi < 2)
805 i = vi;
806 else if (vi == 2)
807 i = -1;
808 else
809 i = vi - 1;
810
811 if (i < 0)
812 {
813 n0 = sums[0];
814 n1 = sums[1];
815 printf("calls :");
816 }
817 else
818 {
819 if (i == 0)
820 printf("jcc :");
821 else if (i == 1)
822 printf("jump :");
823 else
824 printf("call %02x :", i - 2);
825 n0 = bcj2_stats[i][0];
826 n1 = bcj2_stats[i][1];
827 }
828
829 const UInt32 sum = n0 + n1;
830 printf(" %10u", sum);
831
832 #define PRINT_PERC(val, sum) \
833 { UInt32 _sum = sum; if (_sum == 0) _sum = 1; \
834 printf(" %7.3f %%", (double)((double)val * (double)100 / (double)_sum )); }
835
836 if (i >= 2 || i < 0)
837 {
838 PRINT_PERC(sum, sums2);
839 }
840 else
841 printf("%10s", "");
842
843 printf(" :%10u", n0);
844 PRINT_PERC(n0, sum);
845
846 printf(" :%10u", n1);
847 PRINT_PERC(n1, sum);
848 }
849 printf("\n\n");
850 fflush(stdout);
851 }
852 } g_CBcjStat;
853 */
854