tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

XzHandler.cpp (30793B)


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