// ZstdHandler.cpp #include "StdAfx.h" // #define Z7_USE_ZSTD_ORIG_DECODER // #define Z7_USE_ZSTD_COMPRESSION #include "../../Common/ComTry.h" #include "../Common/MethodProps.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" #include "../Compress/CopyCoder.h" #include "../Compress/ZstdDecoder.h" #ifdef Z7_USE_ZSTD_ORIG_DECODER #include "../Compress/Zstd2Decoder.h" #endif #ifdef Z7_USE_ZSTD_COMPRESSION #include "../Compress/ZstdEncoder.h" #include "../Compress/ZstdEncoderProps.h" #include "Common/HandlerOut.h" #endif #include "Common/DummyOutStream.h" #include "../../../C/CpuArch.h" using namespace NWindows; namespace NArchive { namespace NZstd { #define DESCRIPTOR_Get_DictionaryId_Flag(d) ((d) & 3) #define DESCRIPTOR_FLAG_CHECKSUM (1 << 2) #define DESCRIPTOR_FLAG_RESERVED (1 << 3) #define DESCRIPTOR_FLAG_UNUSED (1 << 4) #define DESCRIPTOR_FLAG_SINGLE (1 << 5) #define DESCRIPTOR_Get_ContentSize_Flag3(d) ((d) >> 5) #define DESCRIPTOR_Is_ContentSize_Defined(d) (((d) & 0xe0) != 0) struct CFrameHeader { Byte Descriptor; Byte WindowDescriptor; UInt32 DictionaryId; UInt64 ContentSize; /* by zstd specification: the decoder must check that (Is_Reserved() == false) the decoder must ignore Unused_bit */ bool Is_Reserved() const { return (Descriptor & DESCRIPTOR_FLAG_RESERVED) != 0; } bool Is_Checksum() const { return (Descriptor & DESCRIPTOR_FLAG_CHECKSUM) != 0; } bool Is_SingleSegment() const { return (Descriptor & DESCRIPTOR_FLAG_SINGLE) != 0; } bool Is_ContentSize_Defined() const { return DESCRIPTOR_Is_ContentSize_Defined(Descriptor); } unsigned Get_DictionaryId_Flag() const { return DESCRIPTOR_Get_DictionaryId_Flag(Descriptor); } unsigned Get_ContentSize_Flag3() const { return DESCRIPTOR_Get_ContentSize_Flag3(Descriptor); } const Byte *Parse(const Byte *p, int size) { if ((unsigned)size < 2) return NULL; Descriptor = *p++; size--; { Byte w = 0; if (!Is_SingleSegment()) { w = *p++; size--; } WindowDescriptor = w; } { unsigned n = Get_DictionaryId_Flag(); UInt32 d = 0; if (n) { n = (unsigned)1 << (n - 1); if ((size -= (int)n) < 0) return NULL; d = GetUi32(p) & ((UInt32)(Int32)-1 >> (32 - 8u * n)); p += n; } DictionaryId = d; } { unsigned n = Get_ContentSize_Flag3(); UInt64 v = 0; if (n) { n >>= 1; if (n == 1) v = 256; n = (unsigned)1 << n; if ((size -= (int)n) < 0) return NULL; v += GetUi64(p) & ((UInt64)(Int64)-1 >> (64 - 8u * n)); p += n; } ContentSize = v; } return p; } }; class CHandler Z7_final: public IInArchive, public IArchiveOpenSeq, public ISetProperties, #ifdef Z7_USE_ZSTD_COMPRESSION public IOutArchive, #endif public CMyUnknownImp { Z7_COM_QI_BEGIN2(IInArchive) Z7_COM_QI_ENTRY(IArchiveOpenSeq) Z7_COM_QI_ENTRY(ISetProperties) #ifdef Z7_USE_ZSTD_COMPRESSION Z7_COM_QI_ENTRY(IOutArchive) #endif Z7_COM_QI_END Z7_COM_ADDREF_RELEASE Z7_IFACE_COM7_IMP(IInArchive) Z7_IFACE_COM7_IMP(IArchiveOpenSeq) Z7_IFACE_COM7_IMP(ISetProperties) #ifdef Z7_USE_ZSTD_COMPRESSION Z7_IFACE_COM7_IMP(IOutArchive) #endif bool _isArc; bool _needSeekToStart; // bool _dataAfterEnd; // bool _needMoreInput; bool _unsupportedBlock; bool _wasParsed; bool _phySize_Decoded_Defined; bool _unpackSize_Defined; // decoded bool _decoded_Info_Defined; bool _parseMode; bool _disableHash; // bool _smallMode; UInt64 _phySize; UInt64 _phySize_Decoded; UInt64 _unpackSize; CZstdDecInfo _parsed_Info; CZstdDecInfo _decoded_Info; CMyComPtr _stream; CMyComPtr _seqStream; #ifdef Z7_USE_ZSTD_COMPRESSION CSingleMethodProps _props; #endif public: CHandler(): _parseMode(false), _disableHash(false) // _smallMode(false) {} }; static const Byte kProps[] = { kpidSize, kpidPackSize }; static const Byte kArcProps[] = { kpidNumStreams, kpidNumBlocks, kpidMethod, // kpidChecksum kpidCRC }; IMP_IInArchive_Props IMP_IInArchive_ArcProps // static const unsigned kBlockType_Raw = 0; static const unsigned kBlockType_RLE = 1; // static const unsigned kBlockType_Compressed = 2; static const unsigned kBlockType_Reserved = 3; /* static const char * const kNames[] = { "RAW" , "RLE" , "Compressed" , "Reserved" }; */ static void Add_UInt64(AString &s, const char *name, UInt64 v) { s.Add_OptSpaced(name); s.Add_Colon(); s.Add_UInt64(v); } static void PrintSize(AString &s, UInt64 w) { char c = 0; if ((w & ((1 << 30) - 1)) == 0) { c = 'G'; w >>= 30; } else if ((w & ((1 << 20) - 1)) == 0) { c = 'M'; w >>= 20; } else if ((w & ((1 << 10) - 1)) == 0) { c = 'K'; w >>= 10; } s.Add_UInt64(w); if (c) { s.Add_Char(c); s += "iB"; } } Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) { NCOM::CPropVariant prop; CZstdDecInfo *p = NULL; if (_wasParsed || !_decoded_Info_Defined) p = &_parsed_Info; else if (_decoded_Info_Defined) p = &_decoded_Info; switch (propID) { case kpidPhySize: if (_wasParsed) prop = _phySize; else if (_phySize_Decoded_Defined) prop = _phySize_Decoded; break; case kpidUnpackSize: if (_unpackSize_Defined) prop = _unpackSize; break; case kpidNumStreams: if (p) if (_wasParsed || _decoded_Info_Defined) prop = p->num_DataFrames; break; case kpidNumBlocks: if (p) if (_wasParsed || _decoded_Info_Defined) prop = p->num_Blocks; break; // case kpidChecksum: case kpidCRC: if (p) if (p->checksum_Defined && p->num_DataFrames == 1) prop = p->checksum; // it's checksum from last frame break; case kpidMethod: { AString s; s.Add_OptSpaced(p == &_decoded_Info ? "decoded:" : _wasParsed ? "parsed:" : "header-open-only:"); if (p->dictionaryId != 0) { if (p->are_DictionaryId_Different) s.Add_OptSpaced("different-dictionary-IDs"); s.Add_OptSpaced("dictionary-ID:"); s.Add_UInt32(p->dictionaryId); } /* if (ContentSize_Defined) { s.Add_OptSpaced("ContentSize="); s.Add_UInt64(ContentSize_Total); } */ // if (p->are_Checksums) if (p->descriptor_OR & DESCRIPTOR_FLAG_CHECKSUM) s.Add_OptSpaced("XXH64"); if (p->descriptor_NOT_OR & DESCRIPTOR_FLAG_CHECKSUM) s.Add_OptSpaced("NO-XXH64"); if (p->descriptor_OR & DESCRIPTOR_FLAG_UNUSED) s.Add_OptSpaced("unused_bit"); if (p->descriptor_OR & DESCRIPTOR_FLAG_SINGLE) s.Add_OptSpaced("single-segments"); if (p->descriptor_NOT_OR & DESCRIPTOR_FLAG_SINGLE) { // Add_UInt64(s, "wnd-descriptors", p->num_WindowDescriptors); s.Add_OptSpaced("wnd-desc-log-MAX:"); // WindowDescriptor_MAX = 16 << 3; // for debug const unsigned e = p->windowDescriptor_MAX >> 3; s.Add_UInt32(e + 10); const unsigned m = p->windowDescriptor_MAX & 7; if (m != 0) { s.Add_Dot(); s.Add_UInt32(m); } } if (DESCRIPTOR_Is_ContentSize_Defined(p->descriptor_OR) || (p->descriptor_NOT_OR & DESCRIPTOR_FLAG_SINGLE)) /* if (p->are_ContentSize_Known || p->are_WindowDescriptors) */ { s.Add_OptSpaced("wnd-MAX:"); PrintSize(s, p->windowSize_MAX); if (p->windowSize_MAX != p->windowSize_Allocate_MAX) { s.Add_OptSpaced("wnd-use-MAX:"); PrintSize(s, p->windowSize_Allocate_MAX); } } if (p->num_DataFrames != 1) Add_UInt64(s, "data-frames", p->num_DataFrames); if (p->num_SkipFrames != 0) { Add_UInt64(s, "skip-frames", p->num_SkipFrames); Add_UInt64(s, "skip-frames-size-total", p->skipFrames_Size); } if (p->are_ContentSize_Unknown) s.Add_OptSpaced("unknown-content-size"); if (DESCRIPTOR_Is_ContentSize_Defined(p->descriptor_OR)) { Add_UInt64(s, "content-size-frame-max", p->contentSize_MAX); Add_UInt64(s, "content-size-total", p->contentSize_Total); } /* for (unsigned i = 0; i < 4; i++) { const UInt64 n = p->num_Blocks_forType[i]; if (n) { s.Add_OptSpaced(kNames[i]); s += "-blocks:"; s.Add_UInt64(n); s.Add_OptSpaced(kNames[i]); s += "-block-bytes:"; s.Add_UInt64(p->num_BlockBytes_forType[i]); } } */ prop = s; break; } case kpidErrorFlags: { UInt32 v = 0; if (!_isArc) v |= kpv_ErrorFlags_IsNotArc; // if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd; // if (_dataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd; if (_unsupportedBlock) v |= kpv_ErrorFlags_UnsupportedMethod; /* if (_parsed_Info.numBlocks_forType[kBlockType_Reserved]) v |= kpv_ErrorFlags_UnsupportedMethod; */ prop = v; break; } default: break; } prop.Detach(value); return S_OK; } Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems)) { *numItems = 1; return S_OK; } Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)) { NCOM::CPropVariant prop; switch (propID) { case kpidPackSize: if (_wasParsed) prop = _phySize; else if (_phySize_Decoded_Defined) prop = _phySize_Decoded; break; case kpidSize: if (_wasParsed && !_parsed_Info.are_ContentSize_Unknown) prop = _parsed_Info.contentSize_Total; else if (_unpackSize_Defined) prop = _unpackSize; break; default: break; } prop.Detach(value); return S_OK; } static const unsigned kSignatureSize = 4; static const Byte k_Signature[kSignatureSize] = { 0x28, 0xb5, 0x2f, 0xfd } ; static const UInt32 kDataFrameSignature32 = 0xfd2fb528; static const UInt32 kSkipFrameSignature = 0x184d2a50; static const UInt32 kSkipFrameSignature_Mask = 0xfffffff0; /* API_FUNC_static_IsArc IsArc_Zstd(const Byte *p, size_t size) { if (size < kSignatureSize) return k_IsArc_Res_NEED_MORE; if (memcmp(p, k_Signature, kSignatureSize) != 0) { const UInt32 v = GetUi32(p); if ((v & kSkipFrameSignature_Mask) != kSkipFrameSignature) return k_IsArc_Res_NO; return k_IsArc_Res_YES; } p += 4; // return k_IsArc_Res_YES; } } */ // kBufSize must be >= (ZSTD_FRAMEHEADERSIZE_MAX = 18) // we use big buffer for fast parsing of worst case small blocks. static const unsigned kBufSize = 1 << 9; // 1 << 14; // fastest in real file struct CStreamBuffer { unsigned pos; unsigned lim; IInStream *Stream; UInt64 StreamOffset; Byte buf[kBufSize]; CStreamBuffer(): pos(0), lim(0), StreamOffset(0) {} unsigned Avail() const { return lim - pos; } const Byte *GetPtr() const { return &buf[pos]; } UInt64 GetCurOffset() const { return StreamOffset - Avail(); } void SkipInBuf(UInt32 size) { pos += size; } HRESULT Skip(UInt32 size); HRESULT Read(unsigned num); }; HRESULT CStreamBuffer::Skip(UInt32 size) { unsigned rem = lim - pos; if (rem != 0) { if (rem > size) rem = size; pos += rem; size -= rem; if (pos != lim) return S_OK; } if (size == 0) return S_OK; return Stream->Seek(size, STREAM_SEEK_CUR, &StreamOffset); } HRESULT CStreamBuffer::Read(unsigned num) { if (lim - pos >= num) return S_OK; if (pos != 0) { lim -= pos; memmove(buf, buf + pos, lim); pos = 0; } size_t processed = kBufSize - ((unsigned)StreamOffset & (kBufSize - 1)); const unsigned avail = kBufSize - lim; num -= lim; if (avail < processed || processed < num) processed = avail; const HRESULT res = ReadStream(Stream, buf + lim, &processed); StreamOffset += processed; lim += (unsigned)processed; return res; } static const unsigned k_ZSTD_FRAMEHEADERSIZE_MAX = 4 + 14; Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback)) { COM_TRY_BEGIN Close(); CZstdDecInfo *p = &_parsed_Info; // p->are_ContentSize_Unknown = False; CStreamBuffer sb; sb.Stream = stream; for (;;) { RINOK(sb.Read(k_ZSTD_FRAMEHEADERSIZE_MAX)) if (sb.Avail() < kSignatureSize) break; if (callback && (ZstdDecInfo_GET_NUM_FRAMES(p) & 0xFFF) == 2) { const UInt64 numBytes = sb.GetCurOffset(); RINOK(callback->SetCompleted(NULL, &numBytes)) } const UInt32 v = GetUi32(sb.GetPtr()); if (v != kDataFrameSignature32) { if ((v & kSkipFrameSignature_Mask) != kSkipFrameSignature) break; _phySize = sb.GetCurOffset() + 8; p->num_SkipFrames++; sb.SkipInBuf(4); if (sb.Avail() < 4) break; const UInt32 size = GetUi32(sb.GetPtr()); p->skipFrames_Size += size; sb.SkipInBuf(4); _phySize = sb.GetCurOffset() + size; RINOK(sb.Skip(size)) continue; } p->num_DataFrames++; // _numStreams_Defined = true; sb.SkipInBuf(4); CFrameHeader fh; { const Byte *data = fh.Parse(sb.GetPtr(), (int)sb.Avail()); if (!data) { // _needMoreInput = true; // we set size for one byte more to show that stream was truncated _phySize = sb.StreamOffset + 1; break; } if (fh.Is_Reserved()) { // we don't want false detection if (ZstdDecInfo_GET_NUM_FRAMES(p) == 1) return S_FALSE; // _phySize = sb.GetCurOffset(); break; } sb.SkipInBuf((unsigned)(data - sb.GetPtr())); } p->descriptor_OR = (Byte)(p->descriptor_OR | fh.Descriptor); p->descriptor_NOT_OR = (Byte)(p->descriptor_NOT_OR | ~fh.Descriptor); // _numBlocks_Defined = true; // if (fh.Get_DictionaryId_Flag()) // p->dictionaryId_Cur = fh.DictionaryId; if (fh.DictionaryId != 0) { if (p->dictionaryId == 0) p->dictionaryId = fh.DictionaryId; else if (p->dictionaryId != fh.DictionaryId) p->are_DictionaryId_Different = True; } UInt32 blockSizeAllowedMax = (UInt32)1 << 17; { UInt64 winSize = fh.ContentSize; UInt64 winSize_forAllocate = fh.ContentSize; if (!fh.Is_SingleSegment()) { if (p->windowDescriptor_MAX < fh.WindowDescriptor) p->windowDescriptor_MAX = fh.WindowDescriptor; const unsigned e = (fh.WindowDescriptor >> 3); const unsigned m = (fh.WindowDescriptor & 7); winSize = (UInt64)(8 + m) << (e + 10 - 3); if (!fh.Is_ContentSize_Defined() || fh.DictionaryId != 0 || winSize_forAllocate > winSize) winSize_forAllocate = winSize; // p->are_WindowDescriptors = true; } else { // p->are_SingleSegments = True; } if (blockSizeAllowedMax > winSize) blockSizeAllowedMax = (UInt32)winSize; if (p->windowSize_MAX < winSize) p->windowSize_MAX = winSize; if (p->windowSize_Allocate_MAX < winSize_forAllocate) p->windowSize_Allocate_MAX = winSize_forAllocate; } if (fh.Is_ContentSize_Defined()) { // p->are_ContentSize_Known = True; p->contentSize_Total += fh.ContentSize; if (p->contentSize_MAX < fh.ContentSize) p->contentSize_MAX = fh.ContentSize; } else { p->are_ContentSize_Unknown = True; } p->checksum_Defined = false; // p->numBlocks_forType[3] += 99; // for debug if (!_parseMode) { if (ZstdDecInfo_GET_NUM_FRAMES(p) == 1) break; } _wasParsed = true; bool blocksWereParsed = false; for (;;) { if (callback && (p->num_Blocks & 0xFFF) == 2) { // Sleep(10); const UInt64 numBytes = sb.GetCurOffset(); RINOK(callback->SetCompleted(NULL, &numBytes)) } _phySize = sb.GetCurOffset() + 3; RINOK(sb.Read(3)) if (sb.Avail() < 3) { // _needMoreInput = true; // return S_FALSE; break; // change it } const unsigned pos = sb.pos; sb.pos = pos + 3; UInt32 b = 0; b += (UInt32)sb.buf[pos]; b += (UInt32)sb.buf[pos + 1] << (8 * 1); b += (UInt32)sb.buf[pos + 2] << (8 * 2); p->num_Blocks++; const unsigned blockType = (b >> 1) & 3; UInt32 size = b >> 3; // p->num_Blocks_forType[blockType]++; // p->num_BlockBytes_forType[blockType] += size; if (size > blockSizeAllowedMax || blockType == kBlockType_Reserved) { _unsupportedBlock = true; if (ZstdDecInfo_GET_NUM_FRAMES(p) == 1 && p->num_Blocks == 1) return S_FALSE; break; } if (blockType == kBlockType_RLE) size = 1; _phySize = sb.GetCurOffset() + size; RINOK(sb.Skip(size)) if (b & 1) { // it's last block blocksWereParsed = true; break; } } if (!blocksWereParsed) break; if (fh.Is_Checksum()) { _phySize = sb.GetCurOffset() + 4; RINOK(sb.Read(4)) if (sb.Avail() < 4) break; p->checksum_Defined = true; // if (p->num_DataFrames == 1) p->checksum = GetUi32(sb.GetPtr()); sb.SkipInBuf(4); } } if (ZstdDecInfo_GET_NUM_FRAMES(p) == 0) return S_FALSE; _needSeekToStart = true; // } // _parseMode _isArc = true; _stream = stream; _seqStream = stream; return S_OK; COM_TRY_END } Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream)) { Close(); _isArc = true; _seqStream = stream; return S_OK; } Z7_COM7F_IMF(CHandler::Close()) { _isArc = false; _needSeekToStart = false; // _dataAfterEnd = false; // _needMoreInput = false; _unsupportedBlock = false; _wasParsed = false; _phySize_Decoded_Defined = false; _unpackSize_Defined = false; _decoded_Info_Defined = false; ZstdDecInfo_CLEAR(&_parsed_Info) ZstdDecInfo_CLEAR(&_decoded_Info) _phySize = 0; _phySize_Decoded = 0; _unpackSize = 0; _seqStream.Release(); _stream.Release(); return S_OK; } Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback)) { COM_TRY_BEGIN if (numItems == 0) return S_OK; if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) return E_INVALIDARG; if (_wasParsed) { RINOK(extractCallback->SetTotal(_phySize)) } Int32 opRes; { CMyComPtr realOutStream; const Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; RINOK(extractCallback->GetStream(0, &realOutStream, askMode)) if (!testMode && !realOutStream) return S_OK; extractCallback->PrepareOperation(askMode); if (_needSeekToStart) { if (!_stream) return E_FAIL; RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)) } else _needSeekToStart = true; CMyComPtr2_Create lps; lps->Init(extractCallback, true); #ifdef Z7_USE_ZSTD_ORIG_DECODER CMyComPtr2_Create decoder; #else CMyComPtr2_Create decoder; #endif CMyComPtr2_Create outStreamSpec; outStreamSpec->SetStream(realOutStream); outStreamSpec->Init(); // realOutStream.Release(); decoder->FinishMode = true; #ifndef Z7_USE_ZSTD_ORIG_DECODER decoder->DisableHash = _disableHash; #endif // _dataAfterEnd = false; // _needMoreInput = false; const HRESULT hres = decoder.Interface()->Code(_seqStream, outStreamSpec, NULL, NULL, lps); /* { UInt64 t1 = decoder->GetInputProcessedSize(); // for debug const UInt32 kTempSize = 64; Byte buf[kTempSize]; UInt32 processedSize = 0; RINOK(decoder->ReadUnusedFromInBuf(buf, kTempSize, &processedSize)) processedSize -= processedSize; UInt64 t2 = decoder->GetInputProcessedSize(); t2 = t2; t1 = t1; } */ const UInt64 outSize = outStreamSpec->GetSize(); // } // if (hres == E_ABORT) return hres; opRes = NExtract::NOperationResult::kDataError; if (hres == E_OUTOFMEMORY) { return hres; // opRes = NExtract::NOperationResult::kMemError; } else if (hres == S_OK || hres == S_FALSE) { #ifndef Z7_USE_ZSTD_ORIG_DECODER _decoded_Info_Defined = true; _decoded_Info = decoder->_state.info; // NumDataFrames_Decoded = decoder->_state.info.num_DataFrames; // NumSkipFrames_Decoded = decoder->_state.info.num_SkipFrames; const UInt64 inSize = decoder->_inProcessed; #else const UInt64 inSize = decoder->GetInputProcessedSize(); #endif _phySize_Decoded = inSize; _phySize_Decoded_Defined = true; _unpackSize_Defined = true; _unpackSize = outSize; // RINOK( lps.Interface()->SetRatioInfo(&inSize, &outSize); #ifdef Z7_USE_ZSTD_ORIG_DECODER if (hres == S_OK) opRes = NExtract::NOperationResult::kOK; #else if (decoder->ResInfo.decode_SRes == SZ_ERROR_CRC) { opRes = NExtract::NOperationResult::kCRCError; } else if (decoder->ResInfo.decode_SRes == SZ_ERROR_NO_ARCHIVE) { _isArc = false; opRes = NExtract::NOperationResult::kIsNotArc; } else if (decoder->ResInfo.decode_SRes == SZ_ERROR_INPUT_EOF) opRes = NExtract::NOperationResult::kUnexpectedEnd; else { if (hres == S_OK && decoder->ResInfo.decode_SRes == SZ_OK) opRes = NExtract::NOperationResult::kOK; if (decoder->ResInfo.extraSize) { // if (inSize == 0) _isArc = false; opRes = NExtract::NOperationResult::kDataAfterEnd; } /* if (decoder->ResInfo.unexpededEnd) opRes = NExtract::NOperationResult::kUnexpectedEnd; */ } #endif } else if (hres == E_NOTIMPL) { opRes = NExtract::NOperationResult::kUnsupportedMethod; } else return hres; } return extractCallback->SetOperationResult(opRes); COM_TRY_END } Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)) { // return _props.SetProperties(names, values, numProps); // _smallMode = false; _disableHash = false; _parseMode = false; // _parseMode = true; // for debug #ifdef Z7_USE_ZSTD_COMPRESSION _props.Init(); #endif for (UInt32 i = 0; i < numProps; i++) { UString name = names[i]; const PROPVARIANT &value = values[i]; if (name.IsEqualTo("parse")) { bool parseMode = true; RINOK(PROPVARIANT_to_bool(value, parseMode)) _parseMode = parseMode; continue; } if (name.IsPrefixedBy_Ascii_NoCase("crc")) { name.Delete(0, 3); UInt32 crcSize = 4; RINOK(ParsePropToUInt32(name, value, crcSize)) if (crcSize == 0) _disableHash = true; else if (crcSize == 4) _disableHash = false; else return E_INVALIDARG; continue; } #ifdef Z7_USE_ZSTD_COMPRESSION /* if (name.IsEqualTo("small")) { bool smallMode = true; RINOK(PROPVARIANT_to_bool(value, smallMode)) _smallMode = smallMode; continue; } */ RINOK(_props.SetProperty(names[i], value)) #endif } return S_OK; } #ifdef Z7_USE_ZSTD_COMPRESSION Z7_COM7F_IMF(CHandler::GetFileTimeType(UInt32 *timeType)) { *timeType = GET_FileTimeType_NotDefined_for_GetFileTimeType; // *timeType = NFileTimeType::kUnix; return S_OK; } Z7_COM7F_IMF(CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback)) { COM_TRY_BEGIN if (numItems != 1) return E_INVALIDARG; { CMyComPtr setRestriction; outStream->QueryInterface(IID_IStreamSetRestriction, (void **)&setRestriction); if (setRestriction) RINOK(setRestriction->SetRestriction(0, 0)) } Int32 newData, newProps; UInt32 indexInArchive; if (!updateCallback) return E_FAIL; RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)) if (IntToBool(newProps)) { { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)) if (prop.vt != VT_EMPTY) if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE) return E_INVALIDARG; } } if (IntToBool(newData)) { UInt64 size; { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(0, kpidSize, &prop)) if (prop.vt != VT_UI8) return E_INVALIDARG; size = prop.uhVal.QuadPart; } if (!_props.MethodName.IsEmpty() && !_props.MethodName.IsEqualTo_Ascii_NoCase("zstd")) return E_INVALIDARG; { CMyComPtr fileInStream; RINOK(updateCallback->GetStream(0, &fileInStream)) if (!fileInStream) return S_FALSE; { CMyComPtr streamGetSize; fileInStream.QueryInterface(IID_IStreamGetSize, &streamGetSize); if (streamGetSize) { UInt64 size2; if (streamGetSize->GetSize(&size2) == S_OK) size = size2; } } RINOK(updateCallback->SetTotal(size)) CMethodProps props2 = _props; #ifndef Z7_ST /* CSingleMethodProps (_props) derives from CMethodProps (props2) So we transfer additional variable (num Threads) to CMethodProps list of properties */ UInt32 numThreads = _props._numThreads; if (numThreads > Z7_ZSTDMT_NBWORKERS_MAX) numThreads = Z7_ZSTDMT_NBWORKERS_MAX; if (_props.FindProp(NCoderPropID::kNumThreads) < 0) { if (!_props._numThreads_WasForced && numThreads >= 1 && _props._memUsage_WasSet) { NCompress::NZstd::CEncoderProps zstdProps; RINOK(zstdProps.SetFromMethodProps(_props)) ZstdEncProps_NormalizeFull(&zstdProps.EncProps); numThreads = ZstdEncProps_GetNumThreads_for_MemUsageLimit( &zstdProps.EncProps, _props._memUsage_Compress, numThreads); } props2.AddProp_NumThreads(numThreads); } #endif // Z7_ST CMyComPtr2_Create lps; lps->Init(updateCallback, true); { CMyComPtr2_Create encoder; // size = 1 << 24; // for debug RINOK(props2.SetCoderProps(encoder.ClsPtr(), size != (UInt64)(Int64)-1 ? &size : NULL)) // encoderSpec->_props.SmallFileOpt = _smallMode; // we must set kExpectedDataSize just before Code(). encoder->SrcSizeHint64 = size; /* CMyComPtr optProps; _compressEncoder->QueryInterface(IID_ICompressSetCoderPropertiesOpt, (void**)&optProps); if (optProps) { PROPID propID = NCoderPropID::kExpectedDataSize; NWindows::NCOM::CPropVariant prop = (UInt64)size; // RINOK(optProps->SetCoderPropertiesOpt(&propID, &prop, 1)) RINOK(encoderSpec->SetCoderPropertiesOpt(&propID, &prop, 1)) } */ RINOK(encoder.Interface()->Code(fileInStream, outStream, NULL, NULL, lps)) } } return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK); } if (indexInArchive != 0) return E_INVALIDARG; CMyComPtr2_Create lps; lps->Init(updateCallback, true); CMyComPtr opCallback; updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback); if (opCallback) { RINOK(opCallback->ReportOperation( NEventIndexType::kInArcIndex, 0, NUpdateNotifyOp::kReplicate)) } if (_stream) RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)) return NCompress::CopyStream(_stream, outStream, lps); COM_TRY_END } #endif #ifndef Z7_USE_ZSTD_COMPRESSION #undef IMP_CreateArcOut #define IMP_CreateArcOut #undef CreateArcOut #define CreateArcOut NULL #endif #ifdef Z7_USE_ZSTD_COMPRESSION REGISTER_ARC_IO( "zstd2", "zst tzst", "* .tar", 0xe + 1, k_Signature, 0 , NArcInfoFlags::kKeepName , 0 , NULL) #else REGISTER_ARC_IO( "zstd", "zst tzst", "* .tar", 0xe, k_Signature, 0 , NArcInfoFlags::kKeepName , 0 , NULL) #endif }}