xref: /aosp_15_r20/external/lzma/CPP/7zip/Compress/Bcj2Coder.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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