xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/XzHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // XzHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/Alloc.h"
6 
7 #include "../../Common/ComTry.h"
8 #include "../../Common/Defs.h"
9 #include "../../Common/IntToString.h"
10 #include "../../Common/MyBuffer.h"
11 #include "../../Common/StringToInt.h"
12 
13 #include "../../Windows/PropVariant.h"
14 #include "../../Windows/System.h"
15 
16 #include "../Common/CWrappers.h"
17 #include "../Common/ProgressUtils.h"
18 #include "../Common/RegisterArc.h"
19 #include "../Common/StreamUtils.h"
20 
21 #include "../Compress/CopyCoder.h"
22 #include "../Compress/XzDecoder.h"
23 #include "../Compress/XzEncoder.h"
24 
25 #include "IArchive.h"
26 
27 #include "Common/HandlerOut.h"
28 
29 using namespace NWindows;
30 
31 namespace NArchive {
32 namespace NXz {
33 
34 #define k_LZMA2_Name "LZMA2"
35 
36 
37 struct CBlockInfo
38 {
39   unsigned StreamFlags;
40   UInt64 PackPos;
41   UInt64 PackSize; // pure value from Index record, it doesn't include pad zeros
42   UInt64 UnpackPos;
43 };
44 
45 
46 Z7_class_CHandler_final:
47   public IInArchive,
48   public IArchiveOpenSeq,
49   public IInArchiveGetStream,
50   public ISetProperties,
51  #ifndef Z7_EXTRACT_ONLY
52   public IOutArchive,
53  #endif
54   public CMyUnknownImp,
55  #ifndef Z7_EXTRACT_ONLY
56   public CMultiMethodProps
57  #else
58   public CCommonMethodProps
59  #endif
60 {
61   Z7_COM_QI_BEGIN2(IInArchive)
62   Z7_COM_QI_ENTRY(IArchiveOpenSeq)
63   Z7_COM_QI_ENTRY(IInArchiveGetStream)
64   Z7_COM_QI_ENTRY(ISetProperties)
65  #ifndef Z7_EXTRACT_ONLY
66   Z7_COM_QI_ENTRY(IOutArchive)
67  #endif
68   Z7_COM_QI_END
69   Z7_COM_ADDREF_RELEASE
70 
71   Z7_IFACE_COM7_IMP(IInArchive)
72   Z7_IFACE_COM7_IMP(IArchiveOpenSeq)
73   Z7_IFACE_COM7_IMP(IInArchiveGetStream)
74   Z7_IFACE_COM7_IMP(ISetProperties)
75  #ifndef Z7_EXTRACT_ONLY
76   Z7_IFACE_COM7_IMP(IOutArchive)
77  #endif
78 
79   bool _stat_defined;
80   bool _stat2_defined;
81   bool _isArc;
82   bool _needSeekToStart;
83   bool _firstBlockWasRead;
84   SRes _stat2_decode_SRes;
85 
86   CXzStatInfo _stat;    // it's stat from backward parsing
87   CXzStatInfo _stat2;   // it's data from forward parsing, if the decoder was called
88 
89   const CXzStatInfo *GetStat() const
90   {
91     if (_stat_defined) return &_stat;
92     if (_stat2_defined) return &_stat2;
93     return NULL;
94   }
95 
96   AString _methodsString;
97 
98 
99   #ifndef Z7_EXTRACT_ONLY
100 
101   UInt32 _filterId;
102   UInt64 _numSolidBytes;
103 
104   void InitXz()
105   {
106     _filterId = 0;
107     _numSolidBytes = XZ_PROPS_BLOCK_SIZE_AUTO;
108   }
109 
110   #endif
111 
112 
113   void Init()
114   {
115     #ifndef Z7_EXTRACT_ONLY
116       InitXz();
117       CMultiMethodProps::Init();
118     #else
119       CCommonMethodProps::InitCommon();
120     #endif
121   }
122 
123   HRESULT SetProperty(const wchar_t *name, const PROPVARIANT &value);
124 
125   HRESULT Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback);
126 
127   HRESULT Decode(NCompress::NXz::CDecoder &decoder,
128       ISequentialInStream *seqInStream,
129       ISequentialOutStream *outStream,
130       ICompressProgressInfo *progress)
131   {
132     #ifndef Z7_ST
133     decoder._numThreads = _numThreads;
134     #endif
135     decoder._memUsage = _memUsage_Decompress;
136 
137     const HRESULT hres = decoder.Decode(seqInStream, outStream,
138         NULL, // *outSizeLimit
139         true, // finishStream
140         progress);
141 
142     if (decoder.MainDecodeSRes_wasUsed
143         && decoder.MainDecodeSRes != SZ_ERROR_MEM
144         && decoder.MainDecodeSRes != SZ_ERROR_UNSUPPORTED)
145     {
146       // if (!_stat2_defined)
147       {
148         _stat2_decode_SRes = decoder.MainDecodeSRes;
149         _stat2 = decoder.Stat;
150         _stat2_defined = true;
151       }
152     }
153 
154     if (hres == S_OK && progress)
155     {
156       // RINOK(
157       progress->SetRatioInfo(&decoder.Stat.InSize, &decoder.Stat.OutSize);
158     }
159     return hres;
160   }
161 
162 public:
163   CBlockInfo *_blocks;
164   size_t _blocksArraySize;
165   UInt64 _maxBlocksSize;
166   CMyComPtr<IInStream> _stream;
167   CMyComPtr<ISequentialInStream> _seqStream;
168 
169   CXzBlock _firstBlock;
170 
171   CHandler();
172   ~CHandler();
173 
174   HRESULT SeekToPackPos(UInt64 pos)
175   {
176     return InStream_SeekSet(_stream, pos);
177   }
178 };
179 
180 
181 CHandler::CHandler():
182     _blocks(NULL),
183     _blocksArraySize(0)
184 {
185   #ifndef Z7_EXTRACT_ONLY
186   InitXz();
187   #endif
188 }
189 
190 CHandler::~CHandler()
191 {
192   MyFree(_blocks);
193 }
194 
195 
196 static const Byte kProps[] =
197 {
198   kpidSize,
199   kpidPackSize,
200   kpidMethod
201 };
202 
203 static const Byte kArcProps[] =
204 {
205   kpidMethod,
206   kpidNumStreams,
207   kpidNumBlocks,
208   kpidClusterSize,
209   kpidCharacts
210 };
211 
212 IMP_IInArchive_Props
213 IMP_IInArchive_ArcProps
214 
215 static void Lzma2PropToString(AString &s, unsigned prop)
216 {
217   char c = 0;
218   UInt32 size;
219   if ((prop & 1) == 0)
220     size = prop / 2 + 12;
221   else
222   {
223     c = 'k';
224     size = (UInt32)(2 | (prop & 1)) << (prop / 2 + 1);
225     if (prop > 17)
226     {
227       size >>= 10;
228       c = 'm';
229     }
230   }
231   s.Add_UInt32(size);
232   if (c != 0)
233     s.Add_Char(c);
234 }
235 
236 struct CMethodNamePair
237 {
238   UInt32 Id;
239   const char *Name;
240 };
241 
242 static const CMethodNamePair g_NamePairs[] =
243 {
244   { XZ_ID_Subblock, "SB" },
245   { XZ_ID_Delta, "Delta" },
246   { XZ_ID_X86, "BCJ" },
247   { XZ_ID_PPC, "PPC" },
248   { XZ_ID_IA64, "IA64" },
249   { XZ_ID_ARM, "ARM" },
250   { XZ_ID_ARMT, "ARMT" },
251   { XZ_ID_SPARC, "SPARC" },
252   { XZ_ID_ARM64, "ARM64" },
253   { XZ_ID_RISCV, "RISCV" },
254   { XZ_ID_LZMA2, "LZMA2" }
255 };
256 
257 static void AddMethodString(AString &s, const CXzFilter &f)
258 {
259   const char *p = NULL;
260   for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_NamePairs); i++)
261     if (g_NamePairs[i].Id == f.id)
262     {
263       p = g_NamePairs[i].Name;
264       break;
265     }
266   char temp[32];
267   if (!p)
268   {
269     ::ConvertUInt64ToString(f.id, temp);
270     p = temp;
271   }
272 
273   s += p;
274 
275   if (f.propsSize > 0)
276   {
277     s.Add_Colon();
278     if (f.id == XZ_ID_LZMA2 && f.propsSize == 1)
279       Lzma2PropToString(s, f.props[0]);
280     else if (f.id == XZ_ID_Delta && f.propsSize == 1)
281       s.Add_UInt32((UInt32)f.props[0] + 1);
282     else if (f.id == XZ_ID_ARM64 && f.propsSize == 1)
283       s.Add_UInt32((UInt32)f.props[0] + 16 + 2);
284     else
285     {
286       s.Add_Char('[');
287       for (UInt32 bi = 0; bi < f.propsSize; bi++)
288       {
289         const unsigned v = f.props[bi];
290         s.Add_Char(GET_HEX_CHAR_UPPER(v >> 4));
291         s.Add_Char(GET_HEX_CHAR_UPPER(v & 15));
292       }
293       s.Add_Char(']');
294     }
295   }
296 }
297 
298 static const char * const kChecks[] =
299 {
300     "NoCheck"
301   , "CRC32"
302   , NULL
303   , NULL
304   , "CRC64"
305   , NULL
306   , NULL
307   , NULL
308   , NULL
309   , NULL
310   , "SHA256"
311   , NULL
312   , NULL
313   , NULL
314   , NULL
315   , NULL
316 };
317 
318 static void AddCheckString(AString &s, const CXzs &xzs)
319 {
320   size_t i;
321   UInt32 mask = 0;
322   for (i = 0; i < xzs.num; i++)
323     mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags));
324   for (i = 0; i <= XZ_CHECK_MASK; i++)
325     if (((mask >> i) & 1) != 0)
326     {
327       s.Add_Space_if_NotEmpty();
328       if (kChecks[i])
329         s += kChecks[i];
330       else
331       {
332         s += "Check-";
333         s.Add_UInt32((UInt32)i);
334       }
335     }
336 }
337 
338 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
339 {
340   COM_TRY_BEGIN
341   NCOM::CPropVariant prop;
342 
343   const CXzStatInfo *stat = GetStat();
344 
345   switch (propID)
346   {
347     case kpidPhySize: if (stat) prop = stat->InSize; break;
348     case kpidNumStreams: if (stat && stat->NumStreams_Defined) prop = stat->NumStreams; break;
349     case kpidNumBlocks: if (stat && stat->NumBlocks_Defined) prop = stat->NumBlocks; break;
350     case kpidUnpackSize: if (stat && stat->UnpackSize_Defined) prop = stat->OutSize; break;
351     case kpidClusterSize: if (_stat_defined && _stat.NumBlocks_Defined && stat->NumBlocks > 1) prop = _maxBlocksSize; break;
352     case kpidCharacts:
353       if (_firstBlockWasRead)
354       {
355         AString s;
356         if (XzBlock_HasPackSize(&_firstBlock))
357           s.Add_OptSpaced("BlockPackSize");
358         if (XzBlock_HasUnpackSize(&_firstBlock))
359           s.Add_OptSpaced("BlockUnpackSize");
360         if (!s.IsEmpty())
361           prop = s;
362       }
363       break;
364 
365 
366     case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
367     case kpidErrorFlags:
368     {
369       UInt32 v = 0;
370       SRes sres = _stat2_decode_SRes;
371       if (!_isArc)                      v |= kpv_ErrorFlags_IsNotArc;
372       if (sres == SZ_ERROR_INPUT_EOF)   v |= kpv_ErrorFlags_UnexpectedEnd;
373       if (_stat2_defined && _stat2.DataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd;
374       if (sres == SZ_ERROR_ARCHIVE)     v |= kpv_ErrorFlags_HeadersError;
375       if (sres == SZ_ERROR_UNSUPPORTED) v |= kpv_ErrorFlags_UnsupportedMethod;
376       if (sres == SZ_ERROR_DATA)        v |= kpv_ErrorFlags_DataError;
377       if (sres == SZ_ERROR_CRC)         v |= kpv_ErrorFlags_CrcError;
378       if (v != 0)
379         prop = v;
380       break;
381     }
382 
383     case kpidMainSubfile:
384     {
385       // debug only, comment it:
386       // if (_blocks) prop = (UInt32)0;
387       break;
388     }
389     default: break;
390   }
391   prop.Detach(value);
392   return S_OK;
393   COM_TRY_END
394 }
395 
396 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
397 {
398   *numItems = 1;
399   return S_OK;
400 }
401 
402 Z7_COM7F_IMF(CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value))
403 {
404   COM_TRY_BEGIN
405   const CXzStatInfo *stat = GetStat();
406   NCOM::CPropVariant prop;
407   switch (propID)
408   {
409     case kpidSize: if (stat && stat->UnpackSize_Defined) prop = stat->OutSize; break;
410     case kpidPackSize: if (stat) prop = stat->InSize; break;
411     case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
412     default: break;
413   }
414   prop.Detach(value);
415   return S_OK;
416   COM_TRY_END
417 }
418 
419 
420 struct COpenCallbackWrap
421 {
422   ICompressProgress vt;
423   IArchiveOpenCallback *OpenCallback;
424   HRESULT Res;
425 
426   // new clang shows "non-POD" warning for offsetof(), if we use constructor instead of Init()
427   void Init(IArchiveOpenCallback *progress);
428 };
429 
430 static SRes OpenCallbackProgress(ICompressProgressPtr pp, UInt64 inSize, UInt64 /* outSize */)
431 {
432   Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR_pp_vt_p(COpenCallbackWrap)
433   if (p->OpenCallback)
434     p->Res = p->OpenCallback->SetCompleted(NULL, &inSize);
435   return HRESULT_To_SRes(p->Res, SZ_ERROR_PROGRESS);
436 }
437 
438 void COpenCallbackWrap::Init(IArchiveOpenCallback *callback)
439 {
440   vt.Progress = OpenCallbackProgress;
441   OpenCallback = callback;
442   Res = SZ_OK;
443 }
444 
445 
446 struct CXzsCPP
447 {
448   CXzs p;
449   CXzsCPP() { Xzs_Construct(&p); }
450   ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); }
451 };
452 
453 #define kInputBufSize ((size_t)1 << 10)
454 
455 struct CLookToRead2_CPP: public CLookToRead2
456 {
457   CLookToRead2_CPP()
458   {
459     buf = NULL;
460     LookToRead2_CreateVTable(this,
461         True // Lookahead ?
462         );
463   }
464   void Alloc(size_t allocSize)
465   {
466     buf = (Byte *)MyAlloc(allocSize);
467     if (buf)
468       this->bufSize = allocSize;
469   }
470   ~CLookToRead2_CPP()
471   {
472     MyFree(buf);
473   }
474 };
475 
476 
477 static HRESULT SRes_to_Open_HRESULT(SRes res)
478 {
479   switch (res)
480   {
481     case SZ_OK: return S_OK;
482     case SZ_ERROR_MEM: return E_OUTOFMEMORY;
483     case SZ_ERROR_PROGRESS: return E_ABORT;
484     /*
485     case SZ_ERROR_UNSUPPORTED:
486     case SZ_ERROR_CRC:
487     case SZ_ERROR_DATA:
488     case SZ_ERROR_ARCHIVE:
489     case SZ_ERROR_NO_ARCHIVE:
490       return S_FALSE;
491     */
492     default: break;
493   }
494   return S_FALSE;
495 }
496 
497 
498 
499 HRESULT CHandler::Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback)
500 {
501   _needSeekToStart = true;
502 
503   {
504     CXzStreamFlags st;
505     CSeqInStreamWrap inStreamWrap;
506 
507     inStreamWrap.Init(inStream);
508 
509     SRes res = Xz_ReadHeader(&st, &inStreamWrap.vt);
510 
511     if (inStreamWrap.Res != S_OK)
512       return inStreamWrap.Res;
513     if (res != SZ_OK)
514       return SRes_to_Open_HRESULT(res);
515 
516     {
517       CXzBlock block;
518       BoolInt isIndex;
519       UInt32 headerSizeRes;
520 
521       SRes res2 = XzBlock_ReadHeader(&block, &inStreamWrap.vt, &isIndex, &headerSizeRes);
522 
523       if (inStreamWrap.Res != S_OK)
524         return inStreamWrap.Res;
525 
526       if (res2 != SZ_OK)
527       {
528         if (res2 == SZ_ERROR_INPUT_EOF)
529         {
530           _stat2_decode_SRes = res2;
531           _stream = inStream;
532           _seqStream = inStream;
533           _isArc = true;
534           return S_OK;
535         }
536 
537         if (res2 == SZ_ERROR_ARCHIVE)
538           return S_FALSE;
539       }
540       else if (!isIndex)
541       {
542         _firstBlockWasRead = true;
543         _firstBlock = block;
544 
545         unsigned numFilters = XzBlock_GetNumFilters(&block);
546         for (unsigned i = 0; i < numFilters; i++)
547         {
548           _methodsString.Add_Space_if_NotEmpty();
549           AddMethodString(_methodsString, block.filters[i]);
550         }
551       }
552     }
553   }
554 
555   RINOK(InStream_GetSize_SeekToEnd(inStream, _stat.InSize))
556   if (callback)
557   {
558     RINOK(callback->SetTotal(NULL, &_stat.InSize))
559   }
560 
561   CSeekInStreamWrap inStreamImp;
562 
563   inStreamImp.Init(inStream);
564 
565   CLookToRead2_CPP lookStream;
566 
567   lookStream.Alloc(kInputBufSize);
568 
569   if (!lookStream.buf)
570     return E_OUTOFMEMORY;
571 
572   lookStream.realStream = &inStreamImp.vt;
573   LookToRead2_INIT(&lookStream)
574 
575   COpenCallbackWrap openWrap;
576   openWrap.Init(callback);
577 
578   CXzsCPP xzs;
579   Int64 startPosition;
580   SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.vt, &startPosition, &openWrap.vt, &g_Alloc);
581   if (res == SZ_ERROR_PROGRESS)
582     return (openWrap.Res == S_OK) ? E_FAIL : openWrap.Res;
583   /*
584   if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0)
585     res = SZ_OK;
586   */
587   if (res == SZ_OK && startPosition == 0)
588   {
589     _stat_defined = true;
590 
591     _stat.OutSize = Xzs_GetUnpackSize(&xzs.p);
592     _stat.UnpackSize_Defined = true;
593 
594     _stat.NumStreams = xzs.p.num;
595     _stat.NumStreams_Defined = true;
596 
597     _stat.NumBlocks = Xzs_GetNumBlocks(&xzs.p);
598     _stat.NumBlocks_Defined = true;
599 
600     AddCheckString(_methodsString, xzs.p);
601 
602     const size_t numBlocks = (size_t)_stat.NumBlocks + 1;
603     const size_t bytesAlloc = numBlocks * sizeof(CBlockInfo);
604 
605     if (bytesAlloc / sizeof(CBlockInfo) == _stat.NumBlocks + 1)
606     {
607       _blocks = (CBlockInfo *)MyAlloc(bytesAlloc);
608       if (_blocks)
609       {
610         unsigned blockIndex = 0;
611         UInt64 unpackPos = 0;
612 
613         for (size_t si = xzs.p.num; si != 0;)
614         {
615           si--;
616           const CXzStream &str = xzs.p.streams[si];
617           UInt64 packPos = str.startOffset + XZ_STREAM_HEADER_SIZE;
618 
619           for (size_t bi = 0; bi < str.numBlocks; bi++)
620           {
621             const CXzBlockSizes &bs = str.blocks[bi];
622             const UInt64 packSizeAligned = bs.totalSize + ((0 - (unsigned)bs.totalSize) & 3);
623 
624             if (bs.unpackSize != 0)
625             {
626               if (blockIndex >= _stat.NumBlocks)
627                 return E_FAIL;
628 
629               CBlockInfo &block = _blocks[blockIndex++];
630               block.StreamFlags = str.flags;
631               block.PackSize = bs.totalSize; // packSizeAligned;
632               block.PackPos = packPos;
633               block.UnpackPos = unpackPos;
634             }
635             packPos += packSizeAligned;
636             unpackPos += bs.unpackSize;
637             if (_maxBlocksSize < bs.unpackSize)
638               _maxBlocksSize = bs.unpackSize;
639           }
640         }
641 
642         /*
643         if (blockIndex != _stat.NumBlocks)
644         {
645           // there are Empty blocks;
646         }
647         */
648         if (_stat.OutSize != unpackPos)
649           return E_FAIL;
650         CBlockInfo &block = _blocks[blockIndex++];
651         block.StreamFlags = 0;
652         block.PackSize = 0;
653         block.PackPos = 0;
654         block.UnpackPos = unpackPos;
655         _blocksArraySize = blockIndex;
656       }
657     }
658   }
659   else
660   {
661     res = SZ_OK;
662   }
663 
664   RINOK(SRes_to_Open_HRESULT(res))
665 
666   _stream = inStream;
667   _seqStream = inStream;
668   _isArc = true;
669   return S_OK;
670 }
671 
672 
673 
674 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback))
675 {
676   COM_TRY_BEGIN
677   {
678     Close();
679     return Open2(inStream, callback);
680   }
681   COM_TRY_END
682 }
683 
684 Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream))
685 {
686   Close();
687   _seqStream = stream;
688   _isArc = true;
689   _needSeekToStart = false;
690   return S_OK;
691 }
692 
693 Z7_COM7F_IMF(CHandler::Close())
694 {
695   XzStatInfo_Clear(&_stat);
696   XzStatInfo_Clear(&_stat2);
697   _stat_defined = false;
698   _stat2_defined = false;
699   _stat2_decode_SRes = SZ_OK;
700 
701   _isArc = false;
702   _needSeekToStart = false;
703   _firstBlockWasRead = false;
704 
705    _methodsString.Empty();
706   _stream.Release();
707   _seqStream.Release();
708 
709   MyFree(_blocks);
710   _blocks = NULL;
711   _blocksArraySize = 0;
712   _maxBlocksSize = 0;
713 
714   return S_OK;
715 }
716 
717 
718 struct CXzUnpackerCPP2
719 {
720   Byte *InBuf;
721   // Byte *OutBuf;
722   CXzUnpacker p;
723 
724   CXzUnpackerCPP2();
725   ~CXzUnpackerCPP2();
726 };
727 
728 CXzUnpackerCPP2::CXzUnpackerCPP2(): InBuf(NULL)
729   // , OutBuf(NULL)
730 {
731   XzUnpacker_Construct(&p, &g_Alloc);
732 }
733 
734 CXzUnpackerCPP2::~CXzUnpackerCPP2()
735 {
736   XzUnpacker_Free(&p);
737   MidFree(InBuf);
738   // MidFree(OutBuf);
739 }
740 
741 
742 Z7_CLASS_IMP_IInStream(
743   CInStream
744 )
745 
746   UInt64 _virtPos;
747 public:
748   UInt64 Size;
749   UInt64 _cacheStartPos;
750   size_t _cacheSize;
751   CByteBuffer _cache;
752   // UInt64 _startPos;
753   CXzUnpackerCPP2 xz;
754 
755   void InitAndSeek()
756   {
757     _virtPos = 0;
758     _cacheStartPos = 0;
759     _cacheSize = 0;
760     // _startPos = startPos;
761   }
762 
763   CMyComPtr2<IInArchive, CHandler> _handlerSpec;
764   // ~CInStream();
765 };
766 
767 /*
768 CInStream::~CInStream()
769 {
770   // _cache.Free();
771 }
772 */
773 
774 static size_t FindBlock(const CBlockInfo *blocks, size_t numBlocks, UInt64 pos)
775 {
776   size_t left = 0, right = numBlocks;
777   for (;;)
778   {
779     size_t mid = (left + right) / 2;
780     if (mid == left)
781       return left;
782     if (pos < blocks[mid].UnpackPos)
783       right = mid;
784     else
785       left = mid;
786   }
787 }
788 
789 
790 
791 static HRESULT DecodeBlock(CXzUnpackerCPP2 &xzu,
792     ISequentialInStream *seqInStream,
793     unsigned streamFlags,
794     UInt64 packSize, // pure size from Index record, it doesn't include pad zeros
795     size_t unpackSize, Byte *dest
796     // , ICompressProgressInfo *progress
797     )
798 {
799   const size_t kInBufSize = (size_t)1 << 16;
800 
801   XzUnpacker_Init(&xzu.p);
802 
803   if (!xzu.InBuf)
804   {
805     xzu.InBuf = (Byte *)MidAlloc(kInBufSize);
806     if (!xzu.InBuf)
807       return E_OUTOFMEMORY;
808   }
809 
810   xzu.p.streamFlags = (UInt16)streamFlags;
811   XzUnpacker_PrepareToRandomBlockDecoding(&xzu.p);
812 
813   XzUnpacker_SetOutBuf(&xzu.p, dest, unpackSize);
814 
815   const UInt64 packSizeAligned = packSize + ((0 - (unsigned)packSize) & 3);
816   UInt64 packRem = packSizeAligned;
817 
818   UInt32 inSize = 0;
819   SizeT inPos = 0;
820   SizeT outPos = 0;
821 
822   HRESULT readRes = S_OK;
823 
824   for (;;)
825   {
826     if (inPos == inSize && readRes == S_OK)
827     {
828       inPos = 0;
829       inSize = 0;
830       UInt32 rem = kInBufSize;
831       if (rem > packRem)
832         rem = (UInt32)packRem;
833       if (rem != 0)
834         readRes = seqInStream->Read(xzu.InBuf, rem, &inSize);
835     }
836 
837     SizeT inLen = inSize - inPos;
838     SizeT outLen = unpackSize - outPos;
839 
840     ECoderStatus status;
841 
842     const SRes res = XzUnpacker_Code(&xzu.p,
843         // dest + outPos,
844         NULL,
845         &outLen,
846         xzu.InBuf + inPos, &inLen,
847         (inLen == 0), // srcFinished
848         CODER_FINISH_END, &status);
849 
850     // return E_OUTOFMEMORY;
851     // res = SZ_ERROR_CRC;
852 
853     if (res != SZ_OK)
854     {
855       if (res == SZ_ERROR_CRC)
856         return S_FALSE;
857       return SResToHRESULT(res);
858     }
859 
860     inPos += inLen;
861     outPos += outLen;
862 
863     packRem -= inLen;
864 
865     const BoolInt blockFinished = XzUnpacker_IsBlockFinished(&xzu.p);
866 
867     if ((inLen == 0 && outLen == 0) || blockFinished)
868     {
869       if (packRem != 0 || !blockFinished || unpackSize != outPos)
870         return S_FALSE;
871       if (XzUnpacker_GetPackSizeForIndex(&xzu.p) != packSize)
872         return S_FALSE;
873       return S_OK;
874     }
875   }
876 }
877 
878 
879 Z7_COM7F_IMF(CInStream::Read(void *data, UInt32 size, UInt32 *processedSize))
880 {
881   COM_TRY_BEGIN
882 
883   if (processedSize)
884     *processedSize = 0;
885   if (size == 0)
886     return S_OK;
887 
888   {
889     if (_virtPos >= Size)
890       return S_OK; // (Size == _virtPos) ? S_OK: E_FAIL;
891     {
892       UInt64 rem = Size - _virtPos;
893       if (size > rem)
894         size = (UInt32)rem;
895     }
896   }
897 
898   if (size == 0)
899     return S_OK;
900 
901   if (_virtPos < _cacheStartPos || _virtPos >= _cacheStartPos + _cacheSize)
902   {
903     const size_t bi = FindBlock(_handlerSpec->_blocks, _handlerSpec->_blocksArraySize, _virtPos);
904     const CBlockInfo &block = _handlerSpec->_blocks[bi];
905     const UInt64 unpackSize = _handlerSpec->_blocks[bi + 1].UnpackPos - block.UnpackPos;
906     if (_cache.Size() < unpackSize)
907       return E_FAIL;
908 
909     _cacheSize = 0;
910 
911     RINOK(_handlerSpec->SeekToPackPos(block.PackPos))
912     RINOK(DecodeBlock(xz, _handlerSpec->_seqStream, block.StreamFlags, block.PackSize,
913         (size_t)unpackSize, _cache))
914     _cacheStartPos = block.UnpackPos;
915     _cacheSize = (size_t)unpackSize;
916   }
917 
918   {
919     const size_t offset = (size_t)(_virtPos - _cacheStartPos);
920     const size_t rem = _cacheSize - offset;
921     if (size > rem)
922       size = (UInt32)rem;
923     memcpy(data, _cache.ConstData() + offset, size);
924     _virtPos += size;
925     if (processedSize)
926       *processedSize = size;
927     return S_OK;
928   }
929 
930   COM_TRY_END
931 }
932 
933 
934 Z7_COM7F_IMF(CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
935 {
936   switch (seekOrigin)
937   {
938     case STREAM_SEEK_SET: break;
939     case STREAM_SEEK_CUR: offset += _virtPos; break;
940     case STREAM_SEEK_END: offset += Size; break;
941     default: return STG_E_INVALIDFUNCTION;
942   }
943   if (offset < 0)
944     return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
945   _virtPos = (UInt64)offset;
946   if (newPosition)
947     *newPosition = (UInt64)offset;
948   return S_OK;
949 }
950 
951 
952 
953 static const UInt64 kMaxBlockSize_for_GetStream = (UInt64)1 << 40;
954 
955 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
956 {
957   COM_TRY_BEGIN
958 
959   *stream = NULL;
960 
961   if (index != 0)
962     return E_INVALIDARG;
963 
964   if (!_stat.UnpackSize_Defined
965       || _maxBlocksSize == 0 // 18.02
966       || _maxBlocksSize > kMaxBlockSize_for_GetStream
967       || _maxBlocksSize != (size_t)_maxBlocksSize)
968     return S_FALSE;
969 
970   size_t memSize;
971   if (!NSystem::GetRamSize(memSize))
972     memSize = (size_t)sizeof(size_t) << 28;
973   {
974     if (_maxBlocksSize > memSize / 4)
975       return S_FALSE;
976   }
977 
978   CMyComPtr2<ISequentialInStream, CInStream> spec;
979   spec.Create_if_Empty();
980   spec->_cache.Alloc((size_t)_maxBlocksSize);
981   spec->_handlerSpec.SetFromCls(this);
982   // spec->_handler = (IInArchive *)this;
983   spec->Size = _stat.OutSize;
984   spec->InitAndSeek();
985 
986   *stream = spec.Detach();
987   return S_OK;
988 
989   COM_TRY_END
990 }
991 
992 
993 static Int32 Get_Extract_OperationResult(const NCompress::NXz::CDecoder &decoder)
994 {
995   Int32 opRes;
996   SRes sres = decoder.MainDecodeSRes;
997   if (sres == SZ_ERROR_NO_ARCHIVE) // (!IsArc)
998     opRes = NExtract::NOperationResult::kIsNotArc;
999   else if (sres == SZ_ERROR_INPUT_EOF) // (UnexpectedEnd)
1000     opRes = NExtract::NOperationResult::kUnexpectedEnd;
1001   else if (decoder.Stat.DataAfterEnd)
1002     opRes = NExtract::NOperationResult::kDataAfterEnd;
1003   else if (sres == SZ_ERROR_CRC) // (CrcError)
1004     opRes = NExtract::NOperationResult::kCRCError;
1005   else if (sres == SZ_ERROR_UNSUPPORTED) // (Unsupported)
1006     opRes = NExtract::NOperationResult::kUnsupportedMethod;
1007   else if (sres == SZ_ERROR_ARCHIVE) //  (HeadersError)
1008     opRes = NExtract::NOperationResult::kDataError;
1009   else if (sres == SZ_ERROR_DATA)  // (DataError)
1010     opRes = NExtract::NOperationResult::kDataError;
1011   else if (sres != SZ_OK)
1012     opRes = NExtract::NOperationResult::kDataError;
1013   else
1014     opRes = NExtract::NOperationResult::kOK;
1015   return opRes;
1016 }
1017 
1018 
1019 
1020 
1021 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1022     Int32 testMode, IArchiveExtractCallback *extractCallback))
1023 {
1024   COM_TRY_BEGIN
1025   if (numItems == 0)
1026     return S_OK;
1027   if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
1028     return E_INVALIDARG;
1029 
1030   const CXzStatInfo *stat = GetStat();
1031 
1032   if (stat)
1033     RINOK(extractCallback->SetTotal(stat->InSize))
1034 
1035   UInt64 currentTotalPacked = 0;
1036   RINOK(extractCallback->SetCompleted(&currentTotalPacked))
1037   Int32 opRes;
1038  {
1039   CMyComPtr<ISequentialOutStream> realOutStream;
1040   const Int32 askMode = testMode ?
1041       NExtract::NAskMode::kTest :
1042       NExtract::NAskMode::kExtract;
1043 
1044   RINOK(extractCallback->GetStream(0, &realOutStream, askMode))
1045 
1046   if (!testMode && !realOutStream)
1047     return S_OK;
1048 
1049   RINOK(extractCallback->PrepareOperation(askMode))
1050 
1051   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
1052   lps->Init(extractCallback, true);
1053 
1054   if (_needSeekToStart)
1055   {
1056     if (!_stream)
1057       return E_FAIL;
1058     RINOK(InStream_SeekToBegin(_stream))
1059   }
1060   else
1061     _needSeekToStart = true;
1062 
1063 
1064   NCompress::NXz::CDecoder decoder;
1065 
1066   const HRESULT hres = Decode(decoder, _seqStream, realOutStream, lps);
1067 
1068   if (!decoder.MainDecodeSRes_wasUsed)
1069     return hres == S_OK ? E_FAIL : hres;
1070 
1071   opRes = Get_Extract_OperationResult(decoder);
1072   if (opRes == NExtract::NOperationResult::kOK
1073       && hres != S_OK)
1074     opRes = NExtract::NOperationResult::kDataError;
1075 
1076   // realOutStream.Release();
1077  }
1078   return extractCallback->SetOperationResult(opRes);
1079   COM_TRY_END
1080 }
1081 
1082 
1083 
1084 #ifndef Z7_EXTRACT_ONLY
1085 
1086 Z7_COM7F_IMF(CHandler::GetFileTimeType(UInt32 *timeType))
1087 {
1088   *timeType = GET_FileTimeType_NotDefined_for_GetFileTimeType;
1089   // *timeType = NFileTimeType::kUnix;
1090   return S_OK;
1091 }
1092 
1093 
1094 Z7_COM7F_IMF(CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
1095     IArchiveUpdateCallback *updateCallback))
1096 {
1097   COM_TRY_BEGIN
1098 
1099   if (numItems == 0)
1100   {
1101     CSeqOutStreamWrap seqOutStream;
1102     seqOutStream.Init(outStream);
1103     SRes res = Xz_EncodeEmpty(&seqOutStream.vt);
1104     return SResToHRESULT(res);
1105   }
1106 
1107   if (numItems != 1)
1108     return E_INVALIDARG;
1109 
1110   {
1111     Z7_DECL_CMyComPtr_QI_FROM(
1112         IStreamSetRestriction,
1113         setRestriction, outStream)
1114     if (setRestriction)
1115       RINOK(setRestriction->SetRestriction(0, 0))
1116   }
1117 
1118   Int32 newData, newProps;
1119   UInt32 indexInArchive;
1120   if (!updateCallback)
1121     return E_FAIL;
1122   RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive))
1123 
1124   if (IntToBool(newProps))
1125   {
1126     {
1127       NCOM::CPropVariant prop;
1128       RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop))
1129       if (prop.vt != VT_EMPTY)
1130         if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE)
1131           return E_INVALIDARG;
1132     }
1133   }
1134 
1135   if (IntToBool(newData))
1136   {
1137     UInt64 dataSize;
1138     {
1139       NCOM::CPropVariant prop;
1140       RINOK(updateCallback->GetProperty(0, kpidSize, &prop))
1141       if (prop.vt != VT_UI8)
1142         return E_INVALIDARG;
1143       dataSize = prop.uhVal.QuadPart;
1144     }
1145 
1146     CMyComPtr2_Create<ICompressCoder, NCompress::NXz::CEncoder> encoder;
1147 
1148     CXzProps &xzProps = encoder->xzProps;
1149     CLzma2EncProps &lzma2Props = xzProps.lzma2Props;
1150 
1151     lzma2Props.lzmaProps.level = GetLevel();
1152 
1153     xzProps.reduceSize = dataSize;
1154     /*
1155     {
1156       NCOM::CPropVariant prop = (UInt64)dataSize;
1157       RINOK(encoder->SetCoderProp(NCoderPropID::kReduceSize, prop))
1158     }
1159     */
1160 
1161     #ifndef Z7_ST
1162 
1163     UInt32 numThreads = _numThreads;
1164 
1165     const UInt32 kNumThreads_Max = 1024;
1166     if (numThreads > kNumThreads_Max)
1167       numThreads = kNumThreads_Max;
1168 
1169     if (!_numThreads_WasForced
1170         && _numThreads >= 1
1171         && _memUsage_WasSet)
1172     {
1173       COneMethodInfo oneMethodInfo;
1174       if (!_methods.IsEmpty())
1175         oneMethodInfo = _methods[0];
1176 
1177       SetGlobalLevelTo(oneMethodInfo);
1178 
1179       const bool numThreads_WasSpecifiedInMethod = (oneMethodInfo.Get_NumThreads() >= 0);
1180       if (!numThreads_WasSpecifiedInMethod)
1181       {
1182         // here we set the (NCoderPropID::kNumThreads) property in each method, only if there is no such property already
1183         CMultiMethodProps::SetMethodThreadsTo_IfNotFinded(oneMethodInfo, numThreads);
1184       }
1185 
1186       UInt64 cs = _numSolidBytes;
1187       if (cs != XZ_PROPS_BLOCK_SIZE_AUTO)
1188         oneMethodInfo.AddProp_BlockSize2(cs);
1189       cs = oneMethodInfo.Get_Xz_BlockSize();
1190 
1191       if (cs != XZ_PROPS_BLOCK_SIZE_AUTO &&
1192           cs != XZ_PROPS_BLOCK_SIZE_SOLID)
1193       {
1194         const UInt32 lzmaThreads = oneMethodInfo.Get_Lzma_NumThreads();
1195         const UInt32 numBlockThreads_Original = numThreads / lzmaThreads;
1196 
1197         if (numBlockThreads_Original > 1)
1198         {
1199           UInt32 numBlockThreads = numBlockThreads_Original;
1200           {
1201             const UInt64 lzmaMemUsage = oneMethodInfo.Get_Lzma_MemUsage(false);
1202             for (; numBlockThreads > 1; numBlockThreads--)
1203             {
1204               UInt64 size = numBlockThreads * (lzmaMemUsage + cs);
1205               UInt32 numPackChunks = numBlockThreads + (numBlockThreads / 8) + 1;
1206               if (cs < ((UInt32)1 << 26)) numPackChunks++;
1207               if (cs < ((UInt32)1 << 24)) numPackChunks++;
1208               if (cs < ((UInt32)1 << 22)) numPackChunks++;
1209               size += numPackChunks * cs;
1210               // printf("\nnumBlockThreads = %d, size = %d\n", (unsigned)(numBlockThreads), (unsigned)(size >> 20));
1211               if (size <= _memUsage_Compress)
1212                 break;
1213             }
1214           }
1215           if (numBlockThreads == 0)
1216             numBlockThreads = 1;
1217           if (numBlockThreads != numBlockThreads_Original)
1218             numThreads = numBlockThreads * lzmaThreads;
1219         }
1220       }
1221     }
1222     xzProps.numTotalThreads = (int)numThreads;
1223 
1224     #endif // Z7_ST
1225 
1226 
1227     xzProps.blockSize = _numSolidBytes;
1228     if (_numSolidBytes == XZ_PROPS_BLOCK_SIZE_SOLID)
1229     {
1230       xzProps.lzma2Props.blockSize = LZMA2_ENC_PROPS_BLOCK_SIZE_SOLID;
1231     }
1232 
1233     RINOK(encoder->SetCheckSize(_crcSize))
1234 
1235     {
1236       CXzFilterProps &filter = xzProps.filterProps;
1237 
1238       if (_filterId == XZ_ID_Delta)
1239       {
1240         bool deltaDefined = false;
1241         FOR_VECTOR (j, _filterMethod.Props)
1242         {
1243           const CProp &prop = _filterMethod.Props[j];
1244           if (prop.Id == NCoderPropID::kDefaultProp && prop.Value.vt == VT_UI4)
1245           {
1246             UInt32 delta = (UInt32)prop.Value.ulVal;
1247             if (delta < 1 || delta > 256)
1248               return E_INVALIDARG;
1249             filter.delta = delta;
1250             deltaDefined = true;
1251           }
1252           else
1253             return E_INVALIDARG;
1254         }
1255         if (!deltaDefined)
1256           return E_INVALIDARG;
1257       }
1258       filter.id = _filterId;
1259     }
1260 
1261     FOR_VECTOR (i, _methods)
1262     {
1263       COneMethodInfo &m = _methods[i];
1264 
1265       FOR_VECTOR (j, m.Props)
1266       {
1267         const CProp &prop = m.Props[j];
1268         RINOK(encoder->SetCoderProp(prop.Id, prop.Value))
1269       }
1270     }
1271 
1272     {
1273       CMyComPtr<ISequentialInStream> fileInStream;
1274       RINOK(updateCallback->GetStream(0, &fileInStream))
1275       if (!fileInStream)
1276         return S_FALSE;
1277       {
1278         CMyComPtr<IStreamGetSize> streamGetSize;
1279         fileInStream.QueryInterface(IID_IStreamGetSize, &streamGetSize);
1280         if (streamGetSize)
1281         {
1282           UInt64 size;
1283           if (streamGetSize->GetSize(&size) == S_OK)
1284             dataSize = size;
1285         }
1286       }
1287       RINOK(updateCallback->SetTotal(dataSize))
1288       CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
1289       lps->Init(updateCallback, true);
1290       RINOK(encoder.Interface()->Code(fileInStream, outStream, NULL, NULL, lps))
1291     }
1292 
1293     return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);
1294   }
1295 
1296   if (indexInArchive != 0)
1297     return E_INVALIDARG;
1298 
1299   Z7_DECL_CMyComPtr_QI_FROM(
1300       IArchiveUpdateCallbackFile,
1301       opCallback, updateCallback)
1302   if (opCallback)
1303   {
1304     RINOK(opCallback->ReportOperation(NEventIndexType::kInArcIndex, 0, NUpdateNotifyOp::kReplicate))
1305   }
1306 
1307   if (_stream)
1308   {
1309     const CXzStatInfo *stat = GetStat();
1310     if (stat)
1311     {
1312       RINOK(updateCallback->SetTotal(stat->InSize))
1313     }
1314     RINOK(InStream_SeekToBegin(_stream))
1315   }
1316 
1317   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
1318   lps->Init(updateCallback, true);
1319 
1320   return NCompress::CopyStream(_stream, outStream, lps);
1321 
1322   COM_TRY_END
1323 }
1324 
1325 #endif
1326 
1327 
1328 HRESULT CHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
1329 {
1330   UString name = nameSpec;
1331   name.MakeLower_Ascii();
1332   if (name.IsEmpty())
1333     return E_INVALIDARG;
1334 
1335   #ifndef Z7_EXTRACT_ONLY
1336 
1337   if (name[0] == L's')
1338   {
1339     const wchar_t *s = name.Ptr(1);
1340     if (*s == 0)
1341     {
1342       bool useStr = false;
1343       bool isSolid;
1344       switch (value.vt)
1345       {
1346         case VT_EMPTY: isSolid = true; break;
1347         case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break;
1348         case VT_BSTR:
1349           if (!StringToBool(value.bstrVal, isSolid))
1350             useStr = true;
1351           break;
1352         default: return E_INVALIDARG;
1353       }
1354       if (!useStr)
1355       {
1356         _numSolidBytes = (isSolid ? XZ_PROPS_BLOCK_SIZE_SOLID : XZ_PROPS_BLOCK_SIZE_AUTO);
1357         return S_OK;
1358       }
1359     }
1360     return ParseSizeString(s, value,
1361         0, // percentsBase
1362         _numSolidBytes) ? S_OK: E_INVALIDARG;
1363   }
1364 
1365   return CMultiMethodProps::SetProperty(name, value);
1366 
1367   #else
1368 
1369   {
1370     HRESULT hres;
1371     if (SetCommonProperty(name, value, hres))
1372       return hres;
1373   }
1374 
1375   return E_INVALIDARG;
1376 
1377   #endif
1378 }
1379 
1380 
1381 
1382 Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
1383 {
1384   COM_TRY_BEGIN
1385 
1386   Init();
1387 
1388   for (UInt32 i = 0; i < numProps; i++)
1389   {
1390     RINOK(SetProperty(names[i], values[i]))
1391   }
1392 
1393   #ifndef Z7_EXTRACT_ONLY
1394 
1395   if (!_filterMethod.MethodName.IsEmpty())
1396   {
1397     unsigned k;
1398     for (k = 0; k < Z7_ARRAY_SIZE(g_NamePairs); k++)
1399     {
1400       const CMethodNamePair &pair = g_NamePairs[k];
1401       if (StringsAreEqualNoCase_Ascii(_filterMethod.MethodName, pair.Name))
1402       {
1403         _filterId = pair.Id;
1404         break;
1405       }
1406     }
1407     if (k == Z7_ARRAY_SIZE(g_NamePairs))
1408       return E_INVALIDARG;
1409   }
1410 
1411   _methods.DeleteFrontal(GetNumEmptyMethods());
1412   if (_methods.Size() > 1)
1413     return E_INVALIDARG;
1414   if (_methods.Size() == 1)
1415   {
1416     AString &methodName = _methods[0].MethodName;
1417     if (methodName.IsEmpty())
1418       methodName = k_LZMA2_Name;
1419     else if (
1420         !methodName.IsEqualTo_Ascii_NoCase(k_LZMA2_Name)
1421         && !methodName.IsEqualTo_Ascii_NoCase("xz"))
1422       return E_INVALIDARG;
1423   }
1424 
1425   #endif
1426 
1427   return S_OK;
1428 
1429   COM_TRY_END
1430 }
1431 
1432 
1433 REGISTER_ARC_IO(
1434   "xz", "xz txz", "* .tar", 0xC,
1435   XZ_SIG, 0
1436   , NArcInfoFlags::kKeepName
1437   , 0
1438   , NULL)
1439 
1440 }}
1441