From 951ebb3fa297628f7ae49cda4e2395cb9b913859 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Dec 2025 12:30:37 +0000
Subject: [PATCH] Complete ZstdSharp integration: Add all Unsafe implementation
files and remove ZstdSharp.Port dependency
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
---
src/SharpCompress/Common/Zip/ZipFilePart.cs | 2 +-
.../Compressors/LZMA/Registry.cs | 2 +-
.../Compressors/ZStandard/BitOperations.cs | 311 +
.../ZStandard/CompressionStream.cs | 301 +
.../Compressors/ZStandard/Compressor.cs | 204 +
.../Compressors/ZStandard/Constants.cs | 8 +
.../ZStandard/DecompressionStream.cs | 293 +
.../Compressors/ZStandard/Decompressor.cs | 176 +
.../Compressors/ZStandard/JobThreadPool.cs | 141 +
.../Compressors/ZStandard/SafeHandles.cs | 163 +
.../ZStandard/SynchronizationWrapper.cs | 22 +
.../Compressors/ZStandard/ThrowHelper.cs | 2 +-
.../Compressors/ZStandard/UnmanagedObject.cs | 18 +
.../ZStandard/Unsafe/BIT_CStream_t.cs | 14 +
.../ZStandard/Unsafe/BIT_DStream_status.cs | 16 +
.../ZStandard/Unsafe/BIT_DStream_t.cs | 13 +
.../Compressors/ZStandard/Unsafe/Bitstream.cs | 739 +
.../ZStandard/Unsafe/BlockSummary.cs | 8 +
.../ZStandard/Unsafe/COVER_best_s.cs | 20 +
.../ZStandard/Unsafe/COVER_ctx_t.cs | 19 +
.../ZStandard/Unsafe/COVER_dictSelection.cs | 11 +
.../ZStandard/Unsafe/COVER_epoch_info_t.cs | 10 +
.../ZStandard/Unsafe/COVER_map_pair_t_s.cs | 7 +
.../ZStandard/Unsafe/COVER_map_s.cs | 9 +
.../ZStandard/Unsafe/COVER_segment_t.cs | 11 +
.../Unsafe/COVER_tryParameters_data_s.cs | 12 +
.../Compressors/ZStandard/Unsafe/Clevels.cs | 849 ++
.../Compressors/ZStandard/Unsafe/Cover.cs | 444 +
.../ZStandard/Unsafe/DTableDesc.cs | 12 +
.../ZStandard/Unsafe/EStats_ress_t.cs | 13 +
.../ZStandard/Unsafe/EntropyCommon.cs | 447 +
.../ZStandard/Unsafe/EstimatedBlockSize.cs | 7 +
.../ZStandard/Unsafe/FASTCOVER_accel_t.cs | 19 +
.../ZStandard/Unsafe/FASTCOVER_ctx_t.cs | 19 +
.../Unsafe/FASTCOVER_tryParameters_data_s.cs | 12 +
.../Compressors/ZStandard/Unsafe/FPStats.cs | 7 +
.../ZStandard/Unsafe/FSE_CState_t.cs | 16 +
.../ZStandard/Unsafe/FSE_DState_t.cs | 12 +
.../ZStandard/Unsafe/FSE_DTableHeader.cs | 8 +
.../ZStandard/Unsafe/FSE_DecompressWksp.cs | 6 +
.../ZStandard/Unsafe/FSE_decode_t.cs | 8 +
.../ZStandard/Unsafe/FSE_repeat.cs | 13 +
.../Unsafe/FSE_symbolCompressionTransform.cs | 10 +
.../Compressors/ZStandard/Unsafe/Fastcover.cs | 761 ++
.../ZStandard/Unsafe/Fingerprint.cs | 7 +
.../Compressors/ZStandard/Unsafe/Fse.cs | 198 +
.../ZStandard/Unsafe/FseCompress.cs | 782 ++
.../ZStandard/Unsafe/FseDecompress.cs | 462 +
.../ZStandard/Unsafe/HIST_checkInput_e.cs | 7 +
.../ZStandard/Unsafe/HUF_CStream_t.cs | 22 +
.../ZStandard/Unsafe/HUF_CTableHeader.cs | 8 +
.../Unsafe/HUF_CompressWeightsWksp.cs | 9 +
.../ZStandard/Unsafe/HUF_DEltX1.cs | 11 +
.../ZStandard/Unsafe/HUF_DEltX2.cs | 12 +
.../Unsafe/HUF_DecompressFastArgs.cs | 49 +
.../Unsafe/HUF_ReadDTableX1_Workspace.cs | 10 +
.../Unsafe/HUF_ReadDTableX2_Workspace.cs | 307 +
.../ZStandard/Unsafe/HUF_WriteCTableWksp.cs | 10 +
.../Unsafe/HUF_buildCTable_wksp_tables.cs | 739 +
.../ZStandard/Unsafe/HUF_compress_tables_t.cs | 280 +
.../ZStandard/Unsafe/HUF_flags_e.cs | 44 +
.../ZStandard/Unsafe/HUF_nbStreams_e.cs | 7 +
.../ZStandard/Unsafe/HUF_repeat.cs | 13 +
.../Compressors/ZStandard/Unsafe/Hist.cs | 273 +
.../ZStandard/Unsafe/HufCompress.cs | 1825 +++
.../ZStandard/Unsafe/HufDecompress.cs | 2505 ++++
.../Compressors/ZStandard/Unsafe/Pool.cs | 122 +
.../ZStandard/Unsafe/RSyncState_t.cs | 8 +
.../Compressors/ZStandard/Unsafe/Range.cs | 14 +
.../ZStandard/Unsafe/RawSeqStore_t.cs | 29 +
.../ZStandard/Unsafe/RoundBuff_t.cs | 28 +
.../ZStandard/Unsafe/SeqCollector.cs | 9 +
.../Compressors/ZStandard/Unsafe/SeqDef_s.cs | 14 +
.../ZStandard/Unsafe/SeqStore_t.cs | 27 +
.../ZStandard/Unsafe/SerialState.cs | 23 +
.../ZStandard/Unsafe/SymbolEncodingType_e.cs | 9 +
.../Compressors/ZStandard/Unsafe/SyncPoint.cs | 10 +
.../ZStandard/Unsafe/XXH32_canonical_t.cs | 10 +
.../ZStandard/Unsafe/XXH32_state_s.cs | 34 +
.../ZStandard/Unsafe/XXH64_canonical_t.cs | 9 +
.../ZStandard/Unsafe/XXH64_state_s.cs | 34 +
.../ZStandard/Unsafe/XXH_alignment.cs | 14 +
.../ZStandard/Unsafe/XXH_errorcode.cs | 13 +
.../Compressors/ZStandard/Unsafe/Xxhash.cs | 626 +
.../ZStandard/Unsafe/ZDICT_cover_params_t.cs | 30 +
.../Unsafe/ZDICT_fastCover_params_t.cs | 32 +
.../ZStandard/Unsafe/ZDICT_legacy_params_t.cs | 8 +
.../ZStandard/Unsafe/ZDICT_params_t.cs | 20 +
.../ZStandard/Unsafe/ZSTDMT_CCtxPool.cs | 12 +
.../ZStandard/Unsafe/ZSTDMT_CCtx_s.cs | 32 +
.../ZStandard/Unsafe/ZSTDMT_bufferPool_s.cs | 11 +
.../ZStandard/Unsafe/ZSTDMT_jobDescription.cs | 61 +
.../Unsafe/ZSTD_BlockCompressor_f.cs | 12 +
.../ZStandard/Unsafe/ZSTD_BuildCTableWksp.cs | 7 +
.../ZStandard/Unsafe/ZSTD_BuildSeqStore_e.cs | 7 +
.../ZStandard/Unsafe/ZSTD_CCtx_params_s.cs | 86 +
.../ZStandard/Unsafe/ZSTD_CCtx_s.cs | 91 +
.../ZStandard/Unsafe/ZSTD_CDict_s.cs | 30 +
.../ZStandard/Unsafe/ZSTD_CParamMode_e.cs | 29 +
.../ZStandard/Unsafe/ZSTD_DCtx_s.cs | 95 +
.../ZStandard/Unsafe/ZSTD_DDictHashSet.cs | 9 +
.../ZStandard/Unsafe/ZSTD_DDict_s.cs | 15 +
.../ZStandard/Unsafe/ZSTD_DefaultPolicy_e.cs | 7 +
.../ZStandard/Unsafe/ZSTD_EndDirective.cs | 23 +-
.../ZStandard/Unsafe/ZSTD_MatchState_t.cs | 66 +
.../ZStandard/Unsafe/ZSTD_OffsetInfo.cs | 7 +
.../ZStandard/Unsafe/ZSTD_OptPrice_e.cs | 7 +
.../ZStandard/Unsafe/ZSTD_Sequence.cs | 40 +
.../ZStandard/Unsafe/ZSTD_SequenceLength.cs | 7 +
.../ZStandard/Unsafe/ZSTD_SequencePosition.cs | 13 +
.../ZStandard/Unsafe/ZSTD_blockSplitCtx.cs | 12 +
.../ZStandard/Unsafe/ZSTD_blockState_t.cs | 8 +
.../ZStandard/Unsafe/ZSTD_bufferMode_e.cs | 11 +
.../Unsafe/ZSTD_buffered_policy_e.cs | 12 +
.../ZStandard/Unsafe/ZSTD_cParameter.cs | 247 +-
.../ZStandard/Unsafe/ZSTD_cStreamStage.cs | 8 +
.../Unsafe/ZSTD_compResetPolicy_e.cs | 14 +
.../Unsafe/ZSTD_compressedBlockState_t.cs | 7 +
.../Unsafe/ZSTD_compressionParameters.cs | 14 +-
.../Unsafe/ZSTD_compressionStage_e.cs | 12 +
.../ZStandard/Unsafe/ZSTD_cwksp.cs | 110 +
.../Unsafe/ZSTD_cwksp_alloc_phase_e.cs | 12 +
.../Unsafe/ZSTD_cwksp_static_alloc_e.cs | 12 +
.../ZStandard/Unsafe/ZSTD_dStage.cs | 13 +
.../ZStandard/Unsafe/ZSTD_dStreamStage.cs | 10 +
.../ZStandard/Unsafe/ZSTD_dictAttachPref_e.cs | 16 +
.../Unsafe/ZSTD_dictContentType_e.cs | 6 +-
.../ZStandard/Unsafe/ZSTD_dictLoadMethod_e.cs | 4 +-
.../ZStandard/Unsafe/ZSTD_dictMode_e.cs | 9 +
.../Unsafe/ZSTD_dictTableLoadMethod_e.cs | 7 +
.../ZStandard/Unsafe/ZSTD_dictUses_e.cs | 13 +
.../Unsafe/ZSTD_entropyCTablesMetadata_t.cs | 7 +
.../ZStandard/Unsafe/ZSTD_entropyCTables_t.cs | 7 +
.../ZStandard/Unsafe/ZSTD_entropyDTables_t.cs | 1342 ++
.../Unsafe/ZSTD_forceIgnoreChecksum_e.cs | 8 +
.../ZStandard/Unsafe/ZSTD_format_e.cs | 12 +
.../ZStandard/Unsafe/ZSTD_frameHeader.cs | 8 +-
.../ZStandard/Unsafe/ZSTD_frameParameters.cs | 6 +-
.../ZStandard/Unsafe/ZSTD_frameProgression.cs | 22 +
.../ZStandard/Unsafe/ZSTD_frameSizeInfo.cs | 14 +
.../Unsafe/ZSTD_fseCTablesMetadata_t.cs | 18 +
.../ZStandard/Unsafe/ZSTD_fseCTables_t.cs | 11 +
.../ZStandard/Unsafe/ZSTD_fseState.cs | 7 +
.../ZStandard/Unsafe/ZSTD_getAllMatchesFn.cs | 15 +
.../Unsafe/ZSTD_hufCTablesMetadata_t.cs | 16 +
.../ZStandard/Unsafe/ZSTD_hufCTables_t.cs | 279 +
.../ZStandard/Unsafe/ZSTD_inBuffer_s.cs | 6 +-
.../Unsafe/ZSTD_indexResetPolicy_e.cs | 12 +
.../ZStandard/Unsafe/ZSTD_litLocation_e.cs | 13 +
.../Unsafe/ZSTD_literalCompressionMode_e.cs | 16 +
.../ZStandard/Unsafe/ZSTD_localDict.cs | 10 +
.../ZStandard/Unsafe/ZSTD_longLengthType_e.cs | 14 +
.../ZStandard/Unsafe/ZSTD_longOffset_e.cs | 7 +
.../ZStandard/Unsafe/ZSTD_match_t.cs | 13 +
.../ZStandard/Unsafe/ZSTD_nextInputType_e.cs | 11 +
.../ZStandard/Unsafe/ZSTD_optLdm_t.cs | 17 +
.../ZStandard/Unsafe/ZSTD_optimal_t.cs | 19 +
.../ZStandard/Unsafe/ZSTD_outBuffer_s.cs | 6 +-
.../ZStandard/Unsafe/ZSTD_overlap_e.cs | 7 +
.../ZStandard/Unsafe/ZSTD_paramSwitch_e.cs | 13 +
.../ZStandard/Unsafe/ZSTD_prefixDict_s.cs | 8 +
.../Unsafe/ZSTD_refMultipleDDicts_e.cs | 8 +
.../ZStandard/Unsafe/ZSTD_resetTarget_e.cs | 7 +
.../ZStandard/Unsafe/ZSTD_seqSymbol.cs | 17 +
.../ZStandard/Unsafe/ZSTD_seqSymbol_header.cs | 10 +
.../ZStandard/Unsafe/ZSTD_sequenceFormat_e.cs | 10 +
.../Unsafe/ZSTD_symbolEncodingTypeStats_t.cs | 16 +
.../Unsafe/ZSTD_tableFillPurpose_e.cs | 7 +
.../ZStandard/Unsafe/ZSTD_window_t.cs | 25 +
.../Compressors/ZStandard/Unsafe/Zdict.cs | 640 +
.../Compressors/ZStandard/Unsafe/Zstd.cs | 10 +
.../ZStandard/Unsafe/ZstdCommon.cs | 13 -
.../ZStandard/Unsafe/ZstdCompress.cs | 11225 ++++++++++++++++
.../ZStandard/Unsafe/ZstdCompressInternal.cs | 1444 ++
.../ZStandard/Unsafe/ZstdCompressLiterals.cs | 314 +
.../ZStandard/Unsafe/ZstdCompressSequences.cs | 1211 ++
.../Unsafe/ZstdCompressSuperblock.cs | 976 ++
.../Compressors/ZStandard/Unsafe/ZstdCwksp.cs | 581 +
.../Compressors/ZStandard/Unsafe/ZstdDdict.cs | 292 +
.../ZStandard/Unsafe/ZstdDecompress.cs | 3420 +++++
.../ZStandard/Unsafe/ZstdDecompressBlock.cs | 3218 +++++
.../Unsafe/ZstdDecompressInternal.cs | 394 +
.../ZStandard/Unsafe/ZstdDoubleFast.cs | 1118 ++
.../Compressors/ZStandard/Unsafe/ZstdFast.cs | 1209 ++
.../ZStandard/Unsafe/ZstdInternal.cs | 638 +
.../Compressors/ZStandard/Unsafe/ZstdLazy.cs | 4661 +++++++
.../Compressors/ZStandard/Unsafe/ZstdLdm.cs | 940 ++
.../ZStandard/Unsafe/ZstdLdmGeartab.cs | 539 +
.../Compressors/ZStandard/Unsafe/ZstdOpt.cs | 2254 ++++
.../ZStandard/Unsafe/ZstdPresplit.cs | 295 +
.../ZStandard/Unsafe/ZstdmtCompress.cs | 1894 +++
.../ZStandard/Unsafe/_wksps_e__Union.cs | 16 +
.../ZStandard/Unsafe/algo_time_t.cs | 13 +
.../ZStandard/Unsafe/base_directive_e.cs | 7 +
.../ZStandard/Unsafe/blockProperties_t.cs | 8 +
.../ZStandard/Unsafe/blockType_e.cs | 9 +
.../Compressors/ZStandard/Unsafe/buffer_s.cs | 15 +
.../Compressors/ZStandard/Unsafe/dictItem.cs | 8 +
.../Compressors/ZStandard/Unsafe/inBuff_t.cs | 12 +
.../ZStandard/Unsafe/ldmEntry_t.cs | 7 +
.../ZStandard/Unsafe/ldmMatchCandidate_t.cs | 9 +
.../ZStandard/Unsafe/ldmParams_t.cs | 22 +
.../ZStandard/Unsafe/ldmRollingHashState_t.cs | 7 +
.../ZStandard/Unsafe/ldmState_t.cs | 170 +
.../Compressors/ZStandard/Unsafe/nodeElt_s.cs | 12 +
.../ZStandard/Unsafe/offsetCount_t.cs | 7 +
.../ZStandard/Unsafe/optState_t.cs | 53 +
.../Compressors/ZStandard/Unsafe/rankPos.cs | 7 +
.../ZStandard/Unsafe/rankValCol_t.cs | 6 +
.../Compressors/ZStandard/Unsafe/rawSeq.cs | 13 +
.../ZStandard/Unsafe/repcodes_s.cs | 6 +
.../ZStandard/Unsafe/searchMethod_e.cs | 8 +
.../ZStandard/Unsafe/seqState_t.cs | 17 +
.../ZStandard/Unsafe/seqStoreSplits.cs | 11 +
.../Compressors/ZStandard/Unsafe/seq_t.cs | 8 +
.../ZStandard/Unsafe/sortedSymbol_t.cs | 6 +
.../ZStandard/Unsafe/streaming_operation.cs | 8 +
.../Compressors/ZStandard/ZStandardStream.cs | 2 +-
.../ZStandard/ZstandardConstants.cs | 10 +-
.../Compressors/ZStandard/ZstdException.cs | 2 +-
src/SharpCompress/SharpCompress.csproj | 1 -
src/SharpCompress/Writers/Zip/ZipWriter.cs | 3 +-
src/SharpCompress/packages.lock.json | 23 -
tests/SharpCompress.Test/packages.lock.json | 43 +-
224 files changed, 54783 insertions(+), 218 deletions(-)
create mode 100644 src/SharpCompress/Compressors/ZStandard/BitOperations.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/CompressionStream.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Compressor.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Constants.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/DecompressionStream.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Decompressor.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/JobThreadPool.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/SafeHandles.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/SynchronizationWrapper.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/UnmanagedObject.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_CStream_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_DStream_status.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_DStream_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Bitstream.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/BlockSummary.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_best_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_ctx_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_dictSelection.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_epoch_info_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_map_pair_t_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_map_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_segment_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_tryParameters_data_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Clevels.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Cover.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/DTableDesc.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/EStats_ress_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/EntropyCommon.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/EstimatedBlockSize.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_accel_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_ctx_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_tryParameters_data_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FPStats.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_CState_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DState_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DTableHeader.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DecompressWksp.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_decode_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_repeat.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_symbolCompressionTransform.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Fastcover.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Fingerprint.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Fse.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FseCompress.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/FseDecompress.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HIST_checkInput_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CStream_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CTableHeader.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CompressWeightsWksp.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DEltX1.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DEltX2.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DecompressFastArgs.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_ReadDTableX1_Workspace.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_ReadDTableX2_Workspace.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_WriteCTableWksp.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_buildCTable_wksp_tables.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_compress_tables_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_flags_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_nbStreams_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_repeat.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Hist.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HufCompress.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/HufDecompress.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Pool.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/RSyncState_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Range.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/RawSeqStore_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/RoundBuff_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/SeqCollector.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/SeqDef_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/SeqStore_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/SerialState.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/SymbolEncodingType_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/SyncPoint.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/XXH32_canonical_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/XXH32_state_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/XXH64_canonical_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/XXH64_state_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/XXH_alignment.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/XXH_errorcode.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Xxhash.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_cover_params_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_fastCover_params_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_legacy_params_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_params_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_CCtxPool.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_CCtx_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_bufferPool_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_jobDescription.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BlockCompressor_f.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BuildCTableWksp.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BuildSeqStore_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_CCtx_params_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_CCtx_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_CDict_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_CParamMode_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_DCtx_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_DDictHashSet.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_DDict_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_DefaultPolicy_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_MatchState_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_OffsetInfo.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_OptPrice_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_Sequence.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_SequenceLength.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_SequencePosition.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_blockSplitCtx.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_blockState_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_bufferMode_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_buffered_policy_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cStreamStage.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compResetPolicy_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressedBlockState_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressionStage_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp_alloc_phase_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp_static_alloc_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dStage.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dStreamStage.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictAttachPref_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictMode_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictTableLoadMethod_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictUses_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyCTablesMetadata_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyCTables_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyDTables_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_forceIgnoreChecksum_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_format_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameProgression.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameSizeInfo.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseCTablesMetadata_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseCTables_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseState.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_getAllMatchesFn.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_hufCTablesMetadata_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_hufCTables_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_indexResetPolicy_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_litLocation_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_literalCompressionMode_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_localDict.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_longLengthType_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_longOffset_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_match_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_nextInputType_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_optLdm_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_optimal_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_overlap_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_paramSwitch_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_prefixDict_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_refMultipleDDicts_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_resetTarget_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_seqSymbol.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_seqSymbol_header.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_sequenceFormat_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_symbolEncodingTypeStats_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_tableFillPurpose_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_window_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Zdict.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Zstd.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompress.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressInternal.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressLiterals.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressSequences.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressSuperblock.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCwksp.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDdict.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompress.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompressBlock.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompressInternal.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDoubleFast.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdFast.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdInternal.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLazy.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLdm.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLdmGeartab.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdOpt.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdPresplit.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdmtCompress.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/_wksps_e__Union.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/algo_time_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/base_directive_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/blockProperties_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/blockType_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/buffer_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/dictItem.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/inBuff_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ldmEntry_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ldmMatchCandidate_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ldmParams_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ldmRollingHashState_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ldmState_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/nodeElt_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/offsetCount_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/optState_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/rankPos.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/rankValCol_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/rawSeq.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/repcodes_s.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/searchMethod_e.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/seqState_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/seqStoreSplits.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/seq_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/sortedSymbol_t.cs
create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/streaming_operation.cs
diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs
index 77dc4abb..16eb8e1a 100644
--- a/src/SharpCompress/Common/Zip/ZipFilePart.cs
+++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs
@@ -13,8 +13,8 @@ using SharpCompress.Compressors.PPMd;
using SharpCompress.Compressors.Reduce;
using SharpCompress.Compressors.Shrink;
using SharpCompress.Compressors.Xz;
+using SharpCompress.Compressors.ZStandard;
using SharpCompress.IO;
-using ZstdSharp;
namespace SharpCompress.Common.Zip;
diff --git a/src/SharpCompress/Compressors/LZMA/Registry.cs b/src/SharpCompress/Compressors/LZMA/Registry.cs
index eb3e3bdd..d71abded 100644
--- a/src/SharpCompress/Compressors/LZMA/Registry.cs
+++ b/src/SharpCompress/Compressors/LZMA/Registry.cs
@@ -7,7 +7,7 @@ using SharpCompress.Compressors.Deflate;
using SharpCompress.Compressors.Filters;
using SharpCompress.Compressors.LZMA.Utilites;
using SharpCompress.Compressors.PPMd;
-using ZstdSharp;
+using SharpCompress.Compressors.ZStandard;
namespace SharpCompress.Compressors.LZMA;
diff --git a/src/SharpCompress/Compressors/ZStandard/BitOperations.cs b/src/SharpCompress/Compressors/ZStandard/BitOperations.cs
new file mode 100644
index 00000000..fc8e3108
--- /dev/null
+++ b/src/SharpCompress/Compressors/ZStandard/BitOperations.cs
@@ -0,0 +1,311 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#if !NETCOREAPP3_0_OR_GREATER
+
+using System.Runtime.CompilerServices;
+using static SharpCompress.Compressors.ZStandard.UnsafeHelper;
+
+// Some routines inspired by the Stanford Bit Twiddling Hacks by Sean Eron Anderson:
+// http://graphics.stanford.edu/~seander/bithacks.html
+
+namespace System.Numerics
+{
+ ///
+ /// Utility methods for intrinsic bit-twiddling operations.
+ /// The methods use hardware intrinsics when available on the underlying platform,
+ /// otherwise they use optimized software fallbacks.
+ ///
+ public static unsafe class BitOperations
+ {
+ // hack: should be public because of inline
+ public static readonly byte* TrailingZeroCountDeBruijn = GetArrayPointer(
+ new byte[]
+ {
+ 00,
+ 01,
+ 28,
+ 02,
+ 29,
+ 14,
+ 24,
+ 03,
+ 30,
+ 22,
+ 20,
+ 15,
+ 25,
+ 17,
+ 04,
+ 08,
+ 31,
+ 27,
+ 13,
+ 23,
+ 21,
+ 19,
+ 16,
+ 07,
+ 26,
+ 12,
+ 18,
+ 06,
+ 11,
+ 05,
+ 10,
+ 09,
+ }
+ );
+
+ // hack: should be public because of inline
+ public static readonly byte* Log2DeBruijn = GetArrayPointer(
+ new byte[]
+ {
+ 00,
+ 09,
+ 01,
+ 10,
+ 13,
+ 21,
+ 02,
+ 29,
+ 11,
+ 14,
+ 16,
+ 18,
+ 22,
+ 25,
+ 03,
+ 30,
+ 08,
+ 12,
+ 20,
+ 28,
+ 15,
+ 17,
+ 24,
+ 07,
+ 19,
+ 27,
+ 23,
+ 06,
+ 26,
+ 05,
+ 04,
+ 31,
+ }
+ );
+
+ ///
+ /// Returns the integer (floor) log of the specified value, base 2.
+ /// Note that by convention, input value 0 returns 0 since log(0) is undefined.
+ ///
+ /// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Log2(uint value)
+ {
+ // The 0->0 contract is fulfilled by setting the LSB to 1.
+ // Log(1) is 0, and setting the LSB for values > 1 does not change the log2 result.
+ value |= 1;
+
+ // value lzcnt actual expected
+ // ..0001 31 31-31 0
+ // ..0010 30 31-30 1
+ // 0010.. 2 31-2 29
+ // 0100.. 1 31-1 30
+ // 1000.. 0 31-0 31
+
+ // Fallback contract is 0->0
+ // No AggressiveInlining due to large method size
+ // Has conventional contract 0->0 (Log(0) is undefined)
+
+ // Fill trailing zeros with ones, eg 00010010 becomes 00011111
+ value |= value >> 01;
+ value |= value >> 02;
+ value |= value >> 04;
+ value |= value >> 08;
+ value |= value >> 16;
+
+ // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
+ return Log2DeBruijn[
+ // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u
+ (int)((value * 0x07C4ACDDu) >> 27)
+ ];
+ }
+
+ ///
+ /// Returns the integer (floor) log of the specified value, base 2.
+ /// Note that by convention, input value 0 returns 0 since log(0) is undefined.
+ ///
+ /// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Log2(ulong value)
+ {
+ value |= 1;
+
+ uint hi = (uint)(value >> 32);
+
+ if (hi == 0)
+ {
+ return Log2((uint)value);
+ }
+
+ return 32 + Log2(hi);
+ }
+
+ ///
+ /// Count the number of trailing zero bits in an integer value.
+ /// Similar in behavior to the x86 instruction TZCNT.
+ ///
+ /// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int TrailingZeroCount(int value) => TrailingZeroCount((uint)value);
+
+ ///
+ /// Count the number of trailing zero bits in an integer value.
+ /// Similar in behavior to the x86 instruction TZCNT.
+ ///
+ /// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int TrailingZeroCount(uint value)
+ {
+ // Unguarded fallback contract is 0->0, BSF contract is 0->undefined
+ if (value == 0)
+ {
+ return 32;
+ }
+
+ // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
+ return TrailingZeroCountDeBruijn[
+ // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_0111_1100_1011_0101_0011_0001u
+ (int)(((value & (uint)-(int)value) * 0x077CB531u) >> 27)
+ ]; // Multi-cast mitigates redundant conv.u8
+ }
+
+ ///
+ /// Count the number of trailing zero bits in a mask.
+ /// Similar in behavior to the x86 instruction TZCNT.
+ ///
+ /// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int TrailingZeroCount(long value) => TrailingZeroCount((ulong)value);
+
+ ///
+ /// Count the number of trailing zero bits in a mask.
+ /// Similar in behavior to the x86 instruction TZCNT.
+ ///
+ /// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int TrailingZeroCount(ulong value)
+ {
+ uint lo = (uint)value;
+
+ if (lo == 0)
+ {
+ return 32 + TrailingZeroCount((uint)(value >> 32));
+ }
+
+ return TrailingZeroCount(lo);
+ }
+
+ ///
+ /// Rotates the specified value left by the specified number of bits.
+ /// Similar in behavior to the x86 instruction ROL.
+ ///
+ /// The value to rotate.
+ /// The number of bits to rotate by.
+ /// Any value outside the range [0..31] is treated as congruent mod 32.
+ /// The rotated value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint RotateLeft(uint value, int offset) =>
+ (value << offset) | (value >> (32 - offset));
+
+ ///
+ /// Rotates the specified value left by the specified number of bits.
+ /// Similar in behavior to the x86 instruction ROL.
+ ///
+ /// The value to rotate.
+ /// The number of bits to rotate by.
+ /// Any value outside the range [0..63] is treated as congruent mod 64.
+ /// The rotated value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong RotateLeft(ulong value, int offset) =>
+ (value << offset) | (value >> (64 - offset));
+
+ ///
+ /// Rotates the specified value right by the specified number of bits.
+ /// Similar in behavior to the x86 instruction ROR.
+ ///
+ /// The value to rotate.
+ /// The number of bits to rotate by.
+ /// Any value outside the range [0..31] is treated as congruent mod 32.
+ /// The rotated value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint RotateRight(uint value, int offset) =>
+ (value >> offset) | (value << (32 - offset));
+
+ ///
+ /// Rotates the specified value right by the specified number of bits.
+ /// Similar in behavior to the x86 instruction ROR.
+ ///
+ /// The value to rotate.
+ /// The number of bits to rotate by.
+ /// Any value outside the range [0..63] is treated as congruent mod 64.
+ /// The rotated value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong RotateRight(ulong value, int offset) =>
+ (value >> offset) | (value << (64 - offset));
+
+ ///
+ /// Count the number of leading zero bits in a mask.
+ /// Similar in behavior to the x86 instruction LZCNT.
+ ///
+ /// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LeadingZeroCount(uint value)
+ {
+ // Unguarded fallback contract is 0->31, BSR contract is 0->undefined
+ if (value == 0)
+ {
+ return 32;
+ }
+
+ // No AggressiveInlining due to large method size
+ // Has conventional contract 0->0 (Log(0) is undefined)
+
+ // Fill trailing zeros with ones, eg 00010010 becomes 00011111
+ value |= value >> 01;
+ value |= value >> 02;
+ value |= value >> 04;
+ value |= value >> 08;
+ value |= value >> 16;
+
+ // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
+ return 31
+ ^ Log2DeBruijn[
+ // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here
+ (int)((value * 0x07C4ACDDu) >> 27)
+ ];
+ }
+
+ ///
+ /// Count the number of leading zero bits in a mask.
+ /// Similar in behavior to the x86 instruction LZCNT.
+ ///
+ /// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LeadingZeroCount(ulong value)
+ {
+ uint hi = (uint)(value >> 32);
+
+ if (hi == 0)
+ {
+ return 32 + LeadingZeroCount((uint)value);
+ }
+
+ return LeadingZeroCount(hi);
+ }
+ }
+}
+
+#endif
diff --git a/src/SharpCompress/Compressors/ZStandard/CompressionStream.cs b/src/SharpCompress/Compressors/ZStandard/CompressionStream.cs
new file mode 100644
index 00000000..92de03b3
--- /dev/null
+++ b/src/SharpCompress/Compressors/ZStandard/CompressionStream.cs
@@ -0,0 +1,301 @@
+using System;
+using System.Buffers;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using SharpCompress.Compressors.ZStandard.Unsafe;
+
+namespace SharpCompress.Compressors.ZStandard;
+
+public class CompressionStream : Stream
+{
+ private readonly Stream innerStream;
+ private readonly byte[] outputBuffer;
+ private readonly bool preserveCompressor;
+ private readonly bool leaveOpen;
+ private Compressor? compressor;
+ private ZSTD_outBuffer_s output;
+
+ public CompressionStream(
+ Stream stream,
+ int level = Compressor.DefaultCompressionLevel,
+ int bufferSize = 0,
+ bool leaveOpen = true
+ )
+ : this(stream, new Compressor(level), bufferSize, false, leaveOpen) { }
+
+ public CompressionStream(
+ Stream stream,
+ Compressor compressor,
+ int bufferSize = 0,
+ bool preserveCompressor = true,
+ bool leaveOpen = true
+ )
+ {
+ if (stream == null)
+ throw new ArgumentNullException(nameof(stream));
+
+ if (!stream.CanWrite)
+ throw new ArgumentException("Stream is not writable", nameof(stream));
+
+ if (bufferSize < 0)
+ throw new ArgumentOutOfRangeException(nameof(bufferSize));
+
+ innerStream = stream;
+ this.compressor = compressor;
+ this.preserveCompressor = preserveCompressor;
+ this.leaveOpen = leaveOpen;
+
+ var outputBufferSize =
+ bufferSize > 0
+ ? bufferSize
+ : (int)Unsafe.Methods.ZSTD_CStreamOutSize().EnsureZstdSuccess();
+ outputBuffer = ArrayPool.Shared.Rent(outputBufferSize);
+ output = new ZSTD_outBuffer_s { pos = 0, size = (nuint)outputBufferSize };
+ }
+
+ public void SetParameter(ZSTD_cParameter parameter, int value)
+ {
+ EnsureNotDisposed();
+ compressor.NotNull().SetParameter(parameter, value);
+ }
+
+ public int GetParameter(ZSTD_cParameter parameter)
+ {
+ EnsureNotDisposed();
+ return compressor.NotNull().GetParameter(parameter);
+ }
+
+ public void LoadDictionary(byte[] dict)
+ {
+ EnsureNotDisposed();
+ compressor.NotNull().LoadDictionary(dict);
+ }
+
+ ~CompressionStream() => Dispose(false);
+
+#if !NETSTANDARD2_0 && !NETFRAMEWORK
+ public override async ValueTask DisposeAsync()
+#else
+ public async Task DisposeAsync()
+#endif
+ {
+ if (compressor == null)
+ return;
+
+ try
+ {
+ await FlushInternalAsync(ZSTD_EndDirective.ZSTD_e_end).ConfigureAwait(false);
+ }
+ finally
+ {
+ ReleaseUnmanagedResources();
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (compressor == null)
+ return;
+
+ try
+ {
+ if (disposing)
+ FlushInternal(ZSTD_EndDirective.ZSTD_e_end);
+ }
+ finally
+ {
+ ReleaseUnmanagedResources();
+ }
+ }
+
+ private void ReleaseUnmanagedResources()
+ {
+ if (!preserveCompressor)
+ {
+ compressor.NotNull().Dispose();
+ }
+ compressor = null;
+
+ if (outputBuffer != null)
+ {
+ ArrayPool.Shared.Return(outputBuffer);
+ }
+
+ if (!leaveOpen)
+ {
+ innerStream.Dispose();
+ }
+ }
+
+ public override void Flush() => FlushInternal(ZSTD_EndDirective.ZSTD_e_flush);
+
+ public override async Task FlushAsync(CancellationToken cancellationToken) =>
+ await FlushInternalAsync(ZSTD_EndDirective.ZSTD_e_flush, cancellationToken)
+ .ConfigureAwait(false);
+
+ private void FlushInternal(ZSTD_EndDirective directive) => WriteInternal(null, directive);
+
+ private async Task FlushInternalAsync(
+ ZSTD_EndDirective directive,
+ CancellationToken cancellationToken = default
+ ) => await WriteInternalAsync(null, directive, cancellationToken).ConfigureAwait(false);
+
+ public override void Write(byte[] buffer, int offset, int count) =>
+ Write(new ReadOnlySpan(buffer, offset, count));
+
+#if !NETSTANDARD2_0 && !NETFRAMEWORK
+ public override void Write(ReadOnlySpan buffer) =>
+ WriteInternal(buffer, ZSTD_EndDirective.ZSTD_e_continue);
+#else
+ public void Write(ReadOnlySpan buffer) =>
+ WriteInternal(buffer, ZSTD_EndDirective.ZSTD_e_continue);
+#endif
+
+ private void WriteInternal(ReadOnlySpan buffer, ZSTD_EndDirective directive)
+ {
+ EnsureNotDisposed();
+
+ var input = new ZSTD_inBuffer_s
+ {
+ pos = 0,
+ size = buffer != null ? (nuint)buffer.Length : 0,
+ };
+ nuint remaining;
+ do
+ {
+ output.pos = 0;
+ remaining = CompressStream(ref input, buffer, directive);
+
+ var written = (int)output.pos;
+ if (written > 0)
+ innerStream.Write(outputBuffer, 0, written);
+ } while (
+ directive == ZSTD_EndDirective.ZSTD_e_continue ? input.pos < input.size : remaining > 0
+ );
+ }
+
+#if !NETSTANDARD2_0 && !NETFRAMEWORK
+ private async ValueTask WriteInternalAsync(
+ ReadOnlyMemory? buffer,
+ ZSTD_EndDirective directive,
+ CancellationToken cancellationToken = default
+ )
+#else
+ private async Task WriteInternalAsync(
+ ReadOnlyMemory? buffer,
+ ZSTD_EndDirective directive,
+ CancellationToken cancellationToken = default
+ )
+#endif
+
+ {
+ EnsureNotDisposed();
+
+ var input = new ZSTD_inBuffer_s
+ {
+ pos = 0,
+ size = buffer.HasValue ? (nuint)buffer.Value.Length : 0,
+ };
+ nuint remaining;
+ do
+ {
+ output.pos = 0;
+ remaining = CompressStream(
+ ref input,
+ buffer.HasValue ? buffer.Value.Span : null,
+ directive
+ );
+
+ var written = (int)output.pos;
+ if (written > 0)
+ await innerStream
+ .WriteAsync(outputBuffer, 0, written, cancellationToken)
+ .ConfigureAwait(false);
+ } while (
+ directive == ZSTD_EndDirective.ZSTD_e_continue ? input.pos < input.size : remaining > 0
+ );
+ }
+
+#if !NETSTANDARD2_0 && !NETFRAMEWORK
+
+ public override Task WriteAsync(
+ byte[] buffer,
+ int offset,
+ int count,
+ CancellationToken cancellationToken
+ ) => WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask();
+
+ public override async ValueTask WriteAsync(
+ ReadOnlyMemory buffer,
+ CancellationToken cancellationToken = default
+ ) =>
+ await WriteInternalAsync(buffer, ZSTD_EndDirective.ZSTD_e_continue, cancellationToken)
+ .ConfigureAwait(false);
+#else
+
+ public override Task WriteAsync(
+ byte[] buffer,
+ int offset,
+ int count,
+ CancellationToken cancellationToken
+ ) => WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken);
+
+ public async Task WriteAsync(
+ ReadOnlyMemory buffer,
+ CancellationToken cancellationToken = default
+ ) =>
+ await WriteInternalAsync(buffer, ZSTD_EndDirective.ZSTD_e_continue, cancellationToken)
+ .ConfigureAwait(false);
+#endif
+
+ internal unsafe nuint CompressStream(
+ ref ZSTD_inBuffer_s input,
+ ReadOnlySpan inputBuffer,
+ ZSTD_EndDirective directive
+ )
+ {
+ fixed (byte* inputBufferPtr = inputBuffer)
+ fixed (byte* outputBufferPtr = outputBuffer)
+ {
+ input.src = inputBufferPtr;
+ output.dst = outputBufferPtr;
+ return compressor
+ .NotNull()
+ .CompressStream(ref input, ref output, directive)
+ .EnsureZstdSuccess();
+ }
+ }
+
+ public override bool CanRead => false;
+ public override bool CanSeek => false;
+ public override bool CanWrite => true;
+
+ public override long Length => throw new NotSupportedException();
+
+ public override long Position
+ {
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
+ }
+
+ public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
+
+ public override void SetLength(long value) => throw new NotSupportedException();
+
+ public override int Read(byte[] buffer, int offset, int count) =>
+ throw new NotSupportedException();
+
+ private void EnsureNotDisposed()
+ {
+ if (compressor == null)
+ throw new ObjectDisposedException(nameof(CompressionStream));
+ }
+
+ public void SetPledgedSrcSize(ulong pledgedSrcSize)
+ {
+ EnsureNotDisposed();
+ compressor.NotNull().SetPledgedSrcSize(pledgedSrcSize);
+ }
+}
diff --git a/src/SharpCompress/Compressors/ZStandard/Compressor.cs b/src/SharpCompress/Compressors/ZStandard/Compressor.cs
new file mode 100644
index 00000000..66860601
--- /dev/null
+++ b/src/SharpCompress/Compressors/ZStandard/Compressor.cs
@@ -0,0 +1,204 @@
+using System;
+using SharpCompress.Compressors.ZStandard.Unsafe;
+
+namespace SharpCompress.Compressors.ZStandard;
+
+public unsafe class Compressor : IDisposable
+{
+ ///
+ /// Minimum negative compression level allowed
+ ///
+ public static int MinCompressionLevel => Unsafe.Methods.ZSTD_minCLevel();
+
+ ///
+ /// Maximum compression level available
+ ///
+ public static int MaxCompressionLevel => Unsafe.Methods.ZSTD_maxCLevel();
+
+ ///
+ /// Default compression level
+ ///
+ ///
+ public const int DefaultCompressionLevel = 3;
+
+ private int level = DefaultCompressionLevel;
+
+ private readonly SafeCctxHandle handle;
+
+ public int Level
+ {
+ get => level;
+ set
+ {
+ if (level != value)
+ {
+ level = value;
+ SetParameter(ZSTD_cParameter.ZSTD_c_compressionLevel, value);
+ }
+ }
+ }
+
+ public void SetParameter(ZSTD_cParameter parameter, int value)
+ {
+ using var cctx = handle.Acquire();
+ Unsafe.Methods.ZSTD_CCtx_setParameter(cctx, parameter, value).EnsureZstdSuccess();
+ }
+
+ public int GetParameter(ZSTD_cParameter parameter)
+ {
+ using var cctx = handle.Acquire();
+ int value;
+ Unsafe.Methods.ZSTD_CCtx_getParameter(cctx, parameter, &value).EnsureZstdSuccess();
+ return value;
+ }
+
+ public void LoadDictionary(byte[] dict)
+ {
+ var dictReadOnlySpan = new ReadOnlySpan(dict);
+ LoadDictionary(dictReadOnlySpan);
+ }
+
+ public void LoadDictionary(ReadOnlySpan dict)
+ {
+ using var cctx = handle.Acquire();
+ fixed (byte* dictPtr = dict)
+ Unsafe
+ .Methods.ZSTD_CCtx_loadDictionary(cctx, dictPtr, (nuint)dict.Length)
+ .EnsureZstdSuccess();
+ }
+
+ public Compressor(int level = DefaultCompressionLevel)
+ {
+ handle = SafeCctxHandle.Create();
+ Level = level;
+ }
+
+ public static int GetCompressBound(int length) =>
+ (int)Unsafe.Methods.ZSTD_compressBound((nuint)length);
+
+ public static ulong GetCompressBoundLong(ulong length) =>
+ Unsafe.Methods.ZSTD_compressBound((nuint)length);
+
+ public Span Wrap(ReadOnlySpan src)
+ {
+ var dest = new byte[GetCompressBound(src.Length)];
+ var length = Wrap(src, dest);
+ return new Span(dest, 0, length);
+ }
+
+ public int Wrap(byte[] src, byte[] dest, int offset) =>
+ Wrap(src, new Span(dest, offset, dest.Length - offset));
+
+ public int Wrap(ReadOnlySpan src, Span dest)
+ {
+ fixed (byte* srcPtr = src)
+ fixed (byte* destPtr = dest)
+ {
+ using var cctx = handle.Acquire();
+ return (int)
+ Unsafe
+ .Methods.ZSTD_compress2(
+ cctx,
+ destPtr,
+ (nuint)dest.Length,
+ srcPtr,
+ (nuint)src.Length
+ )
+ .EnsureZstdSuccess();
+ }
+ }
+
+ public int Wrap(ArraySegment src, ArraySegment dest) =>
+ Wrap((ReadOnlySpan)src, dest);
+
+ public int Wrap(
+ byte[] src,
+ int srcOffset,
+ int srcLength,
+ byte[] dst,
+ int dstOffset,
+ int dstLength
+ ) =>
+ Wrap(
+ new ReadOnlySpan(src, srcOffset, srcLength),
+ new Span(dst, dstOffset, dstLength)
+ );
+
+ public bool TryWrap(byte[] src, byte[] dest, int offset, out int written) =>
+ TryWrap(src, new Span(dest, offset, dest.Length - offset), out written);
+
+ public bool TryWrap(ReadOnlySpan src, Span dest, out int written)
+ {
+ fixed (byte* srcPtr = src)
+ fixed (byte* destPtr = dest)
+ {
+ nuint returnValue;
+ using (var cctx = handle.Acquire())
+ {
+ returnValue = Unsafe.Methods.ZSTD_compress2(
+ cctx,
+ destPtr,
+ (nuint)dest.Length,
+ srcPtr,
+ (nuint)src.Length
+ );
+ }
+
+ if (returnValue == unchecked(0 - (nuint)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall))
+ {
+ written = default;
+ return false;
+ }
+
+ returnValue.EnsureZstdSuccess();
+ written = (int)returnValue;
+ return true;
+ }
+ }
+
+ public bool TryWrap(ArraySegment src, ArraySegment dest, out int written) =>
+ TryWrap((ReadOnlySpan)src, dest, out written);
+
+ public bool TryWrap(
+ byte[] src,
+ int srcOffset,
+ int srcLength,
+ byte[] dst,
+ int dstOffset,
+ int dstLength,
+ out int written
+ ) =>
+ TryWrap(
+ new ReadOnlySpan(src, srcOffset, srcLength),
+ new Span(dst, dstOffset, dstLength),
+ out written
+ );
+
+ public void Dispose()
+ {
+ handle.Dispose();
+ GC.SuppressFinalize(this);
+ }
+
+ internal nuint CompressStream(
+ ref ZSTD_inBuffer_s input,
+ ref ZSTD_outBuffer_s output,
+ ZSTD_EndDirective directive
+ )
+ {
+ fixed (ZSTD_inBuffer_s* inputPtr = &input)
+ fixed (ZSTD_outBuffer_s* outputPtr = &output)
+ {
+ using var cctx = handle.Acquire();
+ return Unsafe
+ .Methods.ZSTD_compressStream2(cctx, outputPtr, inputPtr, directive)
+ .EnsureZstdSuccess();
+ }
+ }
+
+ public void SetPledgedSrcSize(ulong pledgedSrcSize)
+ {
+ using var cctx = handle.Acquire();
+ Unsafe.Methods.ZSTD_CCtx_setPledgedSrcSize(cctx, pledgedSrcSize).EnsureZstdSuccess();
+ }
+}
diff --git a/src/SharpCompress/Compressors/ZStandard/Constants.cs b/src/SharpCompress/Compressors/ZStandard/Constants.cs
new file mode 100644
index 00000000..cce84fc0
--- /dev/null
+++ b/src/SharpCompress/Compressors/ZStandard/Constants.cs
@@ -0,0 +1,8 @@
+namespace SharpCompress.Compressors.ZStandard;
+
+internal class Constants
+{
+ //NOTE: https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element#remarks
+ //NOTE: https://github.com/dotnet/runtime/blob/v5.0.0-rtm.20519.4/src/libraries/System.Private.CoreLib/src/System/Array.cs#L27
+ public const ulong MaxByteArrayLength = 0x7FFFFFC7;
+}
diff --git a/src/SharpCompress/Compressors/ZStandard/DecompressionStream.cs b/src/SharpCompress/Compressors/ZStandard/DecompressionStream.cs
new file mode 100644
index 00000000..9864a805
--- /dev/null
+++ b/src/SharpCompress/Compressors/ZStandard/DecompressionStream.cs
@@ -0,0 +1,293 @@
+using System;
+using System.Buffers;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using SharpCompress.Compressors.ZStandard.Unsafe;
+
+namespace SharpCompress.Compressors.ZStandard;
+
+public class DecompressionStream : Stream
+{
+ private readonly Stream innerStream;
+ private readonly byte[] inputBuffer;
+ private readonly int inputBufferSize;
+ private readonly bool preserveDecompressor;
+ private readonly bool leaveOpen;
+ private readonly bool checkEndOfStream;
+ private Decompressor? decompressor;
+ private ZSTD_inBuffer_s input;
+ private nuint lastDecompressResult = 0;
+ private bool contextDrained = true;
+
+ public DecompressionStream(
+ Stream stream,
+ int bufferSize = 0,
+ bool checkEndOfStream = true,
+ bool leaveOpen = true
+ )
+ : this(stream, new Decompressor(), bufferSize, checkEndOfStream, false, leaveOpen) { }
+
+ public DecompressionStream(
+ Stream stream,
+ Decompressor decompressor,
+ int bufferSize = 0,
+ bool checkEndOfStream = true,
+ bool preserveDecompressor = true,
+ bool leaveOpen = true
+ )
+ {
+ if (stream == null)
+ throw new ArgumentNullException(nameof(stream));
+
+ if (!stream.CanRead)
+ throw new ArgumentException("Stream is not readable", nameof(stream));
+
+ if (bufferSize < 0)
+ throw new ArgumentOutOfRangeException(nameof(bufferSize));
+
+ innerStream = stream;
+ this.decompressor = decompressor;
+ this.preserveDecompressor = preserveDecompressor;
+ this.leaveOpen = leaveOpen;
+ this.checkEndOfStream = checkEndOfStream;
+
+ inputBufferSize =
+ bufferSize > 0
+ ? bufferSize
+ : (int)Unsafe.Methods.ZSTD_DStreamInSize().EnsureZstdSuccess();
+ inputBuffer = ArrayPool.Shared.Rent(inputBufferSize);
+ input = new ZSTD_inBuffer_s { pos = (nuint)inputBufferSize, size = (nuint)inputBufferSize };
+ }
+
+ public void SetParameter(ZSTD_dParameter parameter, int value)
+ {
+ EnsureNotDisposed();
+ decompressor.NotNull().SetParameter(parameter, value);
+ }
+
+ public int GetParameter(ZSTD_dParameter parameter)
+ {
+ EnsureNotDisposed();
+ return decompressor.NotNull().GetParameter(parameter);
+ }
+
+ public void LoadDictionary(byte[] dict)
+ {
+ EnsureNotDisposed();
+ decompressor.NotNull().LoadDictionary(dict);
+ }
+
+ ~DecompressionStream() => Dispose(false);
+
+ protected override void Dispose(bool disposing)
+ {
+ if (decompressor == null)
+ return;
+
+ if (!preserveDecompressor)
+ {
+ decompressor.Dispose();
+ }
+ decompressor = null;
+
+ if (inputBuffer != null)
+ {
+ ArrayPool.Shared.Return(inputBuffer);
+ }
+
+ if (!leaveOpen)
+ {
+ innerStream.Dispose();
+ }
+ }
+
+ public override int Read(byte[] buffer, int offset, int count) =>
+ Read(new Span(buffer, offset, count));
+
+#if !NETSTANDARD2_0 && !NETFRAMEWORK
+ public override int Read(Span buffer)
+#else
+ public int Read(Span buffer)
+#endif
+ {
+ EnsureNotDisposed();
+
+ // Guard against infinite loop (output.pos would never become non-zero)
+ if (buffer.Length == 0)
+ {
+ return 0;
+ }
+
+ var output = new ZSTD_outBuffer_s { pos = 0, size = (nuint)buffer.Length };
+ while (true)
+ {
+ // If there is still input available, or there might be data buffered in the decompressor context, flush that out
+ while (input.pos < input.size || !contextDrained)
+ {
+ nuint oldInputPos = input.pos;
+ nuint result = DecompressStream(ref output, buffer);
+ if (output.pos > 0 || oldInputPos != input.pos)
+ {
+ // Keep result from last decompress call that made some progress, so we known if we're at end of frame
+ lastDecompressResult = result;
+ }
+ // If decompression filled the output buffer, there might still be data buffered in the decompressor context
+ contextDrained = output.pos < output.size;
+ // If we have data to return, return it immediately, so we won't stall on Read
+ if (output.pos > 0)
+ {
+ return (int)output.pos;
+ }
+ }
+
+ // Otherwise, read some more input
+ int bytesRead;
+ if ((bytesRead = innerStream.Read(inputBuffer, 0, inputBufferSize)) == 0)
+ {
+ if (checkEndOfStream && lastDecompressResult != 0)
+ {
+ throw new EndOfStreamException("Premature end of stream");
+ }
+
+ return 0;
+ }
+
+ input.size = (nuint)bytesRead;
+ input.pos = 0;
+ }
+ }
+
+#if !NETSTANDARD2_0 && !NETFRAMEWORK
+ public override Task ReadAsync(
+ byte[] buffer,
+ int offset,
+ int count,
+ CancellationToken cancellationToken
+ ) => ReadAsync(new Memory(buffer, offset, count), cancellationToken).AsTask();
+
+ public override async ValueTask ReadAsync(
+ Memory buffer,
+ CancellationToken cancellationToken = default
+ )
+#else
+
+ public override Task ReadAsync(
+ byte[] buffer,
+ int offset,
+ int count,
+ CancellationToken cancellationToken
+ ) => ReadAsync(new Memory(buffer, offset, count), cancellationToken);
+
+ public async Task ReadAsync(
+ Memory buffer,
+ CancellationToken cancellationToken = default
+ )
+#endif
+ {
+ EnsureNotDisposed();
+
+ // Guard against infinite loop (output.pos would never become non-zero)
+ if (buffer.Length == 0)
+ {
+ return 0;
+ }
+
+ var output = new ZSTD_outBuffer_s { pos = 0, size = (nuint)buffer.Length };
+ while (true)
+ {
+ // If there is still input available, or there might be data buffered in the decompressor context, flush that out
+ while (input.pos < input.size || !contextDrained)
+ {
+ nuint oldInputPos = input.pos;
+ nuint result = DecompressStream(ref output, buffer.Span);
+ if (output.pos > 0 || oldInputPos != input.pos)
+ {
+ // Keep result from last decompress call that made some progress, so we known if we're at end of frame
+ lastDecompressResult = result;
+ }
+ // If decompression filled the output buffer, there might still be data buffered in the decompressor context
+ contextDrained = output.pos < output.size;
+ // If we have data to return, return it immediately, so we won't stall on Read
+ if (output.pos > 0)
+ {
+ return (int)output.pos;
+ }
+ }
+
+ // Otherwise, read some more input
+ int bytesRead;
+ if (
+ (
+ bytesRead = await innerStream
+ .ReadAsync(inputBuffer, 0, inputBufferSize, cancellationToken)
+ .ConfigureAwait(false)
+ ) == 0
+ )
+ {
+ if (checkEndOfStream && lastDecompressResult != 0)
+ {
+ throw new EndOfStreamException("Premature end of stream");
+ }
+
+ return 0;
+ }
+
+ input.size = (nuint)bytesRead;
+ input.pos = 0;
+ }
+ }
+
+ private unsafe nuint DecompressStream(ref ZSTD_outBuffer_s output, Span outputBuffer)
+ {
+ fixed (byte* inputBufferPtr = inputBuffer)
+ fixed (byte* outputBufferPtr = outputBuffer)
+ {
+ input.src = inputBufferPtr;
+ output.dst = outputBufferPtr;
+ return decompressor.NotNull().DecompressStream(ref input, ref output);
+ }
+ }
+
+ public override bool CanRead => true;
+ public override bool CanSeek => false;
+ public override bool CanWrite => false;
+
+ public override long Length => throw new NotSupportedException();
+
+ public override long Position
+ {
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
+ }
+
+ public override void Flush() => throw new NotSupportedException();
+
+ public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
+
+ public override void SetLength(long value) => throw new NotSupportedException();
+
+ public override void Write(byte[] buffer, int offset, int count) =>
+ throw new NotSupportedException();
+
+ private void EnsureNotDisposed()
+ {
+ if (decompressor == null)
+ throw new ObjectDisposedException(nameof(DecompressionStream));
+ }
+
+#if NETSTANDARD2_0 || NETFRAMEWORK
+ public virtual Task DisposeAsync()
+ {
+ try
+ {
+ Dispose();
+ return Task.CompletedTask;
+ }
+ catch (Exception exc)
+ {
+ return Task.FromException(exc);
+ }
+ }
+#endif
+}
diff --git a/src/SharpCompress/Compressors/ZStandard/Decompressor.cs b/src/SharpCompress/Compressors/ZStandard/Decompressor.cs
new file mode 100644
index 00000000..8a63c4d9
--- /dev/null
+++ b/src/SharpCompress/Compressors/ZStandard/Decompressor.cs
@@ -0,0 +1,176 @@
+using System;
+using SharpCompress.Compressors.ZStandard.Unsafe;
+
+namespace SharpCompress.Compressors.ZStandard;
+
+public unsafe class Decompressor : IDisposable
+{
+ private readonly SafeDctxHandle handle;
+
+ public Decompressor()
+ {
+ handle = SafeDctxHandle.Create();
+ }
+
+ public void SetParameter(ZSTD_dParameter parameter, int value)
+ {
+ using var dctx = handle.Acquire();
+ Unsafe.Methods.ZSTD_DCtx_setParameter(dctx, parameter, value).EnsureZstdSuccess();
+ }
+
+ public int GetParameter(ZSTD_dParameter parameter)
+ {
+ using var dctx = handle.Acquire();
+ int value;
+ Unsafe.Methods.ZSTD_DCtx_getParameter(dctx, parameter, &value).EnsureZstdSuccess();
+ return value;
+ }
+
+ public void LoadDictionary(byte[] dict)
+ {
+ var dictReadOnlySpan = new ReadOnlySpan(dict);
+ this.LoadDictionary(dictReadOnlySpan);
+ }
+
+ public void LoadDictionary(ReadOnlySpan dict)
+ {
+ using var dctx = handle.Acquire();
+ fixed (byte* dictPtr = dict)
+ Unsafe
+ .Methods.ZSTD_DCtx_loadDictionary(dctx, dictPtr, (nuint)dict.Length)
+ .EnsureZstdSuccess();
+ }
+
+ public static ulong GetDecompressedSize(ReadOnlySpan src)
+ {
+ fixed (byte* srcPtr = src)
+ return Unsafe
+ .Methods.ZSTD_decompressBound(srcPtr, (nuint)src.Length)
+ .EnsureContentSizeOk();
+ }
+
+ public static ulong GetDecompressedSize(ArraySegment src) =>
+ GetDecompressedSize((ReadOnlySpan)src);
+
+ public static ulong GetDecompressedSize(byte[] src, int srcOffset, int srcLength) =>
+ GetDecompressedSize(new ReadOnlySpan(src, srcOffset, srcLength));
+
+ public Span Unwrap(ReadOnlySpan src, int maxDecompressedSize = int.MaxValue)
+ {
+ var expectedDstSize = GetDecompressedSize(src);
+ if (expectedDstSize > (ulong)maxDecompressedSize)
+ throw new ZstdException(
+ ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall,
+ $"Decompressed content size {expectedDstSize} is greater than {nameof(maxDecompressedSize)} {maxDecompressedSize}"
+ );
+ if (expectedDstSize > Constants.MaxByteArrayLength)
+ throw new ZstdException(
+ ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall,
+ $"Decompressed content size {expectedDstSize} is greater than max possible byte array size {Constants.MaxByteArrayLength}"
+ );
+
+ var dest = new byte[expectedDstSize];
+ var length = Unwrap(src, dest);
+ return new Span(dest, 0, length);
+ }
+
+ public int Unwrap(byte[] src, byte[] dest, int offset) =>
+ Unwrap(src, new Span(dest, offset, dest.Length - offset));
+
+ public int Unwrap(ReadOnlySpan src, Span dest)
+ {
+ fixed (byte* srcPtr = src)
+ fixed (byte* destPtr = dest)
+ {
+ using var dctx = handle.Acquire();
+ return (int)
+ Unsafe
+ .Methods.ZSTD_decompressDCtx(
+ dctx,
+ destPtr,
+ (nuint)dest.Length,
+ srcPtr,
+ (nuint)src.Length
+ )
+ .EnsureZstdSuccess();
+ }
+ }
+
+ public int Unwrap(
+ byte[] src,
+ int srcOffset,
+ int srcLength,
+ byte[] dst,
+ int dstOffset,
+ int dstLength
+ ) =>
+ Unwrap(
+ new ReadOnlySpan(src, srcOffset, srcLength),
+ new Span(dst, dstOffset, dstLength)
+ );
+
+ public bool TryUnwrap(byte[] src, byte[] dest, int offset, out int written) =>
+ TryUnwrap(src, new Span(dest, offset, dest.Length - offset), out written);
+
+ public bool TryUnwrap(ReadOnlySpan src, Span dest, out int written)
+ {
+ fixed (byte* srcPtr = src)
+ fixed (byte* destPtr = dest)
+ {
+ nuint returnValue;
+ using (var dctx = handle.Acquire())
+ {
+ returnValue = Unsafe.Methods.ZSTD_decompressDCtx(
+ dctx,
+ destPtr,
+ (nuint)dest.Length,
+ srcPtr,
+ (nuint)src.Length
+ );
+ }
+
+ if (returnValue == unchecked(0 - (nuint)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall))
+ {
+ written = default;
+ return false;
+ }
+
+ returnValue.EnsureZstdSuccess();
+ written = (int)returnValue;
+ return true;
+ }
+ }
+
+ public bool TryUnwrap(
+ byte[] src,
+ int srcOffset,
+ int srcLength,
+ byte[] dst,
+ int dstOffset,
+ int dstLength,
+ out int written
+ ) =>
+ TryUnwrap(
+ new ReadOnlySpan(src, srcOffset, srcLength),
+ new Span(dst, dstOffset, dstLength),
+ out written
+ );
+
+ public void Dispose()
+ {
+ handle.Dispose();
+ GC.SuppressFinalize(this);
+ }
+
+ internal nuint DecompressStream(ref ZSTD_inBuffer_s input, ref ZSTD_outBuffer_s output)
+ {
+ fixed (ZSTD_inBuffer_s* inputPtr = &input)
+ fixed (ZSTD_outBuffer_s* outputPtr = &output)
+ {
+ using var dctx = handle.Acquire();
+ return Unsafe
+ .Methods.ZSTD_decompressStream(dctx, outputPtr, inputPtr)
+ .EnsureZstdSuccess();
+ }
+ }
+}
diff --git a/src/SharpCompress/Compressors/ZStandard/JobThreadPool.cs b/src/SharpCompress/Compressors/ZStandard/JobThreadPool.cs
new file mode 100644
index 00000000..e783940d
--- /dev/null
+++ b/src/SharpCompress/Compressors/ZStandard/JobThreadPool.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace SharpCompress.Compressors.ZStandard;
+
+internal unsafe class JobThreadPool : IDisposable
+{
+ private int numThreads;
+ private readonly List threads;
+ private readonly BlockingCollection queue;
+
+ private struct Job
+ {
+ public void* function;
+ public void* opaque;
+ }
+
+ private class JobThread
+ {
+ private Thread Thread { get; }
+ public CancellationTokenSource CancellationTokenSource { get; }
+
+ public JobThread(Thread thread)
+ {
+ CancellationTokenSource = new CancellationTokenSource();
+ Thread = thread;
+ }
+
+ public void Start()
+ {
+ Thread.Start(this);
+ }
+
+ public void Cancel()
+ {
+ CancellationTokenSource.Cancel();
+ }
+
+ public void Join()
+ {
+ Thread.Join();
+ }
+ }
+
+ private void Worker(object? obj)
+ {
+ if (obj is not JobThread poolThread)
+ return;
+
+ var cancellationToken = poolThread.CancellationTokenSource.Token;
+ while (!queue.IsCompleted && !cancellationToken.IsCancellationRequested)
+ {
+ try
+ {
+ if (queue.TryTake(out var job, -1, cancellationToken))
+ ((delegate* managed)job.function)(job.opaque);
+ }
+ catch (InvalidOperationException) { }
+ catch (OperationCanceledException) { }
+ }
+ }
+
+ public JobThreadPool(int num, int queueSize)
+ {
+ numThreads = num;
+ queue = new BlockingCollection(queueSize + 1);
+ threads = new List(num);
+ for (var i = 0; i < numThreads; i++)
+ CreateThread();
+ }
+
+ private void CreateThread()
+ {
+ var poolThread = new JobThread(new Thread(Worker));
+ threads.Add(poolThread);
+ poolThread.Start();
+ }
+
+ public void Resize(int num)
+ {
+ lock (threads)
+ {
+ if (num < numThreads)
+ {
+ for (var i = numThreads - 1; i >= num; i--)
+ {
+ threads[i].Cancel();
+ threads.RemoveAt(i);
+ }
+ }
+ else
+ {
+ for (var i = numThreads; i < num; i++)
+ CreateThread();
+ }
+ }
+
+ numThreads = num;
+ }
+
+ public void Add(void* function, void* opaque)
+ {
+ queue.Add(new Job { function = function, opaque = opaque });
+ }
+
+ public bool TryAdd(void* function, void* opaque)
+ {
+ return queue.TryAdd(new Job { function = function, opaque = opaque });
+ }
+
+ public void Join(bool cancel = true)
+ {
+ queue.CompleteAdding();
+ List jobThreads;
+ lock (threads)
+ jobThreads = new List(threads);
+
+ if (cancel)
+ {
+ foreach (var thread in jobThreads)
+ thread.Cancel();
+ }
+
+ foreach (var thread in jobThreads)
+ thread.Join();
+ }
+
+ public void Dispose()
+ {
+ queue.Dispose();
+ }
+
+ public int Size()
+ {
+ // todo not implemented
+ // https://github.com/dotnet/runtime/issues/24200
+ return 0;
+ }
+}
diff --git a/src/SharpCompress/Compressors/ZStandard/SafeHandles.cs b/src/SharpCompress/Compressors/ZStandard/SafeHandles.cs
new file mode 100644
index 00000000..3b49bdce
--- /dev/null
+++ b/src/SharpCompress/Compressors/ZStandard/SafeHandles.cs
@@ -0,0 +1,163 @@
+using System;
+using System.Runtime.InteropServices;
+using SharpCompress.Compressors.ZStandard.Unsafe;
+
+namespace SharpCompress.Compressors.ZStandard;
+
+///
+/// Provides the base class for ZstdSharp implementations.
+///
+///
+/// Even though ZstdSharp is a managed library, its internals are using unmanaged
+/// memory and we are using safe handles in the library's high-level API to ensure
+/// proper disposal of unmanaged resources and increase safety.
+///
+///
+///
+internal abstract unsafe class SafeZstdHandle : SafeHandle
+{
+ ///
+ /// Parameterless constructor is hidden. Use the static Create factory
+ /// method to create a new safe handle instance.
+ ///
+ protected SafeZstdHandle()
+ : base(IntPtr.Zero, true) { }
+
+ public sealed override bool IsInvalid => handle == IntPtr.Zero;
+}
+
+///
+/// Safely wraps an unmanaged Zstd compression context.
+///
+internal sealed unsafe class SafeCctxHandle : SafeZstdHandle
+{
+ ///
+ private SafeCctxHandle() { }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ ///
+ /// Creation failed.
+ public static SafeCctxHandle Create()
+ {
+ var safeHandle = new SafeCctxHandle();
+ bool success = false;
+ try
+ {
+ var cctx = Unsafe.Methods.ZSTD_createCCtx();
+ if (cctx == null)
+ throw new ZstdException(ZSTD_ErrorCode.ZSTD_error_GENERIC, "Failed to create cctx");
+ safeHandle.SetHandle((IntPtr)cctx);
+ success = true;
+ }
+ finally
+ {
+ if (!success)
+ {
+ safeHandle.SetHandleAsInvalid();
+ }
+ }
+ return safeHandle;
+ }
+
+ ///
+ /// Acquires a reference to the safe handle.
+ ///
+ ///
+ /// A instance that can be implicitly converted to a pointer
+ /// to .
+ ///
+ public SafeHandleHolder Acquire() => new(this);
+
+ protected override bool ReleaseHandle()
+ {
+ return Unsafe.Methods.ZSTD_freeCCtx((ZSTD_CCtx_s*)handle) == 0;
+ }
+}
+
+///
+/// Safely wraps an unmanaged Zstd compression context.
+///
+internal sealed unsafe class SafeDctxHandle : SafeZstdHandle
+{
+ ///
+ private SafeDctxHandle() { }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ ///
+ /// Creation failed.
+ public static SafeDctxHandle Create()
+ {
+ var safeHandle = new SafeDctxHandle();
+ bool success = false;
+ try
+ {
+ var dctx = Unsafe.Methods.ZSTD_createDCtx();
+ if (dctx == null)
+ throw new ZstdException(ZSTD_ErrorCode.ZSTD_error_GENERIC, "Failed to create dctx");
+ safeHandle.SetHandle((IntPtr)dctx);
+ success = true;
+ }
+ finally
+ {
+ if (!success)
+ {
+ safeHandle.SetHandleAsInvalid();
+ }
+ }
+ return safeHandle;
+ }
+
+ ///
+ /// Acquires a reference to the safe handle.
+ ///
+ ///
+ /// A instance that can be implicitly converted to a pointer
+ /// to .
+ ///
+ public SafeHandleHolder Acquire() => new(this);
+
+ protected override bool ReleaseHandle()
+ {
+ return Unsafe.Methods.ZSTD_freeDCtx((ZSTD_DCtx_s*)handle) == 0;
+ }
+}
+
+///
+/// Provides a convenient interface to safely acquire pointers of a specific type
+/// from a , by utilizing blocks.
+///
+/// The type of pointers to return.
+///
+/// Safe handle holders can be d to decrement the safe handle's
+/// reference count, and can be implicitly converted to pointers to .
+///
+internal unsafe ref struct SafeHandleHolder
+ where T : unmanaged
+{
+ private readonly SafeHandle _handle;
+
+ private bool _refAdded;
+
+ public SafeHandleHolder(SafeHandle safeHandle)
+ {
+ _handle = safeHandle;
+ _refAdded = false;
+ safeHandle.DangerousAddRef(ref _refAdded);
+ }
+
+ public static implicit operator T*(SafeHandleHolder holder) =>
+ (T*)holder._handle.DangerousGetHandle();
+
+ public void Dispose()
+ {
+ if (_refAdded)
+ {
+ _handle.DangerousRelease();
+ _refAdded = false;
+ }
+ }
+}
diff --git a/src/SharpCompress/Compressors/ZStandard/SynchronizationWrapper.cs b/src/SharpCompress/Compressors/ZStandard/SynchronizationWrapper.cs
new file mode 100644
index 00000000..406cacd4
--- /dev/null
+++ b/src/SharpCompress/Compressors/ZStandard/SynchronizationWrapper.cs
@@ -0,0 +1,22 @@
+using System.Threading;
+
+namespace SharpCompress.Compressors.ZStandard;
+
+internal static unsafe class SynchronizationWrapper
+{
+ private static object UnwrapObject(void** obj) => UnmanagedObject.Unwrap