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 -= (UInt32)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 /* v23.02: we call Read(0) for BCJ2_STREAM_CALL and BCJ2_STREAM_JUMP streams,
660 if there were no Read() calls for such stream.
661 So the handlers of these input streams objects can do
662 Init/Flushing even for case when stream is empty:
663 */
664 for (unsigned i = BCJ2_STREAM_CALL; i < BCJ2_STREAM_CALL + 2; i++)
665 {
666 if (_readSizes[i])
667 continue;
668 Byte b;
669 UInt32 processed;
670 RINOK(inStreams[i]->Read(&b, 0, &processed))
671 }
672 }
673
674 return S_OK;
675 }
676
677
Z7_COM7F_IMF(CDecoder::GetInStreamProcessedSize2 (UInt32 streamIndex,UInt64 * value))678 Z7_COM7F_IMF(CDecoder::GetInStreamProcessedSize2(UInt32 streamIndex, UInt64 *value))
679 {
680 *value = GetProcessedSize_ForInStream(streamIndex);
681 return S_OK;
682 }
683
684
685 #ifndef Z7_NO_READ_FROM_CODER
686
Z7_COM7F_IMF(CDecoder::SetInStream2 (UInt32 streamIndex,ISequentialInStream * inStream))687 Z7_COM7F_IMF(CDecoder::SetInStream2(UInt32 streamIndex, ISequentialInStream *inStream))
688 {
689 _inStreams[streamIndex] = inStream;
690 return S_OK;
691 }
692
Z7_COM7F_IMF(CDecoder::ReleaseInStream2 (UInt32 streamIndex))693 Z7_COM7F_IMF(CDecoder::ReleaseInStream2(UInt32 streamIndex))
694 {
695 _inStreams[streamIndex].Release();
696 return S_OK;
697 }
698
Z7_COM7F_IMF(CDecoder::SetOutStreamSize (const UInt64 * outSize))699 Z7_COM7F_IMF(CDecoder::SetOutStreamSize(const UInt64 *outSize))
700 {
701 _outSizeDefined = (outSize != NULL);
702 _outSize = 0;
703 if (_outSizeDefined)
704 _outSize = *outSize;
705 _outSize_Processed = 0;
706
707 const HRESULT res = Alloc(false); // allocForOrig
708 InitCommon();
709 dec.destLim = dec.dest = NULL;
710 return res;
711 }
712
713
Z7_COM7F_IMF(CDecoder::Read (void * data,UInt32 size,UInt32 * processedSize))714 Z7_COM7F_IMF(CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize))
715 {
716 if (processedSize)
717 *processedSize = 0;
718
719 /* Note the case:
720 The output (ORIG) stream can be empty.
721 But BCJ2_STREAM_RC stream always is not empty.
722 And we want to support full data processing for all streams.
723 We disable check (size == 0) here.
724 So if the caller calls this CDecoder::Read() with (size == 0),
725 we execute required Init/Flushing code in this CDecoder object.
726 Also this CDecoder::Read() function will call Read() for input streams.
727 So the handlers of input streams objects also can do Init/Flushing.
728 */
729 // if (size == 0) return S_OK; // disabled to allow (size == 0) processing
730
731 UInt32 totalProcessed = 0;
732
733 if (_outSizeDefined)
734 {
735 const UInt64 rem = _outSize - _outSize_Processed;
736 if (size > rem)
737 size = (UInt32)rem;
738 }
739 dec.dest = (Byte *)data;
740 dec.destLim = (const Byte *)data + size;
741
742 HRESULT res = S_OK;
743
744 for (;;)
745 {
746 if (Bcj2Dec_Decode(&dec) != SZ_OK)
747 return S_FALSE; // this error can be only at start of stream
748 {
749 const UInt32 curSize = (UInt32)(size_t)(dec.dest - (Byte *)data);
750 if (curSize != 0)
751 {
752 data = (void *)((Byte *)data + curSize);
753 size -= curSize;
754 _outSize_Processed += curSize;
755 totalProcessed += curSize;
756 if (processedSize)
757 *processedSize = totalProcessed;
758 }
759 }
760 if (dec.state >= BCJ2_NUM_STREAMS)
761 break;
762 ReadInStream(_inStreams[dec.state]);
763 if (dec.lims[dec.state] == _bufs[dec.state])
764 {
765 /* we break decoding, if there are no new data in input stream.
766 and we ignore error code, if some data were written to output buffer. */
767 if (totalProcessed == 0)
768 res = _readRes[dec.state];
769 break;
770 }
771 }
772
773 if (res == S_OK)
774 if (_finishMode && _outSizeDefined && _outSize == _outSize_Processed)
775 {
776 if (!Bcj2Dec_IsMaybeFinished_code(&dec))
777 return S_FALSE;
778 if (dec.state != BCJ2_STREAM_MAIN)
779 if (dec.state != BCJ2_DEC_STATE_ORIG)
780 return S_FALSE;
781 }
782
783 return res;
784 }
785
786 #endif
787
788 }}
789
790
791 /*
792 extern "C"
793 {
794 extern UInt32 bcj2_stats[256 + 2][2];
795 }
796
797 static class CBcj2Stat
798 {
799 public:
800 ~CBcj2Stat()
801 {
802 printf("\nBCJ2 stat:");
803 unsigned sums[2] = { 0, 0 };
804 int i;
805 for (i = 2; i < 256 + 2; i++)
806 {
807 sums[0] += bcj2_stats[i][0];
808 sums[1] += bcj2_stats[i][1];
809 }
810 const unsigned sums2 = sums[0] + sums[1];
811 for (int vi = 0; vi < 256 + 3; vi++)
812 {
813 printf("\n");
814 UInt32 n0, n1;
815 if (vi < 4)
816 printf("\n");
817
818 if (vi < 2)
819 i = vi;
820 else if (vi == 2)
821 i = -1;
822 else
823 i = vi - 1;
824
825 if (i < 0)
826 {
827 n0 = sums[0];
828 n1 = sums[1];
829 printf("calls :");
830 }
831 else
832 {
833 if (i == 0)
834 printf("jcc :");
835 else if (i == 1)
836 printf("jump :");
837 else
838 printf("call %02x :", i - 2);
839 n0 = bcj2_stats[i][0];
840 n1 = bcj2_stats[i][1];
841 }
842
843 const UInt32 sum = n0 + n1;
844 printf(" %10u", sum);
845
846 #define PRINT_PERC(val, sum) \
847 { UInt32 _sum = sum; if (_sum == 0) _sum = 1; \
848 printf(" %7.3f %%", (double)((double)val * (double)100 / (double)_sum )); }
849
850 if (i >= 2 || i < 0)
851 {
852 PRINT_PERC(sum, sums2);
853 }
854 else
855 printf("%10s", "");
856
857 printf(" :%10u", n0);
858 PRINT_PERC(n0, sum);
859
860 printf(" :%10u", n1);
861 PRINT_PERC(n1, sum);
862 }
863 printf("\n\n");
864 fflush(stdout);
865 }
866 } g_CBcjStat;
867 */
868