From aaa97e2ce26ba230b464d17aec52579bb4fca791 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 11:36:44 +0000 Subject: [PATCH] Merge master branch - add ZStandard compression support and TarHeaderWriteFormat Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- .config/dotnet-tools.json | 2 +- .github/workflows/dotnetcore.yml | 2 +- src/SharpCompress/AssemblyInfo.cs | 3 +- .../Common/Tar/Headers/TarHeader.cs | 133 +- .../Tar/Headers/TarHeaderWriteFormat.cs | 7 + 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 | 48 + .../Compressors/ZStandard/UnmanagedObject.cs | 18 + .../ZStandard/Unsafe/Allocations.cs | 52 + .../ZStandard/Unsafe/BIT_CStream_t.cs | 14 + .../ZStandard/Unsafe/BIT_DStream_status.cs | 16 + .../ZStandard/Unsafe/BIT_DStream_t.cs | 13 + .../Compressors/ZStandard/Unsafe/Bits.cs | 60 + .../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/Compiler.cs | 61 + .../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/ErrorPrivate.cs | 110 + .../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/Mem.cs | 162 + .../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 | 21 + .../ZStandard/Unsafe/ZSTD_ErrorCode.cs | 59 + .../ZStandard/Unsafe/ZSTD_MatchState_t.cs | 66 + .../ZStandard/Unsafe/ZSTD_OffsetInfo.cs | 7 + .../ZStandard/Unsafe/ZSTD_OptPrice_e.cs | 7 + .../ZStandard/Unsafe/ZSTD_ResetDirective.cs | 8 + .../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_bounds.cs | 8 + .../ZStandard/Unsafe/ZSTD_bufferMode_e.cs | 11 + .../Unsafe/ZSTD_buffered_policy_e.cs | 12 + .../ZStandard/Unsafe/ZSTD_cParameter.cs | 218 + .../ZStandard/Unsafe/ZSTD_cStreamStage.cs | 8 + .../Unsafe/ZSTD_compResetPolicy_e.cs | 14 + .../Unsafe/ZSTD_compressedBlockState_t.cs | 7 + .../Unsafe/ZSTD_compressionParameters.cs | 44 + .../Unsafe/ZSTD_compressionStage_e.cs | 12 + .../ZStandard/Unsafe/ZSTD_customMem.cs | 15 + .../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_dParameter.cs | 38 + .../ZStandard/Unsafe/ZSTD_dStage.cs | 13 + .../ZStandard/Unsafe/ZSTD_dStreamStage.cs | 10 + .../ZStandard/Unsafe/ZSTD_dictAttachPref_e.cs | 16 + .../Unsafe/ZSTD_dictContentType_e.cs | 13 + .../ZStandard/Unsafe/ZSTD_dictLoadMethod_e.cs | 10 + .../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 | 21 + .../ZStandard/Unsafe/ZSTD_frameParameters.cs | 13 + .../ZStandard/Unsafe/ZSTD_frameProgression.cs | 22 + .../ZStandard/Unsafe/ZSTD_frameSizeInfo.cs | 14 + .../ZStandard/Unsafe/ZSTD_frameType_e.cs | 7 + .../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 | 16 + .../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 | 13 + .../ZStandard/Unsafe/ZSTD_overlap_e.cs | 7 + .../ZStandard/Unsafe/ZSTD_paramSwitch_e.cs | 13 + .../ZStandard/Unsafe/ZSTD_parameters.cs | 7 + .../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 + .../ZStandard/Unsafe/ZSTD_strategy.cs | 15 + .../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 | 48 + .../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/UnsafeHelper.cs | 107 + .../Compressors/ZStandard/ZStandardStream.cs | 2 +- .../ZStandard/ZstandardConstants.cs | 6 - .../Compressors/ZStandard/ZstdException.cs | 12 + src/SharpCompress/SharpCompress.csproj | 1 - src/SharpCompress/Writers/Tar/TarWriter.cs | 4 +- .../Writers/Tar/TarWriterOptions.cs | 15 +- src/SharpCompress/Writers/Zip/ZipWriter.cs | 3 +- src/SharpCompress/packages.lock.json | 23 - tests/SharpCompress.Test/packages.lock.json | 43 +- 245 files changed, 55930 insertions(+), 82 deletions(-) create mode 100644 src/SharpCompress/Common/Tar/Headers/TarHeaderWriteFormat.cs 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/ThrowHelper.cs create mode 100644 src/SharpCompress/Compressors/ZStandard/UnmanagedObject.cs create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/Allocations.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/Bits.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/Compiler.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/ErrorPrivate.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/Mem.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_EndDirective.cs create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_ErrorCode.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_ResetDirective.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_bounds.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_cParameter.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_compressionParameters.cs create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressionStage_e.cs create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_customMem.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_dParameter.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_dictContentType_e.cs create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictLoadMethod_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_frameHeader.cs create mode 100644 src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameParameters.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_frameType_e.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_inBuffer_s.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_outBuffer_s.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_parameters.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_strategy.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/ZstdCommon.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 create mode 100644 src/SharpCompress/Compressors/ZStandard/UnsafeHelper.cs create mode 100644 src/SharpCompress/Compressors/ZStandard/ZstdException.cs diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 03df2323..05325293 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "csharpier": { - "version": "1.2.1", + "version": "1.2.3", "commands": [ "csharpier" ], diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 3f6bef75..2ce8f4dd 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -19,7 +19,7 @@ jobs: with: dotnet-version: 10.0.x - run: dotnet run --project build/build.csproj - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v6 with: name: ${{ matrix.os }}-sharpcompress.nupkg path: artifacts/* diff --git a/src/SharpCompress/AssemblyInfo.cs b/src/SharpCompress/AssemblyInfo.cs index e51d97fe..c11eb8e0 100644 --- a/src/SharpCompress/AssemblyInfo.cs +++ b/src/SharpCompress/AssemblyInfo.cs @@ -1,7 +1,8 @@ using System; using System.Runtime.CompilerServices; -[assembly: CLSCompliant(true)] +// CLSCompliant(false) is required because ZStandard integration uses unsafe code +[assembly: CLSCompliant(false)] [assembly: InternalsVisibleTo( "SharpCompress.Test,PublicKey=0024000004800000940000000602000000240000525341310004000001000100158bebf1433f76dffc356733c138babea7a47536c65ed8009b16372c6f4edbb20554db74a62687f56b97c20a6ce8c4b123280279e33c894e7b3aa93ab3c573656fde4db576cfe07dba09619ead26375b25d2c4a8e43f7be257d712b0dd2eb546f67adb09281338618a58ac834fc038dd7e2740a7ab3591826252e4f4516306dc" )] diff --git a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs index 06a1f10a..e6b7c265 100644 --- a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs +++ b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs @@ -1,5 +1,6 @@ using System; using System.Buffers.Binary; +using System.Collections.Generic; using System.IO; using System.Text; @@ -9,8 +10,16 @@ internal sealed class TarHeader { internal static readonly DateTime EPOCH = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - public TarHeader(ArchiveEncoding archiveEncoding) => ArchiveEncoding = archiveEncoding; + public TarHeader( + ArchiveEncoding archiveEncoding, + TarHeaderWriteFormat writeFormat = TarHeaderWriteFormat.GNU_TAR_LONG_LINK + ) + { + ArchiveEncoding = archiveEncoding; + WriteFormat = writeFormat; + } + internal TarHeaderWriteFormat WriteFormat { get; set; } internal string? Name { get; set; } internal string? LinkName { get; set; } @@ -30,6 +39,114 @@ internal sealed class TarHeader private const int MAX_LONG_NAME_SIZE = 32768; internal void Write(Stream output) + { + switch (WriteFormat) + { + case TarHeaderWriteFormat.GNU_TAR_LONG_LINK: + WriteGnuTarLongLink(output); + break; + case TarHeaderWriteFormat.USTAR: + WriteUstar(output); + break; + default: + throw new Exception("This should be impossible..."); + } + } + + internal void WriteUstar(Stream output) + { + var buffer = new byte[BLOCK_SIZE]; + + WriteOctalBytes(511, buffer, 100, 8); // file mode + WriteOctalBytes(0, buffer, 108, 8); // owner ID + WriteOctalBytes(0, buffer, 116, 8); // group ID + + //ArchiveEncoding.UTF8.GetBytes("magic").CopyTo(buffer, 257); + var nameByteCount = ArchiveEncoding + .GetEncoding() + .GetByteCount(Name.NotNull("Name is null")); + + if (nameByteCount > 100) + { + // if name is longer, try to split it into name and namePrefix + + string fullName = Name.NotNull("Name is null"); + + // find all directory separators + List dirSeps = new List(); + for (int i = 0; i < fullName.Length; i++) + { + if (fullName[i] == Path.DirectorySeparatorChar) + { + dirSeps.Add(i); + } + } + + // find the right place to split the name + int splitIndex = -1; + for (int i = 0; i < dirSeps.Count; i++) + { + int count = ArchiveEncoding + .GetEncoding() + .GetByteCount(fullName.Substring(0, dirSeps[i])); + if (count < 155) + { + splitIndex = dirSeps[i]; + } + else + { + break; + } + } + + if (splitIndex == -1) + { + throw new Exception( + $"Tar header USTAR format can not fit file name \"{fullName}\" of length {nameByteCount}! Directory separator not found! Try using GNU Tar format instead!" + ); + } + + string namePrefix = fullName.Substring(0, splitIndex); + string name = fullName.Substring(splitIndex + 1); + + if (this.ArchiveEncoding.GetEncoding().GetByteCount(namePrefix) >= 155) + throw new Exception( + $"Tar header USTAR format can not fit file name \"{fullName}\" of length {nameByteCount}! Try using GNU Tar format instead!" + ); + + if (this.ArchiveEncoding.GetEncoding().GetByteCount(name) >= 100) + throw new Exception( + $"Tar header USTAR format can not fit file name \"{fullName}\" of length {nameByteCount}! Try using GNU Tar format instead!" + ); + + // write name prefix + WriteStringBytes(ArchiveEncoding.Encode(namePrefix), buffer, 345, 100); + // write partial name + WriteStringBytes(ArchiveEncoding.Encode(name), buffer, 100); + } + else + { + WriteStringBytes(ArchiveEncoding.Encode(Name.NotNull("Name is null")), buffer, 100); + } + + WriteOctalBytes(Size, buffer, 124, 12); + var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds; + WriteOctalBytes(time, buffer, 136, 12); + buffer[156] = (byte)EntryType; + + // write ustar magic field + WriteStringBytes(Encoding.ASCII.GetBytes("ustar"), buffer, 257, 6); + // write ustar version "00" + buffer[263] = 0x30; + buffer[264] = 0x30; + + var crc = RecalculateChecksum(buffer); + WriteOctalBytes(crc, buffer, 148, 8); + + output.Write(buffer, 0, buffer.Length); + } + + internal void WriteGnuTarLongLink(Stream output) { var buffer = new byte[BLOCK_SIZE]; @@ -85,7 +202,7 @@ internal sealed class TarHeader 0, 100 - ArchiveEncoding.GetEncoding().GetMaxByteCount(1) ); - Write(output); + WriteGnuTarLongLink(output); } } @@ -241,6 +358,18 @@ internal sealed class TarHeader buffer.Slice(i, length - i).Clear(); } + private static void WriteStringBytes( + ReadOnlySpan name, + Span buffer, + int offset, + int length + ) + { + name.CopyTo(buffer.Slice(offset)); + var i = Math.Min(length, name.Length); + buffer.Slice(offset + i, length - i).Clear(); + } + private static void WriteStringBytes(string name, byte[] buffer, int offset, int length) { int i; diff --git a/src/SharpCompress/Common/Tar/Headers/TarHeaderWriteFormat.cs b/src/SharpCompress/Common/Tar/Headers/TarHeaderWriteFormat.cs new file mode 100644 index 00000000..3a3a434a --- /dev/null +++ b/src/SharpCompress/Common/Tar/Headers/TarHeaderWriteFormat.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Common.Tar.Headers; + +public enum TarHeaderWriteFormat +{ + GNU_TAR_LONG_LINK, + USTAR, +} 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(*obj); + + public static void Init(void** obj) => *obj = UnmanagedObject.Wrap(new object()); + + public static void Free(void** obj) => UnmanagedObject.Free(*obj); + + public static void Enter(void** obj) => Monitor.Enter(UnwrapObject(obj)); + + public static void Exit(void** obj) => Monitor.Exit(UnwrapObject(obj)); + + public static void Pulse(void** obj) => Monitor.Pulse(UnwrapObject(obj)); + + public static void PulseAll(void** obj) => Monitor.PulseAll(UnwrapObject(obj)); + + public static void Wait(void** mutex) => Monitor.Wait(UnwrapObject(mutex)); +} diff --git a/src/SharpCompress/Compressors/ZStandard/ThrowHelper.cs b/src/SharpCompress/Compressors/ZStandard/ThrowHelper.cs new file mode 100644 index 00000000..5e051deb --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/ThrowHelper.cs @@ -0,0 +1,48 @@ +using SharpCompress.Compressors.ZStandard.Unsafe; + +namespace SharpCompress.Compressors.ZStandard; + +public static unsafe class ThrowHelper +{ + private const ulong ZSTD_CONTENTSIZE_UNKNOWN = unchecked(0UL - 1); + private const ulong ZSTD_CONTENTSIZE_ERROR = unchecked(0UL - 2); + + public static nuint EnsureZstdSuccess(this nuint returnValue) + { + if (Unsafe.Methods.ZSTD_isError(returnValue)) + ThrowException(returnValue, Unsafe.Methods.ZSTD_getErrorName(returnValue)); + + return returnValue; + } + + public static nuint EnsureZdictSuccess(this nuint returnValue) + { + if (Unsafe.Methods.ZDICT_isError(returnValue)) + ThrowException(returnValue, Unsafe.Methods.ZDICT_getErrorName(returnValue)); + + return returnValue; + } + + public static ulong EnsureContentSizeOk(this ulong returnValue) + { + if (returnValue == ZSTD_CONTENTSIZE_UNKNOWN) + throw new ZstdException( + ZSTD_ErrorCode.ZSTD_error_GENERIC, + "Decompressed content size is not specified" + ); + + if (returnValue == ZSTD_CONTENTSIZE_ERROR) + throw new ZstdException( + ZSTD_ErrorCode.ZSTD_error_GENERIC, + "Decompressed content size cannot be determined (e.g. invalid magic number, srcSize too small)" + ); + + return returnValue; + } + + private static void ThrowException(nuint returnValue, string message) + { + var code = 0 - returnValue; + throw new ZstdException((ZSTD_ErrorCode)code, message); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/UnmanagedObject.cs b/src/SharpCompress/Compressors/ZStandard/UnmanagedObject.cs new file mode 100644 index 00000000..a5bb31be --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/UnmanagedObject.cs @@ -0,0 +1,18 @@ +using System; +using System.Runtime.InteropServices; + +namespace SharpCompress.Compressors.ZStandard; + +/* + * Wrap object to void* to make it unmanaged + */ +internal static unsafe class UnmanagedObject +{ + public static void* Wrap(object obj) => (void*)GCHandle.ToIntPtr(GCHandle.Alloc(obj)); + + private static GCHandle UnwrapGcHandle(void* value) => GCHandle.FromIntPtr((IntPtr)value); + + public static T Unwrap(void* value) => (T)UnwrapGcHandle(value).Target!; + + public static void Free(void* value) => UnwrapGcHandle(value).Free(); +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Allocations.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Allocations.cs new file mode 100644 index 00000000..cecdd011 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Allocations.cs @@ -0,0 +1,52 @@ +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /* custom memory allocation functions */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void* ZSTD_customMalloc(nuint size, ZSTD_customMem customMem) + { + if (customMem.customAlloc != null) + return ((delegate* managed)customMem.customAlloc)( + customMem.opaque, + size + ); + return malloc(size); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void* ZSTD_customCalloc(nuint size, ZSTD_customMem customMem) + { + if (customMem.customAlloc != null) + { + /* calloc implemented as malloc+memset; + * not as efficient as calloc, but next best guess for custom malloc */ + void* ptr = ((delegate* managed)customMem.customAlloc)( + customMem.opaque, + size + ); + memset(ptr, 0, (uint)size); + return ptr; + } + + return calloc(1, size); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_customFree(void* ptr, ZSTD_customMem customMem) + { + if (ptr != null) + { + if (customMem.customFree != null) + ((delegate* managed)customMem.customFree)( + customMem.opaque, + ptr + ); + else + free(ptr); + } + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_CStream_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_CStream_t.cs new file mode 100644 index 00000000..ab23c39a --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_CStream_t.cs @@ -0,0 +1,14 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* bitStream can mix input from multiple sources. + * A critical property of these streams is that they encode and decode in **reverse** direction. + * So the first bit sequence you add will be the last to be read, like a LIFO stack. + */ +public unsafe struct BIT_CStream_t +{ + public nuint bitContainer; + public uint bitPos; + public sbyte* startPtr; + public sbyte* ptr; + public sbyte* endPtr; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_DStream_status.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_DStream_status.cs new file mode 100644 index 00000000..60b468b1 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_DStream_status.cs @@ -0,0 +1,16 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum BIT_DStream_status +{ + /* fully refilled */ + BIT_DStream_unfinished = 0, + + /* still some bits left in bitstream */ + BIT_DStream_endOfBuffer = 1, + + /* bitstream entirely consumed, bit-exact */ + BIT_DStream_completed = 2, + + /* user requested more bits than present in bitstream */ + BIT_DStream_overflow = 3, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_DStream_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_DStream_t.cs new file mode 100644 index 00000000..c0e96134 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/BIT_DStream_t.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*-******************************************** + * bitStream decoding API (read backward) + **********************************************/ +public unsafe struct BIT_DStream_t +{ + public nuint bitContainer; + public uint bitsConsumed; + public sbyte* ptr; + public sbyte* start; + public sbyte* limitPtr; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Bits.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Bits.cs new file mode 100644 index 00000000..e5484ae1 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Bits.cs @@ -0,0 +1,60 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_countTrailingZeros32(uint val) + { + assert(val != 0); + return (uint)BitOperations.TrailingZeroCount(val); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_countLeadingZeros32(uint val) + { + assert(val != 0); + return (uint)BitOperations.LeadingZeroCount(val); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_countTrailingZeros64(ulong val) + { + assert(val != 0); + return (uint)BitOperations.TrailingZeroCount(val); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_countLeadingZeros64(ulong val) + { + assert(val != 0); + return (uint)BitOperations.LeadingZeroCount(val); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_NbCommonBytes(nuint val) + { + assert(val != 0); + if (BitConverter.IsLittleEndian) + { + return MEM_64bits + ? (uint)BitOperations.TrailingZeroCount(val) >> 3 + : (uint)BitOperations.TrailingZeroCount((uint)val) >> 3; + } + + return MEM_64bits + ? (uint)BitOperations.LeadingZeroCount(val) >> 3 + : (uint)BitOperations.LeadingZeroCount((uint)val) >> 3; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_highbit32(uint val) + { + assert(val != 0); + return (uint)BitOperations.Log2(val); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Bitstream.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Bitstream.cs new file mode 100644 index 00000000..88302a49 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Bitstream.cs @@ -0,0 +1,739 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; +#if NETCOREAPP3_0_OR_GREATER +using System.Runtime.Intrinsics.X86; +#endif + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_BIT_mask => + new uint[32] + { + 0, + 1, + 3, + 7, + 0xF, + 0x1F, + 0x3F, + 0x7F, + 0xFF, + 0x1FF, + 0x3FF, + 0x7FF, + 0xFFF, + 0x1FFF, + 0x3FFF, + 0x7FFF, + 0xFFFF, + 0x1FFFF, + 0x3FFFF, + 0x7FFFF, + 0xFFFFF, + 0x1FFFFF, + 0x3FFFFF, + 0x7FFFFF, + 0xFFFFFF, + 0x1FFFFFF, + 0x3FFFFFF, + 0x7FFFFFF, + 0xFFFFFFF, + 0x1FFFFFFF, + 0x3FFFFFFF, + 0x7FFFFFFF, + }; + private static uint* BIT_mask => + (uint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_BIT_mask) + ); +#else + + private static readonly uint* BIT_mask = GetArrayPointer( + new uint[32] + { + 0, + 1, + 3, + 7, + 0xF, + 0x1F, + 0x3F, + 0x7F, + 0xFF, + 0x1FF, + 0x3FF, + 0x7FF, + 0xFFF, + 0x1FFF, + 0x3FFF, + 0x7FFF, + 0xFFFF, + 0x1FFFF, + 0x3FFFF, + 0x7FFFF, + 0xFFFFF, + 0x1FFFFF, + 0x3FFFFF, + 0x7FFFFF, + 0xFFFFFF, + 0x1FFFFFF, + 0x3FFFFFF, + 0x7FFFFFF, + 0xFFFFFFF, + 0x1FFFFFFF, + 0x3FFFFFFF, + 0x7FFFFFFF, + } + ); +#endif + /*-************************************************************** + * bitStream encoding + ****************************************************************/ + /*! BIT_initCStream() : + * `dstCapacity` must be > sizeof(size_t) + * @return : 0 if success, + * otherwise an error code (can be tested using ERR_isError()) */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_initCStream(ref BIT_CStream_t bitC, void* startPtr, nuint dstCapacity) + { + bitC.bitContainer = 0; + bitC.bitPos = 0; + bitC.startPtr = (sbyte*)startPtr; + bitC.ptr = bitC.startPtr; + bitC.endPtr = bitC.startPtr + dstCapacity - sizeof(nuint); + if (dstCapacity <= (nuint)sizeof(nuint)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + return 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_getLowerBits(nuint bitContainer, uint nbBits) + { + assert(nbBits < sizeof(uint) * 32 / sizeof(uint)); +#if NETCOREAPP3_1_OR_GREATER + if (Bmi2.X64.IsSupported) + { + return (nuint)Bmi2.X64.ZeroHighBits(bitContainer, nbBits); + } + + if (Bmi2.IsSupported) + { + return Bmi2.ZeroHighBits((uint)bitContainer, nbBits); + } +#endif + + return bitContainer & BIT_mask[nbBits]; + } + + /*! BIT_addBits() : + * can add up to 31 bits into `bitC`. + * Note : does not check for register overflow ! */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void BIT_addBits( + ref nuint bitC_bitContainer, + ref uint bitC_bitPos, + nuint value, + uint nbBits + ) + { + assert(nbBits < sizeof(uint) * 32 / sizeof(uint)); + assert(nbBits + bitC_bitPos < (uint)(sizeof(nuint) * 8)); + bitC_bitContainer |= BIT_getLowerBits(value, nbBits) << (int)bitC_bitPos; + bitC_bitPos += nbBits; + } + + /*! BIT_addBitsFast() : + * works only if `value` is _clean_, + * meaning all high bits above nbBits are 0 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void BIT_addBitsFast( + ref nuint bitC_bitContainer, + ref uint bitC_bitPos, + nuint value, + uint nbBits + ) + { + assert(value >> (int)nbBits == 0); + assert(nbBits + bitC_bitPos < (uint)(sizeof(nuint) * 8)); + bitC_bitContainer |= value << (int)bitC_bitPos; + bitC_bitPos += nbBits; + } + + /*! BIT_flushBitsFast() : + * assumption : bitContainer has not overflowed + * unsafe version; does not check buffer overflow */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void BIT_flushBitsFast( + ref nuint bitC_bitContainer, + ref uint bitC_bitPos, + ref sbyte* bitC_ptr, + sbyte* bitC_endPtr + ) + { + nuint nbBytes = bitC_bitPos >> 3; + assert(bitC_bitPos < (uint)(sizeof(nuint) * 8)); + assert(bitC_ptr <= bitC_endPtr); + MEM_writeLEST(bitC_ptr, bitC_bitContainer); + bitC_ptr += nbBytes; + bitC_bitPos &= 7; + bitC_bitContainer >>= (int)(nbBytes * 8); + } + + /*! BIT_flushBits() : + * assumption : bitContainer has not overflowed + * safe version; check for buffer overflow, and prevents it. + * note : does not signal buffer overflow. + * overflow will be revealed later on using BIT_closeCStream() */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void BIT_flushBits( + ref nuint bitC_bitContainer, + ref uint bitC_bitPos, + ref sbyte* bitC_ptr, + sbyte* bitC_endPtr + ) + { + nuint nbBytes = bitC_bitPos >> 3; + assert(bitC_bitPos < (uint)(sizeof(nuint) * 8)); + assert(bitC_ptr <= bitC_endPtr); + MEM_writeLEST(bitC_ptr, bitC_bitContainer); + bitC_ptr += nbBytes; + if (bitC_ptr > bitC_endPtr) + bitC_ptr = bitC_endPtr; + bitC_bitPos &= 7; + bitC_bitContainer >>= (int)(nbBytes * 8); + } + + /*! BIT_closeCStream() : + * @return : size of CStream, in bytes, + * or 0 if it could not fit into dstBuffer */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_closeCStream( + ref nuint bitC_bitContainer, + ref uint bitC_bitPos, + sbyte* bitC_ptr, + sbyte* bitC_endPtr, + sbyte* bitC_startPtr + ) + { + BIT_addBitsFast(ref bitC_bitContainer, ref bitC_bitPos, 1, 1); + BIT_flushBits(ref bitC_bitContainer, ref bitC_bitPos, ref bitC_ptr, bitC_endPtr); + if (bitC_ptr >= bitC_endPtr) + return 0; + return (nuint)(bitC_ptr - bitC_startPtr) + (nuint)(bitC_bitPos > 0 ? 1 : 0); + } + + /*-******************************************************** + * bitStream decoding + **********************************************************/ + /*! BIT_initDStream() : + * Initialize a BIT_DStream_t. + * `bitD` : a pointer to an already allocated BIT_DStream_t structure. + * `srcSize` must be the *exact* size of the bitStream, in bytes. + * @return : size of stream (== srcSize), or an errorCode if a problem is detected + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_initDStream(BIT_DStream_t* bitD, void* srcBuffer, nuint srcSize) + { + if (srcSize < 1) + { + *bitD = new BIT_DStream_t(); + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + bitD->start = (sbyte*)srcBuffer; + bitD->limitPtr = bitD->start + sizeof(nuint); + if (srcSize >= (nuint)sizeof(nuint)) + { + bitD->ptr = (sbyte*)srcBuffer + srcSize - sizeof(nuint); + bitD->bitContainer = MEM_readLEST(bitD->ptr); + { + byte lastByte = ((byte*)srcBuffer)[srcSize - 1]; + bitD->bitsConsumed = lastByte != 0 ? 8 - ZSTD_highbit32(lastByte) : 0; + if (lastByte == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + } + else + { + bitD->ptr = bitD->start; + bitD->bitContainer = *(byte*)bitD->start; + switch (srcSize) + { + case 7: + bitD->bitContainer += (nuint)((byte*)srcBuffer)[6] << sizeof(nuint) * 8 - 16; + goto case 6; + case 6: + bitD->bitContainer += (nuint)((byte*)srcBuffer)[5] << sizeof(nuint) * 8 - 24; + goto case 5; + case 5: + bitD->bitContainer += (nuint)((byte*)srcBuffer)[4] << sizeof(nuint) * 8 - 32; + goto case 4; + case 4: + bitD->bitContainer += (nuint)((byte*)srcBuffer)[3] << 24; + goto case 3; + case 3: + bitD->bitContainer += (nuint)((byte*)srcBuffer)[2] << 16; + goto case 2; + case 2: + bitD->bitContainer += (nuint)((byte*)srcBuffer)[1] << 8; + goto default; + default: + break; + } + + { + byte lastByte = ((byte*)srcBuffer)[srcSize - 1]; + bitD->bitsConsumed = lastByte != 0 ? 8 - ZSTD_highbit32(lastByte) : 0; + if (lastByte == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + bitD->bitsConsumed += (uint)((nuint)sizeof(nuint) - srcSize) * 8; + } + + return srcSize; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_getUpperBits(nuint bitContainer, uint start) + { + return bitContainer >> (int)start; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_getMiddleBits(nuint bitContainer, uint start, uint nbBits) + { + uint regMask = (uint)(sizeof(nuint) * 8 - 1); + assert(nbBits < sizeof(uint) * 32 / sizeof(uint)); +#if NETCOREAPP3_1_OR_GREATER + if (Bmi2.X64.IsSupported) + { + return (nuint)Bmi2.X64.ZeroHighBits(bitContainer >> (int)(start & regMask), nbBits); + } + + if (Bmi2.IsSupported) + { + return Bmi2.ZeroHighBits((uint)(bitContainer >> (int)(start & regMask)), nbBits); + } +#endif + + return (nuint)(bitContainer >> (int)(start & regMask) & ((ulong)1 << (int)nbBits) - 1); + } + + /*! BIT_lookBits() : + * Provides next n bits from local register. + * local register is not modified. + * On 32-bits, maxNbBits==24. + * On 64-bits, maxNbBits==56. + * @return : value extracted */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_lookBits(BIT_DStream_t* bitD, uint nbBits) + { + return BIT_getMiddleBits( + bitD->bitContainer, + (uint)(sizeof(nuint) * 8) - bitD->bitsConsumed - nbBits, + nbBits + ); + } + + /*! BIT_lookBitsFast() : + * unsafe version; only works if nbBits >= 1 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_lookBitsFast(BIT_DStream_t* bitD, uint nbBits) + { + uint regMask = (uint)(sizeof(nuint) * 8 - 1); + assert(nbBits >= 1); + return bitD->bitContainer + << (int)(bitD->bitsConsumed & regMask) + >> (int)(regMask + 1 - nbBits & regMask); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void BIT_skipBits(BIT_DStream_t* bitD, uint nbBits) + { + bitD->bitsConsumed += nbBits; + } + + /*! BIT_readBits() : + * Read (consume) next n bits from local register and update. + * Pay attention to not read more than nbBits contained into local register. + * @return : extracted value. */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_readBits(BIT_DStream_t* bitD, uint nbBits) + { + nuint value = BIT_lookBits(bitD, nbBits); + BIT_skipBits(bitD, nbBits); + return value; + } + + /*! BIT_readBitsFast() : + * unsafe version; only works if nbBits >= 1 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_readBitsFast(BIT_DStream_t* bitD, uint nbBits) + { + nuint value = BIT_lookBitsFast(bitD, nbBits); + assert(nbBits >= 1); + BIT_skipBits(bitD, nbBits); + return value; + } + + /*! BIT_reloadDStream_internal() : + * Simple variant of BIT_reloadDStream(), with two conditions: + * 1. bitstream is valid : bitsConsumed <= sizeof(bitD->bitContainer)*8 + * 2. look window is valid after shifted down : bitD->ptr >= bitD->start + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static BIT_DStream_status BIT_reloadDStream_internal(BIT_DStream_t* bitD) + { + assert(bitD->bitsConsumed <= (uint)(sizeof(nuint) * 8)); + bitD->ptr -= bitD->bitsConsumed >> 3; + assert(bitD->ptr >= bitD->start); + bitD->bitsConsumed &= 7; + bitD->bitContainer = MEM_readLEST(bitD->ptr); + return BIT_DStream_status.BIT_DStream_unfinished; + } + + /*! BIT_reloadDStreamFast() : + * Similar to BIT_reloadDStream(), but with two differences: + * 1. bitsConsumed <= sizeof(bitD->bitContainer)*8 must hold! + * 2. Returns BIT_DStream_overflow when bitD->ptr < bitD->limitPtr, at this + * point you must use BIT_reloadDStream() to reload. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static BIT_DStream_status BIT_reloadDStreamFast(BIT_DStream_t* bitD) + { + if (bitD->ptr < bitD->limitPtr) + return BIT_DStream_status.BIT_DStream_overflow; + return BIT_reloadDStream_internal(bitD); + } + +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_static_zeroFilled => + new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; + private static nuint* static_zeroFilled => + (nuint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_static_zeroFilled) + ); +#else + + private static readonly nuint* static_zeroFilled = (nuint*)GetArrayPointer( + new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 } + ); +#endif + /*! BIT_reloadDStream() : + * Refill `bitD` from buffer previously set in BIT_initDStream() . + * This function is safe, it guarantees it will not never beyond src buffer. + * @return : status of `BIT_DStream_t` internal register. + * when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) + { + if (bitD->bitsConsumed > (uint)(sizeof(nuint) * 8)) + { + bitD->ptr = (sbyte*)&static_zeroFilled[0]; + return BIT_DStream_status.BIT_DStream_overflow; + } + + assert(bitD->ptr >= bitD->start); + if (bitD->ptr >= bitD->limitPtr) + { + return BIT_reloadDStream_internal(bitD); + } + + if (bitD->ptr == bitD->start) + { + if (bitD->bitsConsumed < (uint)(sizeof(nuint) * 8)) + return BIT_DStream_status.BIT_DStream_endOfBuffer; + return BIT_DStream_status.BIT_DStream_completed; + } + + { + uint nbBytes = bitD->bitsConsumed >> 3; + BIT_DStream_status result = BIT_DStream_status.BIT_DStream_unfinished; + if (bitD->ptr - nbBytes < bitD->start) + { + nbBytes = (uint)(bitD->ptr - bitD->start); + result = BIT_DStream_status.BIT_DStream_endOfBuffer; + } + + bitD->ptr -= nbBytes; + bitD->bitsConsumed -= nbBytes * 8; + bitD->bitContainer = MEM_readLEST(bitD->ptr); + return result; + } + } + + /*! BIT_endOfDStream() : + * @return : 1 if DStream has _exactly_ reached its end (all bits consumed). + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint BIT_endOfDStream(BIT_DStream_t* DStream) + { + return DStream->ptr == DStream->start && DStream->bitsConsumed == (uint)(sizeof(nuint) * 8) + ? 1U + : 0U; + } + + /*-******************************************************** + * bitStream decoding + **********************************************************/ + /*! BIT_initDStream() : + * Initialize a BIT_DStream_t. + * `bitD` : a pointer to an already allocated BIT_DStream_t structure. + * `srcSize` must be the *exact* size of the bitStream, in bytes. + * @return : size of stream (== srcSize), or an errorCode if a problem is detected + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_initDStream(ref BIT_DStream_t bitD, void* srcBuffer, nuint srcSize) + { + if (srcSize < 1) + { + bitD = new BIT_DStream_t(); + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + bitD.start = (sbyte*)srcBuffer; + bitD.limitPtr = bitD.start + sizeof(nuint); + if (srcSize >= (nuint)sizeof(nuint)) + { + bitD.ptr = (sbyte*)srcBuffer + srcSize - sizeof(nuint); + bitD.bitContainer = MEM_readLEST(bitD.ptr); + { + byte lastByte = ((byte*)srcBuffer)[srcSize - 1]; + bitD.bitsConsumed = lastByte != 0 ? 8 - ZSTD_highbit32(lastByte) : 0; + if (lastByte == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + } + else + { + bitD.ptr = bitD.start; + bitD.bitContainer = *(byte*)bitD.start; + switch (srcSize) + { + case 7: + bitD.bitContainer += (nuint)((byte*)srcBuffer)[6] << sizeof(nuint) * 8 - 16; + goto case 6; + case 6: + bitD.bitContainer += (nuint)((byte*)srcBuffer)[5] << sizeof(nuint) * 8 - 24; + goto case 5; + case 5: + bitD.bitContainer += (nuint)((byte*)srcBuffer)[4] << sizeof(nuint) * 8 - 32; + goto case 4; + case 4: + bitD.bitContainer += (nuint)((byte*)srcBuffer)[3] << 24; + goto case 3; + case 3: + bitD.bitContainer += (nuint)((byte*)srcBuffer)[2] << 16; + goto case 2; + case 2: + bitD.bitContainer += (nuint)((byte*)srcBuffer)[1] << 8; + goto default; + default: + break; + } + + { + byte lastByte = ((byte*)srcBuffer)[srcSize - 1]; + bitD.bitsConsumed = lastByte != 0 ? 8 - ZSTD_highbit32(lastByte) : 0; + if (lastByte == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + bitD.bitsConsumed += (uint)((nuint)sizeof(nuint) - srcSize) * 8; + } + + return srcSize; + } + + /*! BIT_lookBits() : + * Provides next n bits from local register. + * local register is not modified. + * On 32-bits, maxNbBits==24. + * On 64-bits, maxNbBits==56. + * @return : value extracted */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_lookBits(nuint bitD_bitContainer, uint bitD_bitsConsumed, uint nbBits) + { + return BIT_getMiddleBits( + bitD_bitContainer, + (uint)(sizeof(nuint) * 8) - bitD_bitsConsumed - nbBits, + nbBits + ); + } + + /*! BIT_lookBitsFast() : + * unsafe version; only works if nbBits >= 1 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_lookBitsFast( + nuint bitD_bitContainer, + uint bitD_bitsConsumed, + uint nbBits + ) + { + uint regMask = (uint)(sizeof(nuint) * 8 - 1); + assert(nbBits >= 1); + return bitD_bitContainer + << (int)(bitD_bitsConsumed & regMask) + >> (int)(regMask + 1 - nbBits & regMask); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void BIT_skipBits(ref uint bitD_bitsConsumed, uint nbBits) + { + bitD_bitsConsumed += nbBits; + } + + /*! BIT_readBits() : + * Read (consume) next n bits from local register and update. + * Pay attention to not read more than nbBits contained into local register. + * @return : extracted value. */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_readBits( + nuint bitD_bitContainer, + ref uint bitD_bitsConsumed, + uint nbBits + ) + { + nuint value = BIT_lookBits(bitD_bitContainer, bitD_bitsConsumed, nbBits); + BIT_skipBits(ref bitD_bitsConsumed, nbBits); + return value; + } + + /*! BIT_readBitsFast() : + * unsafe version; only works if nbBits >= 1 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint BIT_readBitsFast( + nuint bitD_bitContainer, + ref uint bitD_bitsConsumed, + uint nbBits + ) + { + nuint value = BIT_lookBitsFast(bitD_bitContainer, bitD_bitsConsumed, nbBits); + assert(nbBits >= 1); + BIT_skipBits(ref bitD_bitsConsumed, nbBits); + return value; + } + + /*! BIT_reloadDStreamFast() : + * Similar to BIT_reloadDStream(), but with two differences: + * 1. bitsConsumed <= sizeof(bitD->bitContainer)*8 must hold! + * 2. Returns BIT_DStream_overflow when bitD->ptr < bitD->limitPtr, at this + * point you must use BIT_reloadDStream() to reload. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static BIT_DStream_status BIT_reloadDStreamFast( + ref nuint bitD_bitContainer, + ref uint bitD_bitsConsumed, + ref sbyte* bitD_ptr, + sbyte* bitD_start, + sbyte* bitD_limitPtr + ) + { + if (bitD_ptr < bitD_limitPtr) + return BIT_DStream_status.BIT_DStream_overflow; + return BIT_reloadDStream_internal( + ref bitD_bitContainer, + ref bitD_bitsConsumed, + ref bitD_ptr, + bitD_start + ); + } + + /*! BIT_reloadDStream() : + * Refill `bitD` from buffer previously set in BIT_initDStream() . + * This function is safe, it guarantees it will not never beyond src buffer. + * @return : status of `BIT_DStream_t` internal register. + * when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static BIT_DStream_status BIT_reloadDStream( + ref nuint bitD_bitContainer, + ref uint bitD_bitsConsumed, + ref sbyte* bitD_ptr, + sbyte* bitD_start, + sbyte* bitD_limitPtr + ) + { + if (bitD_bitsConsumed > (uint)(sizeof(nuint) * 8)) + { + bitD_ptr = (sbyte*)&static_zeroFilled[0]; + return BIT_DStream_status.BIT_DStream_overflow; + } + + assert(bitD_ptr >= bitD_start); + if (bitD_ptr >= bitD_limitPtr) + { + return BIT_reloadDStream_internal( + ref bitD_bitContainer, + ref bitD_bitsConsumed, + ref bitD_ptr, + bitD_start + ); + } + + if (bitD_ptr == bitD_start) + { + if (bitD_bitsConsumed < (uint)(sizeof(nuint) * 8)) + return BIT_DStream_status.BIT_DStream_endOfBuffer; + return BIT_DStream_status.BIT_DStream_completed; + } + + { + uint nbBytes = bitD_bitsConsumed >> 3; + BIT_DStream_status result = BIT_DStream_status.BIT_DStream_unfinished; + if (bitD_ptr - nbBytes < bitD_start) + { + nbBytes = (uint)(bitD_ptr - bitD_start); + result = BIT_DStream_status.BIT_DStream_endOfBuffer; + } + + bitD_ptr -= nbBytes; + bitD_bitsConsumed -= nbBytes * 8; + bitD_bitContainer = MEM_readLEST(bitD_ptr); + return result; + } + } + + /*! BIT_reloadDStream_internal() : + * Simple variant of BIT_reloadDStream(), with two conditions: + * 1. bitstream is valid : bitsConsumed <= sizeof(bitD->bitContainer)*8 + * 2. look window is valid after shifted down : bitD->ptr >= bitD->start + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static BIT_DStream_status BIT_reloadDStream_internal( + ref nuint bitD_bitContainer, + ref uint bitD_bitsConsumed, + ref sbyte* bitD_ptr, + sbyte* bitD_start + ) + { + assert(bitD_bitsConsumed <= (uint)(sizeof(nuint) * 8)); + bitD_ptr -= bitD_bitsConsumed >> 3; + assert(bitD_ptr >= bitD_start); + bitD_bitsConsumed &= 7; + bitD_bitContainer = MEM_readLEST(bitD_ptr); + return BIT_DStream_status.BIT_DStream_unfinished; + } + + /*! BIT_endOfDStream() : + * @return : 1 if DStream has _exactly_ reached its end (all bits consumed). + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint BIT_endOfDStream( + uint DStream_bitsConsumed, + sbyte* DStream_ptr, + sbyte* DStream_start + ) + { + return DStream_ptr == DStream_start && DStream_bitsConsumed == (uint)(sizeof(nuint) * 8) + ? 1U + : 0U; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/BlockSummary.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/BlockSummary.cs new file mode 100644 index 00000000..9a53651d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/BlockSummary.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct BlockSummary +{ + public nuint nbSequences; + public nuint blockSize; + public nuint litSize; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_best_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_best_s.cs new file mode 100644 index 00000000..38083132 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_best_s.cs @@ -0,0 +1,20 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * COVER_best_t is used for two purposes: + * 1. Synchronizing threads. + * 2. Saving the best parameters and dictionary. + * + * All of the methods except COVER_best_init() are thread safe if zstd is + * compiled with multithreaded support. + */ +public unsafe struct COVER_best_s +{ + public void* mutex; + public void* cond; + public nuint liveJobs; + public void* dict; + public nuint dictSize; + public ZDICT_cover_params_t parameters; + public nuint compressedSize; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_ctx_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_ctx_t.cs new file mode 100644 index 00000000..28712762 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_ctx_t.cs @@ -0,0 +1,19 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*-************************************* + * Context + ***************************************/ +public unsafe struct COVER_ctx_t +{ + public byte* samples; + public nuint* offsets; + public nuint* samplesSizes; + public nuint nbSamples; + public nuint nbTrainSamples; + public nuint nbTestSamples; + public uint* suffix; + public nuint suffixSize; + public uint* freqs; + public uint* dmerAt; + public uint d; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_dictSelection.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_dictSelection.cs new file mode 100644 index 00000000..f88d1e67 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_dictSelection.cs @@ -0,0 +1,11 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * Struct used for the dictionary selection function. + */ +public unsafe struct COVER_dictSelection +{ + public byte* dictContent; + public nuint dictSize; + public nuint totalCompressedSize; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_epoch_info_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_epoch_info_t.cs new file mode 100644 index 00000000..dbb29890 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_epoch_info_t.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + *Number of epochs and size of each epoch. + */ +public struct COVER_epoch_info_t +{ + public uint num; + public uint size; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_map_pair_t_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_map_pair_t_s.cs new file mode 100644 index 00000000..502f33b0 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_map_pair_t_s.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct COVER_map_pair_t_s +{ + public uint key; + public uint value; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_map_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_map_s.cs new file mode 100644 index 00000000..dbfa337f --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_map_s.cs @@ -0,0 +1,9 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct COVER_map_s +{ + public COVER_map_pair_t_s* data; + public uint sizeLog; + public uint size; + public uint sizeMask; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_segment_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_segment_t.cs new file mode 100644 index 00000000..7e852ea9 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_segment_t.cs @@ -0,0 +1,11 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * A segment is a range in the source as well as the score of the segment. + */ +public struct COVER_segment_t +{ + public uint begin; + public uint end; + public uint score; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_tryParameters_data_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_tryParameters_data_s.cs new file mode 100644 index 00000000..789ca9dc --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/COVER_tryParameters_data_s.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * Parameters for COVER_tryParameters(). + */ +public unsafe struct COVER_tryParameters_data_s +{ + public COVER_ctx_t* ctx; + public COVER_best_s* best; + public nuint dictBufferCapacity; + public ZDICT_cover_params_t parameters; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Clevels.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Clevels.cs new file mode 100644 index 00000000..9b3e7839 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Clevels.cs @@ -0,0 +1,849 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + private static readonly ZSTD_compressionParameters[][] ZSTD_defaultCParameters = + new ZSTD_compressionParameters[4][] + { + new ZSTD_compressionParameters[23] + { + new ZSTD_compressionParameters( + windowLog: 19, + chainLog: 12, + hashLog: 13, + searchLog: 1, + minMatch: 6, + targetLength: 1, + strategy: ZSTD_strategy.ZSTD_fast + ), + new ZSTD_compressionParameters( + windowLog: 19, + chainLog: 13, + hashLog: 14, + searchLog: 1, + minMatch: 7, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_fast + ), + new ZSTD_compressionParameters( + windowLog: 20, + chainLog: 15, + hashLog: 16, + searchLog: 1, + minMatch: 6, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_fast + ), + new ZSTD_compressionParameters( + windowLog: 21, + chainLog: 16, + hashLog: 17, + searchLog: 1, + minMatch: 5, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_dfast + ), + new ZSTD_compressionParameters( + windowLog: 21, + chainLog: 18, + hashLog: 18, + searchLog: 1, + minMatch: 5, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_dfast + ), + new ZSTD_compressionParameters( + windowLog: 21, + chainLog: 18, + hashLog: 19, + searchLog: 3, + minMatch: 5, + targetLength: 2, + strategy: ZSTD_strategy.ZSTD_greedy + ), + new ZSTD_compressionParameters( + windowLog: 21, + chainLog: 18, + hashLog: 19, + searchLog: 3, + minMatch: 5, + targetLength: 4, + strategy: ZSTD_strategy.ZSTD_lazy + ), + new ZSTD_compressionParameters( + windowLog: 21, + chainLog: 19, + hashLog: 20, + searchLog: 4, + minMatch: 5, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_lazy + ), + new ZSTD_compressionParameters( + windowLog: 21, + chainLog: 19, + hashLog: 20, + searchLog: 4, + minMatch: 5, + targetLength: 16, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 22, + chainLog: 20, + hashLog: 21, + searchLog: 4, + minMatch: 5, + targetLength: 16, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 22, + chainLog: 21, + hashLog: 22, + searchLog: 5, + minMatch: 5, + targetLength: 16, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 22, + chainLog: 21, + hashLog: 22, + searchLog: 6, + minMatch: 5, + targetLength: 16, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 22, + chainLog: 22, + hashLog: 23, + searchLog: 6, + minMatch: 5, + targetLength: 32, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 22, + chainLog: 22, + hashLog: 22, + searchLog: 4, + minMatch: 5, + targetLength: 32, + strategy: ZSTD_strategy.ZSTD_btlazy2 + ), + new ZSTD_compressionParameters( + windowLog: 22, + chainLog: 22, + hashLog: 23, + searchLog: 5, + minMatch: 5, + targetLength: 32, + strategy: ZSTD_strategy.ZSTD_btlazy2 + ), + new ZSTD_compressionParameters( + windowLog: 22, + chainLog: 23, + hashLog: 23, + searchLog: 6, + minMatch: 5, + targetLength: 32, + strategy: ZSTD_strategy.ZSTD_btlazy2 + ), + new ZSTD_compressionParameters( + windowLog: 22, + chainLog: 22, + hashLog: 22, + searchLog: 5, + minMatch: 5, + targetLength: 48, + strategy: ZSTD_strategy.ZSTD_btopt + ), + new ZSTD_compressionParameters( + windowLog: 23, + chainLog: 23, + hashLog: 22, + searchLog: 5, + minMatch: 4, + targetLength: 64, + strategy: ZSTD_strategy.ZSTD_btopt + ), + new ZSTD_compressionParameters( + windowLog: 23, + chainLog: 23, + hashLog: 22, + searchLog: 6, + minMatch: 3, + targetLength: 64, + strategy: ZSTD_strategy.ZSTD_btultra + ), + new ZSTD_compressionParameters( + windowLog: 23, + chainLog: 24, + hashLog: 22, + searchLog: 7, + minMatch: 3, + targetLength: 256, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 25, + chainLog: 25, + hashLog: 23, + searchLog: 7, + minMatch: 3, + targetLength: 256, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 26, + chainLog: 26, + hashLog: 24, + searchLog: 7, + minMatch: 3, + targetLength: 512, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 27, + chainLog: 27, + hashLog: 25, + searchLog: 9, + minMatch: 3, + targetLength: 999, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + }, + new ZSTD_compressionParameters[23] + { + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 12, + hashLog: 13, + searchLog: 1, + minMatch: 5, + targetLength: 1, + strategy: ZSTD_strategy.ZSTD_fast + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 13, + hashLog: 14, + searchLog: 1, + minMatch: 6, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_fast + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 14, + hashLog: 14, + searchLog: 1, + minMatch: 5, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_dfast + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 16, + hashLog: 16, + searchLog: 1, + minMatch: 4, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_dfast + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 16, + hashLog: 17, + searchLog: 3, + minMatch: 5, + targetLength: 2, + strategy: ZSTD_strategy.ZSTD_greedy + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 17, + hashLog: 18, + searchLog: 5, + minMatch: 5, + targetLength: 2, + strategy: ZSTD_strategy.ZSTD_greedy + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 18, + hashLog: 19, + searchLog: 3, + minMatch: 5, + targetLength: 4, + strategy: ZSTD_strategy.ZSTD_lazy + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 18, + hashLog: 19, + searchLog: 4, + minMatch: 4, + targetLength: 4, + strategy: ZSTD_strategy.ZSTD_lazy + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 18, + hashLog: 19, + searchLog: 4, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 18, + hashLog: 19, + searchLog: 5, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 18, + hashLog: 19, + searchLog: 6, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 18, + hashLog: 19, + searchLog: 5, + minMatch: 4, + targetLength: 12, + strategy: ZSTD_strategy.ZSTD_btlazy2 + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 19, + hashLog: 19, + searchLog: 7, + minMatch: 4, + targetLength: 12, + strategy: ZSTD_strategy.ZSTD_btlazy2 + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 18, + hashLog: 19, + searchLog: 4, + minMatch: 4, + targetLength: 16, + strategy: ZSTD_strategy.ZSTD_btopt + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 18, + hashLog: 19, + searchLog: 4, + minMatch: 3, + targetLength: 32, + strategy: ZSTD_strategy.ZSTD_btopt + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 18, + hashLog: 19, + searchLog: 6, + minMatch: 3, + targetLength: 128, + strategy: ZSTD_strategy.ZSTD_btopt + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 19, + hashLog: 19, + searchLog: 6, + minMatch: 3, + targetLength: 128, + strategy: ZSTD_strategy.ZSTD_btultra + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 19, + hashLog: 19, + searchLog: 8, + minMatch: 3, + targetLength: 256, + strategy: ZSTD_strategy.ZSTD_btultra + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 19, + hashLog: 19, + searchLog: 6, + minMatch: 3, + targetLength: 128, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 19, + hashLog: 19, + searchLog: 8, + minMatch: 3, + targetLength: 256, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 19, + hashLog: 19, + searchLog: 10, + minMatch: 3, + targetLength: 512, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 19, + hashLog: 19, + searchLog: 12, + minMatch: 3, + targetLength: 512, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 18, + chainLog: 19, + hashLog: 19, + searchLog: 13, + minMatch: 3, + targetLength: 999, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + }, + new ZSTD_compressionParameters[23] + { + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 12, + hashLog: 12, + searchLog: 1, + minMatch: 5, + targetLength: 1, + strategy: ZSTD_strategy.ZSTD_fast + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 12, + hashLog: 13, + searchLog: 1, + minMatch: 6, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_fast + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 13, + hashLog: 15, + searchLog: 1, + minMatch: 5, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_fast + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 15, + hashLog: 16, + searchLog: 2, + minMatch: 5, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_dfast + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 17, + hashLog: 17, + searchLog: 2, + minMatch: 4, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_dfast + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 16, + hashLog: 17, + searchLog: 3, + minMatch: 4, + targetLength: 2, + strategy: ZSTD_strategy.ZSTD_greedy + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 16, + hashLog: 17, + searchLog: 3, + minMatch: 4, + targetLength: 4, + strategy: ZSTD_strategy.ZSTD_lazy + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 16, + hashLog: 17, + searchLog: 3, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 16, + hashLog: 17, + searchLog: 4, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 16, + hashLog: 17, + searchLog: 5, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 16, + hashLog: 17, + searchLog: 6, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 17, + hashLog: 17, + searchLog: 5, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_btlazy2 + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 18, + hashLog: 17, + searchLog: 7, + minMatch: 4, + targetLength: 12, + strategy: ZSTD_strategy.ZSTD_btlazy2 + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 18, + hashLog: 17, + searchLog: 3, + minMatch: 4, + targetLength: 12, + strategy: ZSTD_strategy.ZSTD_btopt + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 18, + hashLog: 17, + searchLog: 4, + minMatch: 3, + targetLength: 32, + strategy: ZSTD_strategy.ZSTD_btopt + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 18, + hashLog: 17, + searchLog: 6, + minMatch: 3, + targetLength: 256, + strategy: ZSTD_strategy.ZSTD_btopt + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 18, + hashLog: 17, + searchLog: 6, + minMatch: 3, + targetLength: 128, + strategy: ZSTD_strategy.ZSTD_btultra + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 18, + hashLog: 17, + searchLog: 8, + minMatch: 3, + targetLength: 256, + strategy: ZSTD_strategy.ZSTD_btultra + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 18, + hashLog: 17, + searchLog: 10, + minMatch: 3, + targetLength: 512, + strategy: ZSTD_strategy.ZSTD_btultra + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 18, + hashLog: 17, + searchLog: 5, + minMatch: 3, + targetLength: 256, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 18, + hashLog: 17, + searchLog: 7, + minMatch: 3, + targetLength: 512, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 18, + hashLog: 17, + searchLog: 9, + minMatch: 3, + targetLength: 512, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 17, + chainLog: 18, + hashLog: 17, + searchLog: 11, + minMatch: 3, + targetLength: 999, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + }, + new ZSTD_compressionParameters[23] + { + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 12, + hashLog: 13, + searchLog: 1, + minMatch: 5, + targetLength: 1, + strategy: ZSTD_strategy.ZSTD_fast + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 14, + hashLog: 15, + searchLog: 1, + minMatch: 5, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_fast + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 14, + hashLog: 15, + searchLog: 1, + minMatch: 4, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_fast + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 14, + hashLog: 15, + searchLog: 2, + minMatch: 4, + targetLength: 0, + strategy: ZSTD_strategy.ZSTD_dfast + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 14, + hashLog: 14, + searchLog: 4, + minMatch: 4, + targetLength: 2, + strategy: ZSTD_strategy.ZSTD_greedy + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 14, + hashLog: 14, + searchLog: 3, + minMatch: 4, + targetLength: 4, + strategy: ZSTD_strategy.ZSTD_lazy + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 14, + hashLog: 14, + searchLog: 4, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 14, + hashLog: 14, + searchLog: 6, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 14, + hashLog: 14, + searchLog: 8, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_lazy2 + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 14, + searchLog: 5, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_btlazy2 + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 14, + searchLog: 9, + minMatch: 4, + targetLength: 8, + strategy: ZSTD_strategy.ZSTD_btlazy2 + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 14, + searchLog: 3, + minMatch: 4, + targetLength: 12, + strategy: ZSTD_strategy.ZSTD_btopt + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 14, + searchLog: 4, + minMatch: 3, + targetLength: 24, + strategy: ZSTD_strategy.ZSTD_btopt + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 14, + searchLog: 5, + minMatch: 3, + targetLength: 32, + strategy: ZSTD_strategy.ZSTD_btultra + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 15, + searchLog: 6, + minMatch: 3, + targetLength: 64, + strategy: ZSTD_strategy.ZSTD_btultra + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 15, + searchLog: 7, + minMatch: 3, + targetLength: 256, + strategy: ZSTD_strategy.ZSTD_btultra + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 15, + searchLog: 5, + minMatch: 3, + targetLength: 48, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 15, + searchLog: 6, + minMatch: 3, + targetLength: 128, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 15, + searchLog: 7, + minMatch: 3, + targetLength: 256, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 15, + searchLog: 8, + minMatch: 3, + targetLength: 256, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 15, + searchLog: 8, + minMatch: 3, + targetLength: 512, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 15, + searchLog: 9, + minMatch: 3, + targetLength: 512, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + new ZSTD_compressionParameters( + windowLog: 14, + chainLog: 15, + hashLog: 15, + searchLog: 10, + minMatch: 3, + targetLength: 999, + strategy: ZSTD_strategy.ZSTD_btultra2 + ), + }, + }; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Compiler.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Compiler.cs new file mode 100644 index 00000000..97fead59 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Compiler.cs @@ -0,0 +1,61 @@ +using System.Runtime.CompilerServices; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /* @return 1 if @u is a 2^n value, 0 otherwise + * useful to check a value is valid for alignment restrictions */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_isPower2(nuint u) + { + return (u & u - 1) == 0 ? 1 : 0; + } + + /** + * Helper function to perform a wrapped pointer difference without triggering + * UBSAN. + * + * @returns lhs - rhs with wrapping + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nint ZSTD_wrappedPtrDiff(byte* lhs, byte* rhs) + { + return (nint)(lhs - rhs); + } + + /** + * Helper function to perform a wrapped pointer add without triggering UBSAN. + * + * @return ptr + add with wrapping + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte* ZSTD_wrappedPtrAdd(byte* ptr, nint add) + { + return ptr + add; + } + + /** + * Helper function to perform a wrapped pointer subtraction without triggering + * UBSAN. + * + * @return ptr - sub with wrapping + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte* ZSTD_wrappedPtrSub(byte* ptr, nint sub) + { + return ptr - sub; + } + + /** + * Helper function to add to a pointer that works around C's undefined behavior + * of adding 0 to NULL. + * + * @returns `ptr + add` except it defines `NULL + 0 == NULL`. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte* ZSTD_maybeNullPtrAdd(byte* ptr, nint add) + { + return add > 0 ? ptr + add : ptr; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Cover.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Cover.cs new file mode 100644 index 00000000..11c79f3c --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Cover.cs @@ -0,0 +1,444 @@ +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + private static int g_displayLevel = 0; + + /** + * Returns the sum of the sample sizes. + */ + private static nuint COVER_sum(nuint* samplesSizes, uint nbSamples) + { + nuint sum = 0; + uint i; + for (i = 0; i < nbSamples; ++i) + { + sum += samplesSizes[i]; + } + + return sum; + } + + /** + * Warns the user when their corpus is too small. + */ + private static void COVER_warnOnSmallCorpus(nuint maxDictSize, nuint nbDmers, int displayLevel) + { + double ratio = nbDmers / (double)maxDictSize; + if (ratio >= 10) + { + return; + } + } + + /** + * Computes the number of epochs and the size of each epoch. + * We will make sure that each epoch gets at least 10 * k bytes. + * + * The COVER algorithms divide the data up into epochs of equal size and + * select one segment from each epoch. + * + * @param maxDictSize The maximum allowed dictionary size. + * @param nbDmers The number of dmers we are training on. + * @param k The parameter k (segment size). + * @param passes The target number of passes over the dmer corpus. + * More passes means a better dictionary. + */ + private static COVER_epoch_info_t COVER_computeEpochs( + uint maxDictSize, + uint nbDmers, + uint k, + uint passes + ) + { + uint minEpochSize = k * 10; + COVER_epoch_info_t epochs; + epochs.num = 1 > maxDictSize / k / passes ? 1 : maxDictSize / k / passes; + epochs.size = nbDmers / epochs.num; + if (epochs.size >= minEpochSize) + { + assert(epochs.size * epochs.num <= nbDmers); + return epochs; + } + + epochs.size = minEpochSize < nbDmers ? minEpochSize : nbDmers; + epochs.num = nbDmers / epochs.size; + assert(epochs.size * epochs.num <= nbDmers); + return epochs; + } + + /** + * Checks total compressed size of a dictionary + */ + private static nuint COVER_checkTotalCompressedSize( + ZDICT_cover_params_t parameters, + nuint* samplesSizes, + byte* samples, + nuint* offsets, + nuint nbTrainSamples, + nuint nbSamples, + byte* dict, + nuint dictBufferCapacity + ) + { + nuint totalCompressedSize = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + /* Pointers */ + ZSTD_CCtx_s* cctx; + ZSTD_CDict_s* cdict; + void* dst; + /* Local variables */ + nuint dstCapacity; + nuint i; + { + nuint maxSampleSize = 0; + i = parameters.splitPoint < 1 ? nbTrainSamples : 0; + for (; i < nbSamples; ++i) + { + maxSampleSize = samplesSizes[i] > maxSampleSize ? samplesSizes[i] : maxSampleSize; + } + + dstCapacity = ZSTD_compressBound(maxSampleSize); + dst = malloc(dstCapacity); + } + + cctx = ZSTD_createCCtx(); + cdict = ZSTD_createCDict(dict, dictBufferCapacity, parameters.zParams.compressionLevel); + if (dst == null || cctx == null || cdict == null) + { + goto _compressCleanup; + } + + totalCompressedSize = dictBufferCapacity; + i = parameters.splitPoint < 1 ? nbTrainSamples : 0; + for (; i < nbSamples; ++i) + { + nuint size = ZSTD_compress_usingCDict( + cctx, + dst, + dstCapacity, + samples + offsets[i], + samplesSizes[i], + cdict + ); + if (ERR_isError(size)) + { + totalCompressedSize = size; + goto _compressCleanup; + } + + totalCompressedSize += size; + } + + _compressCleanup: + ZSTD_freeCCtx(cctx); + ZSTD_freeCDict(cdict); + if (dst != null) + { + free(dst); + } + + return totalCompressedSize; + } + + /** + * Initialize the `COVER_best_t`. + */ + private static void COVER_best_init(COVER_best_s* best) + { + if (best == null) + return; + SynchronizationWrapper.Init(&best->mutex); + best->liveJobs = 0; + best->dict = null; + best->dictSize = 0; + best->compressedSize = unchecked((nuint)(-1)); + best->parameters = new ZDICT_cover_params_t(); + } + + /** + * Wait until liveJobs == 0. + */ + private static void COVER_best_wait(COVER_best_s* best) + { + if (best == null) + { + return; + } + + SynchronizationWrapper.Enter(&best->mutex); + while (best->liveJobs != 0) + { + SynchronizationWrapper.Wait(&best->mutex); + } + + SynchronizationWrapper.Exit(&best->mutex); + } + + /** + * Call COVER_best_wait() and then destroy the COVER_best_t. + */ + private static void COVER_best_destroy(COVER_best_s* best) + { + if (best == null) + { + return; + } + + COVER_best_wait(best); + if (best->dict != null) + { + free(best->dict); + } + + SynchronizationWrapper.Free(&best->mutex); + } + + /** + * Called when a thread is about to be launched. + * Increments liveJobs. + */ + private static void COVER_best_start(COVER_best_s* best) + { + if (best == null) + { + return; + } + + SynchronizationWrapper.Enter(&best->mutex); + ++best->liveJobs; + SynchronizationWrapper.Exit(&best->mutex); + } + + /** + * Called when a thread finishes executing, both on error or success. + * Decrements liveJobs and signals any waiting threads if liveJobs == 0. + * If this dictionary is the best so far save it and its parameters. + */ + private static void COVER_best_finish( + COVER_best_s* best, + ZDICT_cover_params_t parameters, + COVER_dictSelection selection + ) + { + void* dict = selection.dictContent; + nuint compressedSize = selection.totalCompressedSize; + nuint dictSize = selection.dictSize; + if (best == null) + { + return; + } + + { + nuint liveJobs; + SynchronizationWrapper.Enter(&best->mutex); + --best->liveJobs; + liveJobs = best->liveJobs; + if (compressedSize < best->compressedSize) + { + if (best->dict == null || best->dictSize < dictSize) + { + if (best->dict != null) + { + free(best->dict); + } + + best->dict = malloc(dictSize); + if (best->dict == null) + { + best->compressedSize = unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC) + ); + best->dictSize = 0; + SynchronizationWrapper.Pulse(&best->mutex); + SynchronizationWrapper.Exit(&best->mutex); + return; + } + } + + if (dict != null) + { + memcpy(best->dict, dict, (uint)dictSize); + best->dictSize = dictSize; + best->parameters = parameters; + best->compressedSize = compressedSize; + } + } + + if (liveJobs == 0) + { + SynchronizationWrapper.PulseAll(&best->mutex); + } + + SynchronizationWrapper.Exit(&best->mutex); + } + } + + private static COVER_dictSelection setDictSelection(byte* buf, nuint s, nuint csz) + { + COVER_dictSelection ds; + ds.dictContent = buf; + ds.dictSize = s; + ds.totalCompressedSize = csz; + return ds; + } + + /** + * Error function for COVER_selectDict function. Returns a struct where + * return.totalCompressedSize is a ZSTD error. + */ + private static COVER_dictSelection COVER_dictSelectionError(nuint error) + { + return setDictSelection(null, 0, error); + } + + /** + * Error function for COVER_selectDict function. Checks if the return + * value is an error. + */ + private static uint COVER_dictSelectionIsError(COVER_dictSelection selection) + { + return ERR_isError(selection.totalCompressedSize) || selection.dictContent == null + ? 1U + : 0U; + } + + /** + * Always call after selectDict is called to free up used memory from + * newly created dictionary. + */ + private static void COVER_dictSelectionFree(COVER_dictSelection selection) + { + free(selection.dictContent); + } + + /** + * Called to finalize the dictionary and select one based on whether or not + * the shrink-dict flag was enabled. If enabled the dictionary used is the + * smallest dictionary within a specified regression of the compressed size + * from the largest dictionary. + */ + private static COVER_dictSelection COVER_selectDict( + byte* customDictContent, + nuint dictBufferCapacity, + nuint dictContentSize, + byte* samplesBuffer, + nuint* samplesSizes, + uint nbFinalizeSamples, + nuint nbCheckSamples, + nuint nbSamples, + ZDICT_cover_params_t @params, + nuint* offsets, + nuint totalCompressedSize + ) + { + nuint largestDict = 0; + nuint largestCompressed = 0; + byte* customDictContentEnd = customDictContent + dictContentSize; + byte* largestDictbuffer = (byte*)malloc(dictBufferCapacity); + byte* candidateDictBuffer = (byte*)malloc(dictBufferCapacity); + double regressionTolerance = (double)@params.shrinkDictMaxRegression / 100 + 1; + if (largestDictbuffer == null || candidateDictBuffer == null) + { + free(largestDictbuffer); + free(candidateDictBuffer); + return COVER_dictSelectionError(dictContentSize); + } + + memcpy(largestDictbuffer, customDictContent, (uint)dictContentSize); + dictContentSize = ZDICT_finalizeDictionary( + largestDictbuffer, + dictBufferCapacity, + customDictContent, + dictContentSize, + samplesBuffer, + samplesSizes, + nbFinalizeSamples, + @params.zParams + ); + if (ZDICT_isError(dictContentSize)) + { + free(largestDictbuffer); + free(candidateDictBuffer); + return COVER_dictSelectionError(dictContentSize); + } + + totalCompressedSize = COVER_checkTotalCompressedSize( + @params, + samplesSizes, + samplesBuffer, + offsets, + nbCheckSamples, + nbSamples, + largestDictbuffer, + dictContentSize + ); + if (ERR_isError(totalCompressedSize)) + { + free(largestDictbuffer); + free(candidateDictBuffer); + return COVER_dictSelectionError(totalCompressedSize); + } + + if (@params.shrinkDict == 0) + { + free(candidateDictBuffer); + return setDictSelection(largestDictbuffer, dictContentSize, totalCompressedSize); + } + + largestDict = dictContentSize; + largestCompressed = totalCompressedSize; + dictContentSize = 256; + while (dictContentSize < largestDict) + { + memcpy(candidateDictBuffer, largestDictbuffer, (uint)largestDict); + dictContentSize = ZDICT_finalizeDictionary( + candidateDictBuffer, + dictBufferCapacity, + customDictContentEnd - dictContentSize, + dictContentSize, + samplesBuffer, + samplesSizes, + nbFinalizeSamples, + @params.zParams + ); + if (ZDICT_isError(dictContentSize)) + { + free(largestDictbuffer); + free(candidateDictBuffer); + return COVER_dictSelectionError(dictContentSize); + } + + totalCompressedSize = COVER_checkTotalCompressedSize( + @params, + samplesSizes, + samplesBuffer, + offsets, + nbCheckSamples, + nbSamples, + candidateDictBuffer, + dictContentSize + ); + if (ERR_isError(totalCompressedSize)) + { + free(largestDictbuffer); + free(candidateDictBuffer); + return COVER_dictSelectionError(totalCompressedSize); + } + + if (totalCompressedSize <= largestCompressed * regressionTolerance) + { + free(largestDictbuffer); + return setDictSelection(candidateDictBuffer, dictContentSize, totalCompressedSize); + } + + dictContentSize *= 2; + } + + dictContentSize = largestDict; + totalCompressedSize = largestCompressed; + free(candidateDictBuffer); + return setDictSelection(largestDictbuffer, dictContentSize, totalCompressedSize); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/DTableDesc.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/DTableDesc.cs new file mode 100644 index 00000000..6586ac46 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/DTableDesc.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*-***************************/ +/* generic DTableDesc */ +/*-***************************/ +public struct DTableDesc +{ + public byte maxTableLog; + public byte tableType; + public byte tableLog; + public byte reserved; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/EStats_ress_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/EStats_ress_t.cs new file mode 100644 index 00000000..9d3f1ba2 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/EStats_ress_t.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct EStats_ress_t +{ + /* dictionary */ + public ZSTD_CDict_s* dict; + + /* working context */ + public ZSTD_CCtx_s* zc; + + /* must be ZSTD_BLOCKSIZE_MAX allocated */ + public void* workPlace; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/EntropyCommon.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/EntropyCommon.cs new file mode 100644 index 00000000..e14f925f --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/EntropyCommon.cs @@ -0,0 +1,447 @@ +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /*=== Version ===*/ + private static uint FSE_versionNumber() + { + return 0 * 100 * 100 + 9 * 100 + 0; + } + + /*=== Error Management ===*/ + private static bool FSE_isError(nuint code) + { + return ERR_isError(code); + } + + private static string FSE_getErrorName(nuint code) + { + return ERR_getErrorName(code); + } + + /* Error Management */ + private static bool HUF_isError(nuint code) + { + return ERR_isError(code); + } + + private static string HUF_getErrorName(nuint code) + { + return ERR_getErrorName(code); + } + + /*-************************************************************** + * FSE NCount encoding-decoding + ****************************************************************/ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint FSE_readNCount_body( + short* normalizedCounter, + uint* maxSVPtr, + uint* tableLogPtr, + void* headerBuffer, + nuint hbSize + ) + { + byte* istart = (byte*)headerBuffer; + byte* iend = istart + hbSize; + byte* ip = istart; + int nbBits; + int remaining; + int threshold; + uint bitStream; + int bitCount; + uint charnum = 0; + uint maxSV1 = *maxSVPtr + 1; + int previous0 = 0; + if (hbSize < 8) + { + sbyte* buffer = stackalloc sbyte[8]; + /* This function only works when hbSize >= 8 */ + memset(buffer, 0, sizeof(sbyte) * 8); + memcpy(buffer, headerBuffer, (uint)hbSize); + { + nuint countSize = FSE_readNCount( + normalizedCounter, + maxSVPtr, + tableLogPtr, + buffer, + sizeof(sbyte) * 8 + ); + if (FSE_isError(countSize)) + return countSize; + if (countSize > hbSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + return countSize; + } + } + + assert(hbSize >= 8); + memset(normalizedCounter, 0, (*maxSVPtr + 1) * sizeof(short)); + bitStream = MEM_readLE32(ip); + nbBits = (int)((bitStream & 0xF) + 5); + if (nbBits > 15) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + bitStream >>= 4; + bitCount = 4; + *tableLogPtr = (uint)nbBits; + remaining = (1 << nbBits) + 1; + threshold = 1 << nbBits; + nbBits++; + for (; ; ) + { + if (previous0 != 0) + { + /* Count the number of repeats. Each time the + * 2-bit repeat code is 0b11 there is another + * repeat. + * Avoid UB by setting the high bit to 1. + */ + int repeats = (int)(ZSTD_countTrailingZeros32(~bitStream | 0x80000000) >> 1); + while (repeats >= 12) + { + charnum += 3 * 12; + if (ip <= iend - 7) + { + ip += 3; + } + else + { + bitCount -= (int)(8 * (iend - 7 - ip)); + bitCount &= 31; + ip = iend - 4; + } + + bitStream = MEM_readLE32(ip) >> bitCount; + repeats = (int)(ZSTD_countTrailingZeros32(~bitStream | 0x80000000) >> 1); + } + + charnum += (uint)(3 * repeats); + bitStream >>= 2 * repeats; + bitCount += 2 * repeats; + assert((bitStream & 3) < 3); + charnum += bitStream & 3; + bitCount += 2; + if (charnum >= maxSV1) + break; + if (ip <= iend - 7 || ip + (bitCount >> 3) <= iend - 4) + { + assert(bitCount >> 3 <= 3); + ip += bitCount >> 3; + bitCount &= 7; + } + else + { + bitCount -= (int)(8 * (iend - 4 - ip)); + bitCount &= 31; + ip = iend - 4; + } + + bitStream = MEM_readLE32(ip) >> bitCount; + } + + { + int max = 2 * threshold - 1 - remaining; + int count; + if ((bitStream & (uint)(threshold - 1)) < (uint)max) + { + count = (int)(bitStream & (uint)(threshold - 1)); + bitCount += nbBits - 1; + } + else + { + count = (int)(bitStream & (uint)(2 * threshold - 1)); + if (count >= threshold) + count -= max; + bitCount += nbBits; + } + + count--; + if (count >= 0) + { + remaining -= count; + } + else + { + assert(count == -1); + remaining += count; + } + + normalizedCounter[charnum++] = (short)count; + previous0 = count == 0 ? 1 : 0; + assert(threshold > 1); + if (remaining < threshold) + { + if (remaining <= 1) + break; + nbBits = (int)(ZSTD_highbit32((uint)remaining) + 1); + threshold = 1 << nbBits - 1; + } + + if (charnum >= maxSV1) + break; + if (ip <= iend - 7 || ip + (bitCount >> 3) <= iend - 4) + { + ip += bitCount >> 3; + bitCount &= 7; + } + else + { + bitCount -= (int)(8 * (iend - 4 - ip)); + bitCount &= 31; + ip = iend - 4; + } + + bitStream = MEM_readLE32(ip) >> bitCount; + } + } + + if (remaining != 1) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (charnum > maxSV1) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooSmall)); + if (bitCount > 32) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + *maxSVPtr = charnum - 1; + ip += bitCount + 7 >> 3; + return (nuint)(ip - istart); + } + + /* Avoids the FORCE_INLINE of the _body() function. */ + private static nuint FSE_readNCount_body_default( + short* normalizedCounter, + uint* maxSVPtr, + uint* tableLogPtr, + void* headerBuffer, + nuint hbSize + ) + { + return FSE_readNCount_body(normalizedCounter, maxSVPtr, tableLogPtr, headerBuffer, hbSize); + } + + /*! FSE_readNCount_bmi2(): + * Same as FSE_readNCount() but pass bmi2=1 when your CPU supports BMI2 and 0 otherwise. + */ + private static nuint FSE_readNCount_bmi2( + short* normalizedCounter, + uint* maxSVPtr, + uint* tableLogPtr, + void* headerBuffer, + nuint hbSize, + int bmi2 + ) + { + return FSE_readNCount_body_default( + normalizedCounter, + maxSVPtr, + tableLogPtr, + headerBuffer, + hbSize + ); + } + + /*! FSE_readNCount(): + Read compactly saved 'normalizedCounter' from 'rBuffer'. + @return : size read from 'rBuffer', + or an errorCode, which can be tested using FSE_isError(). + maxSymbolValuePtr[0] and tableLogPtr[0] will also be updated with their respective values */ + private static nuint FSE_readNCount( + short* normalizedCounter, + uint* maxSVPtr, + uint* tableLogPtr, + void* headerBuffer, + nuint hbSize + ) + { + return FSE_readNCount_bmi2( + normalizedCounter, + maxSVPtr, + tableLogPtr, + headerBuffer, + hbSize, + 0 + ); + } + + /*! HUF_readStats() : + Read compact Huffman tree, saved by HUF_writeCTable(). + `huffWeight` is destination buffer. + `rankStats` is assumed to be a table of at least HUF_TABLELOG_MAX U32. + @return : size read from `src` , or an error Code . + Note : Needed by HUF_readCTable() and HUF_readDTableX?() . + */ + private static nuint HUF_readStats( + byte* huffWeight, + nuint hwSize, + uint* rankStats, + uint* nbSymbolsPtr, + uint* tableLogPtr, + void* src, + nuint srcSize + ) + { + uint* wksp = stackalloc uint[219]; + return HUF_readStats_wksp( + huffWeight, + hwSize, + rankStats, + nbSymbolsPtr, + tableLogPtr, + src, + srcSize, + wksp, + sizeof(uint) * 219, + 0 + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint HUF_readStats_body( + byte* huffWeight, + nuint hwSize, + uint* rankStats, + uint* nbSymbolsPtr, + uint* tableLogPtr, + void* src, + nuint srcSize, + void* workSpace, + nuint wkspSize, + int bmi2 + ) + { + uint weightTotal; + byte* ip = (byte*)src; + nuint iSize; + nuint oSize; + if (srcSize == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + iSize = ip[0]; + if (iSize >= 128) + { + oSize = iSize - 127; + iSize = (oSize + 1) / 2; + if (iSize + 1 > srcSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + if (oSize >= hwSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + ip += 1; + { + uint n; + for (n = 0; n < oSize; n += 2) + { + huffWeight[n] = (byte)(ip[n / 2] >> 4); + huffWeight[n + 1] = (byte)(ip[n / 2] & 15); + } + } + } + else + { + if (iSize + 1 > srcSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + oSize = FSE_decompress_wksp_bmi2( + huffWeight, + hwSize - 1, + ip + 1, + iSize, + 6, + workSpace, + wkspSize, + bmi2 + ); + if (FSE_isError(oSize)) + return oSize; + } + + memset(rankStats, 0, (12 + 1) * sizeof(uint)); + weightTotal = 0; + { + uint n; + for (n = 0; n < oSize; n++) + { + if (huffWeight[n] > 12) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + rankStats[huffWeight[n]]++; + weightTotal += (uint)(1 << huffWeight[n] >> 1); + } + } + + if (weightTotal == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + { + uint tableLog = ZSTD_highbit32(weightTotal) + 1; + if (tableLog > 12) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + *tableLogPtr = tableLog; + { + uint total = (uint)(1 << (int)tableLog); + uint rest = total - weightTotal; + uint verif = (uint)(1 << (int)ZSTD_highbit32(rest)); + uint lastWeight = ZSTD_highbit32(rest) + 1; + if (verif != rest) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + huffWeight[oSize] = (byte)lastWeight; + rankStats[lastWeight]++; + } + } + + if (rankStats[1] < 2 || (rankStats[1] & 1) != 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + *nbSymbolsPtr = (uint)(oSize + 1); + return iSize + 1; + } + + /* Avoids the FORCE_INLINE of the _body() function. */ + private static nuint HUF_readStats_body_default( + byte* huffWeight, + nuint hwSize, + uint* rankStats, + uint* nbSymbolsPtr, + uint* tableLogPtr, + void* src, + nuint srcSize, + void* workSpace, + nuint wkspSize + ) + { + return HUF_readStats_body( + huffWeight, + hwSize, + rankStats, + nbSymbolsPtr, + tableLogPtr, + src, + srcSize, + workSpace, + wkspSize, + 0 + ); + } + + private static nuint HUF_readStats_wksp( + byte* huffWeight, + nuint hwSize, + uint* rankStats, + uint* nbSymbolsPtr, + uint* tableLogPtr, + void* src, + nuint srcSize, + void* workSpace, + nuint wkspSize, + int flags + ) + { + return HUF_readStats_body_default( + huffWeight, + hwSize, + rankStats, + nbSymbolsPtr, + tableLogPtr, + src, + srcSize, + workSpace, + wkspSize + ); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ErrorPrivate.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ErrorPrivate.cs new file mode 100644 index 00000000..e211bda7 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ErrorPrivate.cs @@ -0,0 +1,110 @@ +using System.Runtime.CompilerServices; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool ERR_isError(nuint code) + { + return code > unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxCode)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ZSTD_ErrorCode ERR_getErrorCode(nuint code) + { + if (!ERR_isError(code)) + return 0; + return (ZSTD_ErrorCode)(0 - code); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static string ERR_getErrorName(nuint code) + { + return ERR_getErrorString(ERR_getErrorCode(code)); + } + + /*-**************************************** + * Error Strings + ******************************************/ + private static string ERR_getErrorString(ZSTD_ErrorCode code) + { + const string notErrorCode = "Unspecified error code"; + switch (code) + { + case ZSTD_ErrorCode.ZSTD_error_no_error: + return "No error detected"; + case ZSTD_ErrorCode.ZSTD_error_GENERIC: + return "Error (generic)"; + case ZSTD_ErrorCode.ZSTD_error_prefix_unknown: + return "Unknown frame descriptor"; + case ZSTD_ErrorCode.ZSTD_error_version_unsupported: + return "Version not supported"; + case ZSTD_ErrorCode.ZSTD_error_frameParameter_unsupported: + return "Unsupported frame parameter"; + case ZSTD_ErrorCode.ZSTD_error_frameParameter_windowTooLarge: + return "Frame requires too much memory for decoding"; + case ZSTD_ErrorCode.ZSTD_error_corruption_detected: + return "Data corruption detected"; + case ZSTD_ErrorCode.ZSTD_error_checksum_wrong: + return "Restored data doesn't match checksum"; + case ZSTD_ErrorCode.ZSTD_error_literals_headerWrong: + return "Header of Literals' block doesn't respect format specification"; + case ZSTD_ErrorCode.ZSTD_error_parameter_unsupported: + return "Unsupported parameter"; + case ZSTD_ErrorCode.ZSTD_error_parameter_combination_unsupported: + return "Unsupported combination of parameters"; + case ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound: + return "Parameter is out of bound"; + case ZSTD_ErrorCode.ZSTD_error_init_missing: + return "Context should be init first"; + case ZSTD_ErrorCode.ZSTD_error_memory_allocation: + return "Allocation error : not enough memory"; + case ZSTD_ErrorCode.ZSTD_error_workSpace_tooSmall: + return "workSpace buffer is not large enough"; + case ZSTD_ErrorCode.ZSTD_error_stage_wrong: + return "Operation not authorized at current processing stage"; + case ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge: + return "tableLog requires too much memory : unsupported"; + case ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooLarge: + return "Unsupported max Symbol Value : too large"; + case ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooSmall: + return "Specified maxSymbolValue is too small"; + case ZSTD_ErrorCode.ZSTD_error_cannotProduce_uncompressedBlock: + return "This mode cannot generate an uncompressed block"; + case ZSTD_ErrorCode.ZSTD_error_stabilityCondition_notRespected: + return "pledged buffer stability condition is not respected"; + case ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted: + return "Dictionary is corrupted"; + case ZSTD_ErrorCode.ZSTD_error_dictionary_wrong: + return "Dictionary mismatch"; + case ZSTD_ErrorCode.ZSTD_error_dictionaryCreation_failed: + return "Cannot create Dictionary from provided samples"; + case ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall: + return "Destination buffer is too small"; + case ZSTD_ErrorCode.ZSTD_error_srcSize_wrong: + return "Src size is incorrect"; + case ZSTD_ErrorCode.ZSTD_error_dstBuffer_null: + return "Operation on NULL destination buffer"; + case ZSTD_ErrorCode.ZSTD_error_noForwardProgress_destFull: + return "Operation made no progress over multiple calls, due to output buffer being full"; + case ZSTD_ErrorCode.ZSTD_error_noForwardProgress_inputEmpty: + return "Operation made no progress over multiple calls, due to input being empty"; + case ZSTD_ErrorCode.ZSTD_error_frameIndex_tooLarge: + return "Frame index is too large"; + case ZSTD_ErrorCode.ZSTD_error_seekableIO: + return "An I/O error occurred when reading/seeking"; + case ZSTD_ErrorCode.ZSTD_error_dstBuffer_wrong: + return "Destination buffer is wrong"; + case ZSTD_ErrorCode.ZSTD_error_srcBuffer_wrong: + return "Source buffer is wrong"; + case ZSTD_ErrorCode.ZSTD_error_sequenceProducer_failed: + return "Block-level external sequence producer returned an error code"; + case ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid: + return "External sequences are not valid"; + case ZSTD_ErrorCode.ZSTD_error_maxCode: + default: + return notErrorCode; + } + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/EstimatedBlockSize.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/EstimatedBlockSize.cs new file mode 100644 index 00000000..a2f59adc --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/EstimatedBlockSize.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct EstimatedBlockSize +{ + public nuint estLitSize; + public nuint estBlockSize; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_accel_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_accel_t.cs new file mode 100644 index 00000000..c5d239fe --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_accel_t.cs @@ -0,0 +1,19 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*-************************************* + * Acceleration + ***************************************/ +public struct FASTCOVER_accel_t +{ + /* Percentage of training samples used for ZDICT_finalizeDictionary */ + public uint finalize; + + /* Number of dmer skipped between each dmer counted in computeFrequency */ + public uint skip; + + public FASTCOVER_accel_t(uint finalize, uint skip) + { + this.finalize = finalize; + this.skip = skip; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_ctx_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_ctx_t.cs new file mode 100644 index 00000000..8c79a30d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_ctx_t.cs @@ -0,0 +1,19 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*-************************************* + * Context + ***************************************/ +public unsafe struct FASTCOVER_ctx_t +{ + public byte* samples; + public nuint* offsets; + public nuint* samplesSizes; + public nuint nbSamples; + public nuint nbTrainSamples; + public nuint nbTestSamples; + public nuint nbDmers; + public uint* freqs; + public uint d; + public uint f; + public FASTCOVER_accel_t accelParams; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_tryParameters_data_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_tryParameters_data_s.cs new file mode 100644 index 00000000..3a6e14f0 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FASTCOVER_tryParameters_data_s.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * Parameters for FASTCOVER_tryParameters(). + */ +public unsafe struct FASTCOVER_tryParameters_data_s +{ + public FASTCOVER_ctx_t* ctx; + public COVER_best_s* best; + public nuint dictBufferCapacity; + public ZDICT_cover_params_t parameters; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FPStats.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FPStats.cs new file mode 100644 index 00000000..7a97d8be --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FPStats.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct FPStats +{ + public Fingerprint pastEvents; + public Fingerprint newEvents; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_CState_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_CState_t.cs new file mode 100644 index 00000000..dfdc049b --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_CState_t.cs @@ -0,0 +1,16 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* ***************************************** + * FSE symbol compression API + *******************************************/ +/*! +This API consists of small unitary functions, which highly benefit from being inlined. +Hence their body are included in next section. + */ +public unsafe struct FSE_CState_t +{ + public nint value; + public void* stateTable; + public void* symbolTT; + public uint stateLog; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DState_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DState_t.cs new file mode 100644 index 00000000..61e0fb68 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DState_t.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* ***************************************** + * FSE symbol decompression API + *******************************************/ +public unsafe struct FSE_DState_t +{ + public nuint state; + + /* precise table may vary, depending on U16 */ + public void* table; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DTableHeader.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DTableHeader.cs new file mode 100644 index 00000000..db091f0c --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DTableHeader.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* ====== Decompression ====== */ +public struct FSE_DTableHeader +{ + public ushort tableLog; + public ushort fastMode; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DecompressWksp.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DecompressWksp.cs new file mode 100644 index 00000000..cd03c36f --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_DecompressWksp.cs @@ -0,0 +1,6 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct FSE_DecompressWksp +{ + public fixed short ncount[256]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_decode_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_decode_t.cs new file mode 100644 index 00000000..66c49635 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_decode_t.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct FSE_decode_t +{ + public ushort newState; + public byte symbol; + public byte nbBits; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_repeat.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_repeat.cs new file mode 100644 index 00000000..6f2ceb53 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_repeat.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum FSE_repeat +{ + /**< Cannot use the previous table */ + FSE_repeat_none, + + /**< Can use the previous table but it must be checked */ + FSE_repeat_check, + + /**< Can use the previous table and it is assumed to be valid */ + FSE_repeat_valid, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_symbolCompressionTransform.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_symbolCompressionTransform.cs new file mode 100644 index 00000000..c4b43033 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FSE_symbolCompressionTransform.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* ***************************************** + * Implementation of inlined functions + *******************************************/ +public struct FSE_symbolCompressionTransform +{ + public int deltaFindState; + public uint deltaNbBits; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Fastcover.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Fastcover.cs new file mode 100644 index 00000000..2bd7e30c --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Fastcover.cs @@ -0,0 +1,761 @@ +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /*-************************************* + * Hash Functions + ***************************************/ + /** + * Hash the d-byte value pointed to by p and mod 2^f into the frequency vector + */ + private static nuint FASTCOVER_hashPtrToIndex(void* p, uint f, uint d) + { + if (d == 6) + { + return ZSTD_hash6Ptr(p, f); + } + + return ZSTD_hash8Ptr(p, f); + } + + private static readonly FASTCOVER_accel_t* FASTCOVER_defaultAccelParameters = GetArrayPointer( + new FASTCOVER_accel_t[11] + { + new FASTCOVER_accel_t(finalize: 100, skip: 0), + new FASTCOVER_accel_t(finalize: 100, skip: 0), + new FASTCOVER_accel_t(finalize: 50, skip: 1), + new FASTCOVER_accel_t(finalize: 34, skip: 2), + new FASTCOVER_accel_t(finalize: 25, skip: 3), + new FASTCOVER_accel_t(finalize: 20, skip: 4), + new FASTCOVER_accel_t(finalize: 17, skip: 5), + new FASTCOVER_accel_t(finalize: 14, skip: 6), + new FASTCOVER_accel_t(finalize: 13, skip: 7), + new FASTCOVER_accel_t(finalize: 11, skip: 8), + new FASTCOVER_accel_t(finalize: 10, skip: 9), + } + ); + + /*-************************************* + * Helper functions + ***************************************/ + /** + * Selects the best segment in an epoch. + * Segments of are scored according to the function: + * + * Let F(d) be the frequency of all dmers with hash value d. + * Let S_i be hash value of the dmer at position i of segment S which has length k. + * + * Score(S) = F(S_1) + F(S_2) + ... + F(S_{k-d+1}) + * + * Once the dmer with hash value d is in the dictionary we set F(d) = 0. + */ + private static COVER_segment_t FASTCOVER_selectSegment( + FASTCOVER_ctx_t* ctx, + uint* freqs, + uint begin, + uint end, + ZDICT_cover_params_t parameters, + ushort* segmentFreqs + ) + { + /* Constants */ + uint k = parameters.k; + uint d = parameters.d; + uint f = ctx->f; + uint dmersInK = k - d + 1; + /* Try each segment (activeSegment) and save the best (bestSegment) */ + COVER_segment_t bestSegment = new COVER_segment_t + { + begin = 0, + end = 0, + score = 0, + }; + COVER_segment_t activeSegment; + activeSegment.begin = begin; + activeSegment.end = begin; + activeSegment.score = 0; + while (activeSegment.end < end) + { + /* Get hash value of current dmer */ + nuint idx = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.end, f, d); + if (segmentFreqs[idx] == 0) + { + activeSegment.score += freqs[idx]; + } + + activeSegment.end += 1; + segmentFreqs[idx] += 1; + if (activeSegment.end - activeSegment.begin == dmersInK + 1) + { + /* Get hash value of the dmer to be eliminated from active segment */ + nuint delIndex = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.begin, f, d); + segmentFreqs[delIndex] -= 1; + if (segmentFreqs[delIndex] == 0) + { + activeSegment.score -= freqs[delIndex]; + } + + activeSegment.begin += 1; + } + + if (activeSegment.score > bestSegment.score) + { + bestSegment = activeSegment; + } + } + + while (activeSegment.begin < end) + { + nuint delIndex = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.begin, f, d); + segmentFreqs[delIndex] -= 1; + activeSegment.begin += 1; + } + + { + /* Zero the frequency of hash value of each dmer covered by the chosen segment. */ + uint pos; + for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) + { + nuint i = FASTCOVER_hashPtrToIndex(ctx->samples + pos, f, d); + freqs[i] = 0; + } + } + + return bestSegment; + } + + private static int FASTCOVER_checkParameters( + ZDICT_cover_params_t parameters, + nuint maxDictSize, + uint f, + uint accel + ) + { + if (parameters.d == 0 || parameters.k == 0) + { + return 0; + } + + if (parameters.d != 6 && parameters.d != 8) + { + return 0; + } + + if (parameters.k > maxDictSize) + { + return 0; + } + + if (parameters.d > parameters.k) + { + return 0; + } + + if (f > 31 || f == 0) + { + return 0; + } + + if (parameters.splitPoint <= 0 || parameters.splitPoint > 1) + { + return 0; + } + + if (accel > 10 || accel == 0) + { + return 0; + } + + return 1; + } + + /** + * Clean up a context initialized with `FASTCOVER_ctx_init()`. + */ + private static void FASTCOVER_ctx_destroy(FASTCOVER_ctx_t* ctx) + { + if (ctx == null) + return; + free(ctx->freqs); + ctx->freqs = null; + free(ctx->offsets); + ctx->offsets = null; + } + + /** + * Calculate for frequency of hash value of each dmer in ctx->samples + */ + private static void FASTCOVER_computeFrequency(uint* freqs, FASTCOVER_ctx_t* ctx) + { + uint f = ctx->f; + uint d = ctx->d; + uint skip = ctx->accelParams.skip; + uint readLength = d > 8 ? d : 8; + nuint i; + assert(ctx->nbTrainSamples >= 5); + assert(ctx->nbTrainSamples <= ctx->nbSamples); + for (i = 0; i < ctx->nbTrainSamples; i++) + { + /* start of current dmer */ + nuint start = ctx->offsets[i]; + nuint currSampleEnd = ctx->offsets[i + 1]; + while (start + readLength <= currSampleEnd) + { + nuint dmerIndex = FASTCOVER_hashPtrToIndex(ctx->samples + start, f, d); + freqs[dmerIndex]++; + start = start + skip + 1; + } + } + } + + /** + * Prepare a context for dictionary building. + * The context is only dependent on the parameter `d` and can be used multiple + * times. + * Returns 0 on success or error code on error. + * The context must be destroyed with `FASTCOVER_ctx_destroy()`. + */ + private static nuint FASTCOVER_ctx_init( + FASTCOVER_ctx_t* ctx, + void* samplesBuffer, + nuint* samplesSizes, + uint nbSamples, + uint d, + double splitPoint, + uint f, + FASTCOVER_accel_t accelParams + ) + { + byte* samples = (byte*)samplesBuffer; + nuint totalSamplesSize = COVER_sum(samplesSizes, nbSamples); + /* Split samples into testing and training sets */ + uint nbTrainSamples = splitPoint < 1 ? (uint)(nbSamples * splitPoint) : nbSamples; + uint nbTestSamples = splitPoint < 1 ? nbSamples - nbTrainSamples : nbSamples; + nuint trainingSamplesSize = + splitPoint < 1 ? COVER_sum(samplesSizes, nbTrainSamples) : totalSamplesSize; + nuint testSamplesSize = + splitPoint < 1 + ? COVER_sum(samplesSizes + nbTrainSamples, nbTestSamples) + : totalSamplesSize; + if ( + totalSamplesSize < (d > sizeof(ulong) ? d : sizeof(ulong)) + || totalSamplesSize >= (sizeof(nuint) == 8 ? unchecked((uint)-1) : 1 * (1U << 30)) + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if (nbTrainSamples < 5) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if (nbTestSamples < 1) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + *ctx = new FASTCOVER_ctx_t + { + samples = samples, + samplesSizes = samplesSizes, + nbSamples = nbSamples, + nbTrainSamples = nbTrainSamples, + nbTestSamples = nbTestSamples, + nbDmers = trainingSamplesSize - (d > sizeof(ulong) ? d : sizeof(ulong)) + 1, + d = d, + f = f, + accelParams = accelParams, + offsets = (nuint*)calloc(nbSamples + 1, (ulong)sizeof(nuint)), + }; + if (ctx->offsets == null) + { + FASTCOVER_ctx_destroy(ctx); + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + { + uint i; + ctx->offsets[0] = 0; + assert(nbSamples >= 5); + for (i = 1; i <= nbSamples; ++i) + { + ctx->offsets[i] = ctx->offsets[i - 1] + samplesSizes[i - 1]; + } + } + + ctx->freqs = (uint*)calloc((ulong)1 << (int)f, sizeof(uint)); + if (ctx->freqs == null) + { + FASTCOVER_ctx_destroy(ctx); + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + FASTCOVER_computeFrequency(ctx->freqs, ctx); + return 0; + } + + /** + * Given the prepared context build the dictionary. + */ + private static nuint FASTCOVER_buildDictionary( + FASTCOVER_ctx_t* ctx, + uint* freqs, + void* dictBuffer, + nuint dictBufferCapacity, + ZDICT_cover_params_t parameters, + ushort* segmentFreqs + ) + { + byte* dict = (byte*)dictBuffer; + nuint tail = dictBufferCapacity; + /* Divide the data into epochs. We will select one segment from each epoch. */ + COVER_epoch_info_t epochs = COVER_computeEpochs( + (uint)dictBufferCapacity, + (uint)ctx->nbDmers, + parameters.k, + 1 + ); + const nuint maxZeroScoreRun = 10; + nuint zeroScoreRun = 0; + nuint epoch; + for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs.num) + { + uint epochBegin = (uint)(epoch * epochs.size); + uint epochEnd = epochBegin + epochs.size; + nuint segmentSize; + /* Select a segment */ + COVER_segment_t segment = FASTCOVER_selectSegment( + ctx, + freqs, + epochBegin, + epochEnd, + parameters, + segmentFreqs + ); + if (segment.score == 0) + { + if (++zeroScoreRun >= maxZeroScoreRun) + { + break; + } + + continue; + } + + zeroScoreRun = 0; + segmentSize = + segment.end - segment.begin + parameters.d - 1 < tail + ? segment.end - segment.begin + parameters.d - 1 + : tail; + if (segmentSize < parameters.d) + { + break; + } + + tail -= segmentSize; + memcpy(dict + tail, ctx->samples + segment.begin, (uint)segmentSize); + } + + return tail; + } + + /** + * Tries a set of parameters and updates the COVER_best_t with the results. + * This function is thread safe if zstd is compiled with multithreaded support. + * It takes its parameters as an *OWNING* opaque pointer to support threading. + */ + private static void FASTCOVER_tryParameters(void* opaque) + { + /* Save parameters as local variables */ + FASTCOVER_tryParameters_data_s* data = (FASTCOVER_tryParameters_data_s*)opaque; + FASTCOVER_ctx_t* ctx = data->ctx; + ZDICT_cover_params_t parameters = data->parameters; + nuint dictBufferCapacity = data->dictBufferCapacity; + nuint totalCompressedSize = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + /* Initialize array to keep track of frequency of dmer within activeSegment */ + ushort* segmentFreqs = (ushort*)calloc((ulong)1 << (int)ctx->f, sizeof(ushort)); + /* Allocate space for hash table, dict, and freqs */ + byte* dict = (byte*)malloc(dictBufferCapacity); + COVER_dictSelection selection = COVER_dictSelectionError( + unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)) + ); + uint* freqs = (uint*)malloc(((ulong)1 << (int)ctx->f) * sizeof(uint)); + if (segmentFreqs == null || dict == null || freqs == null) + { + goto _cleanup; + } + + memcpy(freqs, ctx->freqs, (uint)(((ulong)1 << (int)ctx->f) * sizeof(uint))); + { + nuint tail = FASTCOVER_buildDictionary( + ctx, + freqs, + dict, + dictBufferCapacity, + parameters, + segmentFreqs + ); + uint nbFinalizeSamples = (uint)(ctx->nbTrainSamples * ctx->accelParams.finalize / 100); + selection = COVER_selectDict( + dict + tail, + dictBufferCapacity, + dictBufferCapacity - tail, + ctx->samples, + ctx->samplesSizes, + nbFinalizeSamples, + ctx->nbTrainSamples, + ctx->nbSamples, + parameters, + ctx->offsets, + totalCompressedSize + ); + if (COVER_dictSelectionIsError(selection) != 0) + { + goto _cleanup; + } + } + + _cleanup: + free(dict); + COVER_best_finish(data->best, parameters, selection); + free(data); + free(segmentFreqs); + COVER_dictSelectionFree(selection); + free(freqs); + } + + private static void FASTCOVER_convertToCoverParams( + ZDICT_fastCover_params_t fastCoverParams, + ZDICT_cover_params_t* coverParams + ) + { + coverParams->k = fastCoverParams.k; + coverParams->d = fastCoverParams.d; + coverParams->steps = fastCoverParams.steps; + coverParams->nbThreads = fastCoverParams.nbThreads; + coverParams->splitPoint = fastCoverParams.splitPoint; + coverParams->zParams = fastCoverParams.zParams; + coverParams->shrinkDict = fastCoverParams.shrinkDict; + } + + private static void FASTCOVER_convertToFastCoverParams( + ZDICT_cover_params_t coverParams, + ZDICT_fastCover_params_t* fastCoverParams, + uint f, + uint accel + ) + { + fastCoverParams->k = coverParams.k; + fastCoverParams->d = coverParams.d; + fastCoverParams->steps = coverParams.steps; + fastCoverParams->nbThreads = coverParams.nbThreads; + fastCoverParams->splitPoint = coverParams.splitPoint; + fastCoverParams->f = f; + fastCoverParams->accel = accel; + fastCoverParams->zParams = coverParams.zParams; + fastCoverParams->shrinkDict = coverParams.shrinkDict; + } + + /*! ZDICT_trainFromBuffer_fastCover(): + * Train a dictionary from an array of samples using a modified version of COVER algorithm. + * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, + * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. + * d and k are required. + * All other parameters are optional, will use default values if not provided + * The resulting dictionary will be saved into `dictBuffer`. + * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) + * or an error code, which can be tested with ZDICT_isError(). + * See ZDICT_trainFromBuffer() for details on failure modes. + * Note: ZDICT_trainFromBuffer_fastCover() requires 6 * 2^f bytes of memory. + * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. + * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`. + * In general, it's recommended to provide a few thousands samples, though this can vary a lot. + * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. + */ + public static nuint ZDICT_trainFromBuffer_fastCover( + void* dictBuffer, + nuint dictBufferCapacity, + void* samplesBuffer, + nuint* samplesSizes, + uint nbSamples, + ZDICT_fastCover_params_t parameters + ) + { + byte* dict = (byte*)dictBuffer; + FASTCOVER_ctx_t ctx; + ZDICT_cover_params_t coverParams; + FASTCOVER_accel_t accelParams; + g_displayLevel = (int)parameters.zParams.notificationLevel; + parameters.splitPoint = 1; + parameters.f = parameters.f == 0 ? 20 : parameters.f; + parameters.accel = parameters.accel == 0 ? 1 : parameters.accel; + coverParams = new ZDICT_cover_params_t(); + FASTCOVER_convertToCoverParams(parameters, &coverParams); + if ( + FASTCOVER_checkParameters( + coverParams, + dictBufferCapacity, + parameters.f, + parameters.accel + ) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + if (nbSamples == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if (dictBufferCapacity < 256) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + accelParams = FASTCOVER_defaultAccelParameters[parameters.accel]; + { + nuint initVal = FASTCOVER_ctx_init( + &ctx, + samplesBuffer, + samplesSizes, + nbSamples, + coverParams.d, + parameters.splitPoint, + parameters.f, + accelParams + ); + if (ERR_isError(initVal)) + { + return initVal; + } + } + + COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.nbDmers, g_displayLevel); + { + /* Initialize array to keep track of frequency of dmer within activeSegment */ + ushort* segmentFreqs = (ushort*)calloc((ulong)1 << (int)parameters.f, sizeof(ushort)); + nuint tail = FASTCOVER_buildDictionary( + &ctx, + ctx.freqs, + dictBuffer, + dictBufferCapacity, + coverParams, + segmentFreqs + ); + uint nbFinalizeSamples = (uint)(ctx.nbTrainSamples * ctx.accelParams.finalize / 100); + nuint dictionarySize = ZDICT_finalizeDictionary( + dict, + dictBufferCapacity, + dict + tail, + dictBufferCapacity - tail, + samplesBuffer, + samplesSizes, + nbFinalizeSamples, + coverParams.zParams + ); + if (!ERR_isError(dictionarySize)) { } + + FASTCOVER_ctx_destroy(&ctx); + free(segmentFreqs); + return dictionarySize; + } + } + + /*! ZDICT_optimizeTrainFromBuffer_fastCover(): + * The same requirements as above hold for all the parameters except `parameters`. + * This function tries many parameter combinations (specifically, k and d combinations) + * and picks the best parameters. `*parameters` is filled with the best parameters found, + * dictionary constructed with those parameters is stored in `dictBuffer`. + * All of the parameters d, k, steps, f, and accel are optional. + * If d is non-zero then we don't check multiple values of d, otherwise we check d = {6, 8}. + * if steps is zero it defaults to its default value. + * If k is non-zero then we don't check multiple values of k, otherwise we check steps values in [50, 2000]. + * If f is zero, default value of 20 is used. + * If accel is zero, default value of 1 is used. + * + * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) + * or an error code, which can be tested with ZDICT_isError(). + * On success `*parameters` contains the parameters selected. + * See ZDICT_trainFromBuffer() for details on failure modes. + * Note: ZDICT_optimizeTrainFromBuffer_fastCover() requires about 6 * 2^f bytes of memory for each thread. + */ + public static nuint ZDICT_optimizeTrainFromBuffer_fastCover( + void* dictBuffer, + nuint dictBufferCapacity, + void* samplesBuffer, + nuint* samplesSizes, + uint nbSamples, + ZDICT_fastCover_params_t* parameters + ) + { + ZDICT_cover_params_t coverParams; + FASTCOVER_accel_t accelParams; + /* constants */ + uint nbThreads = parameters->nbThreads; + double splitPoint = parameters->splitPoint <= 0 ? 0.75 : parameters->splitPoint; + uint kMinD = parameters->d == 0 ? 6 : parameters->d; + uint kMaxD = parameters->d == 0 ? 8 : parameters->d; + uint kMinK = parameters->k == 0 ? 50 : parameters->k; + uint kMaxK = parameters->k == 0 ? 2000 : parameters->k; + uint kSteps = parameters->steps == 0 ? 40 : parameters->steps; + uint kStepSize = (kMaxK - kMinK) / kSteps > 1 ? (kMaxK - kMinK) / kSteps : 1; + uint kIterations = (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize); + uint f = parameters->f == 0 ? 20 : parameters->f; + uint accel = parameters->accel == 0 ? 1 : parameters->accel; + const uint shrinkDict = 0; + /* Local variables */ + int displayLevel = (int)parameters->zParams.notificationLevel; + uint iteration = 1; + uint d; + uint k; + COVER_best_s best; + void* pool = null; + int warned = 0; + if (splitPoint <= 0 || splitPoint > 1) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + if (accel == 0 || accel > 10) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + if (kMinK < kMaxD || kMaxK < kMinK) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + if (nbSamples == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if (dictBufferCapacity < 256) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (nbThreads > 1) + { + pool = POOL_create(nbThreads, 1); + if (pool == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + } + + COVER_best_init(&best); + coverParams = new ZDICT_cover_params_t(); + FASTCOVER_convertToCoverParams(*parameters, &coverParams); + accelParams = FASTCOVER_defaultAccelParameters[accel]; + g_displayLevel = displayLevel == 0 ? 0 : displayLevel - 1; + for (d = kMinD; d <= kMaxD; d += 2) + { + /* Initialize the context for this value of d */ + FASTCOVER_ctx_t ctx; + { + nuint initVal = FASTCOVER_ctx_init( + &ctx, + samplesBuffer, + samplesSizes, + nbSamples, + d, + splitPoint, + f, + accelParams + ); + if (ERR_isError(initVal)) + { + COVER_best_destroy(&best); + POOL_free(pool); + return initVal; + } + } + + if (warned == 0) + { + COVER_warnOnSmallCorpus(dictBufferCapacity, ctx.nbDmers, displayLevel); + warned = 1; + } + + for (k = kMinK; k <= kMaxK; k += kStepSize) + { + /* Prepare the arguments */ + FASTCOVER_tryParameters_data_s* data = (FASTCOVER_tryParameters_data_s*)malloc( + (ulong)sizeof(FASTCOVER_tryParameters_data_s) + ); + if (data == null) + { + COVER_best_destroy(&best); + FASTCOVER_ctx_destroy(&ctx); + POOL_free(pool); + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + data->ctx = &ctx; + data->best = &best; + data->dictBufferCapacity = dictBufferCapacity; + data->parameters = coverParams; + data->parameters.k = k; + data->parameters.d = d; + data->parameters.splitPoint = splitPoint; + data->parameters.steps = kSteps; + data->parameters.shrinkDict = shrinkDict; + data->parameters.zParams.notificationLevel = (uint)g_displayLevel; + if ( + FASTCOVER_checkParameters( + data->parameters, + dictBufferCapacity, + data->ctx->f, + accel + ) == 0 + ) + { + free(data); + continue; + } + + COVER_best_start(&best); + if (pool != null) + { + POOL_add( + pool, + (delegate* managed)(&FASTCOVER_tryParameters), + data + ); + } + else + { + FASTCOVER_tryParameters(data); + } + + ++iteration; + } + + COVER_best_wait(&best); + FASTCOVER_ctx_destroy(&ctx); + } + + { + nuint dictSize = best.dictSize; + if (ERR_isError(best.compressedSize)) + { + nuint compressedSize = best.compressedSize; + COVER_best_destroy(&best); + POOL_free(pool); + return compressedSize; + } + + FASTCOVER_convertToFastCoverParams(best.parameters, parameters, f, accel); + memcpy(dictBuffer, best.dict, (uint)dictSize); + COVER_best_destroy(&best); + POOL_free(pool); + return dictSize; + } + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Fingerprint.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Fingerprint.cs new file mode 100644 index 00000000..c4877235 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Fingerprint.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct Fingerprint +{ + public fixed uint events[1024]; + public nuint nbEvents; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Fse.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Fse.cs new file mode 100644 index 00000000..32798dfe --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Fse.cs @@ -0,0 +1,198 @@ +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void FSE_initCState(FSE_CState_t* statePtr, uint* ct) + { + void* ptr = ct; + ushort* u16ptr = (ushort*)ptr; + uint tableLog = MEM_read16(ptr); + statePtr->value = (nint)1 << (int)tableLog; + statePtr->stateTable = u16ptr + 2; + statePtr->symbolTT = ct + 1 + (tableLog != 0 ? 1 << (int)(tableLog - 1) : 1); + statePtr->stateLog = tableLog; + } + + /*! FSE_initCState2() : + * Same as FSE_initCState(), but the first symbol to include (which will be the last to be read) + * uses the smallest state value possible, saving the cost of this symbol */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void FSE_initCState2(ref FSE_CState_t statePtr, uint* ct, uint symbol) + { + FSE_initCState(ref statePtr, ct); + { + FSE_symbolCompressionTransform symbolTT = ( + (FSE_symbolCompressionTransform*)statePtr.symbolTT + )[symbol]; + ushort* stateTable = (ushort*)statePtr.stateTable; + uint nbBitsOut = symbolTT.deltaNbBits + (1 << 15) >> 16; + statePtr.value = (nint)((nbBitsOut << 16) - symbolTT.deltaNbBits); + statePtr.value = stateTable[ + (statePtr.value >> (int)nbBitsOut) + symbolTT.deltaFindState + ]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void FSE_encodeSymbol( + ref nuint bitC_bitContainer, + ref uint bitC_bitPos, + ref FSE_CState_t statePtr, + uint symbol + ) + { + FSE_symbolCompressionTransform symbolTT = ( + (FSE_symbolCompressionTransform*)statePtr.symbolTT + )[symbol]; + ushort* stateTable = (ushort*)statePtr.stateTable; + uint nbBitsOut = (uint)statePtr.value + symbolTT.deltaNbBits >> 16; + BIT_addBits(ref bitC_bitContainer, ref bitC_bitPos, (nuint)statePtr.value, nbBitsOut); + statePtr.value = stateTable[(statePtr.value >> (int)nbBitsOut) + symbolTT.deltaFindState]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void FSE_flushCState( + ref nuint bitC_bitContainer, + ref uint bitC_bitPos, + ref sbyte* bitC_ptr, + sbyte* bitC_endPtr, + ref FSE_CState_t statePtr + ) + { + BIT_addBits( + ref bitC_bitContainer, + ref bitC_bitPos, + (nuint)statePtr.value, + statePtr.stateLog + ); + BIT_flushBits(ref bitC_bitContainer, ref bitC_bitPos, ref bitC_ptr, bitC_endPtr); + } + + /* FSE_getMaxNbBits() : + * Approximate maximum cost of a symbol, in bits. + * Fractional get rounded up (i.e. a symbol with a normalized frequency of 3 gives the same result as a frequency of 2) + * note 1 : assume symbolValue is valid (<= maxSymbolValue) + * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint FSE_getMaxNbBits(void* symbolTTPtr, uint symbolValue) + { + FSE_symbolCompressionTransform* symbolTT = (FSE_symbolCompressionTransform*)symbolTTPtr; + return symbolTT[symbolValue].deltaNbBits + ((1 << 16) - 1) >> 16; + } + + /* FSE_bitCost() : + * Approximate symbol cost, as fractional value, using fixed-point format (accuracyLog fractional bits) + * note 1 : assume symbolValue is valid (<= maxSymbolValue) + * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint FSE_bitCost( + void* symbolTTPtr, + uint tableLog, + uint symbolValue, + uint accuracyLog + ) + { + FSE_symbolCompressionTransform* symbolTT = (FSE_symbolCompressionTransform*)symbolTTPtr; + uint minNbBits = symbolTT[symbolValue].deltaNbBits >> 16; + uint threshold = minNbBits + 1 << 16; + assert(tableLog < 16); + assert(accuracyLog < 31 - tableLog); + { + uint tableSize = (uint)(1 << (int)tableLog); + uint deltaFromThreshold = threshold - (symbolTT[symbolValue].deltaNbBits + tableSize); + /* linear interpolation (very approximate) */ + uint normalizedDeltaFromThreshold = + deltaFromThreshold << (int)accuracyLog >> (int)tableLog; + uint bitMultiplier = (uint)(1 << (int)accuracyLog); + assert(symbolTT[symbolValue].deltaNbBits + tableSize <= threshold); + assert(normalizedDeltaFromThreshold <= bitMultiplier); + return (minNbBits + 1) * bitMultiplier - normalizedDeltaFromThreshold; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void FSE_initDState(ref FSE_DState_t DStatePtr, ref BIT_DStream_t bitD, uint* dt) + { + void* ptr = dt; + FSE_DTableHeader* DTableH = (FSE_DTableHeader*)ptr; + DStatePtr.state = BIT_readBits(bitD.bitContainer, ref bitD.bitsConsumed, DTableH->tableLog); + BIT_reloadDStream( + ref bitD.bitContainer, + ref bitD.bitsConsumed, + ref bitD.ptr, + bitD.start, + bitD.limitPtr + ); + DStatePtr.table = dt + 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte FSE_peekSymbol(FSE_DState_t* DStatePtr) + { + FSE_decode_t DInfo = ((FSE_decode_t*)DStatePtr->table)[DStatePtr->state]; + return DInfo.symbol; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void FSE_updateState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD) + { + FSE_decode_t DInfo = ((FSE_decode_t*)DStatePtr->table)[DStatePtr->state]; + uint nbBits = DInfo.nbBits; + nuint lowBits = BIT_readBits(bitD, nbBits); + DStatePtr->state = DInfo.newState + lowBits; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte FSE_decodeSymbol( + ref FSE_DState_t DStatePtr, + nuint bitD_bitContainer, + ref uint bitD_bitsConsumed + ) + { + FSE_decode_t DInfo = ((FSE_decode_t*)DStatePtr.table)[DStatePtr.state]; + uint nbBits = DInfo.nbBits; + byte symbol = DInfo.symbol; + nuint lowBits = BIT_readBits(bitD_bitContainer, ref bitD_bitsConsumed, nbBits); + DStatePtr.state = DInfo.newState + lowBits; + return symbol; + } + + /*! FSE_decodeSymbolFast() : + unsafe, only works if no symbol has a probability > 50% */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte FSE_decodeSymbolFast( + ref FSE_DState_t DStatePtr, + nuint bitD_bitContainer, + ref uint bitD_bitsConsumed + ) + { + FSE_decode_t DInfo = ((FSE_decode_t*)DStatePtr.table)[DStatePtr.state]; + uint nbBits = DInfo.nbBits; + byte symbol = DInfo.symbol; + nuint lowBits = BIT_readBitsFast(bitD_bitContainer, ref bitD_bitsConsumed, nbBits); + DStatePtr.state = DInfo.newState + lowBits; + return symbol; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint FSE_endOfDState(FSE_DState_t* DStatePtr) + { + return DStatePtr->state == 0 ? 1U : 0U; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void FSE_initCState(ref FSE_CState_t statePtr, uint* ct) + { + void* ptr = ct; + ushort* u16ptr = (ushort*)ptr; + uint tableLog = MEM_read16(ptr); + statePtr.value = (nint)1 << (int)tableLog; + statePtr.stateTable = u16ptr + 2; + statePtr.symbolTT = ct + 1 + (tableLog != 0 ? 1 << (int)(tableLog - 1) : 1); + statePtr.stateLog = tableLog; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FseCompress.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FseCompress.cs new file mode 100644 index 00000000..c4c763a1 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FseCompress.cs @@ -0,0 +1,782 @@ +using System; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /* FSE_buildCTable_wksp() : + * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`). + * wkspSize should be sized to handle worst case situation, which is `1<> 1 : 1); + FSE_symbolCompressionTransform* symbolTT = (FSE_symbolCompressionTransform*)FSCT; + uint step = (tableSize >> 1) + (tableSize >> 3) + 3; + uint maxSV1 = maxSymbolValue + 1; + /* size = maxSV1 */ + ushort* cumul = (ushort*)workSpace; + /* size = tableSize */ + byte* tableSymbol = (byte*)(cumul + (maxSV1 + 1)); + uint highThreshold = tableSize - 1; + assert(((nuint)workSpace & 1) == 0); + if ( + sizeof(uint) + * ((maxSymbolValue + 2 + (1UL << (int)tableLog)) / 2 + sizeof(ulong) / sizeof(uint)) + > wkspSize + ) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + tableU16[-2] = (ushort)tableLog; + tableU16[-1] = (ushort)maxSymbolValue; + assert(tableLog < 16); + { + uint u; + cumul[0] = 0; + for (u = 1; u <= maxSV1; u++) + { + if (normalizedCounter[u - 1] == -1) + { + cumul[u] = (ushort)(cumul[u - 1] + 1); + tableSymbol[highThreshold--] = (byte)(u - 1); + } + else + { + assert(normalizedCounter[u - 1] >= 0); + cumul[u] = (ushort)(cumul[u - 1] + (ushort)normalizedCounter[u - 1]); + assert(cumul[u] >= cumul[u - 1]); + } + } + + cumul[maxSV1] = (ushort)(tableSize + 1); + } + + if (highThreshold == tableSize - 1) + { + /* size = tableSize + 8 (may write beyond tableSize) */ + byte* spread = tableSymbol + tableSize; + { + const ulong add = 0x0101010101010101UL; + nuint pos = 0; + ulong sv = 0; + uint s; + for (s = 0; s < maxSV1; ++s, sv += add) + { + int i; + int n = normalizedCounter[s]; + MEM_write64(spread + pos, sv); + for (i = 8; i < n; i += 8) + { + MEM_write64(spread + pos + i, sv); + } + + assert(n >= 0); + pos += (nuint)n; + } + } + + { + nuint position = 0; + nuint s; + /* Experimentally determined optimal unroll */ + const nuint unroll = 2; + assert(tableSize % unroll == 0); + for (s = 0; s < tableSize; s += unroll) + { + nuint u; + for (u = 0; u < unroll; ++u) + { + nuint uPosition = position + u * step & tableMask; + tableSymbol[uPosition] = spread[s + u]; + } + + position = position + unroll * step & tableMask; + } + + assert(position == 0); + } + } + else + { + uint position = 0; + uint symbol; + for (symbol = 0; symbol < maxSV1; symbol++) + { + int nbOccurrences; + int freq = normalizedCounter[symbol]; + for (nbOccurrences = 0; nbOccurrences < freq; nbOccurrences++) + { + tableSymbol[position] = (byte)symbol; + position = position + step & tableMask; + while (position > highThreshold) + position = position + step & tableMask; + } + } + + assert(position == 0); + } + + { + uint u; + for (u = 0; u < tableSize; u++) + { + /* note : static analyzer may not understand tableSymbol is properly initialized */ + byte s = tableSymbol[u]; + tableU16[cumul[s]++] = (ushort)(tableSize + u); + } + } + + { + uint total = 0; + uint s; + for (s = 0; s <= maxSymbolValue; s++) + { + switch (normalizedCounter[s]) + { + case 0: + symbolTT[s].deltaNbBits = (tableLog + 1 << 16) - (uint)(1 << (int)tableLog); + break; + case -1: + case 1: + symbolTT[s].deltaNbBits = (tableLog << 16) - (uint)(1 << (int)tableLog); + assert(total <= 2147483647); + symbolTT[s].deltaFindState = (int)(total - 1); + total++; + break; + default: + assert(normalizedCounter[s] > 1); + + { + uint maxBitsOut = + tableLog - ZSTD_highbit32((uint)normalizedCounter[s] - 1); + uint minStatePlus = (uint)normalizedCounter[s] << (int)maxBitsOut; + symbolTT[s].deltaNbBits = (maxBitsOut << 16) - minStatePlus; + symbolTT[s].deltaFindState = (int)(total - (uint)normalizedCounter[s]); + total += (uint)normalizedCounter[s]; + } + + break; + } + } + } + + return 0; + } + + /*-************************************************************** + * FSE NCount encoding + ****************************************************************/ + private static nuint FSE_NCountWriteBound(uint maxSymbolValue, uint tableLog) + { + nuint maxHeaderSize = ((maxSymbolValue + 1) * tableLog + 4 + 2) / 8 + 1 + 2; + return maxSymbolValue != 0 ? maxHeaderSize : 512; + } + + private static nuint FSE_writeNCount_generic( + void* header, + nuint headerBufferSize, + short* normalizedCounter, + uint maxSymbolValue, + uint tableLog, + uint writeIsSafe + ) + { + byte* ostart = (byte*)header; + byte* @out = ostart; + byte* oend = ostart + headerBufferSize; + int nbBits; + int tableSize = 1 << (int)tableLog; + int remaining; + int threshold; + uint bitStream = 0; + int bitCount = 0; + uint symbol = 0; + uint alphabetSize = maxSymbolValue + 1; + int previousIs0 = 0; + bitStream += tableLog - 5 << bitCount; + bitCount += 4; + remaining = tableSize + 1; + threshold = tableSize; + nbBits = (int)tableLog + 1; + while (symbol < alphabetSize && remaining > 1) + { + if (previousIs0 != 0) + { + uint start = symbol; + while (symbol < alphabetSize && normalizedCounter[symbol] == 0) + symbol++; + if (symbol == alphabetSize) + break; + while (symbol >= start + 24) + { + start += 24; + bitStream += 0xFFFFU << bitCount; + if (writeIsSafe == 0 && @out > oend - 2) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + @out[0] = (byte)bitStream; + @out[1] = (byte)(bitStream >> 8); + @out += 2; + bitStream >>= 16; + } + + while (symbol >= start + 3) + { + start += 3; + bitStream += 3U << bitCount; + bitCount += 2; + } + + bitStream += symbol - start << bitCount; + bitCount += 2; + if (bitCount > 16) + { + if (writeIsSafe == 0 && @out > oend - 2) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + @out[0] = (byte)bitStream; + @out[1] = (byte)(bitStream >> 8); + @out += 2; + bitStream >>= 16; + bitCount -= 16; + } + } + + { + int count = normalizedCounter[symbol++]; + int max = 2 * threshold - 1 - remaining; + remaining -= count < 0 ? -count : count; + count++; + if (count >= threshold) + count += max; + bitStream += (uint)count << bitCount; + bitCount += nbBits; + bitCount -= count < max ? 1 : 0; + previousIs0 = count == 1 ? 1 : 0; + if (remaining < 1) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + while (remaining < threshold) + { + nbBits--; + threshold >>= 1; + } + } + + if (bitCount > 16) + { + if (writeIsSafe == 0 && @out > oend - 2) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + @out[0] = (byte)bitStream; + @out[1] = (byte)(bitStream >> 8); + @out += 2; + bitStream >>= 16; + bitCount -= 16; + } + } + + if (remaining != 1) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + assert(symbol <= alphabetSize); + if (writeIsSafe == 0 && @out > oend - 2) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + @out[0] = (byte)bitStream; + @out[1] = (byte)(bitStream >> 8); + @out += (bitCount + 7) / 8; + assert(@out >= ostart); + return (nuint)(@out - ostart); + } + + /*! FSE_writeNCount(): + Compactly save 'normalizedCounter' into 'buffer'. + @return : size of the compressed table, + or an errorCode, which can be tested using FSE_isError(). */ + private static nuint FSE_writeNCount( + void* buffer, + nuint bufferSize, + short* normalizedCounter, + uint maxSymbolValue, + uint tableLog + ) + { + if (tableLog > 14 - 2) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + if (tableLog < 5) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + if (bufferSize < FSE_NCountWriteBound(maxSymbolValue, tableLog)) + return FSE_writeNCount_generic( + buffer, + bufferSize, + normalizedCounter, + maxSymbolValue, + tableLog, + 0 + ); + return FSE_writeNCount_generic( + buffer, + bufferSize, + normalizedCounter, + maxSymbolValue, + tableLog, + 1 + ); + } + + /* provides the minimum logSize to safely represent a distribution */ + private static uint FSE_minTableLog(nuint srcSize, uint maxSymbolValue) + { + uint minBitsSrc = ZSTD_highbit32((uint)srcSize) + 1; + uint minBitsSymbols = ZSTD_highbit32(maxSymbolValue) + 2; + uint minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols; + assert(srcSize > 1); + return minBits; + } + + /* ***************************************** + * FSE advanced API + ***************************************** */ + private static uint FSE_optimalTableLog_internal( + uint maxTableLog, + nuint srcSize, + uint maxSymbolValue, + uint minus + ) + { + uint maxBitsSrc = ZSTD_highbit32((uint)(srcSize - 1)) - minus; + uint tableLog = maxTableLog; + uint minBits = FSE_minTableLog(srcSize, maxSymbolValue); + assert(srcSize > 1); + if (tableLog == 0) + tableLog = 13 - 2; + if (maxBitsSrc < tableLog) + tableLog = maxBitsSrc; + if (minBits > tableLog) + tableLog = minBits; + if (tableLog < 5) + tableLog = 5; + if (tableLog > 14 - 2) + tableLog = 14 - 2; + return tableLog; + } + + /*! FSE_optimalTableLog(): + dynamically downsize 'tableLog' when conditions are met. + It saves CPU time, by using smaller tables, while preserving or even improving compression ratio. + @return : recommended tableLog (necessarily <= 'maxTableLog') */ + private static uint FSE_optimalTableLog(uint maxTableLog, nuint srcSize, uint maxSymbolValue) + { + return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 2); + } + + /* Secondary normalization method. + To be used when primary method fails. */ + private static nuint FSE_normalizeM2( + short* norm, + uint tableLog, + uint* count, + nuint total, + uint maxSymbolValue, + short lowProbCount + ) + { + const short NOT_YET_ASSIGNED = -2; + uint s; + uint distributed = 0; + uint ToDistribute; + /* Init */ + uint lowThreshold = (uint)(total >> (int)tableLog); + uint lowOne = (uint)(total * 3 >> (int)(tableLog + 1)); + for (s = 0; s <= maxSymbolValue; s++) + { + if (count[s] == 0) + { + norm[s] = 0; + continue; + } + + if (count[s] <= lowThreshold) + { + norm[s] = lowProbCount; + distributed++; + total -= count[s]; + continue; + } + + if (count[s] <= lowOne) + { + norm[s] = 1; + distributed++; + total -= count[s]; + continue; + } + + norm[s] = NOT_YET_ASSIGNED; + } + + ToDistribute = (uint)(1 << (int)tableLog) - distributed; + if (ToDistribute == 0) + return 0; + if (total / ToDistribute > lowOne) + { + lowOne = (uint)(total * 3 / (ToDistribute * 2)); + for (s = 0; s <= maxSymbolValue; s++) + { + if (norm[s] == NOT_YET_ASSIGNED && count[s] <= lowOne) + { + norm[s] = 1; + distributed++; + total -= count[s]; + continue; + } + } + + ToDistribute = (uint)(1 << (int)tableLog) - distributed; + } + + if (distributed == maxSymbolValue + 1) + { + /* all values are pretty poor; + probably incompressible data (should have already been detected); + find max, then give all remaining points to max */ + uint maxV = 0, + maxC = 0; + for (s = 0; s <= maxSymbolValue; s++) + if (count[s] > maxC) + { + maxV = s; + maxC = count[s]; + } + + norm[maxV] += (short)ToDistribute; + return 0; + } + + if (total == 0) + { + for (s = 0; ToDistribute > 0; s = (s + 1) % (maxSymbolValue + 1)) + if (norm[s] > 0) + { + ToDistribute--; + norm[s]++; + } + + return 0; + } + + { + ulong vStepLog = 62 - tableLog; + ulong mid = (1UL << (int)(vStepLog - 1)) - 1; + /* scale on remaining */ + ulong rStep = (((ulong)1 << (int)vStepLog) * ToDistribute + mid) / (uint)total; + ulong tmpTotal = mid; + for (s = 0; s <= maxSymbolValue; s++) + { + if (norm[s] == NOT_YET_ASSIGNED) + { + ulong end = tmpTotal + count[s] * rStep; + uint sStart = (uint)(tmpTotal >> (int)vStepLog); + uint sEnd = (uint)(end >> (int)vStepLog); + uint weight = sEnd - sStart; + if (weight < 1) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + norm[s] = (short)weight; + tmpTotal = end; + } + } + } + + return 0; + } + +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_rtbTable => + new uint[8] { 0, 473195, 504333, 520860, 550000, 700000, 750000, 830000 }; + private static uint* rtbTable => + (uint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_rtbTable) + ); +#else + + private static readonly uint* rtbTable = GetArrayPointer( + new uint[8] { 0, 473195, 504333, 520860, 550000, 700000, 750000, 830000 } + ); +#endif + /*! FSE_normalizeCount(): + normalize counts so that sum(count[]) == Power_of_2 (2^tableLog) + 'normalizedCounter' is a table of short, of minimum size (maxSymbolValue+1). + useLowProbCount is a boolean parameter which trades off compressed size for + faster header decoding. When it is set to 1, the compressed data will be slightly + smaller. And when it is set to 0, FSE_readNCount() and FSE_buildDTable() will be + faster. If you are compressing a small amount of data (< 2 KB) then useLowProbCount=0 + is a good default, since header deserialization makes a big speed difference. + Otherwise, useLowProbCount=1 is a good default, since the speed difference is small. + @return : tableLog, + or an errorCode, which can be tested using FSE_isError() */ + private static nuint FSE_normalizeCount( + short* normalizedCounter, + uint tableLog, + uint* count, + nuint total, + uint maxSymbolValue, + uint useLowProbCount + ) + { + if (tableLog == 0) + tableLog = 13 - 2; + if (tableLog < 5) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + if (tableLog > 14 - 2) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + if (tableLog < FSE_minTableLog(total, maxSymbolValue)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + { + short lowProbCount = (short)(useLowProbCount != 0 ? -1 : 1); + ulong scale = 62 - tableLog; + /* <== here, one division ! */ + ulong step = ((ulong)1 << 62) / (uint)total; + ulong vStep = 1UL << (int)(scale - 20); + int stillToDistribute = 1 << (int)tableLog; + uint s; + uint largest = 0; + short largestP = 0; + uint lowThreshold = (uint)(total >> (int)tableLog); + for (s = 0; s <= maxSymbolValue; s++) + { + if (count[s] == total) + return 0; + if (count[s] == 0) + { + normalizedCounter[s] = 0; + continue; + } + + if (count[s] <= lowThreshold) + { + normalizedCounter[s] = lowProbCount; + stillToDistribute--; + } + else + { + short proba = (short)(count[s] * step >> (int)scale); + if (proba < 8) + { + ulong restToBeat = vStep * rtbTable[proba]; + proba += (short)( + count[s] * step - ((ulong)proba << (int)scale) > restToBeat ? 1 : 0 + ); + } + + if (proba > largestP) + { + largestP = proba; + largest = s; + } + + normalizedCounter[s] = proba; + stillToDistribute -= proba; + } + } + + if (-stillToDistribute >= normalizedCounter[largest] >> 1) + { + /* corner case, need another normalization method */ + nuint errorCode = FSE_normalizeM2( + normalizedCounter, + tableLog, + count, + total, + maxSymbolValue, + lowProbCount + ); + if (ERR_isError(errorCode)) + return errorCode; + } + else + normalizedCounter[largest] += (short)stillToDistribute; + } + + return tableLog; + } + + /* fake FSE_CTable, for rle input (always same symbol) */ + private static nuint FSE_buildCTable_rle(uint* ct, byte symbolValue) + { + void* ptr = ct; + ushort* tableU16 = (ushort*)ptr + 2; + void* FSCTptr = (uint*)ptr + 2; + FSE_symbolCompressionTransform* symbolTT = (FSE_symbolCompressionTransform*)FSCTptr; + tableU16[-2] = 0; + tableU16[-1] = symbolValue; + tableU16[0] = 0; + tableU16[1] = 0; + symbolTT[symbolValue].deltaNbBits = 0; + symbolTT[symbolValue].deltaFindState = 0; + return 0; + } + + private static nuint FSE_compress_usingCTable_generic( + void* dst, + nuint dstSize, + void* src, + nuint srcSize, + uint* ct, + uint fast + ) + { + byte* istart = (byte*)src; + byte* iend = istart + srcSize; + byte* ip = iend; + BIT_CStream_t bitC; + System.Runtime.CompilerServices.Unsafe.SkipInit(out bitC); + FSE_CState_t CState1, + CState2; + System.Runtime.CompilerServices.Unsafe.SkipInit(out CState1); + System.Runtime.CompilerServices.Unsafe.SkipInit(out CState2); + if (srcSize <= 2) + return 0; + { + nuint initError = BIT_initCStream(ref bitC, dst, dstSize); + if (ERR_isError(initError)) + return 0; + } + + nuint bitC_bitContainer = bitC.bitContainer; + uint bitC_bitPos = bitC.bitPos; + sbyte* bitC_ptr = bitC.ptr; + sbyte* bitC_endPtr = bitC.endPtr; + if ((srcSize & 1) != 0) + { + FSE_initCState2(ref CState1, ct, *--ip); + FSE_initCState2(ref CState2, ct, *--ip); + FSE_encodeSymbol(ref bitC_bitContainer, ref bitC_bitPos, ref CState1, *--ip); + if (fast != 0) + BIT_flushBitsFast( + ref bitC_bitContainer, + ref bitC_bitPos, + ref bitC_ptr, + bitC_endPtr + ); + else + BIT_flushBits(ref bitC_bitContainer, ref bitC_bitPos, ref bitC_ptr, bitC_endPtr); + } + else + { + FSE_initCState2(ref CState2, ct, *--ip); + FSE_initCState2(ref CState1, ct, *--ip); + } + + srcSize -= 2; + if (sizeof(nuint) * 8 > (14 - 2) * 4 + 7 && (srcSize & 2) != 0) + { + FSE_encodeSymbol(ref bitC_bitContainer, ref bitC_bitPos, ref CState2, *--ip); + FSE_encodeSymbol(ref bitC_bitContainer, ref bitC_bitPos, ref CState1, *--ip); + if (fast != 0) + BIT_flushBitsFast( + ref bitC_bitContainer, + ref bitC_bitPos, + ref bitC_ptr, + bitC_endPtr + ); + else + BIT_flushBits(ref bitC_bitContainer, ref bitC_bitPos, ref bitC_ptr, bitC_endPtr); + } + + while (ip > istart) + { + FSE_encodeSymbol(ref bitC_bitContainer, ref bitC_bitPos, ref CState2, *--ip); + if (sizeof(nuint) * 8 < (14 - 2) * 2 + 7) + if (fast != 0) + BIT_flushBitsFast( + ref bitC_bitContainer, + ref bitC_bitPos, + ref bitC_ptr, + bitC_endPtr + ); + else + BIT_flushBits( + ref bitC_bitContainer, + ref bitC_bitPos, + ref bitC_ptr, + bitC_endPtr + ); + FSE_encodeSymbol(ref bitC_bitContainer, ref bitC_bitPos, ref CState1, *--ip); + if (sizeof(nuint) * 8 > (14 - 2) * 4 + 7) + { + FSE_encodeSymbol(ref bitC_bitContainer, ref bitC_bitPos, ref CState2, *--ip); + FSE_encodeSymbol(ref bitC_bitContainer, ref bitC_bitPos, ref CState1, *--ip); + } + + if (fast != 0) + BIT_flushBitsFast( + ref bitC_bitContainer, + ref bitC_bitPos, + ref bitC_ptr, + bitC_endPtr + ); + else + BIT_flushBits(ref bitC_bitContainer, ref bitC_bitPos, ref bitC_ptr, bitC_endPtr); + } + + FSE_flushCState( + ref bitC_bitContainer, + ref bitC_bitPos, + ref bitC_ptr, + bitC_endPtr, + ref CState2 + ); + FSE_flushCState( + ref bitC_bitContainer, + ref bitC_bitPos, + ref bitC_ptr, + bitC_endPtr, + ref CState1 + ); + return BIT_closeCStream( + ref bitC_bitContainer, + ref bitC_bitPos, + bitC_ptr, + bitC_endPtr, + bitC.startPtr + ); + } + + /*! FSE_compress_usingCTable(): + Compress `src` using `ct` into `dst` which must be already allocated. + @return : size of compressed data (<= `dstCapacity`), + or 0 if compressed data could not fit into `dst`, + or an errorCode, which can be tested using FSE_isError() */ + private static nuint FSE_compress_usingCTable( + void* dst, + nuint dstSize, + void* src, + nuint srcSize, + uint* ct + ) + { + uint fast = dstSize >= srcSize + (srcSize >> 7) + 4 + (nuint)sizeof(nuint) ? 1U : 0U; + if (fast != 0) + return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 1); + else + return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 0); + } + + /*-***************************************** + * Tool functions + ******************************************/ + private static nuint FSE_compressBound(nuint size) + { + return 512 + (size + (size >> 7) + 4 + (nuint)sizeof(nuint)); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/FseDecompress.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/FseDecompress.cs new file mode 100644 index 00000000..12be4ef6 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/FseDecompress.cs @@ -0,0 +1,462 @@ +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + private static nuint FSE_buildDTable_internal( + uint* dt, + short* normalizedCounter, + uint maxSymbolValue, + uint tableLog, + void* workSpace, + nuint wkspSize + ) + { + /* because *dt is unsigned, 32-bits aligned on 32-bits */ + void* tdPtr = dt + 1; + FSE_decode_t* tableDecode = (FSE_decode_t*)tdPtr; + ushort* symbolNext = (ushort*)workSpace; + byte* spread = (byte*)(symbolNext + maxSymbolValue + 1); + uint maxSV1 = maxSymbolValue + 1; + uint tableSize = (uint)(1 << (int)tableLog); + uint highThreshold = tableSize - 1; + if (sizeof(short) * (maxSymbolValue + 1) + (1UL << (int)tableLog) + 8 > wkspSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooLarge)); + if (maxSymbolValue > 255) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooLarge)); + if (tableLog > 14 - 2) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + { + FSE_DTableHeader DTableH; + DTableH.tableLog = (ushort)tableLog; + DTableH.fastMode = 1; + { + short largeLimit = (short)(1 << (int)(tableLog - 1)); + uint s; + for (s = 0; s < maxSV1; s++) + { + if (normalizedCounter[s] == -1) + { + tableDecode[highThreshold--].symbol = (byte)s; + symbolNext[s] = 1; + } + else + { + if (normalizedCounter[s] >= largeLimit) + DTableH.fastMode = 0; + symbolNext[s] = (ushort)normalizedCounter[s]; + } + } + } + + memcpy(dt, &DTableH, (uint)sizeof(FSE_DTableHeader)); + } + + if (highThreshold == tableSize - 1) + { + nuint tableMask = tableSize - 1; + nuint step = (tableSize >> 1) + (tableSize >> 3) + 3; + { + const ulong add = 0x0101010101010101UL; + nuint pos = 0; + ulong sv = 0; + uint s; + for (s = 0; s < maxSV1; ++s, sv += add) + { + int i; + int n = normalizedCounter[s]; + MEM_write64(spread + pos, sv); + for (i = 8; i < n; i += 8) + { + MEM_write64(spread + pos + i, sv); + } + + pos += (nuint)n; + } + } + + { + nuint position = 0; + nuint s; + const nuint unroll = 2; + assert(tableSize % unroll == 0); + for (s = 0; s < tableSize; s += unroll) + { + nuint u; + for (u = 0; u < unroll; ++u) + { + nuint uPosition = position + u * step & tableMask; + tableDecode[uPosition].symbol = spread[s + u]; + } + + position = position + unroll * step & tableMask; + } + + assert(position == 0); + } + } + else + { + uint tableMask = tableSize - 1; + uint step = (tableSize >> 1) + (tableSize >> 3) + 3; + uint s, + position = 0; + for (s = 0; s < maxSV1; s++) + { + int i; + for (i = 0; i < normalizedCounter[s]; i++) + { + tableDecode[position].symbol = (byte)s; + position = position + step & tableMask; + while (position > highThreshold) + position = position + step & tableMask; + } + } + + if (position != 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + + { + uint u; + for (u = 0; u < tableSize; u++) + { + byte symbol = tableDecode[u].symbol; + uint nextState = symbolNext[symbol]++; + tableDecode[u].nbBits = (byte)(tableLog - ZSTD_highbit32(nextState)); + tableDecode[u].newState = (ushort)( + (nextState << tableDecode[u].nbBits) - tableSize + ); + } + } + + return 0; + } + + private static nuint FSE_buildDTable_wksp( + uint* dt, + short* normalizedCounter, + uint maxSymbolValue, + uint tableLog, + void* workSpace, + nuint wkspSize + ) + { + return FSE_buildDTable_internal( + dt, + normalizedCounter, + maxSymbolValue, + tableLog, + workSpace, + wkspSize + ); + } + + /*-******************************************************* + * Decompression (Byte symbols) + *********************************************************/ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint FSE_decompress_usingDTable_generic( + void* dst, + nuint maxDstSize, + void* cSrc, + nuint cSrcSize, + uint* dt, + uint fast + ) + { + byte* ostart = (byte*)dst; + byte* op = ostart; + byte* omax = op + maxDstSize; + byte* olimit = omax - 3; + BIT_DStream_t bitD; + System.Runtime.CompilerServices.Unsafe.SkipInit(out bitD); + FSE_DState_t state1; + System.Runtime.CompilerServices.Unsafe.SkipInit(out state1); + FSE_DState_t state2; + System.Runtime.CompilerServices.Unsafe.SkipInit(out state2); + { + /* Init */ + nuint _var_err__ = BIT_initDStream(ref bitD, cSrc, cSrcSize); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + FSE_initDState(ref state1, ref bitD, dt); + FSE_initDState(ref state2, ref bitD, dt); + nuint bitD_bitContainer = bitD.bitContainer; + uint bitD_bitsConsumed = bitD.bitsConsumed; + sbyte* bitD_ptr = bitD.ptr; + sbyte* bitD_start = bitD.start; + sbyte* bitD_limitPtr = bitD.limitPtr; + if ( + BIT_reloadDStream( + ref bitD_bitContainer, + ref bitD_bitsConsumed, + ref bitD_ptr, + bitD_start, + bitD_limitPtr + ) == BIT_DStream_status.BIT_DStream_overflow + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + for ( + ; + BIT_reloadDStream( + ref bitD_bitContainer, + ref bitD_bitsConsumed, + ref bitD_ptr, + bitD_start, + bitD_limitPtr + ) == BIT_DStream_status.BIT_DStream_unfinished + && op < olimit; + op += 4 + ) + { + op[0] = + fast != 0 + ? FSE_decodeSymbolFast(ref state1, bitD_bitContainer, ref bitD_bitsConsumed) + : FSE_decodeSymbol(ref state1, bitD_bitContainer, ref bitD_bitsConsumed); + if ((14 - 2) * 2 + 7 > sizeof(nuint) * 8) + BIT_reloadDStream( + ref bitD_bitContainer, + ref bitD_bitsConsumed, + ref bitD_ptr, + bitD_start, + bitD_limitPtr + ); + op[1] = + fast != 0 + ? FSE_decodeSymbolFast(ref state2, bitD_bitContainer, ref bitD_bitsConsumed) + : FSE_decodeSymbol(ref state2, bitD_bitContainer, ref bitD_bitsConsumed); + if ((14 - 2) * 4 + 7 > sizeof(nuint) * 8) + { + if ( + BIT_reloadDStream( + ref bitD_bitContainer, + ref bitD_bitsConsumed, + ref bitD_ptr, + bitD_start, + bitD_limitPtr + ) > BIT_DStream_status.BIT_DStream_unfinished + ) + { + op += 2; + break; + } + } + + op[2] = + fast != 0 + ? FSE_decodeSymbolFast(ref state1, bitD_bitContainer, ref bitD_bitsConsumed) + : FSE_decodeSymbol(ref state1, bitD_bitContainer, ref bitD_bitsConsumed); + if ((14 - 2) * 2 + 7 > sizeof(nuint) * 8) + BIT_reloadDStream( + ref bitD_bitContainer, + ref bitD_bitsConsumed, + ref bitD_ptr, + bitD_start, + bitD_limitPtr + ); + op[3] = + fast != 0 + ? FSE_decodeSymbolFast(ref state2, bitD_bitContainer, ref bitD_bitsConsumed) + : FSE_decodeSymbol(ref state2, bitD_bitContainer, ref bitD_bitsConsumed); + } + + while (true) + { + if (op > omax - 2) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + *op++ = + fast != 0 + ? FSE_decodeSymbolFast(ref state1, bitD_bitContainer, ref bitD_bitsConsumed) + : FSE_decodeSymbol(ref state1, bitD_bitContainer, ref bitD_bitsConsumed); + if ( + BIT_reloadDStream( + ref bitD_bitContainer, + ref bitD_bitsConsumed, + ref bitD_ptr, + bitD_start, + bitD_limitPtr + ) == BIT_DStream_status.BIT_DStream_overflow + ) + { + *op++ = + fast != 0 + ? FSE_decodeSymbolFast(ref state2, bitD_bitContainer, ref bitD_bitsConsumed) + : FSE_decodeSymbol(ref state2, bitD_bitContainer, ref bitD_bitsConsumed); + break; + } + + if (op > omax - 2) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + *op++ = + fast != 0 + ? FSE_decodeSymbolFast(ref state2, bitD_bitContainer, ref bitD_bitsConsumed) + : FSE_decodeSymbol(ref state2, bitD_bitContainer, ref bitD_bitsConsumed); + if ( + BIT_reloadDStream( + ref bitD_bitContainer, + ref bitD_bitsConsumed, + ref bitD_ptr, + bitD_start, + bitD_limitPtr + ) == BIT_DStream_status.BIT_DStream_overflow + ) + { + *op++ = + fast != 0 + ? FSE_decodeSymbolFast(ref state1, bitD_bitContainer, ref bitD_bitsConsumed) + : FSE_decodeSymbol(ref state1, bitD_bitContainer, ref bitD_bitsConsumed); + break; + } + } + + assert(op >= ostart); + return (nuint)(op - ostart); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint FSE_decompress_wksp_body( + void* dst, + nuint dstCapacity, + void* cSrc, + nuint cSrcSize, + uint maxLog, + void* workSpace, + nuint wkspSize, + int bmi2 + ) + { + byte* istart = (byte*)cSrc; + byte* ip = istart; + uint tableLog; + uint maxSymbolValue = 255; + FSE_DecompressWksp* wksp = (FSE_DecompressWksp*)workSpace; + nuint dtablePos = (nuint)(sizeof(FSE_DecompressWksp) / sizeof(uint)); + uint* dtable = (uint*)workSpace + dtablePos; + if (wkspSize < (nuint)sizeof(FSE_DecompressWksp)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + { + nuint NCountLength = FSE_readNCount_bmi2( + wksp->ncount, + &maxSymbolValue, + &tableLog, + istart, + cSrcSize, + bmi2 + ); + if (ERR_isError(NCountLength)) + return NCountLength; + if (tableLog > maxLog) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + assert(NCountLength <= cSrcSize); + ip += NCountLength; + cSrcSize -= NCountLength; + } + + if ( + ( + (ulong)(1 + (1 << (int)tableLog) + 1) + + ( + sizeof(short) * (maxSymbolValue + 1) + + (1UL << (int)tableLog) + + 8 + + sizeof(uint) + - 1 + ) / sizeof(uint) + + (255 + 1) / 2 + + 1 + ) * sizeof(uint) + > wkspSize + ) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + assert( + (nuint)(sizeof(FSE_DecompressWksp) + (1 + (1 << (int)tableLog)) * sizeof(uint)) + <= wkspSize + ); + workSpace = + (byte*)workSpace + + sizeof(FSE_DecompressWksp) + + (1 + (1 << (int)tableLog)) * sizeof(uint); + wkspSize -= (nuint)(sizeof(FSE_DecompressWksp) + (1 + (1 << (int)tableLog)) * sizeof(uint)); + { + nuint _var_err__ = FSE_buildDTable_internal( + dtable, + wksp->ncount, + maxSymbolValue, + tableLog, + workSpace, + wkspSize + ); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + { + void* ptr = dtable; + FSE_DTableHeader* DTableH = (FSE_DTableHeader*)ptr; + uint fastMode = DTableH->fastMode; + if (fastMode != 0) + return FSE_decompress_usingDTable_generic( + dst, + dstCapacity, + ip, + cSrcSize, + dtable, + 1 + ); + return FSE_decompress_usingDTable_generic(dst, dstCapacity, ip, cSrcSize, dtable, 0); + } + } + + /* Avoids the FORCE_INLINE of the _body() function. */ + private static nuint FSE_decompress_wksp_body_default( + void* dst, + nuint dstCapacity, + void* cSrc, + nuint cSrcSize, + uint maxLog, + void* workSpace, + nuint wkspSize + ) + { + return FSE_decompress_wksp_body( + dst, + dstCapacity, + cSrc, + cSrcSize, + maxLog, + workSpace, + wkspSize, + 0 + ); + } + + private static nuint FSE_decompress_wksp_bmi2( + void* dst, + nuint dstCapacity, + void* cSrc, + nuint cSrcSize, + uint maxLog, + void* workSpace, + nuint wkspSize, + int bmi2 + ) + { + return FSE_decompress_wksp_body_default( + dst, + dstCapacity, + cSrc, + cSrcSize, + maxLog, + workSpace, + wkspSize + ); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HIST_checkInput_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HIST_checkInput_e.cs new file mode 100644 index 00000000..8aca6e8f --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HIST_checkInput_e.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum HIST_checkInput_e +{ + trustInput, + checkMaxSymbolValue, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CStream_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CStream_t.cs new file mode 100644 index 00000000..b86d99f3 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CStream_t.cs @@ -0,0 +1,22 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct HUF_CStream_t +{ + public _bitContainer_e__FixedBuffer bitContainer; + public _bitPos_e__FixedBuffer bitPos; + public byte* startPtr; + public byte* ptr; + public byte* endPtr; + + public unsafe struct _bitContainer_e__FixedBuffer + { + public nuint e0; + public nuint e1; + } + + public unsafe struct _bitPos_e__FixedBuffer + { + public nuint e0; + public nuint e1; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CTableHeader.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CTableHeader.cs new file mode 100644 index 00000000..cc542493 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CTableHeader.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct HUF_CTableHeader +{ + public byte tableLog; + public byte maxSymbolValue; + public fixed byte unused[6]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CompressWeightsWksp.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CompressWeightsWksp.cs new file mode 100644 index 00000000..789f4e7f --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_CompressWeightsWksp.cs @@ -0,0 +1,9 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct HUF_CompressWeightsWksp +{ + public fixed uint CTable[59]; + public fixed uint scratchBuffer[41]; + public fixed uint count[13]; + public fixed short norm[13]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DEltX1.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DEltX1.cs new file mode 100644 index 00000000..94d25d9d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DEltX1.cs @@ -0,0 +1,11 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*-***************************/ +/* single-symbol decoding */ +/*-***************************/ +public struct HUF_DEltX1 +{ + /* single-symbol decoding */ + public byte nbBits; + public byte @byte; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DEltX2.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DEltX2.cs new file mode 100644 index 00000000..0ab3f1d4 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DEltX2.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* *************************/ +/* double-symbols decoding */ +/* *************************/ +public struct HUF_DEltX2 +{ + /* double-symbols decoding */ + public ushort sequence; + public byte nbBits; + public byte length; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DecompressFastArgs.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DecompressFastArgs.cs new file mode 100644 index 00000000..c9b8d2d5 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_DecompressFastArgs.cs @@ -0,0 +1,49 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * The input/output arguments to the Huffman fast decoding loop: + * + * ip [in/out] - The input pointers, must be updated to reflect what is consumed. + * op [in/out] - The output pointers, must be updated to reflect what is written. + * bits [in/out] - The bitstream containers, must be updated to reflect the current state. + * dt [in] - The decoding table. + * ilowest [in] - The beginning of the valid range of the input. Decoders may read + * down to this pointer. It may be below iend[0]. + * oend [in] - The end of the output stream. op[3] must not cross oend. + * iend [in] - The end of each input stream. ip[i] may cross iend[i], + * as long as it is above ilowest, but that indicates corruption. + */ +public unsafe struct HUF_DecompressFastArgs +{ + public _ip_e__FixedBuffer ip; + public _op_e__FixedBuffer op; + public fixed ulong bits[4]; + public void* dt; + public byte* ilowest; + public byte* oend; + public _iend_e__FixedBuffer iend; + + public unsafe struct _ip_e__FixedBuffer + { + public byte* e0; + public byte* e1; + public byte* e2; + public byte* e3; + } + + public unsafe struct _op_e__FixedBuffer + { + public byte* e0; + public byte* e1; + public byte* e2; + public byte* e3; + } + + public unsafe struct _iend_e__FixedBuffer + { + public byte* e0; + public byte* e1; + public byte* e2; + public byte* e3; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_ReadDTableX1_Workspace.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_ReadDTableX1_Workspace.cs new file mode 100644 index 00000000..529a3e5e --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_ReadDTableX1_Workspace.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct HUF_ReadDTableX1_Workspace +{ + public fixed uint rankVal[13]; + public fixed uint rankStart[13]; + public fixed uint statsWksp[219]; + public fixed byte symbols[256]; + public fixed byte huffWeight[256]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_ReadDTableX2_Workspace.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_ReadDTableX2_Workspace.cs new file mode 100644 index 00000000..a85bbb6c --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_ReadDTableX2_Workspace.cs @@ -0,0 +1,307 @@ +using System.Runtime.CompilerServices; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct HUF_ReadDTableX2_Workspace +{ + public _rankVal_e__FixedBuffer rankVal; + public fixed uint rankStats[13]; + public fixed uint rankStart0[15]; + public _sortedSymbol_e__FixedBuffer sortedSymbol; + public fixed byte weightList[256]; + public fixed uint calleeWksp[219]; + +#if NET8_0_OR_GREATER + [InlineArray(12)] + public unsafe struct _rankVal_e__FixedBuffer + { + public rankValCol_t e0; + } + +#else + public unsafe struct _rankVal_e__FixedBuffer + { + public rankValCol_t e0; + public rankValCol_t e1; + public rankValCol_t e2; + public rankValCol_t e3; + public rankValCol_t e4; + public rankValCol_t e5; + public rankValCol_t e6; + public rankValCol_t e7; + public rankValCol_t e8; + public rankValCol_t e9; + public rankValCol_t e10; + public rankValCol_t e11; + } +#endif + +#if NET8_0_OR_GREATER + [InlineArray(256)] + public unsafe struct _sortedSymbol_e__FixedBuffer + { + public sortedSymbol_t e0; + } + +#else + public unsafe struct _sortedSymbol_e__FixedBuffer + { + public sortedSymbol_t e0; + public sortedSymbol_t e1; + public sortedSymbol_t e2; + public sortedSymbol_t e3; + public sortedSymbol_t e4; + public sortedSymbol_t e5; + public sortedSymbol_t e6; + public sortedSymbol_t e7; + public sortedSymbol_t e8; + public sortedSymbol_t e9; + public sortedSymbol_t e10; + public sortedSymbol_t e11; + public sortedSymbol_t e12; + public sortedSymbol_t e13; + public sortedSymbol_t e14; + public sortedSymbol_t e15; + public sortedSymbol_t e16; + public sortedSymbol_t e17; + public sortedSymbol_t e18; + public sortedSymbol_t e19; + public sortedSymbol_t e20; + public sortedSymbol_t e21; + public sortedSymbol_t e22; + public sortedSymbol_t e23; + public sortedSymbol_t e24; + public sortedSymbol_t e25; + public sortedSymbol_t e26; + public sortedSymbol_t e27; + public sortedSymbol_t e28; + public sortedSymbol_t e29; + public sortedSymbol_t e30; + public sortedSymbol_t e31; + public sortedSymbol_t e32; + public sortedSymbol_t e33; + public sortedSymbol_t e34; + public sortedSymbol_t e35; + public sortedSymbol_t e36; + public sortedSymbol_t e37; + public sortedSymbol_t e38; + public sortedSymbol_t e39; + public sortedSymbol_t e40; + public sortedSymbol_t e41; + public sortedSymbol_t e42; + public sortedSymbol_t e43; + public sortedSymbol_t e44; + public sortedSymbol_t e45; + public sortedSymbol_t e46; + public sortedSymbol_t e47; + public sortedSymbol_t e48; + public sortedSymbol_t e49; + public sortedSymbol_t e50; + public sortedSymbol_t e51; + public sortedSymbol_t e52; + public sortedSymbol_t e53; + public sortedSymbol_t e54; + public sortedSymbol_t e55; + public sortedSymbol_t e56; + public sortedSymbol_t e57; + public sortedSymbol_t e58; + public sortedSymbol_t e59; + public sortedSymbol_t e60; + public sortedSymbol_t e61; + public sortedSymbol_t e62; + public sortedSymbol_t e63; + public sortedSymbol_t e64; + public sortedSymbol_t e65; + public sortedSymbol_t e66; + public sortedSymbol_t e67; + public sortedSymbol_t e68; + public sortedSymbol_t e69; + public sortedSymbol_t e70; + public sortedSymbol_t e71; + public sortedSymbol_t e72; + public sortedSymbol_t e73; + public sortedSymbol_t e74; + public sortedSymbol_t e75; + public sortedSymbol_t e76; + public sortedSymbol_t e77; + public sortedSymbol_t e78; + public sortedSymbol_t e79; + public sortedSymbol_t e80; + public sortedSymbol_t e81; + public sortedSymbol_t e82; + public sortedSymbol_t e83; + public sortedSymbol_t e84; + public sortedSymbol_t e85; + public sortedSymbol_t e86; + public sortedSymbol_t e87; + public sortedSymbol_t e88; + public sortedSymbol_t e89; + public sortedSymbol_t e90; + public sortedSymbol_t e91; + public sortedSymbol_t e92; + public sortedSymbol_t e93; + public sortedSymbol_t e94; + public sortedSymbol_t e95; + public sortedSymbol_t e96; + public sortedSymbol_t e97; + public sortedSymbol_t e98; + public sortedSymbol_t e99; + public sortedSymbol_t e100; + public sortedSymbol_t e101; + public sortedSymbol_t e102; + public sortedSymbol_t e103; + public sortedSymbol_t e104; + public sortedSymbol_t e105; + public sortedSymbol_t e106; + public sortedSymbol_t e107; + public sortedSymbol_t e108; + public sortedSymbol_t e109; + public sortedSymbol_t e110; + public sortedSymbol_t e111; + public sortedSymbol_t e112; + public sortedSymbol_t e113; + public sortedSymbol_t e114; + public sortedSymbol_t e115; + public sortedSymbol_t e116; + public sortedSymbol_t e117; + public sortedSymbol_t e118; + public sortedSymbol_t e119; + public sortedSymbol_t e120; + public sortedSymbol_t e121; + public sortedSymbol_t e122; + public sortedSymbol_t e123; + public sortedSymbol_t e124; + public sortedSymbol_t e125; + public sortedSymbol_t e126; + public sortedSymbol_t e127; + public sortedSymbol_t e128; + public sortedSymbol_t e129; + public sortedSymbol_t e130; + public sortedSymbol_t e131; + public sortedSymbol_t e132; + public sortedSymbol_t e133; + public sortedSymbol_t e134; + public sortedSymbol_t e135; + public sortedSymbol_t e136; + public sortedSymbol_t e137; + public sortedSymbol_t e138; + public sortedSymbol_t e139; + public sortedSymbol_t e140; + public sortedSymbol_t e141; + public sortedSymbol_t e142; + public sortedSymbol_t e143; + public sortedSymbol_t e144; + public sortedSymbol_t e145; + public sortedSymbol_t e146; + public sortedSymbol_t e147; + public sortedSymbol_t e148; + public sortedSymbol_t e149; + public sortedSymbol_t e150; + public sortedSymbol_t e151; + public sortedSymbol_t e152; + public sortedSymbol_t e153; + public sortedSymbol_t e154; + public sortedSymbol_t e155; + public sortedSymbol_t e156; + public sortedSymbol_t e157; + public sortedSymbol_t e158; + public sortedSymbol_t e159; + public sortedSymbol_t e160; + public sortedSymbol_t e161; + public sortedSymbol_t e162; + public sortedSymbol_t e163; + public sortedSymbol_t e164; + public sortedSymbol_t e165; + public sortedSymbol_t e166; + public sortedSymbol_t e167; + public sortedSymbol_t e168; + public sortedSymbol_t e169; + public sortedSymbol_t e170; + public sortedSymbol_t e171; + public sortedSymbol_t e172; + public sortedSymbol_t e173; + public sortedSymbol_t e174; + public sortedSymbol_t e175; + public sortedSymbol_t e176; + public sortedSymbol_t e177; + public sortedSymbol_t e178; + public sortedSymbol_t e179; + public sortedSymbol_t e180; + public sortedSymbol_t e181; + public sortedSymbol_t e182; + public sortedSymbol_t e183; + public sortedSymbol_t e184; + public sortedSymbol_t e185; + public sortedSymbol_t e186; + public sortedSymbol_t e187; + public sortedSymbol_t e188; + public sortedSymbol_t e189; + public sortedSymbol_t e190; + public sortedSymbol_t e191; + public sortedSymbol_t e192; + public sortedSymbol_t e193; + public sortedSymbol_t e194; + public sortedSymbol_t e195; + public sortedSymbol_t e196; + public sortedSymbol_t e197; + public sortedSymbol_t e198; + public sortedSymbol_t e199; + public sortedSymbol_t e200; + public sortedSymbol_t e201; + public sortedSymbol_t e202; + public sortedSymbol_t e203; + public sortedSymbol_t e204; + public sortedSymbol_t e205; + public sortedSymbol_t e206; + public sortedSymbol_t e207; + public sortedSymbol_t e208; + public sortedSymbol_t e209; + public sortedSymbol_t e210; + public sortedSymbol_t e211; + public sortedSymbol_t e212; + public sortedSymbol_t e213; + public sortedSymbol_t e214; + public sortedSymbol_t e215; + public sortedSymbol_t e216; + public sortedSymbol_t e217; + public sortedSymbol_t e218; + public sortedSymbol_t e219; + public sortedSymbol_t e220; + public sortedSymbol_t e221; + public sortedSymbol_t e222; + public sortedSymbol_t e223; + public sortedSymbol_t e224; + public sortedSymbol_t e225; + public sortedSymbol_t e226; + public sortedSymbol_t e227; + public sortedSymbol_t e228; + public sortedSymbol_t e229; + public sortedSymbol_t e230; + public sortedSymbol_t e231; + public sortedSymbol_t e232; + public sortedSymbol_t e233; + public sortedSymbol_t e234; + public sortedSymbol_t e235; + public sortedSymbol_t e236; + public sortedSymbol_t e237; + public sortedSymbol_t e238; + public sortedSymbol_t e239; + public sortedSymbol_t e240; + public sortedSymbol_t e241; + public sortedSymbol_t e242; + public sortedSymbol_t e243; + public sortedSymbol_t e244; + public sortedSymbol_t e245; + public sortedSymbol_t e246; + public sortedSymbol_t e247; + public sortedSymbol_t e248; + public sortedSymbol_t e249; + public sortedSymbol_t e250; + public sortedSymbol_t e251; + public sortedSymbol_t e252; + public sortedSymbol_t e253; + public sortedSymbol_t e254; + public sortedSymbol_t e255; + } +#endif +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_WriteCTableWksp.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_WriteCTableWksp.cs new file mode 100644 index 00000000..2400dba7 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_WriteCTableWksp.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct HUF_WriteCTableWksp +{ + public HUF_CompressWeightsWksp wksp; + + /* precomputed conversion table */ + public fixed byte bitsToWeight[13]; + public fixed byte huffWeight[255]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_buildCTable_wksp_tables.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_buildCTable_wksp_tables.cs new file mode 100644 index 00000000..4fe92db7 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_buildCTable_wksp_tables.cs @@ -0,0 +1,739 @@ +using System.Runtime.CompilerServices; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct HUF_buildCTable_wksp_tables +{ + public _huffNodeTbl_e__FixedBuffer huffNodeTbl; + public _rankPosition_e__FixedBuffer rankPosition; + +#if NET8_0_OR_GREATER + [InlineArray(512)] + public unsafe struct _huffNodeTbl_e__FixedBuffer + { + public nodeElt_s e0; + } + +#else + public unsafe struct _huffNodeTbl_e__FixedBuffer + { + public nodeElt_s e0; + public nodeElt_s e1; + public nodeElt_s e2; + public nodeElt_s e3; + public nodeElt_s e4; + public nodeElt_s e5; + public nodeElt_s e6; + public nodeElt_s e7; + public nodeElt_s e8; + public nodeElt_s e9; + public nodeElt_s e10; + public nodeElt_s e11; + public nodeElt_s e12; + public nodeElt_s e13; + public nodeElt_s e14; + public nodeElt_s e15; + public nodeElt_s e16; + public nodeElt_s e17; + public nodeElt_s e18; + public nodeElt_s e19; + public nodeElt_s e20; + public nodeElt_s e21; + public nodeElt_s e22; + public nodeElt_s e23; + public nodeElt_s e24; + public nodeElt_s e25; + public nodeElt_s e26; + public nodeElt_s e27; + public nodeElt_s e28; + public nodeElt_s e29; + public nodeElt_s e30; + public nodeElt_s e31; + public nodeElt_s e32; + public nodeElt_s e33; + public nodeElt_s e34; + public nodeElt_s e35; + public nodeElt_s e36; + public nodeElt_s e37; + public nodeElt_s e38; + public nodeElt_s e39; + public nodeElt_s e40; + public nodeElt_s e41; + public nodeElt_s e42; + public nodeElt_s e43; + public nodeElt_s e44; + public nodeElt_s e45; + public nodeElt_s e46; + public nodeElt_s e47; + public nodeElt_s e48; + public nodeElt_s e49; + public nodeElt_s e50; + public nodeElt_s e51; + public nodeElt_s e52; + public nodeElt_s e53; + public nodeElt_s e54; + public nodeElt_s e55; + public nodeElt_s e56; + public nodeElt_s e57; + public nodeElt_s e58; + public nodeElt_s e59; + public nodeElt_s e60; + public nodeElt_s e61; + public nodeElt_s e62; + public nodeElt_s e63; + public nodeElt_s e64; + public nodeElt_s e65; + public nodeElt_s e66; + public nodeElt_s e67; + public nodeElt_s e68; + public nodeElt_s e69; + public nodeElt_s e70; + public nodeElt_s e71; + public nodeElt_s e72; + public nodeElt_s e73; + public nodeElt_s e74; + public nodeElt_s e75; + public nodeElt_s e76; + public nodeElt_s e77; + public nodeElt_s e78; + public nodeElt_s e79; + public nodeElt_s e80; + public nodeElt_s e81; + public nodeElt_s e82; + public nodeElt_s e83; + public nodeElt_s e84; + public nodeElt_s e85; + public nodeElt_s e86; + public nodeElt_s e87; + public nodeElt_s e88; + public nodeElt_s e89; + public nodeElt_s e90; + public nodeElt_s e91; + public nodeElt_s e92; + public nodeElt_s e93; + public nodeElt_s e94; + public nodeElt_s e95; + public nodeElt_s e96; + public nodeElt_s e97; + public nodeElt_s e98; + public nodeElt_s e99; + public nodeElt_s e100; + public nodeElt_s e101; + public nodeElt_s e102; + public nodeElt_s e103; + public nodeElt_s e104; + public nodeElt_s e105; + public nodeElt_s e106; + public nodeElt_s e107; + public nodeElt_s e108; + public nodeElt_s e109; + public nodeElt_s e110; + public nodeElt_s e111; + public nodeElt_s e112; + public nodeElt_s e113; + public nodeElt_s e114; + public nodeElt_s e115; + public nodeElt_s e116; + public nodeElt_s e117; + public nodeElt_s e118; + public nodeElt_s e119; + public nodeElt_s e120; + public nodeElt_s e121; + public nodeElt_s e122; + public nodeElt_s e123; + public nodeElt_s e124; + public nodeElt_s e125; + public nodeElt_s e126; + public nodeElt_s e127; + public nodeElt_s e128; + public nodeElt_s e129; + public nodeElt_s e130; + public nodeElt_s e131; + public nodeElt_s e132; + public nodeElt_s e133; + public nodeElt_s e134; + public nodeElt_s e135; + public nodeElt_s e136; + public nodeElt_s e137; + public nodeElt_s e138; + public nodeElt_s e139; + public nodeElt_s e140; + public nodeElt_s e141; + public nodeElt_s e142; + public nodeElt_s e143; + public nodeElt_s e144; + public nodeElt_s e145; + public nodeElt_s e146; + public nodeElt_s e147; + public nodeElt_s e148; + public nodeElt_s e149; + public nodeElt_s e150; + public nodeElt_s e151; + public nodeElt_s e152; + public nodeElt_s e153; + public nodeElt_s e154; + public nodeElt_s e155; + public nodeElt_s e156; + public nodeElt_s e157; + public nodeElt_s e158; + public nodeElt_s e159; + public nodeElt_s e160; + public nodeElt_s e161; + public nodeElt_s e162; + public nodeElt_s e163; + public nodeElt_s e164; + public nodeElt_s e165; + public nodeElt_s e166; + public nodeElt_s e167; + public nodeElt_s e168; + public nodeElt_s e169; + public nodeElt_s e170; + public nodeElt_s e171; + public nodeElt_s e172; + public nodeElt_s e173; + public nodeElt_s e174; + public nodeElt_s e175; + public nodeElt_s e176; + public nodeElt_s e177; + public nodeElt_s e178; + public nodeElt_s e179; + public nodeElt_s e180; + public nodeElt_s e181; + public nodeElt_s e182; + public nodeElt_s e183; + public nodeElt_s e184; + public nodeElt_s e185; + public nodeElt_s e186; + public nodeElt_s e187; + public nodeElt_s e188; + public nodeElt_s e189; + public nodeElt_s e190; + public nodeElt_s e191; + public nodeElt_s e192; + public nodeElt_s e193; + public nodeElt_s e194; + public nodeElt_s e195; + public nodeElt_s e196; + public nodeElt_s e197; + public nodeElt_s e198; + public nodeElt_s e199; + public nodeElt_s e200; + public nodeElt_s e201; + public nodeElt_s e202; + public nodeElt_s e203; + public nodeElt_s e204; + public nodeElt_s e205; + public nodeElt_s e206; + public nodeElt_s e207; + public nodeElt_s e208; + public nodeElt_s e209; + public nodeElt_s e210; + public nodeElt_s e211; + public nodeElt_s e212; + public nodeElt_s e213; + public nodeElt_s e214; + public nodeElt_s e215; + public nodeElt_s e216; + public nodeElt_s e217; + public nodeElt_s e218; + public nodeElt_s e219; + public nodeElt_s e220; + public nodeElt_s e221; + public nodeElt_s e222; + public nodeElt_s e223; + public nodeElt_s e224; + public nodeElt_s e225; + public nodeElt_s e226; + public nodeElt_s e227; + public nodeElt_s e228; + public nodeElt_s e229; + public nodeElt_s e230; + public nodeElt_s e231; + public nodeElt_s e232; + public nodeElt_s e233; + public nodeElt_s e234; + public nodeElt_s e235; + public nodeElt_s e236; + public nodeElt_s e237; + public nodeElt_s e238; + public nodeElt_s e239; + public nodeElt_s e240; + public nodeElt_s e241; + public nodeElt_s e242; + public nodeElt_s e243; + public nodeElt_s e244; + public nodeElt_s e245; + public nodeElt_s e246; + public nodeElt_s e247; + public nodeElt_s e248; + public nodeElt_s e249; + public nodeElt_s e250; + public nodeElt_s e251; + public nodeElt_s e252; + public nodeElt_s e253; + public nodeElt_s e254; + public nodeElt_s e255; + public nodeElt_s e256; + public nodeElt_s e257; + public nodeElt_s e258; + public nodeElt_s e259; + public nodeElt_s e260; + public nodeElt_s e261; + public nodeElt_s e262; + public nodeElt_s e263; + public nodeElt_s e264; + public nodeElt_s e265; + public nodeElt_s e266; + public nodeElt_s e267; + public nodeElt_s e268; + public nodeElt_s e269; + public nodeElt_s e270; + public nodeElt_s e271; + public nodeElt_s e272; + public nodeElt_s e273; + public nodeElt_s e274; + public nodeElt_s e275; + public nodeElt_s e276; + public nodeElt_s e277; + public nodeElt_s e278; + public nodeElt_s e279; + public nodeElt_s e280; + public nodeElt_s e281; + public nodeElt_s e282; + public nodeElt_s e283; + public nodeElt_s e284; + public nodeElt_s e285; + public nodeElt_s e286; + public nodeElt_s e287; + public nodeElt_s e288; + public nodeElt_s e289; + public nodeElt_s e290; + public nodeElt_s e291; + public nodeElt_s e292; + public nodeElt_s e293; + public nodeElt_s e294; + public nodeElt_s e295; + public nodeElt_s e296; + public nodeElt_s e297; + public nodeElt_s e298; + public nodeElt_s e299; + public nodeElt_s e300; + public nodeElt_s e301; + public nodeElt_s e302; + public nodeElt_s e303; + public nodeElt_s e304; + public nodeElt_s e305; + public nodeElt_s e306; + public nodeElt_s e307; + public nodeElt_s e308; + public nodeElt_s e309; + public nodeElt_s e310; + public nodeElt_s e311; + public nodeElt_s e312; + public nodeElt_s e313; + public nodeElt_s e314; + public nodeElt_s e315; + public nodeElt_s e316; + public nodeElt_s e317; + public nodeElt_s e318; + public nodeElt_s e319; + public nodeElt_s e320; + public nodeElt_s e321; + public nodeElt_s e322; + public nodeElt_s e323; + public nodeElt_s e324; + public nodeElt_s e325; + public nodeElt_s e326; + public nodeElt_s e327; + public nodeElt_s e328; + public nodeElt_s e329; + public nodeElt_s e330; + public nodeElt_s e331; + public nodeElt_s e332; + public nodeElt_s e333; + public nodeElt_s e334; + public nodeElt_s e335; + public nodeElt_s e336; + public nodeElt_s e337; + public nodeElt_s e338; + public nodeElt_s e339; + public nodeElt_s e340; + public nodeElt_s e341; + public nodeElt_s e342; + public nodeElt_s e343; + public nodeElt_s e344; + public nodeElt_s e345; + public nodeElt_s e346; + public nodeElt_s e347; + public nodeElt_s e348; + public nodeElt_s e349; + public nodeElt_s e350; + public nodeElt_s e351; + public nodeElt_s e352; + public nodeElt_s e353; + public nodeElt_s e354; + public nodeElt_s e355; + public nodeElt_s e356; + public nodeElt_s e357; + public nodeElt_s e358; + public nodeElt_s e359; + public nodeElt_s e360; + public nodeElt_s e361; + public nodeElt_s e362; + public nodeElt_s e363; + public nodeElt_s e364; + public nodeElt_s e365; + public nodeElt_s e366; + public nodeElt_s e367; + public nodeElt_s e368; + public nodeElt_s e369; + public nodeElt_s e370; + public nodeElt_s e371; + public nodeElt_s e372; + public nodeElt_s e373; + public nodeElt_s e374; + public nodeElt_s e375; + public nodeElt_s e376; + public nodeElt_s e377; + public nodeElt_s e378; + public nodeElt_s e379; + public nodeElt_s e380; + public nodeElt_s e381; + public nodeElt_s e382; + public nodeElt_s e383; + public nodeElt_s e384; + public nodeElt_s e385; + public nodeElt_s e386; + public nodeElt_s e387; + public nodeElt_s e388; + public nodeElt_s e389; + public nodeElt_s e390; + public nodeElt_s e391; + public nodeElt_s e392; + public nodeElt_s e393; + public nodeElt_s e394; + public nodeElt_s e395; + public nodeElt_s e396; + public nodeElt_s e397; + public nodeElt_s e398; + public nodeElt_s e399; + public nodeElt_s e400; + public nodeElt_s e401; + public nodeElt_s e402; + public nodeElt_s e403; + public nodeElt_s e404; + public nodeElt_s e405; + public nodeElt_s e406; + public nodeElt_s e407; + public nodeElt_s e408; + public nodeElt_s e409; + public nodeElt_s e410; + public nodeElt_s e411; + public nodeElt_s e412; + public nodeElt_s e413; + public nodeElt_s e414; + public nodeElt_s e415; + public nodeElt_s e416; + public nodeElt_s e417; + public nodeElt_s e418; + public nodeElt_s e419; + public nodeElt_s e420; + public nodeElt_s e421; + public nodeElt_s e422; + public nodeElt_s e423; + public nodeElt_s e424; + public nodeElt_s e425; + public nodeElt_s e426; + public nodeElt_s e427; + public nodeElt_s e428; + public nodeElt_s e429; + public nodeElt_s e430; + public nodeElt_s e431; + public nodeElt_s e432; + public nodeElt_s e433; + public nodeElt_s e434; + public nodeElt_s e435; + public nodeElt_s e436; + public nodeElt_s e437; + public nodeElt_s e438; + public nodeElt_s e439; + public nodeElt_s e440; + public nodeElt_s e441; + public nodeElt_s e442; + public nodeElt_s e443; + public nodeElt_s e444; + public nodeElt_s e445; + public nodeElt_s e446; + public nodeElt_s e447; + public nodeElt_s e448; + public nodeElt_s e449; + public nodeElt_s e450; + public nodeElt_s e451; + public nodeElt_s e452; + public nodeElt_s e453; + public nodeElt_s e454; + public nodeElt_s e455; + public nodeElt_s e456; + public nodeElt_s e457; + public nodeElt_s e458; + public nodeElt_s e459; + public nodeElt_s e460; + public nodeElt_s e461; + public nodeElt_s e462; + public nodeElt_s e463; + public nodeElt_s e464; + public nodeElt_s e465; + public nodeElt_s e466; + public nodeElt_s e467; + public nodeElt_s e468; + public nodeElt_s e469; + public nodeElt_s e470; + public nodeElt_s e471; + public nodeElt_s e472; + public nodeElt_s e473; + public nodeElt_s e474; + public nodeElt_s e475; + public nodeElt_s e476; + public nodeElt_s e477; + public nodeElt_s e478; + public nodeElt_s e479; + public nodeElt_s e480; + public nodeElt_s e481; + public nodeElt_s e482; + public nodeElt_s e483; + public nodeElt_s e484; + public nodeElt_s e485; + public nodeElt_s e486; + public nodeElt_s e487; + public nodeElt_s e488; + public nodeElt_s e489; + public nodeElt_s e490; + public nodeElt_s e491; + public nodeElt_s e492; + public nodeElt_s e493; + public nodeElt_s e494; + public nodeElt_s e495; + public nodeElt_s e496; + public nodeElt_s e497; + public nodeElt_s e498; + public nodeElt_s e499; + public nodeElt_s e500; + public nodeElt_s e501; + public nodeElt_s e502; + public nodeElt_s e503; + public nodeElt_s e504; + public nodeElt_s e505; + public nodeElt_s e506; + public nodeElt_s e507; + public nodeElt_s e508; + public nodeElt_s e509; + public nodeElt_s e510; + public nodeElt_s e511; + } +#endif + +#if NET8_0_OR_GREATER + [InlineArray(192)] + public unsafe struct _rankPosition_e__FixedBuffer + { + public rankPos e0; + } + +#else + public unsafe struct _rankPosition_e__FixedBuffer + { + public rankPos e0; + public rankPos e1; + public rankPos e2; + public rankPos e3; + public rankPos e4; + public rankPos e5; + public rankPos e6; + public rankPos e7; + public rankPos e8; + public rankPos e9; + public rankPos e10; + public rankPos e11; + public rankPos e12; + public rankPos e13; + public rankPos e14; + public rankPos e15; + public rankPos e16; + public rankPos e17; + public rankPos e18; + public rankPos e19; + public rankPos e20; + public rankPos e21; + public rankPos e22; + public rankPos e23; + public rankPos e24; + public rankPos e25; + public rankPos e26; + public rankPos e27; + public rankPos e28; + public rankPos e29; + public rankPos e30; + public rankPos e31; + public rankPos e32; + public rankPos e33; + public rankPos e34; + public rankPos e35; + public rankPos e36; + public rankPos e37; + public rankPos e38; + public rankPos e39; + public rankPos e40; + public rankPos e41; + public rankPos e42; + public rankPos e43; + public rankPos e44; + public rankPos e45; + public rankPos e46; + public rankPos e47; + public rankPos e48; + public rankPos e49; + public rankPos e50; + public rankPos e51; + public rankPos e52; + public rankPos e53; + public rankPos e54; + public rankPos e55; + public rankPos e56; + public rankPos e57; + public rankPos e58; + public rankPos e59; + public rankPos e60; + public rankPos e61; + public rankPos e62; + public rankPos e63; + public rankPos e64; + public rankPos e65; + public rankPos e66; + public rankPos e67; + public rankPos e68; + public rankPos e69; + public rankPos e70; + public rankPos e71; + public rankPos e72; + public rankPos e73; + public rankPos e74; + public rankPos e75; + public rankPos e76; + public rankPos e77; + public rankPos e78; + public rankPos e79; + public rankPos e80; + public rankPos e81; + public rankPos e82; + public rankPos e83; + public rankPos e84; + public rankPos e85; + public rankPos e86; + public rankPos e87; + public rankPos e88; + public rankPos e89; + public rankPos e90; + public rankPos e91; + public rankPos e92; + public rankPos e93; + public rankPos e94; + public rankPos e95; + public rankPos e96; + public rankPos e97; + public rankPos e98; + public rankPos e99; + public rankPos e100; + public rankPos e101; + public rankPos e102; + public rankPos e103; + public rankPos e104; + public rankPos e105; + public rankPos e106; + public rankPos e107; + public rankPos e108; + public rankPos e109; + public rankPos e110; + public rankPos e111; + public rankPos e112; + public rankPos e113; + public rankPos e114; + public rankPos e115; + public rankPos e116; + public rankPos e117; + public rankPos e118; + public rankPos e119; + public rankPos e120; + public rankPos e121; + public rankPos e122; + public rankPos e123; + public rankPos e124; + public rankPos e125; + public rankPos e126; + public rankPos e127; + public rankPos e128; + public rankPos e129; + public rankPos e130; + public rankPos e131; + public rankPos e132; + public rankPos e133; + public rankPos e134; + public rankPos e135; + public rankPos e136; + public rankPos e137; + public rankPos e138; + public rankPos e139; + public rankPos e140; + public rankPos e141; + public rankPos e142; + public rankPos e143; + public rankPos e144; + public rankPos e145; + public rankPos e146; + public rankPos e147; + public rankPos e148; + public rankPos e149; + public rankPos e150; + public rankPos e151; + public rankPos e152; + public rankPos e153; + public rankPos e154; + public rankPos e155; + public rankPos e156; + public rankPos e157; + public rankPos e158; + public rankPos e159; + public rankPos e160; + public rankPos e161; + public rankPos e162; + public rankPos e163; + public rankPos e164; + public rankPos e165; + public rankPos e166; + public rankPos e167; + public rankPos e168; + public rankPos e169; + public rankPos e170; + public rankPos e171; + public rankPos e172; + public rankPos e173; + public rankPos e174; + public rankPos e175; + public rankPos e176; + public rankPos e177; + public rankPos e178; + public rankPos e179; + public rankPos e180; + public rankPos e181; + public rankPos e182; + public rankPos e183; + public rankPos e184; + public rankPos e185; + public rankPos e186; + public rankPos e187; + public rankPos e188; + public rankPos e189; + public rankPos e190; + public rankPos e191; + } +#endif +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_compress_tables_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_compress_tables_t.cs new file mode 100644 index 00000000..6e6e12c0 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_compress_tables_t.cs @@ -0,0 +1,280 @@ +using System.Runtime.CompilerServices; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct HUF_compress_tables_t +{ + public fixed uint count[256]; + public _CTable_e__FixedBuffer CTable; + public _wksps_e__Union wksps; + +#if NET8_0_OR_GREATER + [InlineArray(257)] + public unsafe struct _CTable_e__FixedBuffer + { + public nuint e0; + } + +#else + public unsafe struct _CTable_e__FixedBuffer + { + public nuint e0; + public nuint e1; + public nuint e2; + public nuint e3; + public nuint e4; + public nuint e5; + public nuint e6; + public nuint e7; + public nuint e8; + public nuint e9; + public nuint e10; + public nuint e11; + public nuint e12; + public nuint e13; + public nuint e14; + public nuint e15; + public nuint e16; + public nuint e17; + public nuint e18; + public nuint e19; + public nuint e20; + public nuint e21; + public nuint e22; + public nuint e23; + public nuint e24; + public nuint e25; + public nuint e26; + public nuint e27; + public nuint e28; + public nuint e29; + public nuint e30; + public nuint e31; + public nuint e32; + public nuint e33; + public nuint e34; + public nuint e35; + public nuint e36; + public nuint e37; + public nuint e38; + public nuint e39; + public nuint e40; + public nuint e41; + public nuint e42; + public nuint e43; + public nuint e44; + public nuint e45; + public nuint e46; + public nuint e47; + public nuint e48; + public nuint e49; + public nuint e50; + public nuint e51; + public nuint e52; + public nuint e53; + public nuint e54; + public nuint e55; + public nuint e56; + public nuint e57; + public nuint e58; + public nuint e59; + public nuint e60; + public nuint e61; + public nuint e62; + public nuint e63; + public nuint e64; + public nuint e65; + public nuint e66; + public nuint e67; + public nuint e68; + public nuint e69; + public nuint e70; + public nuint e71; + public nuint e72; + public nuint e73; + public nuint e74; + public nuint e75; + public nuint e76; + public nuint e77; + public nuint e78; + public nuint e79; + public nuint e80; + public nuint e81; + public nuint e82; + public nuint e83; + public nuint e84; + public nuint e85; + public nuint e86; + public nuint e87; + public nuint e88; + public nuint e89; + public nuint e90; + public nuint e91; + public nuint e92; + public nuint e93; + public nuint e94; + public nuint e95; + public nuint e96; + public nuint e97; + public nuint e98; + public nuint e99; + public nuint e100; + public nuint e101; + public nuint e102; + public nuint e103; + public nuint e104; + public nuint e105; + public nuint e106; + public nuint e107; + public nuint e108; + public nuint e109; + public nuint e110; + public nuint e111; + public nuint e112; + public nuint e113; + public nuint e114; + public nuint e115; + public nuint e116; + public nuint e117; + public nuint e118; + public nuint e119; + public nuint e120; + public nuint e121; + public nuint e122; + public nuint e123; + public nuint e124; + public nuint e125; + public nuint e126; + public nuint e127; + public nuint e128; + public nuint e129; + public nuint e130; + public nuint e131; + public nuint e132; + public nuint e133; + public nuint e134; + public nuint e135; + public nuint e136; + public nuint e137; + public nuint e138; + public nuint e139; + public nuint e140; + public nuint e141; + public nuint e142; + public nuint e143; + public nuint e144; + public nuint e145; + public nuint e146; + public nuint e147; + public nuint e148; + public nuint e149; + public nuint e150; + public nuint e151; + public nuint e152; + public nuint e153; + public nuint e154; + public nuint e155; + public nuint e156; + public nuint e157; + public nuint e158; + public nuint e159; + public nuint e160; + public nuint e161; + public nuint e162; + public nuint e163; + public nuint e164; + public nuint e165; + public nuint e166; + public nuint e167; + public nuint e168; + public nuint e169; + public nuint e170; + public nuint e171; + public nuint e172; + public nuint e173; + public nuint e174; + public nuint e175; + public nuint e176; + public nuint e177; + public nuint e178; + public nuint e179; + public nuint e180; + public nuint e181; + public nuint e182; + public nuint e183; + public nuint e184; + public nuint e185; + public nuint e186; + public nuint e187; + public nuint e188; + public nuint e189; + public nuint e190; + public nuint e191; + public nuint e192; + public nuint e193; + public nuint e194; + public nuint e195; + public nuint e196; + public nuint e197; + public nuint e198; + public nuint e199; + public nuint e200; + public nuint e201; + public nuint e202; + public nuint e203; + public nuint e204; + public nuint e205; + public nuint e206; + public nuint e207; + public nuint e208; + public nuint e209; + public nuint e210; + public nuint e211; + public nuint e212; + public nuint e213; + public nuint e214; + public nuint e215; + public nuint e216; + public nuint e217; + public nuint e218; + public nuint e219; + public nuint e220; + public nuint e221; + public nuint e222; + public nuint e223; + public nuint e224; + public nuint e225; + public nuint e226; + public nuint e227; + public nuint e228; + public nuint e229; + public nuint e230; + public nuint e231; + public nuint e232; + public nuint e233; + public nuint e234; + public nuint e235; + public nuint e236; + public nuint e237; + public nuint e238; + public nuint e239; + public nuint e240; + public nuint e241; + public nuint e242; + public nuint e243; + public nuint e244; + public nuint e245; + public nuint e246; + public nuint e247; + public nuint e248; + public nuint e249; + public nuint e250; + public nuint e251; + public nuint e252; + public nuint e253; + public nuint e254; + public nuint e255; + public nuint e256; + } +#endif +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_flags_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_flags_e.cs new file mode 100644 index 00000000..a38bc711 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_flags_e.cs @@ -0,0 +1,44 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * Huffman flags bitset. + * For all flags, 0 is the default value. + */ +public enum HUF_flags_e +{ + /** + * If compiled with DYNAMIC_BMI2: Set flag only if the CPU supports BMI2 at runtime. + * Otherwise: Ignored. + */ + HUF_flags_bmi2 = 1 << 0, + + /** + * If set: Test possible table depths to find the one that produces the smallest header + encoded size. + * If unset: Use heuristic to find the table depth. + */ + HUF_flags_optimalDepth = 1 << 1, + + /** + * If set: If the previous table can encode the input, always reuse the previous table. + * If unset: If the previous table can encode the input, reuse the previous table if it results in a smaller output. + */ + HUF_flags_preferRepeat = 1 << 2, + + /** + * If set: Sample the input and check if the sample is uncompressible, if it is then don't attempt to compress. + * If unset: Always histogram the entire input. + */ + HUF_flags_suspectUncompressible = 1 << 3, + + /** + * If set: Don't use assembly implementations + * If unset: Allow using assembly implementations + */ + HUF_flags_disableAsm = 1 << 4, + + /** + * If set: Don't use the fast decoding loop, always use the fallback decoding loop. + * If unset: Use the fast decoding loop when possible. + */ + HUF_flags_disableFast = 1 << 5, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_nbStreams_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_nbStreams_e.cs new file mode 100644 index 00000000..ff7e26e7 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_nbStreams_e.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum HUF_nbStreams_e +{ + HUF_singleStream, + HUF_fourStreams, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_repeat.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_repeat.cs new file mode 100644 index 00000000..5ab8b1da --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HUF_repeat.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum HUF_repeat +{ + /**< Cannot use the previous table */ + HUF_repeat_none, + + /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, 4}X_repeat */ + HUF_repeat_check, + + /**< Can use the previous table and it is assumed to be valid */ + HUF_repeat_valid, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Hist.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Hist.cs new file mode 100644 index 00000000..10a433ce --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Hist.cs @@ -0,0 +1,273 @@ +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /* --- Error management --- */ + private static bool HIST_isError(nuint code) + { + return ERR_isError(code); + } + + /*-************************************************************** + * Histogram functions + ****************************************************************/ + private static void HIST_add(uint* count, void* src, nuint srcSize) + { + byte* ip = (byte*)src; + byte* end = ip + srcSize; + while (ip < end) + { + count[*ip++]++; + } + } + + /*! HIST_count_simple() : + * Same as HIST_countFast(), this function is unsafe, + * and will segfault if any value within `src` is `> *maxSymbolValuePtr`. + * It is also a bit slower for large inputs. + * However, it does not need any additional memory (not even on stack). + * @return : count of the most frequent symbol. + * Note this function doesn't produce any error (i.e. it must succeed). + */ + private static uint HIST_count_simple( + uint* count, + uint* maxSymbolValuePtr, + void* src, + nuint srcSize + ) + { + byte* ip = (byte*)src; + byte* end = ip + srcSize; + uint maxSymbolValue = *maxSymbolValuePtr; + uint largestCount = 0; + memset(count, 0, (maxSymbolValue + 1) * sizeof(uint)); + if (srcSize == 0) + { + *maxSymbolValuePtr = 0; + return 0; + } + + while (ip < end) + { + assert(*ip <= maxSymbolValue); + count[*ip++]++; + } + + while (count[maxSymbolValue] == 0) + maxSymbolValue--; + *maxSymbolValuePtr = maxSymbolValue; + { + uint s; + for (s = 0; s <= maxSymbolValue; s++) + if (count[s] > largestCount) + largestCount = count[s]; + } + + return largestCount; + } + + /* HIST_count_parallel_wksp() : + * store histogram into 4 intermediate tables, recombined at the end. + * this design makes better use of OoO cpus, + * and is noticeably faster when some values are heavily repeated. + * But it needs some additional workspace for intermediate tables. + * `workSpace` must be a U32 table of size >= HIST_WKSP_SIZE_U32. + * @return : largest histogram frequency, + * or an error code (notably when histogram's alphabet is larger than *maxSymbolValuePtr) */ + private static nuint HIST_count_parallel_wksp( + uint* count, + uint* maxSymbolValuePtr, + void* source, + nuint sourceSize, + HIST_checkInput_e check, + uint* workSpace + ) + { + byte* ip = (byte*)source; + byte* iend = ip + sourceSize; + nuint countSize = (*maxSymbolValuePtr + 1) * sizeof(uint); + uint max = 0; + uint* Counting1 = workSpace; + uint* Counting2 = Counting1 + 256; + uint* Counting3 = Counting2 + 256; + uint* Counting4 = Counting3 + 256; + assert(*maxSymbolValuePtr <= 255); + if (sourceSize == 0) + { + memset(count, 0, (uint)countSize); + *maxSymbolValuePtr = 0; + return 0; + } + + memset(workSpace, 0, 4 * 256 * sizeof(uint)); + { + uint cached = MEM_read32(ip); + ip += 4; + while (ip < iend - 15) + { + uint c = cached; + cached = MEM_read32(ip); + ip += 4; + Counting1[(byte)c]++; + Counting2[(byte)(c >> 8)]++; + Counting3[(byte)(c >> 16)]++; + Counting4[c >> 24]++; + c = cached; + cached = MEM_read32(ip); + ip += 4; + Counting1[(byte)c]++; + Counting2[(byte)(c >> 8)]++; + Counting3[(byte)(c >> 16)]++; + Counting4[c >> 24]++; + c = cached; + cached = MEM_read32(ip); + ip += 4; + Counting1[(byte)c]++; + Counting2[(byte)(c >> 8)]++; + Counting3[(byte)(c >> 16)]++; + Counting4[c >> 24]++; + c = cached; + cached = MEM_read32(ip); + ip += 4; + Counting1[(byte)c]++; + Counting2[(byte)(c >> 8)]++; + Counting3[(byte)(c >> 16)]++; + Counting4[c >> 24]++; + } + + ip -= 4; + } + + while (ip < iend) + Counting1[*ip++]++; + { + uint s; + for (s = 0; s < 256; s++) + { + Counting1[s] += Counting2[s] + Counting3[s] + Counting4[s]; + if (Counting1[s] > max) + max = Counting1[s]; + } + } + + { + uint maxSymbolValue = 255; + while (Counting1[maxSymbolValue] == 0) + maxSymbolValue--; + if (check != default && maxSymbolValue > *maxSymbolValuePtr) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooSmall)); + *maxSymbolValuePtr = maxSymbolValue; + memmove(count, Counting1, countSize); + } + + return max; + } + + /* HIST_countFast_wksp() : + * Same as HIST_countFast(), but using an externally provided scratch buffer. + * `workSpace` is a writable buffer which must be 4-bytes aligned, + * `workSpaceSize` must be >= HIST_WKSP_SIZE + */ + private static nuint HIST_countFast_wksp( + uint* count, + uint* maxSymbolValuePtr, + void* source, + nuint sourceSize, + void* workSpace, + nuint workSpaceSize + ) + { + if (sourceSize < 1500) + return HIST_count_simple(count, maxSymbolValuePtr, source, sourceSize); + if (((nuint)workSpace & 3) != 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + if (workSpaceSize < 1024 * sizeof(uint)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_workSpace_tooSmall)); + return HIST_count_parallel_wksp( + count, + maxSymbolValuePtr, + source, + sourceSize, + HIST_checkInput_e.trustInput, + (uint*)workSpace + ); + } + + /* HIST_count_wksp() : + * Same as HIST_count(), but using an externally provided scratch buffer. + * `workSpace` size must be table of >= HIST_WKSP_SIZE_U32 unsigned */ + private static nuint HIST_count_wksp( + uint* count, + uint* maxSymbolValuePtr, + void* source, + nuint sourceSize, + void* workSpace, + nuint workSpaceSize + ) + { + if (((nuint)workSpace & 3) != 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + if (workSpaceSize < 1024 * sizeof(uint)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_workSpace_tooSmall)); + if (*maxSymbolValuePtr < 255) + return HIST_count_parallel_wksp( + count, + maxSymbolValuePtr, + source, + sourceSize, + HIST_checkInput_e.checkMaxSymbolValue, + (uint*)workSpace + ); + *maxSymbolValuePtr = 255; + return HIST_countFast_wksp( + count, + maxSymbolValuePtr, + source, + sourceSize, + workSpace, + workSpaceSize + ); + } + + /* fast variant (unsafe : won't check if src contains values beyond count[] limit) */ + private static nuint HIST_countFast( + uint* count, + uint* maxSymbolValuePtr, + void* source, + nuint sourceSize + ) + { + uint* tmpCounters = stackalloc uint[1024]; + return HIST_countFast_wksp( + count, + maxSymbolValuePtr, + source, + sourceSize, + tmpCounters, + sizeof(uint) * 1024 + ); + } + + /*! HIST_count(): + * Provides the precise count of each byte within a table 'count'. + * 'count' is a table of unsigned int, of minimum size (*maxSymbolValuePtr+1). + * Updates *maxSymbolValuePtr with actual largest symbol value detected. + * @return : count of the most frequent symbol (which isn't identified). + * or an error code, which can be tested using HIST_isError(). + * note : if return == srcSize, there is only one symbol. + */ + private static nuint HIST_count(uint* count, uint* maxSymbolValuePtr, void* src, nuint srcSize) + { + uint* tmpCounters = stackalloc uint[1024]; + return HIST_count_wksp( + count, + maxSymbolValuePtr, + src, + srcSize, + tmpCounters, + sizeof(uint) * 1024 + ); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HufCompress.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HufCompress.cs new file mode 100644 index 00000000..785c8143 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HufCompress.cs @@ -0,0 +1,1825 @@ +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + private static void* HUF_alignUpWorkspace(void* workspace, nuint* workspaceSizePtr, nuint align) + { + nuint mask = align - 1; + nuint rem = (nuint)workspace & mask; + nuint add = align - rem & mask; + byte* aligned = (byte*)workspace + add; + assert((align & align - 1) == 0); + assert(align <= 8); + if (*workspaceSizePtr >= add) + { + assert(add < align); + assert(((nuint)aligned & mask) == 0); + *workspaceSizePtr -= add; + return aligned; + } + else + { + *workspaceSizePtr = 0; + return null; + } + } + + private static nuint HUF_compressWeights( + void* dst, + nuint dstSize, + void* weightTable, + nuint wtSize, + void* workspace, + nuint workspaceSize + ) + { + byte* ostart = (byte*)dst; + byte* op = ostart; + byte* oend = ostart + dstSize; + uint maxSymbolValue = 12; + uint tableLog = 6; + HUF_CompressWeightsWksp* wksp = (HUF_CompressWeightsWksp*)HUF_alignUpWorkspace( + workspace, + &workspaceSize, + sizeof(uint) + ); + if (workspaceSize < (nuint)sizeof(HUF_CompressWeightsWksp)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + if (wtSize <= 1) + return 0; + { + /* never fails */ + uint maxCount = HIST_count_simple(wksp->count, &maxSymbolValue, weightTable, wtSize); + if (maxCount == wtSize) + return 1; + if (maxCount == 1) + return 0; + } + + tableLog = FSE_optimalTableLog(tableLog, wtSize, maxSymbolValue); + { + /* useLowProbCount */ + nuint _var_err__ = FSE_normalizeCount( + wksp->norm, + tableLog, + wksp->count, + wtSize, + maxSymbolValue, + 0 + ); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + { + nuint hSize = FSE_writeNCount( + op, + (nuint)(oend - op), + wksp->norm, + maxSymbolValue, + tableLog + ); + if (ERR_isError(hSize)) + return hSize; + op += hSize; + } + + { + /* Compress */ + nuint _var_err__ = FSE_buildCTable_wksp( + wksp->CTable, + wksp->norm, + maxSymbolValue, + tableLog, + wksp->scratchBuffer, + sizeof(uint) * 41 + ); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + { + nuint cSize = FSE_compress_usingCTable( + op, + (nuint)(oend - op), + weightTable, + wtSize, + wksp->CTable + ); + if (ERR_isError(cSize)) + return cSize; + if (cSize == 0) + return 0; + op += cSize; + } + + return (nuint)(op - ostart); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint HUF_getNbBits(nuint elt) + { + return elt & 0xFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint HUF_getNbBitsFast(nuint elt) + { + return elt; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint HUF_getValue(nuint elt) + { + return elt & ~(nuint)0xFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint HUF_getValueFast(nuint elt) + { + return elt; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HUF_setNbBits(nuint* elt, nuint nbBits) + { + assert(nbBits <= 12); + *elt = nbBits; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HUF_setValue(nuint* elt, nuint value) + { + nuint nbBits = HUF_getNbBits(*elt); + if (nbBits > 0) + { + assert(value >> (int)nbBits == 0); + *elt |= value << (int)((nuint)(sizeof(nuint) * 8) - nbBits); + } + } + + /** HUF_readCTableHeader() : + * @returns The header from the CTable specifying the tableLog and the maxSymbolValue. + */ + private static HUF_CTableHeader HUF_readCTableHeader(nuint* ctable) + { + HUF_CTableHeader header; + memcpy(&header, ctable, (uint)sizeof(nuint)); + return header; + } + + private static void HUF_writeCTableHeader(nuint* ctable, uint tableLog, uint maxSymbolValue) + { + HUF_CTableHeader header; + memset(&header, 0, (uint)sizeof(nuint)); + assert(tableLog < 256); + header.tableLog = (byte)tableLog; + assert(maxSymbolValue < 256); + header.maxSymbolValue = (byte)maxSymbolValue; + memcpy(ctable, &header, (uint)sizeof(nuint)); + } + + private static nuint HUF_writeCTable_wksp( + void* dst, + nuint maxDstSize, + nuint* CTable, + uint maxSymbolValue, + uint huffLog, + void* workspace, + nuint workspaceSize + ) + { + nuint* ct = CTable + 1; + byte* op = (byte*)dst; + uint n; + HUF_WriteCTableWksp* wksp = (HUF_WriteCTableWksp*)HUF_alignUpWorkspace( + workspace, + &workspaceSize, + sizeof(uint) + ); + assert(HUF_readCTableHeader(CTable).maxSymbolValue == maxSymbolValue); + assert(HUF_readCTableHeader(CTable).tableLog == huffLog); + if (workspaceSize < (nuint)sizeof(HUF_WriteCTableWksp)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + if (maxSymbolValue > 255) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooLarge)); + wksp->bitsToWeight[0] = 0; + for (n = 1; n < huffLog + 1; n++) + wksp->bitsToWeight[n] = (byte)(huffLog + 1 - n); + for (n = 0; n < maxSymbolValue; n++) + wksp->huffWeight[n] = wksp->bitsToWeight[HUF_getNbBits(ct[n])]; + if (maxDstSize < 1) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + { + nuint hSize = HUF_compressWeights( + op + 1, + maxDstSize - 1, + wksp->huffWeight, + maxSymbolValue, + &wksp->wksp, + (nuint)sizeof(HUF_CompressWeightsWksp) + ); + if (ERR_isError(hSize)) + return hSize; + if (hSize > 1 && hSize < maxSymbolValue / 2) + { + op[0] = (byte)hSize; + return hSize + 1; + } + } + + if (maxSymbolValue > 256 - 128) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + if ((maxSymbolValue + 1) / 2 + 1 > maxDstSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + op[0] = (byte)(128 + (maxSymbolValue - 1)); + wksp->huffWeight[maxSymbolValue] = 0; + for (n = 0; n < maxSymbolValue; n += 2) + op[n / 2 + 1] = (byte)((wksp->huffWeight[n] << 4) + wksp->huffWeight[n + 1]); + return (maxSymbolValue + 1) / 2 + 1; + } + + /** HUF_readCTable() : + * Loading a CTable saved with HUF_writeCTable() */ + private static nuint HUF_readCTable( + nuint* CTable, + uint* maxSymbolValuePtr, + void* src, + nuint srcSize, + uint* hasZeroWeights + ) + { + /* init not required, even though some static analyzer may complain */ + byte* huffWeight = stackalloc byte[256]; + /* large enough for values from 0 to 16 */ + uint* rankVal = stackalloc uint[13]; + uint tableLog = 0; + uint nbSymbols = 0; + nuint* ct = CTable + 1; + /* get symbol weights */ + nuint readSize = HUF_readStats( + huffWeight, + 255 + 1, + rankVal, + &nbSymbols, + &tableLog, + src, + srcSize + ); + if (ERR_isError(readSize)) + return readSize; + *hasZeroWeights = rankVal[0] > 0 ? 1U : 0U; + if (tableLog > 12) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + if (nbSymbols > *maxSymbolValuePtr + 1) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooSmall)); + *maxSymbolValuePtr = nbSymbols - 1; + HUF_writeCTableHeader(CTable, tableLog, *maxSymbolValuePtr); + { + uint n, + nextRankStart = 0; + for (n = 1; n <= tableLog; n++) + { + uint curr = nextRankStart; + nextRankStart += rankVal[n] << (int)(n - 1); + rankVal[n] = curr; + } + } + + { + uint n; + for (n = 0; n < nbSymbols; n++) + { + uint w = huffWeight[n]; + HUF_setNbBits(ct + n, (nuint)((byte)(tableLog + 1 - w) & -(w != 0 ? 1 : 0))); + } + } + + { + ushort* nbPerRank = stackalloc ushort[14]; + /* support w=0=>n=tableLog+1 */ + memset(nbPerRank, 0, sizeof(ushort) * 14); + ushort* valPerRank = stackalloc ushort[14]; + memset(valPerRank, 0, sizeof(ushort) * 14); + { + uint n; + for (n = 0; n < nbSymbols; n++) + nbPerRank[HUF_getNbBits(ct[n])]++; + } + + valPerRank[tableLog + 1] = 0; + { + ushort min = 0; + /* start at n=tablelog <-> w=1 */ + uint n; + for (n = tableLog; n > 0; n--) + { + valPerRank[n] = min; + min += nbPerRank[n]; + min >>= 1; + } + } + + { + uint n; + for (n = 0; n < nbSymbols; n++) + HUF_setValue(ct + n, valPerRank[HUF_getNbBits(ct[n])]++); + } + } + + return readSize; + } + + /** HUF_getNbBitsFromCTable() : + * Read nbBits from CTable symbolTable, for symbol `symbolValue` presumed <= HUF_SYMBOLVALUE_MAX + * Note 1 : If symbolValue > HUF_readCTableHeader(symbolTable).maxSymbolValue, returns 0 + * Note 2 : is not inlined, as HUF_CElt definition is private + */ + private static uint HUF_getNbBitsFromCTable(nuint* CTable, uint symbolValue) + { + nuint* ct = CTable + 1; + assert(symbolValue <= 255); + if (symbolValue > HUF_readCTableHeader(CTable).maxSymbolValue) + return 0; + return (uint)HUF_getNbBits(ct[symbolValue]); + } + + /** + * HUF_setMaxHeight(): + * Try to enforce @targetNbBits on the Huffman tree described in @huffNode. + * + * It attempts to convert all nodes with nbBits > @targetNbBits + * to employ @targetNbBits instead. Then it adjusts the tree + * so that it remains a valid canonical Huffman tree. + * + * @pre The sum of the ranks of each symbol == 2^largestBits, + * where largestBits == huffNode[lastNonNull].nbBits. + * @post The sum of the ranks of each symbol == 2^largestBits, + * where largestBits is the return value (expected <= targetNbBits). + * + * @param huffNode The Huffman tree modified in place to enforce targetNbBits. + * It's presumed sorted, from most frequent to rarest symbol. + * @param lastNonNull The symbol with the lowest count in the Huffman tree. + * @param targetNbBits The allowed number of bits, which the Huffman tree + * may not respect. After this function the Huffman tree will + * respect targetNbBits. + * @return The maximum number of bits of the Huffman tree after adjustment. + */ + private static uint HUF_setMaxHeight(nodeElt_s* huffNode, uint lastNonNull, uint targetNbBits) + { + uint largestBits = huffNode[lastNonNull].nbBits; + if (largestBits <= targetNbBits) + return largestBits; + { + int totalCost = 0; + uint baseCost = (uint)(1 << (int)(largestBits - targetNbBits)); + int n = (int)lastNonNull; + while (huffNode[n].nbBits > targetNbBits) + { + totalCost += (int)(baseCost - (uint)(1 << (int)(largestBits - huffNode[n].nbBits))); + huffNode[n].nbBits = (byte)targetNbBits; + n--; + } + + assert(huffNode[n].nbBits <= targetNbBits); + while (huffNode[n].nbBits == targetNbBits) + --n; + assert(((uint)totalCost & baseCost - 1) == 0); + totalCost >>= (int)(largestBits - targetNbBits); + assert(totalCost > 0); + { + const uint noSymbol = 0xF0F0F0F0; + uint* rankLast = stackalloc uint[14]; + memset(rankLast, 0xF0, sizeof(uint) * 14); + { + uint currentNbBits = targetNbBits; + int pos; + for (pos = n; pos >= 0; pos--) + { + if (huffNode[pos].nbBits >= currentNbBits) + continue; + currentNbBits = huffNode[pos].nbBits; + rankLast[targetNbBits - currentNbBits] = (uint)pos; + } + } + + while (totalCost > 0) + { + /* Try to reduce the next power of 2 above totalCost because we + * gain back half the rank. + */ + uint nBitsToDecrease = ZSTD_highbit32((uint)totalCost) + 1; + for (; nBitsToDecrease > 1; nBitsToDecrease--) + { + uint highPos = rankLast[nBitsToDecrease]; + uint lowPos = rankLast[nBitsToDecrease - 1]; + if (highPos == noSymbol) + continue; + if (lowPos == noSymbol) + break; + { + uint highTotal = huffNode[highPos].count; + uint lowTotal = 2 * huffNode[lowPos].count; + if (highTotal <= lowTotal) + break; + } + } + + assert(rankLast[nBitsToDecrease] != noSymbol || nBitsToDecrease == 1); + while (nBitsToDecrease <= 12 && rankLast[nBitsToDecrease] == noSymbol) + nBitsToDecrease++; + assert(rankLast[nBitsToDecrease] != noSymbol); + totalCost -= 1 << (int)(nBitsToDecrease - 1); + huffNode[rankLast[nBitsToDecrease]].nbBits++; + if (rankLast[nBitsToDecrease - 1] == noSymbol) + rankLast[nBitsToDecrease - 1] = rankLast[nBitsToDecrease]; + if (rankLast[nBitsToDecrease] == 0) + rankLast[nBitsToDecrease] = noSymbol; + else + { + rankLast[nBitsToDecrease]--; + if ( + huffNode[rankLast[nBitsToDecrease]].nbBits + != targetNbBits - nBitsToDecrease + ) + rankLast[nBitsToDecrease] = noSymbol; + } + } + + while (totalCost < 0) + { + if (rankLast[1] == noSymbol) + { + while (huffNode[n].nbBits == targetNbBits) + n--; + huffNode[n + 1].nbBits--; + assert(n >= 0); + rankLast[1] = (uint)(n + 1); + totalCost++; + continue; + } + + huffNode[rankLast[1] + 1].nbBits--; + rankLast[1]++; + totalCost++; + } + } + } + + return targetNbBits; + } + + /* Return the appropriate bucket index for a given count. See definition of + * RANK_POSITION_DISTINCT_COUNT_CUTOFF for explanation of bucketing strategy. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint HUF_getIndex(uint count) + { + return count < 192 - 1 - 32 - 1 + ZSTD_highbit32(192 - 1 - 32 - 1) + ? count + : ZSTD_highbit32(count) + (192 - 1 - 32 - 1); + } + + /* Helper swap function for HUF_quickSortPartition() */ + private static void HUF_swapNodes(nodeElt_s* a, nodeElt_s* b) + { + nodeElt_s tmp = *a; + *a = *b; + *b = tmp; + } + + /* Returns 0 if the huffNode array is not sorted by descending count */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int HUF_isSorted(nodeElt_s* huffNode, uint maxSymbolValue1) + { + uint i; + for (i = 1; i < maxSymbolValue1; ++i) + { + if (huffNode[i].count > huffNode[i - 1].count) + { + return 0; + } + } + + return 1; + } + + /* Insertion sort by descending order */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HUF_insertionSort(nodeElt_s* huffNode, int low, int high) + { + int i; + int size = high - low + 1; + huffNode += low; + for (i = 1; i < size; ++i) + { + nodeElt_s key = huffNode[i]; + int j = i - 1; + while (j >= 0 && huffNode[j].count < key.count) + { + huffNode[j + 1] = huffNode[j]; + j--; + } + + huffNode[j + 1] = key; + } + } + + /* Pivot helper function for quicksort. */ + private static int HUF_quickSortPartition(nodeElt_s* arr, int low, int high) + { + /* Simply select rightmost element as pivot. "Better" selectors like + * median-of-three don't experimentally appear to have any benefit. + */ + uint pivot = arr[high].count; + int i = low - 1; + int j = low; + for (; j < high; j++) + { + if (arr[j].count > pivot) + { + i++; + HUF_swapNodes(&arr[i], &arr[j]); + } + } + + HUF_swapNodes(&arr[i + 1], &arr[high]); + return i + 1; + } + + /* Classic quicksort by descending with partially iterative calls + * to reduce worst case callstack size. + */ + private static void HUF_simpleQuickSort(nodeElt_s* arr, int low, int high) + { + const int kInsertionSortThreshold = 8; + if (high - low < kInsertionSortThreshold) + { + HUF_insertionSort(arr, low, high); + return; + } + + while (low < high) + { + int idx = HUF_quickSortPartition(arr, low, high); + if (idx - low < high - idx) + { + HUF_simpleQuickSort(arr, low, idx - 1); + low = idx + 1; + } + else + { + HUF_simpleQuickSort(arr, idx + 1, high); + high = idx - 1; + } + } + } + + /** + * HUF_sort(): + * Sorts the symbols [0, maxSymbolValue] by count[symbol] in decreasing order. + * This is a typical bucket sorting strategy that uses either quicksort or insertion sort to sort each bucket. + * + * @param[out] huffNode Sorted symbols by decreasing count. Only members `.count` and `.byte` are filled. + * Must have (maxSymbolValue + 1) entries. + * @param[in] count Histogram of the symbols. + * @param[in] maxSymbolValue Maximum symbol value. + * @param rankPosition This is a scratch workspace. Must have RANK_POSITION_TABLE_SIZE entries. + */ + private static void HUF_sort( + nodeElt_s* huffNode, + uint* count, + uint maxSymbolValue, + rankPos* rankPosition + ) + { + uint n; + uint maxSymbolValue1 = maxSymbolValue + 1; + memset(rankPosition, 0, (uint)(sizeof(rankPos) * 192)); + for (n = 0; n < maxSymbolValue1; ++n) + { + uint lowerRank = HUF_getIndex(count[n]); + assert(lowerRank < 192 - 1); + rankPosition[lowerRank].@base++; + } + + assert(rankPosition[192 - 1].@base == 0); + for (n = 192 - 1; n > 0; --n) + { + rankPosition[n - 1].@base += rankPosition[n].@base; + rankPosition[n - 1].curr = rankPosition[n - 1].@base; + } + + for (n = 0; n < maxSymbolValue1; ++n) + { + uint c = count[n]; + uint r = HUF_getIndex(c) + 1; + uint pos = rankPosition[r].curr++; + assert(pos < maxSymbolValue1); + huffNode[pos].count = c; + huffNode[pos].@byte = (byte)n; + } + + for (n = 192 - 1 - 32 - 1 + ZSTD_highbit32(192 - 1 - 32 - 1); n < 192 - 1; ++n) + { + int bucketSize = rankPosition[n].curr - rankPosition[n].@base; + uint bucketStartIdx = rankPosition[n].@base; + if (bucketSize > 1) + { + assert(bucketStartIdx < maxSymbolValue1); + HUF_simpleQuickSort(huffNode + bucketStartIdx, 0, bucketSize - 1); + } + } + + assert(HUF_isSorted(huffNode, maxSymbolValue1) != 0); + } + + /* HUF_buildTree(): + * Takes the huffNode array sorted by HUF_sort() and builds an unlimited-depth Huffman tree. + * + * @param huffNode The array sorted by HUF_sort(). Builds the Huffman tree in this array. + * @param maxSymbolValue The maximum symbol value. + * @return The smallest node in the Huffman tree (by count). + */ + private static int HUF_buildTree(nodeElt_s* huffNode, uint maxSymbolValue) + { + nodeElt_s* huffNode0 = huffNode - 1; + int nonNullRank; + int lowS, + lowN; + int nodeNb = 255 + 1; + int n, + nodeRoot; + nonNullRank = (int)maxSymbolValue; + while (huffNode[nonNullRank].count == 0) + nonNullRank--; + lowS = nonNullRank; + nodeRoot = nodeNb + lowS - 1; + lowN = nodeNb; + huffNode[nodeNb].count = huffNode[lowS].count + huffNode[lowS - 1].count; + huffNode[lowS].parent = huffNode[lowS - 1].parent = (ushort)nodeNb; + nodeNb++; + lowS -= 2; + for (n = nodeNb; n <= nodeRoot; n++) + huffNode[n].count = 1U << 30; + huffNode0[0].count = 1U << 31; + while (nodeNb <= nodeRoot) + { + int n1 = huffNode[lowS].count < huffNode[lowN].count ? lowS-- : lowN++; + int n2 = huffNode[lowS].count < huffNode[lowN].count ? lowS-- : lowN++; + huffNode[nodeNb].count = huffNode[n1].count + huffNode[n2].count; + huffNode[n1].parent = huffNode[n2].parent = (ushort)nodeNb; + nodeNb++; + } + + huffNode[nodeRoot].nbBits = 0; + for (n = nodeRoot - 1; n >= 255 + 1; n--) + huffNode[n].nbBits = (byte)(huffNode[huffNode[n].parent].nbBits + 1); + for (n = 0; n <= nonNullRank; n++) + huffNode[n].nbBits = (byte)(huffNode[huffNode[n].parent].nbBits + 1); + return nonNullRank; + } + + /** + * HUF_buildCTableFromTree(): + * Build the CTable given the Huffman tree in huffNode. + * + * @param[out] CTable The output Huffman CTable. + * @param huffNode The Huffman tree. + * @param nonNullRank The last and smallest node in the Huffman tree. + * @param maxSymbolValue The maximum symbol value. + * @param maxNbBits The exact maximum number of bits used in the Huffman tree. + */ + private static void HUF_buildCTableFromTree( + nuint* CTable, + nodeElt_s* huffNode, + int nonNullRank, + uint maxSymbolValue, + uint maxNbBits + ) + { + nuint* ct = CTable + 1; + /* fill result into ctable (val, nbBits) */ + int n; + ushort* nbPerRank = stackalloc ushort[13]; + memset(nbPerRank, 0, sizeof(ushort) * 13); + ushort* valPerRank = stackalloc ushort[13]; + memset(valPerRank, 0, sizeof(ushort) * 13); + int alphabetSize = (int)(maxSymbolValue + 1); + for (n = 0; n <= nonNullRank; n++) + nbPerRank[huffNode[n].nbBits]++; + { + ushort min = 0; + for (n = (int)maxNbBits; n > 0; n--) + { + valPerRank[n] = min; + min += nbPerRank[n]; + min >>= 1; + } + } + + for (n = 0; n < alphabetSize; n++) + HUF_setNbBits(ct + huffNode[n].@byte, huffNode[n].nbBits); + for (n = 0; n < alphabetSize; n++) + HUF_setValue(ct + n, valPerRank[HUF_getNbBits(ct[n])]++); + HUF_writeCTableHeader(CTable, maxNbBits, maxSymbolValue); + } + + private static nuint HUF_buildCTable_wksp( + nuint* CTable, + uint* count, + uint maxSymbolValue, + uint maxNbBits, + void* workSpace, + nuint wkspSize + ) + { + HUF_buildCTable_wksp_tables* wksp_tables = + (HUF_buildCTable_wksp_tables*)HUF_alignUpWorkspace(workSpace, &wkspSize, sizeof(uint)); + nodeElt_s* huffNode0 = &wksp_tables->huffNodeTbl.e0; + nodeElt_s* huffNode = huffNode0 + 1; + int nonNullRank; + if (wkspSize < (nuint)sizeof(HUF_buildCTable_wksp_tables)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_workSpace_tooSmall)); + if (maxNbBits == 0) + maxNbBits = 11; + if (maxSymbolValue > 255) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooLarge)); + memset(huffNode0, 0, (uint)(sizeof(nodeElt_s) * 512)); + HUF_sort(huffNode, count, maxSymbolValue, &wksp_tables->rankPosition.e0); + nonNullRank = HUF_buildTree(huffNode, maxSymbolValue); + maxNbBits = HUF_setMaxHeight(huffNode, (uint)nonNullRank, maxNbBits); + if (maxNbBits > 12) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + HUF_buildCTableFromTree(CTable, huffNode, nonNullRank, maxSymbolValue, maxNbBits); + return maxNbBits; + } + + private static nuint HUF_estimateCompressedSize(nuint* CTable, uint* count, uint maxSymbolValue) + { + nuint* ct = CTable + 1; + nuint nbBits = 0; + int s; + for (s = 0; s <= (int)maxSymbolValue; ++s) + { + nbBits += HUF_getNbBits(ct[s]) * count[s]; + } + + return nbBits >> 3; + } + + private static int HUF_validateCTable(nuint* CTable, uint* count, uint maxSymbolValue) + { + HUF_CTableHeader header = HUF_readCTableHeader(CTable); + nuint* ct = CTable + 1; + int bad = 0; + int s; + assert(header.tableLog <= 12); + if (header.maxSymbolValue < maxSymbolValue) + return 0; + for (s = 0; s <= (int)maxSymbolValue; ++s) + { + bad |= count[s] != 0 && HUF_getNbBits(ct[s]) == 0 ? 1 : 0; + } + + return bad == 0 ? 1 : 0; + } + + private static nuint HUF_compressBound(nuint size) + { + return 129 + (size + (size >> 8) + 8); + } + + /**! HUF_initCStream(): + * Initializes the bitstream. + * @returns 0 or an error code. + */ + private static nuint HUF_initCStream(ref HUF_CStream_t bitC, void* startPtr, nuint dstCapacity) + { + bitC = new HUF_CStream_t + { + startPtr = (byte*)startPtr, + ptr = (byte*)startPtr, + endPtr = (byte*)startPtr + dstCapacity - sizeof(nuint), + }; + if (dstCapacity <= (nuint)sizeof(nuint)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + return 0; + } + + /*! HUF_addBits(): + * Adds the symbol stored in HUF_CElt elt to the bitstream. + * + * @param elt The element we're adding. This is a (nbBits, value) pair. + * See the HUF_CStream_t docs for the format. + * @param idx Insert into the bitstream at this idx. + * @param kFast This is a template parameter. If the bitstream is guaranteed + * to have at least 4 unused bits after this call it may be 1, + * otherwise it must be 0. HUF_addBits() is faster when fast is set. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HUF_addBits( + ref nuint bitC_bitContainer_e0, + ref nuint bitC_bitPos_e0, + nuint elt, + int kFast + ) + { + assert(HUF_getNbBits(elt) <= 12); + bitC_bitContainer_e0 >>= (int)HUF_getNbBits(elt); + bitC_bitContainer_e0 |= kFast != 0 ? HUF_getValueFast(elt) : HUF_getValue(elt); + bitC_bitPos_e0 += HUF_getNbBitsFast(elt); + assert((bitC_bitPos_e0 & 0xFF) <= (nuint)(sizeof(nuint) * 8)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HUF_zeroIndex1(ref nuint bitC_bitContainer_e1, ref nuint bitC_bitPos_e1) + { + bitC_bitContainer_e1 = 0; + bitC_bitPos_e1 = 0; + } + + /*! HUF_mergeIndex1() : + * Merges the bit container @ index 1 into the bit container @ index 0 + * and zeros the bit container @ index 1. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HUF_mergeIndex1( + ref nuint bitC_bitContainer_e0, + ref nuint bitC_bitPos_e0, + ref nuint bitC_bitContainer_e1, + ref nuint bitC_bitPos_e1 + ) + { + assert((bitC_bitPos_e1 & 0xFF) < (nuint)(sizeof(nuint) * 8)); + bitC_bitContainer_e0 >>= (int)(bitC_bitPos_e1 & 0xFF); + bitC_bitContainer_e0 |= bitC_bitContainer_e1; + bitC_bitPos_e0 += bitC_bitPos_e1; + assert((bitC_bitPos_e0 & 0xFF) <= (nuint)(sizeof(nuint) * 8)); + } + + /*! HUF_flushBits() : + * Flushes the bits in the bit container @ index 0. + * + * @post bitPos will be < 8. + * @param kFast If kFast is set then we must know a-priori that + * the bit container will not overflow. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HUF_flushBits( + ref nuint bitC_bitContainer_e0, + ref nuint bitC_bitPos_e0, + ref byte* bitC_ptr, + byte* bitC_endPtr, + int kFast + ) + { + /* The upper bits of bitPos are noisy, so we must mask by 0xFF. */ + nuint nbBits = bitC_bitPos_e0 & 0xFF; + nuint nbBytes = nbBits >> 3; + /* The top nbBits bits of bitContainer are the ones we need. */ + nuint bitContainer = bitC_bitContainer_e0 >> (int)((nuint)(sizeof(nuint) * 8) - nbBits); + bitC_bitPos_e0 &= 7; + assert(nbBits > 0); + assert(nbBits <= (nuint)(sizeof(nuint) * 8)); + assert(bitC_ptr <= bitC_endPtr); + MEM_writeLEST(bitC_ptr, bitContainer); + bitC_ptr += nbBytes; + assert(kFast == 0 || bitC_ptr <= bitC_endPtr); + if (kFast == 0 && bitC_ptr > bitC_endPtr) + bitC_ptr = bitC_endPtr; + } + + /*! HUF_endMark() + * @returns The Huffman stream end mark: A 1-bit value = 1. + */ + private static nuint HUF_endMark() + { + nuint endMark; + HUF_setNbBits(&endMark, 1); + HUF_setValue(&endMark, 1); + return endMark; + } + + /*! HUF_closeCStream() : + * @return Size of CStream, in bytes, + * or 0 if it could not fit into dstBuffer */ + private static nuint HUF_closeCStream(ref HUF_CStream_t bitC) + { + HUF_addBits(ref bitC.bitContainer.e0, ref bitC.bitPos.e0, HUF_endMark(), 0); + HUF_flushBits(ref bitC.bitContainer.e0, ref bitC.bitPos.e0, ref bitC.ptr, bitC.endPtr, 0); + { + nuint nbBits = bitC.bitPos.e0 & 0xFF; + if (bitC.ptr >= bitC.endPtr) + return 0; + return (nuint)(bitC.ptr - bitC.startPtr) + (nuint)(nbBits > 0 ? 1 : 0); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HUF_encodeSymbol( + ref nuint bitCPtr_bitContainer_e0, + ref nuint bitCPtr_bitPos_e0, + uint symbol, + nuint* CTable, + int fast + ) + { + HUF_addBits(ref bitCPtr_bitContainer_e0, ref bitCPtr_bitPos_e0, CTable[symbol], fast); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HUF_compress1X_usingCTable_internal_body_loop( + ref HUF_CStream_t bitC, + byte* ip, + nuint srcSize, + nuint* ct, + int kUnroll, + int kFastFlush, + int kLastFast + ) + { + byte* bitC_ptr = bitC.ptr; + byte* bitC_endPtr = bitC.endPtr; + nuint bitC_bitContainer_e0 = bitC.bitContainer.e0; + nuint bitC_bitPos_e0 = bitC.bitPos.e0; + nuint bitC_bitContainer_e1 = bitC.bitContainer.e1; + nuint bitC_bitPos_e1 = bitC.bitPos.e1; + /* Join to kUnroll */ + int n = (int)srcSize; + int rem = n % kUnroll; + if (rem > 0) + { + for (; rem > 0; --rem) + { + HUF_encodeSymbol(ref bitC_bitContainer_e0, ref bitC_bitPos_e0, ip[--n], ct, 0); + } + + HUF_flushBits( + ref bitC_bitContainer_e0, + ref bitC_bitPos_e0, + ref bitC_ptr, + bitC_endPtr, + kFastFlush + ); + } + + assert(n % kUnroll == 0); + if (n % (2 * kUnroll) != 0) + { + int u; + for (u = 1; u < kUnroll; ++u) + { + HUF_encodeSymbol(ref bitC_bitContainer_e0, ref bitC_bitPos_e0, ip[n - u], ct, 1); + } + + HUF_encodeSymbol( + ref bitC_bitContainer_e0, + ref bitC_bitPos_e0, + ip[n - kUnroll], + ct, + kLastFast + ); + HUF_flushBits( + ref bitC_bitContainer_e0, + ref bitC_bitPos_e0, + ref bitC_ptr, + bitC_endPtr, + kFastFlush + ); + n -= kUnroll; + } + + assert(n % (2 * kUnroll) == 0); + for (; n > 0; n -= 2 * kUnroll) + { + /* Encode kUnroll symbols into the bitstream @ index 0. */ + int u; + for (u = 1; u < kUnroll; ++u) + { + HUF_encodeSymbol(ref bitC_bitContainer_e0, ref bitC_bitPos_e0, ip[n - u], ct, 1); + } + + HUF_encodeSymbol( + ref bitC_bitContainer_e0, + ref bitC_bitPos_e0, + ip[n - kUnroll], + ct, + kLastFast + ); + HUF_flushBits( + ref bitC_bitContainer_e0, + ref bitC_bitPos_e0, + ref bitC_ptr, + bitC_endPtr, + kFastFlush + ); + HUF_zeroIndex1(ref bitC_bitContainer_e1, ref bitC_bitPos_e1); + for (u = 1; u < kUnroll; ++u) + { + HUF_encodeSymbol( + ref bitC_bitContainer_e1, + ref bitC_bitPos_e1, + ip[n - kUnroll - u], + ct, + 1 + ); + } + + HUF_encodeSymbol( + ref bitC_bitContainer_e1, + ref bitC_bitPos_e1, + ip[n - kUnroll - kUnroll], + ct, + kLastFast + ); + HUF_mergeIndex1( + ref bitC_bitContainer_e0, + ref bitC_bitPos_e0, + ref bitC_bitContainer_e1, + ref bitC_bitPos_e1 + ); + HUF_flushBits( + ref bitC_bitContainer_e0, + ref bitC_bitPos_e0, + ref bitC_ptr, + bitC_endPtr, + kFastFlush + ); + } + + assert(n == 0); + bitC.ptr = bitC_ptr; + bitC.endPtr = bitC_endPtr; + bitC.bitContainer.e0 = bitC_bitContainer_e0; + bitC.bitPos.e0 = bitC_bitPos_e0; + bitC.bitContainer.e1 = bitC_bitContainer_e1; + bitC.bitPos.e1 = bitC_bitPos_e1; + } + + /** + * Returns a tight upper bound on the output space needed by Huffman + * with 8 bytes buffer to handle over-writes. If the output is at least + * this large we don't need to do bounds checks during Huffman encoding. + */ + private static nuint HUF_tightCompressBound(nuint srcSize, nuint tableLog) + { + return (srcSize * tableLog >> 3) + 8; + } + + private static nuint HUF_compress1X_usingCTable_internal_body( + void* dst, + nuint dstSize, + void* src, + nuint srcSize, + nuint* CTable + ) + { + uint tableLog = HUF_readCTableHeader(CTable).tableLog; + nuint* ct = CTable + 1; + byte* ip = (byte*)src; + byte* ostart = (byte*)dst; + byte* oend = ostart + dstSize; + HUF_CStream_t bitC; + System.Runtime.CompilerServices.Unsafe.SkipInit(out bitC); + if (dstSize < 8) + return 0; + { + byte* op = ostart; + nuint initErr = HUF_initCStream(ref bitC, op, (nuint)(oend - op)); + if (ERR_isError(initErr)) + return 0; + } + + if (dstSize < HUF_tightCompressBound(srcSize, tableLog) || tableLog > 11) + HUF_compress1X_usingCTable_internal_body_loop( + ref bitC, + ip, + srcSize, + ct, + MEM_32bits ? 2 : 4, + 0, + 0 + ); + else + { + if (MEM_32bits) + { + switch (tableLog) + { + case 11: + HUF_compress1X_usingCTable_internal_body_loop( + ref bitC, + ip, + srcSize, + ct, + 2, + 1, + 0 + ); + break; + case 10: + case 9: + case 8: + HUF_compress1X_usingCTable_internal_body_loop( + ref bitC, + ip, + srcSize, + ct, + 2, + 1, + 1 + ); + break; + case 7: + default: + HUF_compress1X_usingCTable_internal_body_loop( + ref bitC, + ip, + srcSize, + ct, + 3, + 1, + 1 + ); + break; + } + } + else + { + switch (tableLog) + { + case 11: + HUF_compress1X_usingCTable_internal_body_loop( + ref bitC, + ip, + srcSize, + ct, + 5, + 1, + 0 + ); + break; + case 10: + HUF_compress1X_usingCTable_internal_body_loop( + ref bitC, + ip, + srcSize, + ct, + 5, + 1, + 1 + ); + break; + case 9: + HUF_compress1X_usingCTable_internal_body_loop( + ref bitC, + ip, + srcSize, + ct, + 6, + 1, + 0 + ); + break; + case 8: + HUF_compress1X_usingCTable_internal_body_loop( + ref bitC, + ip, + srcSize, + ct, + 7, + 1, + 0 + ); + break; + case 7: + HUF_compress1X_usingCTable_internal_body_loop( + ref bitC, + ip, + srcSize, + ct, + 8, + 1, + 0 + ); + break; + case 6: + default: + HUF_compress1X_usingCTable_internal_body_loop( + ref bitC, + ip, + srcSize, + ct, + 9, + 1, + 1 + ); + break; + } + } + } + + assert(bitC.ptr <= bitC.endPtr); + return HUF_closeCStream(ref bitC); + } + + private static nuint HUF_compress1X_usingCTable_internal( + void* dst, + nuint dstSize, + void* src, + nuint srcSize, + nuint* CTable, + int flags + ) + { + return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable); + } + + /* ====================== */ + /* single stream variants */ + /* ====================== */ + private static nuint HUF_compress1X_usingCTable( + void* dst, + nuint dstSize, + void* src, + nuint srcSize, + nuint* CTable, + int flags + ) + { + return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, flags); + } + + private static nuint HUF_compress4X_usingCTable_internal( + void* dst, + nuint dstSize, + void* src, + nuint srcSize, + nuint* CTable, + int flags + ) + { + /* first 3 segments */ + nuint segmentSize = (srcSize + 3) / 4; + byte* ip = (byte*)src; + byte* iend = ip + srcSize; + byte* ostart = (byte*)dst; + byte* oend = ostart + dstSize; + byte* op = ostart; + if (dstSize < 6 + 1 + 1 + 1 + 8) + return 0; + if (srcSize < 12) + return 0; + op += 6; + assert(op <= oend); + { + nuint cSize = HUF_compress1X_usingCTable_internal( + op, + (nuint)(oend - op), + ip, + segmentSize, + CTable, + flags + ); + if (ERR_isError(cSize)) + return cSize; + if (cSize == 0 || cSize > 65535) + return 0; + MEM_writeLE16(ostart, (ushort)cSize); + op += cSize; + } + + ip += segmentSize; + assert(op <= oend); + { + nuint cSize = HUF_compress1X_usingCTable_internal( + op, + (nuint)(oend - op), + ip, + segmentSize, + CTable, + flags + ); + if (ERR_isError(cSize)) + return cSize; + if (cSize == 0 || cSize > 65535) + return 0; + MEM_writeLE16(ostart + 2, (ushort)cSize); + op += cSize; + } + + ip += segmentSize; + assert(op <= oend); + { + nuint cSize = HUF_compress1X_usingCTable_internal( + op, + (nuint)(oend - op), + ip, + segmentSize, + CTable, + flags + ); + if (ERR_isError(cSize)) + return cSize; + if (cSize == 0 || cSize > 65535) + return 0; + MEM_writeLE16(ostart + 4, (ushort)cSize); + op += cSize; + } + + ip += segmentSize; + assert(op <= oend); + assert(ip <= iend); + { + nuint cSize = HUF_compress1X_usingCTable_internal( + op, + (nuint)(oend - op), + ip, + (nuint)(iend - ip), + CTable, + flags + ); + if (ERR_isError(cSize)) + return cSize; + if (cSize == 0 || cSize > 65535) + return 0; + op += cSize; + } + + return (nuint)(op - ostart); + } + + private static nuint HUF_compress4X_usingCTable( + void* dst, + nuint dstSize, + void* src, + nuint srcSize, + nuint* CTable, + int flags + ) + { + return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, flags); + } + + private static nuint HUF_compressCTable_internal( + byte* ostart, + byte* op, + byte* oend, + void* src, + nuint srcSize, + HUF_nbStreams_e nbStreams, + nuint* CTable, + int flags + ) + { + nuint cSize = + nbStreams == HUF_nbStreams_e.HUF_singleStream + ? HUF_compress1X_usingCTable_internal( + op, + (nuint)(oend - op), + src, + srcSize, + CTable, + flags + ) + : HUF_compress4X_usingCTable_internal( + op, + (nuint)(oend - op), + src, + srcSize, + CTable, + flags + ); + if (ERR_isError(cSize)) + { + return cSize; + } + + if (cSize == 0) + { + return 0; + } + + op += cSize; + assert(op >= ostart); + if ((nuint)(op - ostart) >= srcSize - 1) + { + return 0; + } + + return (nuint)(op - ostart); + } + + private static uint HUF_cardinality(uint* count, uint maxSymbolValue) + { + uint cardinality = 0; + uint i; + for (i = 0; i < maxSymbolValue + 1; i++) + { + if (count[i] != 0) + cardinality += 1; + } + + return cardinality; + } + + /*! HUF_compress() does the following: + * 1. count symbol occurrence from source[] into table count[] using FSE_count() (exposed within "fse.h") + * 2. (optional) refine tableLog using HUF_optimalTableLog() + * 3. build Huffman table from count using HUF_buildCTable() + * 4. save Huffman table to memory buffer using HUF_writeCTable() + * 5. encode the data stream using HUF_compress4X_usingCTable() + * + * The following API allows targeting specific sub-functions for advanced tasks. + * For example, it's possible to compress several blocks using the same 'CTable', + * or to save and regenerate 'CTable' using external methods. + */ + private static uint HUF_minTableLog(uint symbolCardinality) + { + uint minBitsSymbols = ZSTD_highbit32(symbolCardinality) + 1; + return minBitsSymbols; + } + + private static uint HUF_optimalTableLog( + uint maxTableLog, + nuint srcSize, + uint maxSymbolValue, + void* workSpace, + nuint wkspSize, + nuint* table, + uint* count, + int flags + ) + { + assert(srcSize > 1); + assert(wkspSize >= (nuint)sizeof(HUF_buildCTable_wksp_tables)); + if ((flags & (int)HUF_flags_e.HUF_flags_optimalDepth) == 0) + { + return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1); + } + + { + byte* dst = (byte*)workSpace + sizeof(HUF_WriteCTableWksp); + nuint dstSize = wkspSize - (nuint)sizeof(HUF_WriteCTableWksp); + nuint hSize, + newSize; + uint symbolCardinality = HUF_cardinality(count, maxSymbolValue); + uint minTableLog = HUF_minTableLog(symbolCardinality); + nuint optSize = unchecked((nuint)~0) - 1; + uint optLog = maxTableLog, + optLogGuess; + for (optLogGuess = minTableLog; optLogGuess <= maxTableLog; optLogGuess++) + { + { + nuint maxBits = HUF_buildCTable_wksp( + table, + count, + maxSymbolValue, + optLogGuess, + workSpace, + wkspSize + ); + if (ERR_isError(maxBits)) + continue; + if (maxBits < optLogGuess && optLogGuess > minTableLog) + break; + hSize = HUF_writeCTable_wksp( + dst, + dstSize, + table, + maxSymbolValue, + (uint)maxBits, + workSpace, + wkspSize + ); + } + + if (ERR_isError(hSize)) + continue; + newSize = HUF_estimateCompressedSize(table, count, maxSymbolValue) + hSize; + if (newSize > optSize + 1) + { + break; + } + + if (newSize < optSize) + { + optSize = newSize; + optLog = optLogGuess; + } + } + + assert(optLog <= 12); + return optLog; + } + } + + /* HUF_compress_internal() : + * `workSpace_align4` must be aligned on 4-bytes boundaries, + * and occupies the same space as a table of HUF_WORKSPACE_SIZE_U64 unsigned */ + private static nuint HUF_compress_internal( + void* dst, + nuint dstSize, + void* src, + nuint srcSize, + uint maxSymbolValue, + uint huffLog, + HUF_nbStreams_e nbStreams, + void* workSpace, + nuint wkspSize, + nuint* oldHufTable, + HUF_repeat* repeat, + int flags + ) + { + HUF_compress_tables_t* table = (HUF_compress_tables_t*)HUF_alignUpWorkspace( + workSpace, + &wkspSize, + sizeof(ulong) + ); + byte* ostart = (byte*)dst; + byte* oend = ostart + dstSize; + byte* op = ostart; + if (wkspSize < (nuint)sizeof(HUF_compress_tables_t)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_workSpace_tooSmall)); + if (srcSize == 0) + return 0; + if (dstSize == 0) + return 0; + if (srcSize > 128 * 1024) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + if (huffLog > 12) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + if (maxSymbolValue > 255) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxSymbolValue_tooLarge)); + if (maxSymbolValue == 0) + maxSymbolValue = 255; + if (huffLog == 0) + huffLog = 11; + if ( + (flags & (int)HUF_flags_e.HUF_flags_preferRepeat) != 0 + && repeat != null + && *repeat == HUF_repeat.HUF_repeat_valid + ) + { + return HUF_compressCTable_internal( + ostart, + op, + oend, + src, + srcSize, + nbStreams, + oldHufTable, + flags + ); + } + + if ((flags & (int)HUF_flags_e.HUF_flags_suspectUncompressible) != 0 && srcSize >= 4096 * 10) + { + nuint largestTotal = 0; + { + uint maxSymbolValueBegin = maxSymbolValue; + nuint largestBegin = HIST_count_simple( + table->count, + &maxSymbolValueBegin, + (byte*)src, + 4096 + ); + if (ERR_isError(largestBegin)) + return largestBegin; + largestTotal += largestBegin; + } + + { + uint maxSymbolValueEnd = maxSymbolValue; + nuint largestEnd = HIST_count_simple( + table->count, + &maxSymbolValueEnd, + (byte*)src + srcSize - 4096, + 4096 + ); + if (ERR_isError(largestEnd)) + return largestEnd; + largestTotal += largestEnd; + } + + if (largestTotal <= (2 * 4096 >> 7) + 4) + return 0; + } + + { + nuint largest = HIST_count_wksp( + table->count, + &maxSymbolValue, + (byte*)src, + srcSize, + table->wksps.hist_wksp, + sizeof(uint) * 1024 + ); + if (ERR_isError(largest)) + return largest; + if (largest == srcSize) + { + *ostart = ((byte*)src)[0]; + return 1; + } + + if (largest <= (srcSize >> 7) + 4) + return 0; + } + + if ( + repeat != null + && *repeat == HUF_repeat.HUF_repeat_check + && HUF_validateCTable(oldHufTable, table->count, maxSymbolValue) == 0 + ) + { + *repeat = HUF_repeat.HUF_repeat_none; + } + + if ( + (flags & (int)HUF_flags_e.HUF_flags_preferRepeat) != 0 + && repeat != null + && *repeat != HUF_repeat.HUF_repeat_none + ) + { + return HUF_compressCTable_internal( + ostart, + op, + oend, + src, + srcSize, + nbStreams, + oldHufTable, + flags + ); + } + + huffLog = HUF_optimalTableLog( + huffLog, + srcSize, + maxSymbolValue, + &table->wksps, + (nuint)sizeof(_wksps_e__Union), + &table->CTable.e0, + table->count, + flags + ); + { + nuint maxBits = HUF_buildCTable_wksp( + &table->CTable.e0, + table->count, + maxSymbolValue, + huffLog, + &table->wksps.buildCTable_wksp, + (nuint)sizeof(HUF_buildCTable_wksp_tables) + ); + { + nuint _var_err__ = maxBits; + if (ERR_isError(_var_err__)) + return _var_err__; + } + + huffLog = (uint)maxBits; + } + + { + nuint hSize = HUF_writeCTable_wksp( + op, + dstSize, + &table->CTable.e0, + maxSymbolValue, + huffLog, + &table->wksps.writeCTable_wksp, + (nuint)sizeof(HUF_WriteCTableWksp) + ); + if (ERR_isError(hSize)) + return hSize; + if (repeat != null && *repeat != HUF_repeat.HUF_repeat_none) + { + nuint oldSize = HUF_estimateCompressedSize( + oldHufTable, + table->count, + maxSymbolValue + ); + nuint newSize = HUF_estimateCompressedSize( + &table->CTable.e0, + table->count, + maxSymbolValue + ); + if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) + { + return HUF_compressCTable_internal( + ostart, + op, + oend, + src, + srcSize, + nbStreams, + oldHufTable, + flags + ); + } + } + + if (hSize + 12U >= srcSize) + { + return 0; + } + + op += hSize; + if (repeat != null) + { + *repeat = HUF_repeat.HUF_repeat_none; + } + + if (oldHufTable != null) + memcpy(oldHufTable, &table->CTable.e0, sizeof(ulong) * 257); + } + + return HUF_compressCTable_internal( + ostart, + op, + oend, + src, + srcSize, + nbStreams, + &table->CTable.e0, + flags + ); + } + + /** HUF_compress1X_repeat() : + * Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. + * If it uses hufTable it does not modify hufTable or repeat. + * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. + * If preferRepeat then the old table will always be used if valid. + * If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding */ + private static nuint HUF_compress1X_repeat( + void* dst, + nuint dstSize, + void* src, + nuint srcSize, + uint maxSymbolValue, + uint huffLog, + void* workSpace, + nuint wkspSize, + nuint* hufTable, + HUF_repeat* repeat, + int flags + ) + { + return HUF_compress_internal( + dst, + dstSize, + src, + srcSize, + maxSymbolValue, + huffLog, + HUF_nbStreams_e.HUF_singleStream, + workSpace, + wkspSize, + hufTable, + repeat, + flags + ); + } + + /* HUF_compress4X_repeat(): + * compress input using 4 streams. + * consider skipping quickly + * reuse an existing huffman compression table */ + private static nuint HUF_compress4X_repeat( + void* dst, + nuint dstSize, + void* src, + nuint srcSize, + uint maxSymbolValue, + uint huffLog, + void* workSpace, + nuint wkspSize, + nuint* hufTable, + HUF_repeat* repeat, + int flags + ) + { + return HUF_compress_internal( + dst, + dstSize, + src, + srcSize, + maxSymbolValue, + huffLog, + HUF_nbStreams_e.HUF_fourStreams, + workSpace, + wkspSize, + hufTable, + repeat, + flags + ); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/HufDecompress.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/HufDecompress.cs new file mode 100644 index 00000000..26278708 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/HufDecompress.cs @@ -0,0 +1,2505 @@ +using System; +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + private static DTableDesc HUF_getDTableDesc(uint* table) + { + DTableDesc dtd; + memcpy(&dtd, table, (uint)sizeof(DTableDesc)); + return dtd; + } + + private static nuint HUF_initFastDStream(byte* ip) + { + byte lastByte = ip[7]; + nuint bitsConsumed = lastByte != 0 ? 8 - ZSTD_highbit32(lastByte) : 0; + nuint value = MEM_readLEST(ip) | 1; + assert(bitsConsumed <= 8); + assert(sizeof(nuint) == 8); + return value << (int)bitsConsumed; + } + + /** + * Initializes args for the fast decoding loop. + * @returns 1 on success + * 0 if the fallback implementation should be used. + * Or an error code on failure. + */ + private static nuint HUF_DecompressFastArgs_init( + HUF_DecompressFastArgs* args, + void* dst, + nuint dstSize, + void* src, + nuint srcSize, + uint* DTable + ) + { + void* dt = DTable + 1; + uint dtLog = HUF_getDTableDesc(DTable).tableLog; + byte* istart = (byte*)src; + byte* oend = ZSTD_maybeNullPtrAdd((byte*)dst, (nint)dstSize); + if (!BitConverter.IsLittleEndian || MEM_32bits) + return 0; + if (dstSize == 0) + return 0; + assert(dst != null); + if (srcSize < 10) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (dtLog != 11) + return 0; + { + nuint length1 = MEM_readLE16(istart); + nuint length2 = MEM_readLE16(istart + 2); + nuint length3 = MEM_readLE16(istart + 4); + nuint length4 = srcSize - (length1 + length2 + length3 + 6); + args->iend.e0 = istart + 6; + args->iend.e1 = args->iend.e0 + length1; + args->iend.e2 = args->iend.e1 + length2; + args->iend.e3 = args->iend.e2 + length3; + if (length1 < 8 || length2 < 8 || length3 < 8 || length4 < 8) + return 0; + if (length4 > srcSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + args->ip.e0 = args->iend.e1 - sizeof(ulong); + args->ip.e1 = args->iend.e2 - sizeof(ulong); + args->ip.e2 = args->iend.e3 - sizeof(ulong); + args->ip.e3 = (byte*)src + srcSize - sizeof(ulong); + args->op.e0 = (byte*)dst; + args->op.e1 = args->op.e0 + (dstSize + 3) / 4; + args->op.e2 = args->op.e1 + (dstSize + 3) / 4; + args->op.e3 = args->op.e2 + (dstSize + 3) / 4; + if (args->op.e3 >= oend) + return 0; + args->bits[0] = HUF_initFastDStream(args->ip.e0); + args->bits[1] = HUF_initFastDStream(args->ip.e1); + args->bits[2] = HUF_initFastDStream(args->ip.e2); + args->bits[3] = HUF_initFastDStream(args->ip.e3); + args->ilowest = istart; + args->oend = oend; + args->dt = dt; + return 1; + } + + private static nuint HUF_initRemainingDStream( + BIT_DStream_t* bit, + HUF_DecompressFastArgs* args, + int stream, + byte* segmentEnd + ) + { + if ((&args->op.e0)[stream] > segmentEnd) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if ((&args->ip.e0)[stream] < (&args->iend.e0)[stream] - 8) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + assert(sizeof(nuint) == 8); + bit->bitContainer = MEM_readLEST((&args->ip.e0)[stream]); + bit->bitsConsumed = ZSTD_countTrailingZeros64(args->bits[stream]); + bit->start = (sbyte*)args->ilowest; + bit->limitPtr = bit->start + sizeof(nuint); + bit->ptr = (sbyte*)(&args->ip.e0)[stream]; + return 0; + } + + /** + * Packs 4 HUF_DEltX1 structs into a U64. This is used to lay down 4 entries at + * a time. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong HUF_DEltX1_set4(byte symbol, byte nbBits) + { + ulong D4; + if (BitConverter.IsLittleEndian) + { + D4 = (ulong)((symbol << 8) + nbBits); + } + else + { + D4 = (ulong)(symbol + (nbBits << 8)); + } + + assert(D4 < 1U << 16); + D4 *= 0x0001000100010001UL; + return D4; + } + + /** + * Increase the tableLog to targetTableLog and rescales the stats. + * If tableLog > targetTableLog this is a no-op. + * @returns New tableLog + */ + private static uint HUF_rescaleStats( + byte* huffWeight, + uint* rankVal, + uint nbSymbols, + uint tableLog, + uint targetTableLog + ) + { + if (tableLog > targetTableLog) + return tableLog; + if (tableLog < targetTableLog) + { + uint scale = targetTableLog - tableLog; + uint s; + for (s = 0; s < nbSymbols; ++s) + { + huffWeight[s] += (byte)(huffWeight[s] == 0 ? 0 : scale); + } + + for (s = targetTableLog; s > scale; --s) + { + rankVal[s] = rankVal[s - scale]; + } + + for (s = scale; s > 0; --s) + { + rankVal[s] = 0; + } + } + + return targetTableLog; + } + + private static nuint HUF_readDTableX1_wksp( + uint* DTable, + void* src, + nuint srcSize, + void* workSpace, + nuint wkspSize, + int flags + ) + { + uint tableLog = 0; + uint nbSymbols = 0; + nuint iSize; + void* dtPtr = DTable + 1; + HUF_DEltX1* dt = (HUF_DEltX1*)dtPtr; + HUF_ReadDTableX1_Workspace* wksp = (HUF_ReadDTableX1_Workspace*)workSpace; + if ((nuint)sizeof(HUF_ReadDTableX1_Workspace) > wkspSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + iSize = HUF_readStats_wksp( + wksp->huffWeight, + 255 + 1, + wksp->rankVal, + &nbSymbols, + &tableLog, + src, + srcSize, + wksp->statsWksp, + sizeof(uint) * 219, + flags + ); + if (ERR_isError(iSize)) + return iSize; + { + DTableDesc dtd = HUF_getDTableDesc(DTable); + uint maxTableLog = (uint)(dtd.maxTableLog + 1); + uint targetTableLog = maxTableLog < 11 ? maxTableLog : 11; + tableLog = HUF_rescaleStats( + wksp->huffWeight, + wksp->rankVal, + nbSymbols, + tableLog, + targetTableLog + ); + if (tableLog > (uint)(dtd.maxTableLog + 1)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + dtd.tableType = 0; + dtd.tableLog = (byte)tableLog; + memcpy(DTable, &dtd, (uint)sizeof(DTableDesc)); + } + + { + int n; + uint nextRankStart = 0; + const int unroll = 4; + int nLimit = (int)nbSymbols - unroll + 1; + for (n = 0; n < (int)tableLog + 1; n++) + { + uint curr = nextRankStart; + nextRankStart += wksp->rankVal[n]; + wksp->rankStart[n] = curr; + } + + for (n = 0; n < nLimit; n += unroll) + { + int u; + for (u = 0; u < unroll; ++u) + { + nuint w = wksp->huffWeight[n + u]; + wksp->symbols[wksp->rankStart[w]++] = (byte)(n + u); + } + } + + for (; n < (int)nbSymbols; ++n) + { + nuint w = wksp->huffWeight[n]; + wksp->symbols[wksp->rankStart[w]++] = (byte)n; + } + } + + { + uint w; + int symbol = (int)wksp->rankVal[0]; + int rankStart = 0; + for (w = 1; w < tableLog + 1; ++w) + { + int symbolCount = (int)wksp->rankVal[w]; + int length = 1 << (int)w >> 1; + int uStart = rankStart; + byte nbBits = (byte)(tableLog + 1 - w); + int s; + int u; + switch (length) + { + case 1: + for (s = 0; s < symbolCount; ++s) + { + HUF_DEltX1 D; + D.@byte = wksp->symbols[symbol + s]; + D.nbBits = nbBits; + dt[uStart] = D; + uStart += 1; + } + + break; + case 2: + for (s = 0; s < symbolCount; ++s) + { + HUF_DEltX1 D; + D.@byte = wksp->symbols[symbol + s]; + D.nbBits = nbBits; + dt[uStart + 0] = D; + dt[uStart + 1] = D; + uStart += 2; + } + + break; + case 4: + for (s = 0; s < symbolCount; ++s) + { + ulong D4 = HUF_DEltX1_set4(wksp->symbols[symbol + s], nbBits); + MEM_write64(dt + uStart, D4); + uStart += 4; + } + + break; + case 8: + for (s = 0; s < symbolCount; ++s) + { + ulong D4 = HUF_DEltX1_set4(wksp->symbols[symbol + s], nbBits); + MEM_write64(dt + uStart, D4); + MEM_write64(dt + uStart + 4, D4); + uStart += 8; + } + + break; + default: + for (s = 0; s < symbolCount; ++s) + { + ulong D4 = HUF_DEltX1_set4(wksp->symbols[symbol + s], nbBits); + for (u = 0; u < length; u += 16) + { + MEM_write64(dt + uStart + u + 0, D4); + MEM_write64(dt + uStart + u + 4, D4); + MEM_write64(dt + uStart + u + 8, D4); + MEM_write64(dt + uStart + u + 12, D4); + } + + assert(u == length); + uStart += length; + } + + break; + } + + symbol += symbolCount; + rankStart += symbolCount * length; + } + } + + return iSize; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte HUF_decodeSymbolX1(BIT_DStream_t* Dstream, HUF_DEltX1* dt, uint dtLog) + { + /* note : dtLog >= 1 */ + nuint val = BIT_lookBitsFast(Dstream, dtLog); + byte c = dt[val].@byte; + BIT_skipBits(Dstream, dt[val].nbBits); + return c; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint HUF_decodeStreamX1( + byte* p, + BIT_DStream_t* bitDPtr, + byte* pEnd, + HUF_DEltX1* dt, + uint dtLog + ) + { + byte* pStart = p; + if (pEnd - p > 3) + { + while ( + BIT_reloadDStream(bitDPtr) == BIT_DStream_status.BIT_DStream_unfinished + && p < pEnd - 3 + ) + { + if (MEM_64bits) + *p++ = HUF_decodeSymbolX1(bitDPtr, dt, dtLog); + *p++ = HUF_decodeSymbolX1(bitDPtr, dt, dtLog); + if (MEM_64bits) + *p++ = HUF_decodeSymbolX1(bitDPtr, dt, dtLog); + *p++ = HUF_decodeSymbolX1(bitDPtr, dt, dtLog); + } + } + else + { + BIT_reloadDStream(bitDPtr); + } + + if (MEM_32bits) + while ( + BIT_reloadDStream(bitDPtr) == BIT_DStream_status.BIT_DStream_unfinished && p < pEnd + ) + *p++ = HUF_decodeSymbolX1(bitDPtr, dt, dtLog); + while (p < pEnd) + *p++ = HUF_decodeSymbolX1(bitDPtr, dt, dtLog); + return (nuint)(pEnd - pStart); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint HUF_decompress1X1_usingDTable_internal_body( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable + ) + { + byte* op = (byte*)dst; + byte* oend = ZSTD_maybeNullPtrAdd(op, (nint)dstSize); + void* dtPtr = DTable + 1; + HUF_DEltX1* dt = (HUF_DEltX1*)dtPtr; + BIT_DStream_t bitD; + DTableDesc dtd = HUF_getDTableDesc(DTable); + uint dtLog = dtd.tableLog; + { + nuint _var_err__ = BIT_initDStream(&bitD, cSrc, cSrcSize); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + HUF_decodeStreamX1(op, &bitD, oend, dt, dtLog); + if (BIT_endOfDStream(&bitD) == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + return dstSize; + } + + /* HUF_decompress4X1_usingDTable_internal_body(): + * Conditions : + * @dstSize >= 6 + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint HUF_decompress4X1_usingDTable_internal_body( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable + ) + { + if (cSrcSize < 10) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (dstSize < 6) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + { + byte* istart = (byte*)cSrc; + byte* ostart = (byte*)dst; + byte* oend = ostart + dstSize; + byte* olimit = oend - 3; + void* dtPtr = DTable + 1; + HUF_DEltX1* dt = (HUF_DEltX1*)dtPtr; + /* Init */ + BIT_DStream_t bitD1; + BIT_DStream_t bitD2; + BIT_DStream_t bitD3; + BIT_DStream_t bitD4; + nuint length1 = MEM_readLE16(istart); + nuint length2 = MEM_readLE16(istart + 2); + nuint length3 = MEM_readLE16(istart + 4); + nuint length4 = cSrcSize - (length1 + length2 + length3 + 6); + /* jumpTable */ + byte* istart1 = istart + 6; + byte* istart2 = istart1 + length1; + byte* istart3 = istart2 + length2; + byte* istart4 = istart3 + length3; + nuint segmentSize = (dstSize + 3) / 4; + byte* opStart2 = ostart + segmentSize; + byte* opStart3 = opStart2 + segmentSize; + byte* opStart4 = opStart3 + segmentSize; + byte* op1 = ostart; + byte* op2 = opStart2; + byte* op3 = opStart3; + byte* op4 = opStart4; + DTableDesc dtd = HUF_getDTableDesc(DTable); + uint dtLog = dtd.tableLog; + uint endSignal = 1; + if (length4 > cSrcSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (opStart4 > oend) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + assert(dstSize >= 6); + { + nuint _var_err__ = BIT_initDStream(&bitD1, istart1, length1); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + { + nuint _var_err__ = BIT_initDStream(&bitD2, istart2, length2); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + { + nuint _var_err__ = BIT_initDStream(&bitD3, istart3, length3); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + { + nuint _var_err__ = BIT_initDStream(&bitD4, istart4, length4); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + if ((nuint)(oend - op4) >= (nuint)sizeof(nuint)) + { + for (; (endSignal & (uint)(op4 < olimit ? 1 : 0)) != 0; ) + { + if (MEM_64bits) + *op1++ = HUF_decodeSymbolX1(&bitD1, dt, dtLog); + if (MEM_64bits) + *op2++ = HUF_decodeSymbolX1(&bitD2, dt, dtLog); + if (MEM_64bits) + *op3++ = HUF_decodeSymbolX1(&bitD3, dt, dtLog); + if (MEM_64bits) + *op4++ = HUF_decodeSymbolX1(&bitD4, dt, dtLog); + *op1++ = HUF_decodeSymbolX1(&bitD1, dt, dtLog); + *op2++ = HUF_decodeSymbolX1(&bitD2, dt, dtLog); + *op3++ = HUF_decodeSymbolX1(&bitD3, dt, dtLog); + *op4++ = HUF_decodeSymbolX1(&bitD4, dt, dtLog); + if (MEM_64bits) + *op1++ = HUF_decodeSymbolX1(&bitD1, dt, dtLog); + if (MEM_64bits) + *op2++ = HUF_decodeSymbolX1(&bitD2, dt, dtLog); + if (MEM_64bits) + *op3++ = HUF_decodeSymbolX1(&bitD3, dt, dtLog); + if (MEM_64bits) + *op4++ = HUF_decodeSymbolX1(&bitD4, dt, dtLog); + *op1++ = HUF_decodeSymbolX1(&bitD1, dt, dtLog); + *op2++ = HUF_decodeSymbolX1(&bitD2, dt, dtLog); + *op3++ = HUF_decodeSymbolX1(&bitD3, dt, dtLog); + *op4++ = HUF_decodeSymbolX1(&bitD4, dt, dtLog); + endSignal &= + BIT_reloadDStreamFast(&bitD1) == BIT_DStream_status.BIT_DStream_unfinished + ? 1U + : 0U; + endSignal &= + BIT_reloadDStreamFast(&bitD2) == BIT_DStream_status.BIT_DStream_unfinished + ? 1U + : 0U; + endSignal &= + BIT_reloadDStreamFast(&bitD3) == BIT_DStream_status.BIT_DStream_unfinished + ? 1U + : 0U; + endSignal &= + BIT_reloadDStreamFast(&bitD4) == BIT_DStream_status.BIT_DStream_unfinished + ? 1U + : 0U; + } + } + + if (op1 > opStart2) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (op2 > opStart3) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (op3 > opStart4) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + HUF_decodeStreamX1(op1, &bitD1, opStart2, dt, dtLog); + HUF_decodeStreamX1(op2, &bitD2, opStart3, dt, dtLog); + HUF_decodeStreamX1(op3, &bitD3, opStart4, dt, dtLog); + HUF_decodeStreamX1(op4, &bitD4, oend, dt, dtLog); + { + uint endCheck = + BIT_endOfDStream(&bitD1) + & BIT_endOfDStream(&bitD2) + & BIT_endOfDStream(&bitD3) + & BIT_endOfDStream(&bitD4); + if (endCheck == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + return dstSize; + } + } + + private static nuint HUF_decompress4X1_usingDTable_internal_default( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable + ) + { + return HUF_decompress4X1_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable); + } + + private static void HUF_decompress4X1_usingDTable_internal_fast_c_loop( + HUF_DecompressFastArgs* args + ) + { + ulong bits0, + bits1, + bits2, + bits3; + byte* ip0, + ip1, + ip2, + ip3; + byte* op0, + op1, + op2, + op3; + ushort* dtable = (ushort*)args->dt; + byte* oend = args->oend; + byte* ilowest = args->ilowest; + bits0 = args->bits[0]; + bits1 = args->bits[1]; + bits2 = args->bits[2]; + bits3 = args->bits[3]; + ip0 = args->ip.e0; + ip1 = args->ip.e1; + ip2 = args->ip.e2; + ip3 = args->ip.e3; + op0 = args->op.e0; + op1 = args->op.e1; + op2 = args->op.e2; + op3 = args->op.e3; + assert(BitConverter.IsLittleEndian); + assert(!MEM_32bits); + for (; ; ) + { + byte* olimit; + { + assert(op0 <= op1); + assert(ip0 >= ilowest); + } + + { + assert(op1 <= op2); + assert(ip1 >= ilowest); + } + + { + assert(op2 <= op3); + assert(ip2 >= ilowest); + } + + { + assert(op3 <= oend); + assert(ip3 >= ilowest); + } + + { + /* Each iteration produces 5 output symbols per stream */ + nuint oiters = (nuint)(oend - op3) / 5; + /* Each iteration consumes up to 11 bits * 5 = 55 bits < 7 bytes + * per stream. + */ + nuint iiters = (nuint)(ip0 - ilowest) / 7; + /* We can safely run iters iterations before running bounds checks */ + nuint iters = oiters < iiters ? oiters : iiters; + nuint symbols = iters * 5; + olimit = op3 + symbols; + if (op3 == olimit) + break; + { + if (ip1 < ip0) + goto _out; + } + + { + if (ip2 < ip1) + goto _out; + } + + { + if (ip3 < ip2) + goto _out; + } + } + + { + assert(ip1 >= ip0); + } + + { + assert(ip2 >= ip1); + } + + { + assert(ip3 >= ip2); + } + + do + { + { + { + /* Decode 5 symbols in each of the 4 streams */ + int index = (int)(bits0 >> 53); + int entry = dtable[index]; + bits0 <<= entry & 0x3F; + op0[0] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits1 >> 53); + int entry = dtable[index]; + bits1 <<= entry & 0x3F; + op1[0] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits2 >> 53); + int entry = dtable[index]; + bits2 <<= entry & 0x3F; + op2[0] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits3 >> 53); + int entry = dtable[index]; + bits3 <<= entry & 0x3F; + op3[0] = (byte)(entry >> 8 & 0xFF); + } + } + + { + { + int index = (int)(bits0 >> 53); + int entry = dtable[index]; + bits0 <<= entry & 0x3F; + op0[1] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits1 >> 53); + int entry = dtable[index]; + bits1 <<= entry & 0x3F; + op1[1] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits2 >> 53); + int entry = dtable[index]; + bits2 <<= entry & 0x3F; + op2[1] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits3 >> 53); + int entry = dtable[index]; + bits3 <<= entry & 0x3F; + op3[1] = (byte)(entry >> 8 & 0xFF); + } + } + + { + { + int index = (int)(bits0 >> 53); + int entry = dtable[index]; + bits0 <<= entry & 0x3F; + op0[2] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits1 >> 53); + int entry = dtable[index]; + bits1 <<= entry & 0x3F; + op1[2] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits2 >> 53); + int entry = dtable[index]; + bits2 <<= entry & 0x3F; + op2[2] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits3 >> 53); + int entry = dtable[index]; + bits3 <<= entry & 0x3F; + op3[2] = (byte)(entry >> 8 & 0xFF); + } + } + + { + { + int index = (int)(bits0 >> 53); + int entry = dtable[index]; + bits0 <<= entry & 0x3F; + op0[3] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits1 >> 53); + int entry = dtable[index]; + bits1 <<= entry & 0x3F; + op1[3] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits2 >> 53); + int entry = dtable[index]; + bits2 <<= entry & 0x3F; + op2[3] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits3 >> 53); + int entry = dtable[index]; + bits3 <<= entry & 0x3F; + op3[3] = (byte)(entry >> 8 & 0xFF); + } + } + + { + { + int index = (int)(bits0 >> 53); + int entry = dtable[index]; + bits0 <<= entry & 0x3F; + op0[4] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits1 >> 53); + int entry = dtable[index]; + bits1 <<= entry & 0x3F; + op1[4] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits2 >> 53); + int entry = dtable[index]; + bits2 <<= entry & 0x3F; + op2[4] = (byte)(entry >> 8 & 0xFF); + } + + { + int index = (int)(bits3 >> 53); + int entry = dtable[index]; + bits3 <<= entry & 0x3F; + op3[4] = (byte)(entry >> 8 & 0xFF); + } + } + + { + { + /* Reload each of the 4 the bitstreams */ + int ctz = (int)ZSTD_countTrailingZeros64(bits0); + int nbBits = ctz & 7; + int nbBytes = ctz >> 3; + op0 += 5; + ip0 -= nbBytes; + bits0 = MEM_read64(ip0) | 1; + bits0 <<= nbBits; + } + + { + int ctz = (int)ZSTD_countTrailingZeros64(bits1); + int nbBits = ctz & 7; + int nbBytes = ctz >> 3; + op1 += 5; + ip1 -= nbBytes; + bits1 = MEM_read64(ip1) | 1; + bits1 <<= nbBits; + } + + { + int ctz = (int)ZSTD_countTrailingZeros64(bits2); + int nbBits = ctz & 7; + int nbBytes = ctz >> 3; + op2 += 5; + ip2 -= nbBytes; + bits2 = MEM_read64(ip2) | 1; + bits2 <<= nbBits; + } + + { + int ctz = (int)ZSTD_countTrailingZeros64(bits3); + int nbBits = ctz & 7; + int nbBytes = ctz >> 3; + op3 += 5; + ip3 -= nbBytes; + bits3 = MEM_read64(ip3) | 1; + bits3 <<= nbBits; + } + } + } while (op3 < olimit); + } + + _out: + args->bits[0] = bits0; + args->bits[1] = bits1; + args->bits[2] = bits2; + args->bits[3] = bits3; + args->ip.e0 = ip0; + args->ip.e1 = ip1; + args->ip.e2 = ip2; + args->ip.e3 = ip3; + args->op.e0 = op0; + args->op.e1 = op1; + args->op.e2 = op2; + args->op.e3 = op3; + } + + /** + * @returns @p dstSize on success (>= 6) + * 0 if the fallback implementation should be used + * An error if an error occurred + */ + private static nuint HUF_decompress4X1_usingDTable_internal_fast( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable, + void* loopFn + ) + { + void* dt = DTable + 1; + byte* ilowest = (byte*)cSrc; + byte* oend = ZSTD_maybeNullPtrAdd((byte*)dst, (nint)dstSize); + HUF_DecompressFastArgs args; + { + nuint ret = HUF_DecompressFastArgs_init(&args, dst, dstSize, cSrc, cSrcSize, DTable); + { + nuint err_code = ret; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (ret == 0) + return 0; + } + + assert(args.ip.e0 >= args.ilowest); + ((delegate* managed)loopFn)(&args); + assert(args.ip.e0 >= ilowest); + assert(args.ip.e0 >= ilowest); + assert(args.ip.e1 >= ilowest); + assert(args.ip.e2 >= ilowest); + assert(args.ip.e3 >= ilowest); + assert(args.op.e3 <= oend); + assert(ilowest == args.ilowest); + assert(ilowest + 6 == args.iend.e0); + { + nuint segmentSize = (dstSize + 3) / 4; + byte* segmentEnd = (byte*)dst; + int i; + for (i = 0; i < 4; ++i) + { + BIT_DStream_t bit; + if (segmentSize <= (nuint)(oend - segmentEnd)) + segmentEnd += segmentSize; + else + segmentEnd = oend; + { + nuint err_code = HUF_initRemainingDStream(&bit, &args, i, segmentEnd); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + (&args.op.e0)[i] += HUF_decodeStreamX1( + (&args.op.e0)[i], + &bit, + segmentEnd, + (HUF_DEltX1*)dt, + 11 + ); + if ((&args.op.e0)[i] != segmentEnd) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + } + + assert(dstSize != 0); + return dstSize; + } + + private static nuint HUF_decompress1X1_usingDTable_internal( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable, + int flags + ) + { + return HUF_decompress1X1_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable); + } + + private static nuint HUF_decompress4X1_usingDTable_internal( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable, + int flags + ) + { + void* fallbackFn = (delegate* managed)( + &HUF_decompress4X1_usingDTable_internal_default + ); + void* loopFn = (delegate* managed)( + &HUF_decompress4X1_usingDTable_internal_fast_c_loop + ); + if ((flags & (int)HUF_flags_e.HUF_flags_disableFast) == 0) + { + nuint ret = HUF_decompress4X1_usingDTable_internal_fast( + dst, + dstSize, + cSrc, + cSrcSize, + DTable, + loopFn + ); + if (ret != 0) + return ret; + } + + return ((delegate* managed)fallbackFn)( + dst, + dstSize, + cSrc, + cSrcSize, + DTable + ); + } + + private static nuint HUF_decompress4X1_DCtx_wksp( + uint* dctx, + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + void* workSpace, + nuint wkspSize, + int flags + ) + { + byte* ip = (byte*)cSrc; + nuint hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize, flags); + if (ERR_isError(hSize)) + return hSize; + if (hSize >= cSrcSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + ip += hSize; + cSrcSize -= hSize; + return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags); + } + + /** + * Constructs a HUF_DEltX2 in a U32. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint HUF_buildDEltX2U32(uint symbol, uint nbBits, uint baseSeq, int level) + { + uint seq; + if (BitConverter.IsLittleEndian) + { + seq = level == 1 ? symbol : baseSeq + (symbol << 8); + return seq + (nbBits << 16) + ((uint)level << 24); + } + else + { + seq = level == 1 ? symbol << 8 : (baseSeq << 8) + symbol; + return (seq << 16) + (nbBits << 8) + (uint)level; + } + } + + /** + * Constructs a HUF_DEltX2. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static HUF_DEltX2 HUF_buildDEltX2(uint symbol, uint nbBits, uint baseSeq, int level) + { + HUF_DEltX2 DElt; + uint val = HUF_buildDEltX2U32(symbol, nbBits, baseSeq, level); + memcpy(&DElt, &val, sizeof(uint)); + return DElt; + } + + /** + * Constructs 2 HUF_DEltX2s and packs them into a U64. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong HUF_buildDEltX2U64(uint symbol, uint nbBits, ushort baseSeq, int level) + { + uint DElt = HUF_buildDEltX2U32(symbol, nbBits, baseSeq, level); + return DElt + ((ulong)DElt << 32); + } + + /** + * Fills the DTable rank with all the symbols from [begin, end) that are each + * nbBits long. + * + * @param DTableRank The start of the rank in the DTable. + * @param begin The first symbol to fill (inclusive). + * @param end The last symbol to fill (exclusive). + * @param nbBits Each symbol is nbBits long. + * @param tableLog The table log. + * @param baseSeq If level == 1 { 0 } else { the first level symbol } + * @param level The level in the table. Must be 1 or 2. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HUF_fillDTableX2ForWeight( + HUF_DEltX2* DTableRank, + sortedSymbol_t* begin, + sortedSymbol_t* end, + uint nbBits, + uint tableLog, + ushort baseSeq, + int level + ) + { + /* quiet static-analyzer */ + uint length = 1U << (int)(tableLog - nbBits & 0x1F); + sortedSymbol_t* ptr; + assert(level >= 1 && level <= 2); + switch (length) + { + case 1: + for (ptr = begin; ptr != end; ++ptr) + { + HUF_DEltX2 DElt = HUF_buildDEltX2(ptr->symbol, nbBits, baseSeq, level); + *DTableRank++ = DElt; + } + + break; + case 2: + for (ptr = begin; ptr != end; ++ptr) + { + HUF_DEltX2 DElt = HUF_buildDEltX2(ptr->symbol, nbBits, baseSeq, level); + DTableRank[0] = DElt; + DTableRank[1] = DElt; + DTableRank += 2; + } + + break; + case 4: + for (ptr = begin; ptr != end; ++ptr) + { + ulong DEltX2 = HUF_buildDEltX2U64(ptr->symbol, nbBits, baseSeq, level); + memcpy(DTableRank + 0, &DEltX2, sizeof(ulong)); + memcpy(DTableRank + 2, &DEltX2, sizeof(ulong)); + DTableRank += 4; + } + + break; + case 8: + for (ptr = begin; ptr != end; ++ptr) + { + ulong DEltX2 = HUF_buildDEltX2U64(ptr->symbol, nbBits, baseSeq, level); + memcpy(DTableRank + 0, &DEltX2, sizeof(ulong)); + memcpy(DTableRank + 2, &DEltX2, sizeof(ulong)); + memcpy(DTableRank + 4, &DEltX2, sizeof(ulong)); + memcpy(DTableRank + 6, &DEltX2, sizeof(ulong)); + DTableRank += 8; + } + + break; + default: + for (ptr = begin; ptr != end; ++ptr) + { + ulong DEltX2 = HUF_buildDEltX2U64(ptr->symbol, nbBits, baseSeq, level); + HUF_DEltX2* DTableRankEnd = DTableRank + length; + for (; DTableRank != DTableRankEnd; DTableRank += 8) + { + memcpy(DTableRank + 0, &DEltX2, sizeof(ulong)); + memcpy(DTableRank + 2, &DEltX2, sizeof(ulong)); + memcpy(DTableRank + 4, &DEltX2, sizeof(ulong)); + memcpy(DTableRank + 6, &DEltX2, sizeof(ulong)); + } + } + + break; + } + } + + /* HUF_fillDTableX2Level2() : + * `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void HUF_fillDTableX2Level2( + HUF_DEltX2* DTable, + uint targetLog, + uint consumedBits, + uint* rankVal, + int minWeight, + int maxWeight1, + sortedSymbol_t* sortedSymbols, + uint* rankStart, + uint nbBitsBaseline, + ushort baseSeq + ) + { + if (minWeight > 1) + { + /* quiet static-analyzer */ + uint length = 1U << (int)(targetLog - consumedBits & 0x1F); + /* baseSeq */ + ulong DEltX2 = HUF_buildDEltX2U64(baseSeq, consumedBits, 0, 1); + int skipSize = (int)rankVal[minWeight]; + assert(length > 1); + assert((uint)skipSize < length); + switch (length) + { + case 2: + assert(skipSize == 1); + memcpy(DTable, &DEltX2, sizeof(ulong)); + break; + case 4: + assert(skipSize <= 4); + memcpy(DTable + 0, &DEltX2, sizeof(ulong)); + memcpy(DTable + 2, &DEltX2, sizeof(ulong)); + break; + default: + { + int i; + for (i = 0; i < skipSize; i += 8) + { + memcpy(DTable + i + 0, &DEltX2, sizeof(ulong)); + memcpy(DTable + i + 2, &DEltX2, sizeof(ulong)); + memcpy(DTable + i + 4, &DEltX2, sizeof(ulong)); + memcpy(DTable + i + 6, &DEltX2, sizeof(ulong)); + } + } + + break; + } + } + + { + int w; + for (w = minWeight; w < maxWeight1; ++w) + { + int begin = (int)rankStart[w]; + int end = (int)rankStart[w + 1]; + uint nbBits = nbBitsBaseline - (uint)w; + uint totalBits = nbBits + consumedBits; + HUF_fillDTableX2ForWeight( + DTable + rankVal[w], + sortedSymbols + begin, + sortedSymbols + end, + totalBits, + targetLog, + baseSeq, + 2 + ); + } + } + } + + private static void HUF_fillDTableX2( + HUF_DEltX2* DTable, + uint targetLog, + sortedSymbol_t* sortedList, + uint* rankStart, + rankValCol_t* rankValOrigin, + uint maxWeight, + uint nbBitsBaseline + ) + { + uint* rankVal = (uint*)&rankValOrigin[0]; + /* note : targetLog >= srcLog, hence scaleLog <= 1 */ + int scaleLog = (int)(nbBitsBaseline - targetLog); + uint minBits = nbBitsBaseline - maxWeight; + int w; + int wEnd = (int)maxWeight + 1; + for (w = 1; w < wEnd; ++w) + { + int begin = (int)rankStart[w]; + int end = (int)rankStart[w + 1]; + uint nbBits = nbBitsBaseline - (uint)w; + if (targetLog - nbBits >= minBits) + { + /* Enough room for a second symbol. */ + int start = (int)rankVal[w]; + /* quiet static-analyzer */ + uint length = 1U << (int)(targetLog - nbBits & 0x1F); + int minWeight = (int)(nbBits + (uint)scaleLog); + int s; + if (minWeight < 1) + minWeight = 1; + for (s = begin; s != end; ++s) + { + HUF_fillDTableX2Level2( + DTable + start, + targetLog, + nbBits, + (uint*)&rankValOrigin[nbBits], + minWeight, + wEnd, + sortedList, + rankStart, + nbBitsBaseline, + sortedList[s].symbol + ); + start += (int)length; + } + } + else + { + HUF_fillDTableX2ForWeight( + DTable + rankVal[w], + sortedList + begin, + sortedList + end, + nbBits, + targetLog, + 0, + 1 + ); + } + } + } + + private static nuint HUF_readDTableX2_wksp( + uint* DTable, + void* src, + nuint srcSize, + void* workSpace, + nuint wkspSize, + int flags + ) + { + uint tableLog, + maxW, + nbSymbols; + DTableDesc dtd = HUF_getDTableDesc(DTable); + uint maxTableLog = dtd.maxTableLog; + nuint iSize; + /* force compiler to avoid strict-aliasing */ + void* dtPtr = DTable + 1; + HUF_DEltX2* dt = (HUF_DEltX2*)dtPtr; + uint* rankStart; + HUF_ReadDTableX2_Workspace* wksp = (HUF_ReadDTableX2_Workspace*)workSpace; + if ((nuint)sizeof(HUF_ReadDTableX2_Workspace) > wkspSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + rankStart = wksp->rankStart0 + 1; + memset(wksp->rankStats, 0, sizeof(uint) * 13); + memset(wksp->rankStart0, 0, sizeof(uint) * 15); + if (maxTableLog > 12) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + iSize = HUF_readStats_wksp( + wksp->weightList, + 255 + 1, + wksp->rankStats, + &nbSymbols, + &tableLog, + src, + srcSize, + wksp->calleeWksp, + sizeof(uint) * 219, + flags + ); + if (ERR_isError(iSize)) + return iSize; + if (tableLog > maxTableLog) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_tableLog_tooLarge)); + if (tableLog <= 11 && maxTableLog > 11) + maxTableLog = 11; + for (maxW = tableLog; wksp->rankStats[maxW] == 0; maxW--) { } + + { + uint w, + nextRankStart = 0; + for (w = 1; w < maxW + 1; w++) + { + uint curr = nextRankStart; + nextRankStart += wksp->rankStats[w]; + rankStart[w] = curr; + } + + rankStart[0] = nextRankStart; + rankStart[maxW + 1] = nextRankStart; + } + + { + uint s; + for (s = 0; s < nbSymbols; s++) + { + uint w = wksp->weightList[s]; + uint r = rankStart[w]++; + (&wksp->sortedSymbol.e0)[r].symbol = (byte)s; + } + + rankStart[0] = 0; + } + + { + uint* rankVal0 = (uint*)&wksp->rankVal.e0; + { + /* tableLog <= maxTableLog */ + int rescale = (int)(maxTableLog - tableLog - 1); + uint nextRankVal = 0; + uint w; + for (w = 1; w < maxW + 1; w++) + { + uint curr = nextRankVal; + nextRankVal += wksp->rankStats[w] << (int)(w + (uint)rescale); + rankVal0[w] = curr; + } + } + + { + uint minBits = tableLog + 1 - maxW; + uint consumed; + for (consumed = minBits; consumed < maxTableLog - minBits + 1; consumed++) + { + uint* rankValPtr = (uint*)&(&wksp->rankVal.e0)[consumed]; + uint w; + for (w = 1; w < maxW + 1; w++) + { + rankValPtr[w] = rankVal0[w] >> (int)consumed; + } + } + } + } + + HUF_fillDTableX2( + dt, + maxTableLog, + &wksp->sortedSymbol.e0, + wksp->rankStart0, + &wksp->rankVal.e0, + maxW, + tableLog + 1 + ); + dtd.tableLog = (byte)maxTableLog; + dtd.tableType = 1; + memcpy(DTable, &dtd, (uint)sizeof(DTableDesc)); + return iSize; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint HUF_decodeSymbolX2( + void* op, + BIT_DStream_t* DStream, + HUF_DEltX2* dt, + uint dtLog + ) + { + /* note : dtLog >= 1 */ + nuint val = BIT_lookBitsFast(DStream, dtLog); + memcpy(op, &dt[val].sequence, 2); + BIT_skipBits(DStream, dt[val].nbBits); + return dt[val].length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint HUF_decodeLastSymbolX2( + void* op, + BIT_DStream_t* DStream, + HUF_DEltX2* dt, + uint dtLog + ) + { + /* note : dtLog >= 1 */ + nuint val = BIT_lookBitsFast(DStream, dtLog); + memcpy(op, &dt[val].sequence, 1); + if (dt[val].length == 1) + { + BIT_skipBits(DStream, dt[val].nbBits); + } + else + { + if (DStream->bitsConsumed < (uint)(sizeof(nuint) * 8)) + { + BIT_skipBits(DStream, dt[val].nbBits); + if (DStream->bitsConsumed > (uint)(sizeof(nuint) * 8)) + DStream->bitsConsumed = (uint)(sizeof(nuint) * 8); + } + } + + return 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint HUF_decodeStreamX2( + byte* p, + BIT_DStream_t* bitDPtr, + byte* pEnd, + HUF_DEltX2* dt, + uint dtLog + ) + { + byte* pStart = p; + if ((nuint)(pEnd - p) >= (nuint)sizeof(nuint)) + { + if (dtLog <= 11 && MEM_64bits) + { + while ( + BIT_reloadDStream(bitDPtr) == BIT_DStream_status.BIT_DStream_unfinished + && p < pEnd - 9 + ) + { + p += HUF_decodeSymbolX2(p, bitDPtr, dt, dtLog); + p += HUF_decodeSymbolX2(p, bitDPtr, dt, dtLog); + p += HUF_decodeSymbolX2(p, bitDPtr, dt, dtLog); + p += HUF_decodeSymbolX2(p, bitDPtr, dt, dtLog); + p += HUF_decodeSymbolX2(p, bitDPtr, dt, dtLog); + } + } + else + { + while ( + BIT_reloadDStream(bitDPtr) == BIT_DStream_status.BIT_DStream_unfinished + && p < pEnd - (sizeof(nuint) - 1) + ) + { + if (MEM_64bits) + p += HUF_decodeSymbolX2(p, bitDPtr, dt, dtLog); + p += HUF_decodeSymbolX2(p, bitDPtr, dt, dtLog); + if (MEM_64bits) + p += HUF_decodeSymbolX2(p, bitDPtr, dt, dtLog); + p += HUF_decodeSymbolX2(p, bitDPtr, dt, dtLog); + } + } + } + else + { + BIT_reloadDStream(bitDPtr); + } + + if ((nuint)(pEnd - p) >= 2) + { + while ( + BIT_reloadDStream(bitDPtr) == BIT_DStream_status.BIT_DStream_unfinished + && p <= pEnd - 2 + ) + p += HUF_decodeSymbolX2(p, bitDPtr, dt, dtLog); + while (p <= pEnd - 2) + p += HUF_decodeSymbolX2(p, bitDPtr, dt, dtLog); + } + + if (p < pEnd) + p += HUF_decodeLastSymbolX2(p, bitDPtr, dt, dtLog); + return (nuint)(p - pStart); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint HUF_decompress1X2_usingDTable_internal_body( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable + ) + { + BIT_DStream_t bitD; + { + /* Init */ + nuint _var_err__ = BIT_initDStream(&bitD, cSrc, cSrcSize); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + { + byte* ostart = (byte*)dst; + byte* oend = ZSTD_maybeNullPtrAdd(ostart, (nint)dstSize); + /* force compiler to not use strict-aliasing */ + void* dtPtr = DTable + 1; + HUF_DEltX2* dt = (HUF_DEltX2*)dtPtr; + DTableDesc dtd = HUF_getDTableDesc(DTable); + HUF_decodeStreamX2(ostart, &bitD, oend, dt, dtd.tableLog); + } + + if (BIT_endOfDStream(&bitD) == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + return dstSize; + } + + /* HUF_decompress4X2_usingDTable_internal_body(): + * Conditions: + * @dstSize >= 6 + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint HUF_decompress4X2_usingDTable_internal_body( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable + ) + { + if (cSrcSize < 10) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (dstSize < 6) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + { + byte* istart = (byte*)cSrc; + byte* ostart = (byte*)dst; + byte* oend = ostart + dstSize; + byte* olimit = oend - (sizeof(nuint) - 1); + void* dtPtr = DTable + 1; + HUF_DEltX2* dt = (HUF_DEltX2*)dtPtr; + /* Init */ + BIT_DStream_t bitD1; + BIT_DStream_t bitD2; + BIT_DStream_t bitD3; + BIT_DStream_t bitD4; + nuint length1 = MEM_readLE16(istart); + nuint length2 = MEM_readLE16(istart + 2); + nuint length3 = MEM_readLE16(istart + 4); + nuint length4 = cSrcSize - (length1 + length2 + length3 + 6); + /* jumpTable */ + byte* istart1 = istart + 6; + byte* istart2 = istart1 + length1; + byte* istart3 = istart2 + length2; + byte* istart4 = istart3 + length3; + nuint segmentSize = (dstSize + 3) / 4; + byte* opStart2 = ostart + segmentSize; + byte* opStart3 = opStart2 + segmentSize; + byte* opStart4 = opStart3 + segmentSize; + byte* op1 = ostart; + byte* op2 = opStart2; + byte* op3 = opStart3; + byte* op4 = opStart4; + uint endSignal = 1; + DTableDesc dtd = HUF_getDTableDesc(DTable); + uint dtLog = dtd.tableLog; + if (length4 > cSrcSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (opStart4 > oend) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + assert(dstSize >= 6); + { + nuint _var_err__ = BIT_initDStream(&bitD1, istart1, length1); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + { + nuint _var_err__ = BIT_initDStream(&bitD2, istart2, length2); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + { + nuint _var_err__ = BIT_initDStream(&bitD3, istart3, length3); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + { + nuint _var_err__ = BIT_initDStream(&bitD4, istart4, length4); + if (ERR_isError(_var_err__)) + return _var_err__; + } + + if ((nuint)(oend - op4) >= (nuint)sizeof(nuint)) + { + for (; (endSignal & (uint)(op4 < olimit ? 1 : 0)) != 0; ) + { + if (MEM_64bits) + op1 += HUF_decodeSymbolX2(op1, &bitD1, dt, dtLog); + op1 += HUF_decodeSymbolX2(op1, &bitD1, dt, dtLog); + if (MEM_64bits) + op1 += HUF_decodeSymbolX2(op1, &bitD1, dt, dtLog); + op1 += HUF_decodeSymbolX2(op1, &bitD1, dt, dtLog); + if (MEM_64bits) + op2 += HUF_decodeSymbolX2(op2, &bitD2, dt, dtLog); + op2 += HUF_decodeSymbolX2(op2, &bitD2, dt, dtLog); + if (MEM_64bits) + op2 += HUF_decodeSymbolX2(op2, &bitD2, dt, dtLog); + op2 += HUF_decodeSymbolX2(op2, &bitD2, dt, dtLog); + endSignal &= + BIT_reloadDStreamFast(&bitD1) == BIT_DStream_status.BIT_DStream_unfinished + ? 1U + : 0U; + endSignal &= + BIT_reloadDStreamFast(&bitD2) == BIT_DStream_status.BIT_DStream_unfinished + ? 1U + : 0U; + if (MEM_64bits) + op3 += HUF_decodeSymbolX2(op3, &bitD3, dt, dtLog); + op3 += HUF_decodeSymbolX2(op3, &bitD3, dt, dtLog); + if (MEM_64bits) + op3 += HUF_decodeSymbolX2(op3, &bitD3, dt, dtLog); + op3 += HUF_decodeSymbolX2(op3, &bitD3, dt, dtLog); + if (MEM_64bits) + op4 += HUF_decodeSymbolX2(op4, &bitD4, dt, dtLog); + op4 += HUF_decodeSymbolX2(op4, &bitD4, dt, dtLog); + if (MEM_64bits) + op4 += HUF_decodeSymbolX2(op4, &bitD4, dt, dtLog); + op4 += HUF_decodeSymbolX2(op4, &bitD4, dt, dtLog); + endSignal &= + BIT_reloadDStreamFast(&bitD3) == BIT_DStream_status.BIT_DStream_unfinished + ? 1U + : 0U; + endSignal &= + BIT_reloadDStreamFast(&bitD4) == BIT_DStream_status.BIT_DStream_unfinished + ? 1U + : 0U; + } + } + + if (op1 > opStart2) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (op2 > opStart3) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (op3 > opStart4) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog); + HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog); + HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog); + HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog); + { + uint endCheck = + BIT_endOfDStream(&bitD1) + & BIT_endOfDStream(&bitD2) + & BIT_endOfDStream(&bitD3) + & BIT_endOfDStream(&bitD4); + if (endCheck == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + return dstSize; + } + } + + private static nuint HUF_decompress4X2_usingDTable_internal_default( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable + ) + { + return HUF_decompress4X2_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable); + } + + private static void HUF_decompress4X2_usingDTable_internal_fast_c_loop( + HUF_DecompressFastArgs* args + ) + { + ulong bits0, + bits1, + bits2, + bits3; + byte* ip0, + ip1, + ip2, + ip3; + byte* op0, + op1, + op2, + op3; + byte* oend0, + oend1, + oend2, + oend3; + HUF_DEltX2* dtable = (HUF_DEltX2*)args->dt; + byte* ilowest = args->ilowest; + bits0 = args->bits[0]; + bits1 = args->bits[1]; + bits2 = args->bits[2]; + bits3 = args->bits[3]; + ip0 = args->ip.e0; + ip1 = args->ip.e1; + ip2 = args->ip.e2; + ip3 = args->ip.e3; + op0 = args->op.e0; + op1 = args->op.e1; + op2 = args->op.e2; + op3 = args->op.e3; + oend0 = op1; + oend1 = op2; + oend2 = op3; + oend3 = args->oend; + assert(BitConverter.IsLittleEndian); + assert(!MEM_32bits); + for (; ; ) + { + byte* olimit; + { + assert(op0 <= oend0); + assert(ip0 >= ilowest); + } + + { + assert(op1 <= oend1); + assert(ip1 >= ilowest); + } + + { + assert(op2 <= oend2); + assert(ip2 >= ilowest); + } + + { + assert(op3 <= oend3); + assert(ip3 >= ilowest); + } + + { + /* Each loop does 5 table lookups for each of the 4 streams. + * Each table lookup consumes up to 11 bits of input, and produces + * up to 2 bytes of output. + */ + /* We can consume up to 7 bytes of input per iteration per stream. + * We also know that each input pointer is >= ip[0]. So we can run + * iters loops before running out of input. + */ + nuint iters = (nuint)(ip0 - ilowest) / 7; + { + nuint oiters = (nuint)(oend0 - op0) / 10; + iters = iters < oiters ? iters : oiters; + } + + { + nuint oiters = (nuint)(oend1 - op1) / 10; + iters = iters < oiters ? iters : oiters; + } + + { + nuint oiters = (nuint)(oend2 - op2) / 10; + iters = iters < oiters ? iters : oiters; + } + + { + nuint oiters = (nuint)(oend3 - op3) / 10; + iters = iters < oiters ? iters : oiters; + } + + olimit = op3 + iters * 5; + if (op3 == olimit) + break; + { + if (ip1 < ip0) + goto _out; + } + + { + if (ip2 < ip1) + goto _out; + } + + { + if (ip3 < ip2) + goto _out; + } + } + + { + assert(ip1 >= ip0); + } + + { + assert(ip2 >= ip1); + } + + { + assert(ip3 >= ip2); + } + + do + { + { + { + /* Decode 5 symbols from each of the first 3 streams. + * The final stream will be decoded during the reload phase + * to reduce register pressure. + */ + int index = (int)(bits0 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op0, entry.sequence); + bits0 <<= entry.nbBits & 0x3F; + op0 += entry.length; + } + + { + int index = (int)(bits1 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op1, entry.sequence); + bits1 <<= entry.nbBits & 0x3F; + op1 += entry.length; + } + + { + int index = (int)(bits2 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op2, entry.sequence); + bits2 <<= entry.nbBits & 0x3F; + op2 += entry.length; + } + } + + { + { + int index = (int)(bits0 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op0, entry.sequence); + bits0 <<= entry.nbBits & 0x3F; + op0 += entry.length; + } + + { + int index = (int)(bits1 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op1, entry.sequence); + bits1 <<= entry.nbBits & 0x3F; + op1 += entry.length; + } + + { + int index = (int)(bits2 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op2, entry.sequence); + bits2 <<= entry.nbBits & 0x3F; + op2 += entry.length; + } + } + + { + { + int index = (int)(bits0 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op0, entry.sequence); + bits0 <<= entry.nbBits & 0x3F; + op0 += entry.length; + } + + { + int index = (int)(bits1 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op1, entry.sequence); + bits1 <<= entry.nbBits & 0x3F; + op1 += entry.length; + } + + { + int index = (int)(bits2 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op2, entry.sequence); + bits2 <<= entry.nbBits & 0x3F; + op2 += entry.length; + } + } + + { + { + int index = (int)(bits0 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op0, entry.sequence); + bits0 <<= entry.nbBits & 0x3F; + op0 += entry.length; + } + + { + int index = (int)(bits1 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op1, entry.sequence); + bits1 <<= entry.nbBits & 0x3F; + op1 += entry.length; + } + + { + int index = (int)(bits2 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op2, entry.sequence); + bits2 <<= entry.nbBits & 0x3F; + op2 += entry.length; + } + } + + { + { + int index = (int)(bits0 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op0, entry.sequence); + bits0 <<= entry.nbBits & 0x3F; + op0 += entry.length; + } + + { + int index = (int)(bits1 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op1, entry.sequence); + bits1 <<= entry.nbBits & 0x3F; + op1 += entry.length; + } + + { + int index = (int)(bits2 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op2, entry.sequence); + bits2 <<= entry.nbBits & 0x3F; + op2 += entry.length; + } + } + + { + /* Decode one symbol from the final stream */ + int index = (int)(bits3 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op3, entry.sequence); + bits3 <<= entry.nbBits & 0x3F; + op3 += entry.length; + } + + { + { + { + /* Decode 4 symbols from the final stream & reload bitstreams. + * The final stream is reloaded last, meaning that all 5 symbols + * are decoded from the final stream before it is reloaded. + */ + int index = (int)(bits3 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op3, entry.sequence); + bits3 <<= entry.nbBits & 0x3F; + op3 += entry.length; + } + + { + int ctz = (int)ZSTD_countTrailingZeros64(bits0); + int nbBits = ctz & 7; + int nbBytes = ctz >> 3; + ip0 -= nbBytes; + bits0 = MEM_read64(ip0) | 1; + bits0 <<= nbBits; + } + } + + { + { + int index = (int)(bits3 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op3, entry.sequence); + bits3 <<= entry.nbBits & 0x3F; + op3 += entry.length; + } + + { + int ctz = (int)ZSTD_countTrailingZeros64(bits1); + int nbBits = ctz & 7; + int nbBytes = ctz >> 3; + ip1 -= nbBytes; + bits1 = MEM_read64(ip1) | 1; + bits1 <<= nbBits; + } + } + + { + { + int index = (int)(bits3 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op3, entry.sequence); + bits3 <<= entry.nbBits & 0x3F; + op3 += entry.length; + } + + { + int ctz = (int)ZSTD_countTrailingZeros64(bits2); + int nbBits = ctz & 7; + int nbBytes = ctz >> 3; + ip2 -= nbBytes; + bits2 = MEM_read64(ip2) | 1; + bits2 <<= nbBits; + } + } + + { + { + int index = (int)(bits3 >> 53); + HUF_DEltX2 entry = dtable[index]; + MEM_write16(op3, entry.sequence); + bits3 <<= entry.nbBits & 0x3F; + op3 += entry.length; + } + + { + int ctz = (int)ZSTD_countTrailingZeros64(bits3); + int nbBits = ctz & 7; + int nbBytes = ctz >> 3; + ip3 -= nbBytes; + bits3 = MEM_read64(ip3) | 1; + bits3 <<= nbBits; + } + } + } + } while (op3 < olimit); + } + + _out: + args->bits[0] = bits0; + args->bits[1] = bits1; + args->bits[2] = bits2; + args->bits[3] = bits3; + args->ip.e0 = ip0; + args->ip.e1 = ip1; + args->ip.e2 = ip2; + args->ip.e3 = ip3; + args->op.e0 = op0; + args->op.e1 = op1; + args->op.e2 = op2; + args->op.e3 = op3; + } + + private static nuint HUF_decompress4X2_usingDTable_internal_fast( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable, + void* loopFn + ) + { + void* dt = DTable + 1; + byte* ilowest = (byte*)cSrc; + byte* oend = ZSTD_maybeNullPtrAdd((byte*)dst, (nint)dstSize); + HUF_DecompressFastArgs args; + { + nuint ret = HUF_DecompressFastArgs_init(&args, dst, dstSize, cSrc, cSrcSize, DTable); + { + nuint err_code = ret; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (ret == 0) + return 0; + } + + assert(args.ip.e0 >= args.ilowest); + ((delegate* managed)loopFn)(&args); + assert(args.ip.e0 >= ilowest); + assert(args.ip.e1 >= ilowest); + assert(args.ip.e2 >= ilowest); + assert(args.ip.e3 >= ilowest); + assert(args.op.e3 <= oend); + assert(ilowest == args.ilowest); + assert(ilowest + 6 == args.iend.e0); + { + nuint segmentSize = (dstSize + 3) / 4; + byte* segmentEnd = (byte*)dst; + int i; + for (i = 0; i < 4; ++i) + { + BIT_DStream_t bit; + if (segmentSize <= (nuint)(oend - segmentEnd)) + segmentEnd += segmentSize; + else + segmentEnd = oend; + { + nuint err_code = HUF_initRemainingDStream(&bit, &args, i, segmentEnd); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + (&args.op.e0)[i] += HUF_decodeStreamX2( + (&args.op.e0)[i], + &bit, + segmentEnd, + (HUF_DEltX2*)dt, + 11 + ); + if ((&args.op.e0)[i] != segmentEnd) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + } + + return dstSize; + } + + private static nuint HUF_decompress4X2_usingDTable_internal( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable, + int flags + ) + { + void* fallbackFn = (delegate* managed)( + &HUF_decompress4X2_usingDTable_internal_default + ); + void* loopFn = (delegate* managed)( + &HUF_decompress4X2_usingDTable_internal_fast_c_loop + ); + if ((flags & (int)HUF_flags_e.HUF_flags_disableFast) == 0) + { + nuint ret = HUF_decompress4X2_usingDTable_internal_fast( + dst, + dstSize, + cSrc, + cSrcSize, + DTable, + loopFn + ); + if (ret != 0) + return ret; + } + + return ((delegate* managed)fallbackFn)( + dst, + dstSize, + cSrc, + cSrcSize, + DTable + ); + } + + private static nuint HUF_decompress1X2_usingDTable_internal( + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable, + int flags + ) + { + return HUF_decompress1X2_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable); + } + + private static nuint HUF_decompress1X2_DCtx_wksp( + uint* DCtx, + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + void* workSpace, + nuint wkspSize, + int flags + ) + { + byte* ip = (byte*)cSrc; + nuint hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize, flags); + if (ERR_isError(hSize)) + return hSize; + if (hSize >= cSrcSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + ip += hSize; + cSrcSize -= hSize; + return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, flags); + } + + private static nuint HUF_decompress4X2_DCtx_wksp( + uint* dctx, + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + void* workSpace, + nuint wkspSize, + int flags + ) + { + byte* ip = (byte*)cSrc; + nuint hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize, flags); + if (ERR_isError(hSize)) + return hSize; + if (hSize >= cSrcSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + ip += hSize; + cSrcSize -= hSize; + return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags); + } + + private static readonly algo_time_t[][] algoTime = new algo_time_t[16][] + { + new algo_time_t[2] + { + new algo_time_t(tableTime: 0, decode256Time: 0), + new algo_time_t(tableTime: 1, decode256Time: 1), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 0, decode256Time: 0), + new algo_time_t(tableTime: 1, decode256Time: 1), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 150, decode256Time: 216), + new algo_time_t(tableTime: 381, decode256Time: 119), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 170, decode256Time: 205), + new algo_time_t(tableTime: 514, decode256Time: 112), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 177, decode256Time: 199), + new algo_time_t(tableTime: 539, decode256Time: 110), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 197, decode256Time: 194), + new algo_time_t(tableTime: 644, decode256Time: 107), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 221, decode256Time: 192), + new algo_time_t(tableTime: 735, decode256Time: 107), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 256, decode256Time: 189), + new algo_time_t(tableTime: 881, decode256Time: 106), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 359, decode256Time: 188), + new algo_time_t(tableTime: 1167, decode256Time: 109), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 582, decode256Time: 187), + new algo_time_t(tableTime: 1570, decode256Time: 114), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 688, decode256Time: 187), + new algo_time_t(tableTime: 1712, decode256Time: 122), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 825, decode256Time: 186), + new algo_time_t(tableTime: 1965, decode256Time: 136), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 976, decode256Time: 185), + new algo_time_t(tableTime: 2131, decode256Time: 150), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 1180, decode256Time: 186), + new algo_time_t(tableTime: 2070, decode256Time: 175), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 1377, decode256Time: 185), + new algo_time_t(tableTime: 1731, decode256Time: 202), + }, + new algo_time_t[2] + { + new algo_time_t(tableTime: 1412, decode256Time: 185), + new algo_time_t(tableTime: 1695, decode256Time: 202), + }, + }; + + /** HUF_selectDecoder() : + * Tells which decoder is likely to decode faster, + * based on a set of pre-computed metrics. + * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X2 . + * Assumption : 0 < dstSize <= 128 KB */ + private static uint HUF_selectDecoder(nuint dstSize, nuint cSrcSize) + { + assert(dstSize > 0); + assert(dstSize <= 128 * 1024); + { + /* Q < 16 */ + uint Q = cSrcSize >= dstSize ? 15 : (uint)(cSrcSize * 16 / dstSize); + uint D256 = (uint)(dstSize >> 8); + uint DTime0 = algoTime[Q][0].tableTime + algoTime[Q][0].decode256Time * D256; + uint DTime1 = algoTime[Q][1].tableTime + algoTime[Q][1].decode256Time * D256; + DTime1 += DTime1 >> 5; + return DTime1 < DTime0 ? 1U : 0U; + } + } + + private static nuint HUF_decompress1X_DCtx_wksp( + uint* dctx, + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + void* workSpace, + nuint wkspSize, + int flags + ) + { + if (dstSize == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + if (cSrcSize > dstSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (cSrcSize == dstSize) + { + memcpy(dst, cSrc, (uint)dstSize); + return dstSize; + } + + if (cSrcSize == 1) + { + memset(dst, *(byte*)cSrc, (uint)dstSize); + return dstSize; + } + + { + uint algoNb = HUF_selectDecoder(dstSize, cSrcSize); + return algoNb != 0 + ? HUF_decompress1X2_DCtx_wksp( + dctx, + dst, + dstSize, + cSrc, + cSrcSize, + workSpace, + wkspSize, + flags + ) + : HUF_decompress1X1_DCtx_wksp( + dctx, + dst, + dstSize, + cSrc, + cSrcSize, + workSpace, + wkspSize, + flags + ); + } + } + + /* BMI2 variants. + * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0. + */ + private static nuint HUF_decompress1X_usingDTable( + void* dst, + nuint maxDstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable, + int flags + ) + { + DTableDesc dtd = HUF_getDTableDesc(DTable); + return dtd.tableType != 0 + ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags) + : HUF_decompress1X1_usingDTable_internal( + dst, + maxDstSize, + cSrc, + cSrcSize, + DTable, + flags + ); + } + + private static nuint HUF_decompress1X1_DCtx_wksp( + uint* dctx, + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + void* workSpace, + nuint wkspSize, + int flags + ) + { + byte* ip = (byte*)cSrc; + nuint hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize, flags); + if (ERR_isError(hSize)) + return hSize; + if (hSize >= cSrcSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + ip += hSize; + cSrcSize -= hSize; + return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags); + } + + private static nuint HUF_decompress4X_usingDTable( + void* dst, + nuint maxDstSize, + void* cSrc, + nuint cSrcSize, + uint* DTable, + int flags + ) + { + DTableDesc dtd = HUF_getDTableDesc(DTable); + return dtd.tableType != 0 + ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags) + : HUF_decompress4X1_usingDTable_internal( + dst, + maxDstSize, + cSrc, + cSrcSize, + DTable, + flags + ); + } + + private static nuint HUF_decompress4X_hufOnly_wksp( + uint* dctx, + void* dst, + nuint dstSize, + void* cSrc, + nuint cSrcSize, + void* workSpace, + nuint wkspSize, + int flags + ) + { + if (dstSize == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + if (cSrcSize == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + { + uint algoNb = HUF_selectDecoder(dstSize, cSrcSize); + return algoNb != 0 + ? HUF_decompress4X2_DCtx_wksp( + dctx, + dst, + dstSize, + cSrc, + cSrcSize, + workSpace, + wkspSize, + flags + ) + : HUF_decompress4X1_DCtx_wksp( + dctx, + dst, + dstSize, + cSrc, + cSrcSize, + workSpace, + wkspSize, + flags + ); + } + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Mem.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Mem.cs new file mode 100644 index 00000000..673c7c95 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Mem.cs @@ -0,0 +1,162 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using BclUnsafe = System.Runtime.CompilerServices.Unsafe; + +// ReSharper disable InconsistentNaming +// ReSharper disable IdentifierTypo + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /*-************************************************************** + * Memory I/O API + *****************************************************************/ + /*=== Static platform detection ===*/ + private static bool MEM_32bits + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => sizeof(nint) == 4; + } + + private static bool MEM_64bits + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => sizeof(nint) == 8; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + /* default method, safe and standard. + can sometimes prove slower */ + private static ushort MEM_read16(void* memPtr) => BclUnsafe.ReadUnaligned(memPtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MEM_read32(void* memPtr) => BclUnsafe.ReadUnaligned(memPtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong MEM_read64(void* memPtr) => BclUnsafe.ReadUnaligned(memPtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint MEM_readST(void* memPtr) => BclUnsafe.ReadUnaligned(memPtr); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void MEM_write16(void* memPtr, ushort value) => + BclUnsafe.WriteUnaligned(memPtr, value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void MEM_write64(void* memPtr, ulong value) => + BclUnsafe.WriteUnaligned(memPtr, value); + + /*=== Little endian r/w ===*/ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort MEM_readLE16(void* memPtr) + { + var val = BclUnsafe.ReadUnaligned(memPtr); + if (!BitConverter.IsLittleEndian) + { + val = BinaryPrimitives.ReverseEndianness(val); + } + return val; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void MEM_writeLE16(void* memPtr, ushort val) + { + if (!BitConverter.IsLittleEndian) + { + val = BinaryPrimitives.ReverseEndianness(val); + } + BclUnsafe.WriteUnaligned(memPtr, val); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MEM_readLE24(void* memPtr) => + (uint)(MEM_readLE16(memPtr) + (((byte*)memPtr)[2] << 16)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void MEM_writeLE24(void* memPtr, uint val) + { + MEM_writeLE16(memPtr, (ushort)val); + ((byte*)memPtr)[2] = (byte)(val >> 16); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MEM_readLE32(void* memPtr) + { + var val = BclUnsafe.ReadUnaligned(memPtr); + if (!BitConverter.IsLittleEndian) + { + val = BinaryPrimitives.ReverseEndianness(val); + } + return val; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void MEM_writeLE32(void* memPtr, uint val32) + { + if (!BitConverter.IsLittleEndian) + { + val32 = BinaryPrimitives.ReverseEndianness(val32); + } + BclUnsafe.WriteUnaligned(memPtr, val32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong MEM_readLE64(void* memPtr) + { + var val = BclUnsafe.ReadUnaligned(memPtr); + if (!BitConverter.IsLittleEndian) + { + val = BinaryPrimitives.ReverseEndianness(val); + } + return val; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void MEM_writeLE64(void* memPtr, ulong val64) + { + if (!BitConverter.IsLittleEndian) + { + val64 = BinaryPrimitives.ReverseEndianness(val64); + } + BclUnsafe.WriteUnaligned(memPtr, val64); + } + +#if !NET8_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ReverseEndiannessNative(nuint val) => + MEM_32bits + ? BinaryPrimitives.ReverseEndianness((uint)val) + : (nuint)BinaryPrimitives.ReverseEndianness(val); +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint MEM_readLEST(void* memPtr) + { + var val = BclUnsafe.ReadUnaligned(memPtr); + if (!BitConverter.IsLittleEndian) + { +#if NET8_0_OR_GREATER + val = BinaryPrimitives.ReverseEndianness(val); +#else + val = ReverseEndiannessNative(val); +#endif + } + return val; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void MEM_writeLEST(void* memPtr, nuint val) + { + if (!BitConverter.IsLittleEndian) + { +#if NET8_0_OR_GREATER + val = BinaryPrimitives.ReverseEndianness(val); +#else + val = ReverseEndiannessNative(val); +#endif + } + BclUnsafe.WriteUnaligned(memPtr, val); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Pool.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Pool.cs new file mode 100644 index 00000000..a7a4e5fe --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Pool.cs @@ -0,0 +1,122 @@ +using SharpCompress.Compressors.ZStandard.Unsafe; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + private static JobThreadPool GetThreadPool(void* ctx) => + UnmanagedObject.Unwrap(ctx); + + /* ZSTD_createThreadPool() : public access point */ + public static void* ZSTD_createThreadPool(nuint numThreads) + { + return POOL_create(numThreads, 0); + } + + /*! POOL_create() : + * Create a thread pool with at most `numThreads` threads. + * `numThreads` must be at least 1. + * The maximum number of queued jobs before blocking is `queueSize`. + * @return : POOL_ctx pointer on success, else NULL. + */ + private static void* POOL_create(nuint numThreads, nuint queueSize) + { + return POOL_create_advanced(numThreads, queueSize, Unsafe.Methods.ZSTD_defaultCMem); + } + + private static void* POOL_create_advanced( + nuint numThreads, + nuint queueSize, + ZSTD_customMem customMem + ) + { + var jobThreadPool = new JobThreadPool((int)numThreads, (int)queueSize); + return UnmanagedObject.Wrap(jobThreadPool); + } + + /*! POOL_join() : + Shutdown the queue, wake any sleeping threads, and join all of the threads. + */ + private static void POOL_join(void* ctx) + { + GetThreadPool(ctx).Join(); + } + + /*! POOL_free() : + * Free a thread pool returned by POOL_create(). + */ + private static void POOL_free(void* ctx) + { + if (ctx == null) + { + return; + } + + var jobThreadPool = GetThreadPool(ctx); + jobThreadPool.Join(); + jobThreadPool.Dispose(); + UnmanagedObject.Free(ctx); + } + + /*! POOL_joinJobs() : + * Waits for all queued jobs to finish executing. + */ + private static void POOL_joinJobs(void* ctx) + { + var jobThreadPool = GetThreadPool(ctx); + jobThreadPool.Join(false); + } + + public static void ZSTD_freeThreadPool(void* pool) + { + POOL_free(pool); + } + + /*! POOL_sizeof() : + * @return threadpool memory usage + * note : compatible with NULL (returns 0 in this case) + */ + private static nuint POOL_sizeof(void* ctx) + { + if (ctx == null) + return 0; + var jobThreadPool = GetThreadPool(ctx); + return (nuint)jobThreadPool.Size(); + } + + /* @return : 0 on success, 1 on error */ + private static int POOL_resize(void* ctx, nuint numThreads) + { + if (ctx == null) + return 1; + var jobThreadPool = GetThreadPool(ctx); + jobThreadPool.Resize((int)numThreads); + return 0; + } + + /*! POOL_add() : + * Add the job `function(opaque)` to the thread pool. `ctx` must be valid. + * Possibly blocks until there is room in the queue. + * Note : The function may be executed asynchronously, + * therefore, `opaque` must live until function has been completed. + */ + private static void POOL_add(void* ctx, void* function, void* opaque) + { + assert(ctx != null); + var jobThreadPool = GetThreadPool(ctx); + jobThreadPool.Add(function, opaque); + } + + /*! POOL_tryAdd() : + * Add the job `function(opaque)` to thread pool _if_ a queue slot is available. + * Returns immediately even if not (does not block). + * @return : 1 if successful, 0 if not. + */ + private static int POOL_tryAdd(void* ctx, void* function, void* opaque) + { + assert(ctx != null); + var jobThreadPool = GetThreadPool(ctx); + return jobThreadPool.TryAdd(function, opaque) ? 1 : 0; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/RSyncState_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/RSyncState_t.cs new file mode 100644 index 00000000..7b5f08ad --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/RSyncState_t.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct RSyncState_t +{ + public ulong hash; + public ulong hitMask; + public ulong primePower; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Range.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Range.cs new file mode 100644 index 00000000..fabf08b0 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Range.cs @@ -0,0 +1,14 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* ==== Serial State ==== */ +public unsafe struct Range +{ + public void* start; + public nuint size; + + public Range(void* start, nuint size) + { + this.start = start; + this.size = size; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/RawSeqStore_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/RawSeqStore_t.cs new file mode 100644 index 00000000..07db05a1 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/RawSeqStore_t.cs @@ -0,0 +1,29 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct RawSeqStore_t +{ + /* The start of the sequences */ + public rawSeq* seq; + + /* The index in seq where reading stopped. pos <= size. */ + public nuint pos; + + /* The position within the sequence at seq[pos] where reading + stopped. posInSequence <= seq[pos].litLength + seq[pos].matchLength */ + public nuint posInSequence; + + /* The number of sequences. <= capacity. */ + public nuint size; + + /* The capacity starting from `seq` pointer */ + public nuint capacity; + + public RawSeqStore_t(rawSeq* seq, nuint pos, nuint posInSequence, nuint size, nuint capacity) + { + this.seq = seq; + this.pos = pos; + this.posInSequence = posInSequence; + this.size = size; + this.capacity = capacity; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/RoundBuff_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/RoundBuff_t.cs new file mode 100644 index 00000000..84e42ca4 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/RoundBuff_t.cs @@ -0,0 +1,28 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct RoundBuff_t +{ + /* The round input buffer. All jobs get references + * to pieces of the buffer. ZSTDMT_tryGetInputRange() + * handles handing out job input buffers, and makes + * sure it doesn't overlap with any pieces still in use. + */ + public byte* buffer; + + /* The capacity of buffer. */ + public nuint capacity; + + /* The position of the current inBuff in the round + * buffer. Updated past the end if the inBuff once + * the inBuff is sent to the worker thread. + * pos <= capacity. + */ + public nuint pos; + + public RoundBuff_t(byte* buffer, nuint capacity, nuint pos) + { + this.buffer = buffer; + this.capacity = capacity; + this.pos = pos; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/SeqCollector.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/SeqCollector.cs new file mode 100644 index 00000000..8026ed92 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/SeqCollector.cs @@ -0,0 +1,9 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct SeqCollector +{ + public int collectSequences; + public ZSTD_Sequence* seqStart; + public nuint seqIndex; + public nuint maxSequences; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/SeqDef_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/SeqDef_s.cs new file mode 100644 index 00000000..1e5aac57 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/SeqDef_s.cs @@ -0,0 +1,14 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*********************************************** + * Sequences * + ***********************************************/ +public struct SeqDef_s +{ + /* offBase == Offset + ZSTD_REP_NUM, or repcode 1,2,3 */ + public uint offBase; + public ushort litLength; + + /* mlBase == matchLength - MINMATCH */ + public ushort mlBase; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/SeqStore_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/SeqStore_t.cs new file mode 100644 index 00000000..6aa75161 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/SeqStore_t.cs @@ -0,0 +1,27 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct SeqStore_t +{ + public SeqDef_s* sequencesStart; + + /* ptr to end of sequences */ + public SeqDef_s* sequences; + public byte* litStart; + + /* ptr to end of literals */ + public byte* lit; + public byte* llCode; + public byte* mlCode; + public byte* ofCode; + public nuint maxNbSeq; + public nuint maxNbLit; + + /* longLengthPos and longLengthType to allow us to represent either a single litLength or matchLength + * in the seqStore that has a value larger than U16 (if it exists). To do so, we increment + * the existing value of the litLength or matchLength by 0x10000. + */ + public ZSTD_longLengthType_e longLengthType; + + /* Index of the sequence to apply long length modification to */ + public uint longLengthPos; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/SerialState.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/SerialState.cs new file mode 100644 index 00000000..91d403e3 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/SerialState.cs @@ -0,0 +1,23 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct SerialState +{ + /* All variables in the struct are protected by mutex. */ + public void* mutex; + public void* cond; + public ZSTD_CCtx_params_s @params; + public ldmState_t ldmState; + public XXH64_state_s xxhState; + public uint nextJobID; + + /* Protects ldmWindow. + * Must be acquired after the main mutex when acquiring both. + */ + public void* ldmWindowMutex; + + /* Signaled when ldmWindow is updated */ + public void* ldmWindowCond; + + /* A thread-safe copy of ldmState.window */ + public ZSTD_window_t ldmWindow; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/SymbolEncodingType_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/SymbolEncodingType_e.cs new file mode 100644 index 00000000..06d78d45 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/SymbolEncodingType_e.cs @@ -0,0 +1,9 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum SymbolEncodingType_e +{ + set_basic, + set_rle, + set_compressed, + set_repeat, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/SyncPoint.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/SyncPoint.cs new file mode 100644 index 00000000..fda2ea51 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/SyncPoint.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct SyncPoint +{ + /* The number of bytes to load from the input. */ + public nuint toLoad; + + /* Boolean declaring if we must flush because we found a synchronization point. */ + public int flush; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH32_canonical_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH32_canonical_t.cs new file mode 100644 index 00000000..7da74bdf --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH32_canonical_t.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*! + * @brief Canonical (big endian) representation of @ref XXH32_hash_t. + */ +public unsafe struct XXH32_canonical_t +{ + /*!< Hash bytes, big endian */ + public fixed byte digest[4]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH32_state_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH32_state_s.cs new file mode 100644 index 00000000..09559a84 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH32_state_s.cs @@ -0,0 +1,34 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*! + * @internal + * @brief Structure for XXH32 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is + * an opaque type. This allows fields to safely be changed. + * + * Typedef'd to @ref XXH32_state_t. + * Do not access the members of this struct directly. + * @see XXH64_state_s, XXH3_state_s + */ +public unsafe struct XXH32_state_s +{ + /*!< Total length hashed, modulo 2^32 */ + public uint total_len_32; + + /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */ + public uint large_len; + + /*!< Accumulator lanes */ + public fixed uint v[4]; + + /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */ + public fixed uint mem32[4]; + + /*!< Amount of data in @ref mem32 */ + public uint memsize; + + /*!< Reserved field. Do not read nor write to it. */ + public uint reserved; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH64_canonical_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH64_canonical_t.cs new file mode 100644 index 00000000..a5040988 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH64_canonical_t.cs @@ -0,0 +1,9 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*! + * @brief Canonical (big endian) representation of @ref XXH64_hash_t. + */ +public unsafe struct XXH64_canonical_t +{ + public fixed byte digest[8]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH64_state_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH64_state_s.cs new file mode 100644 index 00000000..d2503d08 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH64_state_s.cs @@ -0,0 +1,34 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*! + * @internal + * @brief Structure for XXH64 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is + * an opaque type. This allows fields to safely be changed. + * + * Typedef'd to @ref XXH64_state_t. + * Do not access the members of this struct directly. + * @see XXH32_state_s, XXH3_state_s + */ +public unsafe struct XXH64_state_s +{ + /*!< Total length hashed. This is always 64-bit. */ + public ulong total_len; + + /*!< Accumulator lanes */ + public fixed ulong v[4]; + + /*!< Internal buffer for partial reads. Treated as unsigned char[32]. */ + public fixed ulong mem64[4]; + + /*!< Amount of data in @ref mem64 */ + public uint memsize; + + /*!< Reserved field, needed for padding anyways*/ + public uint reserved32; + + /*!< Reserved field. Do not read or write to it. */ + public ulong reserved64; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH_alignment.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH_alignment.cs new file mode 100644 index 00000000..57b98809 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH_alignment.cs @@ -0,0 +1,14 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*! + * @internal + * @brief Enum to indicate whether a pointer is aligned. + */ +public enum XXH_alignment +{ + /*!< Aligned */ + XXH_aligned, + + /*!< Possibly unaligned */ + XXH_unaligned, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH_errorcode.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH_errorcode.cs new file mode 100644 index 00000000..eb65ceb1 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/XXH_errorcode.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*! + * @brief Exit code for the streaming API. + */ +public enum XXH_errorcode +{ + /*!< OK */ + XXH_OK = 0, + + /*!< Error */ + XXH_ERROR, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Xxhash.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Xxhash.cs new file mode 100644 index 00000000..ab8d814b --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Xxhash.cs @@ -0,0 +1,626 @@ +using System; +using System.Buffers.Binary; +using System.Numerics; +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /*! + * @internal + * @brief Modify this function to use a different routine than malloc(). + */ + private static void* XXH_malloc(nuint s) + { + return malloc(s); + } + + /*! + * @internal + * @brief Modify this function to use a different routine than free(). + */ + private static void XXH_free(void* p) + { + free(p); + } + + /*! + * @internal + * @brief Modify this function to use a different routine than memcpy(). + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void XXH_memcpy(void* dest, void* src, nuint size) + { + memcpy(dest, src, (uint)size); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint XXH_readLE32(void* ptr) + { + return BitConverter.IsLittleEndian + ? MEM_read32(ptr) + : BinaryPrimitives.ReverseEndianness(MEM_read32(ptr)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint XXH_readBE32(void* ptr) + { + return BitConverter.IsLittleEndian + ? BinaryPrimitives.ReverseEndianness(MEM_read32(ptr)) + : MEM_read32(ptr); + } + + private static uint XXH_readLE32_align(void* ptr, XXH_alignment align) + { + if (align == XXH_alignment.XXH_unaligned) + { + return XXH_readLE32(ptr); + } + else + { + return BitConverter.IsLittleEndian + ? *(uint*)ptr + : BinaryPrimitives.ReverseEndianness(*(uint*)ptr); + } + } + + /* ************************************* + * Misc + ***************************************/ + /*! @ingroup public */ + private static uint ZSTD_XXH_versionNumber() + { + return 0 * 100 * 100 + 8 * 100 + 2; + } + + /*! + * @internal + * @brief Normal stripe processing routine. + * + * This shuffles the bits so that any bit from @p input impacts several bits in + * @p acc. + * + * @param acc The accumulator lane. + * @param input The stripe of input to mix. + * @return The mixed accumulator lane. + */ + private static uint XXH32_round(uint acc, uint input) + { + acc += input * 0x85EBCA77U; + acc = BitOperations.RotateLeft(acc, 13); + acc *= 0x9E3779B1U; + return acc; + } + + /*! + * @internal + * @brief Mixes all bits to finalize the hash. + * + * The final mix ensures that all input bits have a chance to impact any bit in + * the output digest, resulting in an unbiased distribution. + * + * @param hash The hash to avalanche. + * @return The avalanched hash. + */ + private static uint XXH32_avalanche(uint hash) + { + hash ^= hash >> 15; + hash *= 0x85EBCA77U; + hash ^= hash >> 13; + hash *= 0xC2B2AE3DU; + hash ^= hash >> 16; + return hash; + } + + /*! + * @internal + * @brief Processes the last 0-15 bytes of @p ptr. + * + * There may be up to 15 bytes remaining to consume from the input. + * This final stage will digest them to ensure that all input bytes are present + * in the final mix. + * + * @param hash The hash to finalize. + * @param ptr The pointer to the remaining input. + * @param len The remaining length, modulo 16. + * @param align Whether @p ptr is aligned. + * @return The finalized hash. + * @see XXH64_finalize(). + */ + private static uint XXH32_finalize(uint hash, byte* ptr, nuint len, XXH_alignment align) + { + len &= 15; + while (len >= 4) + { + { + hash += XXH_readLE32_align(ptr, align) * 0xC2B2AE3DU; + ptr += 4; + hash = BitOperations.RotateLeft(hash, 17) * 0x27D4EB2FU; + } + + len -= 4; + } + + while (len > 0) + { + { + hash += *ptr++ * 0x165667B1U; + hash = BitOperations.RotateLeft(hash, 11) * 0x9E3779B1U; + } + + --len; + } + + return XXH32_avalanche(hash); + } + + /*! + * @internal + * @brief The implementation for @ref XXH32(). + * + * @param input , len , seed Directly passed from @ref XXH32(). + * @param align Whether @p input is aligned. + * @return The calculated hash. + */ + private static uint XXH32_endian_align(byte* input, nuint len, uint seed, XXH_alignment align) + { + uint h32; + if (len >= 16) + { + byte* bEnd = input + len; + byte* limit = bEnd - 15; + uint v1 = seed + 0x9E3779B1U + 0x85EBCA77U; + uint v2 = seed + 0x85EBCA77U; + uint v3 = seed + 0; + uint v4 = seed - 0x9E3779B1U; + do + { + v1 = XXH32_round(v1, XXH_readLE32_align(input, align)); + input += 4; + v2 = XXH32_round(v2, XXH_readLE32_align(input, align)); + input += 4; + v3 = XXH32_round(v3, XXH_readLE32_align(input, align)); + input += 4; + v4 = XXH32_round(v4, XXH_readLE32_align(input, align)); + input += 4; + } while (input < limit); + h32 = + BitOperations.RotateLeft(v1, 1) + + BitOperations.RotateLeft(v2, 7) + + BitOperations.RotateLeft(v3, 12) + + BitOperations.RotateLeft(v4, 18); + } + else + { + h32 = seed + 0x165667B1U; + } + + h32 += (uint)len; + return XXH32_finalize(h32, input, len & 15, align); + } + + /*! @ingroup XXH32_family */ + private static uint ZSTD_XXH32(void* input, nuint len, uint seed) + { + return XXH32_endian_align((byte*)input, len, seed, XXH_alignment.XXH_unaligned); + } + + /*! @ingroup XXH32_family */ + private static XXH32_state_s* ZSTD_XXH32_createState() + { + return (XXH32_state_s*)XXH_malloc((nuint)sizeof(XXH32_state_s)); + } + + /*! @ingroup XXH32_family */ + private static XXH_errorcode ZSTD_XXH32_freeState(XXH32_state_s* statePtr) + { + XXH_free(statePtr); + return XXH_errorcode.XXH_OK; + } + + /*! @ingroup XXH32_family */ + private static void ZSTD_XXH32_copyState(XXH32_state_s* dstState, XXH32_state_s* srcState) + { + XXH_memcpy(dstState, srcState, (nuint)sizeof(XXH32_state_s)); + } + + /*! @ingroup XXH32_family */ + private static XXH_errorcode ZSTD_XXH32_reset(XXH32_state_s* statePtr, uint seed) + { + *statePtr = new XXH32_state_s(); + statePtr->v[0] = seed + 0x9E3779B1U + 0x85EBCA77U; + statePtr->v[1] = seed + 0x85EBCA77U; + statePtr->v[2] = seed + 0; + statePtr->v[3] = seed - 0x9E3779B1U; + return XXH_errorcode.XXH_OK; + } + + /*! @ingroup XXH32_family */ + private static XXH_errorcode ZSTD_XXH32_update(XXH32_state_s* state, void* input, nuint len) + { + if (input == null) + { + return XXH_errorcode.XXH_OK; + } + + { + byte* p = (byte*)input; + byte* bEnd = p + len; + state->total_len_32 += (uint)len; + state->large_len |= len >= 16 || state->total_len_32 >= 16 ? 1U : 0U; + if (state->memsize + len < 16) + { + XXH_memcpy((byte*)state->mem32 + state->memsize, input, len); + state->memsize += (uint)len; + return XXH_errorcode.XXH_OK; + } + + if (state->memsize != 0) + { + XXH_memcpy((byte*)state->mem32 + state->memsize, input, 16 - state->memsize); + { + uint* p32 = state->mem32; + state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p32)); + p32++; + state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p32)); + p32++; + state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p32)); + p32++; + state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p32)); + } + + p += 16 - state->memsize; + state->memsize = 0; + } + + if (p <= bEnd - 16) + { + byte* limit = bEnd - 16; + do + { + state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p)); + p += 4; + state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p)); + p += 4; + state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p)); + p += 4; + state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p)); + p += 4; + } while (p <= limit); + } + + if (p < bEnd) + { + XXH_memcpy(state->mem32, p, (nuint)(bEnd - p)); + state->memsize = (uint)(bEnd - p); + } + } + + return XXH_errorcode.XXH_OK; + } + + /*! @ingroup XXH32_family */ + private static uint ZSTD_XXH32_digest(XXH32_state_s* state) + { + uint h32; + if (state->large_len != 0) + { + h32 = + BitOperations.RotateLeft(state->v[0], 1) + + BitOperations.RotateLeft(state->v[1], 7) + + BitOperations.RotateLeft(state->v[2], 12) + + BitOperations.RotateLeft(state->v[3], 18); + } + else + { + h32 = state->v[2] + 0x165667B1U; + } + + h32 += state->total_len_32; + return XXH32_finalize(h32, (byte*)state->mem32, state->memsize, XXH_alignment.XXH_aligned); + } + + /*! @ingroup XXH32_family */ + private static void ZSTD_XXH32_canonicalFromHash(XXH32_canonical_t* dst, uint hash) + { + assert(sizeof(XXH32_canonical_t) == sizeof(uint)); + if (BitConverter.IsLittleEndian) + hash = BinaryPrimitives.ReverseEndianness(hash); + XXH_memcpy(dst, &hash, (nuint)sizeof(XXH32_canonical_t)); + } + + /*! @ingroup XXH32_family */ + private static uint ZSTD_XXH32_hashFromCanonical(XXH32_canonical_t* src) + { + return XXH_readBE32(src); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH_readLE64(void* ptr) + { + return BitConverter.IsLittleEndian + ? MEM_read64(ptr) + : BinaryPrimitives.ReverseEndianness(MEM_read64(ptr)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH_readBE64(void* ptr) + { + return BitConverter.IsLittleEndian + ? BinaryPrimitives.ReverseEndianness(MEM_read64(ptr)) + : MEM_read64(ptr); + } + + private static ulong XXH_readLE64_align(void* ptr, XXH_alignment align) + { + if (align == XXH_alignment.XXH_unaligned) + return XXH_readLE64(ptr); + else + return BitConverter.IsLittleEndian + ? *(ulong*)ptr + : BinaryPrimitives.ReverseEndianness(*(ulong*)ptr); + } + + /*! @copydoc XXH32_round */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH64_round(ulong acc, ulong input) + { + acc += input * 0xC2B2AE3D27D4EB4FUL; + acc = BitOperations.RotateLeft(acc, 31); + acc *= 0x9E3779B185EBCA87UL; + return acc; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH64_mergeRound(ulong acc, ulong val) + { + val = XXH64_round(0, val); + acc ^= val; + acc = acc * 0x9E3779B185EBCA87UL + 0x85EBCA77C2B2AE63UL; + return acc; + } + + /*! @copydoc XXH32_avalanche */ + private static ulong XXH64_avalanche(ulong hash) + { + hash ^= hash >> 33; + hash *= 0xC2B2AE3D27D4EB4FUL; + hash ^= hash >> 29; + hash *= 0x165667B19E3779F9UL; + hash ^= hash >> 32; + return hash; + } + + /*! + * @internal + * @brief Processes the last 0-31 bytes of @p ptr. + * + * There may be up to 31 bytes remaining to consume from the input. + * This final stage will digest them to ensure that all input bytes are present + * in the final mix. + * + * @param hash The hash to finalize. + * @param ptr The pointer to the remaining input. + * @param len The remaining length, modulo 32. + * @param align Whether @p ptr is aligned. + * @return The finalized hash + * @see XXH32_finalize(). + */ + private static ulong XXH64_finalize(ulong hash, byte* ptr, nuint len, XXH_alignment align) + { + len &= 31; + while (len >= 8) + { + ulong k1 = XXH64_round(0, XXH_readLE64_align(ptr, align)); + ptr += 8; + hash ^= k1; + hash = BitOperations.RotateLeft(hash, 27) * 0x9E3779B185EBCA87UL + 0x85EBCA77C2B2AE63UL; + len -= 8; + } + + if (len >= 4) + { + hash ^= XXH_readLE32_align(ptr, align) * 0x9E3779B185EBCA87UL; + ptr += 4; + hash = BitOperations.RotateLeft(hash, 23) * 0xC2B2AE3D27D4EB4FUL + 0x165667B19E3779F9UL; + len -= 4; + } + + while (len > 0) + { + hash ^= *ptr++ * 0x27D4EB2F165667C5UL; + hash = BitOperations.RotateLeft(hash, 11) * 0x9E3779B185EBCA87UL; + --len; + } + + return XXH64_avalanche(hash); + } + + /*! + * @internal + * @brief The implementation for @ref XXH64(). + * + * @param input , len , seed Directly passed from @ref XXH64(). + * @param align Whether @p input is aligned. + * @return The calculated hash. + */ + private static ulong XXH64_endian_align(byte* input, nuint len, ulong seed, XXH_alignment align) + { + ulong h64; + if (len >= 32) + { + byte* bEnd = input + len; + byte* limit = bEnd - 31; + ulong v1 = seed + 0x9E3779B185EBCA87UL + 0xC2B2AE3D27D4EB4FUL; + ulong v2 = seed + 0xC2B2AE3D27D4EB4FUL; + ulong v3 = seed + 0; + ulong v4 = seed - 0x9E3779B185EBCA87UL; + do + { + v1 = XXH64_round(v1, XXH_readLE64_align(input, align)); + input += 8; + v2 = XXH64_round(v2, XXH_readLE64_align(input, align)); + input += 8; + v3 = XXH64_round(v3, XXH_readLE64_align(input, align)); + input += 8; + v4 = XXH64_round(v4, XXH_readLE64_align(input, align)); + input += 8; + } while (input < limit); + h64 = + BitOperations.RotateLeft(v1, 1) + + BitOperations.RotateLeft(v2, 7) + + BitOperations.RotateLeft(v3, 12) + + BitOperations.RotateLeft(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + } + else + { + h64 = seed + 0x27D4EB2F165667C5UL; + } + + h64 += len; + return XXH64_finalize(h64, input, len, align); + } + + /*! @ingroup XXH64_family */ + private static ulong ZSTD_XXH64(void* input, nuint len, ulong seed) + { + return XXH64_endian_align((byte*)input, len, seed, XXH_alignment.XXH_unaligned); + } + + /*! @ingroup XXH64_family*/ + private static XXH64_state_s* ZSTD_XXH64_createState() + { + return (XXH64_state_s*)XXH_malloc((nuint)sizeof(XXH64_state_s)); + } + + /*! @ingroup XXH64_family */ + private static XXH_errorcode ZSTD_XXH64_freeState(XXH64_state_s* statePtr) + { + XXH_free(statePtr); + return XXH_errorcode.XXH_OK; + } + + /*! @ingroup XXH64_family */ + private static void ZSTD_XXH64_copyState(XXH64_state_s* dstState, XXH64_state_s* srcState) + { + XXH_memcpy(dstState, srcState, (nuint)sizeof(XXH64_state_s)); + } + + /*! @ingroup XXH64_family */ + private static XXH_errorcode ZSTD_XXH64_reset(XXH64_state_s* statePtr, ulong seed) + { + *statePtr = new XXH64_state_s(); + statePtr->v[0] = seed + 0x9E3779B185EBCA87UL + 0xC2B2AE3D27D4EB4FUL; + statePtr->v[1] = seed + 0xC2B2AE3D27D4EB4FUL; + statePtr->v[2] = seed + 0; + statePtr->v[3] = seed - 0x9E3779B185EBCA87UL; + return XXH_errorcode.XXH_OK; + } + + /*! @ingroup XXH64_family */ + private static XXH_errorcode ZSTD_XXH64_update(XXH64_state_s* state, void* input, nuint len) + { + if (input == null) + { + return XXH_errorcode.XXH_OK; + } + + { + byte* p = (byte*)input; + byte* bEnd = p + len; + state->total_len += len; + if (state->memsize + len < 32) + { + XXH_memcpy((byte*)state->mem64 + state->memsize, input, len); + state->memsize += (uint)len; + return XXH_errorcode.XXH_OK; + } + + if (state->memsize != 0) + { + XXH_memcpy((byte*)state->mem64 + state->memsize, input, 32 - state->memsize); + state->v[0] = XXH64_round(state->v[0], XXH_readLE64(state->mem64 + 0)); + state->v[1] = XXH64_round(state->v[1], XXH_readLE64(state->mem64 + 1)); + state->v[2] = XXH64_round(state->v[2], XXH_readLE64(state->mem64 + 2)); + state->v[3] = XXH64_round(state->v[3], XXH_readLE64(state->mem64 + 3)); + p += 32 - state->memsize; + state->memsize = 0; + } + + if (p + 32 <= bEnd) + { + byte* limit = bEnd - 32; + do + { + state->v[0] = XXH64_round(state->v[0], XXH_readLE64(p)); + p += 8; + state->v[1] = XXH64_round(state->v[1], XXH_readLE64(p)); + p += 8; + state->v[2] = XXH64_round(state->v[2], XXH_readLE64(p)); + p += 8; + state->v[3] = XXH64_round(state->v[3], XXH_readLE64(p)); + p += 8; + } while (p <= limit); + } + + if (p < bEnd) + { + XXH_memcpy(state->mem64, p, (nuint)(bEnd - p)); + state->memsize = (uint)(bEnd - p); + } + } + + return XXH_errorcode.XXH_OK; + } + + /*! @ingroup XXH64_family */ + private static ulong ZSTD_XXH64_digest(XXH64_state_s* state) + { + ulong h64; + if (state->total_len >= 32) + { + h64 = + BitOperations.RotateLeft(state->v[0], 1) + + BitOperations.RotateLeft(state->v[1], 7) + + BitOperations.RotateLeft(state->v[2], 12) + + BitOperations.RotateLeft(state->v[3], 18); + h64 = XXH64_mergeRound(h64, state->v[0]); + h64 = XXH64_mergeRound(h64, state->v[1]); + h64 = XXH64_mergeRound(h64, state->v[2]); + h64 = XXH64_mergeRound(h64, state->v[3]); + } + else + { + h64 = state->v[2] + 0x27D4EB2F165667C5UL; + } + + h64 += state->total_len; + return XXH64_finalize( + h64, + (byte*)state->mem64, + (nuint)state->total_len, + XXH_alignment.XXH_aligned + ); + } + + /*! @ingroup XXH64_family */ + private static void ZSTD_XXH64_canonicalFromHash(XXH64_canonical_t* dst, ulong hash) + { + assert(sizeof(XXH64_canonical_t) == sizeof(ulong)); + if (BitConverter.IsLittleEndian) + hash = BinaryPrimitives.ReverseEndianness(hash); + XXH_memcpy(dst, &hash, (nuint)sizeof(XXH64_canonical_t)); + } + + /*! @ingroup XXH64_family */ + private static ulong ZSTD_XXH64_hashFromCanonical(XXH64_canonical_t* src) + { + return XXH_readBE64(src); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_cover_params_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_cover_params_t.cs new file mode 100644 index 00000000..dfc4fecf --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_cover_params_t.cs @@ -0,0 +1,30 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*! ZDICT_cover_params_t: + * k and d are the only required parameters. + * For others, value 0 means default. + */ +public struct ZDICT_cover_params_t +{ + /* Segment size : constraint: 0 < k : Reasonable range [16, 2048+] */ + public uint k; + + /* dmer size : constraint: 0 < d <= k : Reasonable range [6, 16] */ + public uint d; + + /* Number of steps : Only used for optimization : 0 means default (40) : Higher means more parameters checked */ + public uint steps; + + /* Number of threads : constraint: 0 < nbThreads : 1 means single-threaded : Only used for optimization : Ignored if ZSTD_MULTITHREAD is not defined */ + public uint nbThreads; + + /* Percentage of samples used for training: Only used for optimization : the first nbSamples * splitPoint samples will be used to training, the last nbSamples * (1 - splitPoint) samples will be used for testing, 0 means default (1.0), 1.0 when all samples are used for both training and testing */ + public double splitPoint; + + /* Train dictionaries to shrink in size starting from the minimum size and selects the smallest dictionary that is shrinkDictMaxRegression% worse than the largest dictionary. 0 means no shrinking and 1 means shrinking */ + public uint shrinkDict; + + /* Sets shrinkDictMaxRegression so that a smaller dictionary can be at worse shrinkDictMaxRegression% worse than the max dict size dictionary. */ + public uint shrinkDictMaxRegression; + public ZDICT_params_t zParams; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_fastCover_params_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_fastCover_params_t.cs new file mode 100644 index 00000000..a5af135d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_fastCover_params_t.cs @@ -0,0 +1,32 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZDICT_fastCover_params_t +{ + /* Segment size : constraint: 0 < k : Reasonable range [16, 2048+] */ + public uint k; + + /* dmer size : constraint: 0 < d <= k : Reasonable range [6, 16] */ + public uint d; + + /* log of size of frequency array : constraint: 0 < f <= 31 : 1 means default(20)*/ + public uint f; + + /* Number of steps : Only used for optimization : 0 means default (40) : Higher means more parameters checked */ + public uint steps; + + /* Number of threads : constraint: 0 < nbThreads : 1 means single-threaded : Only used for optimization : Ignored if ZSTD_MULTITHREAD is not defined */ + public uint nbThreads; + + /* Percentage of samples used for training: Only used for optimization : the first nbSamples * splitPoint samples will be used to training, the last nbSamples * (1 - splitPoint) samples will be used for testing, 0 means default (0.75), 1.0 when all samples are used for both training and testing */ + public double splitPoint; + + /* Acceleration level: constraint: 0 < accel <= 10, higher means faster and less accurate, 0 means default(1) */ + public uint accel; + + /* Train dictionaries to shrink in size starting from the minimum size and selects the smallest dictionary that is shrinkDictMaxRegression% worse than the largest dictionary. 0 means no shrinking and 1 means shrinking */ + public uint shrinkDict; + + /* Sets shrinkDictMaxRegression so that a smaller dictionary can be at worse shrinkDictMaxRegression% worse than the max dict size dictionary. */ + public uint shrinkDictMaxRegression; + public ZDICT_params_t zParams; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_legacy_params_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_legacy_params_t.cs new file mode 100644 index 00000000..fd061e59 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_legacy_params_t.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZDICT_legacy_params_t +{ + /* 0 means default; larger => select more => larger dictionary */ + public uint selectivityLevel; + public ZDICT_params_t zParams; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_params_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_params_t.cs new file mode 100644 index 00000000..6c3fe758 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZDICT_params_t.cs @@ -0,0 +1,20 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZDICT_params_t +{ + /**< optimize for a specific zstd compression level; 0 means default */ + public int compressionLevel; + + /**< Write log to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ + public uint notificationLevel; + + /**< force dictID value; 0 means auto mode (32-bits random value) + * NOTE: The zstd format reserves some dictionary IDs for future use. + * You may use them in private settings, but be warned that they + * may be used by zstd in a public dictionary registry in the future. + * These dictionary IDs are: + * - low range : <= 32767 + * - high range : >= (2^31) + */ + public uint dictID; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_CCtxPool.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_CCtxPool.cs new file mode 100644 index 00000000..a9c7fccc --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_CCtxPool.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* ===== CCtx Pool ===== */ +/* a single CCtx Pool can be invoked from multiple threads in parallel */ +public unsafe struct ZSTDMT_CCtxPool +{ + public void* poolMutex; + public int totalCCtx; + public int availCCtx; + public ZSTD_customMem cMem; + public ZSTD_CCtx_s** cctxs; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_CCtx_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_CCtx_s.cs new file mode 100644 index 00000000..3e4e2cfe --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_CCtx_s.cs @@ -0,0 +1,32 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTDMT_CCtx_s +{ + public void* factory; + public ZSTDMT_jobDescription* jobs; + public ZSTDMT_bufferPool_s* bufPool; + public ZSTDMT_CCtxPool* cctxPool; + public ZSTDMT_bufferPool_s* seqPool; + public ZSTD_CCtx_params_s @params; + public nuint targetSectionSize; + public nuint targetPrefixSize; + + /* 1 => one job is already prepared, but pool has shortage of workers. Don't create a new job. */ + public int jobReady; + public InBuff_t inBuff; + public RoundBuff_t roundBuff; + public SerialState serial; + public RSyncState_t rsync; + public uint jobIDMask; + public uint doneJobID; + public uint nextJobID; + public uint frameEnded; + public uint allJobsCompleted; + public ulong frameContentSize; + public ulong consumed; + public ulong produced; + public ZSTD_customMem cMem; + public ZSTD_CDict_s* cdictLocal; + public ZSTD_CDict_s* cdict; + public uint providedFactory; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_bufferPool_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_bufferPool_s.cs new file mode 100644 index 00000000..0c64573a --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_bufferPool_s.cs @@ -0,0 +1,11 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTDMT_bufferPool_s +{ + public void* poolMutex; + public nuint bufferSize; + public uint totalBuffers; + public uint nbBuffers; + public ZSTD_customMem cMem; + public buffer_s* buffers; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_jobDescription.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_jobDescription.cs new file mode 100644 index 00000000..cb2c18f6 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTDMT_jobDescription.cs @@ -0,0 +1,61 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTDMT_jobDescription +{ + /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx */ + public nuint consumed; + + /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx, then set0 by mtctx */ + public nuint cSize; + + /* Thread-safe - used by mtctx and worker */ + public void* job_mutex; + + /* Thread-safe - used by mtctx and worker */ + public void* job_cond; + + /* Thread-safe - used by mtctx and (all) workers */ + public ZSTDMT_CCtxPool* cctxPool; + + /* Thread-safe - used by mtctx and (all) workers */ + public ZSTDMT_bufferPool_s* bufPool; + + /* Thread-safe - used by mtctx and (all) workers */ + public ZSTDMT_bufferPool_s* seqPool; + + /* Thread-safe - used by mtctx and (all) workers */ + public SerialState* serial; + + /* set by worker (or mtctx), then read by worker & mtctx, then modified by mtctx => no barrier */ + public buffer_s dstBuff; + + /* set by mtctx, then read by worker & mtctx => no barrier */ + public Range prefix; + + /* set by mtctx, then read by worker & mtctx => no barrier */ + public Range src; + + /* set by mtctx, then read by worker => no barrier */ + public uint jobID; + + /* set by mtctx, then read by worker => no barrier */ + public uint firstJob; + + /* set by mtctx, then read by worker => no barrier */ + public uint lastJob; + + /* set by mtctx, then read by worker => no barrier */ + public ZSTD_CCtx_params_s @params; + + /* set by mtctx, then read by worker => no barrier */ + public ZSTD_CDict_s* cdict; + + /* set by mtctx, then read by worker => no barrier */ + public ulong fullFrameSize; + + /* used only by mtctx */ + public nuint dstFlushed; + + /* used only by mtctx */ + public uint frameChecksumNeeded; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BlockCompressor_f.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BlockCompressor_f.cs new file mode 100644 index 00000000..08a44808 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BlockCompressor_f.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +[UnmanagedFunctionPointer(CallingConvention.Cdecl)] +public unsafe delegate nuint ZSTD_BlockCompressor_f( + ZSTD_MatchState_t* bs, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize +); diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BuildCTableWksp.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BuildCTableWksp.cs new file mode 100644 index 00000000..afdeff1d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BuildCTableWksp.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_BuildCTableWksp +{ + public fixed short norm[53]; + public fixed uint wksp[285]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BuildSeqStore_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BuildSeqStore_e.cs new file mode 100644 index 00000000..5378fc7e --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_BuildSeqStore_e.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_BuildSeqStore_e +{ + ZSTDbss_compress, + ZSTDbss_noCompress, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_CCtx_params_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_CCtx_params_s.cs new file mode 100644 index 00000000..e7a45dbe --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_CCtx_params_s.cs @@ -0,0 +1,86 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_CCtx_params_s +{ + public ZSTD_format_e format; + public ZSTD_compressionParameters cParams; + public ZSTD_frameParameters fParams; + public int compressionLevel; + + /* force back-references to respect limit of + * 1< 0: + * If litLength != 0: + * rep == 1 --> offset == repeat_offset_1 + * rep == 2 --> offset == repeat_offset_2 + * rep == 3 --> offset == repeat_offset_3 + * If litLength == 0: + * rep == 1 --> offset == repeat_offset_2 + * rep == 2 --> offset == repeat_offset_3 + * rep == 3 --> offset == repeat_offset_1 - 1 + * + * Note: This field is optional. ZSTD_generateSequences() will calculate the value of + * 'rep', but repeat offsets do not necessarily need to be calculated from an external + * sequence provider perspective. For example, ZSTD_compressSequences() does not + * use this 'rep' field at all (as of now). + */ + public uint rep; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_SequenceLength.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_SequenceLength.cs new file mode 100644 index 00000000..80e67fc9 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_SequenceLength.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZSTD_SequenceLength +{ + public uint litLength; + public uint matchLength; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_SequencePosition.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_SequencePosition.cs new file mode 100644 index 00000000..db0ec511 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_SequencePosition.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZSTD_SequencePosition +{ + /* Index in array of ZSTD_Sequence */ + public uint idx; + + /* Position within sequence at idx */ + public uint posInSequence; + + /* Number of bytes given by sequences provided so far */ + public nuint posInSrc; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_blockSplitCtx.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_blockSplitCtx.cs new file mode 100644 index 00000000..b83e4e1f --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_blockSplitCtx.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_blockSplitCtx +{ + public SeqStore_t fullSeqStoreChunk; + public SeqStore_t firstHalfSeqStore; + public SeqStore_t secondHalfSeqStore; + public SeqStore_t currSeqStore; + public SeqStore_t nextSeqStore; + public fixed uint partitions[196]; + public ZSTD_entropyCTablesMetadata_t entropyMetadata; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_blockState_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_blockState_t.cs new file mode 100644 index 00000000..5dd067a1 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_blockState_t.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_blockState_t +{ + public ZSTD_compressedBlockState_t* prevCBlock; + public ZSTD_compressedBlockState_t* nextCBlock; + public ZSTD_MatchState_t matchState; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_bounds.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_bounds.cs new file mode 100644 index 00000000..d82a2932 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_bounds.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZSTD_bounds +{ + public nuint error; + public int lowerBound; + public int upperBound; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_bufferMode_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_bufferMode_e.cs new file mode 100644 index 00000000..6ae23f31 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_bufferMode_e.cs @@ -0,0 +1,11 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* Controls whether the input/output buffer is buffered or stable. */ +public enum ZSTD_bufferMode_e +{ + /* Buffer the input/output */ + ZSTD_bm_buffered = 0, + + /* ZSTD_inBuffer/ZSTD_outBuffer is stable */ + ZSTD_bm_stable = 1, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_buffered_policy_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_buffered_policy_e.cs new file mode 100644 index 00000000..8d5a1a91 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_buffered_policy_e.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * Indicates whether this compression proceeds directly from user-provided + * source buffer to user-provided destination buffer (ZSTDb_not_buffered), or + * whether the context needs to buffer the input/output (ZSTDb_buffered). + */ +public enum ZSTD_buffered_policy_e +{ + ZSTDb_not_buffered, + ZSTDb_buffered, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cParameter.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cParameter.cs new file mode 100644 index 00000000..cfe1f38a --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cParameter.cs @@ -0,0 +1,218 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_cParameter +{ + /* Set compression parameters according to pre-defined cLevel table. + * Note that exact compression parameters are dynamically determined, + * depending on both compression level and srcSize (when known). + * Default level is ZSTD_CLEVEL_DEFAULT==3. + * Special: value 0 means default, which is controlled by ZSTD_CLEVEL_DEFAULT. + * Note 1 : it's possible to pass a negative compression level. + * Note 2 : setting a level does not automatically set all other compression parameters + * to default. Setting this will however eventually dynamically impact the compression + * parameters which have not been manually set. The manually set + * ones will 'stick'. */ + ZSTD_c_compressionLevel = 100, + + /* Maximum allowed back-reference distance, expressed as power of 2. + * This will set a memory budget for streaming decompression, + * with larger values requiring more memory + * and typically compressing more. + * Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX. + * Special: value 0 means "use default windowLog". + * Note: Using a windowLog greater than ZSTD_WINDOWLOG_LIMIT_DEFAULT + * requires explicitly allowing such size at streaming decompression stage. */ + ZSTD_c_windowLog = 101, + + /* Size of the initial probe table, as a power of 2. + * Resulting memory usage is (1 << (hashLog+2)). + * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX. + * Larger tables improve compression ratio of strategies <= dFast, + * and improve speed of strategies > dFast. + * Special: value 0 means "use default hashLog". */ + ZSTD_c_hashLog = 102, + + /* Size of the multi-probe search table, as a power of 2. + * Resulting memory usage is (1 << (chainLog+2)). + * Must be clamped between ZSTD_CHAINLOG_MIN and ZSTD_CHAINLOG_MAX. + * Larger tables result in better and slower compression. + * This parameter is useless for "fast" strategy. + * It's still useful when using "dfast" strategy, + * in which case it defines a secondary probe table. + * Special: value 0 means "use default chainLog". */ + ZSTD_c_chainLog = 103, + + /* Number of search attempts, as a power of 2. + * More attempts result in better and slower compression. + * This parameter is useless for "fast" and "dFast" strategies. + * Special: value 0 means "use default searchLog". */ + ZSTD_c_searchLog = 104, + + /* Minimum size of searched matches. + * Note that Zstandard can still find matches of smaller size, + * it just tweaks its search algorithm to look for this size and larger. + * Larger values increase compression and decompression speed, but decrease ratio. + * Must be clamped between ZSTD_MINMATCH_MIN and ZSTD_MINMATCH_MAX. + * Note that currently, for all strategies < btopt, effective minimum is 4. + * , for all strategies > fast, effective maximum is 6. + * Special: value 0 means "use default minMatchLength". */ + ZSTD_c_minMatch = 105, + + /* Impact of this field depends on strategy. + * For strategies btopt, btultra & btultra2: + * Length of Match considered "good enough" to stop search. + * Larger values make compression stronger, and slower. + * For strategy fast: + * Distance between match sampling. + * Larger values make compression faster, and weaker. + * Special: value 0 means "use default targetLength". */ + ZSTD_c_targetLength = 106, + + /* See ZSTD_strategy enum definition. + * The higher the value of selected strategy, the more complex it is, + * resulting in stronger and slower compression. + * Special: value 0 means "use default strategy". */ + ZSTD_c_strategy = 107, + + /* v1.5.6+ + * Attempts to fit compressed block size into approximately targetCBlockSize. + * Bound by ZSTD_TARGETCBLOCKSIZE_MIN and ZSTD_TARGETCBLOCKSIZE_MAX. + * Note that it's not a guarantee, just a convergence target (default:0). + * No target when targetCBlockSize == 0. + * This is helpful in low bandwidth streaming environments to improve end-to-end latency, + * when a client can make use of partial documents (a prominent example being Chrome). + * Note: this parameter is stable since v1.5.6. + * It was present as an experimental parameter in earlier versions, + * but it's not recommended using it with earlier library versions + * due to massive performance regressions. + */ + ZSTD_c_targetCBlockSize = 130, + + /* Enable long distance matching. + * This parameter is designed to improve compression ratio + * for large inputs, by finding large matches at long distance. + * It increases memory usage and window size. + * Note: enabling this parameter increases default ZSTD_c_windowLog to 128 MB + * except when expressly set to a different value. + * Note: will be enabled by default if ZSTD_c_windowLog >= 128 MB and + * compression strategy >= ZSTD_btopt (== compression level 16+) */ + ZSTD_c_enableLongDistanceMatching = 160, + + /* Size of the table for long distance matching, as a power of 2. + * Larger values increase memory usage and compression ratio, + * but decrease compression speed. + * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX + * default: windowlog - 7. + * Special: value 0 means "automatically determine hashlog". */ + ZSTD_c_ldmHashLog = 161, + + /* Minimum match size for long distance matcher. + * Larger/too small values usually decrease compression ratio. + * Must be clamped between ZSTD_LDM_MINMATCH_MIN and ZSTD_LDM_MINMATCH_MAX. + * Special: value 0 means "use default value" (default: 64). */ + ZSTD_c_ldmMinMatch = 162, + + /* Log size of each bucket in the LDM hash table for collision resolution. + * Larger values improve collision resolution but decrease compression speed. + * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX. + * Special: value 0 means "use default value" (default: 3). */ + ZSTD_c_ldmBucketSizeLog = 163, + + /* Frequency of inserting/looking up entries into the LDM hash table. + * Must be clamped between 0 and (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN). + * Default is MAX(0, (windowLog - ldmHashLog)), optimizing hash table usage. + * Larger values improve compression speed. + * Deviating far from default value will likely result in a compression ratio decrease. + * Special: value 0 means "automatically determine hashRateLog". */ + ZSTD_c_ldmHashRateLog = 164, + + /* Content size will be written into frame header _whenever known_ (default:1) + * Content size must be known at the beginning of compression. + * This is automatically the case when using ZSTD_compress2(), + * For streaming scenarios, content size must be provided with ZSTD_CCtx_setPledgedSrcSize() */ + ZSTD_c_contentSizeFlag = 200, + + /* A 32-bits checksum of content is written at end of frame (default:0) */ + ZSTD_c_checksumFlag = 201, + + /* When applicable, dictionary's ID is written into frame header (default:1) */ + ZSTD_c_dictIDFlag = 202, + + /* Select how many threads will be spawned to compress in parallel. + * When nbWorkers >= 1, triggers asynchronous mode when invoking ZSTD_compressStream*() : + * ZSTD_compressStream*() consumes input and flush output if possible, but immediately gives back control to caller, + * while compression is performed in parallel, within worker thread(s). + * (note : a strong exception to this rule is when first invocation of ZSTD_compressStream2() sets ZSTD_e_end : + * in which case, ZSTD_compressStream2() delegates to ZSTD_compress2(), which is always a blocking call). + * More workers improve speed, but also increase memory usage. + * Default value is `0`, aka "single-threaded mode" : no worker is spawned, + * compression is performed inside Caller's thread, and all invocations are blocking */ + ZSTD_c_nbWorkers = 400, + + /* Size of a compression job. This value is enforced only when nbWorkers >= 1. + * Each compression job is completed in parallel, so this value can indirectly impact the nb of active threads. + * 0 means default, which is dynamically determined based on compression parameters. + * Job size must be a minimum of overlap size, or ZSTDMT_JOBSIZE_MIN (= 512 KB), whichever is largest. + * The minimum size is automatically and transparently enforced. */ + ZSTD_c_jobSize = 401, + + /* Control the overlap size, as a fraction of window size. + * The overlap size is an amount of data reloaded from previous job at the beginning of a new job. + * It helps preserve compression ratio, while each job is compressed in parallel. + * This value is enforced only when nbWorkers >= 1. + * Larger values increase compression ratio, but decrease speed. + * Possible values range from 0 to 9 : + * - 0 means "default" : value will be determined by the library, depending on strategy + * - 1 means "no overlap" + * - 9 means "full overlap", using a full window size. + * Each intermediate rank increases/decreases load size by a factor 2 : + * 9: full window; 8: w/2; 7: w/4; 6: w/8; 5:w/16; 4: w/32; 3:w/64; 2:w/128; 1:no overlap; 0:default + * default value varies between 6 and 9, depending on strategy */ + ZSTD_c_overlapLog = 402, + + /* note : additional experimental parameters are also available + * within the experimental section of the API. + * At the time of this writing, they include : + * ZSTD_c_rsyncable + * ZSTD_c_format + * ZSTD_c_forceMaxWindow + * ZSTD_c_forceAttachDict + * ZSTD_c_literalCompressionMode + * ZSTD_c_srcSizeHint + * ZSTD_c_enableDedicatedDictSearch + * ZSTD_c_stableInBuffer + * ZSTD_c_stableOutBuffer + * ZSTD_c_blockDelimiters + * ZSTD_c_validateSequences + * ZSTD_c_blockSplitterLevel + * ZSTD_c_splitAfterSequences + * ZSTD_c_useRowMatchFinder + * ZSTD_c_prefetchCDictTables + * ZSTD_c_enableSeqProducerFallback + * ZSTD_c_maxBlockSize + * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them. + * note : never ever use experimentalParam? names directly; + * also, the enums values themselves are unstable and can still change. + */ + ZSTD_c_experimentalParam1 = 500, + ZSTD_c_experimentalParam2 = 10, + ZSTD_c_experimentalParam3 = 1000, + ZSTD_c_experimentalParam4 = 1001, + ZSTD_c_experimentalParam5 = 1002, + + /* was ZSTD_c_experimentalParam6=1003; is now ZSTD_c_targetCBlockSize */ + ZSTD_c_experimentalParam7 = 1004, + ZSTD_c_experimentalParam8 = 1005, + ZSTD_c_experimentalParam9 = 1006, + ZSTD_c_experimentalParam10 = 1007, + ZSTD_c_experimentalParam11 = 1008, + ZSTD_c_experimentalParam12 = 1009, + ZSTD_c_experimentalParam13 = 1010, + ZSTD_c_experimentalParam14 = 1011, + ZSTD_c_experimentalParam15 = 1012, + ZSTD_c_experimentalParam16 = 1013, + ZSTD_c_experimentalParam17 = 1014, + ZSTD_c_experimentalParam18 = 1015, + ZSTD_c_experimentalParam19 = 1016, + ZSTD_c_experimentalParam20 = 1017, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cStreamStage.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cStreamStage.cs new file mode 100644 index 00000000..c929b7fa --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cStreamStage.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_cStreamStage +{ + zcss_init = 0, + zcss_load, + zcss_flush, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compResetPolicy_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compResetPolicy_e.cs new file mode 100644 index 00000000..d943004d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compResetPolicy_e.cs @@ -0,0 +1,14 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * Controls, for this matchState reset, whether the tables need to be cleared / + * prepared for the coming compression (ZSTDcrp_makeClean), or whether the + * tables can be left unclean (ZSTDcrp_leaveDirty), because we know that a + * subsequent operation will overwrite the table space anyways (e.g., copying + * the matchState contents in from a CDict). + */ +public enum ZSTD_compResetPolicy_e +{ + ZSTDcrp_makeClean, + ZSTDcrp_leaveDirty, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressedBlockState_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressedBlockState_t.cs new file mode 100644 index 00000000..af281c96 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressedBlockState_t.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_compressedBlockState_t +{ + public ZSTD_entropyCTables_t entropy; + public fixed uint rep[3]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressionParameters.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressionParameters.cs new file mode 100644 index 00000000..bacbdea5 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressionParameters.cs @@ -0,0 +1,44 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZSTD_compressionParameters +{ + /**< largest match distance : larger == more compression, more memory needed during decompression */ + public uint windowLog; + + /**< fully searched segment : larger == more compression, slower, more memory (useless for fast) */ + public uint chainLog; + + /**< dispatch table : larger == faster, more memory */ + public uint hashLog; + + /**< nb of searches : larger == more compression, slower */ + public uint searchLog; + + /**< match length searched : larger == faster decompression, sometimes less compression */ + public uint minMatch; + + /**< acceptable match size for optimal parser (only) : larger == more compression, slower */ + public uint targetLength; + + /**< see ZSTD_strategy definition above */ + public ZSTD_strategy strategy; + + public ZSTD_compressionParameters( + uint windowLog, + uint chainLog, + uint hashLog, + uint searchLog, + uint minMatch, + uint targetLength, + ZSTD_strategy strategy + ) + { + this.windowLog = windowLog; + this.chainLog = chainLog; + this.hashLog = hashLog; + this.searchLog = searchLog; + this.minMatch = minMatch; + this.targetLength = targetLength; + this.strategy = strategy; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressionStage_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressionStage_e.cs new file mode 100644 index 00000000..9ffa4833 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_compressionStage_e.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*-************************************* + * Context memory management + ***************************************/ +public enum ZSTD_compressionStage_e +{ + ZSTDcs_created = 0, + ZSTDcs_init, + ZSTDcs_ongoing, + ZSTDcs_ending, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_customMem.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_customMem.cs new file mode 100644 index 00000000..16776a74 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_customMem.cs @@ -0,0 +1,15 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_customMem +{ + public void* customAlloc; + public void* customFree; + public void* opaque; + + public ZSTD_customMem(void* customAlloc, void* customFree, void* opaque) + { + this.customAlloc = customAlloc; + this.customFree = customFree; + this.opaque = opaque; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp.cs new file mode 100644 index 00000000..847b0866 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp.cs @@ -0,0 +1,110 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * Zstd fits all its internal datastructures into a single continuous buffer, + * so that it only needs to perform a single OS allocation (or so that a buffer + * can be provided to it and it can perform no allocations at all). This buffer + * is called the workspace. + * + * Several optimizations complicate that process of allocating memory ranges + * from this workspace for each internal datastructure: + * + * - These different internal datastructures have different setup requirements: + * + * - The static objects need to be cleared once and can then be trivially + * reused for each compression. + * + * - Various buffers don't need to be initialized at all--they are always + * written into before they're read. + * + * - The matchstate tables have a unique requirement that they don't need + * their memory to be totally cleared, but they do need the memory to have + * some bound, i.e., a guarantee that all values in the memory they've been + * allocated is less than some maximum value (which is the starting value + * for the indices that they will then use for compression). When this + * guarantee is provided to them, they can use the memory without any setup + * work. When it can't, they have to clear the area. + * + * - These buffers also have different alignment requirements. + * + * - We would like to reuse the objects in the workspace for multiple + * compressions without having to perform any expensive reallocation or + * reinitialization work. + * + * - We would like to be able to efficiently reuse the workspace across + * multiple compressions **even when the compression parameters change** and + * we need to resize some of the objects (where possible). + * + * To attempt to manage this buffer, given these constraints, the ZSTD_cwksp + * abstraction was created. It works as follows: + * + * Workspace Layout: + * + * [ ... workspace ... ] + * [objects][tables ->] free space [<- buffers][<- aligned][<- init once] + * + * The various objects that live in the workspace are divided into the + * following categories, and are allocated separately: + * + * - Static objects: this is optionally the enclosing ZSTD_CCtx or ZSTD_CDict, + * so that literally everything fits in a single buffer. Note: if present, + * this must be the first object in the workspace, since ZSTD_customFree{CCtx, + * CDict}() rely on a pointer comparison to see whether one or two frees are + * required. + * + * - Fixed size objects: these are fixed-size, fixed-count objects that are + * nonetheless "dynamically" allocated in the workspace so that we can + * control how they're initialized separately from the broader ZSTD_CCtx. + * Examples: + * - Entropy Workspace + * - 2 x ZSTD_compressedBlockState_t + * - CDict dictionary contents + * + * - Tables: these are any of several different datastructures (hash tables, + * chain tables, binary trees) that all respect a common format: they are + * uint32_t arrays, all of whose values are between 0 and (nextSrc - base). + * Their sizes depend on the cparams. These tables are 64-byte aligned. + * + * - Init once: these buffers require to be initialized at least once before + * use. They should be used when we want to skip memory initialization + * while not triggering memory checkers (like Valgrind) when reading from + * from this memory without writing to it first. + * These buffers should be used carefully as they might contain data + * from previous compressions. + * Buffers are aligned to 64 bytes. + * + * - Aligned: these buffers don't require any initialization before they're + * used. The user of the buffer should make sure they write into a buffer + * location before reading from it. + * Buffers are aligned to 64 bytes. + * + * - Buffers: these buffers are used for various purposes that don't require + * any alignment or initialization before they're used. This means they can + * be moved around at no cost for a new compression. + * + * Allocating Memory: + * + * The various types of objects must be allocated in order, so they can be + * correctly packed into the workspace buffer. That order is: + * + * 1. Objects + * 2. Init once / Tables + * 3. Aligned / Tables + * 4. Buffers / Tables + * + * Attempts to reserve objects of different types out of order will fail. + */ +public unsafe struct ZSTD_cwksp +{ + public void* workspace; + public void* workspaceEnd; + public void* objectEnd; + public void* tableEnd; + public void* tableValidEnd; + public void* allocStart; + public void* initOnceStart; + public byte allocFailed; + public int workspaceOversizedDuration; + public ZSTD_cwksp_alloc_phase_e phase; + public ZSTD_cwksp_static_alloc_e isStatic; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp_alloc_phase_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp_alloc_phase_e.cs new file mode 100644 index 00000000..c9fd5aac --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp_alloc_phase_e.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*-************************************* + * Structures + ***************************************/ +public enum ZSTD_cwksp_alloc_phase_e +{ + ZSTD_cwksp_alloc_objects, + ZSTD_cwksp_alloc_aligned_init_once, + ZSTD_cwksp_alloc_aligned, + ZSTD_cwksp_alloc_buffers, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp_static_alloc_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp_static_alloc_e.cs new file mode 100644 index 00000000..8f290f3a --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_cwksp_static_alloc_e.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * Used to describe whether the workspace is statically allocated (and will not + * necessarily ever be freed), or if it's dynamically allocated and we can + * expect a well-formed caller to free this. + */ +public enum ZSTD_cwksp_static_alloc_e +{ + ZSTD_cwksp_dynamic_alloc, + ZSTD_cwksp_static_alloc, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dParameter.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dParameter.cs new file mode 100644 index 00000000..2dc4ecba --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dParameter.cs @@ -0,0 +1,38 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* The advanced API pushes parameters one by one into an existing DCtx context. + * Parameters are sticky, and remain valid for all following frames + * using the same DCtx context. + * It's possible to reset parameters to default values using ZSTD_DCtx_reset(). + * Note : This API is compatible with existing ZSTD_decompressDCtx() and ZSTD_decompressStream(). + * Therefore, no new decompression function is necessary. + */ +public enum ZSTD_dParameter +{ + /* Select a size limit (in power of 2) beyond which + * the streaming API will refuse to allocate memory buffer + * in order to protect the host from unreasonable memory requirements. + * This parameter is only useful in streaming mode, since no internal buffer is allocated in single-pass mode. + * By default, a decompression context accepts window sizes <= (1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT). + * Special: value 0 means "use default maximum windowLog". */ + ZSTD_d_windowLogMax = 100, + + /* note : additional experimental parameters are also available + * within the experimental section of the API. + * At the time of this writing, they include : + * ZSTD_d_format + * ZSTD_d_stableOutBuffer + * ZSTD_d_forceIgnoreChecksum + * ZSTD_d_refMultipleDDicts + * ZSTD_d_disableHuffmanAssembly + * ZSTD_d_maxBlockSize + * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them. + * note : never ever use experimentalParam? names directly + */ + ZSTD_d_experimentalParam1 = 1000, + ZSTD_d_experimentalParam2 = 1001, + ZSTD_d_experimentalParam3 = 1002, + ZSTD_d_experimentalParam4 = 1003, + ZSTD_d_experimentalParam5 = 1004, + ZSTD_d_experimentalParam6 = 1005, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dStage.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dStage.cs new file mode 100644 index 00000000..69b992a2 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dStage.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_dStage +{ + ZSTDds_getFrameHeaderSize, + ZSTDds_decodeFrameHeader, + ZSTDds_decodeBlockHeader, + ZSTDds_decompressBlock, + ZSTDds_decompressLastBlock, + ZSTDds_checkChecksum, + ZSTDds_decodeSkippableHeader, + ZSTDds_skipFrame, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dStreamStage.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dStreamStage.cs new file mode 100644 index 00000000..851e06f7 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dStreamStage.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_dStreamStage +{ + zdss_init = 0, + zdss_loadHeader, + zdss_read, + zdss_load, + zdss_flush, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictAttachPref_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictAttachPref_e.cs new file mode 100644 index 00000000..8f989a42 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictAttachPref_e.cs @@ -0,0 +1,16 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_dictAttachPref_e +{ + /* Use the default heuristic. */ + ZSTD_dictDefaultAttach = 0, + + /* Never copy the dictionary. */ + ZSTD_dictForceAttach = 1, + + /* Always copy the dictionary. */ + ZSTD_dictForceCopy = 2, + + /* Always reload the dictionary */ + ZSTD_dictForceLoad = 3, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictContentType_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictContentType_e.cs new file mode 100644 index 00000000..3b97b12d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictContentType_e.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_dictContentType_e +{ + /* dictionary is "full" when starting with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */ + ZSTD_dct_auto = 0, + + /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */ + ZSTD_dct_rawContent = 1, + + /* refuses to load a dictionary if it does not respect Zstandard's specification, starting with ZSTD_MAGIC_DICTIONARY */ + ZSTD_dct_fullDict = 2, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictLoadMethod_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictLoadMethod_e.cs new file mode 100644 index 00000000..af7ed50b --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictLoadMethod_e.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_dictLoadMethod_e +{ + /**< Copy dictionary content internally */ + ZSTD_dlm_byCopy = 0, + + /**< Reference dictionary content -- the dictionary buffer must outlive its users. */ + ZSTD_dlm_byRef = 1, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictMode_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictMode_e.cs new file mode 100644 index 00000000..f234551d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictMode_e.cs @@ -0,0 +1,9 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_dictMode_e +{ + ZSTD_noDict = 0, + ZSTD_extDict = 1, + ZSTD_dictMatchState = 2, + ZSTD_dedicatedDictSearch = 3, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictTableLoadMethod_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictTableLoadMethod_e.cs new file mode 100644 index 00000000..7cf4f3e5 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictTableLoadMethod_e.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_dictTableLoadMethod_e +{ + ZSTD_dtlm_fast, + ZSTD_dtlm_full, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictUses_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictUses_e.cs new file mode 100644 index 00000000..314507c5 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_dictUses_e.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_dictUses_e +{ + /* Use the dictionary indefinitely */ + ZSTD_use_indefinitely = -1, + + /* Do not use the dictionary (if one exists free it) */ + ZSTD_dont_use = 0, + + /* Use the dictionary once and set to ZSTD_dont_use */ + ZSTD_use_once = 1, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyCTablesMetadata_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyCTablesMetadata_t.cs new file mode 100644 index 00000000..2455590b --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyCTablesMetadata_t.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZSTD_entropyCTablesMetadata_t +{ + public ZSTD_hufCTablesMetadata_t hufMetadata; + public ZSTD_fseCTablesMetadata_t fseMetadata; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyCTables_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyCTables_t.cs new file mode 100644 index 00000000..f3d98358 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyCTables_t.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZSTD_entropyCTables_t +{ + public ZSTD_hufCTables_t huf; + public ZSTD_fseCTables_t fse; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyDTables_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyDTables_t.cs new file mode 100644 index 00000000..91dd9d7d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_entropyDTables_t.cs @@ -0,0 +1,1342 @@ +using System.Runtime.CompilerServices; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_entropyDTables_t +{ + /* Note : Space reserved for FSE Tables */ + public _LLTable_e__FixedBuffer LLTable; + + /* is also used as temporary workspace while building hufTable during DDict creation */ + public _OFTable_e__FixedBuffer OFTable; + + /* and therefore must be at least HUF_DECOMPRESS_WORKSPACE_SIZE large */ + public _MLTable_e__FixedBuffer MLTable; + + /* can accommodate HUF_decompress4X */ + public fixed uint hufTable[4097]; + public fixed uint rep[3]; + public fixed uint workspace[157]; + +#if NET8_0_OR_GREATER + [InlineArray(513)] + public unsafe struct _LLTable_e__FixedBuffer + { + public ZSTD_seqSymbol e0; + } + +#else + public unsafe struct _LLTable_e__FixedBuffer + { + public ZSTD_seqSymbol e0; + public ZSTD_seqSymbol e1; + public ZSTD_seqSymbol e2; + public ZSTD_seqSymbol e3; + public ZSTD_seqSymbol e4; + public ZSTD_seqSymbol e5; + public ZSTD_seqSymbol e6; + public ZSTD_seqSymbol e7; + public ZSTD_seqSymbol e8; + public ZSTD_seqSymbol e9; + public ZSTD_seqSymbol e10; + public ZSTD_seqSymbol e11; + public ZSTD_seqSymbol e12; + public ZSTD_seqSymbol e13; + public ZSTD_seqSymbol e14; + public ZSTD_seqSymbol e15; + public ZSTD_seqSymbol e16; + public ZSTD_seqSymbol e17; + public ZSTD_seqSymbol e18; + public ZSTD_seqSymbol e19; + public ZSTD_seqSymbol e20; + public ZSTD_seqSymbol e21; + public ZSTD_seqSymbol e22; + public ZSTD_seqSymbol e23; + public ZSTD_seqSymbol e24; + public ZSTD_seqSymbol e25; + public ZSTD_seqSymbol e26; + public ZSTD_seqSymbol e27; + public ZSTD_seqSymbol e28; + public ZSTD_seqSymbol e29; + public ZSTD_seqSymbol e30; + public ZSTD_seqSymbol e31; + public ZSTD_seqSymbol e32; + public ZSTD_seqSymbol e33; + public ZSTD_seqSymbol e34; + public ZSTD_seqSymbol e35; + public ZSTD_seqSymbol e36; + public ZSTD_seqSymbol e37; + public ZSTD_seqSymbol e38; + public ZSTD_seqSymbol e39; + public ZSTD_seqSymbol e40; + public ZSTD_seqSymbol e41; + public ZSTD_seqSymbol e42; + public ZSTD_seqSymbol e43; + public ZSTD_seqSymbol e44; + public ZSTD_seqSymbol e45; + public ZSTD_seqSymbol e46; + public ZSTD_seqSymbol e47; + public ZSTD_seqSymbol e48; + public ZSTD_seqSymbol e49; + public ZSTD_seqSymbol e50; + public ZSTD_seqSymbol e51; + public ZSTD_seqSymbol e52; + public ZSTD_seqSymbol e53; + public ZSTD_seqSymbol e54; + public ZSTD_seqSymbol e55; + public ZSTD_seqSymbol e56; + public ZSTD_seqSymbol e57; + public ZSTD_seqSymbol e58; + public ZSTD_seqSymbol e59; + public ZSTD_seqSymbol e60; + public ZSTD_seqSymbol e61; + public ZSTD_seqSymbol e62; + public ZSTD_seqSymbol e63; + public ZSTD_seqSymbol e64; + public ZSTD_seqSymbol e65; + public ZSTD_seqSymbol e66; + public ZSTD_seqSymbol e67; + public ZSTD_seqSymbol e68; + public ZSTD_seqSymbol e69; + public ZSTD_seqSymbol e70; + public ZSTD_seqSymbol e71; + public ZSTD_seqSymbol e72; + public ZSTD_seqSymbol e73; + public ZSTD_seqSymbol e74; + public ZSTD_seqSymbol e75; + public ZSTD_seqSymbol e76; + public ZSTD_seqSymbol e77; + public ZSTD_seqSymbol e78; + public ZSTD_seqSymbol e79; + public ZSTD_seqSymbol e80; + public ZSTD_seqSymbol e81; + public ZSTD_seqSymbol e82; + public ZSTD_seqSymbol e83; + public ZSTD_seqSymbol e84; + public ZSTD_seqSymbol e85; + public ZSTD_seqSymbol e86; + public ZSTD_seqSymbol e87; + public ZSTD_seqSymbol e88; + public ZSTD_seqSymbol e89; + public ZSTD_seqSymbol e90; + public ZSTD_seqSymbol e91; + public ZSTD_seqSymbol e92; + public ZSTD_seqSymbol e93; + public ZSTD_seqSymbol e94; + public ZSTD_seqSymbol e95; + public ZSTD_seqSymbol e96; + public ZSTD_seqSymbol e97; + public ZSTD_seqSymbol e98; + public ZSTD_seqSymbol e99; + public ZSTD_seqSymbol e100; + public ZSTD_seqSymbol e101; + public ZSTD_seqSymbol e102; + public ZSTD_seqSymbol e103; + public ZSTD_seqSymbol e104; + public ZSTD_seqSymbol e105; + public ZSTD_seqSymbol e106; + public ZSTD_seqSymbol e107; + public ZSTD_seqSymbol e108; + public ZSTD_seqSymbol e109; + public ZSTD_seqSymbol e110; + public ZSTD_seqSymbol e111; + public ZSTD_seqSymbol e112; + public ZSTD_seqSymbol e113; + public ZSTD_seqSymbol e114; + public ZSTD_seqSymbol e115; + public ZSTD_seqSymbol e116; + public ZSTD_seqSymbol e117; + public ZSTD_seqSymbol e118; + public ZSTD_seqSymbol e119; + public ZSTD_seqSymbol e120; + public ZSTD_seqSymbol e121; + public ZSTD_seqSymbol e122; + public ZSTD_seqSymbol e123; + public ZSTD_seqSymbol e124; + public ZSTD_seqSymbol e125; + public ZSTD_seqSymbol e126; + public ZSTD_seqSymbol e127; + public ZSTD_seqSymbol e128; + public ZSTD_seqSymbol e129; + public ZSTD_seqSymbol e130; + public ZSTD_seqSymbol e131; + public ZSTD_seqSymbol e132; + public ZSTD_seqSymbol e133; + public ZSTD_seqSymbol e134; + public ZSTD_seqSymbol e135; + public ZSTD_seqSymbol e136; + public ZSTD_seqSymbol e137; + public ZSTD_seqSymbol e138; + public ZSTD_seqSymbol e139; + public ZSTD_seqSymbol e140; + public ZSTD_seqSymbol e141; + public ZSTD_seqSymbol e142; + public ZSTD_seqSymbol e143; + public ZSTD_seqSymbol e144; + public ZSTD_seqSymbol e145; + public ZSTD_seqSymbol e146; + public ZSTD_seqSymbol e147; + public ZSTD_seqSymbol e148; + public ZSTD_seqSymbol e149; + public ZSTD_seqSymbol e150; + public ZSTD_seqSymbol e151; + public ZSTD_seqSymbol e152; + public ZSTD_seqSymbol e153; + public ZSTD_seqSymbol e154; + public ZSTD_seqSymbol e155; + public ZSTD_seqSymbol e156; + public ZSTD_seqSymbol e157; + public ZSTD_seqSymbol e158; + public ZSTD_seqSymbol e159; + public ZSTD_seqSymbol e160; + public ZSTD_seqSymbol e161; + public ZSTD_seqSymbol e162; + public ZSTD_seqSymbol e163; + public ZSTD_seqSymbol e164; + public ZSTD_seqSymbol e165; + public ZSTD_seqSymbol e166; + public ZSTD_seqSymbol e167; + public ZSTD_seqSymbol e168; + public ZSTD_seqSymbol e169; + public ZSTD_seqSymbol e170; + public ZSTD_seqSymbol e171; + public ZSTD_seqSymbol e172; + public ZSTD_seqSymbol e173; + public ZSTD_seqSymbol e174; + public ZSTD_seqSymbol e175; + public ZSTD_seqSymbol e176; + public ZSTD_seqSymbol e177; + public ZSTD_seqSymbol e178; + public ZSTD_seqSymbol e179; + public ZSTD_seqSymbol e180; + public ZSTD_seqSymbol e181; + public ZSTD_seqSymbol e182; + public ZSTD_seqSymbol e183; + public ZSTD_seqSymbol e184; + public ZSTD_seqSymbol e185; + public ZSTD_seqSymbol e186; + public ZSTD_seqSymbol e187; + public ZSTD_seqSymbol e188; + public ZSTD_seqSymbol e189; + public ZSTD_seqSymbol e190; + public ZSTD_seqSymbol e191; + public ZSTD_seqSymbol e192; + public ZSTD_seqSymbol e193; + public ZSTD_seqSymbol e194; + public ZSTD_seqSymbol e195; + public ZSTD_seqSymbol e196; + public ZSTD_seqSymbol e197; + public ZSTD_seqSymbol e198; + public ZSTD_seqSymbol e199; + public ZSTD_seqSymbol e200; + public ZSTD_seqSymbol e201; + public ZSTD_seqSymbol e202; + public ZSTD_seqSymbol e203; + public ZSTD_seqSymbol e204; + public ZSTD_seqSymbol e205; + public ZSTD_seqSymbol e206; + public ZSTD_seqSymbol e207; + public ZSTD_seqSymbol e208; + public ZSTD_seqSymbol e209; + public ZSTD_seqSymbol e210; + public ZSTD_seqSymbol e211; + public ZSTD_seqSymbol e212; + public ZSTD_seqSymbol e213; + public ZSTD_seqSymbol e214; + public ZSTD_seqSymbol e215; + public ZSTD_seqSymbol e216; + public ZSTD_seqSymbol e217; + public ZSTD_seqSymbol e218; + public ZSTD_seqSymbol e219; + public ZSTD_seqSymbol e220; + public ZSTD_seqSymbol e221; + public ZSTD_seqSymbol e222; + public ZSTD_seqSymbol e223; + public ZSTD_seqSymbol e224; + public ZSTD_seqSymbol e225; + public ZSTD_seqSymbol e226; + public ZSTD_seqSymbol e227; + public ZSTD_seqSymbol e228; + public ZSTD_seqSymbol e229; + public ZSTD_seqSymbol e230; + public ZSTD_seqSymbol e231; + public ZSTD_seqSymbol e232; + public ZSTD_seqSymbol e233; + public ZSTD_seqSymbol e234; + public ZSTD_seqSymbol e235; + public ZSTD_seqSymbol e236; + public ZSTD_seqSymbol e237; + public ZSTD_seqSymbol e238; + public ZSTD_seqSymbol e239; + public ZSTD_seqSymbol e240; + public ZSTD_seqSymbol e241; + public ZSTD_seqSymbol e242; + public ZSTD_seqSymbol e243; + public ZSTD_seqSymbol e244; + public ZSTD_seqSymbol e245; + public ZSTD_seqSymbol e246; + public ZSTD_seqSymbol e247; + public ZSTD_seqSymbol e248; + public ZSTD_seqSymbol e249; + public ZSTD_seqSymbol e250; + public ZSTD_seqSymbol e251; + public ZSTD_seqSymbol e252; + public ZSTD_seqSymbol e253; + public ZSTD_seqSymbol e254; + public ZSTD_seqSymbol e255; + public ZSTD_seqSymbol e256; + public ZSTD_seqSymbol e257; + public ZSTD_seqSymbol e258; + public ZSTD_seqSymbol e259; + public ZSTD_seqSymbol e260; + public ZSTD_seqSymbol e261; + public ZSTD_seqSymbol e262; + public ZSTD_seqSymbol e263; + public ZSTD_seqSymbol e264; + public ZSTD_seqSymbol e265; + public ZSTD_seqSymbol e266; + public ZSTD_seqSymbol e267; + public ZSTD_seqSymbol e268; + public ZSTD_seqSymbol e269; + public ZSTD_seqSymbol e270; + public ZSTD_seqSymbol e271; + public ZSTD_seqSymbol e272; + public ZSTD_seqSymbol e273; + public ZSTD_seqSymbol e274; + public ZSTD_seqSymbol e275; + public ZSTD_seqSymbol e276; + public ZSTD_seqSymbol e277; + public ZSTD_seqSymbol e278; + public ZSTD_seqSymbol e279; + public ZSTD_seqSymbol e280; + public ZSTD_seqSymbol e281; + public ZSTD_seqSymbol e282; + public ZSTD_seqSymbol e283; + public ZSTD_seqSymbol e284; + public ZSTD_seqSymbol e285; + public ZSTD_seqSymbol e286; + public ZSTD_seqSymbol e287; + public ZSTD_seqSymbol e288; + public ZSTD_seqSymbol e289; + public ZSTD_seqSymbol e290; + public ZSTD_seqSymbol e291; + public ZSTD_seqSymbol e292; + public ZSTD_seqSymbol e293; + public ZSTD_seqSymbol e294; + public ZSTD_seqSymbol e295; + public ZSTD_seqSymbol e296; + public ZSTD_seqSymbol e297; + public ZSTD_seqSymbol e298; + public ZSTD_seqSymbol e299; + public ZSTD_seqSymbol e300; + public ZSTD_seqSymbol e301; + public ZSTD_seqSymbol e302; + public ZSTD_seqSymbol e303; + public ZSTD_seqSymbol e304; + public ZSTD_seqSymbol e305; + public ZSTD_seqSymbol e306; + public ZSTD_seqSymbol e307; + public ZSTD_seqSymbol e308; + public ZSTD_seqSymbol e309; + public ZSTD_seqSymbol e310; + public ZSTD_seqSymbol e311; + public ZSTD_seqSymbol e312; + public ZSTD_seqSymbol e313; + public ZSTD_seqSymbol e314; + public ZSTD_seqSymbol e315; + public ZSTD_seqSymbol e316; + public ZSTD_seqSymbol e317; + public ZSTD_seqSymbol e318; + public ZSTD_seqSymbol e319; + public ZSTD_seqSymbol e320; + public ZSTD_seqSymbol e321; + public ZSTD_seqSymbol e322; + public ZSTD_seqSymbol e323; + public ZSTD_seqSymbol e324; + public ZSTD_seqSymbol e325; + public ZSTD_seqSymbol e326; + public ZSTD_seqSymbol e327; + public ZSTD_seqSymbol e328; + public ZSTD_seqSymbol e329; + public ZSTD_seqSymbol e330; + public ZSTD_seqSymbol e331; + public ZSTD_seqSymbol e332; + public ZSTD_seqSymbol e333; + public ZSTD_seqSymbol e334; + public ZSTD_seqSymbol e335; + public ZSTD_seqSymbol e336; + public ZSTD_seqSymbol e337; + public ZSTD_seqSymbol e338; + public ZSTD_seqSymbol e339; + public ZSTD_seqSymbol e340; + public ZSTD_seqSymbol e341; + public ZSTD_seqSymbol e342; + public ZSTD_seqSymbol e343; + public ZSTD_seqSymbol e344; + public ZSTD_seqSymbol e345; + public ZSTD_seqSymbol e346; + public ZSTD_seqSymbol e347; + public ZSTD_seqSymbol e348; + public ZSTD_seqSymbol e349; + public ZSTD_seqSymbol e350; + public ZSTD_seqSymbol e351; + public ZSTD_seqSymbol e352; + public ZSTD_seqSymbol e353; + public ZSTD_seqSymbol e354; + public ZSTD_seqSymbol e355; + public ZSTD_seqSymbol e356; + public ZSTD_seqSymbol e357; + public ZSTD_seqSymbol e358; + public ZSTD_seqSymbol e359; + public ZSTD_seqSymbol e360; + public ZSTD_seqSymbol e361; + public ZSTD_seqSymbol e362; + public ZSTD_seqSymbol e363; + public ZSTD_seqSymbol e364; + public ZSTD_seqSymbol e365; + public ZSTD_seqSymbol e366; + public ZSTD_seqSymbol e367; + public ZSTD_seqSymbol e368; + public ZSTD_seqSymbol e369; + public ZSTD_seqSymbol e370; + public ZSTD_seqSymbol e371; + public ZSTD_seqSymbol e372; + public ZSTD_seqSymbol e373; + public ZSTD_seqSymbol e374; + public ZSTD_seqSymbol e375; + public ZSTD_seqSymbol e376; + public ZSTD_seqSymbol e377; + public ZSTD_seqSymbol e378; + public ZSTD_seqSymbol e379; + public ZSTD_seqSymbol e380; + public ZSTD_seqSymbol e381; + public ZSTD_seqSymbol e382; + public ZSTD_seqSymbol e383; + public ZSTD_seqSymbol e384; + public ZSTD_seqSymbol e385; + public ZSTD_seqSymbol e386; + public ZSTD_seqSymbol e387; + public ZSTD_seqSymbol e388; + public ZSTD_seqSymbol e389; + public ZSTD_seqSymbol e390; + public ZSTD_seqSymbol e391; + public ZSTD_seqSymbol e392; + public ZSTD_seqSymbol e393; + public ZSTD_seqSymbol e394; + public ZSTD_seqSymbol e395; + public ZSTD_seqSymbol e396; + public ZSTD_seqSymbol e397; + public ZSTD_seqSymbol e398; + public ZSTD_seqSymbol e399; + public ZSTD_seqSymbol e400; + public ZSTD_seqSymbol e401; + public ZSTD_seqSymbol e402; + public ZSTD_seqSymbol e403; + public ZSTD_seqSymbol e404; + public ZSTD_seqSymbol e405; + public ZSTD_seqSymbol e406; + public ZSTD_seqSymbol e407; + public ZSTD_seqSymbol e408; + public ZSTD_seqSymbol e409; + public ZSTD_seqSymbol e410; + public ZSTD_seqSymbol e411; + public ZSTD_seqSymbol e412; + public ZSTD_seqSymbol e413; + public ZSTD_seqSymbol e414; + public ZSTD_seqSymbol e415; + public ZSTD_seqSymbol e416; + public ZSTD_seqSymbol e417; + public ZSTD_seqSymbol e418; + public ZSTD_seqSymbol e419; + public ZSTD_seqSymbol e420; + public ZSTD_seqSymbol e421; + public ZSTD_seqSymbol e422; + public ZSTD_seqSymbol e423; + public ZSTD_seqSymbol e424; + public ZSTD_seqSymbol e425; + public ZSTD_seqSymbol e426; + public ZSTD_seqSymbol e427; + public ZSTD_seqSymbol e428; + public ZSTD_seqSymbol e429; + public ZSTD_seqSymbol e430; + public ZSTD_seqSymbol e431; + public ZSTD_seqSymbol e432; + public ZSTD_seqSymbol e433; + public ZSTD_seqSymbol e434; + public ZSTD_seqSymbol e435; + public ZSTD_seqSymbol e436; + public ZSTD_seqSymbol e437; + public ZSTD_seqSymbol e438; + public ZSTD_seqSymbol e439; + public ZSTD_seqSymbol e440; + public ZSTD_seqSymbol e441; + public ZSTD_seqSymbol e442; + public ZSTD_seqSymbol e443; + public ZSTD_seqSymbol e444; + public ZSTD_seqSymbol e445; + public ZSTD_seqSymbol e446; + public ZSTD_seqSymbol e447; + public ZSTD_seqSymbol e448; + public ZSTD_seqSymbol e449; + public ZSTD_seqSymbol e450; + public ZSTD_seqSymbol e451; + public ZSTD_seqSymbol e452; + public ZSTD_seqSymbol e453; + public ZSTD_seqSymbol e454; + public ZSTD_seqSymbol e455; + public ZSTD_seqSymbol e456; + public ZSTD_seqSymbol e457; + public ZSTD_seqSymbol e458; + public ZSTD_seqSymbol e459; + public ZSTD_seqSymbol e460; + public ZSTD_seqSymbol e461; + public ZSTD_seqSymbol e462; + public ZSTD_seqSymbol e463; + public ZSTD_seqSymbol e464; + public ZSTD_seqSymbol e465; + public ZSTD_seqSymbol e466; + public ZSTD_seqSymbol e467; + public ZSTD_seqSymbol e468; + public ZSTD_seqSymbol e469; + public ZSTD_seqSymbol e470; + public ZSTD_seqSymbol e471; + public ZSTD_seqSymbol e472; + public ZSTD_seqSymbol e473; + public ZSTD_seqSymbol e474; + public ZSTD_seqSymbol e475; + public ZSTD_seqSymbol e476; + public ZSTD_seqSymbol e477; + public ZSTD_seqSymbol e478; + public ZSTD_seqSymbol e479; + public ZSTD_seqSymbol e480; + public ZSTD_seqSymbol e481; + public ZSTD_seqSymbol e482; + public ZSTD_seqSymbol e483; + public ZSTD_seqSymbol e484; + public ZSTD_seqSymbol e485; + public ZSTD_seqSymbol e486; + public ZSTD_seqSymbol e487; + public ZSTD_seqSymbol e488; + public ZSTD_seqSymbol e489; + public ZSTD_seqSymbol e490; + public ZSTD_seqSymbol e491; + public ZSTD_seqSymbol e492; + public ZSTD_seqSymbol e493; + public ZSTD_seqSymbol e494; + public ZSTD_seqSymbol e495; + public ZSTD_seqSymbol e496; + public ZSTD_seqSymbol e497; + public ZSTD_seqSymbol e498; + public ZSTD_seqSymbol e499; + public ZSTD_seqSymbol e500; + public ZSTD_seqSymbol e501; + public ZSTD_seqSymbol e502; + public ZSTD_seqSymbol e503; + public ZSTD_seqSymbol e504; + public ZSTD_seqSymbol e505; + public ZSTD_seqSymbol e506; + public ZSTD_seqSymbol e507; + public ZSTD_seqSymbol e508; + public ZSTD_seqSymbol e509; + public ZSTD_seqSymbol e510; + public ZSTD_seqSymbol e511; + public ZSTD_seqSymbol e512; + } +#endif + +#if NET8_0_OR_GREATER + [InlineArray(257)] + public unsafe struct _OFTable_e__FixedBuffer + { + public ZSTD_seqSymbol e0; + } + +#else + public unsafe struct _OFTable_e__FixedBuffer + { + public ZSTD_seqSymbol e0; + public ZSTD_seqSymbol e1; + public ZSTD_seqSymbol e2; + public ZSTD_seqSymbol e3; + public ZSTD_seqSymbol e4; + public ZSTD_seqSymbol e5; + public ZSTD_seqSymbol e6; + public ZSTD_seqSymbol e7; + public ZSTD_seqSymbol e8; + public ZSTD_seqSymbol e9; + public ZSTD_seqSymbol e10; + public ZSTD_seqSymbol e11; + public ZSTD_seqSymbol e12; + public ZSTD_seqSymbol e13; + public ZSTD_seqSymbol e14; + public ZSTD_seqSymbol e15; + public ZSTD_seqSymbol e16; + public ZSTD_seqSymbol e17; + public ZSTD_seqSymbol e18; + public ZSTD_seqSymbol e19; + public ZSTD_seqSymbol e20; + public ZSTD_seqSymbol e21; + public ZSTD_seqSymbol e22; + public ZSTD_seqSymbol e23; + public ZSTD_seqSymbol e24; + public ZSTD_seqSymbol e25; + public ZSTD_seqSymbol e26; + public ZSTD_seqSymbol e27; + public ZSTD_seqSymbol e28; + public ZSTD_seqSymbol e29; + public ZSTD_seqSymbol e30; + public ZSTD_seqSymbol e31; + public ZSTD_seqSymbol e32; + public ZSTD_seqSymbol e33; + public ZSTD_seqSymbol e34; + public ZSTD_seqSymbol e35; + public ZSTD_seqSymbol e36; + public ZSTD_seqSymbol e37; + public ZSTD_seqSymbol e38; + public ZSTD_seqSymbol e39; + public ZSTD_seqSymbol e40; + public ZSTD_seqSymbol e41; + public ZSTD_seqSymbol e42; + public ZSTD_seqSymbol e43; + public ZSTD_seqSymbol e44; + public ZSTD_seqSymbol e45; + public ZSTD_seqSymbol e46; + public ZSTD_seqSymbol e47; + public ZSTD_seqSymbol e48; + public ZSTD_seqSymbol e49; + public ZSTD_seqSymbol e50; + public ZSTD_seqSymbol e51; + public ZSTD_seqSymbol e52; + public ZSTD_seqSymbol e53; + public ZSTD_seqSymbol e54; + public ZSTD_seqSymbol e55; + public ZSTD_seqSymbol e56; + public ZSTD_seqSymbol e57; + public ZSTD_seqSymbol e58; + public ZSTD_seqSymbol e59; + public ZSTD_seqSymbol e60; + public ZSTD_seqSymbol e61; + public ZSTD_seqSymbol e62; + public ZSTD_seqSymbol e63; + public ZSTD_seqSymbol e64; + public ZSTD_seqSymbol e65; + public ZSTD_seqSymbol e66; + public ZSTD_seqSymbol e67; + public ZSTD_seqSymbol e68; + public ZSTD_seqSymbol e69; + public ZSTD_seqSymbol e70; + public ZSTD_seqSymbol e71; + public ZSTD_seqSymbol e72; + public ZSTD_seqSymbol e73; + public ZSTD_seqSymbol e74; + public ZSTD_seqSymbol e75; + public ZSTD_seqSymbol e76; + public ZSTD_seqSymbol e77; + public ZSTD_seqSymbol e78; + public ZSTD_seqSymbol e79; + public ZSTD_seqSymbol e80; + public ZSTD_seqSymbol e81; + public ZSTD_seqSymbol e82; + public ZSTD_seqSymbol e83; + public ZSTD_seqSymbol e84; + public ZSTD_seqSymbol e85; + public ZSTD_seqSymbol e86; + public ZSTD_seqSymbol e87; + public ZSTD_seqSymbol e88; + public ZSTD_seqSymbol e89; + public ZSTD_seqSymbol e90; + public ZSTD_seqSymbol e91; + public ZSTD_seqSymbol e92; + public ZSTD_seqSymbol e93; + public ZSTD_seqSymbol e94; + public ZSTD_seqSymbol e95; + public ZSTD_seqSymbol e96; + public ZSTD_seqSymbol e97; + public ZSTD_seqSymbol e98; + public ZSTD_seqSymbol e99; + public ZSTD_seqSymbol e100; + public ZSTD_seqSymbol e101; + public ZSTD_seqSymbol e102; + public ZSTD_seqSymbol e103; + public ZSTD_seqSymbol e104; + public ZSTD_seqSymbol e105; + public ZSTD_seqSymbol e106; + public ZSTD_seqSymbol e107; + public ZSTD_seqSymbol e108; + public ZSTD_seqSymbol e109; + public ZSTD_seqSymbol e110; + public ZSTD_seqSymbol e111; + public ZSTD_seqSymbol e112; + public ZSTD_seqSymbol e113; + public ZSTD_seqSymbol e114; + public ZSTD_seqSymbol e115; + public ZSTD_seqSymbol e116; + public ZSTD_seqSymbol e117; + public ZSTD_seqSymbol e118; + public ZSTD_seqSymbol e119; + public ZSTD_seqSymbol e120; + public ZSTD_seqSymbol e121; + public ZSTD_seqSymbol e122; + public ZSTD_seqSymbol e123; + public ZSTD_seqSymbol e124; + public ZSTD_seqSymbol e125; + public ZSTD_seqSymbol e126; + public ZSTD_seqSymbol e127; + public ZSTD_seqSymbol e128; + public ZSTD_seqSymbol e129; + public ZSTD_seqSymbol e130; + public ZSTD_seqSymbol e131; + public ZSTD_seqSymbol e132; + public ZSTD_seqSymbol e133; + public ZSTD_seqSymbol e134; + public ZSTD_seqSymbol e135; + public ZSTD_seqSymbol e136; + public ZSTD_seqSymbol e137; + public ZSTD_seqSymbol e138; + public ZSTD_seqSymbol e139; + public ZSTD_seqSymbol e140; + public ZSTD_seqSymbol e141; + public ZSTD_seqSymbol e142; + public ZSTD_seqSymbol e143; + public ZSTD_seqSymbol e144; + public ZSTD_seqSymbol e145; + public ZSTD_seqSymbol e146; + public ZSTD_seqSymbol e147; + public ZSTD_seqSymbol e148; + public ZSTD_seqSymbol e149; + public ZSTD_seqSymbol e150; + public ZSTD_seqSymbol e151; + public ZSTD_seqSymbol e152; + public ZSTD_seqSymbol e153; + public ZSTD_seqSymbol e154; + public ZSTD_seqSymbol e155; + public ZSTD_seqSymbol e156; + public ZSTD_seqSymbol e157; + public ZSTD_seqSymbol e158; + public ZSTD_seqSymbol e159; + public ZSTD_seqSymbol e160; + public ZSTD_seqSymbol e161; + public ZSTD_seqSymbol e162; + public ZSTD_seqSymbol e163; + public ZSTD_seqSymbol e164; + public ZSTD_seqSymbol e165; + public ZSTD_seqSymbol e166; + public ZSTD_seqSymbol e167; + public ZSTD_seqSymbol e168; + public ZSTD_seqSymbol e169; + public ZSTD_seqSymbol e170; + public ZSTD_seqSymbol e171; + public ZSTD_seqSymbol e172; + public ZSTD_seqSymbol e173; + public ZSTD_seqSymbol e174; + public ZSTD_seqSymbol e175; + public ZSTD_seqSymbol e176; + public ZSTD_seqSymbol e177; + public ZSTD_seqSymbol e178; + public ZSTD_seqSymbol e179; + public ZSTD_seqSymbol e180; + public ZSTD_seqSymbol e181; + public ZSTD_seqSymbol e182; + public ZSTD_seqSymbol e183; + public ZSTD_seqSymbol e184; + public ZSTD_seqSymbol e185; + public ZSTD_seqSymbol e186; + public ZSTD_seqSymbol e187; + public ZSTD_seqSymbol e188; + public ZSTD_seqSymbol e189; + public ZSTD_seqSymbol e190; + public ZSTD_seqSymbol e191; + public ZSTD_seqSymbol e192; + public ZSTD_seqSymbol e193; + public ZSTD_seqSymbol e194; + public ZSTD_seqSymbol e195; + public ZSTD_seqSymbol e196; + public ZSTD_seqSymbol e197; + public ZSTD_seqSymbol e198; + public ZSTD_seqSymbol e199; + public ZSTD_seqSymbol e200; + public ZSTD_seqSymbol e201; + public ZSTD_seqSymbol e202; + public ZSTD_seqSymbol e203; + public ZSTD_seqSymbol e204; + public ZSTD_seqSymbol e205; + public ZSTD_seqSymbol e206; + public ZSTD_seqSymbol e207; + public ZSTD_seqSymbol e208; + public ZSTD_seqSymbol e209; + public ZSTD_seqSymbol e210; + public ZSTD_seqSymbol e211; + public ZSTD_seqSymbol e212; + public ZSTD_seqSymbol e213; + public ZSTD_seqSymbol e214; + public ZSTD_seqSymbol e215; + public ZSTD_seqSymbol e216; + public ZSTD_seqSymbol e217; + public ZSTD_seqSymbol e218; + public ZSTD_seqSymbol e219; + public ZSTD_seqSymbol e220; + public ZSTD_seqSymbol e221; + public ZSTD_seqSymbol e222; + public ZSTD_seqSymbol e223; + public ZSTD_seqSymbol e224; + public ZSTD_seqSymbol e225; + public ZSTD_seqSymbol e226; + public ZSTD_seqSymbol e227; + public ZSTD_seqSymbol e228; + public ZSTD_seqSymbol e229; + public ZSTD_seqSymbol e230; + public ZSTD_seqSymbol e231; + public ZSTD_seqSymbol e232; + public ZSTD_seqSymbol e233; + public ZSTD_seqSymbol e234; + public ZSTD_seqSymbol e235; + public ZSTD_seqSymbol e236; + public ZSTD_seqSymbol e237; + public ZSTD_seqSymbol e238; + public ZSTD_seqSymbol e239; + public ZSTD_seqSymbol e240; + public ZSTD_seqSymbol e241; + public ZSTD_seqSymbol e242; + public ZSTD_seqSymbol e243; + public ZSTD_seqSymbol e244; + public ZSTD_seqSymbol e245; + public ZSTD_seqSymbol e246; + public ZSTD_seqSymbol e247; + public ZSTD_seqSymbol e248; + public ZSTD_seqSymbol e249; + public ZSTD_seqSymbol e250; + public ZSTD_seqSymbol e251; + public ZSTD_seqSymbol e252; + public ZSTD_seqSymbol e253; + public ZSTD_seqSymbol e254; + public ZSTD_seqSymbol e255; + public ZSTD_seqSymbol e256; + } +#endif + +#if NET8_0_OR_GREATER + [InlineArray(513)] + public unsafe struct _MLTable_e__FixedBuffer + { + public ZSTD_seqSymbol e0; + } + +#else + public unsafe struct _MLTable_e__FixedBuffer + { + public ZSTD_seqSymbol e0; + public ZSTD_seqSymbol e1; + public ZSTD_seqSymbol e2; + public ZSTD_seqSymbol e3; + public ZSTD_seqSymbol e4; + public ZSTD_seqSymbol e5; + public ZSTD_seqSymbol e6; + public ZSTD_seqSymbol e7; + public ZSTD_seqSymbol e8; + public ZSTD_seqSymbol e9; + public ZSTD_seqSymbol e10; + public ZSTD_seqSymbol e11; + public ZSTD_seqSymbol e12; + public ZSTD_seqSymbol e13; + public ZSTD_seqSymbol e14; + public ZSTD_seqSymbol e15; + public ZSTD_seqSymbol e16; + public ZSTD_seqSymbol e17; + public ZSTD_seqSymbol e18; + public ZSTD_seqSymbol e19; + public ZSTD_seqSymbol e20; + public ZSTD_seqSymbol e21; + public ZSTD_seqSymbol e22; + public ZSTD_seqSymbol e23; + public ZSTD_seqSymbol e24; + public ZSTD_seqSymbol e25; + public ZSTD_seqSymbol e26; + public ZSTD_seqSymbol e27; + public ZSTD_seqSymbol e28; + public ZSTD_seqSymbol e29; + public ZSTD_seqSymbol e30; + public ZSTD_seqSymbol e31; + public ZSTD_seqSymbol e32; + public ZSTD_seqSymbol e33; + public ZSTD_seqSymbol e34; + public ZSTD_seqSymbol e35; + public ZSTD_seqSymbol e36; + public ZSTD_seqSymbol e37; + public ZSTD_seqSymbol e38; + public ZSTD_seqSymbol e39; + public ZSTD_seqSymbol e40; + public ZSTD_seqSymbol e41; + public ZSTD_seqSymbol e42; + public ZSTD_seqSymbol e43; + public ZSTD_seqSymbol e44; + public ZSTD_seqSymbol e45; + public ZSTD_seqSymbol e46; + public ZSTD_seqSymbol e47; + public ZSTD_seqSymbol e48; + public ZSTD_seqSymbol e49; + public ZSTD_seqSymbol e50; + public ZSTD_seqSymbol e51; + public ZSTD_seqSymbol e52; + public ZSTD_seqSymbol e53; + public ZSTD_seqSymbol e54; + public ZSTD_seqSymbol e55; + public ZSTD_seqSymbol e56; + public ZSTD_seqSymbol e57; + public ZSTD_seqSymbol e58; + public ZSTD_seqSymbol e59; + public ZSTD_seqSymbol e60; + public ZSTD_seqSymbol e61; + public ZSTD_seqSymbol e62; + public ZSTD_seqSymbol e63; + public ZSTD_seqSymbol e64; + public ZSTD_seqSymbol e65; + public ZSTD_seqSymbol e66; + public ZSTD_seqSymbol e67; + public ZSTD_seqSymbol e68; + public ZSTD_seqSymbol e69; + public ZSTD_seqSymbol e70; + public ZSTD_seqSymbol e71; + public ZSTD_seqSymbol e72; + public ZSTD_seqSymbol e73; + public ZSTD_seqSymbol e74; + public ZSTD_seqSymbol e75; + public ZSTD_seqSymbol e76; + public ZSTD_seqSymbol e77; + public ZSTD_seqSymbol e78; + public ZSTD_seqSymbol e79; + public ZSTD_seqSymbol e80; + public ZSTD_seqSymbol e81; + public ZSTD_seqSymbol e82; + public ZSTD_seqSymbol e83; + public ZSTD_seqSymbol e84; + public ZSTD_seqSymbol e85; + public ZSTD_seqSymbol e86; + public ZSTD_seqSymbol e87; + public ZSTD_seqSymbol e88; + public ZSTD_seqSymbol e89; + public ZSTD_seqSymbol e90; + public ZSTD_seqSymbol e91; + public ZSTD_seqSymbol e92; + public ZSTD_seqSymbol e93; + public ZSTD_seqSymbol e94; + public ZSTD_seqSymbol e95; + public ZSTD_seqSymbol e96; + public ZSTD_seqSymbol e97; + public ZSTD_seqSymbol e98; + public ZSTD_seqSymbol e99; + public ZSTD_seqSymbol e100; + public ZSTD_seqSymbol e101; + public ZSTD_seqSymbol e102; + public ZSTD_seqSymbol e103; + public ZSTD_seqSymbol e104; + public ZSTD_seqSymbol e105; + public ZSTD_seqSymbol e106; + public ZSTD_seqSymbol e107; + public ZSTD_seqSymbol e108; + public ZSTD_seqSymbol e109; + public ZSTD_seqSymbol e110; + public ZSTD_seqSymbol e111; + public ZSTD_seqSymbol e112; + public ZSTD_seqSymbol e113; + public ZSTD_seqSymbol e114; + public ZSTD_seqSymbol e115; + public ZSTD_seqSymbol e116; + public ZSTD_seqSymbol e117; + public ZSTD_seqSymbol e118; + public ZSTD_seqSymbol e119; + public ZSTD_seqSymbol e120; + public ZSTD_seqSymbol e121; + public ZSTD_seqSymbol e122; + public ZSTD_seqSymbol e123; + public ZSTD_seqSymbol e124; + public ZSTD_seqSymbol e125; + public ZSTD_seqSymbol e126; + public ZSTD_seqSymbol e127; + public ZSTD_seqSymbol e128; + public ZSTD_seqSymbol e129; + public ZSTD_seqSymbol e130; + public ZSTD_seqSymbol e131; + public ZSTD_seqSymbol e132; + public ZSTD_seqSymbol e133; + public ZSTD_seqSymbol e134; + public ZSTD_seqSymbol e135; + public ZSTD_seqSymbol e136; + public ZSTD_seqSymbol e137; + public ZSTD_seqSymbol e138; + public ZSTD_seqSymbol e139; + public ZSTD_seqSymbol e140; + public ZSTD_seqSymbol e141; + public ZSTD_seqSymbol e142; + public ZSTD_seqSymbol e143; + public ZSTD_seqSymbol e144; + public ZSTD_seqSymbol e145; + public ZSTD_seqSymbol e146; + public ZSTD_seqSymbol e147; + public ZSTD_seqSymbol e148; + public ZSTD_seqSymbol e149; + public ZSTD_seqSymbol e150; + public ZSTD_seqSymbol e151; + public ZSTD_seqSymbol e152; + public ZSTD_seqSymbol e153; + public ZSTD_seqSymbol e154; + public ZSTD_seqSymbol e155; + public ZSTD_seqSymbol e156; + public ZSTD_seqSymbol e157; + public ZSTD_seqSymbol e158; + public ZSTD_seqSymbol e159; + public ZSTD_seqSymbol e160; + public ZSTD_seqSymbol e161; + public ZSTD_seqSymbol e162; + public ZSTD_seqSymbol e163; + public ZSTD_seqSymbol e164; + public ZSTD_seqSymbol e165; + public ZSTD_seqSymbol e166; + public ZSTD_seqSymbol e167; + public ZSTD_seqSymbol e168; + public ZSTD_seqSymbol e169; + public ZSTD_seqSymbol e170; + public ZSTD_seqSymbol e171; + public ZSTD_seqSymbol e172; + public ZSTD_seqSymbol e173; + public ZSTD_seqSymbol e174; + public ZSTD_seqSymbol e175; + public ZSTD_seqSymbol e176; + public ZSTD_seqSymbol e177; + public ZSTD_seqSymbol e178; + public ZSTD_seqSymbol e179; + public ZSTD_seqSymbol e180; + public ZSTD_seqSymbol e181; + public ZSTD_seqSymbol e182; + public ZSTD_seqSymbol e183; + public ZSTD_seqSymbol e184; + public ZSTD_seqSymbol e185; + public ZSTD_seqSymbol e186; + public ZSTD_seqSymbol e187; + public ZSTD_seqSymbol e188; + public ZSTD_seqSymbol e189; + public ZSTD_seqSymbol e190; + public ZSTD_seqSymbol e191; + public ZSTD_seqSymbol e192; + public ZSTD_seqSymbol e193; + public ZSTD_seqSymbol e194; + public ZSTD_seqSymbol e195; + public ZSTD_seqSymbol e196; + public ZSTD_seqSymbol e197; + public ZSTD_seqSymbol e198; + public ZSTD_seqSymbol e199; + public ZSTD_seqSymbol e200; + public ZSTD_seqSymbol e201; + public ZSTD_seqSymbol e202; + public ZSTD_seqSymbol e203; + public ZSTD_seqSymbol e204; + public ZSTD_seqSymbol e205; + public ZSTD_seqSymbol e206; + public ZSTD_seqSymbol e207; + public ZSTD_seqSymbol e208; + public ZSTD_seqSymbol e209; + public ZSTD_seqSymbol e210; + public ZSTD_seqSymbol e211; + public ZSTD_seqSymbol e212; + public ZSTD_seqSymbol e213; + public ZSTD_seqSymbol e214; + public ZSTD_seqSymbol e215; + public ZSTD_seqSymbol e216; + public ZSTD_seqSymbol e217; + public ZSTD_seqSymbol e218; + public ZSTD_seqSymbol e219; + public ZSTD_seqSymbol e220; + public ZSTD_seqSymbol e221; + public ZSTD_seqSymbol e222; + public ZSTD_seqSymbol e223; + public ZSTD_seqSymbol e224; + public ZSTD_seqSymbol e225; + public ZSTD_seqSymbol e226; + public ZSTD_seqSymbol e227; + public ZSTD_seqSymbol e228; + public ZSTD_seqSymbol e229; + public ZSTD_seqSymbol e230; + public ZSTD_seqSymbol e231; + public ZSTD_seqSymbol e232; + public ZSTD_seqSymbol e233; + public ZSTD_seqSymbol e234; + public ZSTD_seqSymbol e235; + public ZSTD_seqSymbol e236; + public ZSTD_seqSymbol e237; + public ZSTD_seqSymbol e238; + public ZSTD_seqSymbol e239; + public ZSTD_seqSymbol e240; + public ZSTD_seqSymbol e241; + public ZSTD_seqSymbol e242; + public ZSTD_seqSymbol e243; + public ZSTD_seqSymbol e244; + public ZSTD_seqSymbol e245; + public ZSTD_seqSymbol e246; + public ZSTD_seqSymbol e247; + public ZSTD_seqSymbol e248; + public ZSTD_seqSymbol e249; + public ZSTD_seqSymbol e250; + public ZSTD_seqSymbol e251; + public ZSTD_seqSymbol e252; + public ZSTD_seqSymbol e253; + public ZSTD_seqSymbol e254; + public ZSTD_seqSymbol e255; + public ZSTD_seqSymbol e256; + public ZSTD_seqSymbol e257; + public ZSTD_seqSymbol e258; + public ZSTD_seqSymbol e259; + public ZSTD_seqSymbol e260; + public ZSTD_seqSymbol e261; + public ZSTD_seqSymbol e262; + public ZSTD_seqSymbol e263; + public ZSTD_seqSymbol e264; + public ZSTD_seqSymbol e265; + public ZSTD_seqSymbol e266; + public ZSTD_seqSymbol e267; + public ZSTD_seqSymbol e268; + public ZSTD_seqSymbol e269; + public ZSTD_seqSymbol e270; + public ZSTD_seqSymbol e271; + public ZSTD_seqSymbol e272; + public ZSTD_seqSymbol e273; + public ZSTD_seqSymbol e274; + public ZSTD_seqSymbol e275; + public ZSTD_seqSymbol e276; + public ZSTD_seqSymbol e277; + public ZSTD_seqSymbol e278; + public ZSTD_seqSymbol e279; + public ZSTD_seqSymbol e280; + public ZSTD_seqSymbol e281; + public ZSTD_seqSymbol e282; + public ZSTD_seqSymbol e283; + public ZSTD_seqSymbol e284; + public ZSTD_seqSymbol e285; + public ZSTD_seqSymbol e286; + public ZSTD_seqSymbol e287; + public ZSTD_seqSymbol e288; + public ZSTD_seqSymbol e289; + public ZSTD_seqSymbol e290; + public ZSTD_seqSymbol e291; + public ZSTD_seqSymbol e292; + public ZSTD_seqSymbol e293; + public ZSTD_seqSymbol e294; + public ZSTD_seqSymbol e295; + public ZSTD_seqSymbol e296; + public ZSTD_seqSymbol e297; + public ZSTD_seqSymbol e298; + public ZSTD_seqSymbol e299; + public ZSTD_seqSymbol e300; + public ZSTD_seqSymbol e301; + public ZSTD_seqSymbol e302; + public ZSTD_seqSymbol e303; + public ZSTD_seqSymbol e304; + public ZSTD_seqSymbol e305; + public ZSTD_seqSymbol e306; + public ZSTD_seqSymbol e307; + public ZSTD_seqSymbol e308; + public ZSTD_seqSymbol e309; + public ZSTD_seqSymbol e310; + public ZSTD_seqSymbol e311; + public ZSTD_seqSymbol e312; + public ZSTD_seqSymbol e313; + public ZSTD_seqSymbol e314; + public ZSTD_seqSymbol e315; + public ZSTD_seqSymbol e316; + public ZSTD_seqSymbol e317; + public ZSTD_seqSymbol e318; + public ZSTD_seqSymbol e319; + public ZSTD_seqSymbol e320; + public ZSTD_seqSymbol e321; + public ZSTD_seqSymbol e322; + public ZSTD_seqSymbol e323; + public ZSTD_seqSymbol e324; + public ZSTD_seqSymbol e325; + public ZSTD_seqSymbol e326; + public ZSTD_seqSymbol e327; + public ZSTD_seqSymbol e328; + public ZSTD_seqSymbol e329; + public ZSTD_seqSymbol e330; + public ZSTD_seqSymbol e331; + public ZSTD_seqSymbol e332; + public ZSTD_seqSymbol e333; + public ZSTD_seqSymbol e334; + public ZSTD_seqSymbol e335; + public ZSTD_seqSymbol e336; + public ZSTD_seqSymbol e337; + public ZSTD_seqSymbol e338; + public ZSTD_seqSymbol e339; + public ZSTD_seqSymbol e340; + public ZSTD_seqSymbol e341; + public ZSTD_seqSymbol e342; + public ZSTD_seqSymbol e343; + public ZSTD_seqSymbol e344; + public ZSTD_seqSymbol e345; + public ZSTD_seqSymbol e346; + public ZSTD_seqSymbol e347; + public ZSTD_seqSymbol e348; + public ZSTD_seqSymbol e349; + public ZSTD_seqSymbol e350; + public ZSTD_seqSymbol e351; + public ZSTD_seqSymbol e352; + public ZSTD_seqSymbol e353; + public ZSTD_seqSymbol e354; + public ZSTD_seqSymbol e355; + public ZSTD_seqSymbol e356; + public ZSTD_seqSymbol e357; + public ZSTD_seqSymbol e358; + public ZSTD_seqSymbol e359; + public ZSTD_seqSymbol e360; + public ZSTD_seqSymbol e361; + public ZSTD_seqSymbol e362; + public ZSTD_seqSymbol e363; + public ZSTD_seqSymbol e364; + public ZSTD_seqSymbol e365; + public ZSTD_seqSymbol e366; + public ZSTD_seqSymbol e367; + public ZSTD_seqSymbol e368; + public ZSTD_seqSymbol e369; + public ZSTD_seqSymbol e370; + public ZSTD_seqSymbol e371; + public ZSTD_seqSymbol e372; + public ZSTD_seqSymbol e373; + public ZSTD_seqSymbol e374; + public ZSTD_seqSymbol e375; + public ZSTD_seqSymbol e376; + public ZSTD_seqSymbol e377; + public ZSTD_seqSymbol e378; + public ZSTD_seqSymbol e379; + public ZSTD_seqSymbol e380; + public ZSTD_seqSymbol e381; + public ZSTD_seqSymbol e382; + public ZSTD_seqSymbol e383; + public ZSTD_seqSymbol e384; + public ZSTD_seqSymbol e385; + public ZSTD_seqSymbol e386; + public ZSTD_seqSymbol e387; + public ZSTD_seqSymbol e388; + public ZSTD_seqSymbol e389; + public ZSTD_seqSymbol e390; + public ZSTD_seqSymbol e391; + public ZSTD_seqSymbol e392; + public ZSTD_seqSymbol e393; + public ZSTD_seqSymbol e394; + public ZSTD_seqSymbol e395; + public ZSTD_seqSymbol e396; + public ZSTD_seqSymbol e397; + public ZSTD_seqSymbol e398; + public ZSTD_seqSymbol e399; + public ZSTD_seqSymbol e400; + public ZSTD_seqSymbol e401; + public ZSTD_seqSymbol e402; + public ZSTD_seqSymbol e403; + public ZSTD_seqSymbol e404; + public ZSTD_seqSymbol e405; + public ZSTD_seqSymbol e406; + public ZSTD_seqSymbol e407; + public ZSTD_seqSymbol e408; + public ZSTD_seqSymbol e409; + public ZSTD_seqSymbol e410; + public ZSTD_seqSymbol e411; + public ZSTD_seqSymbol e412; + public ZSTD_seqSymbol e413; + public ZSTD_seqSymbol e414; + public ZSTD_seqSymbol e415; + public ZSTD_seqSymbol e416; + public ZSTD_seqSymbol e417; + public ZSTD_seqSymbol e418; + public ZSTD_seqSymbol e419; + public ZSTD_seqSymbol e420; + public ZSTD_seqSymbol e421; + public ZSTD_seqSymbol e422; + public ZSTD_seqSymbol e423; + public ZSTD_seqSymbol e424; + public ZSTD_seqSymbol e425; + public ZSTD_seqSymbol e426; + public ZSTD_seqSymbol e427; + public ZSTD_seqSymbol e428; + public ZSTD_seqSymbol e429; + public ZSTD_seqSymbol e430; + public ZSTD_seqSymbol e431; + public ZSTD_seqSymbol e432; + public ZSTD_seqSymbol e433; + public ZSTD_seqSymbol e434; + public ZSTD_seqSymbol e435; + public ZSTD_seqSymbol e436; + public ZSTD_seqSymbol e437; + public ZSTD_seqSymbol e438; + public ZSTD_seqSymbol e439; + public ZSTD_seqSymbol e440; + public ZSTD_seqSymbol e441; + public ZSTD_seqSymbol e442; + public ZSTD_seqSymbol e443; + public ZSTD_seqSymbol e444; + public ZSTD_seqSymbol e445; + public ZSTD_seqSymbol e446; + public ZSTD_seqSymbol e447; + public ZSTD_seqSymbol e448; + public ZSTD_seqSymbol e449; + public ZSTD_seqSymbol e450; + public ZSTD_seqSymbol e451; + public ZSTD_seqSymbol e452; + public ZSTD_seqSymbol e453; + public ZSTD_seqSymbol e454; + public ZSTD_seqSymbol e455; + public ZSTD_seqSymbol e456; + public ZSTD_seqSymbol e457; + public ZSTD_seqSymbol e458; + public ZSTD_seqSymbol e459; + public ZSTD_seqSymbol e460; + public ZSTD_seqSymbol e461; + public ZSTD_seqSymbol e462; + public ZSTD_seqSymbol e463; + public ZSTD_seqSymbol e464; + public ZSTD_seqSymbol e465; + public ZSTD_seqSymbol e466; + public ZSTD_seqSymbol e467; + public ZSTD_seqSymbol e468; + public ZSTD_seqSymbol e469; + public ZSTD_seqSymbol e470; + public ZSTD_seqSymbol e471; + public ZSTD_seqSymbol e472; + public ZSTD_seqSymbol e473; + public ZSTD_seqSymbol e474; + public ZSTD_seqSymbol e475; + public ZSTD_seqSymbol e476; + public ZSTD_seqSymbol e477; + public ZSTD_seqSymbol e478; + public ZSTD_seqSymbol e479; + public ZSTD_seqSymbol e480; + public ZSTD_seqSymbol e481; + public ZSTD_seqSymbol e482; + public ZSTD_seqSymbol e483; + public ZSTD_seqSymbol e484; + public ZSTD_seqSymbol e485; + public ZSTD_seqSymbol e486; + public ZSTD_seqSymbol e487; + public ZSTD_seqSymbol e488; + public ZSTD_seqSymbol e489; + public ZSTD_seqSymbol e490; + public ZSTD_seqSymbol e491; + public ZSTD_seqSymbol e492; + public ZSTD_seqSymbol e493; + public ZSTD_seqSymbol e494; + public ZSTD_seqSymbol e495; + public ZSTD_seqSymbol e496; + public ZSTD_seqSymbol e497; + public ZSTD_seqSymbol e498; + public ZSTD_seqSymbol e499; + public ZSTD_seqSymbol e500; + public ZSTD_seqSymbol e501; + public ZSTD_seqSymbol e502; + public ZSTD_seqSymbol e503; + public ZSTD_seqSymbol e504; + public ZSTD_seqSymbol e505; + public ZSTD_seqSymbol e506; + public ZSTD_seqSymbol e507; + public ZSTD_seqSymbol e508; + public ZSTD_seqSymbol e509; + public ZSTD_seqSymbol e510; + public ZSTD_seqSymbol e511; + public ZSTD_seqSymbol e512; + } +#endif +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_forceIgnoreChecksum_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_forceIgnoreChecksum_e.cs new file mode 100644 index 00000000..a8322621 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_forceIgnoreChecksum_e.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_forceIgnoreChecksum_e +{ + /* Note: this enum controls ZSTD_d_forceIgnoreChecksum */ + ZSTD_d_validateChecksum = 0, + ZSTD_d_ignoreChecksum = 1, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_format_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_format_e.cs new file mode 100644 index 00000000..28f4de18 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_format_e.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_format_e +{ + /* zstd frame format, specified in zstd_compression_format.md (default) */ + ZSTD_f_zstd1 = 0, + + /* Variant of zstd frame format, without initial 4-bytes magic number. + * Useful to save 4 bytes per generated frame. + * Decoder cannot recognise automatically this format, requiring this instruction. */ + ZSTD_f_zstd1_magicless = 1, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameHeader.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameHeader.cs new file mode 100644 index 00000000..8c80f759 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameHeader.cs @@ -0,0 +1,21 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZSTD_frameHeader +{ + /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */ + public ulong frameContentSize; + + /* can be very large, up to <= frameContentSize */ + public ulong windowSize; + public uint blockSizeMax; + + /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */ + public ZSTD_frameType_e frameType; + public uint headerSize; + + /* for ZSTD_skippableFrame, contains the skippable magic variant [0-15] */ + public uint dictID; + public uint checksumFlag; + public uint _reserved1; + public uint _reserved2; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameParameters.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameParameters.cs new file mode 100644 index 00000000..137ccb37 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameParameters.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZSTD_frameParameters +{ + /**< 1: content size will be in frame header (when known) */ + public int contentSizeFlag; + + /**< 1: generate a 32-bits checksum using XXH64 algorithm at end of frame, for error detection */ + public int checksumFlag; + + /**< 1: no dictID will be saved into frame header (dictID is only useful for dictionary compression) */ + public int noDictIDFlag; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameProgression.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameProgression.cs new file mode 100644 index 00000000..adf34b4d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameProgression.cs @@ -0,0 +1,22 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZSTD_frameProgression +{ + /* nb input bytes read and buffered */ + public ulong ingested; + + /* nb input bytes actually compressed */ + public ulong consumed; + + /* nb of compressed bytes generated and buffered */ + public ulong produced; + + /* nb of compressed bytes flushed : not provided; can be tracked from caller side */ + public ulong flushed; + + /* MT only : latest started job nb */ + public uint currentJobID; + + /* MT only : nb of workers actively compressing at probe time */ + public uint nbActiveWorkers; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameSizeInfo.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameSizeInfo.cs new file mode 100644 index 00000000..7f9b6b08 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameSizeInfo.cs @@ -0,0 +1,14 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * Contains the compressed frame size and an upper-bound for the decompressed frame size. + * Note: before using `compressedSize`, check for errors using ZSTD_isError(). + * similarly, before using `decompressedBound`, check for errors using: + * `decompressedBound != ZSTD_CONTENTSIZE_ERROR` + */ +public struct ZSTD_frameSizeInfo +{ + public nuint nbBlocks; + public nuint compressedSize; + public ulong decompressedBound; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameType_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameType_e.cs new file mode 100644 index 00000000..a399748f --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_frameType_e.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_frameType_e +{ + ZSTD_frame, + ZSTD_skippableFrame, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseCTablesMetadata_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseCTablesMetadata_t.cs new file mode 100644 index 00000000..c6aa0dc8 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseCTablesMetadata_t.cs @@ -0,0 +1,18 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** ZSTD_fseCTablesMetadata_t : + * Stores symbol compression modes for a super-block in {ll, ol, ml}Type, and + * fse tables in fseTablesBuffer. + * fseTablesSize refers to the size of fse tables in bytes. + * This metadata is populated in ZSTD_buildBlockEntropyStats_sequences() */ +public unsafe struct ZSTD_fseCTablesMetadata_t +{ + public SymbolEncodingType_e llType; + public SymbolEncodingType_e ofType; + public SymbolEncodingType_e mlType; + public fixed byte fseTablesBuffer[133]; + public nuint fseTablesSize; + + /* This is to account for bug in 1.3.4. More detail in ZSTD_entropyCompressSeqStore_internal() */ + public nuint lastCountSize; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseCTables_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseCTables_t.cs new file mode 100644 index 00000000..3a9d621b --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseCTables_t.cs @@ -0,0 +1,11 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_fseCTables_t +{ + public fixed uint offcodeCTable[193]; + public fixed uint matchlengthCTable[363]; + public fixed uint litlengthCTable[329]; + public FSE_repeat offcode_repeatMode; + public FSE_repeat matchlength_repeatMode; + public FSE_repeat litlength_repeatMode; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseState.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseState.cs new file mode 100644 index 00000000..2e7b2a2c --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_fseState.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_fseState +{ + public nuint state; + public ZSTD_seqSymbol* table; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_getAllMatchesFn.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_getAllMatchesFn.cs new file mode 100644 index 00000000..4f7ee54f --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_getAllMatchesFn.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +[UnmanagedFunctionPointer(CallingConvention.Cdecl)] +public unsafe delegate uint ZSTD_getAllMatchesFn( + ZSTD_match_t* param0, + ZSTD_MatchState_t* param1, + uint* param2, + byte* param3, + byte* param4, + uint* rep, + uint ll0, + uint lengthToBeat +); diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_hufCTablesMetadata_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_hufCTablesMetadata_t.cs new file mode 100644 index 00000000..481bd647 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_hufCTablesMetadata_t.cs @@ -0,0 +1,16 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*********************************************** + * Entropy buffer statistics structs and funcs * + ***********************************************/ +/** ZSTD_hufCTablesMetadata_t : + * Stores Literals Block Type for a super-block in hType, and + * huffman tree description in hufDesBuffer. + * hufDesSize refers to the size of huffman tree description in bytes. + * This metadata is populated in ZSTD_buildBlockEntropyStats_literals() */ +public unsafe struct ZSTD_hufCTablesMetadata_t +{ + public SymbolEncodingType_e hType; + public fixed byte hufDesBuffer[128]; + public nuint hufDesSize; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_hufCTables_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_hufCTables_t.cs new file mode 100644 index 00000000..bedd8c8b --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_hufCTables_t.cs @@ -0,0 +1,279 @@ +using System.Runtime.CompilerServices; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_hufCTables_t +{ + public _CTable_e__FixedBuffer CTable; + public HUF_repeat repeatMode; + +#if NET8_0_OR_GREATER + [InlineArray(257)] + public unsafe struct _CTable_e__FixedBuffer + { + public nuint e0; + } + +#else + public unsafe struct _CTable_e__FixedBuffer + { + public nuint e0; + public nuint e1; + public nuint e2; + public nuint e3; + public nuint e4; + public nuint e5; + public nuint e6; + public nuint e7; + public nuint e8; + public nuint e9; + public nuint e10; + public nuint e11; + public nuint e12; + public nuint e13; + public nuint e14; + public nuint e15; + public nuint e16; + public nuint e17; + public nuint e18; + public nuint e19; + public nuint e20; + public nuint e21; + public nuint e22; + public nuint e23; + public nuint e24; + public nuint e25; + public nuint e26; + public nuint e27; + public nuint e28; + public nuint e29; + public nuint e30; + public nuint e31; + public nuint e32; + public nuint e33; + public nuint e34; + public nuint e35; + public nuint e36; + public nuint e37; + public nuint e38; + public nuint e39; + public nuint e40; + public nuint e41; + public nuint e42; + public nuint e43; + public nuint e44; + public nuint e45; + public nuint e46; + public nuint e47; + public nuint e48; + public nuint e49; + public nuint e50; + public nuint e51; + public nuint e52; + public nuint e53; + public nuint e54; + public nuint e55; + public nuint e56; + public nuint e57; + public nuint e58; + public nuint e59; + public nuint e60; + public nuint e61; + public nuint e62; + public nuint e63; + public nuint e64; + public nuint e65; + public nuint e66; + public nuint e67; + public nuint e68; + public nuint e69; + public nuint e70; + public nuint e71; + public nuint e72; + public nuint e73; + public nuint e74; + public nuint e75; + public nuint e76; + public nuint e77; + public nuint e78; + public nuint e79; + public nuint e80; + public nuint e81; + public nuint e82; + public nuint e83; + public nuint e84; + public nuint e85; + public nuint e86; + public nuint e87; + public nuint e88; + public nuint e89; + public nuint e90; + public nuint e91; + public nuint e92; + public nuint e93; + public nuint e94; + public nuint e95; + public nuint e96; + public nuint e97; + public nuint e98; + public nuint e99; + public nuint e100; + public nuint e101; + public nuint e102; + public nuint e103; + public nuint e104; + public nuint e105; + public nuint e106; + public nuint e107; + public nuint e108; + public nuint e109; + public nuint e110; + public nuint e111; + public nuint e112; + public nuint e113; + public nuint e114; + public nuint e115; + public nuint e116; + public nuint e117; + public nuint e118; + public nuint e119; + public nuint e120; + public nuint e121; + public nuint e122; + public nuint e123; + public nuint e124; + public nuint e125; + public nuint e126; + public nuint e127; + public nuint e128; + public nuint e129; + public nuint e130; + public nuint e131; + public nuint e132; + public nuint e133; + public nuint e134; + public nuint e135; + public nuint e136; + public nuint e137; + public nuint e138; + public nuint e139; + public nuint e140; + public nuint e141; + public nuint e142; + public nuint e143; + public nuint e144; + public nuint e145; + public nuint e146; + public nuint e147; + public nuint e148; + public nuint e149; + public nuint e150; + public nuint e151; + public nuint e152; + public nuint e153; + public nuint e154; + public nuint e155; + public nuint e156; + public nuint e157; + public nuint e158; + public nuint e159; + public nuint e160; + public nuint e161; + public nuint e162; + public nuint e163; + public nuint e164; + public nuint e165; + public nuint e166; + public nuint e167; + public nuint e168; + public nuint e169; + public nuint e170; + public nuint e171; + public nuint e172; + public nuint e173; + public nuint e174; + public nuint e175; + public nuint e176; + public nuint e177; + public nuint e178; + public nuint e179; + public nuint e180; + public nuint e181; + public nuint e182; + public nuint e183; + public nuint e184; + public nuint e185; + public nuint e186; + public nuint e187; + public nuint e188; + public nuint e189; + public nuint e190; + public nuint e191; + public nuint e192; + public nuint e193; + public nuint e194; + public nuint e195; + public nuint e196; + public nuint e197; + public nuint e198; + public nuint e199; + public nuint e200; + public nuint e201; + public nuint e202; + public nuint e203; + public nuint e204; + public nuint e205; + public nuint e206; + public nuint e207; + public nuint e208; + public nuint e209; + public nuint e210; + public nuint e211; + public nuint e212; + public nuint e213; + public nuint e214; + public nuint e215; + public nuint e216; + public nuint e217; + public nuint e218; + public nuint e219; + public nuint e220; + public nuint e221; + public nuint e222; + public nuint e223; + public nuint e224; + public nuint e225; + public nuint e226; + public nuint e227; + public nuint e228; + public nuint e229; + public nuint e230; + public nuint e231; + public nuint e232; + public nuint e233; + public nuint e234; + public nuint e235; + public nuint e236; + public nuint e237; + public nuint e238; + public nuint e239; + public nuint e240; + public nuint e241; + public nuint e242; + public nuint e243; + public nuint e244; + public nuint e245; + public nuint e246; + public nuint e247; + public nuint e248; + public nuint e249; + public nuint e250; + public nuint e251; + public nuint e252; + public nuint e253; + public nuint e254; + public nuint e255; + public nuint e256; + } +#endif +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_inBuffer_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_inBuffer_s.cs new file mode 100644 index 00000000..58242a2c --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_inBuffer_s.cs @@ -0,0 +1,16 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/**************************** + * Streaming + ****************************/ +public unsafe struct ZSTD_inBuffer_s +{ + /**< start of input buffer */ + public void* src; + + /**< size of input buffer */ + public nuint size; + + /**< position where reading stopped. Will be updated. Necessarily 0 <= pos <= size */ + public nuint pos; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_indexResetPolicy_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_indexResetPolicy_e.cs new file mode 100644 index 00000000..b3638aa0 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_indexResetPolicy_e.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/** + * Controls, for this matchState reset, whether indexing can continue where it + * left off (ZSTDirp_continue), or whether it needs to be restarted from zero + * (ZSTDirp_reset). + */ +public enum ZSTD_indexResetPolicy_e +{ + ZSTDirp_continue, + ZSTDirp_reset, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_litLocation_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_litLocation_e.cs new file mode 100644 index 00000000..55d28562 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_litLocation_e.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_litLocation_e +{ + /* Stored entirely within litExtraBuffer */ + ZSTD_not_in_dst = 0, + + /* Stored entirely within dst (in memory after current output write) */ + ZSTD_in_dst = 1, + + /* Split between litExtraBuffer and dst */ + ZSTD_split = 2, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_literalCompressionMode_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_literalCompressionMode_e.cs new file mode 100644 index 00000000..2c309b06 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_literalCompressionMode_e.cs @@ -0,0 +1,16 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_literalCompressionMode_e +{ + /**< Automatically determine the compression mode based on the compression level. + * Negative compression levels will be uncompressed, and positive compression + * levels will be compressed. */ + ZSTD_lcm_auto = 0, + + /**< Always attempt Huffman compression. Uncompressed literals will still be + * emitted if Huffman compression is not profitable. */ + ZSTD_lcm_huffman = 1, + + /**< Always emit uncompressed literals. */ + ZSTD_lcm_uncompressed = 2, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_localDict.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_localDict.cs new file mode 100644 index 00000000..4b970451 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_localDict.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_localDict +{ + public void* dictBuffer; + public void* dict; + public nuint dictSize; + public ZSTD_dictContentType_e dictContentType; + public ZSTD_CDict_s* cdict; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_longLengthType_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_longLengthType_e.cs new file mode 100644 index 00000000..141643f2 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_longLengthType_e.cs @@ -0,0 +1,14 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* Controls whether seqStore has a single "long" litLength or matchLength. See SeqStore_t. */ +public enum ZSTD_longLengthType_e +{ + /* no longLengthType */ + ZSTD_llt_none = 0, + + /* represents a long literal */ + ZSTD_llt_literalLength = 1, + + /* represents a long match */ + ZSTD_llt_matchLength = 2, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_longOffset_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_longOffset_e.cs new file mode 100644 index 00000000..86881bdd --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_longOffset_e.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_longOffset_e +{ + ZSTD_lo_isRegularOffset, + ZSTD_lo_isLongOffset = 1, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_match_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_match_t.cs new file mode 100644 index 00000000..fa44b9ec --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_match_t.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/********************************* + * Compression internals structs * + *********************************/ +public struct ZSTD_match_t +{ + /* Offset sumtype code for the match, using ZSTD_storeSeq() format */ + public uint off; + + /* Raw length of match */ + public uint len; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_nextInputType_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_nextInputType_e.cs new file mode 100644 index 00000000..5ebea51e --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_nextInputType_e.cs @@ -0,0 +1,11 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_nextInputType_e +{ + ZSTDnit_frameHeader, + ZSTDnit_blockHeader, + ZSTDnit_block, + ZSTDnit_lastBlock, + ZSTDnit_checksum, + ZSTDnit_skippableFrame, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_optLdm_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_optLdm_t.cs new file mode 100644 index 00000000..8319db5b --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_optLdm_t.cs @@ -0,0 +1,17 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* Struct containing info needed to make decision about ldm inclusion */ +public struct ZSTD_optLdm_t +{ + /* External match candidates store for this block */ + public RawSeqStore_t seqStore; + + /* Start position of the current match candidate */ + public uint startPosInBlock; + + /* End position of the current match candidate */ + public uint endPosInBlock; + + /* Offset of the match candidate */ + public uint offset; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_optimal_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_optimal_t.cs new file mode 100644 index 00000000..84845187 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_optimal_t.cs @@ -0,0 +1,19 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_optimal_t +{ + /* price from beginning of segment to this position */ + public int price; + + /* offset of previous match */ + public uint off; + + /* length of previous match */ + public uint mlen; + + /* nb of literals since previous match */ + public uint litlen; + + /* offset history after previous match */ + public fixed uint rep[3]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_outBuffer_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_outBuffer_s.cs new file mode 100644 index 00000000..c2cc0089 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_outBuffer_s.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_outBuffer_s +{ + /**< start of output buffer */ + public void* dst; + + /**< size of output buffer */ + public nuint size; + + /**< position where writing stopped. Will be updated. Necessarily 0 <= pos <= size */ + public nuint pos; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_overlap_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_overlap_e.cs new file mode 100644 index 00000000..bf5b85e1 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_overlap_e.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_overlap_e +{ + ZSTD_no_overlap, + ZSTD_overlap_src_before_dst, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_paramSwitch_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_paramSwitch_e.cs new file mode 100644 index 00000000..3ff21492 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_paramSwitch_e.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_paramSwitch_e +{ + /* Let the library automatically determine whether the feature shall be enabled */ + ZSTD_ps_auto = 0, + + /* Force-enable the feature */ + ZSTD_ps_enable = 1, + + /* Do not use the feature */ + ZSTD_ps_disable = 2, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_parameters.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_parameters.cs new file mode 100644 index 00000000..ebdf1969 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_parameters.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZSTD_parameters +{ + public ZSTD_compressionParameters cParams; + public ZSTD_frameParameters fParams; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_prefixDict_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_prefixDict_s.cs new file mode 100644 index 00000000..2a0db472 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_prefixDict_s.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_prefixDict_s +{ + public void* dict; + public nuint dictSize; + public ZSTD_dictContentType_e dictContentType; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_refMultipleDDicts_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_refMultipleDDicts_e.cs new file mode 100644 index 00000000..c01fcefc --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_refMultipleDDicts_e.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_refMultipleDDicts_e +{ + /* Note: this enum controls ZSTD_d_refMultipleDDicts */ + ZSTD_rmd_refSingleDDict = 0, + ZSTD_rmd_refMultipleDDicts = 1, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_resetTarget_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_resetTarget_e.cs new file mode 100644 index 00000000..679e8b28 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_resetTarget_e.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_resetTarget_e +{ + ZSTD_resetTarget_CDict, + ZSTD_resetTarget_CCtx, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_seqSymbol.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_seqSymbol.cs new file mode 100644 index 00000000..622ff4fc --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_seqSymbol.cs @@ -0,0 +1,17 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ZSTD_seqSymbol +{ + public ushort nextState; + public byte nbAdditionalBits; + public byte nbBits; + public uint baseValue; + + public ZSTD_seqSymbol(ushort nextState, byte nbAdditionalBits, byte nbBits, uint baseValue) + { + this.nextState = nextState; + this.nbAdditionalBits = nbAdditionalBits; + this.nbBits = nbBits; + this.baseValue = baseValue; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_seqSymbol_header.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_seqSymbol_header.cs new file mode 100644 index 00000000..eb87911c --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_seqSymbol_header.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/*-******************************************************* + * Decompression types + *********************************************************/ +public struct ZSTD_seqSymbol_header +{ + public uint fastMode; + public uint tableLog; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_sequenceFormat_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_sequenceFormat_e.cs new file mode 100644 index 00000000..3b6ca525 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_sequenceFormat_e.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_sequenceFormat_e +{ + /* ZSTD_Sequence[] has no block delimiters, just sequences */ + ZSTD_sf_noBlockDelimiters = 0, + + /* ZSTD_Sequence[] contains explicit block delimiters */ + ZSTD_sf_explicitBlockDelimiters = 1, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_strategy.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_strategy.cs new file mode 100644 index 00000000..49d8e23e --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_strategy.cs @@ -0,0 +1,15 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* Compression strategies, listed from fastest to strongest */ +public enum ZSTD_strategy +{ + ZSTD_fast = 1, + ZSTD_dfast = 2, + ZSTD_greedy = 3, + ZSTD_lazy = 4, + ZSTD_lazy2 = 5, + ZSTD_btlazy2 = 6, + ZSTD_btopt = 7, + ZSTD_btultra = 8, + ZSTD_btultra2 = 9, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_symbolEncodingTypeStats_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_symbolEncodingTypeStats_t.cs new file mode 100644 index 00000000..673f2b3d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_symbolEncodingTypeStats_t.cs @@ -0,0 +1,16 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* Type returned by ZSTD_buildSequencesStatistics containing finalized symbol encoding types + * and size of the sequences statistics + */ +public struct ZSTD_symbolEncodingTypeStats_t +{ + public uint LLtype; + public uint Offtype; + public uint MLtype; + public nuint size; + + /* Accounts for bug in 1.3.4. More detail in ZSTD_entropyCompressSeqStore_internal() */ + public nuint lastCountSize; + public int longOffsets; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_tableFillPurpose_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_tableFillPurpose_e.cs new file mode 100644 index 00000000..f218de33 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_tableFillPurpose_e.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum ZSTD_tableFillPurpose_e +{ + ZSTD_tfp_forCCtx, + ZSTD_tfp_forCDict, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_window_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_window_t.cs new file mode 100644 index 00000000..84895dab --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZSTD_window_t.cs @@ -0,0 +1,25 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ZSTD_window_t +{ + /* next block here to continue on current prefix */ + public byte* nextSrc; + + /* All regular indexes relative to this position */ + public byte* @base; + + /* extDict indexes relative to this position */ + public byte* dictBase; + + /* below that point, need extDict */ + public uint dictLimit; + + /* below that point, no more valid data */ + public uint lowLimit; + + /* Number of times overflow correction has run since + * ZSTD_window_init(). Useful for debugging coredumps + * and for ZSTD_WINDOW_OVERFLOW_CORRECT_FREQUENTLY. + */ + public uint nbOverflowCorrections; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Zdict.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Zdict.cs new file mode 100644 index 00000000..a79aa008 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Zdict.cs @@ -0,0 +1,640 @@ +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /*-******************************************************** + * Helper functions + **********************************************************/ + public static bool ZDICT_isError(nuint errorCode) + { + return ERR_isError(errorCode); + } + + public static string ZDICT_getErrorName(nuint errorCode) + { + return ERR_getErrorName(errorCode); + } + + private static void ZDICT_countEStats( + EStats_ress_t esr, + ZSTD_parameters* @params, + uint* countLit, + uint* offsetcodeCount, + uint* matchlengthCount, + uint* litlengthCount, + uint* repOffsets, + void* src, + nuint srcSize, + uint notificationLevel + ) + { + nuint blockSizeMax = (nuint)( + 1 << 17 < 1 << (int)@params->cParams.windowLog + ? 1 << 17 + : 1 << (int)@params->cParams.windowLog + ); + nuint cSize; + if (srcSize > blockSizeMax) + srcSize = blockSizeMax; + { + nuint errorCode = ZSTD_compressBegin_usingCDict_deprecated(esr.zc, esr.dict); + if (ERR_isError(errorCode)) + { + return; + } + } + + cSize = ZSTD_compressBlock_deprecated(esr.zc, esr.workPlace, 1 << 17, src, srcSize); + if (ERR_isError(cSize)) + { + return; + } + + if (cSize != 0) + { + SeqStore_t* seqStorePtr = ZSTD_getSeqStore(esr.zc); + { + byte* bytePtr; + for (bytePtr = seqStorePtr->litStart; bytePtr < seqStorePtr->lit; bytePtr++) + countLit[*bytePtr]++; + } + + { + uint nbSeq = (uint)(seqStorePtr->sequences - seqStorePtr->sequencesStart); + ZSTD_seqToCodes(seqStorePtr); + { + byte* codePtr = seqStorePtr->ofCode; + uint u; + for (u = 0; u < nbSeq; u++) + offsetcodeCount[codePtr[u]]++; + } + + { + byte* codePtr = seqStorePtr->mlCode; + uint u; + for (u = 0; u < nbSeq; u++) + matchlengthCount[codePtr[u]]++; + } + + { + byte* codePtr = seqStorePtr->llCode; + uint u; + for (u = 0; u < nbSeq; u++) + litlengthCount[codePtr[u]]++; + } + + if (nbSeq >= 2) + { + SeqDef_s* seq = seqStorePtr->sequencesStart; + uint offset1 = seq[0].offBase - 3; + uint offset2 = seq[1].offBase - 3; + if (offset1 >= 1024) + offset1 = 0; + if (offset2 >= 1024) + offset2 = 0; + repOffsets[offset1] += 3; + repOffsets[offset2] += 1; + } + } + } + } + + private static nuint ZDICT_totalSampleSize(nuint* fileSizes, uint nbFiles) + { + nuint total = 0; + uint u; + for (u = 0; u < nbFiles; u++) + total += fileSizes[u]; + return total; + } + + private static void ZDICT_insertSortCount(offsetCount_t* table, uint val, uint count) + { + uint u; + table[3].offset = val; + table[3].count = count; + for (u = 3; u > 0; u--) + { + offsetCount_t tmp; + if (table[u - 1].count >= table[u].count) + break; + tmp = table[u - 1]; + table[u - 1] = table[u]; + table[u] = tmp; + } + } + + /* ZDICT_flatLit() : + * rewrite `countLit` to contain a mostly flat but still compressible distribution of literals. + * necessary to avoid generating a non-compressible distribution that HUF_writeCTable() cannot encode. + */ + private static void ZDICT_flatLit(uint* countLit) + { + int u; + for (u = 1; u < 256; u++) + countLit[u] = 2; + countLit[0] = 4; + countLit[253] = 1; + countLit[254] = 1; + } + + private static nuint ZDICT_analyzeEntropy( + void* dstBuffer, + nuint maxDstSize, + int compressionLevel, + void* srcBuffer, + nuint* fileSizes, + uint nbFiles, + void* dictBuffer, + nuint dictBufferSize, + uint notificationLevel + ) + { + uint* countLit = stackalloc uint[256]; + /* no final ; */ + nuint* hufTable = stackalloc nuint[257]; + uint* offcodeCount = stackalloc uint[31]; + short* offcodeNCount = stackalloc short[31]; + uint offcodeMax = ZSTD_highbit32((uint)(dictBufferSize + 128 * (1 << 10))); + uint* matchLengthCount = stackalloc uint[53]; + short* matchLengthNCount = stackalloc short[53]; + uint* litLengthCount = stackalloc uint[36]; + short* litLengthNCount = stackalloc short[36]; + uint* repOffset = stackalloc uint[1024]; + offsetCount_t* bestRepOffset = stackalloc offsetCount_t[4]; + EStats_ress_t esr = new EStats_ress_t + { + dict = null, + zc = null, + workPlace = null, + }; + ZSTD_parameters @params; + uint u, + huffLog = 11, + Offlog = 8, + mlLog = 9, + llLog = 9, + total; + nuint pos = 0, + errorCode; + nuint eSize = 0; + nuint totalSrcSize = ZDICT_totalSampleSize(fileSizes, nbFiles); + nuint averageSampleSize = totalSrcSize / (nbFiles + (uint)(nbFiles == 0 ? 1 : 0)); + byte* dstPtr = (byte*)dstBuffer; + uint* wksp = stackalloc uint[1216]; + if (offcodeMax > 30) + { + eSize = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionaryCreation_failed)); + goto _cleanup; + } + + for (u = 0; u < 256; u++) + countLit[u] = 1; + for (u = 0; u <= offcodeMax; u++) + offcodeCount[u] = 1; + for (u = 0; u <= 52; u++) + matchLengthCount[u] = 1; + for (u = 0; u <= 35; u++) + litLengthCount[u] = 1; + memset(repOffset, 0, sizeof(uint) * 1024); + repOffset[1] = repOffset[4] = repOffset[8] = 1; + memset(bestRepOffset, 0, (uint)(sizeof(offsetCount_t) * 4)); + if (compressionLevel == 0) + compressionLevel = 3; + @params = ZSTD_getParams(compressionLevel, averageSampleSize, dictBufferSize); + esr.dict = ZSTD_createCDict_advanced( + dictBuffer, + dictBufferSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef, + ZSTD_dictContentType_e.ZSTD_dct_rawContent, + @params.cParams, + ZSTD_defaultCMem + ); + esr.zc = ZSTD_createCCtx(); + esr.workPlace = malloc(1 << 17); + if (esr.dict == null || esr.zc == null || esr.workPlace == null) + { + eSize = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + goto _cleanup; + } + + for (u = 0; u < nbFiles; u++) + { + ZDICT_countEStats( + esr, + &@params, + countLit, + offcodeCount, + matchLengthCount, + litLengthCount, + repOffset, + (sbyte*)srcBuffer + pos, + fileSizes[u], + notificationLevel + ); + pos += fileSizes[u]; + } + + if (notificationLevel >= 4) + { + for (u = 0; u <= offcodeMax; u++) { } + } + + { + nuint maxNbBits = HUF_buildCTable_wksp( + hufTable, + countLit, + 255, + huffLog, + wksp, + sizeof(uint) * 1216 + ); + if (ERR_isError(maxNbBits)) + { + eSize = maxNbBits; + goto _cleanup; + } + + if (maxNbBits == 8) + { + ZDICT_flatLit(countLit); + maxNbBits = HUF_buildCTable_wksp( + hufTable, + countLit, + 255, + huffLog, + wksp, + sizeof(uint) * 1216 + ); + assert(maxNbBits == 9); + } + + huffLog = (uint)maxNbBits; + } + + { + uint offset; + for (offset = 1; offset < 1024; offset++) + ZDICT_insertSortCount(bestRepOffset, offset, repOffset[offset]); + } + + total = 0; + for (u = 0; u <= offcodeMax; u++) + total += offcodeCount[u]; + errorCode = FSE_normalizeCount(offcodeNCount, Offlog, offcodeCount, total, offcodeMax, 1); + if (ERR_isError(errorCode)) + { + eSize = errorCode; + goto _cleanup; + } + + Offlog = (uint)errorCode; + total = 0; + for (u = 0; u <= 52; u++) + total += matchLengthCount[u]; + errorCode = FSE_normalizeCount(matchLengthNCount, mlLog, matchLengthCount, total, 52, 1); + if (ERR_isError(errorCode)) + { + eSize = errorCode; + goto _cleanup; + } + + mlLog = (uint)errorCode; + total = 0; + for (u = 0; u <= 35; u++) + total += litLengthCount[u]; + errorCode = FSE_normalizeCount(litLengthNCount, llLog, litLengthCount, total, 35, 1); + if (ERR_isError(errorCode)) + { + eSize = errorCode; + goto _cleanup; + } + + llLog = (uint)errorCode; + { + nuint hhSize = HUF_writeCTable_wksp( + dstPtr, + maxDstSize, + hufTable, + 255, + huffLog, + wksp, + sizeof(uint) * 1216 + ); + if (ERR_isError(hhSize)) + { + eSize = hhSize; + goto _cleanup; + } + + dstPtr += hhSize; + maxDstSize -= hhSize; + eSize += hhSize; + } + + { + nuint ohSize = FSE_writeNCount(dstPtr, maxDstSize, offcodeNCount, 30, Offlog); + if (ERR_isError(ohSize)) + { + eSize = ohSize; + goto _cleanup; + } + + dstPtr += ohSize; + maxDstSize -= ohSize; + eSize += ohSize; + } + + { + nuint mhSize = FSE_writeNCount(dstPtr, maxDstSize, matchLengthNCount, 52, mlLog); + if (ERR_isError(mhSize)) + { + eSize = mhSize; + goto _cleanup; + } + + dstPtr += mhSize; + maxDstSize -= mhSize; + eSize += mhSize; + } + + { + nuint lhSize = FSE_writeNCount(dstPtr, maxDstSize, litLengthNCount, 35, llLog); + if (ERR_isError(lhSize)) + { + eSize = lhSize; + goto _cleanup; + } + + dstPtr += lhSize; + maxDstSize -= lhSize; + eSize += lhSize; + } + + if (maxDstSize < 12) + { + eSize = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + goto _cleanup; + } + + MEM_writeLE32(dstPtr + 0, repStartValue[0]); + MEM_writeLE32(dstPtr + 4, repStartValue[1]); + MEM_writeLE32(dstPtr + 8, repStartValue[2]); + eSize += 12; + _cleanup: + ZSTD_freeCDict(esr.dict); + ZSTD_freeCCtx(esr.zc); + free(esr.workPlace); + return eSize; + } + + /** + * @returns the maximum repcode value + */ + private static uint ZDICT_maxRep(uint* reps) + { + uint maxRep = reps[0]; + int r; + for (r = 1; r < 3; ++r) + maxRep = maxRep > reps[r] ? maxRep : reps[r]; + return maxRep; + } + + /*! ZDICT_finalizeDictionary(): + * Given a custom content as a basis for dictionary, and a set of samples, + * finalize dictionary by adding headers and statistics according to the zstd + * dictionary format. + * + * Samples must be stored concatenated in a flat buffer `samplesBuffer`, + * supplied with an array of sizes `samplesSizes`, providing the size of each + * sample in order. The samples are used to construct the statistics, so they + * should be representative of what you will compress with this dictionary. + * + * The compression level can be set in `parameters`. You should pass the + * compression level you expect to use in production. The statistics for each + * compression level differ, so tuning the dictionary for the compression level + * can help quite a bit. + * + * You can set an explicit dictionary ID in `parameters`, or allow us to pick + * a random dictionary ID for you, but we can't guarantee no collisions. + * + * The dstDictBuffer and the dictContent may overlap, and the content will be + * appended to the end of the header. If the header + the content doesn't fit in + * maxDictSize the beginning of the content is truncated to make room, since it + * is presumed that the most profitable content is at the end of the dictionary, + * since that is the cheapest to reference. + * + * `maxDictSize` must be >= max(dictContentSize, ZDICT_DICTSIZE_MIN). + * + * @return: size of dictionary stored into `dstDictBuffer` (<= `maxDictSize`), + * or an error code, which can be tested by ZDICT_isError(). + * Note: ZDICT_finalizeDictionary() will push notifications into stderr if + * instructed to, using notificationLevel>0. + * NOTE: This function currently may fail in several edge cases including: + * * Not enough samples + * * Samples are uncompressible + * * Samples are all exactly the same + */ + public static nuint ZDICT_finalizeDictionary( + void* dictBuffer, + nuint dictBufferCapacity, + void* customDictContent, + nuint dictContentSize, + void* samplesBuffer, + nuint* samplesSizes, + uint nbSamples, + ZDICT_params_t @params + ) + { + nuint hSize; + byte* header = stackalloc byte[256]; + int compressionLevel = @params.compressionLevel == 0 ? 3 : @params.compressionLevel; + uint notificationLevel = @params.notificationLevel; + /* The final dictionary content must be at least as large as the largest repcode */ + nuint minContentSize = ZDICT_maxRep(repStartValue); + nuint paddingSize; + if (dictBufferCapacity < dictContentSize) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + if (dictBufferCapacity < 256) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + MEM_writeLE32(header, 0xEC30A437); + { + ulong randomID = ZSTD_XXH64(customDictContent, dictContentSize, 0); + uint compliantID = (uint)(randomID % ((1U << 31) - 32768) + 32768); + uint dictID = @params.dictID != 0 ? @params.dictID : compliantID; + MEM_writeLE32(header + 4, dictID); + } + + hSize = 8; + { + nuint eSize = ZDICT_analyzeEntropy( + header + hSize, + 256 - hSize, + compressionLevel, + samplesBuffer, + samplesSizes, + nbSamples, + customDictContent, + dictContentSize, + notificationLevel + ); + if (ZDICT_isError(eSize)) + return eSize; + hSize += eSize; + } + + if (hSize + dictContentSize > dictBufferCapacity) + { + dictContentSize = dictBufferCapacity - hSize; + } + + if (dictContentSize < minContentSize) + { + if (hSize + minContentSize > dictBufferCapacity) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + paddingSize = minContentSize - dictContentSize; + } + else + { + paddingSize = 0; + } + + { + nuint dictSize = hSize + paddingSize + dictContentSize; + /* The dictionary consists of the header, optional padding, and the content. + * The padding comes before the content because the "best" position in the + * dictionary is the last byte. + */ + byte* outDictHeader = (byte*)dictBuffer; + byte* outDictPadding = outDictHeader + hSize; + byte* outDictContent = outDictPadding + paddingSize; + assert(dictSize <= dictBufferCapacity); + assert(outDictContent + dictContentSize == (byte*)dictBuffer + dictSize); + memmove(outDictContent, customDictContent, dictContentSize); + memcpy(outDictHeader, header, (uint)hSize); + memset(outDictPadding, 0, (uint)paddingSize); + return dictSize; + } + } + + private static nuint ZDICT_addEntropyTablesFromBuffer_advanced( + void* dictBuffer, + nuint dictContentSize, + nuint dictBufferCapacity, + void* samplesBuffer, + nuint* samplesSizes, + uint nbSamples, + ZDICT_params_t @params + ) + { + int compressionLevel = @params.compressionLevel == 0 ? 3 : @params.compressionLevel; + uint notificationLevel = @params.notificationLevel; + nuint hSize = 8; + { + nuint eSize = ZDICT_analyzeEntropy( + (sbyte*)dictBuffer + hSize, + dictBufferCapacity - hSize, + compressionLevel, + samplesBuffer, + samplesSizes, + nbSamples, + (sbyte*)dictBuffer + dictBufferCapacity - dictContentSize, + dictContentSize, + notificationLevel + ); + if (ZDICT_isError(eSize)) + return eSize; + hSize += eSize; + } + + MEM_writeLE32(dictBuffer, 0xEC30A437); + { + ulong randomID = ZSTD_XXH64( + (sbyte*)dictBuffer + dictBufferCapacity - dictContentSize, + dictContentSize, + 0 + ); + uint compliantID = (uint)(randomID % ((1U << 31) - 32768) + 32768); + uint dictID = @params.dictID != 0 ? @params.dictID : compliantID; + MEM_writeLE32((sbyte*)dictBuffer + 4, dictID); + } + + if (hSize + dictContentSize < dictBufferCapacity) + memmove( + (sbyte*)dictBuffer + hSize, + (sbyte*)dictBuffer + dictBufferCapacity - dictContentSize, + dictContentSize + ); + return dictBufferCapacity < hSize + dictContentSize + ? dictBufferCapacity + : hSize + dictContentSize; + } + + /*! ZDICT_trainFromBuffer(): + * Train a dictionary from an array of samples. + * Redirect towards ZDICT_optimizeTrainFromBuffer_fastCover() single-threaded, with d=8, steps=4, + * f=20, and accel=1. + * Samples must be stored concatenated in a single flat buffer `samplesBuffer`, + * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. + * The resulting dictionary will be saved into `dictBuffer`. + * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) + * or an error code, which can be tested with ZDICT_isError(). + * Note: Dictionary training will fail if there are not enough samples to construct a + * dictionary, or if most of the samples are too small (< 8 bytes being the lower limit). + * If dictionary training fails, you should use zstd without a dictionary, as the dictionary + * would've been ineffective anyways. If you believe your samples would benefit from a dictionary + * please open an issue with details, and we can look into it. + * Note: ZDICT_trainFromBuffer()'s memory usage is about 6 MB. + * Tips: In general, a reasonable dictionary has a size of ~ 100 KB. + * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`. + * In general, it's recommended to provide a few thousands samples, though this can vary a lot. + * It's recommended that total size of all samples be about ~x100 times the target size of dictionary. + */ + public static nuint ZDICT_trainFromBuffer( + void* dictBuffer, + nuint dictBufferCapacity, + void* samplesBuffer, + nuint* samplesSizes, + uint nbSamples + ) + { + ZDICT_fastCover_params_t @params; + @params = new ZDICT_fastCover_params_t { d = 8, steps = 4 }; + @params.zParams.compressionLevel = 3; + return ZDICT_optimizeTrainFromBuffer_fastCover( + dictBuffer, + dictBufferCapacity, + samplesBuffer, + samplesSizes, + nbSamples, + &@params + ); + } + + public static nuint ZDICT_addEntropyTablesFromBuffer( + void* dictBuffer, + nuint dictContentSize, + nuint dictBufferCapacity, + void* samplesBuffer, + nuint* samplesSizes, + uint nbSamples + ) + { + ZDICT_params_t @params; + @params = new ZDICT_params_t(); + return ZDICT_addEntropyTablesFromBuffer_advanced( + dictBuffer, + dictContentSize, + dictBufferCapacity, + samplesBuffer, + samplesSizes, + nbSamples, + @params + ); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/Zstd.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/Zstd.cs new file mode 100644 index 00000000..ce1018b0 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/Zstd.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + private static readonly ZSTD_customMem ZSTD_defaultCMem = new ZSTD_customMem( + customAlloc: null, + customFree: null, + opaque: null + ); +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCommon.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCommon.cs new file mode 100644 index 00000000..629a435c --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCommon.cs @@ -0,0 +1,48 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /*-**************************************** + * Version + ******************************************/ + public static uint ZSTD_versionNumber() + { + return 1 * 100 * 100 + 5 * 100 + 7; + } + + /*! ZSTD_versionString() : + * Return runtime library version, like "1.4.5". Requires v1.3.0+. */ + public static string ZSTD_versionString() + { + return "1.5.7"; + } + + /*! ZSTD_isError() : + * tells if a return value is an error code + * symbol is required for external callers */ + public static bool ZSTD_isError(nuint code) + { + return ERR_isError(code); + } + + /*! ZSTD_getErrorName() : + * provides error code string from function result (useful for debugging) */ + public static string ZSTD_getErrorName(nuint code) + { + return ERR_getErrorName(code); + } + + /*! ZSTD_getError() : + * convert a `size_t` function result into a proper ZSTD_errorCode enum */ + public static ZSTD_ErrorCode ZSTD_getErrorCode(nuint code) + { + return ERR_getErrorCode(code); + } + + /*! ZSTD_getErrorString() : + * provides error code string from enum */ + public static string ZSTD_getErrorString(ZSTD_ErrorCode code) + { + return ERR_getErrorString(code); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompress.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompress.cs new file mode 100644 index 00000000..8198034b --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompress.cs @@ -0,0 +1,11225 @@ +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /*-************************************* + * Helper functions + ***************************************/ + /* ZSTD_compressBound() + * Note that the result from this function is only valid for + * the one-pass compression functions. + * When employing the streaming mode, + * if flushes are frequently altering the size of blocks, + * the overhead from block headers can make the compressed data larger + * than the return value of ZSTD_compressBound(). + */ + public static nuint ZSTD_compressBound(nuint srcSize) + { + nuint r = + srcSize >= (sizeof(nuint) == 8 ? 0xFF00FF00FF00FF00UL : 0xFF00FF00U) + ? 0 + : srcSize + + (srcSize >> 8) + + (srcSize < 128 << 10 ? (128 << 10) - srcSize >> 11 : 0); + if (r == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + return r; + } + + public static ZSTD_CCtx_s* ZSTD_createCCtx() + { + return ZSTD_createCCtx_advanced(ZSTD_defaultCMem); + } + + private static void ZSTD_initCCtx(ZSTD_CCtx_s* cctx, ZSTD_customMem memManager) + { + assert(cctx != null); + *cctx = new ZSTD_CCtx_s { customMem = memManager, bmi2 = 0 }; + { + nuint err = ZSTD_CCtx_reset(cctx, ZSTD_ResetDirective.ZSTD_reset_parameters); + assert(!ERR_isError(err)); + } + } + + public static ZSTD_CCtx_s* ZSTD_createCCtx_advanced(ZSTD_customMem customMem) + { + if (((customMem.customAlloc == null ? 1 : 0) ^ (customMem.customFree == null ? 1 : 0)) != 0) + return null; + { + ZSTD_CCtx_s* cctx = (ZSTD_CCtx_s*)ZSTD_customMalloc( + (nuint)sizeof(ZSTD_CCtx_s), + customMem + ); + if (cctx == null) + return null; + ZSTD_initCCtx(cctx, customMem); + return cctx; + } + } + + /*! ZSTD_initStatic*() : + * Initialize an object using a pre-allocated fixed-size buffer. + * workspace: The memory area to emplace the object into. + * Provided pointer *must be 8-bytes aligned*. + * Buffer must outlive object. + * workspaceSize: Use ZSTD_estimate*Size() to determine + * how large workspace must be to support target scenario. + * @return : pointer to object (same address as workspace, just different type), + * or NULL if error (size too small, incorrect alignment, etc.) + * Note : zstd will never resize nor malloc() when using a static buffer. + * If the object requires more memory than available, + * zstd will just error out (typically ZSTD_error_memory_allocation). + * Note 2 : there is no corresponding "free" function. + * Since workspace is allocated externally, it must be freed externally too. + * Note 3 : cParams : use ZSTD_getCParams() to convert a compression level + * into its associated cParams. + * Limitation 1 : currently not compatible with internal dictionary creation, triggered by + * ZSTD_CCtx_loadDictionary(), ZSTD_initCStream_usingDict() or ZSTD_initDStream_usingDict(). + * Limitation 2 : static cctx currently not compatible with multi-threading. + * Limitation 3 : static dctx is incompatible with legacy support. + */ + public static ZSTD_CCtx_s* ZSTD_initStaticCCtx(void* workspace, nuint workspaceSize) + { + ZSTD_cwksp ws; + ZSTD_CCtx_s* cctx; + if (workspaceSize <= (nuint)sizeof(ZSTD_CCtx_s)) + return null; + if (((nuint)workspace & 7) != 0) + return null; + ZSTD_cwksp_init( + &ws, + workspace, + workspaceSize, + ZSTD_cwksp_static_alloc_e.ZSTD_cwksp_static_alloc + ); + cctx = (ZSTD_CCtx_s*)ZSTD_cwksp_reserve_object(&ws, (nuint)sizeof(ZSTD_CCtx_s)); + if (cctx == null) + return null; + *cctx = new ZSTD_CCtx_s(); + ZSTD_cwksp_move(&cctx->workspace, &ws); + cctx->staticSize = workspaceSize; + if ( + ZSTD_cwksp_check_available( + &cctx->workspace, + (nuint)( + ( + (8 << 10) + 512 + sizeof(uint) * (52 + 2) > 8208 + ? (8 << 10) + 512 + sizeof(uint) * (52 + 2) + : 8208 + ) + + 2 * sizeof(ZSTD_compressedBlockState_t) + ) + ) == 0 + ) + return null; + cctx->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object( + &cctx->workspace, + (nuint)sizeof(ZSTD_compressedBlockState_t) + ); + cctx->blockState.nextCBlock = (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object( + &cctx->workspace, + (nuint)sizeof(ZSTD_compressedBlockState_t) + ); + cctx->tmpWorkspace = ZSTD_cwksp_reserve_object( + &cctx->workspace, + (8 << 10) + 512 + sizeof(uint) * (52 + 2) > 8208 + ? (8 << 10) + 512 + sizeof(uint) * (52 + 2) + : 8208 + ); + cctx->tmpWkspSize = + (8 << 10) + 512 + sizeof(uint) * (52 + 2) > 8208 + ? (8 << 10) + 512 + sizeof(uint) * (52 + 2) + : 8208; + cctx->bmi2 = 0; + return cctx; + } + + /** + * Clears and frees all of the dictionaries in the CCtx. + */ + private static void ZSTD_clearAllDicts(ZSTD_CCtx_s* cctx) + { + ZSTD_customFree(cctx->localDict.dictBuffer, cctx->customMem); + ZSTD_freeCDict(cctx->localDict.cdict); + cctx->localDict = new ZSTD_localDict(); + cctx->prefixDict = new ZSTD_prefixDict_s(); + cctx->cdict = null; + } + + private static nuint ZSTD_sizeof_localDict(ZSTD_localDict dict) + { + nuint bufferSize = dict.dictBuffer != null ? dict.dictSize : 0; + nuint cdictSize = ZSTD_sizeof_CDict(dict.cdict); + return bufferSize + cdictSize; + } + + private static void ZSTD_freeCCtxContent(ZSTD_CCtx_s* cctx) + { + assert(cctx != null); + assert(cctx->staticSize == 0); + ZSTD_clearAllDicts(cctx); + ZSTDMT_freeCCtx(cctx->mtctx); + cctx->mtctx = null; + ZSTD_cwksp_free(&cctx->workspace, cctx->customMem); + } + + public static nuint ZSTD_freeCCtx(ZSTD_CCtx_s* cctx) + { + if (cctx == null) + return 0; + if (cctx->staticSize != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + { + int cctxInWorkspace = ZSTD_cwksp_owns_buffer(&cctx->workspace, cctx); + ZSTD_freeCCtxContent(cctx); + if (cctxInWorkspace == 0) + ZSTD_customFree(cctx, cctx->customMem); + } + + return 0; + } + + private static nuint ZSTD_sizeof_mtctx(ZSTD_CCtx_s* cctx) + { + return ZSTDMT_sizeof_CCtx(cctx->mtctx); + } + + /*! ZSTD_sizeof_*() : Requires v1.4.0+ + * These functions give the _current_ memory usage of selected object. + * Note that object memory usage can evolve (increase or decrease) over time. */ + public static nuint ZSTD_sizeof_CCtx(ZSTD_CCtx_s* cctx) + { + if (cctx == null) + return 0; + return (nuint)(cctx->workspace.workspace == cctx ? 0 : sizeof(ZSTD_CCtx_s)) + + ZSTD_cwksp_sizeof(&cctx->workspace) + + ZSTD_sizeof_localDict(cctx->localDict) + + ZSTD_sizeof_mtctx(cctx); + } + + public static nuint ZSTD_sizeof_CStream(ZSTD_CCtx_s* zcs) + { + return ZSTD_sizeof_CCtx(zcs); + } + + /* private API call, for dictBuilder only */ + private static SeqStore_t* ZSTD_getSeqStore(ZSTD_CCtx_s* ctx) + { + return &ctx->seqStore; + } + + /* Returns true if the strategy supports using a row based matchfinder */ + private static int ZSTD_rowMatchFinderSupported(ZSTD_strategy strategy) + { + return strategy >= ZSTD_strategy.ZSTD_greedy && strategy <= ZSTD_strategy.ZSTD_lazy2 + ? 1 + : 0; + } + + /* Returns true if the strategy and useRowMatchFinder mode indicate that we will use the row based matchfinder + * for this compression. + */ + private static int ZSTD_rowMatchFinderUsed(ZSTD_strategy strategy, ZSTD_paramSwitch_e mode) + { + assert(mode != ZSTD_paramSwitch_e.ZSTD_ps_auto); + return + ZSTD_rowMatchFinderSupported(strategy) != 0 && mode == ZSTD_paramSwitch_e.ZSTD_ps_enable + ? 1 + : 0; + } + + /* Returns row matchfinder usage given an initial mode and cParams */ + private static ZSTD_paramSwitch_e ZSTD_resolveRowMatchFinderMode( + ZSTD_paramSwitch_e mode, + ZSTD_compressionParameters* cParams + ) + { + if (mode != ZSTD_paramSwitch_e.ZSTD_ps_auto) + return mode; + mode = ZSTD_paramSwitch_e.ZSTD_ps_disable; + if (ZSTD_rowMatchFinderSupported(cParams->strategy) == 0) + return mode; + if (cParams->windowLog > 14) + mode = ZSTD_paramSwitch_e.ZSTD_ps_enable; + return mode; + } + + /* Returns block splitter usage (generally speaking, when using slower/stronger compression modes) */ + private static ZSTD_paramSwitch_e ZSTD_resolveBlockSplitterMode( + ZSTD_paramSwitch_e mode, + ZSTD_compressionParameters* cParams + ) + { + if (mode != ZSTD_paramSwitch_e.ZSTD_ps_auto) + return mode; + return cParams->strategy >= ZSTD_strategy.ZSTD_btopt && cParams->windowLog >= 17 + ? ZSTD_paramSwitch_e.ZSTD_ps_enable + : ZSTD_paramSwitch_e.ZSTD_ps_disable; + } + + /* Returns 1 if the arguments indicate that we should allocate a chainTable, 0 otherwise */ + private static int ZSTD_allocateChainTable( + ZSTD_strategy strategy, + ZSTD_paramSwitch_e useRowMatchFinder, + uint forDDSDict + ) + { + assert(useRowMatchFinder != ZSTD_paramSwitch_e.ZSTD_ps_auto); + return + forDDSDict != 0 + || strategy != ZSTD_strategy.ZSTD_fast + && ZSTD_rowMatchFinderUsed(strategy, useRowMatchFinder) == 0 + ? 1 + : 0; + } + + /* Returns ZSTD_ps_enable if compression parameters are such that we should + * enable long distance matching (wlog >= 27, strategy >= btopt). + * Returns ZSTD_ps_disable otherwise. + */ + private static ZSTD_paramSwitch_e ZSTD_resolveEnableLdm( + ZSTD_paramSwitch_e mode, + ZSTD_compressionParameters* cParams + ) + { + if (mode != ZSTD_paramSwitch_e.ZSTD_ps_auto) + return mode; + return cParams->strategy >= ZSTD_strategy.ZSTD_btopt && cParams->windowLog >= 27 + ? ZSTD_paramSwitch_e.ZSTD_ps_enable + : ZSTD_paramSwitch_e.ZSTD_ps_disable; + } + + private static int ZSTD_resolveExternalSequenceValidation(int mode) + { + return mode; + } + + /* Resolves maxBlockSize to the default if no value is present. */ + private static nuint ZSTD_resolveMaxBlockSize(nuint maxBlockSize) + { + if (maxBlockSize == 0) + { + return 1 << 17; + } + else + { + return maxBlockSize; + } + } + + private static ZSTD_paramSwitch_e ZSTD_resolveExternalRepcodeSearch( + ZSTD_paramSwitch_e value, + int cLevel + ) + { + if (value != ZSTD_paramSwitch_e.ZSTD_ps_auto) + return value; + if (cLevel < 10) + { + return ZSTD_paramSwitch_e.ZSTD_ps_disable; + } + else + { + return ZSTD_paramSwitch_e.ZSTD_ps_enable; + } + } + + /* Returns 1 if compression parameters are such that CDict hashtable and chaintable indices are tagged. + * If so, the tags need to be removed in ZSTD_resetCCtx_byCopyingCDict. */ + private static int ZSTD_CDictIndicesAreTagged(ZSTD_compressionParameters* cParams) + { + return + cParams->strategy == ZSTD_strategy.ZSTD_fast + || cParams->strategy == ZSTD_strategy.ZSTD_dfast + ? 1 + : 0; + } + + private static ZSTD_CCtx_params_s ZSTD_makeCCtxParamsFromCParams( + ZSTD_compressionParameters cParams + ) + { + ZSTD_CCtx_params_s cctxParams; + ZSTD_CCtxParams_init(&cctxParams, 3); + cctxParams.cParams = cParams; + cctxParams.ldmParams.enableLdm = ZSTD_resolveEnableLdm( + cctxParams.ldmParams.enableLdm, + &cParams + ); + if (cctxParams.ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + ZSTD_ldm_adjustParameters(&cctxParams.ldmParams, &cParams); + assert(cctxParams.ldmParams.hashLog >= cctxParams.ldmParams.bucketSizeLog); + assert(cctxParams.ldmParams.hashRateLog < 32); + } + + cctxParams.postBlockSplitter = ZSTD_resolveBlockSplitterMode( + cctxParams.postBlockSplitter, + &cParams + ); + cctxParams.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode( + cctxParams.useRowMatchFinder, + &cParams + ); + cctxParams.validateSequences = ZSTD_resolveExternalSequenceValidation( + cctxParams.validateSequences + ); + cctxParams.maxBlockSize = ZSTD_resolveMaxBlockSize(cctxParams.maxBlockSize); + cctxParams.searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch( + cctxParams.searchForExternalRepcodes, + cctxParams.compressionLevel + ); + assert(ZSTD_checkCParams(cParams) == 0); + return cctxParams; + } + + private static ZSTD_CCtx_params_s* ZSTD_createCCtxParams_advanced(ZSTD_customMem customMem) + { + ZSTD_CCtx_params_s* @params; + if (((customMem.customAlloc == null ? 1 : 0) ^ (customMem.customFree == null ? 1 : 0)) != 0) + return null; + @params = (ZSTD_CCtx_params_s*)ZSTD_customCalloc( + (nuint)sizeof(ZSTD_CCtx_params_s), + customMem + ); + if (@params == null) + { + return null; + } + + ZSTD_CCtxParams_init(@params, 3); + @params->customMem = customMem; + return @params; + } + + /*! ZSTD_CCtx_params : + * Quick howto : + * - ZSTD_createCCtxParams() : Create a ZSTD_CCtx_params structure + * - ZSTD_CCtxParams_setParameter() : Push parameters one by one into + * an existing ZSTD_CCtx_params structure. + * This is similar to + * ZSTD_CCtx_setParameter(). + * - ZSTD_CCtx_setParametersUsingCCtxParams() : Apply parameters to + * an existing CCtx. + * These parameters will be applied to + * all subsequent frames. + * - ZSTD_compressStream2() : Do compression using the CCtx. + * - ZSTD_freeCCtxParams() : Free the memory, accept NULL pointer. + * + * This can be used with ZSTD_estimateCCtxSize_advanced_usingCCtxParams() + * for static allocation of CCtx for single-threaded compression. + */ + public static ZSTD_CCtx_params_s* ZSTD_createCCtxParams() + { + return ZSTD_createCCtxParams_advanced(ZSTD_defaultCMem); + } + + public static nuint ZSTD_freeCCtxParams(ZSTD_CCtx_params_s* @params) + { + if (@params == null) + { + return 0; + } + + ZSTD_customFree(@params, @params->customMem); + return 0; + } + + /*! ZSTD_CCtxParams_reset() : + * Reset params to default values. + */ + public static nuint ZSTD_CCtxParams_reset(ZSTD_CCtx_params_s* @params) + { + return ZSTD_CCtxParams_init(@params, 3); + } + + /*! ZSTD_CCtxParams_init() : + * Initializes the compression parameters of cctxParams according to + * compression level. All other parameters are reset to their default values. + */ + public static nuint ZSTD_CCtxParams_init(ZSTD_CCtx_params_s* cctxParams, int compressionLevel) + { + if (cctxParams == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + + *cctxParams = new ZSTD_CCtx_params_s { compressionLevel = compressionLevel }; + cctxParams->fParams.contentSizeFlag = 1; + return 0; + } + + /** + * Initializes `cctxParams` from `params` and `compressionLevel`. + * @param compressionLevel If params are derived from a compression level then that compression level, otherwise ZSTD_NO_CLEVEL. + */ + private static void ZSTD_CCtxParams_init_internal( + ZSTD_CCtx_params_s* cctxParams, + ZSTD_parameters* @params, + int compressionLevel + ) + { + assert(ZSTD_checkCParams(@params->cParams) == 0); + *cctxParams = new ZSTD_CCtx_params_s + { + cParams = @params->cParams, + fParams = @params->fParams, + compressionLevel = compressionLevel, + useRowMatchFinder = ZSTD_resolveRowMatchFinderMode( + cctxParams->useRowMatchFinder, + &@params->cParams + ), + postBlockSplitter = ZSTD_resolveBlockSplitterMode( + cctxParams->postBlockSplitter, + &@params->cParams + ), + }; + cctxParams->ldmParams.enableLdm = ZSTD_resolveEnableLdm( + cctxParams->ldmParams.enableLdm, + &@params->cParams + ); + cctxParams->validateSequences = ZSTD_resolveExternalSequenceValidation( + cctxParams->validateSequences + ); + cctxParams->maxBlockSize = ZSTD_resolveMaxBlockSize(cctxParams->maxBlockSize); + cctxParams->searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch( + cctxParams->searchForExternalRepcodes, + compressionLevel + ); + } + + /*! ZSTD_CCtxParams_init_advanced() : + * Initializes the compression and frame parameters of cctxParams according to + * params. All other parameters are reset to their default values. + */ + public static nuint ZSTD_CCtxParams_init_advanced( + ZSTD_CCtx_params_s* cctxParams, + ZSTD_parameters @params + ) + { + if (cctxParams == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + + { + nuint err_code = ZSTD_checkCParams(@params.cParams); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ZSTD_CCtxParams_init_internal(cctxParams, &@params, 0); + return 0; + } + + /** + * Sets cctxParams' cParams and fParams from params, but otherwise leaves them alone. + * @param params Validated zstd parameters. + */ + private static void ZSTD_CCtxParams_setZstdParams( + ZSTD_CCtx_params_s* cctxParams, + ZSTD_parameters* @params + ) + { + assert(ZSTD_checkCParams(@params->cParams) == 0); + cctxParams->cParams = @params->cParams; + cctxParams->fParams = @params->fParams; + cctxParams->compressionLevel = 0; + } + + /*! ZSTD_cParam_getBounds() : + * All parameters must belong to an interval with lower and upper bounds, + * otherwise they will either trigger an error or be automatically clamped. + * @return : a structure, ZSTD_bounds, which contains + * - an error status field, which must be tested using ZSTD_isError() + * - lower and upper bounds, both inclusive + */ + public static ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param) + { + ZSTD_bounds bounds = new ZSTD_bounds + { + error = 0, + lowerBound = 0, + upperBound = 0, + }; + switch (param) + { + case ZSTD_cParameter.ZSTD_c_compressionLevel: + bounds.lowerBound = ZSTD_minCLevel(); + bounds.upperBound = ZSTD_maxCLevel(); + return bounds; + case ZSTD_cParameter.ZSTD_c_windowLog: + bounds.lowerBound = 10; + bounds.upperBound = sizeof(nuint) == 4 ? 30 : 31; + return bounds; + case ZSTD_cParameter.ZSTD_c_hashLog: + bounds.lowerBound = 6; + bounds.upperBound = + (sizeof(nuint) == 4 ? 30 : 31) < 30 + ? sizeof(nuint) == 4 + ? 30 + : 31 + : 30; + return bounds; + case ZSTD_cParameter.ZSTD_c_chainLog: + bounds.lowerBound = 6; + bounds.upperBound = sizeof(nuint) == 4 ? 29 : 30; + return bounds; + case ZSTD_cParameter.ZSTD_c_searchLog: + bounds.lowerBound = 1; + bounds.upperBound = (sizeof(nuint) == 4 ? 30 : 31) - 1; + return bounds; + case ZSTD_cParameter.ZSTD_c_minMatch: + bounds.lowerBound = 3; + bounds.upperBound = 7; + return bounds; + case ZSTD_cParameter.ZSTD_c_targetLength: + bounds.lowerBound = 0; + bounds.upperBound = 1 << 17; + return bounds; + case ZSTD_cParameter.ZSTD_c_strategy: + bounds.lowerBound = (int)ZSTD_strategy.ZSTD_fast; + bounds.upperBound = (int)ZSTD_strategy.ZSTD_btultra2; + return bounds; + case ZSTD_cParameter.ZSTD_c_contentSizeFlag: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + case ZSTD_cParameter.ZSTD_c_checksumFlag: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + case ZSTD_cParameter.ZSTD_c_dictIDFlag: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + case ZSTD_cParameter.ZSTD_c_nbWorkers: + bounds.lowerBound = 0; + bounds.upperBound = sizeof(void*) == 4 ? 64 : 256; + return bounds; + case ZSTD_cParameter.ZSTD_c_jobSize: + bounds.lowerBound = 0; + bounds.upperBound = MEM_32bits ? 512 * (1 << 20) : 1024 * (1 << 20); + return bounds; + case ZSTD_cParameter.ZSTD_c_overlapLog: + bounds.lowerBound = 0; + bounds.upperBound = 9; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam8: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + case ZSTD_cParameter.ZSTD_c_enableLongDistanceMatching: + bounds.lowerBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_disable; + return bounds; + case ZSTD_cParameter.ZSTD_c_ldmHashLog: + bounds.lowerBound = 6; + bounds.upperBound = + (sizeof(nuint) == 4 ? 30 : 31) < 30 + ? sizeof(nuint) == 4 + ? 30 + : 31 + : 30; + return bounds; + case ZSTD_cParameter.ZSTD_c_ldmMinMatch: + bounds.lowerBound = 4; + bounds.upperBound = 4096; + return bounds; + case ZSTD_cParameter.ZSTD_c_ldmBucketSizeLog: + bounds.lowerBound = 1; + bounds.upperBound = 8; + return bounds; + case ZSTD_cParameter.ZSTD_c_ldmHashRateLog: + bounds.lowerBound = 0; + bounds.upperBound = (sizeof(nuint) == 4 ? 30 : 31) - 6; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam1: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam3: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam2: + bounds.lowerBound = (int)ZSTD_format_e.ZSTD_f_zstd1; + bounds.upperBound = (int)ZSTD_format_e.ZSTD_f_zstd1_magicless; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam4: + bounds.lowerBound = (int)ZSTD_dictAttachPref_e.ZSTD_dictDefaultAttach; + bounds.upperBound = (int)ZSTD_dictAttachPref_e.ZSTD_dictForceLoad; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam5: + bounds.lowerBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_disable; + return bounds; + case ZSTD_cParameter.ZSTD_c_targetCBlockSize: + bounds.lowerBound = 1340; + bounds.upperBound = 1 << 17; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam7: + bounds.lowerBound = 0; + bounds.upperBound = 2147483647; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam9: + case ZSTD_cParameter.ZSTD_c_experimentalParam10: + bounds.lowerBound = (int)ZSTD_bufferMode_e.ZSTD_bm_buffered; + bounds.upperBound = (int)ZSTD_bufferMode_e.ZSTD_bm_stable; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam11: + bounds.lowerBound = (int)ZSTD_sequenceFormat_e.ZSTD_sf_noBlockDelimiters; + bounds.upperBound = (int)ZSTD_sequenceFormat_e.ZSTD_sf_explicitBlockDelimiters; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam12: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam13: + bounds.lowerBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_disable; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam20: + bounds.lowerBound = 0; + bounds.upperBound = 6; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam14: + bounds.lowerBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_disable; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam15: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam16: + bounds.lowerBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_disable; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam17: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam18: + bounds.lowerBound = 1 << 10; + bounds.upperBound = 1 << 17; + return bounds; + case ZSTD_cParameter.ZSTD_c_experimentalParam19: + bounds.lowerBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_auto; + bounds.upperBound = (int)ZSTD_paramSwitch_e.ZSTD_ps_disable; + return bounds; + default: + bounds.error = unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported) + ); + return bounds; + } + } + + /* ZSTD_cParam_clampBounds: + * Clamps the value into the bounded range. + */ + private static nuint ZSTD_cParam_clampBounds(ZSTD_cParameter cParam, int* value) + { + ZSTD_bounds bounds = ZSTD_cParam_getBounds(cParam); + if (ERR_isError(bounds.error)) + return bounds.error; + if (*value < bounds.lowerBound) + *value = bounds.lowerBound; + if (*value > bounds.upperBound) + *value = bounds.upperBound; + return 0; + } + + private static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param) + { + switch (param) + { + case ZSTD_cParameter.ZSTD_c_compressionLevel: + case ZSTD_cParameter.ZSTD_c_hashLog: + case ZSTD_cParameter.ZSTD_c_chainLog: + case ZSTD_cParameter.ZSTD_c_searchLog: + case ZSTD_cParameter.ZSTD_c_minMatch: + case ZSTD_cParameter.ZSTD_c_targetLength: + case ZSTD_cParameter.ZSTD_c_strategy: + case ZSTD_cParameter.ZSTD_c_experimentalParam20: + return 1; + case ZSTD_cParameter.ZSTD_c_experimentalParam2: + case ZSTD_cParameter.ZSTD_c_windowLog: + case ZSTD_cParameter.ZSTD_c_contentSizeFlag: + case ZSTD_cParameter.ZSTD_c_checksumFlag: + case ZSTD_cParameter.ZSTD_c_dictIDFlag: + case ZSTD_cParameter.ZSTD_c_experimentalParam3: + case ZSTD_cParameter.ZSTD_c_nbWorkers: + case ZSTD_cParameter.ZSTD_c_jobSize: + case ZSTD_cParameter.ZSTD_c_overlapLog: + case ZSTD_cParameter.ZSTD_c_experimentalParam1: + case ZSTD_cParameter.ZSTD_c_experimentalParam8: + case ZSTD_cParameter.ZSTD_c_enableLongDistanceMatching: + case ZSTD_cParameter.ZSTD_c_ldmHashLog: + case ZSTD_cParameter.ZSTD_c_ldmMinMatch: + case ZSTD_cParameter.ZSTD_c_ldmBucketSizeLog: + case ZSTD_cParameter.ZSTD_c_ldmHashRateLog: + case ZSTD_cParameter.ZSTD_c_experimentalParam4: + case ZSTD_cParameter.ZSTD_c_experimentalParam5: + case ZSTD_cParameter.ZSTD_c_targetCBlockSize: + case ZSTD_cParameter.ZSTD_c_experimentalParam7: + case ZSTD_cParameter.ZSTD_c_experimentalParam9: + case ZSTD_cParameter.ZSTD_c_experimentalParam10: + case ZSTD_cParameter.ZSTD_c_experimentalParam11: + case ZSTD_cParameter.ZSTD_c_experimentalParam12: + case ZSTD_cParameter.ZSTD_c_experimentalParam13: + case ZSTD_cParameter.ZSTD_c_experimentalParam14: + case ZSTD_cParameter.ZSTD_c_experimentalParam15: + case ZSTD_cParameter.ZSTD_c_experimentalParam16: + case ZSTD_cParameter.ZSTD_c_experimentalParam17: + case ZSTD_cParameter.ZSTD_c_experimentalParam18: + case ZSTD_cParameter.ZSTD_c_experimentalParam19: + default: + return 0; + } + } + + /*! ZSTD_CCtx_setParameter() : + * Set one compression parameter, selected by enum ZSTD_cParameter. + * All parameters have valid bounds. Bounds can be queried using ZSTD_cParam_getBounds(). + * Providing a value beyond bound will either clamp it, or trigger an error (depending on parameter). + * Setting a parameter is generally only possible during frame initialization (before starting compression). + * Exception : when using multi-threading mode (nbWorkers >= 1), + * the following parameters can be updated _during_ compression (within same frame): + * => compressionLevel, hashLog, chainLog, searchLog, minMatch, targetLength and strategy. + * new parameters will be active for next job only (after a flush()). + * @return : an error code (which can be tested using ZSTD_isError()). + */ + public static nuint ZSTD_CCtx_setParameter(ZSTD_CCtx_s* cctx, ZSTD_cParameter param, int value) + { + if (cctx->streamStage != ZSTD_cStreamStage.zcss_init) + { + if (ZSTD_isUpdateAuthorized(param) != 0) + { + cctx->cParamsChanged = 1; + } + else + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + } + + switch (param) + { + case ZSTD_cParameter.ZSTD_c_nbWorkers: + if (value != 0 && cctx->staticSize != 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported) + ); + } + + break; + case ZSTD_cParameter.ZSTD_c_compressionLevel: + case ZSTD_cParameter.ZSTD_c_windowLog: + case ZSTD_cParameter.ZSTD_c_hashLog: + case ZSTD_cParameter.ZSTD_c_chainLog: + case ZSTD_cParameter.ZSTD_c_searchLog: + case ZSTD_cParameter.ZSTD_c_minMatch: + case ZSTD_cParameter.ZSTD_c_targetLength: + case ZSTD_cParameter.ZSTD_c_strategy: + case ZSTD_cParameter.ZSTD_c_ldmHashRateLog: + case ZSTD_cParameter.ZSTD_c_experimentalParam2: + case ZSTD_cParameter.ZSTD_c_contentSizeFlag: + case ZSTD_cParameter.ZSTD_c_checksumFlag: + case ZSTD_cParameter.ZSTD_c_dictIDFlag: + case ZSTD_cParameter.ZSTD_c_experimentalParam3: + case ZSTD_cParameter.ZSTD_c_experimentalParam4: + case ZSTD_cParameter.ZSTD_c_experimentalParam5: + case ZSTD_cParameter.ZSTD_c_jobSize: + case ZSTD_cParameter.ZSTD_c_overlapLog: + case ZSTD_cParameter.ZSTD_c_experimentalParam1: + case ZSTD_cParameter.ZSTD_c_experimentalParam8: + case ZSTD_cParameter.ZSTD_c_enableLongDistanceMatching: + case ZSTD_cParameter.ZSTD_c_ldmHashLog: + case ZSTD_cParameter.ZSTD_c_ldmMinMatch: + case ZSTD_cParameter.ZSTD_c_ldmBucketSizeLog: + case ZSTD_cParameter.ZSTD_c_targetCBlockSize: + case ZSTD_cParameter.ZSTD_c_experimentalParam7: + case ZSTD_cParameter.ZSTD_c_experimentalParam9: + case ZSTD_cParameter.ZSTD_c_experimentalParam10: + case ZSTD_cParameter.ZSTD_c_experimentalParam11: + case ZSTD_cParameter.ZSTD_c_experimentalParam12: + case ZSTD_cParameter.ZSTD_c_experimentalParam13: + case ZSTD_cParameter.ZSTD_c_experimentalParam20: + case ZSTD_cParameter.ZSTD_c_experimentalParam14: + case ZSTD_cParameter.ZSTD_c_experimentalParam15: + case ZSTD_cParameter.ZSTD_c_experimentalParam16: + case ZSTD_cParameter.ZSTD_c_experimentalParam17: + case ZSTD_cParameter.ZSTD_c_experimentalParam18: + case ZSTD_cParameter.ZSTD_c_experimentalParam19: + break; + default: + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported)); + } + + return ZSTD_CCtxParams_setParameter(&cctx->requestedParams, param, value); + } + + /*! ZSTD_CCtxParams_setParameter() : Requires v1.4.0+ + * Similar to ZSTD_CCtx_setParameter. + * Set one compression parameter, selected by enum ZSTD_cParameter. + * Parameters must be applied to a ZSTD_CCtx using + * ZSTD_CCtx_setParametersUsingCCtxParams(). + * @result : a code representing success or failure (which can be tested with + * ZSTD_isError()). + */ + public static nuint ZSTD_CCtxParams_setParameter( + ZSTD_CCtx_params_s* CCtxParams, + ZSTD_cParameter param, + int value + ) + { + switch (param) + { + case ZSTD_cParameter.ZSTD_c_experimentalParam2: + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam2, value) == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->format = (ZSTD_format_e)value; + return (nuint)CCtxParams->format; + case ZSTD_cParameter.ZSTD_c_compressionLevel: + { + { + nuint err_code = ZSTD_cParam_clampBounds(param, &value); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (value == 0) + CCtxParams->compressionLevel = 3; + else + CCtxParams->compressionLevel = value; + if (CCtxParams->compressionLevel >= 0) + return (nuint)CCtxParams->compressionLevel; + return 0; + } + + case ZSTD_cParameter.ZSTD_c_windowLog: + if (value != 0) + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_windowLog, value) == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + CCtxParams->cParams.windowLog = (uint)value; + return CCtxParams->cParams.windowLog; + case ZSTD_cParameter.ZSTD_c_hashLog: + if (value != 0) + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_hashLog, value) == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + CCtxParams->cParams.hashLog = (uint)value; + return CCtxParams->cParams.hashLog; + case ZSTD_cParameter.ZSTD_c_chainLog: + if (value != 0) + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_chainLog, value) == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + CCtxParams->cParams.chainLog = (uint)value; + return CCtxParams->cParams.chainLog; + case ZSTD_cParameter.ZSTD_c_searchLog: + if (value != 0) + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_searchLog, value) == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + CCtxParams->cParams.searchLog = (uint)value; + return (nuint)value; + case ZSTD_cParameter.ZSTD_c_minMatch: + if (value != 0) + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_minMatch, value) == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + CCtxParams->cParams.minMatch = (uint)value; + return CCtxParams->cParams.minMatch; + case ZSTD_cParameter.ZSTD_c_targetLength: + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_targetLength, value) == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->cParams.targetLength = (uint)value; + return CCtxParams->cParams.targetLength; + case ZSTD_cParameter.ZSTD_c_strategy: + if (value != 0) + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_strategy, value) == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + CCtxParams->cParams.strategy = (ZSTD_strategy)value; + return (nuint)CCtxParams->cParams.strategy; + case ZSTD_cParameter.ZSTD_c_contentSizeFlag: + CCtxParams->fParams.contentSizeFlag = value != 0 ? 1 : 0; + return (nuint)CCtxParams->fParams.contentSizeFlag; + case ZSTD_cParameter.ZSTD_c_checksumFlag: + CCtxParams->fParams.checksumFlag = value != 0 ? 1 : 0; + return (nuint)CCtxParams->fParams.checksumFlag; + case ZSTD_cParameter.ZSTD_c_dictIDFlag: + CCtxParams->fParams.noDictIDFlag = value == 0 ? 1 : 0; + return CCtxParams->fParams.noDictIDFlag == 0 ? 1U : 0U; + case ZSTD_cParameter.ZSTD_c_experimentalParam3: + CCtxParams->forceWindow = value != 0 ? 1 : 0; + return (nuint)CCtxParams->forceWindow; + case ZSTD_cParameter.ZSTD_c_experimentalParam4: + { + ZSTD_dictAttachPref_e pref = (ZSTD_dictAttachPref_e)value; + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam4, (int)pref) + == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->attachDictPref = pref; + return (nuint)CCtxParams->attachDictPref; + } + + case ZSTD_cParameter.ZSTD_c_experimentalParam5: + { + ZSTD_paramSwitch_e lcm = (ZSTD_paramSwitch_e)value; + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam5, (int)lcm) + == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->literalCompressionMode = lcm; + return (nuint)CCtxParams->literalCompressionMode; + } + + case ZSTD_cParameter.ZSTD_c_nbWorkers: + { + nuint err_code = ZSTD_cParam_clampBounds(param, &value); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + CCtxParams->nbWorkers = value; + return (nuint)CCtxParams->nbWorkers; + case ZSTD_cParameter.ZSTD_c_jobSize: + if (value != 0 && value < 512 * (1 << 10)) + value = 512 * (1 << 10); + + { + nuint err_code = ZSTD_cParam_clampBounds(param, &value); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(value >= 0); + CCtxParams->jobSize = (nuint)value; + return CCtxParams->jobSize; + case ZSTD_cParameter.ZSTD_c_overlapLog: + { + nuint err_code = ZSTD_cParam_clampBounds( + ZSTD_cParameter.ZSTD_c_overlapLog, + &value + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + CCtxParams->overlapLog = value; + return (nuint)CCtxParams->overlapLog; + case ZSTD_cParameter.ZSTD_c_experimentalParam1: + { + nuint err_code = ZSTD_cParam_clampBounds( + ZSTD_cParameter.ZSTD_c_overlapLog, + &value + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + CCtxParams->rsyncable = value; + return (nuint)CCtxParams->rsyncable; + case ZSTD_cParameter.ZSTD_c_experimentalParam8: + CCtxParams->enableDedicatedDictSearch = value != 0 ? 1 : 0; + return (nuint)CCtxParams->enableDedicatedDictSearch; + case ZSTD_cParameter.ZSTD_c_enableLongDistanceMatching: + if ( + ZSTD_cParam_withinBounds( + ZSTD_cParameter.ZSTD_c_enableLongDistanceMatching, + value + ) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->ldmParams.enableLdm = (ZSTD_paramSwitch_e)value; + return (nuint)CCtxParams->ldmParams.enableLdm; + case ZSTD_cParameter.ZSTD_c_ldmHashLog: + if (value != 0) + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_ldmHashLog, value) == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + CCtxParams->ldmParams.hashLog = (uint)value; + return CCtxParams->ldmParams.hashLog; + case ZSTD_cParameter.ZSTD_c_ldmMinMatch: + if (value != 0) + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_ldmMinMatch, value) == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + CCtxParams->ldmParams.minMatchLength = (uint)value; + return CCtxParams->ldmParams.minMatchLength; + case ZSTD_cParameter.ZSTD_c_ldmBucketSizeLog: + if (value != 0) + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_ldmBucketSizeLog, value) + == 0 + ) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + CCtxParams->ldmParams.bucketSizeLog = (uint)value; + return CCtxParams->ldmParams.bucketSizeLog; + case ZSTD_cParameter.ZSTD_c_ldmHashRateLog: + if (value != 0) + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_ldmHashRateLog, value) == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + CCtxParams->ldmParams.hashRateLog = (uint)value; + return CCtxParams->ldmParams.hashRateLog; + case ZSTD_cParameter.ZSTD_c_targetCBlockSize: + if (value != 0) + { + value = value > 1340 ? value : 1340; + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_targetCBlockSize, value) + == 0 + ) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + } + + CCtxParams->targetCBlockSize = (uint)value; + return CCtxParams->targetCBlockSize; + case ZSTD_cParameter.ZSTD_c_experimentalParam7: + if (value != 0) + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam7, value) + == 0 + ) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + CCtxParams->srcSizeHint = value; + return (nuint)CCtxParams->srcSizeHint; + case ZSTD_cParameter.ZSTD_c_experimentalParam9: + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam9, value) == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->inBufferMode = (ZSTD_bufferMode_e)value; + return (nuint)CCtxParams->inBufferMode; + case ZSTD_cParameter.ZSTD_c_experimentalParam10: + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam10, value) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->outBufferMode = (ZSTD_bufferMode_e)value; + return (nuint)CCtxParams->outBufferMode; + case ZSTD_cParameter.ZSTD_c_experimentalParam11: + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam11, value) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->blockDelimiters = (ZSTD_sequenceFormat_e)value; + return (nuint)CCtxParams->blockDelimiters; + case ZSTD_cParameter.ZSTD_c_experimentalParam12: + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam12, value) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->validateSequences = value; + return (nuint)CCtxParams->validateSequences; + case ZSTD_cParameter.ZSTD_c_experimentalParam13: + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam13, value) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->postBlockSplitter = (ZSTD_paramSwitch_e)value; + return (nuint)CCtxParams->postBlockSplitter; + case ZSTD_cParameter.ZSTD_c_experimentalParam20: + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam20, value) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->preBlockSplitter_level = value; + return (nuint)CCtxParams->preBlockSplitter_level; + case ZSTD_cParameter.ZSTD_c_experimentalParam14: + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam14, value) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->useRowMatchFinder = (ZSTD_paramSwitch_e)value; + return (nuint)CCtxParams->useRowMatchFinder; + case ZSTD_cParameter.ZSTD_c_experimentalParam15: + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam15, value) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->deterministicRefPrefix = !(value == 0) ? 1 : 0; + return (nuint)CCtxParams->deterministicRefPrefix; + case ZSTD_cParameter.ZSTD_c_experimentalParam16: + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam16, value) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->prefetchCDictTables = (ZSTD_paramSwitch_e)value; + return (nuint)CCtxParams->prefetchCDictTables; + case ZSTD_cParameter.ZSTD_c_experimentalParam17: + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam17, value) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->enableMatchFinderFallback = value; + return (nuint)CCtxParams->enableMatchFinderFallback; + case ZSTD_cParameter.ZSTD_c_experimentalParam18: + if (value != 0) + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam18, value) + == 0 + ) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + + assert(value >= 0); + CCtxParams->maxBlockSize = (nuint)value; + return CCtxParams->maxBlockSize; + case ZSTD_cParameter.ZSTD_c_experimentalParam19: + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam19, value) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + CCtxParams->searchForExternalRepcodes = (ZSTD_paramSwitch_e)value; + return (nuint)CCtxParams->searchForExternalRepcodes; + default: + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported)); + } + } + + /*! ZSTD_CCtx_getParameter() : + * Get the requested compression parameter value, selected by enum ZSTD_cParameter, + * and store it into int* value. + * @return : 0, or an error code (which can be tested with ZSTD_isError()). + */ + public static nuint ZSTD_CCtx_getParameter(ZSTD_CCtx_s* cctx, ZSTD_cParameter param, int* value) + { + return ZSTD_CCtxParams_getParameter(&cctx->requestedParams, param, value); + } + + /*! ZSTD_CCtxParams_getParameter() : + * Similar to ZSTD_CCtx_getParameter. + * Get the requested value of one compression parameter, selected by enum ZSTD_cParameter. + * @result : 0, or an error code (which can be tested with ZSTD_isError()). + */ + public static nuint ZSTD_CCtxParams_getParameter( + ZSTD_CCtx_params_s* CCtxParams, + ZSTD_cParameter param, + int* value + ) + { + switch (param) + { + case ZSTD_cParameter.ZSTD_c_experimentalParam2: + *value = (int)CCtxParams->format; + break; + case ZSTD_cParameter.ZSTD_c_compressionLevel: + *value = CCtxParams->compressionLevel; + break; + case ZSTD_cParameter.ZSTD_c_windowLog: + *value = (int)CCtxParams->cParams.windowLog; + break; + case ZSTD_cParameter.ZSTD_c_hashLog: + *value = (int)CCtxParams->cParams.hashLog; + break; + case ZSTD_cParameter.ZSTD_c_chainLog: + *value = (int)CCtxParams->cParams.chainLog; + break; + case ZSTD_cParameter.ZSTD_c_searchLog: + *value = (int)CCtxParams->cParams.searchLog; + break; + case ZSTD_cParameter.ZSTD_c_minMatch: + *value = (int)CCtxParams->cParams.minMatch; + break; + case ZSTD_cParameter.ZSTD_c_targetLength: + *value = (int)CCtxParams->cParams.targetLength; + break; + case ZSTD_cParameter.ZSTD_c_strategy: + *value = (int)CCtxParams->cParams.strategy; + break; + case ZSTD_cParameter.ZSTD_c_contentSizeFlag: + *value = CCtxParams->fParams.contentSizeFlag; + break; + case ZSTD_cParameter.ZSTD_c_checksumFlag: + *value = CCtxParams->fParams.checksumFlag; + break; + case ZSTD_cParameter.ZSTD_c_dictIDFlag: + *value = CCtxParams->fParams.noDictIDFlag == 0 ? 1 : 0; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam3: + *value = CCtxParams->forceWindow; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam4: + *value = (int)CCtxParams->attachDictPref; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam5: + *value = (int)CCtxParams->literalCompressionMode; + break; + case ZSTD_cParameter.ZSTD_c_nbWorkers: + *value = CCtxParams->nbWorkers; + break; + case ZSTD_cParameter.ZSTD_c_jobSize: + assert(CCtxParams->jobSize <= 2147483647); + *value = (int)CCtxParams->jobSize; + break; + case ZSTD_cParameter.ZSTD_c_overlapLog: + *value = CCtxParams->overlapLog; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam1: + *value = CCtxParams->rsyncable; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam8: + *value = CCtxParams->enableDedicatedDictSearch; + break; + case ZSTD_cParameter.ZSTD_c_enableLongDistanceMatching: + *value = (int)CCtxParams->ldmParams.enableLdm; + break; + case ZSTD_cParameter.ZSTD_c_ldmHashLog: + *value = (int)CCtxParams->ldmParams.hashLog; + break; + case ZSTD_cParameter.ZSTD_c_ldmMinMatch: + *value = (int)CCtxParams->ldmParams.minMatchLength; + break; + case ZSTD_cParameter.ZSTD_c_ldmBucketSizeLog: + *value = (int)CCtxParams->ldmParams.bucketSizeLog; + break; + case ZSTD_cParameter.ZSTD_c_ldmHashRateLog: + *value = (int)CCtxParams->ldmParams.hashRateLog; + break; + case ZSTD_cParameter.ZSTD_c_targetCBlockSize: + *value = (int)CCtxParams->targetCBlockSize; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam7: + *value = CCtxParams->srcSizeHint; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam9: + *value = (int)CCtxParams->inBufferMode; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam10: + *value = (int)CCtxParams->outBufferMode; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam11: + *value = (int)CCtxParams->blockDelimiters; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam12: + *value = CCtxParams->validateSequences; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam13: + *value = (int)CCtxParams->postBlockSplitter; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam20: + *value = CCtxParams->preBlockSplitter_level; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam14: + *value = (int)CCtxParams->useRowMatchFinder; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam15: + *value = CCtxParams->deterministicRefPrefix; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam16: + *value = (int)CCtxParams->prefetchCDictTables; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam17: + *value = CCtxParams->enableMatchFinderFallback; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam18: + *value = (int)CCtxParams->maxBlockSize; + break; + case ZSTD_cParameter.ZSTD_c_experimentalParam19: + *value = (int)CCtxParams->searchForExternalRepcodes; + break; + default: + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported)); + } + + return 0; + } + + /** ZSTD_CCtx_setParametersUsingCCtxParams() : + * just applies `params` into `cctx` + * no action is performed, parameters are merely stored. + * If ZSTDMT is enabled, parameters are pushed to cctx->mtctx. + * This is possible even if a compression is ongoing. + * In which case, new parameters will be applied on the fly, starting with next compression job. + */ + public static nuint ZSTD_CCtx_setParametersUsingCCtxParams( + ZSTD_CCtx_s* cctx, + ZSTD_CCtx_params_s* @params + ) + { + if (cctx->streamStage != ZSTD_cStreamStage.zcss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + if (cctx->cdict != null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + cctx->requestedParams = *@params; + return 0; + } + + /*! ZSTD_CCtx_setCParams() : + * Set all parameters provided within @p cparams into the working @p cctx. + * Note : if modifying parameters during compression (MT mode only), + * note that changes to the .windowLog parameter will be ignored. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + * On failure, no parameters are updated. + */ + public static nuint ZSTD_CCtx_setCParams(ZSTD_CCtx_s* cctx, ZSTD_compressionParameters cparams) + { + { + /* only update if all parameters are valid */ + nuint err_code = ZSTD_checkCParams(cparams); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + cctx, + ZSTD_cParameter.ZSTD_c_windowLog, + (int)cparams.windowLog + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + cctx, + ZSTD_cParameter.ZSTD_c_chainLog, + (int)cparams.chainLog + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + cctx, + ZSTD_cParameter.ZSTD_c_hashLog, + (int)cparams.hashLog + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + cctx, + ZSTD_cParameter.ZSTD_c_searchLog, + (int)cparams.searchLog + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + cctx, + ZSTD_cParameter.ZSTD_c_minMatch, + (int)cparams.minMatch + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + cctx, + ZSTD_cParameter.ZSTD_c_targetLength, + (int)cparams.targetLength + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + cctx, + ZSTD_cParameter.ZSTD_c_strategy, + (int)cparams.strategy + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /*! ZSTD_CCtx_setFParams() : + * Set all parameters provided within @p fparams into the working @p cctx. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + */ + public static nuint ZSTD_CCtx_setFParams(ZSTD_CCtx_s* cctx, ZSTD_frameParameters fparams) + { + { + nuint err_code = ZSTD_CCtx_setParameter( + cctx, + ZSTD_cParameter.ZSTD_c_contentSizeFlag, + fparams.contentSizeFlag != 0 ? 1 : 0 + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + cctx, + ZSTD_cParameter.ZSTD_c_checksumFlag, + fparams.checksumFlag != 0 ? 1 : 0 + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + cctx, + ZSTD_cParameter.ZSTD_c_dictIDFlag, + fparams.noDictIDFlag == 0 ? 1 : 0 + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /*! ZSTD_CCtx_setParams() : + * Set all parameters provided within @p params into the working @p cctx. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + */ + public static nuint ZSTD_CCtx_setParams(ZSTD_CCtx_s* cctx, ZSTD_parameters @params) + { + { + /* First check cParams, because we want to update all or none. */ + nuint err_code = ZSTD_checkCParams(@params.cParams); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + /* Next set fParams, because this could fail if the cctx isn't in init stage. */ + nuint err_code = ZSTD_CCtx_setFParams(cctx, @params.fParams); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + /* Finally set cParams, which should succeed. */ + nuint err_code = ZSTD_CCtx_setCParams(cctx, @params.cParams); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /*! ZSTD_CCtx_setPledgedSrcSize() : + * Total input data size to be compressed as a single frame. + * Value will be written in frame header, unless if explicitly forbidden using ZSTD_c_contentSizeFlag. + * This value will also be controlled at end of frame, and trigger an error if not respected. + * @result : 0, or an error code (which can be tested with ZSTD_isError()). + * Note 1 : pledgedSrcSize==0 actually means zero, aka an empty frame. + * In order to mean "unknown content size", pass constant ZSTD_CONTENTSIZE_UNKNOWN. + * ZSTD_CONTENTSIZE_UNKNOWN is default value for any new frame. + * Note 2 : pledgedSrcSize is only valid once, for the next frame. + * It's discarded at the end of the frame, and replaced by ZSTD_CONTENTSIZE_UNKNOWN. + * Note 3 : Whenever all input data is provided and consumed in a single round, + * for example with ZSTD_compress2(), + * or invoking immediately ZSTD_compressStream2(,,,ZSTD_e_end), + * this value is automatically overridden by srcSize instead. + */ + public static nuint ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx_s* cctx, ulong pledgedSrcSize) + { + if (cctx->streamStage != ZSTD_cStreamStage.zcss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + cctx->pledgedSrcSizePlusOne = pledgedSrcSize + 1; + return 0; + } + + /** + * Initializes the local dictionary using requested parameters. + * NOTE: Initialization does not employ the pledged src size, + * because the dictionary may be used for multiple compressions. + */ + private static nuint ZSTD_initLocalDict(ZSTD_CCtx_s* cctx) + { + ZSTD_localDict* dl = &cctx->localDict; + if (dl->dict == null) + { + assert(dl->dictBuffer == null); + assert(dl->cdict == null); + assert(dl->dictSize == 0); + return 0; + } + + if (dl->cdict != null) + { + assert(cctx->cdict == dl->cdict); + return 0; + } + + assert(dl->dictSize > 0); + assert(cctx->cdict == null); + assert(cctx->prefixDict.dict == null); + dl->cdict = ZSTD_createCDict_advanced2( + dl->dict, + dl->dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef, + dl->dictContentType, + &cctx->requestedParams, + cctx->customMem + ); + if (dl->cdict == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + cctx->cdict = dl->cdict; + return 0; + } + + /*! ZSTD_CCtx_loadDictionary_advanced() : + * Same as ZSTD_CCtx_loadDictionary(), but gives finer control over + * how to load the dictionary (by copy ? by reference ?) + * and how to interpret it (automatic ? force raw mode ? full mode only ?) */ + public static nuint ZSTD_CCtx_loadDictionary_advanced( + ZSTD_CCtx_s* cctx, + void* dict, + nuint dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictContentType_e dictContentType + ) + { + if (cctx->streamStage != ZSTD_cStreamStage.zcss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + ZSTD_clearAllDicts(cctx); + if (dict == null || dictSize == 0) + return 0; + if (dictLoadMethod == ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef) + { + cctx->localDict.dict = dict; + } + else + { + /* copy dictionary content inside CCtx to own its lifetime */ + void* dictBuffer; + if (cctx->staticSize != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + dictBuffer = ZSTD_customMalloc(dictSize, cctx->customMem); + if (dictBuffer == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + memcpy(dictBuffer, dict, (uint)dictSize); + cctx->localDict.dictBuffer = dictBuffer; + cctx->localDict.dict = dictBuffer; + } + + cctx->localDict.dictSize = dictSize; + cctx->localDict.dictContentType = dictContentType; + return 0; + } + + /*! ZSTD_CCtx_loadDictionary_byReference() : + * Same as ZSTD_CCtx_loadDictionary(), but dictionary content is referenced, instead of being copied into CCtx. + * It saves some memory, but also requires that `dict` outlives its usage within `cctx` */ + public static nuint ZSTD_CCtx_loadDictionary_byReference( + ZSTD_CCtx_s* cctx, + void* dict, + nuint dictSize + ) + { + return ZSTD_CCtx_loadDictionary_advanced( + cctx, + dict, + dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef, + ZSTD_dictContentType_e.ZSTD_dct_auto + ); + } + + /*! ZSTD_CCtx_loadDictionary() : Requires v1.4.0+ + * Create an internal CDict from `dict` buffer. + * Decompression will have to use same dictionary. + * @result : 0, or an error code (which can be tested with ZSTD_isError()). + * Special: Loading a NULL (or 0-size) dictionary invalidates previous dictionary, + * meaning "return to no-dictionary mode". + * Note 1 : Dictionary is sticky, it will be used for all future compressed frames, + * until parameters are reset, a new dictionary is loaded, or the dictionary + * is explicitly invalidated by loading a NULL dictionary. + * Note 2 : Loading a dictionary involves building tables. + * It's also a CPU consuming operation, with non-negligible impact on latency. + * Tables are dependent on compression parameters, and for this reason, + * compression parameters can no longer be changed after loading a dictionary. + * Note 3 :`dict` content will be copied internally. + * Use experimental ZSTD_CCtx_loadDictionary_byReference() to reference content instead. + * In such a case, dictionary buffer must outlive its users. + * Note 4 : Use ZSTD_CCtx_loadDictionary_advanced() + * to precisely select how dictionary content must be interpreted. + * Note 5 : This method does not benefit from LDM (long distance mode). + * If you want to employ LDM on some large dictionary content, + * prefer employing ZSTD_CCtx_refPrefix() described below. + */ + public static nuint ZSTD_CCtx_loadDictionary(ZSTD_CCtx_s* cctx, void* dict, nuint dictSize) + { + return ZSTD_CCtx_loadDictionary_advanced( + cctx, + dict, + dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byCopy, + ZSTD_dictContentType_e.ZSTD_dct_auto + ); + } + + /*! ZSTD_CCtx_refCDict() : Requires v1.4.0+ + * Reference a prepared dictionary, to be used for all future compressed frames. + * Note that compression parameters are enforced from within CDict, + * and supersede any compression parameter previously set within CCtx. + * The parameters ignored are labelled as "superseded-by-cdict" in the ZSTD_cParameter enum docs. + * The ignored parameters will be used again if the CCtx is returned to no-dictionary mode. + * The dictionary will remain valid for future compressed frames using same CCtx. + * @result : 0, or an error code (which can be tested with ZSTD_isError()). + * Special : Referencing a NULL CDict means "return to no-dictionary mode". + * Note 1 : Currently, only one dictionary can be managed. + * Referencing a new dictionary effectively "discards" any previous one. + * Note 2 : CDict is just referenced, its lifetime must outlive its usage within CCtx. */ + public static nuint ZSTD_CCtx_refCDict(ZSTD_CCtx_s* cctx, ZSTD_CDict_s* cdict) + { + if (cctx->streamStage != ZSTD_cStreamStage.zcss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + ZSTD_clearAllDicts(cctx); + cctx->cdict = cdict; + return 0; + } + + public static nuint ZSTD_CCtx_refThreadPool(ZSTD_CCtx_s* cctx, void* pool) + { + if (cctx->streamStage != ZSTD_cStreamStage.zcss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + cctx->pool = pool; + return 0; + } + + /*! ZSTD_CCtx_refPrefix() : Requires v1.4.0+ + * Reference a prefix (single-usage dictionary) for next compressed frame. + * A prefix is **only used once**. Tables are discarded at end of frame (ZSTD_e_end). + * Decompression will need same prefix to properly regenerate data. + * Compressing with a prefix is similar in outcome as performing a diff and compressing it, + * but performs much faster, especially during decompression (compression speed is tunable with compression level). + * This method is compatible with LDM (long distance mode). + * @result : 0, or an error code (which can be tested with ZSTD_isError()). + * Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary + * Note 1 : Prefix buffer is referenced. It **must** outlive compression. + * Its content must remain unmodified during compression. + * Note 2 : If the intention is to diff some large src data blob with some prior version of itself, + * ensure that the window size is large enough to contain the entire source. + * See ZSTD_c_windowLog. + * Note 3 : Referencing a prefix involves building tables, which are dependent on compression parameters. + * It's a CPU consuming operation, with non-negligible impact on latency. + * If there is a need to use the same prefix multiple times, consider loadDictionary instead. + * Note 4 : By default, the prefix is interpreted as raw content (ZSTD_dct_rawContent). + * Use experimental ZSTD_CCtx_refPrefix_advanced() to alter dictionary interpretation. */ + public static nuint ZSTD_CCtx_refPrefix(ZSTD_CCtx_s* cctx, void* prefix, nuint prefixSize) + { + return ZSTD_CCtx_refPrefix_advanced( + cctx, + prefix, + prefixSize, + ZSTD_dictContentType_e.ZSTD_dct_rawContent + ); + } + + /*! ZSTD_CCtx_refPrefix_advanced() : + * Same as ZSTD_CCtx_refPrefix(), but gives finer control over + * how to interpret prefix content (automatic ? force raw mode (default) ? full mode only ?) */ + public static nuint ZSTD_CCtx_refPrefix_advanced( + ZSTD_CCtx_s* cctx, + void* prefix, + nuint prefixSize, + ZSTD_dictContentType_e dictContentType + ) + { + if (cctx->streamStage != ZSTD_cStreamStage.zcss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + ZSTD_clearAllDicts(cctx); + if (prefix != null && prefixSize > 0) + { + cctx->prefixDict.dict = prefix; + cctx->prefixDict.dictSize = prefixSize; + cctx->prefixDict.dictContentType = dictContentType; + } + + return 0; + } + + /*! ZSTD_CCtx_reset() : + * Also dumps dictionary */ + public static nuint ZSTD_CCtx_reset(ZSTD_CCtx_s* cctx, ZSTD_ResetDirective reset) + { + if ( + reset == ZSTD_ResetDirective.ZSTD_reset_session_only + || reset == ZSTD_ResetDirective.ZSTD_reset_session_and_parameters + ) + { + cctx->streamStage = ZSTD_cStreamStage.zcss_init; + cctx->pledgedSrcSizePlusOne = 0; + } + + if ( + reset == ZSTD_ResetDirective.ZSTD_reset_parameters + || reset == ZSTD_ResetDirective.ZSTD_reset_session_and_parameters + ) + { + if (cctx->streamStage != ZSTD_cStreamStage.zcss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + ZSTD_clearAllDicts(cctx); + return ZSTD_CCtxParams_reset(&cctx->requestedParams); + } + + return 0; + } + + /** ZSTD_checkCParams() : + control CParam values remain within authorized range. + @return : 0, or an error code if one value is beyond authorized range */ + public static nuint ZSTD_checkCParams(ZSTD_compressionParameters cParams) + { + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_windowLog, (int)cParams.windowLog) == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_chainLog, (int)cParams.chainLog) == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_hashLog, (int)cParams.hashLog) == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_searchLog, (int)cParams.searchLog) == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_minMatch, (int)cParams.minMatch) == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + if ( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_targetLength, (int)cParams.targetLength) + == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + if (ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_strategy, (int)cParams.strategy) == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + return 0; + } + + /** ZSTD_clampCParams() : + * make CParam values within valid range. + * @return : valid CParams */ + private static ZSTD_compressionParameters ZSTD_clampCParams(ZSTD_compressionParameters cParams) + { + { + ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_cParameter.ZSTD_c_windowLog); + if ((int)cParams.windowLog < bounds.lowerBound) + cParams.windowLog = (uint)bounds.lowerBound; + else if ((int)cParams.windowLog > bounds.upperBound) + cParams.windowLog = (uint)bounds.upperBound; + } + + { + ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_cParameter.ZSTD_c_chainLog); + if ((int)cParams.chainLog < bounds.lowerBound) + cParams.chainLog = (uint)bounds.lowerBound; + else if ((int)cParams.chainLog > bounds.upperBound) + cParams.chainLog = (uint)bounds.upperBound; + } + + { + ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_cParameter.ZSTD_c_hashLog); + if ((int)cParams.hashLog < bounds.lowerBound) + cParams.hashLog = (uint)bounds.lowerBound; + else if ((int)cParams.hashLog > bounds.upperBound) + cParams.hashLog = (uint)bounds.upperBound; + } + + { + ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_cParameter.ZSTD_c_searchLog); + if ((int)cParams.searchLog < bounds.lowerBound) + cParams.searchLog = (uint)bounds.lowerBound; + else if ((int)cParams.searchLog > bounds.upperBound) + cParams.searchLog = (uint)bounds.upperBound; + } + + { + ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_cParameter.ZSTD_c_minMatch); + if ((int)cParams.minMatch < bounds.lowerBound) + cParams.minMatch = (uint)bounds.lowerBound; + else if ((int)cParams.minMatch > bounds.upperBound) + cParams.minMatch = (uint)bounds.upperBound; + } + + { + ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_cParameter.ZSTD_c_targetLength); + if ((int)cParams.targetLength < bounds.lowerBound) + cParams.targetLength = (uint)bounds.lowerBound; + else if ((int)cParams.targetLength > bounds.upperBound) + cParams.targetLength = (uint)bounds.upperBound; + } + + { + ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_cParameter.ZSTD_c_strategy); + if ((int)cParams.strategy < bounds.lowerBound) + cParams.strategy = (ZSTD_strategy)bounds.lowerBound; + else if ((int)cParams.strategy > bounds.upperBound) + cParams.strategy = (ZSTD_strategy)bounds.upperBound; + } + + return cParams; + } + + /** ZSTD_cycleLog() : + * condition for correct operation : hashLog > 1 */ + private static uint ZSTD_cycleLog(uint hashLog, ZSTD_strategy strat) + { + uint btScale = (uint)strat >= (uint)ZSTD_strategy.ZSTD_btlazy2 ? 1U : 0U; + return hashLog - btScale; + } + + /** ZSTD_dictAndWindowLog() : + * Returns an adjusted window log that is large enough to fit the source and the dictionary. + * The zstd format says that the entire dictionary is valid if one byte of the dictionary + * is within the window. So the hashLog and chainLog should be large enough to reference both + * the dictionary and the window. So we must use this adjusted dictAndWindowLog when downsizing + * the hashLog and windowLog. + * NOTE: srcSize must not be ZSTD_CONTENTSIZE_UNKNOWN. + */ + private static uint ZSTD_dictAndWindowLog(uint windowLog, ulong srcSize, ulong dictSize) + { + ulong maxWindowSize = 1UL << (sizeof(nuint) == 4 ? 30 : 31); + if (dictSize == 0) + { + return windowLog; + } + + assert(windowLog <= (uint)(sizeof(nuint) == 4 ? 30 : 31)); + assert(srcSize != unchecked(0UL - 1)); + { + ulong windowSize = 1UL << (int)windowLog; + ulong dictAndWindowSize = dictSize + windowSize; + if (windowSize >= dictSize + srcSize) + { + return windowLog; + } + else if (dictAndWindowSize >= maxWindowSize) + { + return (uint)(sizeof(nuint) == 4 ? 30 : 31); + } + else + { + return ZSTD_highbit32((uint)dictAndWindowSize - 1) + 1; + } + } + } + + /** ZSTD_adjustCParams_internal() : + * optimize `cPar` for a specified input (`srcSize` and `dictSize`). + * mostly downsize to reduce memory consumption and initialization latency. + * `srcSize` can be ZSTD_CONTENTSIZE_UNKNOWN when not known. + * `mode` is the mode for parameter adjustment. See docs for `ZSTD_CParamMode_e`. + * note : `srcSize==0` means 0! + * condition : cPar is presumed validated (can be checked using ZSTD_checkCParams()). */ + private static ZSTD_compressionParameters ZSTD_adjustCParams_internal( + ZSTD_compressionParameters cPar, + ulong srcSize, + nuint dictSize, + ZSTD_CParamMode_e mode, + ZSTD_paramSwitch_e useRowMatchFinder + ) + { + /* (1<<9) + 1 */ + const ulong minSrcSize = 513; + ulong maxWindowResize = 1UL << (sizeof(nuint) == 4 ? 30 : 31) - 1; + assert(ZSTD_checkCParams(cPar) == 0); + switch (mode) + { + case ZSTD_CParamMode_e.ZSTD_cpm_unknown: + case ZSTD_CParamMode_e.ZSTD_cpm_noAttachDict: + break; + case ZSTD_CParamMode_e.ZSTD_cpm_createCDict: + if (dictSize != 0 && srcSize == unchecked(0UL - 1)) + srcSize = minSrcSize; + break; + case ZSTD_CParamMode_e.ZSTD_cpm_attachDict: + dictSize = 0; + break; + default: + assert(0 != 0); + break; + } + + if (srcSize <= maxWindowResize && dictSize <= maxWindowResize) + { + uint tSize = (uint)(srcSize + dictSize); + const uint hashSizeMin = 1 << 6; + uint srcLog = tSize < hashSizeMin ? 6 : ZSTD_highbit32(tSize - 1) + 1; + if (cPar.windowLog > srcLog) + cPar.windowLog = srcLog; + } + + if (srcSize != unchecked(0UL - 1)) + { + uint dictAndWindowLog = ZSTD_dictAndWindowLog(cPar.windowLog, srcSize, dictSize); + uint cycleLog = ZSTD_cycleLog(cPar.chainLog, cPar.strategy); + if (cPar.hashLog > dictAndWindowLog + 1) + cPar.hashLog = dictAndWindowLog + 1; + if (cycleLog > dictAndWindowLog) + cPar.chainLog -= cycleLog - dictAndWindowLog; + } + + if (cPar.windowLog < 10) + cPar.windowLog = 10; + if ( + mode == ZSTD_CParamMode_e.ZSTD_cpm_createCDict + && ZSTD_CDictIndicesAreTagged(&cPar) != 0 + ) + { + const uint maxShortCacheHashLog = 32 - 8; + if (cPar.hashLog > maxShortCacheHashLog) + { + cPar.hashLog = maxShortCacheHashLog; + } + + if (cPar.chainLog > maxShortCacheHashLog) + { + cPar.chainLog = maxShortCacheHashLog; + } + } + + if (useRowMatchFinder == ZSTD_paramSwitch_e.ZSTD_ps_auto) + useRowMatchFinder = ZSTD_paramSwitch_e.ZSTD_ps_enable; + if (ZSTD_rowMatchFinderUsed(cPar.strategy, useRowMatchFinder) != 0) + { + /* Switch to 32-entry rows if searchLog is 5 (or more) */ + uint rowLog = + cPar.searchLog <= 4 ? 4 + : cPar.searchLog <= 6 ? cPar.searchLog + : 6; + const uint maxRowHashLog = 32 - 8; + uint maxHashLog = maxRowHashLog + rowLog; + assert(cPar.hashLog >= rowLog); + if (cPar.hashLog > maxHashLog) + { + cPar.hashLog = maxHashLog; + } + } + + return cPar; + } + + /*! ZSTD_adjustCParams() : + * optimize params for a given `srcSize` and `dictSize`. + * `srcSize` can be unknown, in which case use ZSTD_CONTENTSIZE_UNKNOWN. + * `dictSize` must be `0` when there is no dictionary. + * cPar can be invalid : all parameters will be clamped within valid range in the @return struct. + * This function never fails (wide contract) */ + public static ZSTD_compressionParameters ZSTD_adjustCParams( + ZSTD_compressionParameters cPar, + ulong srcSize, + nuint dictSize + ) + { + cPar = ZSTD_clampCParams(cPar); + if (srcSize == 0) + srcSize = unchecked(0UL - 1); + return ZSTD_adjustCParams_internal( + cPar, + srcSize, + dictSize, + ZSTD_CParamMode_e.ZSTD_cpm_unknown, + ZSTD_paramSwitch_e.ZSTD_ps_auto + ); + } + + private static void ZSTD_overrideCParams( + ZSTD_compressionParameters* cParams, + ZSTD_compressionParameters* overrides + ) + { + if (overrides->windowLog != 0) + cParams->windowLog = overrides->windowLog; + if (overrides->hashLog != 0) + cParams->hashLog = overrides->hashLog; + if (overrides->chainLog != 0) + cParams->chainLog = overrides->chainLog; + if (overrides->searchLog != 0) + cParams->searchLog = overrides->searchLog; + if (overrides->minMatch != 0) + cParams->minMatch = overrides->minMatch; + if (overrides->targetLength != 0) + cParams->targetLength = overrides->targetLength; + if (overrides->strategy != default) + cParams->strategy = overrides->strategy; + } + + /* ZSTD_getCParamsFromCCtxParams() : + * cParams are built depending on compressionLevel, src size hints, + * LDM and manually set compression parameters. + * Note: srcSizeHint == 0 means 0! + */ + private static ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams( + ZSTD_CCtx_params_s* CCtxParams, + ulong srcSizeHint, + nuint dictSize, + ZSTD_CParamMode_e mode + ) + { + ZSTD_compressionParameters cParams; + if (srcSizeHint == unchecked(0UL - 1) && CCtxParams->srcSizeHint > 0) + { + assert(CCtxParams->srcSizeHint >= 0); + srcSizeHint = (ulong)CCtxParams->srcSizeHint; + } + + cParams = ZSTD_getCParams_internal( + CCtxParams->compressionLevel, + srcSizeHint, + dictSize, + mode + ); + if (CCtxParams->ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + cParams.windowLog = 27; + ZSTD_overrideCParams(&cParams, &CCtxParams->cParams); + assert(ZSTD_checkCParams(cParams) == 0); + return ZSTD_adjustCParams_internal( + cParams, + srcSizeHint, + dictSize, + mode, + CCtxParams->useRowMatchFinder + ); + } + + private static nuint ZSTD_sizeof_matchState( + ZSTD_compressionParameters* cParams, + ZSTD_paramSwitch_e useRowMatchFinder, + int enableDedicatedDictSearch, + uint forCCtx + ) + { + /* chain table size should be 0 for fast or row-hash strategies */ + nuint chainSize = + ZSTD_allocateChainTable( + cParams->strategy, + useRowMatchFinder, + enableDedicatedDictSearch != 0 && forCCtx == 0 ? 1U : 0U + ) != 0 + ? (nuint)1 << (int)cParams->chainLog + : 0; + nuint hSize = (nuint)1 << (int)cParams->hashLog; + uint hashLog3 = + forCCtx != 0 && cParams->minMatch == 3 + ? 17 < cParams->windowLog + ? 17 + : cParams->windowLog + : 0; + nuint h3Size = hashLog3 != 0 ? (nuint)1 << (int)hashLog3 : 0; + /* We don't use ZSTD_cwksp_alloc_size() here because the tables aren't + * surrounded by redzones in ASAN. */ + nuint tableSpace = chainSize * sizeof(uint) + hSize * sizeof(uint) + h3Size * sizeof(uint); + nuint optPotentialSpace = + ZSTD_cwksp_aligned64_alloc_size((52 + 1) * sizeof(uint)) + + ZSTD_cwksp_aligned64_alloc_size((35 + 1) * sizeof(uint)) + + ZSTD_cwksp_aligned64_alloc_size((31 + 1) * sizeof(uint)) + + ZSTD_cwksp_aligned64_alloc_size((1 << 8) * sizeof(uint)) + + ZSTD_cwksp_aligned64_alloc_size((nuint)(((1 << 12) + 3) * sizeof(ZSTD_match_t))) + + ZSTD_cwksp_aligned64_alloc_size((nuint)(((1 << 12) + 3) * sizeof(ZSTD_optimal_t))); + nuint lazyAdditionalSpace = + ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder) != 0 + ? ZSTD_cwksp_aligned64_alloc_size(hSize) + : 0; + nuint optSpace = + forCCtx != 0 && cParams->strategy >= ZSTD_strategy.ZSTD_btopt ? optPotentialSpace : 0; + nuint slackSpace = ZSTD_cwksp_slack_space_required(); + assert(useRowMatchFinder != ZSTD_paramSwitch_e.ZSTD_ps_auto); + return tableSpace + optSpace + slackSpace + lazyAdditionalSpace; + } + + /* Helper function for calculating memory requirements. + * Gives a tighter bound than ZSTD_sequenceBound() by taking minMatch into account. */ + private static nuint ZSTD_maxNbSeq(nuint blockSize, uint minMatch, int useSequenceProducer) + { + uint divider = (uint)(minMatch == 3 || useSequenceProducer != 0 ? 3 : 4); + return blockSize / divider; + } + + private static nuint ZSTD_estimateCCtxSize_usingCCtxParams_internal( + ZSTD_compressionParameters* cParams, + ldmParams_t* ldmParams, + int isStatic, + ZSTD_paramSwitch_e useRowMatchFinder, + nuint buffInSize, + nuint buffOutSize, + ulong pledgedSrcSize, + int useSequenceProducer, + nuint maxBlockSize + ) + { + nuint windowSize = (nuint)( + 1UL << (int)cParams->windowLog <= 1UL ? 1UL + : 1UL << (int)cParams->windowLog <= pledgedSrcSize ? 1UL << (int)cParams->windowLog + : pledgedSrcSize + ); + nuint blockSize = + ZSTD_resolveMaxBlockSize(maxBlockSize) < windowSize + ? ZSTD_resolveMaxBlockSize(maxBlockSize) + : windowSize; + nuint maxNbSeq = ZSTD_maxNbSeq(blockSize, cParams->minMatch, useSequenceProducer); + nuint tokenSpace = + ZSTD_cwksp_alloc_size(32 + blockSize) + + ZSTD_cwksp_aligned64_alloc_size(maxNbSeq * (nuint)sizeof(SeqDef_s)) + + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(byte)); + nuint tmpWorkSpace = ZSTD_cwksp_alloc_size( + (8 << 10) + 512 + sizeof(uint) * (52 + 2) > 8208 + ? (8 << 10) + 512 + sizeof(uint) * (52 + 2) + : 8208 + ); + nuint blockStateSpace = + 2 * ZSTD_cwksp_alloc_size((nuint)sizeof(ZSTD_compressedBlockState_t)); + /* enableDedicatedDictSearch */ + nuint matchStateSize = ZSTD_sizeof_matchState(cParams, useRowMatchFinder, 0, 1); + nuint ldmSpace = ZSTD_ldm_getTableSize(*ldmParams); + nuint maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(*ldmParams, blockSize); + nuint ldmSeqSpace = + ldmParams->enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable + ? ZSTD_cwksp_aligned64_alloc_size(maxNbLdmSeq * (nuint)sizeof(rawSeq)) + : 0; + nuint bufferSpace = ZSTD_cwksp_alloc_size(buffInSize) + ZSTD_cwksp_alloc_size(buffOutSize); + nuint cctxSpace = isStatic != 0 ? ZSTD_cwksp_alloc_size((nuint)sizeof(ZSTD_CCtx_s)) : 0; + nuint maxNbExternalSeq = ZSTD_sequenceBound(blockSize); + nuint externalSeqSpace = + useSequenceProducer != 0 + ? ZSTD_cwksp_aligned64_alloc_size(maxNbExternalSeq * (nuint)sizeof(ZSTD_Sequence)) + : 0; + nuint neededSpace = + cctxSpace + + tmpWorkSpace + + blockStateSpace + + ldmSpace + + ldmSeqSpace + + matchStateSize + + tokenSpace + + bufferSpace + + externalSeqSpace; + return neededSpace; + } + + public static nuint ZSTD_estimateCCtxSize_usingCCtxParams(ZSTD_CCtx_params_s* @params) + { + ZSTD_compressionParameters cParams = ZSTD_getCParamsFromCCtxParams( + @params, + unchecked(0UL - 1), + 0, + ZSTD_CParamMode_e.ZSTD_cpm_noAttachDict + ); + ZSTD_paramSwitch_e useRowMatchFinder = ZSTD_resolveRowMatchFinderMode( + @params->useRowMatchFinder, + &cParams + ); + if (@params->nbWorkers > 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + + return ZSTD_estimateCCtxSize_usingCCtxParams_internal( + &cParams, + &@params->ldmParams, + 1, + useRowMatchFinder, + 0, + 0, + unchecked(0UL - 1), + ZSTD_hasExtSeqProd(@params), + @params->maxBlockSize + ); + } + + public static nuint ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams) + { + ZSTD_CCtx_params_s initialParams = ZSTD_makeCCtxParamsFromCParams(cParams); + if (ZSTD_rowMatchFinderSupported(cParams.strategy) != 0) + { + /* Pick bigger of not using and using row-based matchfinder for greedy and lazy strategies */ + nuint noRowCCtxSize; + nuint rowCCtxSize; + initialParams.useRowMatchFinder = ZSTD_paramSwitch_e.ZSTD_ps_disable; + noRowCCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(&initialParams); + initialParams.useRowMatchFinder = ZSTD_paramSwitch_e.ZSTD_ps_enable; + rowCCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(&initialParams); + return noRowCCtxSize > rowCCtxSize ? noRowCCtxSize : rowCCtxSize; + } + else + { + return ZSTD_estimateCCtxSize_usingCCtxParams(&initialParams); + } + } + +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_srcSizeTiers => + new ulong[4] { 16 * (1 << 10), 128 * (1 << 10), 256 * (1 << 10), unchecked(0UL - 1) }; + private static ulong* srcSizeTiers => + (ulong*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_srcSizeTiers) + ); +#else + + private static readonly ulong* srcSizeTiers = GetArrayPointer( + new ulong[4] + { + (ulong)(16 * (1 << 10)), + (ulong)(128 * (1 << 10)), + (ulong)(256 * (1 << 10)), + (unchecked(0UL - 1)), + } + ); +#endif + + private static nuint ZSTD_estimateCCtxSize_internal(int compressionLevel) + { + int tier = 0; + nuint largestSize = 0; + for (; tier < 4; ++tier) + { + /* Choose the set of cParams for a given level across all srcSizes that give the largest cctxSize */ + ZSTD_compressionParameters cParams = ZSTD_getCParams_internal( + compressionLevel, + srcSizeTiers[tier], + 0, + ZSTD_CParamMode_e.ZSTD_cpm_noAttachDict + ); + largestSize = + ZSTD_estimateCCtxSize_usingCParams(cParams) > largestSize + ? ZSTD_estimateCCtxSize_usingCParams(cParams) + : largestSize; + } + + return largestSize; + } + + /*! ZSTD_estimate*() : + * These functions make it possible to estimate memory usage + * of a future {D,C}Ctx, before its creation. + * This is useful in combination with ZSTD_initStatic(), + * which makes it possible to employ a static buffer for ZSTD_CCtx* state. + * + * ZSTD_estimateCCtxSize() will provide a memory budget large enough + * to compress data of any size using one-shot compression ZSTD_compressCCtx() or ZSTD_compress2() + * associated with any compression level up to max specified one. + * The estimate will assume the input may be arbitrarily large, + * which is the worst case. + * + * Note that the size estimation is specific for one-shot compression, + * it is not valid for streaming (see ZSTD_estimateCStreamSize*()) + * nor other potential ways of using a ZSTD_CCtx* state. + * + * When srcSize can be bound by a known and rather "small" value, + * this knowledge can be used to provide a tighter budget estimation + * because the ZSTD_CCtx* state will need less memory for small inputs. + * This tighter estimation can be provided by employing more advanced functions + * ZSTD_estimateCCtxSize_usingCParams(), which can be used in tandem with ZSTD_getCParams(), + * and ZSTD_estimateCCtxSize_usingCCtxParams(), which can be used in tandem with ZSTD_CCtxParams_setParameter(). + * Both can be used to estimate memory using custom compression parameters and arbitrary srcSize limits. + * + * Note : only single-threaded compression is supported. + * ZSTD_estimateCCtxSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1. + */ + public static nuint ZSTD_estimateCCtxSize(int compressionLevel) + { + int level; + nuint memBudget = 0; + for ( + level = compressionLevel < 1 ? compressionLevel : 1; + level <= compressionLevel; + level++ + ) + { + /* Ensure monotonically increasing memory usage as compression level increases */ + nuint newMB = ZSTD_estimateCCtxSize_internal(level); + if (newMB > memBudget) + memBudget = newMB; + } + + return memBudget; + } + + public static nuint ZSTD_estimateCStreamSize_usingCCtxParams(ZSTD_CCtx_params_s* @params) + { + if (@params->nbWorkers > 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + + { + ZSTD_compressionParameters cParams = ZSTD_getCParamsFromCCtxParams( + @params, + unchecked(0UL - 1), + 0, + ZSTD_CParamMode_e.ZSTD_cpm_noAttachDict + ); + nuint blockSize = + ZSTD_resolveMaxBlockSize(@params->maxBlockSize) < (nuint)1 << (int)cParams.windowLog + ? ZSTD_resolveMaxBlockSize(@params->maxBlockSize) + : (nuint)1 << (int)cParams.windowLog; + nuint inBuffSize = + @params->inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered + ? ((nuint)1 << (int)cParams.windowLog) + blockSize + : 0; + nuint outBuffSize = + @params->outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered + ? ZSTD_compressBound(blockSize) + 1 + : 0; + ZSTD_paramSwitch_e useRowMatchFinder = ZSTD_resolveRowMatchFinderMode( + @params->useRowMatchFinder, + &@params->cParams + ); + return ZSTD_estimateCCtxSize_usingCCtxParams_internal( + &cParams, + &@params->ldmParams, + 1, + useRowMatchFinder, + inBuffSize, + outBuffSize, + unchecked(0UL - 1), + ZSTD_hasExtSeqProd(@params), + @params->maxBlockSize + ); + } + } + + public static nuint ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams) + { + ZSTD_CCtx_params_s initialParams = ZSTD_makeCCtxParamsFromCParams(cParams); + if (ZSTD_rowMatchFinderSupported(cParams.strategy) != 0) + { + /* Pick bigger of not using and using row-based matchfinder for greedy and lazy strategies */ + nuint noRowCCtxSize; + nuint rowCCtxSize; + initialParams.useRowMatchFinder = ZSTD_paramSwitch_e.ZSTD_ps_disable; + noRowCCtxSize = ZSTD_estimateCStreamSize_usingCCtxParams(&initialParams); + initialParams.useRowMatchFinder = ZSTD_paramSwitch_e.ZSTD_ps_enable; + rowCCtxSize = ZSTD_estimateCStreamSize_usingCCtxParams(&initialParams); + return noRowCCtxSize > rowCCtxSize ? noRowCCtxSize : rowCCtxSize; + } + else + { + return ZSTD_estimateCStreamSize_usingCCtxParams(&initialParams); + } + } + + private static nuint ZSTD_estimateCStreamSize_internal(int compressionLevel) + { + ZSTD_compressionParameters cParams = ZSTD_getCParams_internal( + compressionLevel, + unchecked(0UL - 1), + 0, + ZSTD_CParamMode_e.ZSTD_cpm_noAttachDict + ); + return ZSTD_estimateCStreamSize_usingCParams(cParams); + } + + /*! ZSTD_estimateCStreamSize() : + * ZSTD_estimateCStreamSize() will provide a memory budget large enough for streaming compression + * using any compression level up to the max specified one. + * It will also consider src size to be arbitrarily "large", which is a worst case scenario. + * If srcSize is known to always be small, ZSTD_estimateCStreamSize_usingCParams() can provide a tighter estimation. + * ZSTD_estimateCStreamSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel. + * ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParams_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_c_nbWorkers is >= 1. + * Note : CStream size estimation is only correct for single-threaded compression. + * ZSTD_estimateCStreamSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1. + * Note 2 : ZSTD_estimateCStreamSize* functions are not compatible with the Block-Level Sequence Producer API at this time. + * Size estimates assume that no external sequence producer is registered. + * + * ZSTD_DStream memory budget depends on frame's window Size. + * This information can be passed manually, using ZSTD_estimateDStreamSize, + * or deducted from a valid frame Header, using ZSTD_estimateDStreamSize_fromFrame(); + * Any frame requesting a window size larger than max specified one will be rejected. + * Note : if streaming is init with function ZSTD_init?Stream_usingDict(), + * an internal ?Dict will be created, which additional size is not estimated here. + * In this case, get total size by adding ZSTD_estimate?DictSize + */ + public static nuint ZSTD_estimateCStreamSize(int compressionLevel) + { + int level; + nuint memBudget = 0; + for ( + level = compressionLevel < 1 ? compressionLevel : 1; + level <= compressionLevel; + level++ + ) + { + nuint newMB = ZSTD_estimateCStreamSize_internal(level); + if (newMB > memBudget) + memBudget = newMB; + } + + return memBudget; + } + + /* ZSTD_getFrameProgression(): + * tells how much data has been consumed (input) and produced (output) for current frame. + * able to count progression inside worker threads (non-blocking mode). + */ + public static ZSTD_frameProgression ZSTD_getFrameProgression(ZSTD_CCtx_s* cctx) + { + if (cctx->appliedParams.nbWorkers > 0) + { + return ZSTDMT_getFrameProgression(cctx->mtctx); + } + + { + ZSTD_frameProgression fp; + nuint buffered = cctx->inBuff == null ? 0 : cctx->inBuffPos - cctx->inToCompress; +#if DEBUG + if (buffered != 0) + assert(cctx->inBuffPos >= cctx->inToCompress); +#endif + assert(buffered <= 1 << 17); + fp.ingested = cctx->consumedSrcSize + buffered; + fp.consumed = cctx->consumedSrcSize; + fp.produced = cctx->producedCSize; + fp.flushed = cctx->producedCSize; + fp.currentJobID = 0; + fp.nbActiveWorkers = 0; + return fp; + } + } + + /*! ZSTD_toFlushNow() + * Only useful for multithreading scenarios currently (nbWorkers >= 1). + */ + public static nuint ZSTD_toFlushNow(ZSTD_CCtx_s* cctx) + { + if (cctx->appliedParams.nbWorkers > 0) + { + return ZSTDMT_toFlushNow(cctx->mtctx); + } + + return 0; + } + + [Conditional("DEBUG")] + private static void ZSTD_assertEqualCParams( + ZSTD_compressionParameters cParams1, + ZSTD_compressionParameters cParams2 + ) + { + assert(cParams1.windowLog == cParams2.windowLog); + assert(cParams1.chainLog == cParams2.chainLog); + assert(cParams1.hashLog == cParams2.hashLog); + assert(cParams1.searchLog == cParams2.searchLog); + assert(cParams1.minMatch == cParams2.minMatch); + assert(cParams1.targetLength == cParams2.targetLength); + assert(cParams1.strategy == cParams2.strategy); + } + + private static void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs) + { + int i; + for (i = 0; i < 3; ++i) + bs->rep[i] = repStartValue[i]; + bs->entropy.huf.repeatMode = HUF_repeat.HUF_repeat_none; + bs->entropy.fse.offcode_repeatMode = FSE_repeat.FSE_repeat_none; + bs->entropy.fse.matchlength_repeatMode = FSE_repeat.FSE_repeat_none; + bs->entropy.fse.litlength_repeatMode = FSE_repeat.FSE_repeat_none; + } + + /*! ZSTD_invalidateMatchState() + * Invalidate all the matches in the match finder tables. + * Requires nextSrc and base to be set (can be NULL). + */ + private static void ZSTD_invalidateMatchState(ZSTD_MatchState_t* ms) + { + ZSTD_window_clear(&ms->window); + ms->nextToUpdate = ms->window.dictLimit; + ms->loadedDictEnd = 0; + ms->opt.litLengthSum = 0; + ms->dictMatchState = null; + } + + /* Mixes bits in a 64 bits in a value, based on XXH3_rrmxmx */ + private static ulong ZSTD_bitmix(ulong val, ulong len) + { + val ^= BitOperations.RotateRight(val, 49) ^ BitOperations.RotateRight(val, 24); + val *= 0x9FB21C651E98DF25UL; + val ^= (val >> 35) + len; + val *= 0x9FB21C651E98DF25UL; + return val ^ val >> 28; + } + + /* Mixes in the hashSalt and hashSaltEntropy to create a new hashSalt */ + private static void ZSTD_advanceHashSalt(ZSTD_MatchState_t* ms) + { + ms->hashSalt = ZSTD_bitmix(ms->hashSalt, 8) ^ ZSTD_bitmix(ms->hashSaltEntropy, 4); + } + + private static nuint ZSTD_reset_matchState( + ZSTD_MatchState_t* ms, + ZSTD_cwksp* ws, + ZSTD_compressionParameters* cParams, + ZSTD_paramSwitch_e useRowMatchFinder, + ZSTD_compResetPolicy_e crp, + ZSTD_indexResetPolicy_e forceResetIndex, + ZSTD_resetTarget_e forWho + ) + { + /* disable chain table allocation for fast or row-based strategies */ + nuint chainSize = + ZSTD_allocateChainTable( + cParams->strategy, + useRowMatchFinder, + ms->dedicatedDictSearch != 0 && forWho == ZSTD_resetTarget_e.ZSTD_resetTarget_CDict + ? 1U + : 0U + ) != 0 + ? (nuint)1 << (int)cParams->chainLog + : 0; + nuint hSize = (nuint)1 << (int)cParams->hashLog; + uint hashLog3 = + forWho == ZSTD_resetTarget_e.ZSTD_resetTarget_CCtx && cParams->minMatch == 3 + ? 17 < cParams->windowLog + ? 17 + : cParams->windowLog + : 0; + nuint h3Size = hashLog3 != 0 ? (nuint)1 << (int)hashLog3 : 0; + assert(useRowMatchFinder != ZSTD_paramSwitch_e.ZSTD_ps_auto); + if (forceResetIndex == ZSTD_indexResetPolicy_e.ZSTDirp_reset) + { + ZSTD_window_init(&ms->window); + ZSTD_cwksp_mark_tables_dirty(ws); + } + + ms->hashLog3 = hashLog3; + ms->lazySkipping = 0; + ZSTD_invalidateMatchState(ms); + assert(ZSTD_cwksp_reserve_failed(ws) == 0); + ZSTD_cwksp_clear_tables(ws); + ms->hashTable = (uint*)ZSTD_cwksp_reserve_table(ws, hSize * sizeof(uint)); + ms->chainTable = (uint*)ZSTD_cwksp_reserve_table(ws, chainSize * sizeof(uint)); + ms->hashTable3 = (uint*)ZSTD_cwksp_reserve_table(ws, h3Size * sizeof(uint)); + if (ZSTD_cwksp_reserve_failed(ws) != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + if (crp != ZSTD_compResetPolicy_e.ZSTDcrp_leaveDirty) + { + ZSTD_cwksp_clean_tables(ws); + } + + if (ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder) != 0) + { + /* Row match finder needs an additional table of hashes ("tags") */ + nuint tagTableSize = hSize; + if (forWho == ZSTD_resetTarget_e.ZSTD_resetTarget_CCtx) + { + ms->tagTable = (byte*)ZSTD_cwksp_reserve_aligned_init_once(ws, tagTableSize); + ZSTD_advanceHashSalt(ms); + } + else + { + ms->tagTable = (byte*)ZSTD_cwksp_reserve_aligned64(ws, tagTableSize); + memset(ms->tagTable, 0, (uint)tagTableSize); + ms->hashSalt = 0; + } + + { + uint rowLog = + cParams->searchLog <= 4 ? 4 + : cParams->searchLog <= 6 ? cParams->searchLog + : 6; + assert(cParams->hashLog >= rowLog); + ms->rowHashLog = cParams->hashLog - rowLog; + } + } + + if ( + forWho == ZSTD_resetTarget_e.ZSTD_resetTarget_CCtx + && cParams->strategy >= ZSTD_strategy.ZSTD_btopt + ) + { + ms->opt.litFreq = (uint*)ZSTD_cwksp_reserve_aligned64(ws, (1 << 8) * sizeof(uint)); + ms->opt.litLengthFreq = (uint*)ZSTD_cwksp_reserve_aligned64( + ws, + (35 + 1) * sizeof(uint) + ); + ms->opt.matchLengthFreq = (uint*)ZSTD_cwksp_reserve_aligned64( + ws, + (52 + 1) * sizeof(uint) + ); + ms->opt.offCodeFreq = (uint*)ZSTD_cwksp_reserve_aligned64(ws, (31 + 1) * sizeof(uint)); + ms->opt.matchTable = (ZSTD_match_t*)ZSTD_cwksp_reserve_aligned64( + ws, + (nuint)(((1 << 12) + 3) * sizeof(ZSTD_match_t)) + ); + ms->opt.priceTable = (ZSTD_optimal_t*)ZSTD_cwksp_reserve_aligned64( + ws, + (nuint)(((1 << 12) + 3) * sizeof(ZSTD_optimal_t)) + ); + } + + ms->cParams = *cParams; + if (ZSTD_cwksp_reserve_failed(ws) != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + return 0; + } + + private static int ZSTD_indexTooCloseToMax(ZSTD_window_t w) + { + return + (nuint)(w.nextSrc - w.@base) + > (MEM_64bits ? 3500U * (1 << 20) : 2000U * (1 << 20)) - 16 * (1 << 20) + ? 1 + : 0; + } + + /** ZSTD_dictTooBig(): + * When dictionaries are larger than ZSTD_CHUNKSIZE_MAX they can't be loaded in + * one go generically. So we ensure that in that case we reset the tables to zero, + * so that we can load as much of the dictionary as possible. + */ + private static int ZSTD_dictTooBig(nuint loadedDictSize) + { + return + loadedDictSize + > unchecked((uint)-1) - (MEM_64bits ? 3500U * (1 << 20) : 2000U * (1 << 20)) + ? 1 + : 0; + } + + /*! ZSTD_resetCCtx_internal() : + * @param loadedDictSize The size of the dictionary to be loaded + * into the context, if any. If no dictionary is used, or the + * dictionary is being attached / copied, then pass 0. + * note : `params` are assumed fully validated at this stage. + */ + private static nuint ZSTD_resetCCtx_internal( + ZSTD_CCtx_s* zc, + ZSTD_CCtx_params_s* @params, + ulong pledgedSrcSize, + nuint loadedDictSize, + ZSTD_compResetPolicy_e crp, + ZSTD_buffered_policy_e zbuff + ) + { + ZSTD_cwksp* ws = &zc->workspace; + assert(!ERR_isError(ZSTD_checkCParams(@params->cParams))); + zc->isFirstBlock = 1; + zc->appliedParams = *@params; + @params = &zc->appliedParams; + assert(@params->useRowMatchFinder != ZSTD_paramSwitch_e.ZSTD_ps_auto); + assert(@params->postBlockSplitter != ZSTD_paramSwitch_e.ZSTD_ps_auto); + assert(@params->ldmParams.enableLdm != ZSTD_paramSwitch_e.ZSTD_ps_auto); + assert(@params->maxBlockSize != 0); + if (@params->ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + ZSTD_ldm_adjustParameters(&zc->appliedParams.ldmParams, &@params->cParams); + assert(@params->ldmParams.hashLog >= @params->ldmParams.bucketSizeLog); + assert(@params->ldmParams.hashRateLog < 32); + } + + { + nuint windowSize = + 1 + > (nuint)( + (ulong)1 << (int)@params->cParams.windowLog < pledgedSrcSize + ? (ulong)1 << (int)@params->cParams.windowLog + : pledgedSrcSize + ) + ? 1 + : (nuint)( + (ulong)1 << (int)@params->cParams.windowLog < pledgedSrcSize + ? (ulong)1 << (int)@params->cParams.windowLog + : pledgedSrcSize + ); + nuint blockSize = + @params->maxBlockSize < windowSize ? @params->maxBlockSize : windowSize; + nuint maxNbSeq = ZSTD_maxNbSeq( + blockSize, + @params->cParams.minMatch, + ZSTD_hasExtSeqProd(@params) + ); + nuint buffOutSize = + zbuff == ZSTD_buffered_policy_e.ZSTDb_buffered + && @params->outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered + ? ZSTD_compressBound(blockSize) + 1 + : 0; + nuint buffInSize = + zbuff == ZSTD_buffered_policy_e.ZSTDb_buffered + && @params->inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered + ? windowSize + blockSize + : 0; + nuint maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(@params->ldmParams, blockSize); + int indexTooClose = ZSTD_indexTooCloseToMax(zc->blockState.matchState.window); + int dictTooBig = ZSTD_dictTooBig(loadedDictSize); + ZSTD_indexResetPolicy_e needsIndexReset = + indexTooClose != 0 || dictTooBig != 0 || zc->initialized == 0 + ? ZSTD_indexResetPolicy_e.ZSTDirp_reset + : ZSTD_indexResetPolicy_e.ZSTDirp_continue; + nuint neededSpace = ZSTD_estimateCCtxSize_usingCCtxParams_internal( + &@params->cParams, + &@params->ldmParams, + zc->staticSize != 0 ? 1 : 0, + @params->useRowMatchFinder, + buffInSize, + buffOutSize, + pledgedSrcSize, + ZSTD_hasExtSeqProd(@params), + @params->maxBlockSize + ); + { + nuint err_code = neededSpace; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (zc->staticSize == 0) + ZSTD_cwksp_bump_oversized_duration(ws, 0); + { + int workspaceTooSmall = ZSTD_cwksp_sizeof(ws) < neededSpace ? 1 : 0; + int workspaceWasteful = ZSTD_cwksp_check_wasteful(ws, neededSpace); + int resizeWorkspace = workspaceTooSmall != 0 || workspaceWasteful != 0 ? 1 : 0; + if (resizeWorkspace != 0) + { + if (zc->staticSize != 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation) + ); + } + + needsIndexReset = ZSTD_indexResetPolicy_e.ZSTDirp_reset; + ZSTD_cwksp_free(ws, zc->customMem); + { + nuint err_code = ZSTD_cwksp_create(ws, neededSpace, zc->customMem); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert( + ZSTD_cwksp_check_available( + ws, + (nuint)(2 * sizeof(ZSTD_compressedBlockState_t)) + ) != 0 + ); + zc->blockState.prevCBlock = + (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object( + ws, + (nuint)sizeof(ZSTD_compressedBlockState_t) + ); + if (zc->blockState.prevCBlock == null) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation) + ); + } + + zc->blockState.nextCBlock = + (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object( + ws, + (nuint)sizeof(ZSTD_compressedBlockState_t) + ); + if (zc->blockState.nextCBlock == null) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation) + ); + } + + zc->tmpWorkspace = ZSTD_cwksp_reserve_object( + ws, + (8 << 10) + 512 + sizeof(uint) * (52 + 2) > 8208 + ? (8 << 10) + 512 + sizeof(uint) * (52 + 2) + : 8208 + ); + if (zc->tmpWorkspace == null) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation) + ); + } + + zc->tmpWkspSize = + (8 << 10) + 512 + sizeof(uint) * (52 + 2) > 8208 + ? (8 << 10) + 512 + sizeof(uint) * (52 + 2) + : 8208; + } + } + + ZSTD_cwksp_clear(ws); + zc->blockState.matchState.cParams = @params->cParams; + zc->blockState.matchState.prefetchCDictTables = + @params->prefetchCDictTables == ZSTD_paramSwitch_e.ZSTD_ps_enable ? 1 : 0; + zc->pledgedSrcSizePlusOne = pledgedSrcSize + 1; + zc->consumedSrcSize = 0; + zc->producedCSize = 0; + if (pledgedSrcSize == unchecked(0UL - 1)) + zc->appliedParams.fParams.contentSizeFlag = 0; + zc->blockSizeMax = blockSize; + ZSTD_XXH64_reset(&zc->xxhState, 0); + zc->stage = ZSTD_compressionStage_e.ZSTDcs_init; + zc->dictID = 0; + zc->dictContentSize = 0; + ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock); + { + nuint err_code = ZSTD_reset_matchState( + &zc->blockState.matchState, + ws, + &@params->cParams, + @params->useRowMatchFinder, + crp, + needsIndexReset, + ZSTD_resetTarget_e.ZSTD_resetTarget_CCtx + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + zc->seqStore.sequencesStart = (SeqDef_s*)ZSTD_cwksp_reserve_aligned64( + ws, + maxNbSeq * (nuint)sizeof(SeqDef_s) + ); + if (@params->ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + /* TODO: avoid memset? */ + nuint ldmHSize = (nuint)1 << (int)@params->ldmParams.hashLog; + zc->ldmState.hashTable = (ldmEntry_t*)ZSTD_cwksp_reserve_aligned64( + ws, + ldmHSize * (nuint)sizeof(ldmEntry_t) + ); + memset(zc->ldmState.hashTable, 0, (uint)(ldmHSize * (nuint)sizeof(ldmEntry_t))); + zc->ldmSequences = (rawSeq*)ZSTD_cwksp_reserve_aligned64( + ws, + maxNbLdmSeq * (nuint)sizeof(rawSeq) + ); + zc->maxNbLdmSequences = maxNbLdmSeq; + ZSTD_window_init(&zc->ldmState.window); + zc->ldmState.loadedDictEnd = 0; + } + + if (ZSTD_hasExtSeqProd(@params) != 0) + { + nuint maxNbExternalSeq = ZSTD_sequenceBound(blockSize); + zc->extSeqBufCapacity = maxNbExternalSeq; + zc->extSeqBuf = (ZSTD_Sequence*)ZSTD_cwksp_reserve_aligned64( + ws, + maxNbExternalSeq * (nuint)sizeof(ZSTD_Sequence) + ); + } + + zc->seqStore.litStart = ZSTD_cwksp_reserve_buffer(ws, blockSize + 32); + zc->seqStore.maxNbLit = blockSize; + zc->bufferedPolicy = zbuff; + zc->inBuffSize = buffInSize; + zc->inBuff = (sbyte*)ZSTD_cwksp_reserve_buffer(ws, buffInSize); + zc->outBuffSize = buffOutSize; + zc->outBuff = (sbyte*)ZSTD_cwksp_reserve_buffer(ws, buffOutSize); + if (@params->ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + /* TODO: avoid memset? */ + nuint numBuckets = + (nuint)1 + << (int)(@params->ldmParams.hashLog - @params->ldmParams.bucketSizeLog); + zc->ldmState.bucketOffsets = ZSTD_cwksp_reserve_buffer(ws, numBuckets); + memset(zc->ldmState.bucketOffsets, 0, (uint)numBuckets); + } + + ZSTD_referenceExternalSequences(zc, null, 0); + zc->seqStore.maxNbSeq = maxNbSeq; + zc->seqStore.llCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(byte)); + zc->seqStore.mlCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(byte)); + zc->seqStore.ofCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(byte)); + assert(ZSTD_cwksp_estimated_space_within_bounds(ws, neededSpace) != 0); + zc->initialized = 1; + return 0; + } + } + + /* ZSTD_invalidateRepCodes() : + * ensures next compression will not use repcodes from previous block. + * Note : only works with regular variant; + * do not use with extDict variant ! */ + private static void ZSTD_invalidateRepCodes(ZSTD_CCtx_s* cctx) + { + int i; + for (i = 0; i < 3; i++) + cctx->blockState.prevCBlock->rep[i] = 0; + assert(ZSTD_window_hasExtDict(cctx->blockState.matchState.window) == 0); + } + + private static readonly nuint* attachDictSizeCutoffs = GetArrayPointer( + new nuint[10] + { + 8 * (1 << 10), + 8 * (1 << 10), + 16 * (1 << 10), + 32 * (1 << 10), + 32 * (1 << 10), + 32 * (1 << 10), + 32 * (1 << 10), + 32 * (1 << 10), + 8 * (1 << 10), + 8 * (1 << 10), + } + ); + + private static int ZSTD_shouldAttachDict( + ZSTD_CDict_s* cdict, + ZSTD_CCtx_params_s* @params, + ulong pledgedSrcSize + ) + { + nuint cutoff = attachDictSizeCutoffs[(int)cdict->matchState.cParams.strategy]; + int dedicatedDictSearch = cdict->matchState.dedicatedDictSearch; + return + dedicatedDictSearch != 0 + || ( + pledgedSrcSize <= cutoff + || pledgedSrcSize == unchecked(0UL - 1) + || @params->attachDictPref == ZSTD_dictAttachPref_e.ZSTD_dictForceAttach + ) + && @params->attachDictPref != ZSTD_dictAttachPref_e.ZSTD_dictForceCopy + && @params->forceWindow == 0 + ? 1 + : 0; + } + + private static nuint ZSTD_resetCCtx_byAttachingCDict( + ZSTD_CCtx_s* cctx, + ZSTD_CDict_s* cdict, + ZSTD_CCtx_params_s @params, + ulong pledgedSrcSize, + ZSTD_buffered_policy_e zbuff + ) + { + { + ZSTD_compressionParameters adjusted_cdict_cParams = cdict->matchState.cParams; + uint windowLog = @params.cParams.windowLog; + assert(windowLog != 0); + if (cdict->matchState.dedicatedDictSearch != 0) + { + ZSTD_dedicatedDictSearch_revertCParams(&adjusted_cdict_cParams); + } + + @params.cParams = ZSTD_adjustCParams_internal( + adjusted_cdict_cParams, + pledgedSrcSize, + cdict->dictContentSize, + ZSTD_CParamMode_e.ZSTD_cpm_attachDict, + @params.useRowMatchFinder + ); + @params.cParams.windowLog = windowLog; + @params.useRowMatchFinder = cdict->useRowMatchFinder; + { + nuint err_code = ZSTD_resetCCtx_internal( + cctx, + &@params, + pledgedSrcSize, + 0, + ZSTD_compResetPolicy_e.ZSTDcrp_makeClean, + zbuff + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(cctx->appliedParams.cParams.strategy == adjusted_cdict_cParams.strategy); + } + + { + uint cdictEnd = (uint)( + cdict->matchState.window.nextSrc - cdict->matchState.window.@base + ); + uint cdictLen = cdictEnd - cdict->matchState.window.dictLimit; + if (cdictLen != 0) + { + cctx->blockState.matchState.dictMatchState = &cdict->matchState; + if (cctx->blockState.matchState.window.dictLimit < cdictEnd) + { + cctx->blockState.matchState.window.nextSrc = + cctx->blockState.matchState.window.@base + cdictEnd; + ZSTD_window_clear(&cctx->blockState.matchState.window); + } + + cctx->blockState.matchState.loadedDictEnd = cctx->blockState + .matchState + .window + .dictLimit; + } + } + + cctx->dictID = cdict->dictID; + cctx->dictContentSize = cdict->dictContentSize; + memcpy( + cctx->blockState.prevCBlock, + &cdict->cBlockState, + (uint)sizeof(ZSTD_compressedBlockState_t) + ); + return 0; + } + + private static void ZSTD_copyCDictTableIntoCCtx( + uint* dst, + uint* src, + nuint tableSize, + ZSTD_compressionParameters* cParams + ) + { + if (ZSTD_CDictIndicesAreTagged(cParams) != 0) + { + /* Remove tags from the CDict table if they are present. + * See docs on "short cache" in zstd_compress_internal.h for context. */ + nuint i; + for (i = 0; i < tableSize; i++) + { + uint taggedIndex = src[i]; + uint index = taggedIndex >> 8; + dst[i] = index; + } + } + else + { + memcpy(dst, src, (uint)(tableSize * sizeof(uint))); + } + } + + private static nuint ZSTD_resetCCtx_byCopyingCDict( + ZSTD_CCtx_s* cctx, + ZSTD_CDict_s* cdict, + ZSTD_CCtx_params_s @params, + ulong pledgedSrcSize, + ZSTD_buffered_policy_e zbuff + ) + { + ZSTD_compressionParameters* cdict_cParams = &cdict->matchState.cParams; + assert(cdict->matchState.dedicatedDictSearch == 0); + { + uint windowLog = @params.cParams.windowLog; + assert(windowLog != 0); + @params.cParams = *cdict_cParams; + @params.cParams.windowLog = windowLog; + @params.useRowMatchFinder = cdict->useRowMatchFinder; + { + nuint err_code = ZSTD_resetCCtx_internal( + cctx, + &@params, + pledgedSrcSize, + 0, + ZSTD_compResetPolicy_e.ZSTDcrp_leaveDirty, + zbuff + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy); + assert(cctx->appliedParams.cParams.hashLog == cdict_cParams->hashLog); + assert(cctx->appliedParams.cParams.chainLog == cdict_cParams->chainLog); + } + + ZSTD_cwksp_mark_tables_dirty(&cctx->workspace); + assert(@params.useRowMatchFinder != ZSTD_paramSwitch_e.ZSTD_ps_auto); + { + /* DDS guaranteed disabled */ + nuint chainSize = + ZSTD_allocateChainTable(cdict_cParams->strategy, cdict->useRowMatchFinder, 0) != 0 + ? (nuint)1 << (int)cdict_cParams->chainLog + : 0; + nuint hSize = (nuint)1 << (int)cdict_cParams->hashLog; + ZSTD_copyCDictTableIntoCCtx( + cctx->blockState.matchState.hashTable, + cdict->matchState.hashTable, + hSize, + cdict_cParams + ); + if ( + ZSTD_allocateChainTable( + cctx->appliedParams.cParams.strategy, + cctx->appliedParams.useRowMatchFinder, + 0 + ) != 0 + ) + { + ZSTD_copyCDictTableIntoCCtx( + cctx->blockState.matchState.chainTable, + cdict->matchState.chainTable, + chainSize, + cdict_cParams + ); + } + + if (ZSTD_rowMatchFinderUsed(cdict_cParams->strategy, cdict->useRowMatchFinder) != 0) + { + nuint tagTableSize = hSize; + memcpy( + cctx->blockState.matchState.tagTable, + cdict->matchState.tagTable, + (uint)tagTableSize + ); + cctx->blockState.matchState.hashSalt = cdict->matchState.hashSalt; + } + } + + assert(cctx->blockState.matchState.hashLog3 <= 31); + { + uint h3log = cctx->blockState.matchState.hashLog3; + nuint h3Size = h3log != 0 ? (nuint)1 << (int)h3log : 0; + assert(cdict->matchState.hashLog3 == 0); + memset(cctx->blockState.matchState.hashTable3, 0, (uint)(h3Size * sizeof(uint))); + } + + ZSTD_cwksp_mark_tables_clean(&cctx->workspace); + { + ZSTD_MatchState_t* srcMatchState = &cdict->matchState; + ZSTD_MatchState_t* dstMatchState = &cctx->blockState.matchState; + dstMatchState->window = srcMatchState->window; + dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; + dstMatchState->loadedDictEnd = srcMatchState->loadedDictEnd; + } + + cctx->dictID = cdict->dictID; + cctx->dictContentSize = cdict->dictContentSize; + memcpy( + cctx->blockState.prevCBlock, + &cdict->cBlockState, + (uint)sizeof(ZSTD_compressedBlockState_t) + ); + return 0; + } + + /* We have a choice between copying the dictionary context into the working + * context, or referencing the dictionary context from the working context + * in-place. We decide here which strategy to use. */ + private static nuint ZSTD_resetCCtx_usingCDict( + ZSTD_CCtx_s* cctx, + ZSTD_CDict_s* cdict, + ZSTD_CCtx_params_s* @params, + ulong pledgedSrcSize, + ZSTD_buffered_policy_e zbuff + ) + { + if (ZSTD_shouldAttachDict(cdict, @params, pledgedSrcSize) != 0) + { + return ZSTD_resetCCtx_byAttachingCDict(cctx, cdict, *@params, pledgedSrcSize, zbuff); + } + else + { + return ZSTD_resetCCtx_byCopyingCDict(cctx, cdict, *@params, pledgedSrcSize, zbuff); + } + } + + /*! ZSTD_copyCCtx_internal() : + * Duplicate an existing context `srcCCtx` into another one `dstCCtx`. + * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()). + * The "context", in this case, refers to the hash and chain tables, + * entropy tables, and dictionary references. + * `windowLog` value is enforced if != 0, otherwise value is copied from srcCCtx. + * @return : 0, or an error code */ + private static nuint ZSTD_copyCCtx_internal( + ZSTD_CCtx_s* dstCCtx, + ZSTD_CCtx_s* srcCCtx, + ZSTD_frameParameters fParams, + ulong pledgedSrcSize, + ZSTD_buffered_policy_e zbuff + ) + { + if (srcCCtx->stage != ZSTD_compressionStage_e.ZSTDcs_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + memcpy(&dstCCtx->customMem, &srcCCtx->customMem, (uint)sizeof(ZSTD_customMem)); + { + ZSTD_CCtx_params_s @params = dstCCtx->requestedParams; + @params.cParams = srcCCtx->appliedParams.cParams; + assert(srcCCtx->appliedParams.useRowMatchFinder != ZSTD_paramSwitch_e.ZSTD_ps_auto); + assert(srcCCtx->appliedParams.postBlockSplitter != ZSTD_paramSwitch_e.ZSTD_ps_auto); + assert(srcCCtx->appliedParams.ldmParams.enableLdm != ZSTD_paramSwitch_e.ZSTD_ps_auto); + @params.useRowMatchFinder = srcCCtx->appliedParams.useRowMatchFinder; + @params.postBlockSplitter = srcCCtx->appliedParams.postBlockSplitter; + @params.ldmParams = srcCCtx->appliedParams.ldmParams; + @params.fParams = fParams; + @params.maxBlockSize = srcCCtx->appliedParams.maxBlockSize; + ZSTD_resetCCtx_internal( + dstCCtx, + &@params, + pledgedSrcSize, + 0, + ZSTD_compResetPolicy_e.ZSTDcrp_leaveDirty, + zbuff + ); + assert( + dstCCtx->appliedParams.cParams.windowLog == srcCCtx->appliedParams.cParams.windowLog + ); + assert( + dstCCtx->appliedParams.cParams.strategy == srcCCtx->appliedParams.cParams.strategy + ); + assert( + dstCCtx->appliedParams.cParams.hashLog == srcCCtx->appliedParams.cParams.hashLog + ); + assert( + dstCCtx->appliedParams.cParams.chainLog == srcCCtx->appliedParams.cParams.chainLog + ); + assert( + dstCCtx->blockState.matchState.hashLog3 == srcCCtx->blockState.matchState.hashLog3 + ); + } + + ZSTD_cwksp_mark_tables_dirty(&dstCCtx->workspace); + { + nuint chainSize = + ZSTD_allocateChainTable( + srcCCtx->appliedParams.cParams.strategy, + srcCCtx->appliedParams.useRowMatchFinder, + 0 + ) != 0 + ? (nuint)1 << (int)srcCCtx->appliedParams.cParams.chainLog + : 0; + nuint hSize = (nuint)1 << (int)srcCCtx->appliedParams.cParams.hashLog; + uint h3log = srcCCtx->blockState.matchState.hashLog3; + nuint h3Size = h3log != 0 ? (nuint)1 << (int)h3log : 0; + memcpy( + dstCCtx->blockState.matchState.hashTable, + srcCCtx->blockState.matchState.hashTable, + (uint)(hSize * sizeof(uint)) + ); + memcpy( + dstCCtx->blockState.matchState.chainTable, + srcCCtx->blockState.matchState.chainTable, + (uint)(chainSize * sizeof(uint)) + ); + memcpy( + dstCCtx->blockState.matchState.hashTable3, + srcCCtx->blockState.matchState.hashTable3, + (uint)(h3Size * sizeof(uint)) + ); + } + + ZSTD_cwksp_mark_tables_clean(&dstCCtx->workspace); + { + ZSTD_MatchState_t* srcMatchState = &srcCCtx->blockState.matchState; + ZSTD_MatchState_t* dstMatchState = &dstCCtx->blockState.matchState; + dstMatchState->window = srcMatchState->window; + dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; + dstMatchState->loadedDictEnd = srcMatchState->loadedDictEnd; + } + + dstCCtx->dictID = srcCCtx->dictID; + dstCCtx->dictContentSize = srcCCtx->dictContentSize; + memcpy( + dstCCtx->blockState.prevCBlock, + srcCCtx->blockState.prevCBlock, + (uint)sizeof(ZSTD_compressedBlockState_t) + ); + return 0; + } + + /*! ZSTD_copyCCtx() : + * Duplicate an existing context `srcCCtx` into another one `dstCCtx`. + * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()). + * pledgedSrcSize==0 means "unknown". + * @return : 0, or an error code */ + public static nuint ZSTD_copyCCtx( + ZSTD_CCtx_s* dstCCtx, + ZSTD_CCtx_s* srcCCtx, + ulong pledgedSrcSize + ) + { + /*content*/ + ZSTD_frameParameters fParams = new ZSTD_frameParameters + { + contentSizeFlag = 1, + checksumFlag = 0, + noDictIDFlag = 0, + }; + ZSTD_buffered_policy_e zbuff = srcCCtx->bufferedPolicy; + if (pledgedSrcSize == 0) + pledgedSrcSize = unchecked(0UL - 1); + fParams.contentSizeFlag = pledgedSrcSize != unchecked(0UL - 1) ? 1 : 0; + return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx, fParams, pledgedSrcSize, zbuff); + } + + /*! ZSTD_reduceTable() : + * reduce table indexes by `reducerValue`, or squash to zero. + * PreserveMark preserves "unsorted mark" for btlazy2 strategy. + * It must be set to a clear 0/1 value, to remove branch during inlining. + * Presume table size is a multiple of ZSTD_ROWSIZE + * to help auto-vectorization */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_reduceTable_internal( + uint* table, + uint size, + uint reducerValue, + int preserveMark + ) + { + int nbRows = (int)size / 16; + int cellNb = 0; + int rowNb; + /* Protect special index values < ZSTD_WINDOW_START_INDEX. */ + uint reducerThreshold = reducerValue + 2; + assert((size & 16 - 1) == 0); + assert(size < 1U << 31); + for (rowNb = 0; rowNb < nbRows; rowNb++) + { + int column; + for (column = 0; column < 16; column++) + { + uint newVal; + if (preserveMark != 0 && table[cellNb] == 1) + { + newVal = 1; + } + else if (table[cellNb] < reducerThreshold) + { + newVal = 0; + } + else + { + newVal = table[cellNb] - reducerValue; + } + + table[cellNb] = newVal; + cellNb++; + } + } + } + + private static void ZSTD_reduceTable(uint* table, uint size, uint reducerValue) + { + ZSTD_reduceTable_internal(table, size, reducerValue, 0); + } + + private static void ZSTD_reduceTable_btlazy2(uint* table, uint size, uint reducerValue) + { + ZSTD_reduceTable_internal(table, size, reducerValue, 1); + } + + /*! ZSTD_reduceIndex() : + * rescale all indexes to avoid future overflow (indexes are U32) */ + private static void ZSTD_reduceIndex( + ZSTD_MatchState_t* ms, + ZSTD_CCtx_params_s* @params, + uint reducerValue + ) + { + { + uint hSize = (uint)1 << (int)@params->cParams.hashLog; + ZSTD_reduceTable(ms->hashTable, hSize, reducerValue); + } + + if ( + ZSTD_allocateChainTable( + @params->cParams.strategy, + @params->useRowMatchFinder, + (uint)ms->dedicatedDictSearch + ) != 0 + ) + { + uint chainSize = (uint)1 << (int)@params->cParams.chainLog; + if (@params->cParams.strategy == ZSTD_strategy.ZSTD_btlazy2) + ZSTD_reduceTable_btlazy2(ms->chainTable, chainSize, reducerValue); + else + ZSTD_reduceTable(ms->chainTable, chainSize, reducerValue); + } + + if (ms->hashLog3 != 0) + { + uint h3Size = (uint)1 << (int)ms->hashLog3; + ZSTD_reduceTable(ms->hashTable3, h3Size, reducerValue); + } + } + + /* See doc/zstd_compression_format.md for detailed format description */ + private static int ZSTD_seqToCodes(SeqStore_t* seqStorePtr) + { + SeqDef_s* sequences = seqStorePtr->sequencesStart; + byte* llCodeTable = seqStorePtr->llCode; + byte* ofCodeTable = seqStorePtr->ofCode; + byte* mlCodeTable = seqStorePtr->mlCode; + uint nbSeq = (uint)(seqStorePtr->sequences - seqStorePtr->sequencesStart); + uint u; + int longOffsets = 0; + assert(nbSeq <= seqStorePtr->maxNbSeq); + for (u = 0; u < nbSeq; u++) + { + uint llv = sequences[u].litLength; + uint ofCode = ZSTD_highbit32(sequences[u].offBase); + uint mlv = sequences[u].mlBase; + llCodeTable[u] = (byte)ZSTD_LLcode(llv); + ofCodeTable[u] = (byte)ofCode; + mlCodeTable[u] = (byte)ZSTD_MLcode(mlv); + assert(!(MEM_64bits && ofCode >= (uint)(MEM_32bits ? 25 : 57))); + if (MEM_32bits && ofCode >= (uint)(MEM_32bits ? 25 : 57)) + longOffsets = 1; + } + + if (seqStorePtr->longLengthType == ZSTD_longLengthType_e.ZSTD_llt_literalLength) + llCodeTable[seqStorePtr->longLengthPos] = 35; + if (seqStorePtr->longLengthType == ZSTD_longLengthType_e.ZSTD_llt_matchLength) + mlCodeTable[seqStorePtr->longLengthPos] = 52; + return longOffsets; + } + + /* ZSTD_useTargetCBlockSize(): + * Returns if target compressed block size param is being used. + * If used, compression will do best effort to make a compressed block size to be around targetCBlockSize. + * Returns 1 if true, 0 otherwise. */ + private static int ZSTD_useTargetCBlockSize(ZSTD_CCtx_params_s* cctxParams) + { + return cctxParams->targetCBlockSize != 0 ? 1 : 0; + } + + /* ZSTD_blockSplitterEnabled(): + * Returns if block splitting param is being used + * If used, compression will do best effort to split a block in order to improve compression ratio. + * At the time this function is called, the parameter must be finalized. + * Returns 1 if true, 0 otherwise. */ + private static int ZSTD_blockSplitterEnabled(ZSTD_CCtx_params_s* cctxParams) + { + assert(cctxParams->postBlockSplitter != ZSTD_paramSwitch_e.ZSTD_ps_auto); + return cctxParams->postBlockSplitter == ZSTD_paramSwitch_e.ZSTD_ps_enable ? 1 : 0; + } + + /* ZSTD_buildSequencesStatistics(): + * Returns a ZSTD_symbolEncodingTypeStats_t, or a zstd error code in the `size` field. + * Modifies `nextEntropy` to have the appropriate values as a side effect. + * nbSeq must be greater than 0. + * + * entropyWkspSize must be of size at least ENTROPY_WORKSPACE_SIZE - (MaxSeq + 1)*sizeof(U32) + */ + private static ZSTD_symbolEncodingTypeStats_t ZSTD_buildSequencesStatistics( + SeqStore_t* seqStorePtr, + nuint nbSeq, + ZSTD_fseCTables_t* prevEntropy, + ZSTD_fseCTables_t* nextEntropy, + byte* dst, + byte* dstEnd, + ZSTD_strategy strategy, + uint* countWorkspace, + void* entropyWorkspace, + nuint entropyWkspSize + ) + { + byte* ostart = dst; + byte* oend = dstEnd; + byte* op = ostart; + uint* CTable_LitLength = nextEntropy->litlengthCTable; + uint* CTable_OffsetBits = nextEntropy->offcodeCTable; + uint* CTable_MatchLength = nextEntropy->matchlengthCTable; + byte* ofCodeTable = seqStorePtr->ofCode; + byte* llCodeTable = seqStorePtr->llCode; + byte* mlCodeTable = seqStorePtr->mlCode; + ZSTD_symbolEncodingTypeStats_t stats; + System.Runtime.CompilerServices.Unsafe.SkipInit(out stats); + stats.lastCountSize = 0; + stats.longOffsets = ZSTD_seqToCodes(seqStorePtr); + assert(op <= oend); + assert(nbSeq != 0); + { + uint max = 35; + /* can't fail */ + nuint mostFrequent = HIST_countFast_wksp( + countWorkspace, + &max, + llCodeTable, + nbSeq, + entropyWorkspace, + entropyWkspSize + ); + nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode; + stats.LLtype = (uint)ZSTD_selectEncodingType( + &nextEntropy->litlength_repeatMode, + countWorkspace, + max, + mostFrequent, + nbSeq, + 9, + prevEntropy->litlengthCTable, + LL_defaultNorm, + LL_defaultNormLog, + ZSTD_DefaultPolicy_e.ZSTD_defaultAllowed, + strategy + ); + assert( + SymbolEncodingType_e.set_basic < SymbolEncodingType_e.set_compressed + && SymbolEncodingType_e.set_rle < SymbolEncodingType_e.set_compressed + ); + assert( + !( + stats.LLtype < (uint)SymbolEncodingType_e.set_compressed + && nextEntropy->litlength_repeatMode != FSE_repeat.FSE_repeat_none + ) + ); + { + nuint countSize = ZSTD_buildCTable( + op, + (nuint)(oend - op), + CTable_LitLength, + 9, + (SymbolEncodingType_e)stats.LLtype, + countWorkspace, + max, + llCodeTable, + nbSeq, + LL_defaultNorm, + LL_defaultNormLog, + 35, + prevEntropy->litlengthCTable, + sizeof(uint) * 329, + entropyWorkspace, + entropyWkspSize + ); + if (ERR_isError(countSize)) + { + stats.size = countSize; + return stats; + } + + if (stats.LLtype == (uint)SymbolEncodingType_e.set_compressed) + stats.lastCountSize = countSize; + op += countSize; + assert(op <= oend); + } + } + + { + uint max = 31; + nuint mostFrequent = HIST_countFast_wksp( + countWorkspace, + &max, + ofCodeTable, + nbSeq, + entropyWorkspace, + entropyWkspSize + ); + /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */ + ZSTD_DefaultPolicy_e defaultPolicy = + max <= 28 + ? ZSTD_DefaultPolicy_e.ZSTD_defaultAllowed + : ZSTD_DefaultPolicy_e.ZSTD_defaultDisallowed; + nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode; + stats.Offtype = (uint)ZSTD_selectEncodingType( + &nextEntropy->offcode_repeatMode, + countWorkspace, + max, + mostFrequent, + nbSeq, + 8, + prevEntropy->offcodeCTable, + OF_defaultNorm, + OF_defaultNormLog, + defaultPolicy, + strategy + ); + assert( + !( + stats.Offtype < (uint)SymbolEncodingType_e.set_compressed + && nextEntropy->offcode_repeatMode != FSE_repeat.FSE_repeat_none + ) + ); + { + nuint countSize = ZSTD_buildCTable( + op, + (nuint)(oend - op), + CTable_OffsetBits, + 8, + (SymbolEncodingType_e)stats.Offtype, + countWorkspace, + max, + ofCodeTable, + nbSeq, + OF_defaultNorm, + OF_defaultNormLog, + 28, + prevEntropy->offcodeCTable, + sizeof(uint) * 193, + entropyWorkspace, + entropyWkspSize + ); + if (ERR_isError(countSize)) + { + stats.size = countSize; + return stats; + } + + if (stats.Offtype == (uint)SymbolEncodingType_e.set_compressed) + stats.lastCountSize = countSize; + op += countSize; + assert(op <= oend); + } + } + + { + uint max = 52; + nuint mostFrequent = HIST_countFast_wksp( + countWorkspace, + &max, + mlCodeTable, + nbSeq, + entropyWorkspace, + entropyWkspSize + ); + nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode; + stats.MLtype = (uint)ZSTD_selectEncodingType( + &nextEntropy->matchlength_repeatMode, + countWorkspace, + max, + mostFrequent, + nbSeq, + 9, + prevEntropy->matchlengthCTable, + ML_defaultNorm, + ML_defaultNormLog, + ZSTD_DefaultPolicy_e.ZSTD_defaultAllowed, + strategy + ); + assert( + !( + stats.MLtype < (uint)SymbolEncodingType_e.set_compressed + && nextEntropy->matchlength_repeatMode != FSE_repeat.FSE_repeat_none + ) + ); + { + nuint countSize = ZSTD_buildCTable( + op, + (nuint)(oend - op), + CTable_MatchLength, + 9, + (SymbolEncodingType_e)stats.MLtype, + countWorkspace, + max, + mlCodeTable, + nbSeq, + ML_defaultNorm, + ML_defaultNormLog, + 52, + prevEntropy->matchlengthCTable, + sizeof(uint) * 363, + entropyWorkspace, + entropyWkspSize + ); + if (ERR_isError(countSize)) + { + stats.size = countSize; + return stats; + } + + if (stats.MLtype == (uint)SymbolEncodingType_e.set_compressed) + stats.lastCountSize = countSize; + op += countSize; + assert(op <= oend); + } + } + + stats.size = (nuint)(op - ostart); + return stats; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_entropyCompressSeqStore_internal( + void* dst, + nuint dstCapacity, + void* literals, + nuint litSize, + SeqStore_t* seqStorePtr, + ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + ZSTD_CCtx_params_s* cctxParams, + void* entropyWorkspace, + nuint entropyWkspSize, + int bmi2 + ) + { + ZSTD_strategy strategy = cctxParams->cParams.strategy; + uint* count = (uint*)entropyWorkspace; + uint* CTable_LitLength = nextEntropy->fse.litlengthCTable; + uint* CTable_OffsetBits = nextEntropy->fse.offcodeCTable; + uint* CTable_MatchLength = nextEntropy->fse.matchlengthCTable; + SeqDef_s* sequences = seqStorePtr->sequencesStart; + nuint nbSeq = (nuint)(seqStorePtr->sequences - seqStorePtr->sequencesStart); + byte* ofCodeTable = seqStorePtr->ofCode; + byte* llCodeTable = seqStorePtr->llCode; + byte* mlCodeTable = seqStorePtr->mlCode; + byte* ostart = (byte*)dst; + byte* oend = ostart + dstCapacity; + byte* op = ostart; + nuint lastCountSize; + int longOffsets = 0; + entropyWorkspace = count + (52 + 1); + entropyWkspSize -= (52 + 1) * sizeof(uint); + assert(entropyWkspSize >= (8 << 10) + 512); + { + nuint numSequences = (nuint)(seqStorePtr->sequences - seqStorePtr->sequencesStart); + /* Base suspicion of uncompressibility on ratio of literals to sequences */ + int suspectUncompressible = numSequences == 0 || litSize / numSequences >= 20 ? 1 : 0; + nuint cSize = ZSTD_compressLiterals( + op, + dstCapacity, + literals, + litSize, + entropyWorkspace, + entropyWkspSize, + &prevEntropy->huf, + &nextEntropy->huf, + cctxParams->cParams.strategy, + ZSTD_literalsCompressionIsDisabled(cctxParams), + suspectUncompressible, + bmi2 + ); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(cSize <= dstCapacity); + op += cSize; + } + + if (oend - op < 3 + 1) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (nbSeq < 128) + { + *op++ = (byte)nbSeq; + } + else if (nbSeq < 0x7F00) + { + op[0] = (byte)((nbSeq >> 8) + 0x80); + op[1] = (byte)nbSeq; + op += 2; + } + else + { + op[0] = 0xFF; + MEM_writeLE16(op + 1, (ushort)(nbSeq - 0x7F00)); + op += 3; + } + + assert(op <= oend); + if (nbSeq == 0) + { + memcpy(&nextEntropy->fse, &prevEntropy->fse, (uint)sizeof(ZSTD_fseCTables_t)); + return (nuint)(op - ostart); + } + + { + byte* seqHead = op++; + /* build stats for sequences */ + ZSTD_symbolEncodingTypeStats_t stats = ZSTD_buildSequencesStatistics( + seqStorePtr, + nbSeq, + &prevEntropy->fse, + &nextEntropy->fse, + op, + oend, + strategy, + count, + entropyWorkspace, + entropyWkspSize + ); + { + nuint err_code = stats.size; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + *seqHead = (byte)((stats.LLtype << 6) + (stats.Offtype << 4) + (stats.MLtype << 2)); + lastCountSize = stats.lastCountSize; + op += stats.size; + longOffsets = stats.longOffsets; + } + + { + nuint bitstreamSize = ZSTD_encodeSequences( + op, + (nuint)(oend - op), + CTable_MatchLength, + mlCodeTable, + CTable_OffsetBits, + ofCodeTable, + CTable_LitLength, + llCodeTable, + sequences, + nbSeq, + longOffsets, + bmi2 + ); + { + nuint err_code = bitstreamSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + op += bitstreamSize; + assert(op <= oend); + if (lastCountSize != 0 && lastCountSize + bitstreamSize < 4) + { + assert(lastCountSize + bitstreamSize == 3); + return 0; + } + } + + return (nuint)(op - ostart); + } + + private static nuint ZSTD_entropyCompressSeqStore_wExtLitBuffer( + void* dst, + nuint dstCapacity, + void* literals, + nuint litSize, + nuint blockSize, + SeqStore_t* seqStorePtr, + ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + ZSTD_CCtx_params_s* cctxParams, + void* entropyWorkspace, + nuint entropyWkspSize, + int bmi2 + ) + { + nuint cSize = ZSTD_entropyCompressSeqStore_internal( + dst, + dstCapacity, + literals, + litSize, + seqStorePtr, + prevEntropy, + nextEntropy, + cctxParams, + entropyWorkspace, + entropyWkspSize, + bmi2 + ); + if (cSize == 0) + return 0; + if ( + cSize == unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)) + && blockSize <= dstCapacity + ) + { + return 0; + } + + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint maxCSize = blockSize - ZSTD_minGain(blockSize, cctxParams->cParams.strategy); + if (cSize >= maxCSize) + return 0; + } + + assert(cSize < 1 << 17); + return cSize; + } + + private static nuint ZSTD_entropyCompressSeqStore( + SeqStore_t* seqStorePtr, + ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + ZSTD_CCtx_params_s* cctxParams, + void* dst, + nuint dstCapacity, + nuint srcSize, + void* entropyWorkspace, + nuint entropyWkspSize, + int bmi2 + ) + { + return ZSTD_entropyCompressSeqStore_wExtLitBuffer( + dst, + dstCapacity, + seqStorePtr->litStart, + (nuint)(seqStorePtr->lit - seqStorePtr->litStart), + srcSize, + seqStorePtr, + prevEntropy, + nextEntropy, + cctxParams, + entropyWorkspace, + entropyWkspSize, + bmi2 + ); + } + + private static readonly ZSTD_BlockCompressor_f?[][] blockCompressor = + new ZSTD_BlockCompressor_f?[4][] + { + new ZSTD_BlockCompressor_f[10] + { + ZSTD_compressBlock_fast, + ZSTD_compressBlock_fast, + ZSTD_compressBlock_doubleFast, + ZSTD_compressBlock_greedy, + ZSTD_compressBlock_lazy, + ZSTD_compressBlock_lazy2, + ZSTD_compressBlock_btlazy2, + ZSTD_compressBlock_btopt, + ZSTD_compressBlock_btultra, + ZSTD_compressBlock_btultra2, + }, + new ZSTD_BlockCompressor_f[10] + { + ZSTD_compressBlock_fast_extDict, + ZSTD_compressBlock_fast_extDict, + ZSTD_compressBlock_doubleFast_extDict, + ZSTD_compressBlock_greedy_extDict, + ZSTD_compressBlock_lazy_extDict, + ZSTD_compressBlock_lazy2_extDict, + ZSTD_compressBlock_btlazy2_extDict, + ZSTD_compressBlock_btopt_extDict, + ZSTD_compressBlock_btultra_extDict, + ZSTD_compressBlock_btultra_extDict, + }, + new ZSTD_BlockCompressor_f[10] + { + ZSTD_compressBlock_fast_dictMatchState, + ZSTD_compressBlock_fast_dictMatchState, + ZSTD_compressBlock_doubleFast_dictMatchState, + ZSTD_compressBlock_greedy_dictMatchState, + ZSTD_compressBlock_lazy_dictMatchState, + ZSTD_compressBlock_lazy2_dictMatchState, + ZSTD_compressBlock_btlazy2_dictMatchState, + ZSTD_compressBlock_btopt_dictMatchState, + ZSTD_compressBlock_btultra_dictMatchState, + ZSTD_compressBlock_btultra_dictMatchState, + }, + new ZSTD_BlockCompressor_f?[10] + { + null, + null, + null, + ZSTD_compressBlock_greedy_dedicatedDictSearch, + ZSTD_compressBlock_lazy_dedicatedDictSearch, + ZSTD_compressBlock_lazy2_dedicatedDictSearch, + null, + null, + null, + null, + }, + }; + private static readonly ZSTD_BlockCompressor_f[][] rowBasedBlockCompressors = + new ZSTD_BlockCompressor_f[4][] + { + new ZSTD_BlockCompressor_f[3] + { + ZSTD_compressBlock_greedy_row, + ZSTD_compressBlock_lazy_row, + ZSTD_compressBlock_lazy2_row, + }, + new ZSTD_BlockCompressor_f[3] + { + ZSTD_compressBlock_greedy_extDict_row, + ZSTD_compressBlock_lazy_extDict_row, + ZSTD_compressBlock_lazy2_extDict_row, + }, + new ZSTD_BlockCompressor_f[3] + { + ZSTD_compressBlock_greedy_dictMatchState_row, + ZSTD_compressBlock_lazy_dictMatchState_row, + ZSTD_compressBlock_lazy2_dictMatchState_row, + }, + new ZSTD_BlockCompressor_f[3] + { + ZSTD_compressBlock_greedy_dedicatedDictSearch_row, + ZSTD_compressBlock_lazy_dedicatedDictSearch_row, + ZSTD_compressBlock_lazy2_dedicatedDictSearch_row, + }, + }; + + /* ZSTD_selectBlockCompressor() : + * Not static, but internal use only (used by long distance matcher) + * assumption : strat is a valid strategy */ + private static ZSTD_BlockCompressor_f ZSTD_selectBlockCompressor( + ZSTD_strategy strat, + ZSTD_paramSwitch_e useRowMatchFinder, + ZSTD_dictMode_e dictMode + ) + { + ZSTD_BlockCompressor_f? selectedCompressor; + assert(ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_strategy, (int)strat) != 0); + if (ZSTD_rowMatchFinderUsed(strat, useRowMatchFinder) != 0) + { + assert(useRowMatchFinder != ZSTD_paramSwitch_e.ZSTD_ps_auto); + selectedCompressor = rowBasedBlockCompressors[(int)dictMode][ + (int)strat - (int)ZSTD_strategy.ZSTD_greedy + ]; + } + else + { + selectedCompressor = blockCompressor[(int)dictMode][(int)strat]; + } + + assert(selectedCompressor != null); + return selectedCompressor.NotNull(); + } + + private static void ZSTD_storeLastLiterals( + SeqStore_t* seqStorePtr, + byte* anchor, + nuint lastLLSize + ) + { + memcpy(seqStorePtr->lit, anchor, (uint)lastLLSize); + seqStorePtr->lit += lastLLSize; + } + + private static void ZSTD_resetSeqStore(SeqStore_t* ssPtr) + { + ssPtr->lit = ssPtr->litStart; + ssPtr->sequences = ssPtr->sequencesStart; + ssPtr->longLengthType = ZSTD_longLengthType_e.ZSTD_llt_none; + } + + /* ZSTD_postProcessSequenceProducerResult() : + * Validates and post-processes sequences obtained through the external matchfinder API: + * - Checks whether nbExternalSeqs represents an error condition. + * - Appends a block delimiter to outSeqs if one is not already present. + * See zstd.h for context regarding block delimiters. + * Returns the number of sequences after post-processing, or an error code. */ + private static nuint ZSTD_postProcessSequenceProducerResult( + ZSTD_Sequence* outSeqs, + nuint nbExternalSeqs, + nuint outSeqsCapacity, + nuint srcSize + ) + { + if (nbExternalSeqs > outSeqsCapacity) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_sequenceProducer_failed)); + } + + if (nbExternalSeqs == 0 && srcSize > 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_sequenceProducer_failed)); + } + + if (srcSize == 0) + { + outSeqs[0] = new ZSTD_Sequence(); + return 1; + } + + { + ZSTD_Sequence lastSeq = outSeqs[nbExternalSeqs - 1]; + if (lastSeq.offset == 0 && lastSeq.matchLength == 0) + { + return nbExternalSeqs; + } + + if (nbExternalSeqs == outSeqsCapacity) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_sequenceProducer_failed)); + } + + outSeqs[nbExternalSeqs] = new ZSTD_Sequence(); + return nbExternalSeqs + 1; + } + } + + /* ZSTD_fastSequenceLengthSum() : + * Returns sum(litLen) + sum(matchLen) + lastLits for *seqBuf*. + * Similar to another function in zstd_compress.c (determine_blockSize), + * except it doesn't check for a block delimiter to end summation. + * Removing the early exit allows the compiler to auto-vectorize (https://godbolt.org/z/cY1cajz9P). + * This function can be deleted and replaced by determine_blockSize after we resolve issue #3456. */ + private static nuint ZSTD_fastSequenceLengthSum(ZSTD_Sequence* seqBuf, nuint seqBufSize) + { + nuint matchLenSum, + litLenSum, + i; + matchLenSum = 0; + litLenSum = 0; + for (i = 0; i < seqBufSize; i++) + { + litLenSum += seqBuf[i].litLength; + matchLenSum += seqBuf[i].matchLength; + } + + return litLenSum + matchLenSum; + } + + /** + * Function to validate sequences produced by a block compressor. + */ + private static void ZSTD_validateSeqStore( + SeqStore_t* seqStore, + ZSTD_compressionParameters* cParams + ) { } + + private static nuint ZSTD_buildSeqStore(ZSTD_CCtx_s* zc, void* src, nuint srcSize) + { + ZSTD_MatchState_t* ms = &zc->blockState.matchState; + assert(srcSize <= 1 << 17); + ZSTD_assertEqualCParams(zc->appliedParams.cParams, ms->cParams); + if (srcSize < (nuint)(1 + 1) + ZSTD_blockHeaderSize + 1 + 1) + { + if (zc->appliedParams.cParams.strategy >= ZSTD_strategy.ZSTD_btopt) + { + ZSTD_ldm_skipRawSeqStoreBytes(&zc->externSeqStore, srcSize); + } + else + { + ZSTD_ldm_skipSequences( + &zc->externSeqStore, + srcSize, + zc->appliedParams.cParams.minMatch + ); + } + + return (nuint)ZSTD_BuildSeqStore_e.ZSTDbss_noCompress; + } + + ZSTD_resetSeqStore(&zc->seqStore); + ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy; + ms->opt.literalCompressionMode = zc->appliedParams.literalCompressionMode; + assert(ms->dictMatchState == null || ms->loadedDictEnd == ms->window.dictLimit); + { + byte* @base = ms->window.@base; + byte* istart = (byte*)src; + uint curr = (uint)(istart - @base); +#if DEBUG + if (sizeof(nint) == 8) + assert(istart - @base < unchecked((nint)(uint)-1)); +#endif + if (curr > ms->nextToUpdate + 384) + ms->nextToUpdate = + curr + - (192 < curr - ms->nextToUpdate - 384 ? 192 : curr - ms->nextToUpdate - 384); + } + + { + ZSTD_dictMode_e dictMode = ZSTD_matchState_dictMode(ms); + nuint lastLLSize; + { + int i; + for (i = 0; i < 3; ++i) + zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i]; + } + + if (zc->externSeqStore.pos < zc->externSeqStore.size) + { + assert(zc->appliedParams.ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_disable); + if (ZSTD_hasExtSeqProd(&zc->appliedParams) != 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_combination_unsupported) + ); + } + + lastLLSize = ZSTD_ldm_blockCompress( + &zc->externSeqStore, + ms, + &zc->seqStore, + zc->blockState.nextCBlock->rep, + zc->appliedParams.useRowMatchFinder, + src, + srcSize + ); + assert(zc->externSeqStore.pos <= zc->externSeqStore.size); + } + else if (zc->appliedParams.ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + RawSeqStore_t ldmSeqStore = kNullRawSeqStore; + if (ZSTD_hasExtSeqProd(&zc->appliedParams) != 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_combination_unsupported) + ); + } + + ldmSeqStore.seq = zc->ldmSequences; + ldmSeqStore.capacity = zc->maxNbLdmSequences; + { + /* Updates ldmSeqStore.size */ + nuint err_code = ZSTD_ldm_generateSequences( + &zc->ldmState, + &ldmSeqStore, + &zc->appliedParams.ldmParams, + src, + srcSize + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + lastLLSize = ZSTD_ldm_blockCompress( + &ldmSeqStore, + ms, + &zc->seqStore, + zc->blockState.nextCBlock->rep, + zc->appliedParams.useRowMatchFinder, + src, + srcSize + ); + assert(ldmSeqStore.pos == ldmSeqStore.size); + } + else if (ZSTD_hasExtSeqProd(&zc->appliedParams) != 0) + { + assert(zc->extSeqBufCapacity >= ZSTD_sequenceBound(srcSize)); + assert(zc->appliedParams.extSeqProdFunc != null); + { + uint windowSize = (uint)1 << (int)zc->appliedParams.cParams.windowLog; + nuint nbExternalSeqs = ( + (delegate* managed< + void*, + ZSTD_Sequence*, + nuint, + void*, + nuint, + void*, + nuint, + int, + nuint, + nuint>) + zc->appliedParams.extSeqProdFunc + )( + zc->appliedParams.extSeqProdState, + zc->extSeqBuf, + zc->extSeqBufCapacity, + src, + srcSize, + null, + 0, + zc->appliedParams.compressionLevel, + windowSize + ); + nuint nbPostProcessedSeqs = ZSTD_postProcessSequenceProducerResult( + zc->extSeqBuf, + nbExternalSeqs, + zc->extSeqBufCapacity, + srcSize + ); + if (!ERR_isError(nbPostProcessedSeqs)) + { + ZSTD_SequencePosition seqPos = new ZSTD_SequencePosition + { + idx = 0, + posInSequence = 0, + posInSrc = 0, + }; + nuint seqLenSum = ZSTD_fastSequenceLengthSum( + zc->extSeqBuf, + nbPostProcessedSeqs + ); + if (seqLenSum > srcSize) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid) + ); + } + + { + nuint err_code = ZSTD_transferSequences_wBlockDelim( + zc, + &seqPos, + zc->extSeqBuf, + nbPostProcessedSeqs, + src, + srcSize, + zc->appliedParams.searchForExternalRepcodes + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ms->ldmSeqStore = null; + return (nuint)ZSTD_BuildSeqStore_e.ZSTDbss_compress; + } + + if (zc->appliedParams.enableMatchFinderFallback == 0) + { + return nbPostProcessedSeqs; + } + + { + ZSTD_BlockCompressor_f blockCompressor = ZSTD_selectBlockCompressor( + zc->appliedParams.cParams.strategy, + zc->appliedParams.useRowMatchFinder, + dictMode + ); + ms->ldmSeqStore = null; + lastLLSize = blockCompressor( + ms, + &zc->seqStore, + zc->blockState.nextCBlock->rep, + src, + srcSize + ); + } + } + } + else + { + ZSTD_BlockCompressor_f blockCompressor = ZSTD_selectBlockCompressor( + zc->appliedParams.cParams.strategy, + zc->appliedParams.useRowMatchFinder, + dictMode + ); + ms->ldmSeqStore = null; + lastLLSize = blockCompressor( + ms, + &zc->seqStore, + zc->blockState.nextCBlock->rep, + src, + srcSize + ); + } + + { + byte* lastLiterals = (byte*)src + srcSize - lastLLSize; + ZSTD_storeLastLiterals(&zc->seqStore, lastLiterals, lastLLSize); + } + } + + ZSTD_validateSeqStore(&zc->seqStore, &zc->appliedParams.cParams); + return (nuint)ZSTD_BuildSeqStore_e.ZSTDbss_compress; + } + + private static nuint ZSTD_copyBlockSequences( + SeqCollector* seqCollector, + SeqStore_t* seqStore, + uint* prevRepcodes + ) + { + SeqDef_s* inSeqs = seqStore->sequencesStart; + nuint nbInSequences = (nuint)(seqStore->sequences - inSeqs); + nuint nbInLiterals = (nuint)(seqStore->lit - seqStore->litStart); + ZSTD_Sequence* outSeqs = + seqCollector->seqIndex == 0 + ? seqCollector->seqStart + : seqCollector->seqStart + seqCollector->seqIndex; + nuint nbOutSequences = nbInSequences + 1; + nuint nbOutLiterals = 0; + repcodes_s repcodes; + nuint i; + assert(seqCollector->seqIndex <= seqCollector->maxSequences); + if (nbOutSequences > seqCollector->maxSequences - seqCollector->seqIndex) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + memcpy(&repcodes, prevRepcodes, (uint)sizeof(repcodes_s)); + for (i = 0; i < nbInSequences; ++i) + { + uint rawOffset; + outSeqs[i].litLength = inSeqs[i].litLength; + outSeqs[i].matchLength = (uint)(inSeqs[i].mlBase + 3); + outSeqs[i].rep = 0; + if (i == seqStore->longLengthPos) + { + if (seqStore->longLengthType == ZSTD_longLengthType_e.ZSTD_llt_literalLength) + { + outSeqs[i].litLength += 0x10000; + } + else if (seqStore->longLengthType == ZSTD_longLengthType_e.ZSTD_llt_matchLength) + { + outSeqs[i].matchLength += 0x10000; + } + } + + if (1 <= inSeqs[i].offBase && inSeqs[i].offBase <= 3) + { + assert(1 <= inSeqs[i].offBase && inSeqs[i].offBase <= 3); + uint repcode = inSeqs[i].offBase; + assert(repcode > 0); + outSeqs[i].rep = repcode; + if (outSeqs[i].litLength != 0) + { + rawOffset = repcodes.rep[repcode - 1]; + } + else + { + if (repcode == 3) + { + assert(repcodes.rep[0] > 1); + rawOffset = repcodes.rep[0] - 1; + } + else + { + rawOffset = repcodes.rep[repcode]; + } + } + } + else + { + assert(inSeqs[i].offBase > 3); + rawOffset = inSeqs[i].offBase - 3; + } + + outSeqs[i].offset = rawOffset; + ZSTD_updateRep(repcodes.rep, inSeqs[i].offBase, inSeqs[i].litLength == 0 ? 1U : 0U); + nbOutLiterals += outSeqs[i].litLength; + } + + assert(nbInLiterals >= nbOutLiterals); + { + nuint lastLLSize = nbInLiterals - nbOutLiterals; + outSeqs[nbInSequences].litLength = (uint)lastLLSize; + outSeqs[nbInSequences].matchLength = 0; + outSeqs[nbInSequences].offset = 0; + assert(nbOutSequences == nbInSequences + 1); + } + + seqCollector->seqIndex += nbOutSequences; + assert(seqCollector->seqIndex <= seqCollector->maxSequences); + return 0; + } + + /*! ZSTD_sequenceBound() : + * `srcSize` : size of the input buffer + * @return : upper-bound for the number of sequences that can be generated + * from a buffer of srcSize bytes + * + * note : returns number of sequences - to get bytes, multiply by sizeof(ZSTD_Sequence). + */ + public static nuint ZSTD_sequenceBound(nuint srcSize) + { + nuint maxNbSeq = srcSize / 3 + 1; + nuint maxNbDelims = srcSize / (1 << 10) + 1; + return maxNbSeq + maxNbDelims; + } + + /*! ZSTD_generateSequences() : + * WARNING: This function is meant for debugging and informational purposes ONLY! + * Its implementation is flawed, and it will be deleted in a future version. + * It is not guaranteed to succeed, as there are several cases where it will give + * up and fail. You should NOT use this function in production code. + * + * This function is deprecated, and will be removed in a future version. + * + * Generate sequences using ZSTD_compress2(), given a source buffer. + * + * @param zc The compression context to be used for ZSTD_compress2(). Set any + * compression parameters you need on this context. + * @param outSeqs The output sequences buffer of size @p outSeqsSize + * @param outSeqsCapacity The size of the output sequences buffer. + * ZSTD_sequenceBound(srcSize) is an upper bound on the number + * of sequences that can be generated. + * @param src The source buffer to generate sequences from of size @p srcSize. + * @param srcSize The size of the source buffer. + * + * Each block will end with a dummy sequence + * with offset == 0, matchLength == 0, and litLength == length of last literals. + * litLength may be == 0, and if so, then the sequence of (of: 0 ml: 0 ll: 0) + * simply acts as a block delimiter. + * + * @returns The number of sequences generated, necessarily less than + * ZSTD_sequenceBound(srcSize), or an error code that can be checked + * with ZSTD_isError(). + */ + public static nuint ZSTD_generateSequences( + ZSTD_CCtx_s* zc, + ZSTD_Sequence* outSeqs, + nuint outSeqsSize, + void* src, + nuint srcSize + ) + { + nuint dstCapacity = ZSTD_compressBound(srcSize); + /* Make C90 happy. */ + void* dst; + SeqCollector seqCollector; + { + int targetCBlockSize; + { + nuint err_code = ZSTD_CCtx_getParameter( + zc, + ZSTD_cParameter.ZSTD_c_targetCBlockSize, + &targetCBlockSize + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (targetCBlockSize != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported)); + } + } + + { + int nbWorkers; + { + nuint err_code = ZSTD_CCtx_getParameter( + zc, + ZSTD_cParameter.ZSTD_c_nbWorkers, + &nbWorkers + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (nbWorkers != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported)); + } + } + + dst = ZSTD_customMalloc(dstCapacity, ZSTD_defaultCMem); + if (dst == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + seqCollector.collectSequences = 1; + seqCollector.seqStart = outSeqs; + seqCollector.seqIndex = 0; + seqCollector.maxSequences = outSeqsSize; + zc->seqCollector = seqCollector; + { + nuint ret = ZSTD_compress2(zc, dst, dstCapacity, src, srcSize); + ZSTD_customFree(dst, ZSTD_defaultCMem); + { + nuint err_code = ret; + if (ERR_isError(err_code)) + { + return err_code; + } + } + } + + assert(zc->seqCollector.seqIndex <= ZSTD_sequenceBound(srcSize)); + return zc->seqCollector.seqIndex; + } + + /*! ZSTD_mergeBlockDelimiters() : + * Given an array of ZSTD_Sequence, remove all sequences that represent block delimiters/last literals + * by merging them into the literals of the next sequence. + * + * As such, the final generated result has no explicit representation of block boundaries, + * and the final last literals segment is not represented in the sequences. + * + * The output of this function can be fed into ZSTD_compressSequences() with CCtx + * setting of ZSTD_c_blockDelimiters as ZSTD_sf_noBlockDelimiters + * @return : number of sequences left after merging + */ + public static nuint ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, nuint seqsSize) + { + nuint @in = 0; + nuint @out = 0; + for (; @in < seqsSize; ++@in) + { + if (sequences[@in].offset == 0 && sequences[@in].matchLength == 0) + { + if (@in != seqsSize - 1) + { + sequences[@in + 1].litLength += sequences[@in].litLength; + } + } + else + { + sequences[@out] = sequences[@in]; + ++@out; + } + } + + return @out; + } + + /* Unrolled loop to read four size_ts of input at a time. Returns 1 if is RLE, 0 if not. */ + private static int ZSTD_isRLE(byte* src, nuint length) + { + byte* ip = src; + byte value = ip[0]; + nuint valueST = (nuint)(value * 0x0101010101010101UL); + nuint unrollSize = (nuint)(sizeof(nuint) * 4); + nuint unrollMask = unrollSize - 1; + nuint prefixLength = length & unrollMask; + nuint i; + if (length == 1) + return 1; + if (prefixLength != 0 && ZSTD_count(ip + 1, ip, ip + prefixLength) != prefixLength - 1) + { + return 0; + } + + for (i = prefixLength; i != length; i += unrollSize) + { + nuint u; + for (u = 0; u < unrollSize; u += (nuint)sizeof(nuint)) + { + if (MEM_readST(ip + i + u) != valueST) + { + return 0; + } + } + } + + return 1; + } + + /* Returns true if the given block may be RLE. + * This is just a heuristic based on the compressibility. + * It may return both false positives and false negatives. + */ + private static int ZSTD_maybeRLE(SeqStore_t* seqStore) + { + nuint nbSeqs = (nuint)(seqStore->sequences - seqStore->sequencesStart); + nuint nbLits = (nuint)(seqStore->lit - seqStore->litStart); + return nbSeqs < 4 && nbLits < 10 ? 1 : 0; + } + + private static void ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* bs) + { + ZSTD_compressedBlockState_t* tmp = bs->prevCBlock; + bs->prevCBlock = bs->nextCBlock; + bs->nextCBlock = tmp; + } + + /* Writes the block header */ + private static void writeBlockHeader(void* op, nuint cSize, nuint blockSize, uint lastBlock) + { + uint cBlockHeader = + cSize == 1 + ? lastBlock + ((uint)blockType_e.bt_rle << 1) + (uint)(blockSize << 3) + : lastBlock + ((uint)blockType_e.bt_compressed << 1) + (uint)(cSize << 3); + MEM_writeLE24(op, cBlockHeader); + } + + /** ZSTD_buildBlockEntropyStats_literals() : + * Builds entropy for the literals. + * Stores literals block type (raw, rle, compressed, repeat) and + * huffman description table to hufMetadata. + * Requires ENTROPY_WORKSPACE_SIZE workspace + * @return : size of huffman description table, or an error code + */ + private static nuint ZSTD_buildBlockEntropyStats_literals( + void* src, + nuint srcSize, + ZSTD_hufCTables_t* prevHuf, + ZSTD_hufCTables_t* nextHuf, + ZSTD_hufCTablesMetadata_t* hufMetadata, + int literalsCompressionIsDisabled, + void* workspace, + nuint wkspSize, + int hufFlags + ) + { + byte* wkspStart = (byte*)workspace; + byte* wkspEnd = wkspStart + wkspSize; + byte* countWkspStart = wkspStart; + uint* countWksp = (uint*)workspace; + const nuint countWkspSize = (255 + 1) * sizeof(uint); + byte* nodeWksp = countWkspStart + countWkspSize; + nuint nodeWkspSize = (nuint)(wkspEnd - nodeWksp); + uint maxSymbolValue = 255; + uint huffLog = 11; + HUF_repeat repeat = prevHuf->repeatMode; + memcpy(nextHuf, prevHuf, (uint)sizeof(ZSTD_hufCTables_t)); + if (literalsCompressionIsDisabled != 0) + { + hufMetadata->hType = SymbolEncodingType_e.set_basic; + return 0; + } + + { + nuint minLitSize = (nuint)(prevHuf->repeatMode == HUF_repeat.HUF_repeat_valid ? 6 : 63); + if (srcSize <= minLitSize) + { + hufMetadata->hType = SymbolEncodingType_e.set_basic; + return 0; + } + } + + { + nuint largest = HIST_count_wksp( + countWksp, + &maxSymbolValue, + (byte*)src, + srcSize, + workspace, + wkspSize + ); + { + nuint err_code = largest; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (largest == srcSize) + { + hufMetadata->hType = SymbolEncodingType_e.set_rle; + return 0; + } + + if (largest <= (srcSize >> 7) + 4) + { + hufMetadata->hType = SymbolEncodingType_e.set_basic; + return 0; + } + } + + if ( + repeat == HUF_repeat.HUF_repeat_check + && HUF_validateCTable(&prevHuf->CTable.e0, countWksp, maxSymbolValue) == 0 + ) + { + repeat = HUF_repeat.HUF_repeat_none; + } + + memset(&nextHuf->CTable.e0, 0, sizeof(ulong) * 257); + huffLog = HUF_optimalTableLog( + huffLog, + srcSize, + maxSymbolValue, + nodeWksp, + nodeWkspSize, + &nextHuf->CTable.e0, + countWksp, + hufFlags + ); + assert(huffLog <= 11); + { + nuint maxBits = HUF_buildCTable_wksp( + &nextHuf->CTable.e0, + countWksp, + maxSymbolValue, + huffLog, + nodeWksp, + nodeWkspSize + ); + { + nuint err_code = maxBits; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + huffLog = (uint)maxBits; + } + + { + nuint newCSize = HUF_estimateCompressedSize( + &nextHuf->CTable.e0, + countWksp, + maxSymbolValue + ); + nuint hSize = HUF_writeCTable_wksp( + hufMetadata->hufDesBuffer, + sizeof(byte) * 128, + &nextHuf->CTable.e0, + maxSymbolValue, + huffLog, + nodeWksp, + nodeWkspSize + ); + if (repeat != HUF_repeat.HUF_repeat_none) + { + nuint oldCSize = HUF_estimateCompressedSize( + &prevHuf->CTable.e0, + countWksp, + maxSymbolValue + ); + if (oldCSize < srcSize && (oldCSize <= hSize + newCSize || hSize + 12 >= srcSize)) + { + memcpy(nextHuf, prevHuf, (uint)sizeof(ZSTD_hufCTables_t)); + hufMetadata->hType = SymbolEncodingType_e.set_repeat; + return 0; + } + } + + if (newCSize + hSize >= srcSize) + { + memcpy(nextHuf, prevHuf, (uint)sizeof(ZSTD_hufCTables_t)); + hufMetadata->hType = SymbolEncodingType_e.set_basic; + return 0; + } + + hufMetadata->hType = SymbolEncodingType_e.set_compressed; + nextHuf->repeatMode = HUF_repeat.HUF_repeat_check; + return hSize; + } + } + + /* ZSTD_buildDummySequencesStatistics(): + * Returns a ZSTD_symbolEncodingTypeStats_t with all encoding types as set_basic, + * and updates nextEntropy to the appropriate repeatMode. + */ + private static ZSTD_symbolEncodingTypeStats_t ZSTD_buildDummySequencesStatistics( + ZSTD_fseCTables_t* nextEntropy + ) + { + ZSTD_symbolEncodingTypeStats_t stats = new ZSTD_symbolEncodingTypeStats_t + { + LLtype = (uint)SymbolEncodingType_e.set_basic, + Offtype = (uint)SymbolEncodingType_e.set_basic, + MLtype = (uint)SymbolEncodingType_e.set_basic, + size = 0, + lastCountSize = 0, + longOffsets = 0, + }; + nextEntropy->litlength_repeatMode = FSE_repeat.FSE_repeat_none; + nextEntropy->offcode_repeatMode = FSE_repeat.FSE_repeat_none; + nextEntropy->matchlength_repeatMode = FSE_repeat.FSE_repeat_none; + return stats; + } + + /** ZSTD_buildBlockEntropyStats_sequences() : + * Builds entropy for the sequences. + * Stores symbol compression modes and fse table to fseMetadata. + * Requires ENTROPY_WORKSPACE_SIZE wksp. + * @return : size of fse tables or error code */ + private static nuint ZSTD_buildBlockEntropyStats_sequences( + SeqStore_t* seqStorePtr, + ZSTD_fseCTables_t* prevEntropy, + ZSTD_fseCTables_t* nextEntropy, + ZSTD_CCtx_params_s* cctxParams, + ZSTD_fseCTablesMetadata_t* fseMetadata, + void* workspace, + nuint wkspSize + ) + { + ZSTD_strategy strategy = cctxParams->cParams.strategy; + nuint nbSeq = (nuint)(seqStorePtr->sequences - seqStorePtr->sequencesStart); + byte* ostart = fseMetadata->fseTablesBuffer; + byte* oend = ostart + sizeof(byte) * 133; + byte* op = ostart; + uint* countWorkspace = (uint*)workspace; + uint* entropyWorkspace = countWorkspace + (52 + 1); + nuint entropyWorkspaceSize = wkspSize - (52 + 1) * sizeof(uint); + ZSTD_symbolEncodingTypeStats_t stats; + stats = + nbSeq != 0 + ? ZSTD_buildSequencesStatistics( + seqStorePtr, + nbSeq, + prevEntropy, + nextEntropy, + op, + oend, + strategy, + countWorkspace, + entropyWorkspace, + entropyWorkspaceSize + ) + : ZSTD_buildDummySequencesStatistics(nextEntropy); + { + nuint err_code = stats.size; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + fseMetadata->llType = (SymbolEncodingType_e)stats.LLtype; + fseMetadata->ofType = (SymbolEncodingType_e)stats.Offtype; + fseMetadata->mlType = (SymbolEncodingType_e)stats.MLtype; + fseMetadata->lastCountSize = stats.lastCountSize; + return stats.size; + } + + /** ZSTD_buildBlockEntropyStats() : + * Builds entropy for the block. + * Requires workspace size ENTROPY_WORKSPACE_SIZE + * @return : 0 on success, or an error code + * Note : also employed in superblock + */ + private static nuint ZSTD_buildBlockEntropyStats( + SeqStore_t* seqStorePtr, + ZSTD_entropyCTables_t* prevEntropy, + ZSTD_entropyCTables_t* nextEntropy, + ZSTD_CCtx_params_s* cctxParams, + ZSTD_entropyCTablesMetadata_t* entropyMetadata, + void* workspace, + nuint wkspSize + ) + { + nuint litSize = (nuint)(seqStorePtr->lit - seqStorePtr->litStart); + int huf_useOptDepth = cctxParams->cParams.strategy >= ZSTD_strategy.ZSTD_btultra ? 1 : 0; + int hufFlags = huf_useOptDepth != 0 ? (int)HUF_flags_e.HUF_flags_optimalDepth : 0; + entropyMetadata->hufMetadata.hufDesSize = ZSTD_buildBlockEntropyStats_literals( + seqStorePtr->litStart, + litSize, + &prevEntropy->huf, + &nextEntropy->huf, + &entropyMetadata->hufMetadata, + ZSTD_literalsCompressionIsDisabled(cctxParams), + workspace, + wkspSize, + hufFlags + ); + { + nuint err_code = entropyMetadata->hufMetadata.hufDesSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + entropyMetadata->fseMetadata.fseTablesSize = ZSTD_buildBlockEntropyStats_sequences( + seqStorePtr, + &prevEntropy->fse, + &nextEntropy->fse, + cctxParams, + &entropyMetadata->fseMetadata, + workspace, + wkspSize + ); + { + nuint err_code = entropyMetadata->fseMetadata.fseTablesSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /* Returns the size estimate for the literals section (header + content) of a block */ + private static nuint ZSTD_estimateBlockSize_literal( + byte* literals, + nuint litSize, + ZSTD_hufCTables_t* huf, + ZSTD_hufCTablesMetadata_t* hufMetadata, + void* workspace, + nuint wkspSize, + int writeEntropy + ) + { + uint* countWksp = (uint*)workspace; + uint maxSymbolValue = 255; + nuint literalSectionHeaderSize = (nuint)( + 3 + (litSize >= 1 * (1 << 10) ? 1 : 0) + (litSize >= 16 * (1 << 10) ? 1 : 0) + ); + uint singleStream = litSize < 256 ? 1U : 0U; + if (hufMetadata->hType == SymbolEncodingType_e.set_basic) + return litSize; + else if (hufMetadata->hType == SymbolEncodingType_e.set_rle) + return 1; + else if ( + hufMetadata->hType == SymbolEncodingType_e.set_compressed + || hufMetadata->hType == SymbolEncodingType_e.set_repeat + ) + { + nuint largest = HIST_count_wksp( + countWksp, + &maxSymbolValue, + literals, + litSize, + workspace, + wkspSize + ); + if (ERR_isError(largest)) + return litSize; + { + nuint cLitSizeEstimate = HUF_estimateCompressedSize( + &huf->CTable.e0, + countWksp, + maxSymbolValue + ); + if (writeEntropy != 0) + cLitSizeEstimate += hufMetadata->hufDesSize; + if (singleStream == 0) + cLitSizeEstimate += 6; + return cLitSizeEstimate + literalSectionHeaderSize; + } + } + + assert(0 != 0); + return 0; + } + + /* Returns the size estimate for the FSE-compressed symbols (of, ml, ll) of a block */ + private static nuint ZSTD_estimateBlockSize_symbolType( + SymbolEncodingType_e type, + byte* codeTable, + nuint nbSeq, + uint maxCode, + uint* fseCTable, + byte* additionalBits, + short* defaultNorm, + uint defaultNormLog, + uint defaultMax, + void* workspace, + nuint wkspSize + ) + { + uint* countWksp = (uint*)workspace; + byte* ctp = codeTable; + byte* ctStart = ctp; + byte* ctEnd = ctStart + nbSeq; + nuint cSymbolTypeSizeEstimateInBits = 0; + uint max = maxCode; + HIST_countFast_wksp(countWksp, &max, codeTable, nbSeq, workspace, wkspSize); + if (type == SymbolEncodingType_e.set_basic) + { + assert(max <= defaultMax); + cSymbolTypeSizeEstimateInBits = ZSTD_crossEntropyCost( + defaultNorm, + defaultNormLog, + countWksp, + max + ); + } + else if (type == SymbolEncodingType_e.set_rle) + { + cSymbolTypeSizeEstimateInBits = 0; + } + else if ( + type == SymbolEncodingType_e.set_compressed + || type == SymbolEncodingType_e.set_repeat + ) + { + cSymbolTypeSizeEstimateInBits = ZSTD_fseBitCost(fseCTable, countWksp, max); + } + + if (ERR_isError(cSymbolTypeSizeEstimateInBits)) + { + return nbSeq * 10; + } + + while (ctp < ctEnd) + { + if (additionalBits != null) + cSymbolTypeSizeEstimateInBits += additionalBits[*ctp]; + else + cSymbolTypeSizeEstimateInBits += *ctp; + ctp++; + } + + return cSymbolTypeSizeEstimateInBits >> 3; + } + + /* Returns the size estimate for the sequences section (header + content) of a block */ + private static nuint ZSTD_estimateBlockSize_sequences( + byte* ofCodeTable, + byte* llCodeTable, + byte* mlCodeTable, + nuint nbSeq, + ZSTD_fseCTables_t* fseTables, + ZSTD_fseCTablesMetadata_t* fseMetadata, + void* workspace, + nuint wkspSize, + int writeEntropy + ) + { + /* seqHead */ + nuint sequencesSectionHeaderSize = (nuint)( + 1 + 1 + (nbSeq >= 128 ? 1 : 0) + (nbSeq >= 0x7F00 ? 1 : 0) + ); + nuint cSeqSizeEstimate = 0; + cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType( + fseMetadata->ofType, + ofCodeTable, + nbSeq, + 31, + fseTables->offcodeCTable, + null, + OF_defaultNorm, + OF_defaultNormLog, + 28, + workspace, + wkspSize + ); + cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType( + fseMetadata->llType, + llCodeTable, + nbSeq, + 35, + fseTables->litlengthCTable, + LL_bits, + LL_defaultNorm, + LL_defaultNormLog, + 35, + workspace, + wkspSize + ); + cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType( + fseMetadata->mlType, + mlCodeTable, + nbSeq, + 52, + fseTables->matchlengthCTable, + ML_bits, + ML_defaultNorm, + ML_defaultNormLog, + 52, + workspace, + wkspSize + ); + if (writeEntropy != 0) + cSeqSizeEstimate += fseMetadata->fseTablesSize; + return cSeqSizeEstimate + sequencesSectionHeaderSize; + } + + /* Returns the size estimate for a given stream of literals, of, ll, ml */ + private static nuint ZSTD_estimateBlockSize( + byte* literals, + nuint litSize, + byte* ofCodeTable, + byte* llCodeTable, + byte* mlCodeTable, + nuint nbSeq, + ZSTD_entropyCTables_t* entropy, + ZSTD_entropyCTablesMetadata_t* entropyMetadata, + void* workspace, + nuint wkspSize, + int writeLitEntropy, + int writeSeqEntropy + ) + { + nuint literalsSize = ZSTD_estimateBlockSize_literal( + literals, + litSize, + &entropy->huf, + &entropyMetadata->hufMetadata, + workspace, + wkspSize, + writeLitEntropy + ); + nuint seqSize = ZSTD_estimateBlockSize_sequences( + ofCodeTable, + llCodeTable, + mlCodeTable, + nbSeq, + &entropy->fse, + &entropyMetadata->fseMetadata, + workspace, + wkspSize, + writeSeqEntropy + ); + return seqSize + literalsSize + ZSTD_blockHeaderSize; + } + + /* Builds entropy statistics and uses them for blocksize estimation. + * + * @return: estimated compressed size of the seqStore, or a zstd error. + */ + private static nuint ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize( + SeqStore_t* seqStore, + ZSTD_CCtx_s* zc + ) + { + ZSTD_entropyCTablesMetadata_t* entropyMetadata = &zc->blockSplitCtx.entropyMetadata; + { + nuint err_code = ZSTD_buildBlockEntropyStats( + seqStore, + &zc->blockState.prevCBlock->entropy, + &zc->blockState.nextCBlock->entropy, + &zc->appliedParams, + entropyMetadata, + zc->tmpWorkspace, + zc->tmpWkspSize + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return ZSTD_estimateBlockSize( + seqStore->litStart, + (nuint)(seqStore->lit - seqStore->litStart), + seqStore->ofCode, + seqStore->llCode, + seqStore->mlCode, + (nuint)(seqStore->sequences - seqStore->sequencesStart), + &zc->blockState.nextCBlock->entropy, + entropyMetadata, + zc->tmpWorkspace, + zc->tmpWkspSize, + entropyMetadata->hufMetadata.hType == SymbolEncodingType_e.set_compressed ? 1 : 0, + 1 + ); + } + + /* Returns literals bytes represented in a seqStore */ + private static nuint ZSTD_countSeqStoreLiteralsBytes(SeqStore_t* seqStore) + { + nuint literalsBytes = 0; + nuint nbSeqs = (nuint)(seqStore->sequences - seqStore->sequencesStart); + nuint i; + for (i = 0; i < nbSeqs; ++i) + { + SeqDef_s seq = seqStore->sequencesStart[i]; + literalsBytes += seq.litLength; + if ( + i == seqStore->longLengthPos + && seqStore->longLengthType == ZSTD_longLengthType_e.ZSTD_llt_literalLength + ) + { + literalsBytes += 0x10000; + } + } + + return literalsBytes; + } + + /* Returns match bytes represented in a seqStore */ + private static nuint ZSTD_countSeqStoreMatchBytes(SeqStore_t* seqStore) + { + nuint matchBytes = 0; + nuint nbSeqs = (nuint)(seqStore->sequences - seqStore->sequencesStart); + nuint i; + for (i = 0; i < nbSeqs; ++i) + { + SeqDef_s seq = seqStore->sequencesStart[i]; + matchBytes += (nuint)(seq.mlBase + 3); + if ( + i == seqStore->longLengthPos + && seqStore->longLengthType == ZSTD_longLengthType_e.ZSTD_llt_matchLength + ) + { + matchBytes += 0x10000; + } + } + + return matchBytes; + } + + /* Derives the seqStore that is a chunk of the originalSeqStore from [startIdx, endIdx). + * Stores the result in resultSeqStore. + */ + private static void ZSTD_deriveSeqStoreChunk( + SeqStore_t* resultSeqStore, + SeqStore_t* originalSeqStore, + nuint startIdx, + nuint endIdx + ) + { + *resultSeqStore = *originalSeqStore; + if (startIdx > 0) + { + resultSeqStore->sequences = originalSeqStore->sequencesStart + startIdx; + resultSeqStore->litStart += ZSTD_countSeqStoreLiteralsBytes(resultSeqStore); + } + + if (originalSeqStore->longLengthType != ZSTD_longLengthType_e.ZSTD_llt_none) + { + if ( + originalSeqStore->longLengthPos < startIdx + || originalSeqStore->longLengthPos > endIdx + ) + { + resultSeqStore->longLengthType = ZSTD_longLengthType_e.ZSTD_llt_none; + } + else + { + resultSeqStore->longLengthPos -= (uint)startIdx; + } + } + + resultSeqStore->sequencesStart = originalSeqStore->sequencesStart + startIdx; + resultSeqStore->sequences = originalSeqStore->sequencesStart + endIdx; + if (endIdx == (nuint)(originalSeqStore->sequences - originalSeqStore->sequencesStart)) + { + assert(resultSeqStore->lit == originalSeqStore->lit); + } + else + { + nuint literalsBytes = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore); + resultSeqStore->lit = resultSeqStore->litStart + literalsBytes; + } + + resultSeqStore->llCode += startIdx; + resultSeqStore->mlCode += startIdx; + resultSeqStore->ofCode += startIdx; + } + + /** + * Returns the raw offset represented by the combination of offBase, ll0, and repcode history. + * offBase must represent a repcode in the numeric representation of ZSTD_storeSeq(). + */ + private static uint ZSTD_resolveRepcodeToRawOffset(uint* rep, uint offBase, uint ll0) + { + assert(1 <= offBase && offBase <= 3); + /* [ 0 - 3 ] */ + uint adjustedRepCode = offBase - 1 + ll0; + assert(1 <= offBase && offBase <= 3); + if (adjustedRepCode == 3) + { + assert(ll0 != 0); + return rep[0] - 1; + } + + return rep[adjustedRepCode]; + } + + /** + * ZSTD_seqStore_resolveOffCodes() reconciles any possible divergences in offset history that may arise + * due to emission of RLE/raw blocks that disturb the offset history, + * and replaces any repcodes within the seqStore that may be invalid. + * + * dRepcodes are updated as would be on the decompression side. + * cRepcodes are updated exactly in accordance with the seqStore. + * + * Note : this function assumes seq->offBase respects the following numbering scheme : + * 0 : invalid + * 1-3 : repcode 1-3 + * 4+ : real_offset+3 + */ + private static void ZSTD_seqStore_resolveOffCodes( + repcodes_s* dRepcodes, + repcodes_s* cRepcodes, + SeqStore_t* seqStore, + uint nbSeq + ) + { + uint idx = 0; + uint longLitLenIdx = + seqStore->longLengthType == ZSTD_longLengthType_e.ZSTD_llt_literalLength + ? seqStore->longLengthPos + : nbSeq; + for (; idx < nbSeq; ++idx) + { + SeqDef_s* seq = seqStore->sequencesStart + idx; + uint ll0 = seq->litLength == 0 && idx != longLitLenIdx ? 1U : 0U; + uint offBase = seq->offBase; + assert(offBase > 0); + if (1 <= offBase && offBase <= 3) + { + uint dRawOffset = ZSTD_resolveRepcodeToRawOffset(dRepcodes->rep, offBase, ll0); + uint cRawOffset = ZSTD_resolveRepcodeToRawOffset(cRepcodes->rep, offBase, ll0); + if (dRawOffset != cRawOffset) + { + assert(cRawOffset > 0); + seq->offBase = cRawOffset + 3; + } + } + + ZSTD_updateRep(dRepcodes->rep, seq->offBase, ll0); + ZSTD_updateRep(cRepcodes->rep, offBase, ll0); + } + } + + /* ZSTD_compressSeqStore_singleBlock(): + * Compresses a seqStore into a block with a block header, into the buffer dst. + * + * Returns the total size of that block (including header) or a ZSTD error code. + */ + private static nuint ZSTD_compressSeqStore_singleBlock( + ZSTD_CCtx_s* zc, + SeqStore_t* seqStore, + repcodes_s* dRep, + repcodes_s* cRep, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + uint lastBlock, + uint isPartition + ) + { + const uint rleMaxLength = 25; + byte* op = (byte*)dst; + byte* ip = (byte*)src; + nuint cSize; + nuint cSeqsSize; + /* In case of an RLE or raw block, the simulated decompression repcode history must be reset */ + repcodes_s dRepOriginal = *dRep; + if (isPartition != 0) + ZSTD_seqStore_resolveOffCodes( + dRep, + cRep, + seqStore, + (uint)(seqStore->sequences - seqStore->sequencesStart) + ); + if (dstCapacity < ZSTD_blockHeaderSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + cSeqsSize = ZSTD_entropyCompressSeqStore( + seqStore, + &zc->blockState.prevCBlock->entropy, + &zc->blockState.nextCBlock->entropy, + &zc->appliedParams, + op + ZSTD_blockHeaderSize, + dstCapacity - ZSTD_blockHeaderSize, + srcSize, + zc->tmpWorkspace, + zc->tmpWkspSize, + zc->bmi2 + ); + { + nuint err_code = cSeqsSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if ( + zc->isFirstBlock == 0 + && cSeqsSize < rleMaxLength + && ZSTD_isRLE((byte*)src, srcSize) != 0 + ) + { + cSeqsSize = 1; + } + + if (zc->seqCollector.collectSequences != 0) + { + { + nuint err_code = ZSTD_copyBlockSequences( + &zc->seqCollector, + seqStore, + dRepOriginal.rep + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState); + return 0; + } + + if (cSeqsSize == 0) + { + cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, srcSize, lastBlock); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + *dRep = dRepOriginal; + } + else if (cSeqsSize == 1) + { + cSize = ZSTD_rleCompressBlock(op, dstCapacity, *ip, srcSize, lastBlock); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + *dRep = dRepOriginal; + } + else + { + ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState); + writeBlockHeader(op, cSeqsSize, srcSize, lastBlock); + cSize = ZSTD_blockHeaderSize + cSeqsSize; + } + + if ( + zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat.FSE_repeat_valid + ) + zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat.FSE_repeat_check; + return cSize; + } + + /* Helper function to perform the recursive search for block splits. + * Estimates the cost of seqStore prior to split, and estimates the cost of splitting the sequences in half. + * If advantageous to split, then we recurse down the two sub-blocks. + * If not, or if an error occurred in estimation, then we do not recurse. + * + * Note: The recursion depth is capped by a heuristic minimum number of sequences, + * defined by MIN_SEQUENCES_BLOCK_SPLITTING. + * In theory, this means the absolute largest recursion depth is 10 == log2(maxNbSeqInBlock/MIN_SEQUENCES_BLOCK_SPLITTING). + * In practice, recursion depth usually doesn't go beyond 4. + * + * Furthermore, the number of splits is capped by ZSTD_MAX_NB_BLOCK_SPLITS. + * At ZSTD_MAX_NB_BLOCK_SPLITS == 196 with the current existing blockSize + * maximum of 128 KB, this value is actually impossible to reach. + */ + private static void ZSTD_deriveBlockSplitsHelper( + seqStoreSplits* splits, + nuint startIdx, + nuint endIdx, + ZSTD_CCtx_s* zc, + SeqStore_t* origSeqStore + ) + { + SeqStore_t* fullSeqStoreChunk = &zc->blockSplitCtx.fullSeqStoreChunk; + SeqStore_t* firstHalfSeqStore = &zc->blockSplitCtx.firstHalfSeqStore; + SeqStore_t* secondHalfSeqStore = &zc->blockSplitCtx.secondHalfSeqStore; + nuint estimatedOriginalSize; + nuint estimatedFirstHalfSize; + nuint estimatedSecondHalfSize; + nuint midIdx = (startIdx + endIdx) / 2; + assert(endIdx >= startIdx); + if (endIdx - startIdx < 300 || splits->idx >= 196) + { + return; + } + + ZSTD_deriveSeqStoreChunk(fullSeqStoreChunk, origSeqStore, startIdx, endIdx); + ZSTD_deriveSeqStoreChunk(firstHalfSeqStore, origSeqStore, startIdx, midIdx); + ZSTD_deriveSeqStoreChunk(secondHalfSeqStore, origSeqStore, midIdx, endIdx); + estimatedOriginalSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize( + fullSeqStoreChunk, + zc + ); + estimatedFirstHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize( + firstHalfSeqStore, + zc + ); + estimatedSecondHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize( + secondHalfSeqStore, + zc + ); + if ( + ERR_isError(estimatedOriginalSize) + || ERR_isError(estimatedFirstHalfSize) + || ERR_isError(estimatedSecondHalfSize) + ) + { + return; + } + + if (estimatedFirstHalfSize + estimatedSecondHalfSize < estimatedOriginalSize) + { + ZSTD_deriveBlockSplitsHelper(splits, startIdx, midIdx, zc, origSeqStore); + splits->splitLocations[splits->idx] = (uint)midIdx; + splits->idx++; + ZSTD_deriveBlockSplitsHelper(splits, midIdx, endIdx, zc, origSeqStore); + } + } + + /* Base recursive function. + * Populates a table with intra-block partition indices that can improve compression ratio. + * + * @return: number of splits made (which equals the size of the partition table - 1). + */ + private static nuint ZSTD_deriveBlockSplits(ZSTD_CCtx_s* zc, uint* partitions, uint nbSeq) + { + seqStoreSplits splits; + splits.splitLocations = partitions; + splits.idx = 0; + if (nbSeq <= 4) + { + return 0; + } + + ZSTD_deriveBlockSplitsHelper(&splits, 0, nbSeq, zc, &zc->seqStore); + splits.splitLocations[splits.idx] = nbSeq; + return splits.idx; + } + + /* ZSTD_compressBlock_splitBlock(): + * Attempts to split a given block into multiple blocks to improve compression ratio. + * + * Returns combined size of all blocks (which includes headers), or a ZSTD error code. + */ + private static nuint ZSTD_compressBlock_splitBlock_internal( + ZSTD_CCtx_s* zc, + void* dst, + nuint dstCapacity, + void* src, + nuint blockSize, + uint lastBlock, + uint nbSeq + ) + { + nuint cSize = 0; + byte* ip = (byte*)src; + byte* op = (byte*)dst; + nuint i = 0; + nuint srcBytesTotal = 0; + /* size == ZSTD_MAX_NB_BLOCK_SPLITS */ + uint* partitions = zc->blockSplitCtx.partitions; + SeqStore_t* nextSeqStore = &zc->blockSplitCtx.nextSeqStore; + SeqStore_t* currSeqStore = &zc->blockSplitCtx.currSeqStore; + nuint numSplits = ZSTD_deriveBlockSplits(zc, partitions, nbSeq); + /* If a block is split and some partitions are emitted as RLE/uncompressed, then repcode history + * may become invalid. In order to reconcile potentially invalid repcodes, we keep track of two + * separate repcode histories that simulate repcode history on compression and decompression side, + * and use the histories to determine whether we must replace a particular repcode with its raw offset. + * + * 1) cRep gets updated for each partition, regardless of whether the block was emitted as uncompressed + * or RLE. This allows us to retrieve the offset value that an invalid repcode references within + * a nocompress/RLE block. + * 2) dRep gets updated only for compressed partitions, and when a repcode gets replaced, will use + * the replacement offset value rather than the original repcode to update the repcode history. + * dRep also will be the final repcode history sent to the next block. + * + * See ZSTD_seqStore_resolveOffCodes() for more details. + */ + repcodes_s dRep; + repcodes_s cRep; + memcpy(dRep.rep, zc->blockState.prevCBlock->rep, (uint)sizeof(repcodes_s)); + memcpy(cRep.rep, zc->blockState.prevCBlock->rep, (uint)sizeof(repcodes_s)); + *nextSeqStore = new SeqStore_t(); + if (numSplits == 0) + { + nuint cSizeSingleBlock = ZSTD_compressSeqStore_singleBlock( + zc, + &zc->seqStore, + &dRep, + &cRep, + op, + dstCapacity, + ip, + blockSize, + lastBlock, + 0 + ); + { + nuint err_code = cSizeSingleBlock; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(zc->blockSizeMax <= 1 << 17); + assert(cSizeSingleBlock <= zc->blockSizeMax + ZSTD_blockHeaderSize); + return cSizeSingleBlock; + } + + ZSTD_deriveSeqStoreChunk(currSeqStore, &zc->seqStore, 0, partitions[0]); + for (i = 0; i <= numSplits; ++i) + { + nuint cSizeChunk; + uint lastPartition = i == numSplits ? 1U : 0U; + uint lastBlockEntireSrc = 0; + nuint srcBytes = + ZSTD_countSeqStoreLiteralsBytes(currSeqStore) + + ZSTD_countSeqStoreMatchBytes(currSeqStore); + srcBytesTotal += srcBytes; + if (lastPartition != 0) + { + srcBytes += blockSize - srcBytesTotal; + lastBlockEntireSrc = lastBlock; + } + else + { + ZSTD_deriveSeqStoreChunk( + nextSeqStore, + &zc->seqStore, + partitions[i], + partitions[i + 1] + ); + } + + cSizeChunk = ZSTD_compressSeqStore_singleBlock( + zc, + currSeqStore, + &dRep, + &cRep, + op, + dstCapacity, + ip, + srcBytes, + lastBlockEntireSrc, + 1 + ); + { + nuint err_code = cSizeChunk; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ip += srcBytes; + op += cSizeChunk; + dstCapacity -= cSizeChunk; + cSize += cSizeChunk; + *currSeqStore = *nextSeqStore; + assert(cSizeChunk <= zc->blockSizeMax + ZSTD_blockHeaderSize); + } + + memcpy(zc->blockState.prevCBlock->rep, dRep.rep, (uint)sizeof(repcodes_s)); + return cSize; + } + + private static nuint ZSTD_compressBlock_splitBlock( + ZSTD_CCtx_s* zc, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + uint lastBlock + ) + { + uint nbSeq; + nuint cSize; + assert(zc->appliedParams.postBlockSplitter == ZSTD_paramSwitch_e.ZSTD_ps_enable); + { + nuint bss = ZSTD_buildSeqStore(zc, src, srcSize); + { + nuint err_code = bss; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (bss == (nuint)ZSTD_BuildSeqStore_e.ZSTDbss_noCompress) + { + if ( + zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode + == FSE_repeat.FSE_repeat_valid + ) + zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = + FSE_repeat.FSE_repeat_check; + if (zc->seqCollector.collectSequences != 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_sequenceProducer_failed) + ); + } + + cSize = ZSTD_noCompressBlock(dst, dstCapacity, src, srcSize, lastBlock); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return cSize; + } + + nbSeq = (uint)(zc->seqStore.sequences - zc->seqStore.sequencesStart); + } + + cSize = ZSTD_compressBlock_splitBlock_internal( + zc, + dst, + dstCapacity, + src, + srcSize, + lastBlock, + nbSeq + ); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return cSize; + } + + private static nuint ZSTD_compressBlock_internal( + ZSTD_CCtx_s* zc, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + uint frame + ) + { + /* This is an estimated upper bound for the length of an rle block. + * This isn't the actual upper bound. + * Finding the real threshold needs further investigation. + */ + const uint rleMaxLength = 25; + nuint cSize; + byte* ip = (byte*)src; + byte* op = (byte*)dst; + { + nuint bss = ZSTD_buildSeqStore(zc, src, srcSize); + { + nuint err_code = bss; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (bss == (nuint)ZSTD_BuildSeqStore_e.ZSTDbss_noCompress) + { + if (zc->seqCollector.collectSequences != 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_sequenceProducer_failed) + ); + } + + cSize = 0; + goto @out; + } + } + + if (zc->seqCollector.collectSequences != 0) + { + { + nuint err_code = ZSTD_copyBlockSequences( + &zc->seqCollector, + ZSTD_getSeqStore(zc), + zc->blockState.prevCBlock->rep + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState); + return 0; + } + + cSize = ZSTD_entropyCompressSeqStore( + &zc->seqStore, + &zc->blockState.prevCBlock->entropy, + &zc->blockState.nextCBlock->entropy, + &zc->appliedParams, + dst, + dstCapacity, + srcSize, + zc->tmpWorkspace, + zc->tmpWkspSize, + zc->bmi2 + ); + if ( + frame != 0 + && zc->isFirstBlock == 0 + && cSize < rleMaxLength + && ZSTD_isRLE(ip, srcSize) != 0 + ) + { + cSize = 1; + op[0] = ip[0]; + } + + @out: + if (!ERR_isError(cSize) && cSize > 1) + { + ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState); + } + + if ( + zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat.FSE_repeat_valid + ) + zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat.FSE_repeat_check; + return cSize; + } + + private static nuint ZSTD_compressBlock_targetCBlockSize_body( + ZSTD_CCtx_s* zc, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + nuint bss, + uint lastBlock + ) + { + if (bss == (nuint)ZSTD_BuildSeqStore_e.ZSTDbss_compress) + { + if ( + zc->isFirstBlock == 0 + && ZSTD_maybeRLE(&zc->seqStore) != 0 + && ZSTD_isRLE((byte*)src, srcSize) != 0 + ) + { + return ZSTD_rleCompressBlock(dst, dstCapacity, *(byte*)src, srcSize, lastBlock); + } + + { + nuint cSize = ZSTD_compressSuperBlock( + zc, + dst, + dstCapacity, + src, + srcSize, + lastBlock + ); + if (cSize != unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall))) + { + nuint maxCSize = + srcSize - ZSTD_minGain(srcSize, zc->appliedParams.cParams.strategy); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (cSize != 0 && cSize < maxCSize + ZSTD_blockHeaderSize) + { + ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState); + return cSize; + } + } + } + } + + return ZSTD_noCompressBlock(dst, dstCapacity, src, srcSize, lastBlock); + } + + private static nuint ZSTD_compressBlock_targetCBlockSize( + ZSTD_CCtx_s* zc, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + uint lastBlock + ) + { + nuint cSize = 0; + nuint bss = ZSTD_buildSeqStore(zc, src, srcSize); + { + nuint err_code = bss; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + cSize = ZSTD_compressBlock_targetCBlockSize_body( + zc, + dst, + dstCapacity, + src, + srcSize, + bss, + lastBlock + ); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if ( + zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat.FSE_repeat_valid + ) + zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat.FSE_repeat_check; + return cSize; + } + + private static void ZSTD_overflowCorrectIfNeeded( + ZSTD_MatchState_t* ms, + ZSTD_cwksp* ws, + ZSTD_CCtx_params_s* @params, + void* ip, + void* iend + ) + { + uint cycleLog = ZSTD_cycleLog(@params->cParams.chainLog, @params->cParams.strategy); + uint maxDist = (uint)1 << (int)@params->cParams.windowLog; + if ( + ZSTD_window_needOverflowCorrection( + ms->window, + cycleLog, + maxDist, + ms->loadedDictEnd, + ip, + iend + ) != 0 + ) + { + uint correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, maxDist, ip); + ZSTD_cwksp_mark_tables_dirty(ws); + ZSTD_reduceIndex(ms, @params, correction); + ZSTD_cwksp_mark_tables_clean(ws); + if (ms->nextToUpdate < correction) + ms->nextToUpdate = 0; + else + ms->nextToUpdate -= correction; + ms->loadedDictEnd = 0; + ms->dictMatchState = null; + } + } + +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_splitLevels => + new int[10] { 0, 0, 1, 2, 2, 3, 3, 4, 4, 4 }; + private static int* splitLevels => + (int*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_splitLevels) + ); +#else + + private static readonly int* splitLevels = GetArrayPointer( + new int[10] { 0, 0, 1, 2, 2, 3, 3, 4, 4, 4 } + ); +#endif + + private static nuint ZSTD_optimalBlockSize( + ZSTD_CCtx_s* cctx, + void* src, + nuint srcSize, + nuint blockSizeMax, + int splitLevel, + ZSTD_strategy strat, + long savings + ) + { + if (srcSize < 128 * (1 << 10) || blockSizeMax < 128 * (1 << 10)) + return srcSize < blockSizeMax ? srcSize : blockSizeMax; + if (savings < 3) + { + return 128 * (1 << 10); + } + + if (splitLevel == 1) + return 128 * (1 << 10); + if (splitLevel == 0) + { + assert(ZSTD_strategy.ZSTD_fast <= strat && strat <= ZSTD_strategy.ZSTD_btultra2); + splitLevel = splitLevels[(int)strat]; + } + else + { + assert(2 <= splitLevel && splitLevel <= 6); + splitLevel -= 2; + } + + return ZSTD_splitBlock( + src, + blockSizeMax, + splitLevel, + cctx->tmpWorkspace, + cctx->tmpWkspSize + ); + } + + /*! ZSTD_compress_frameChunk() : + * Compress a chunk of data into one or multiple blocks. + * All blocks will be terminated, all input will be consumed. + * Function will issue an error if there is not enough `dstCapacity` to hold the compressed content. + * Frame is supposed already started (header already produced) + * @return : compressed size, or an error code + */ + private static nuint ZSTD_compress_frameChunk( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + uint lastFrameChunk + ) + { + nuint blockSizeMax = cctx->blockSizeMax; + nuint remaining = srcSize; + byte* ip = (byte*)src; + byte* ostart = (byte*)dst; + byte* op = ostart; + uint maxDist = (uint)1 << (int)cctx->appliedParams.cParams.windowLog; + long savings = (long)cctx->consumedSrcSize - (long)cctx->producedCSize; + assert(cctx->appliedParams.cParams.windowLog <= (uint)(sizeof(nuint) == 4 ? 30 : 31)); + if (cctx->appliedParams.fParams.checksumFlag != 0 && srcSize != 0) + ZSTD_XXH64_update(&cctx->xxhState, src, srcSize); + while (remaining != 0) + { + ZSTD_MatchState_t* ms = &cctx->blockState.matchState; + nuint blockSize = ZSTD_optimalBlockSize( + cctx, + ip, + remaining, + blockSizeMax, + cctx->appliedParams.preBlockSplitter_level, + cctx->appliedParams.cParams.strategy, + savings + ); + uint lastBlock = lastFrameChunk & (uint)(blockSize == remaining ? 1 : 0); + assert(blockSize <= remaining); + if (dstCapacity < ZSTD_blockHeaderSize + (nuint)(1 + 1) + 1) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + ZSTD_overflowCorrectIfNeeded( + ms, + &cctx->workspace, + &cctx->appliedParams, + ip, + ip + blockSize + ); + ZSTD_checkDictValidity( + &ms->window, + ip + blockSize, + maxDist, + &ms->loadedDictEnd, + &ms->dictMatchState + ); + ZSTD_window_enforceMaxDist( + &ms->window, + ip, + maxDist, + &ms->loadedDictEnd, + &ms->dictMatchState + ); + if (ms->nextToUpdate < ms->window.lowLimit) + ms->nextToUpdate = ms->window.lowLimit; + { + nuint cSize; + if (ZSTD_useTargetCBlockSize(&cctx->appliedParams) != 0) + { + cSize = ZSTD_compressBlock_targetCBlockSize( + cctx, + op, + dstCapacity, + ip, + blockSize, + lastBlock + ); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(cSize > 0); + assert(cSize <= blockSize + ZSTD_blockHeaderSize); + } + else if (ZSTD_blockSplitterEnabled(&cctx->appliedParams) != 0) + { + cSize = ZSTD_compressBlock_splitBlock( + cctx, + op, + dstCapacity, + ip, + blockSize, + lastBlock + ); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(cSize > 0 || cctx->seqCollector.collectSequences == 1); + } + else + { + cSize = ZSTD_compressBlock_internal( + cctx, + op + ZSTD_blockHeaderSize, + dstCapacity - ZSTD_blockHeaderSize, + ip, + blockSize, + 1 + ); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (cSize == 0) + { + cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + } + else + { + uint cBlockHeader = + cSize == 1 + ? lastBlock + + ((uint)blockType_e.bt_rle << 1) + + (uint)(blockSize << 3) + : lastBlock + + ((uint)blockType_e.bt_compressed << 1) + + (uint)(cSize << 3); + MEM_writeLE24(op, cBlockHeader); + cSize += ZSTD_blockHeaderSize; + } + } + + savings += (long)blockSize - (long)cSize; + ip += blockSize; + assert(remaining >= blockSize); + remaining -= blockSize; + op += cSize; + assert(dstCapacity >= cSize); + dstCapacity -= cSize; + cctx->isFirstBlock = 0; + } + } + + if (lastFrameChunk != 0 && op > ostart) + cctx->stage = ZSTD_compressionStage_e.ZSTDcs_ending; + return (nuint)(op - ostart); + } + + private static nuint ZSTD_writeFrameHeader( + void* dst, + nuint dstCapacity, + ZSTD_CCtx_params_s* @params, + ulong pledgedSrcSize, + uint dictID + ) + { + byte* op = (byte*)dst; + /* 0-3 */ + uint dictIDSizeCodeLength = (uint)( + (dictID > 0 ? 1 : 0) + (dictID >= 256 ? 1 : 0) + (dictID >= 65536 ? 1 : 0) + ); + /* 0-3 */ + uint dictIDSizeCode = @params->fParams.noDictIDFlag != 0 ? 0 : dictIDSizeCodeLength; + uint checksumFlag = @params->fParams.checksumFlag > 0 ? 1U : 0U; + uint windowSize = (uint)1 << (int)@params->cParams.windowLog; + uint singleSegment = + @params->fParams.contentSizeFlag != 0 && windowSize >= pledgedSrcSize ? 1U : 0U; + byte windowLogByte = (byte)(@params->cParams.windowLog - 10 << 3); + uint fcsCode = (uint)( + @params->fParams.contentSizeFlag != 0 + ? (pledgedSrcSize >= 256 ? 1 : 0) + + (pledgedSrcSize >= 65536 + 256 ? 1 : 0) + + (pledgedSrcSize >= 0xFFFFFFFFU ? 1 : 0) + : 0 + ); + byte frameHeaderDescriptionByte = (byte)( + dictIDSizeCode + (checksumFlag << 2) + (singleSegment << 5) + (fcsCode << 6) + ); + nuint pos = 0; + assert(!(@params->fParams.contentSizeFlag != 0 && pledgedSrcSize == unchecked(0UL - 1))); + if (dstCapacity < 18) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (@params->format == ZSTD_format_e.ZSTD_f_zstd1) + { + MEM_writeLE32(dst, 0xFD2FB528); + pos = 4; + } + + op[pos++] = frameHeaderDescriptionByte; + if (singleSegment == 0) + op[pos++] = windowLogByte; + switch (dictIDSizeCode) + { + default: + assert(0 != 0); + goto case 0; + case 0: + break; + case 1: + op[pos] = (byte)dictID; + pos++; + break; + case 2: + MEM_writeLE16(op + pos, (ushort)dictID); + pos += 2; + break; + case 3: + MEM_writeLE32(op + pos, dictID); + pos += 4; + break; + } + + switch (fcsCode) + { + default: + assert(0 != 0); + goto case 0; + case 0: + if (singleSegment != 0) + op[pos++] = (byte)pledgedSrcSize; + break; + case 1: + MEM_writeLE16(op + pos, (ushort)(pledgedSrcSize - 256)); + pos += 2; + break; + case 2: + MEM_writeLE32(op + pos, (uint)pledgedSrcSize); + pos += 4; + break; + case 3: + MEM_writeLE64(op + pos, pledgedSrcSize); + pos += 8; + break; + } + + return pos; + } + + /* ZSTD_writeSkippableFrame_advanced() : + * Writes out a skippable frame with the specified magic number variant (16 are supported), + * from ZSTD_MAGIC_SKIPPABLE_START to ZSTD_MAGIC_SKIPPABLE_START+15, and the desired source data. + * + * Returns the total number of bytes written, or a ZSTD error code. + */ + public static nuint ZSTD_writeSkippableFrame( + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + uint magicVariant + ) + { + byte* op = (byte*)dst; + if (dstCapacity < srcSize + 8) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (srcSize > 0xFFFFFFFF) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if (magicVariant > 15) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + MEM_writeLE32(op, 0x184D2A50 + magicVariant); + MEM_writeLE32(op + 4, (uint)srcSize); + memcpy(op + 8, src, (uint)srcSize); + return srcSize + 8; + } + + /* ZSTD_writeLastEmptyBlock() : + * output an empty Block with end-of-frame mark to complete a frame + * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h)) + * or an error code if `dstCapacity` is too small (stage == ZSTD_compressionStage_e.ZSTDcs_init); + assert( + nbSeq == 0 + || cctx->appliedParams.ldmParams.enableLdm != ZSTD_paramSwitch_e.ZSTD_ps_enable + ); + cctx->externSeqStore.seq = seq; + cctx->externSeqStore.size = nbSeq; + cctx->externSeqStore.capacity = nbSeq; + cctx->externSeqStore.pos = 0; + cctx->externSeqStore.posInSequence = 0; + } + + private static nuint ZSTD_compressContinue_internal( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + uint frame, + uint lastFrameChunk + ) + { + ZSTD_MatchState_t* ms = &cctx->blockState.matchState; + nuint fhSize = 0; + if (cctx->stage == ZSTD_compressionStage_e.ZSTDcs_created) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + if (frame != 0 && cctx->stage == ZSTD_compressionStage_e.ZSTDcs_init) + { + fhSize = ZSTD_writeFrameHeader( + dst, + dstCapacity, + &cctx->appliedParams, + cctx->pledgedSrcSizePlusOne - 1, + cctx->dictID + ); + { + nuint err_code = fhSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(fhSize <= dstCapacity); + dstCapacity -= fhSize; + dst = (sbyte*)dst + fhSize; + cctx->stage = ZSTD_compressionStage_e.ZSTDcs_ongoing; + } + + if (srcSize == 0) + return fhSize; + if (ZSTD_window_update(&ms->window, src, srcSize, ms->forceNonContiguous) == 0) + { + ms->forceNonContiguous = 0; + ms->nextToUpdate = ms->window.dictLimit; + } + + if (cctx->appliedParams.ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + ZSTD_window_update(&cctx->ldmState.window, src, srcSize, 0); + } + + if (frame == 0) + { + ZSTD_overflowCorrectIfNeeded( + ms, + &cctx->workspace, + &cctx->appliedParams, + src, + (byte*)src + srcSize + ); + } + + { + nuint cSize = + frame != 0 + ? ZSTD_compress_frameChunk(cctx, dst, dstCapacity, src, srcSize, lastFrameChunk) + : ZSTD_compressBlock_internal(cctx, dst, dstCapacity, src, srcSize, 0); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + cctx->consumedSrcSize += srcSize; + cctx->producedCSize += cSize + fhSize; + assert( + !( + cctx->appliedParams.fParams.contentSizeFlag != 0 + && cctx->pledgedSrcSizePlusOne == 0 + ) + ); + if (cctx->pledgedSrcSizePlusOne != 0) + { + if (cctx->consumedSrcSize + 1 > cctx->pledgedSrcSizePlusOne) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + } + + return cSize + fhSize; + } + } + + private static nuint ZSTD_compressContinue_public( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1, 0); + } + + /* NOTE: Must just wrap ZSTD_compressContinue_public() */ + public static nuint ZSTD_compressContinue( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + return ZSTD_compressContinue_public(cctx, dst, dstCapacity, src, srcSize); + } + + private static nuint ZSTD_getBlockSize_deprecated(ZSTD_CCtx_s* cctx) + { + ZSTD_compressionParameters cParams = cctx->appliedParams.cParams; + assert(ZSTD_checkCParams(cParams) == 0); + return cctx->appliedParams.maxBlockSize < (nuint)1 << (int)cParams.windowLog + ? cctx->appliedParams.maxBlockSize + : (nuint)1 << (int)cParams.windowLog; + } + + /* NOTE: Must just wrap ZSTD_getBlockSize_deprecated() */ + public static nuint ZSTD_getBlockSize(ZSTD_CCtx_s* cctx) + { + return ZSTD_getBlockSize_deprecated(cctx); + } + + /* NOTE: Must just wrap ZSTD_compressBlock_deprecated() */ + private static nuint ZSTD_compressBlock_deprecated( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + { + nuint blockSizeMax = ZSTD_getBlockSize_deprecated(cctx); + if (srcSize > blockSizeMax) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + } + + return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0, 0); + } + + /* NOTE: Must just wrap ZSTD_compressBlock_deprecated() */ + public static nuint ZSTD_compressBlock( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_deprecated(cctx, dst, dstCapacity, src, srcSize); + } + + /*! ZSTD_loadDictionaryContent() : + * @return : 0, or an error code + */ + private static nuint ZSTD_loadDictionaryContent( + ZSTD_MatchState_t* ms, + ldmState_t* ls, + ZSTD_cwksp* ws, + ZSTD_CCtx_params_s* @params, + void* src, + nuint srcSize, + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp + ) + { + byte* ip = (byte*)src; + byte* iend = ip + srcSize; + int loadLdmDict = + @params->ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable && ls != null ? 1 : 0; + ZSTD_assertEqualCParams(@params->cParams, ms->cParams); + { + /* Allow the dictionary to set indices up to exactly ZSTD_CURRENT_MAX. + * Dictionaries right at the edge will immediately trigger overflow + * correction, but I don't want to insert extra constraints here. + */ + uint maxDictSize = (MEM_64bits ? 3500U * (1 << 20) : 2000U * (1 << 20)) - 2; + int CDictTaggedIndices = ZSTD_CDictIndicesAreTagged(&@params->cParams); + if (CDictTaggedIndices != 0 && tfp == ZSTD_tableFillPurpose_e.ZSTD_tfp_forCDict) + { + /* Some dictionary matchfinders in zstd use "short cache", + * which treats the lower ZSTD_SHORT_CACHE_TAG_BITS of each + * CDict hashtable entry as a tag rather than as part of an index. + * When short cache is used, we need to truncate the dictionary + * so that its indices don't overlap with the tag. */ + const uint shortCacheMaxDictSize = (1U << 32 - 8) - 2; + maxDictSize = + maxDictSize < shortCacheMaxDictSize ? maxDictSize : shortCacheMaxDictSize; + assert(loadLdmDict == 0); + } + + if (srcSize > maxDictSize) + { + ip = iend - maxDictSize; + src = ip; + srcSize = maxDictSize; + } + } + + if (srcSize > unchecked((uint)-1) - (MEM_64bits ? 3500U * (1 << 20) : 2000U * (1 << 20))) + { + assert(ZSTD_window_isEmpty(ms->window) != 0); +#if DEBUG + if (loadLdmDict != 0) + assert(ZSTD_window_isEmpty(ls->window) != 0); +#endif + } + + ZSTD_window_update(&ms->window, src, srcSize, 0); + if (loadLdmDict != 0) + { + ZSTD_window_update(&ls->window, src, srcSize, 0); + ls->loadedDictEnd = @params->forceWindow != 0 ? 0 : (uint)(iend - ls->window.@base); + ZSTD_ldm_fillHashTable(ls, ip, iend, &@params->ldmParams); + } + + { + uint maxDictSize = + 1U + << (int)( + ( + @params->cParams.hashLog + 3 > @params->cParams.chainLog + 1 + ? @params->cParams.hashLog + 3 + : @params->cParams.chainLog + 1 + ) < 31 + ? @params->cParams.hashLog + 3 > @params->cParams.chainLog + 1 + ? @params->cParams.hashLog + 3 + : @params->cParams.chainLog + 1 + : 31 + ); + if (srcSize > maxDictSize) + { + ip = iend - maxDictSize; + src = ip; + srcSize = maxDictSize; + } + } + + ms->nextToUpdate = (uint)(ip - ms->window.@base); + ms->loadedDictEnd = @params->forceWindow != 0 ? 0 : (uint)(iend - ms->window.@base); + ms->forceNonContiguous = @params->deterministicRefPrefix; + if (srcSize <= 8) + return 0; + ZSTD_overflowCorrectIfNeeded(ms, ws, @params, ip, iend); + switch (@params->cParams.strategy) + { + case ZSTD_strategy.ZSTD_fast: + ZSTD_fillHashTable(ms, iend, dtlm, tfp); + break; + case ZSTD_strategy.ZSTD_dfast: + ZSTD_fillDoubleHashTable(ms, iend, dtlm, tfp); + break; + case ZSTD_strategy.ZSTD_greedy: + case ZSTD_strategy.ZSTD_lazy: + case ZSTD_strategy.ZSTD_lazy2: + assert(srcSize >= 8); + if (ms->dedicatedDictSearch != 0) + { + assert(ms->chainTable != null); + ZSTD_dedicatedDictSearch_lazy_loadDictionary(ms, iend - 8); + } + else + { + assert(@params->useRowMatchFinder != ZSTD_paramSwitch_e.ZSTD_ps_auto); + if (@params->useRowMatchFinder == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + nuint tagTableSize = (nuint)1 << (int)@params->cParams.hashLog; + memset(ms->tagTable, 0, (uint)tagTableSize); + ZSTD_row_update(ms, iend - 8); + } + else + { + ZSTD_insertAndFindFirstIndex(ms, iend - 8); + } + } + + break; + case ZSTD_strategy.ZSTD_btlazy2: + case ZSTD_strategy.ZSTD_btopt: + case ZSTD_strategy.ZSTD_btultra: + case ZSTD_strategy.ZSTD_btultra2: + assert(srcSize >= 8); + ZSTD_updateTree(ms, iend - 8, iend); + break; + default: + assert(0 != 0); + break; + } + + ms->nextToUpdate = (uint)(iend - ms->window.@base); + return 0; + } + + /* Dictionaries that assign zero probability to symbols that show up causes problems + * when FSE encoding. Mark dictionaries with zero probability symbols as FSE_repeat_check + * and only dictionaries with 100% valid symbols can be assumed valid. + */ + private static FSE_repeat ZSTD_dictNCountRepeat( + short* normalizedCounter, + uint dictMaxSymbolValue, + uint maxSymbolValue + ) + { + uint s; + if (dictMaxSymbolValue < maxSymbolValue) + { + return FSE_repeat.FSE_repeat_check; + } + + for (s = 0; s <= maxSymbolValue; ++s) + { + if (normalizedCounter[s] == 0) + { + return FSE_repeat.FSE_repeat_check; + } + } + + return FSE_repeat.FSE_repeat_valid; + } + + /* ZSTD_loadCEntropy() : + * dict : must point at beginning of a valid zstd dictionary. + * return : size of dictionary header (size of magic number + dict ID + entropy tables) + * assumptions : magic number supposed already checked + * and dictSize >= 8 */ + private static nuint ZSTD_loadCEntropy( + ZSTD_compressedBlockState_t* bs, + void* workspace, + void* dict, + nuint dictSize + ) + { + short* offcodeNCount = stackalloc short[32]; + uint offcodeMaxValue = 31; + /* skip magic num and dict ID */ + byte* dictPtr = (byte*)dict; + byte* dictEnd = dictPtr + dictSize; + dictPtr += 8; + bs->entropy.huf.repeatMode = HUF_repeat.HUF_repeat_check; + { + uint maxSymbolValue = 255; + uint hasZeroWeights = 1; + nuint hufHeaderSize = HUF_readCTable( + &bs->entropy.huf.CTable.e0, + &maxSymbolValue, + dictPtr, + (nuint)(dictEnd - dictPtr), + &hasZeroWeights + ); + if (hasZeroWeights == 0 && maxSymbolValue == 255) + bs->entropy.huf.repeatMode = HUF_repeat.HUF_repeat_valid; + if (ERR_isError(hufHeaderSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + dictPtr += hufHeaderSize; + } + + { + uint offcodeLog; + nuint offcodeHeaderSize = FSE_readNCount( + offcodeNCount, + &offcodeMaxValue, + &offcodeLog, + dictPtr, + (nuint)(dictEnd - dictPtr) + ); + if (ERR_isError(offcodeHeaderSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if (offcodeLog > 8) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if ( + ERR_isError( + FSE_buildCTable_wksp( + bs->entropy.fse.offcodeCTable, + offcodeNCount, + 31, + offcodeLog, + workspace, + (8 << 10) + 512 + ) + ) + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + dictPtr += offcodeHeaderSize; + } + + { + short* matchlengthNCount = stackalloc short[53]; + uint matchlengthMaxValue = 52, + matchlengthLog; + nuint matchlengthHeaderSize = FSE_readNCount( + matchlengthNCount, + &matchlengthMaxValue, + &matchlengthLog, + dictPtr, + (nuint)(dictEnd - dictPtr) + ); + if (ERR_isError(matchlengthHeaderSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if (matchlengthLog > 9) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if ( + ERR_isError( + FSE_buildCTable_wksp( + bs->entropy.fse.matchlengthCTable, + matchlengthNCount, + matchlengthMaxValue, + matchlengthLog, + workspace, + (8 << 10) + 512 + ) + ) + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + bs->entropy.fse.matchlength_repeatMode = ZSTD_dictNCountRepeat( + matchlengthNCount, + matchlengthMaxValue, + 52 + ); + dictPtr += matchlengthHeaderSize; + } + + { + short* litlengthNCount = stackalloc short[36]; + uint litlengthMaxValue = 35, + litlengthLog; + nuint litlengthHeaderSize = FSE_readNCount( + litlengthNCount, + &litlengthMaxValue, + &litlengthLog, + dictPtr, + (nuint)(dictEnd - dictPtr) + ); + if (ERR_isError(litlengthHeaderSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if (litlengthLog > 9) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if ( + ERR_isError( + FSE_buildCTable_wksp( + bs->entropy.fse.litlengthCTable, + litlengthNCount, + litlengthMaxValue, + litlengthLog, + workspace, + (8 << 10) + 512 + ) + ) + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + bs->entropy.fse.litlength_repeatMode = ZSTD_dictNCountRepeat( + litlengthNCount, + litlengthMaxValue, + 35 + ); + dictPtr += litlengthHeaderSize; + } + + if (dictPtr + 12 > dictEnd) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + bs->rep[0] = MEM_readLE32(dictPtr + 0); + bs->rep[1] = MEM_readLE32(dictPtr + 4); + bs->rep[2] = MEM_readLE32(dictPtr + 8); + dictPtr += 12; + { + nuint dictContentSize = (nuint)(dictEnd - dictPtr); + uint offcodeMax = 31; + if (dictContentSize <= unchecked((uint)-1) - 128 * (1 << 10)) + { + /* The maximum offset that must be supported */ + uint maxOffset = (uint)dictContentSize + 128 * (1 << 10); + offcodeMax = ZSTD_highbit32(maxOffset); + } + + bs->entropy.fse.offcode_repeatMode = ZSTD_dictNCountRepeat( + offcodeNCount, + offcodeMaxValue, + offcodeMax < 31 ? offcodeMax : 31 + ); + { + uint u; + for (u = 0; u < 3; u++) + { + if (bs->rep[u] == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted) + ); + } + + if (bs->rep[u] > dictContentSize) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted) + ); + } + } + } + } + + return (nuint)(dictPtr - (byte*)dict); + } + + /* Dictionary format : + * See : + * https://github.com/facebook/zstd/blob/release/doc/zstd_compression_format.md#dictionary-format + */ + /*! ZSTD_loadZstdDictionary() : + * @return : dictID, or an error code + * assumptions : magic number supposed already checked + * dictSize supposed >= 8 + */ + private static nuint ZSTD_loadZstdDictionary( + ZSTD_compressedBlockState_t* bs, + ZSTD_MatchState_t* ms, + ZSTD_cwksp* ws, + ZSTD_CCtx_params_s* @params, + void* dict, + nuint dictSize, + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp, + void* workspace + ) + { + byte* dictPtr = (byte*)dict; + byte* dictEnd = dictPtr + dictSize; + nuint dictID; + nuint eSize; + assert(dictSize >= 8); + assert(MEM_readLE32(dictPtr) == 0xEC30A437); + dictID = @params->fParams.noDictIDFlag != 0 ? 0 : MEM_readLE32(dictPtr + 4); + eSize = ZSTD_loadCEntropy(bs, workspace, dict, dictSize); + { + nuint err_code = eSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + dictPtr += eSize; + { + nuint dictContentSize = (nuint)(dictEnd - dictPtr); + { + nuint err_code = ZSTD_loadDictionaryContent( + ms, + null, + ws, + @params, + dictPtr, + dictContentSize, + dtlm, + tfp + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + } + + return dictID; + } + + /** ZSTD_compress_insertDictionary() : + * @return : dictID, or an error code */ + private static nuint ZSTD_compress_insertDictionary( + ZSTD_compressedBlockState_t* bs, + ZSTD_MatchState_t* ms, + ldmState_t* ls, + ZSTD_cwksp* ws, + ZSTD_CCtx_params_s* @params, + void* dict, + nuint dictSize, + ZSTD_dictContentType_e dictContentType, + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp, + void* workspace + ) + { + if (dict == null || dictSize < 8) + { + if (dictContentType == ZSTD_dictContentType_e.ZSTD_dct_fullDict) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_wrong)); + } + + return 0; + } + + ZSTD_reset_compressedBlockState(bs); + if (dictContentType == ZSTD_dictContentType_e.ZSTD_dct_rawContent) + return ZSTD_loadDictionaryContent(ms, ls, ws, @params, dict, dictSize, dtlm, tfp); + if (MEM_readLE32(dict) != 0xEC30A437) + { + if (dictContentType == ZSTD_dictContentType_e.ZSTD_dct_auto) + { + return ZSTD_loadDictionaryContent(ms, ls, ws, @params, dict, dictSize, dtlm, tfp); + } + + if (dictContentType == ZSTD_dictContentType_e.ZSTD_dct_fullDict) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_wrong)); + } + + assert(0 != 0); + } + + return ZSTD_loadZstdDictionary(bs, ms, ws, @params, dict, dictSize, dtlm, tfp, workspace); + } + + /*! ZSTD_compressBegin_internal() : + * Assumption : either @dict OR @cdict (or none) is non-NULL, never both + * @return : 0, or an error code */ + private static nuint ZSTD_compressBegin_internal( + ZSTD_CCtx_s* cctx, + void* dict, + nuint dictSize, + ZSTD_dictContentType_e dictContentType, + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_CDict_s* cdict, + ZSTD_CCtx_params_s* @params, + ulong pledgedSrcSize, + ZSTD_buffered_policy_e zbuff + ) + { + nuint dictContentSize = cdict != null ? cdict->dictContentSize : dictSize; + assert(!ERR_isError(ZSTD_checkCParams(@params->cParams))); + assert(!(dict != null && cdict != null)); + if ( + cdict != null + && cdict->dictContentSize > 0 + && ( + pledgedSrcSize < 128 * (1 << 10) + || pledgedSrcSize < cdict->dictContentSize * 6UL + || pledgedSrcSize == unchecked(0UL - 1) + || cdict->compressionLevel == 0 + ) + && @params->attachDictPref != ZSTD_dictAttachPref_e.ZSTD_dictForceLoad + ) + { + return ZSTD_resetCCtx_usingCDict(cctx, cdict, @params, pledgedSrcSize, zbuff); + } + + { + nuint err_code = ZSTD_resetCCtx_internal( + cctx, + @params, + pledgedSrcSize, + dictContentSize, + ZSTD_compResetPolicy_e.ZSTDcrp_makeClean, + zbuff + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint dictID = + cdict != null + ? ZSTD_compress_insertDictionary( + cctx->blockState.prevCBlock, + &cctx->blockState.matchState, + &cctx->ldmState, + &cctx->workspace, + &cctx->appliedParams, + cdict->dictContent, + cdict->dictContentSize, + cdict->dictContentType, + dtlm, + ZSTD_tableFillPurpose_e.ZSTD_tfp_forCCtx, + cctx->tmpWorkspace + ) + : ZSTD_compress_insertDictionary( + cctx->blockState.prevCBlock, + &cctx->blockState.matchState, + &cctx->ldmState, + &cctx->workspace, + &cctx->appliedParams, + dict, + dictSize, + dictContentType, + dtlm, + ZSTD_tableFillPurpose_e.ZSTD_tfp_forCCtx, + cctx->tmpWorkspace + ); + { + nuint err_code = dictID; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(dictID <= 0xffffffff); + cctx->dictID = (uint)dictID; + cctx->dictContentSize = dictContentSize; + } + + return 0; + } + + /* ZSTD_compressBegin_advanced_internal() : + * Private use only. To be called from zstdmt_compress.c. */ + private static nuint ZSTD_compressBegin_advanced_internal( + ZSTD_CCtx_s* cctx, + void* dict, + nuint dictSize, + ZSTD_dictContentType_e dictContentType, + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_CDict_s* cdict, + ZSTD_CCtx_params_s* @params, + ulong pledgedSrcSize + ) + { + { + /* compression parameters verification and optimization */ + nuint err_code = ZSTD_checkCParams(@params->cParams); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return ZSTD_compressBegin_internal( + cctx, + dict, + dictSize, + dictContentType, + dtlm, + cdict, + @params, + pledgedSrcSize, + ZSTD_buffered_policy_e.ZSTDb_not_buffered + ); + } + + /*! ZSTD_compressBegin_advanced() : + * @return : 0, or an error code */ + public static nuint ZSTD_compressBegin_advanced( + ZSTD_CCtx_s* cctx, + void* dict, + nuint dictSize, + ZSTD_parameters @params, + ulong pledgedSrcSize + ) + { + ZSTD_CCtx_params_s cctxParams; + ZSTD_CCtxParams_init_internal(&cctxParams, &@params, 0); + return ZSTD_compressBegin_advanced_internal( + cctx, + dict, + dictSize, + ZSTD_dictContentType_e.ZSTD_dct_auto, + ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast, + null, + &cctxParams, + pledgedSrcSize + ); + } + + private static nuint ZSTD_compressBegin_usingDict_deprecated( + ZSTD_CCtx_s* cctx, + void* dict, + nuint dictSize, + int compressionLevel + ) + { + ZSTD_CCtx_params_s cctxParams; + { + ZSTD_parameters @params = ZSTD_getParams_internal( + compressionLevel, + unchecked(0UL - 1), + dictSize, + ZSTD_CParamMode_e.ZSTD_cpm_noAttachDict + ); + ZSTD_CCtxParams_init_internal( + &cctxParams, + &@params, + compressionLevel == 0 ? 3 : compressionLevel + ); + } + + return ZSTD_compressBegin_internal( + cctx, + dict, + dictSize, + ZSTD_dictContentType_e.ZSTD_dct_auto, + ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast, + null, + &cctxParams, + unchecked(0UL - 1), + ZSTD_buffered_policy_e.ZSTDb_not_buffered + ); + } + + public static nuint ZSTD_compressBegin_usingDict( + ZSTD_CCtx_s* cctx, + void* dict, + nuint dictSize, + int compressionLevel + ) + { + return ZSTD_compressBegin_usingDict_deprecated(cctx, dict, dictSize, compressionLevel); + } + + /*===== Buffer-less streaming compression functions =====*/ + public static nuint ZSTD_compressBegin(ZSTD_CCtx_s* cctx, int compressionLevel) + { + return ZSTD_compressBegin_usingDict_deprecated(cctx, null, 0, compressionLevel); + } + + /*! ZSTD_writeEpilogue() : + * Ends a frame. + * @return : nb of bytes written into dst (or an error code) */ + private static nuint ZSTD_writeEpilogue(ZSTD_CCtx_s* cctx, void* dst, nuint dstCapacity) + { + byte* ostart = (byte*)dst; + byte* op = ostart; + if (cctx->stage == ZSTD_compressionStage_e.ZSTDcs_created) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + if (cctx->stage == ZSTD_compressionStage_e.ZSTDcs_init) + { + nuint fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, &cctx->appliedParams, 0, 0); + { + nuint err_code = fhSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + dstCapacity -= fhSize; + op += fhSize; + cctx->stage = ZSTD_compressionStage_e.ZSTDcs_ongoing; + } + + if (cctx->stage != ZSTD_compressionStage_e.ZSTDcs_ending) + { + /* last block */ + uint cBlockHeader24 = 1 + ((uint)blockType_e.bt_raw << 1) + 0; + if (dstCapacity < 3) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + MEM_writeLE24(op, cBlockHeader24); + op += ZSTD_blockHeaderSize; + dstCapacity -= ZSTD_blockHeaderSize; + } + + if (cctx->appliedParams.fParams.checksumFlag != 0) + { + uint checksum = (uint)ZSTD_XXH64_digest(&cctx->xxhState); + if (dstCapacity < 4) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + MEM_writeLE32(op, checksum); + op += 4; + } + + cctx->stage = ZSTD_compressionStage_e.ZSTDcs_created; + return (nuint)(op - ostart); + } + + /** ZSTD_CCtx_trace() : + * Trace the end of a compression call. + */ + private static void ZSTD_CCtx_trace(ZSTD_CCtx_s* cctx, nuint extraCSize) { } + + private static nuint ZSTD_compressEnd_public( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + nuint endResult; + nuint cSize = ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1, 1); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + endResult = ZSTD_writeEpilogue(cctx, (sbyte*)dst + cSize, dstCapacity - cSize); + { + nuint err_code = endResult; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert( + !(cctx->appliedParams.fParams.contentSizeFlag != 0 && cctx->pledgedSrcSizePlusOne == 0) + ); + if (cctx->pledgedSrcSizePlusOne != 0) + { + if (cctx->pledgedSrcSizePlusOne != cctx->consumedSrcSize + 1) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + } + + ZSTD_CCtx_trace(cctx, endResult); + return cSize + endResult; + } + + /* NOTE: Must just wrap ZSTD_compressEnd_public() */ + public static nuint ZSTD_compressEnd( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); + } + + /*! ZSTD_compress_advanced() : + * Note : this function is now DEPRECATED. + * It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_setParameter() and other parameter setters. + * This prototype will generate compilation warnings. */ + public static nuint ZSTD_compress_advanced( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + void* dict, + nuint dictSize, + ZSTD_parameters @params + ) + { + { + nuint err_code = ZSTD_checkCParams(@params.cParams); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ZSTD_CCtxParams_init_internal(&cctx->simpleApiParams, &@params, 0); + return ZSTD_compress_advanced_internal( + cctx, + dst, + dstCapacity, + src, + srcSize, + dict, + dictSize, + &cctx->simpleApiParams + ); + } + + /* Internal */ + private static nuint ZSTD_compress_advanced_internal( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + void* dict, + nuint dictSize, + ZSTD_CCtx_params_s* @params + ) + { + { + nuint err_code = ZSTD_compressBegin_internal( + cctx, + dict, + dictSize, + ZSTD_dictContentType_e.ZSTD_dct_auto, + ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast, + null, + @params, + srcSize, + ZSTD_buffered_policy_e.ZSTDb_not_buffered + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); + } + + /************************** + * Simple dictionary API + ***************************/ + /*! ZSTD_compress_usingDict() : + * Compression at an explicit compression level using a Dictionary. + * A dictionary can be any arbitrary data segment (also called a prefix), + * or a buffer with specified information (see zdict.h). + * Note : This function loads the dictionary, resulting in significant startup delay. + * It's intended for a dictionary used only once. + * Note 2 : When `dict == NULL || dictSize < 8` no dictionary is used. */ + public static nuint ZSTD_compress_usingDict( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + void* dict, + nuint dictSize, + int compressionLevel + ) + { + { + ZSTD_parameters @params = ZSTD_getParams_internal( + compressionLevel, + srcSize, + dict != null ? dictSize : 0, + ZSTD_CParamMode_e.ZSTD_cpm_noAttachDict + ); + assert(@params.fParams.contentSizeFlag == 1); + ZSTD_CCtxParams_init_internal( + &cctx->simpleApiParams, + &@params, + compressionLevel == 0 ? 3 : compressionLevel + ); + } + + return ZSTD_compress_advanced_internal( + cctx, + dst, + dstCapacity, + src, + srcSize, + dict, + dictSize, + &cctx->simpleApiParams + ); + } + + /*! ZSTD_compressCCtx() : + * Same as ZSTD_compress(), using an explicit ZSTD_CCtx. + * Important : in order to mirror `ZSTD_compress()` behavior, + * this function compresses at the requested compression level, + * __ignoring any other advanced parameter__ . + * If any advanced parameter was set using the advanced API, + * they will all be reset. Only @compressionLevel remains. + */ + public static nuint ZSTD_compressCCtx( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + int compressionLevel + ) + { + assert(cctx != null); + return ZSTD_compress_usingDict( + cctx, + dst, + dstCapacity, + src, + srcSize, + null, + 0, + compressionLevel + ); + } + + /*************************************** + * Simple Core API + ***************************************/ + /*! ZSTD_compress() : + * Compresses `src` content as a single zstd compressed frame into already allocated `dst`. + * NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have + * enough space to successfully compress the data. + * @return : compressed size written into `dst` (<= `dstCapacity), + * or an error code if it fails (which can be tested using ZSTD_isError()). */ + public static nuint ZSTD_compress( + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + int compressionLevel + ) + { + nuint result; + ZSTD_CCtx_s ctxBody; + ZSTD_initCCtx(&ctxBody, ZSTD_defaultCMem); + result = ZSTD_compressCCtx(&ctxBody, dst, dstCapacity, src, srcSize, compressionLevel); + ZSTD_freeCCtxContent(&ctxBody); + return result; + } + + /*! ZSTD_estimateCDictSize_advanced() : + * Estimate amount of memory that will be needed to create a dictionary with following arguments */ + public static nuint ZSTD_estimateCDictSize_advanced( + nuint dictSize, + ZSTD_compressionParameters cParams, + ZSTD_dictLoadMethod_e dictLoadMethod + ) + { + return ZSTD_cwksp_alloc_size((nuint)sizeof(ZSTD_CDict_s)) + + ZSTD_cwksp_alloc_size((8 << 10) + 512) + + ZSTD_sizeof_matchState( + &cParams, + ZSTD_resolveRowMatchFinderMode(ZSTD_paramSwitch_e.ZSTD_ps_auto, &cParams), + 1, + 0 + ) + + ( + dictLoadMethod == ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef + ? 0 + : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, (nuint)sizeof(void*))) + ); + } + + /*! ZSTD_estimate?DictSize() : + * ZSTD_estimateCDictSize() will bet that src size is relatively "small", and content is copied, like ZSTD_createCDict(). + * ZSTD_estimateCDictSize_advanced() makes it possible to control compression parameters precisely, like ZSTD_createCDict_advanced(). + * Note : dictionaries created by reference (`ZSTD_dlm_byRef`) are logically smaller. + */ + public static nuint ZSTD_estimateCDictSize(nuint dictSize, int compressionLevel) + { + ZSTD_compressionParameters cParams = ZSTD_getCParams_internal( + compressionLevel, + unchecked(0UL - 1), + dictSize, + ZSTD_CParamMode_e.ZSTD_cpm_createCDict + ); + return ZSTD_estimateCDictSize_advanced( + dictSize, + cParams, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byCopy + ); + } + + public static nuint ZSTD_sizeof_CDict(ZSTD_CDict_s* cdict) + { + if (cdict == null) + return 0; + return (nuint)(cdict->workspace.workspace == cdict ? 0 : sizeof(ZSTD_CDict_s)) + + ZSTD_cwksp_sizeof(&cdict->workspace); + } + + private static nuint ZSTD_initCDict_internal( + ZSTD_CDict_s* cdict, + void* dictBuffer, + nuint dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictContentType_e dictContentType, + ZSTD_CCtx_params_s @params + ) + { + assert(ZSTD_checkCParams(@params.cParams) == 0); + cdict->matchState.cParams = @params.cParams; + cdict->matchState.dedicatedDictSearch = @params.enableDedicatedDictSearch; + if ( + dictLoadMethod == ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef + || dictBuffer == null + || dictSize == 0 + ) + { + cdict->dictContent = dictBuffer; + } + else + { + void* internalBuffer = ZSTD_cwksp_reserve_object( + &cdict->workspace, + ZSTD_cwksp_align(dictSize, (nuint)sizeof(void*)) + ); + if (internalBuffer == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + cdict->dictContent = internalBuffer; + memcpy(internalBuffer, dictBuffer, (uint)dictSize); + } + + cdict->dictContentSize = dictSize; + cdict->dictContentType = dictContentType; + cdict->entropyWorkspace = (uint*)ZSTD_cwksp_reserve_object( + &cdict->workspace, + (8 << 10) + 512 + ); + ZSTD_reset_compressedBlockState(&cdict->cBlockState); + { + nuint err_code = ZSTD_reset_matchState( + &cdict->matchState, + &cdict->workspace, + &@params.cParams, + @params.useRowMatchFinder, + ZSTD_compResetPolicy_e.ZSTDcrp_makeClean, + ZSTD_indexResetPolicy_e.ZSTDirp_reset, + ZSTD_resetTarget_e.ZSTD_resetTarget_CDict + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + @params.compressionLevel = 3; + @params.fParams.contentSizeFlag = 1; + { + nuint dictID = ZSTD_compress_insertDictionary( + &cdict->cBlockState, + &cdict->matchState, + null, + &cdict->workspace, + &@params, + cdict->dictContent, + cdict->dictContentSize, + dictContentType, + ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_full, + ZSTD_tableFillPurpose_e.ZSTD_tfp_forCDict, + cdict->entropyWorkspace + ); + { + nuint err_code = dictID; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(dictID <= unchecked((uint)-1)); + cdict->dictID = (uint)dictID; + } + } + + return 0; + } + + private static ZSTD_CDict_s* ZSTD_createCDict_advanced_internal( + nuint dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_compressionParameters cParams, + ZSTD_paramSwitch_e useRowMatchFinder, + int enableDedicatedDictSearch, + ZSTD_customMem customMem + ) + { + if (((customMem.customAlloc == null ? 1 : 0) ^ (customMem.customFree == null ? 1 : 0)) != 0) + return null; + { + nuint workspaceSize = + ZSTD_cwksp_alloc_size((nuint)sizeof(ZSTD_CDict_s)) + + ZSTD_cwksp_alloc_size((8 << 10) + 512) + + ZSTD_sizeof_matchState(&cParams, useRowMatchFinder, enableDedicatedDictSearch, 0) + + ( + dictLoadMethod == ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef + ? 0 + : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, (nuint)sizeof(void*))) + ); + void* workspace = ZSTD_customMalloc(workspaceSize, customMem); + ZSTD_cwksp ws; + ZSTD_CDict_s* cdict; + if (workspace == null) + { + ZSTD_customFree(workspace, customMem); + return null; + } + + ZSTD_cwksp_init( + &ws, + workspace, + workspaceSize, + ZSTD_cwksp_static_alloc_e.ZSTD_cwksp_dynamic_alloc + ); + cdict = (ZSTD_CDict_s*)ZSTD_cwksp_reserve_object(&ws, (nuint)sizeof(ZSTD_CDict_s)); + assert(cdict != null); + ZSTD_cwksp_move(&cdict->workspace, &ws); + cdict->customMem = customMem; + cdict->compressionLevel = 0; + cdict->useRowMatchFinder = useRowMatchFinder; + return cdict; + } + } + + public static ZSTD_CDict_s* ZSTD_createCDict_advanced( + void* dictBuffer, + nuint dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictContentType_e dictContentType, + ZSTD_compressionParameters cParams, + ZSTD_customMem customMem + ) + { + ZSTD_CCtx_params_s cctxParams; + cctxParams = new ZSTD_CCtx_params_s(); + ZSTD_CCtxParams_init(&cctxParams, 0); + cctxParams.cParams = cParams; + cctxParams.customMem = customMem; + return ZSTD_createCDict_advanced2( + dictBuffer, + dictSize, + dictLoadMethod, + dictContentType, + &cctxParams, + customMem + ); + } + + /* + * This API is temporary and is expected to change or disappear in the future! + */ + public static ZSTD_CDict_s* ZSTD_createCDict_advanced2( + void* dict, + nuint dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictContentType_e dictContentType, + ZSTD_CCtx_params_s* originalCctxParams, + ZSTD_customMem customMem + ) + { + ZSTD_CCtx_params_s cctxParams = *originalCctxParams; + ZSTD_compressionParameters cParams; + ZSTD_CDict_s* cdict; + if (((customMem.customAlloc == null ? 1 : 0) ^ (customMem.customFree == null ? 1 : 0)) != 0) + return null; + if (cctxParams.enableDedicatedDictSearch != 0) + { + cParams = ZSTD_dedicatedDictSearch_getCParams(cctxParams.compressionLevel, dictSize); + ZSTD_overrideCParams(&cParams, &cctxParams.cParams); + } + else + { + cParams = ZSTD_getCParamsFromCCtxParams( + &cctxParams, + unchecked(0UL - 1), + dictSize, + ZSTD_CParamMode_e.ZSTD_cpm_createCDict + ); + } + + if (ZSTD_dedicatedDictSearch_isSupported(&cParams) == 0) + { + cctxParams.enableDedicatedDictSearch = 0; + cParams = ZSTD_getCParamsFromCCtxParams( + &cctxParams, + unchecked(0UL - 1), + dictSize, + ZSTD_CParamMode_e.ZSTD_cpm_createCDict + ); + } + + cctxParams.cParams = cParams; + cctxParams.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode( + cctxParams.useRowMatchFinder, + &cParams + ); + cdict = ZSTD_createCDict_advanced_internal( + dictSize, + dictLoadMethod, + cctxParams.cParams, + cctxParams.useRowMatchFinder, + cctxParams.enableDedicatedDictSearch, + customMem + ); + if ( + cdict == null + || ERR_isError( + ZSTD_initCDict_internal( + cdict, + dict, + dictSize, + dictLoadMethod, + dictContentType, + cctxParams + ) + ) + ) + { + ZSTD_freeCDict(cdict); + return null; + } + + return cdict; + } + + /*! ZSTD_createCDict() : + * When compressing multiple messages or blocks using the same dictionary, + * it's recommended to digest the dictionary only once, since it's a costly operation. + * ZSTD_createCDict() will create a state from digesting a dictionary. + * The resulting state can be used for future compression operations with very limited startup cost. + * ZSTD_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only. + * @dictBuffer can be released after ZSTD_CDict creation, because its content is copied within CDict. + * Note 1 : Consider experimental function `ZSTD_createCDict_byReference()` if you prefer to not duplicate @dictBuffer content. + * Note 2 : A ZSTD_CDict can be created from an empty @dictBuffer, + * in which case the only thing that it transports is the @compressionLevel. + * This can be useful in a pipeline featuring ZSTD_compress_usingCDict() exclusively, + * expecting a ZSTD_CDict parameter with any data, including those without a known dictionary. */ + public static ZSTD_CDict_s* ZSTD_createCDict(void* dict, nuint dictSize, int compressionLevel) + { + ZSTD_compressionParameters cParams = ZSTD_getCParams_internal( + compressionLevel, + unchecked(0UL - 1), + dictSize, + ZSTD_CParamMode_e.ZSTD_cpm_createCDict + ); + ZSTD_CDict_s* cdict = ZSTD_createCDict_advanced( + dict, + dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byCopy, + ZSTD_dictContentType_e.ZSTD_dct_auto, + cParams, + ZSTD_defaultCMem + ); + if (cdict != null) + cdict->compressionLevel = compressionLevel == 0 ? 3 : compressionLevel; + return cdict; + } + + /*! ZSTD_createCDict_byReference() : + * Create a digested dictionary for compression + * Dictionary content is just referenced, not duplicated. + * As a consequence, `dictBuffer` **must** outlive CDict, + * and its content must remain unmodified throughout the lifetime of CDict. + * note: equivalent to ZSTD_createCDict_advanced(), with dictLoadMethod==ZSTD_dlm_byRef */ + public static ZSTD_CDict_s* ZSTD_createCDict_byReference( + void* dict, + nuint dictSize, + int compressionLevel + ) + { + ZSTD_compressionParameters cParams = ZSTD_getCParams_internal( + compressionLevel, + unchecked(0UL - 1), + dictSize, + ZSTD_CParamMode_e.ZSTD_cpm_createCDict + ); + ZSTD_CDict_s* cdict = ZSTD_createCDict_advanced( + dict, + dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef, + ZSTD_dictContentType_e.ZSTD_dct_auto, + cParams, + ZSTD_defaultCMem + ); + if (cdict != null) + cdict->compressionLevel = compressionLevel == 0 ? 3 : compressionLevel; + return cdict; + } + + /*! ZSTD_freeCDict() : + * Function frees memory allocated by ZSTD_createCDict(). + * If a NULL pointer is passed, no operation is performed. */ + public static nuint ZSTD_freeCDict(ZSTD_CDict_s* cdict) + { + if (cdict == null) + return 0; + { + ZSTD_customMem cMem = cdict->customMem; + int cdictInWorkspace = ZSTD_cwksp_owns_buffer(&cdict->workspace, cdict); + ZSTD_cwksp_free(&cdict->workspace, cMem); + if (cdictInWorkspace == 0) + { + ZSTD_customFree(cdict, cMem); + } + + return 0; + } + } + + /*! ZSTD_initStaticCDict_advanced() : + * Generate a digested dictionary in provided memory area. + * workspace: The memory area to emplace the dictionary into. + * Provided pointer must 8-bytes aligned. + * It must outlive dictionary usage. + * workspaceSize: Use ZSTD_estimateCDictSize() + * to determine how large workspace must be. + * cParams : use ZSTD_getCParams() to transform a compression level + * into its relevant cParams. + * @return : pointer to ZSTD_CDict*, or NULL if error (size too small) + * Note : there is no corresponding "free" function. + * Since workspace was allocated externally, it must be freed externally. + */ + public static ZSTD_CDict_s* ZSTD_initStaticCDict( + void* workspace, + nuint workspaceSize, + void* dict, + nuint dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictContentType_e dictContentType, + ZSTD_compressionParameters cParams + ) + { + ZSTD_paramSwitch_e useRowMatchFinder = ZSTD_resolveRowMatchFinderMode( + ZSTD_paramSwitch_e.ZSTD_ps_auto, + &cParams + ); + /* enableDedicatedDictSearch */ + nuint matchStateSize = ZSTD_sizeof_matchState(&cParams, useRowMatchFinder, 1, 0); + nuint neededSize = + ZSTD_cwksp_alloc_size((nuint)sizeof(ZSTD_CDict_s)) + + ( + dictLoadMethod == ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef + ? 0 + : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, (nuint)sizeof(void*))) + ) + + ZSTD_cwksp_alloc_size((8 << 10) + 512) + + matchStateSize; + ZSTD_CDict_s* cdict; + ZSTD_CCtx_params_s @params; + if (((nuint)workspace & 7) != 0) + return null; + { + ZSTD_cwksp ws; + ZSTD_cwksp_init( + &ws, + workspace, + workspaceSize, + ZSTD_cwksp_static_alloc_e.ZSTD_cwksp_static_alloc + ); + cdict = (ZSTD_CDict_s*)ZSTD_cwksp_reserve_object(&ws, (nuint)sizeof(ZSTD_CDict_s)); + if (cdict == null) + return null; + ZSTD_cwksp_move(&cdict->workspace, &ws); + } + + if (workspaceSize < neededSize) + return null; + ZSTD_CCtxParams_init(&@params, 0); + @params.cParams = cParams; + @params.useRowMatchFinder = useRowMatchFinder; + cdict->useRowMatchFinder = useRowMatchFinder; + cdict->compressionLevel = 0; + if ( + ERR_isError( + ZSTD_initCDict_internal( + cdict, + dict, + dictSize, + dictLoadMethod, + dictContentType, + @params + ) + ) + ) + return null; + return cdict; + } + + /*! ZSTD_getCParamsFromCDict() : + * as the name implies */ + private static ZSTD_compressionParameters ZSTD_getCParamsFromCDict(ZSTD_CDict_s* cdict) + { + assert(cdict != null); + return cdict->matchState.cParams; + } + + /*! ZSTD_getDictID_fromCDict() : + * Provides the dictID of the dictionary loaded into `cdict`. + * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty. + * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */ + public static uint ZSTD_getDictID_fromCDict(ZSTD_CDict_s* cdict) + { + if (cdict == null) + return 0; + return cdict->dictID; + } + + /* ZSTD_compressBegin_usingCDict_internal() : + * Implementation of various ZSTD_compressBegin_usingCDict* functions. + */ + private static nuint ZSTD_compressBegin_usingCDict_internal( + ZSTD_CCtx_s* cctx, + ZSTD_CDict_s* cdict, + ZSTD_frameParameters fParams, + ulong pledgedSrcSize + ) + { + ZSTD_CCtx_params_s cctxParams; + if (cdict == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_wrong)); + } + + { + ZSTD_parameters @params; + @params.fParams = fParams; + @params.cParams = + pledgedSrcSize < 128 * (1 << 10) + || pledgedSrcSize < cdict->dictContentSize * 6UL + || pledgedSrcSize == unchecked(0UL - 1) + || cdict->compressionLevel == 0 + ? ZSTD_getCParamsFromCDict(cdict) + : ZSTD_getCParams( + cdict->compressionLevel, + pledgedSrcSize, + cdict->dictContentSize + ); + ZSTD_CCtxParams_init_internal(&cctxParams, &@params, cdict->compressionLevel); + } + + if (pledgedSrcSize != unchecked(0UL - 1)) + { + uint limitedSrcSize = (uint)(pledgedSrcSize < 1U << 19 ? pledgedSrcSize : 1U << 19); + uint limitedSrcLog = limitedSrcSize > 1 ? ZSTD_highbit32(limitedSrcSize - 1) + 1 : 1; + cctxParams.cParams.windowLog = + cctxParams.cParams.windowLog > limitedSrcLog + ? cctxParams.cParams.windowLog + : limitedSrcLog; + } + + return ZSTD_compressBegin_internal( + cctx, + null, + 0, + ZSTD_dictContentType_e.ZSTD_dct_auto, + ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast, + cdict, + &cctxParams, + pledgedSrcSize, + ZSTD_buffered_policy_e.ZSTDb_not_buffered + ); + } + + /* ZSTD_compressBegin_usingCDict_advanced() : + * This function is DEPRECATED. + * cdict must be != NULL */ + public static nuint ZSTD_compressBegin_usingCDict_advanced( + ZSTD_CCtx_s* cctx, + ZSTD_CDict_s* cdict, + ZSTD_frameParameters fParams, + ulong pledgedSrcSize + ) + { + return ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, pledgedSrcSize); + } + + /* ZSTD_compressBegin_usingCDict() : + * cdict must be != NULL */ + private static nuint ZSTD_compressBegin_usingCDict_deprecated( + ZSTD_CCtx_s* cctx, + ZSTD_CDict_s* cdict + ) + { + /*content*/ + ZSTD_frameParameters fParams = new ZSTD_frameParameters + { + contentSizeFlag = 0, + checksumFlag = 0, + noDictIDFlag = 0, + }; + return ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, unchecked(0UL - 1)); + } + + public static nuint ZSTD_compressBegin_usingCDict(ZSTD_CCtx_s* cctx, ZSTD_CDict_s* cdict) + { + return ZSTD_compressBegin_usingCDict_deprecated(cctx, cdict); + } + + /*! ZSTD_compress_usingCDict_internal(): + * Implementation of various ZSTD_compress_usingCDict* functions. + */ + private static nuint ZSTD_compress_usingCDict_internal( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + ZSTD_CDict_s* cdict, + ZSTD_frameParameters fParams + ) + { + { + /* will check if cdict != NULL */ + nuint err_code = ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, srcSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); + } + + /*! ZSTD_compress_usingCDict_advanced(): + * This function is DEPRECATED. + */ + public static nuint ZSTD_compress_usingCDict_advanced( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + ZSTD_CDict_s* cdict, + ZSTD_frameParameters fParams + ) + { + return ZSTD_compress_usingCDict_internal( + cctx, + dst, + dstCapacity, + src, + srcSize, + cdict, + fParams + ); + } + + /*! ZSTD_compress_usingCDict() : + * Compression using a digested Dictionary. + * Faster startup than ZSTD_compress_usingDict(), recommended when same dictionary is used multiple times. + * Note that compression parameters are decided at CDict creation time + * while frame parameters are hardcoded */ + public static nuint ZSTD_compress_usingCDict( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + ZSTD_CDict_s* cdict + ) + { + /*content*/ + ZSTD_frameParameters fParams = new ZSTD_frameParameters + { + contentSizeFlag = 1, + checksumFlag = 0, + noDictIDFlag = 0, + }; + return ZSTD_compress_usingCDict_internal( + cctx, + dst, + dstCapacity, + src, + srcSize, + cdict, + fParams + ); + } + + /* ****************************************************************** + * Streaming + ********************************************************************/ + public static ZSTD_CCtx_s* ZSTD_createCStream() + { + return ZSTD_createCStream_advanced(ZSTD_defaultCMem); + } + + public static ZSTD_CCtx_s* ZSTD_initStaticCStream(void* workspace, nuint workspaceSize) + { + return ZSTD_initStaticCCtx(workspace, workspaceSize); + } + + public static ZSTD_CCtx_s* ZSTD_createCStream_advanced(ZSTD_customMem customMem) + { + return ZSTD_createCCtx_advanced(customMem); + } + + public static nuint ZSTD_freeCStream(ZSTD_CCtx_s* zcs) + { + return ZSTD_freeCCtx(zcs); + } + + /*====== Initialization ======*/ + public static nuint ZSTD_CStreamInSize() + { + return 1 << 17; + } + + public static nuint ZSTD_CStreamOutSize() + { + return ZSTD_compressBound(1 << 17) + ZSTD_blockHeaderSize + 4; + } + + private static ZSTD_CParamMode_e ZSTD_getCParamMode( + ZSTD_CDict_s* cdict, + ZSTD_CCtx_params_s* @params, + ulong pledgedSrcSize + ) + { + if (cdict != null && ZSTD_shouldAttachDict(cdict, @params, pledgedSrcSize) != 0) + return ZSTD_CParamMode_e.ZSTD_cpm_attachDict; + else + return ZSTD_CParamMode_e.ZSTD_cpm_noAttachDict; + } + + /* ZSTD_resetCStream(): + * pledgedSrcSize == 0 means "unknown" */ + public static nuint ZSTD_resetCStream(ZSTD_CCtx_s* zcs, ulong pss) + { + /* temporary : 0 interpreted as "unknown" during transition period. + * Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN. + * 0 will be interpreted as "empty" in the future. + */ + ulong pledgedSrcSize = pss == 0 ? unchecked(0UL - 1) : pss; + { + nuint err_code = ZSTD_CCtx_reset(zcs, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /*! ZSTD_initCStream_internal() : + * Note : for lib/compress only. Used by zstdmt_compress.c. + * Assumption 1 : params are valid + * Assumption 2 : either dict, or cdict, is defined, not both */ + private static nuint ZSTD_initCStream_internal( + ZSTD_CCtx_s* zcs, + void* dict, + nuint dictSize, + ZSTD_CDict_s* cdict, + ZSTD_CCtx_params_s* @params, + ulong pledgedSrcSize + ) + { + { + nuint err_code = ZSTD_CCtx_reset(zcs, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(!ERR_isError(ZSTD_checkCParams(@params->cParams))); + zcs->requestedParams = *@params; + assert(!(dict != null && cdict != null)); + if (dict != null) + { + nuint err_code = ZSTD_CCtx_loadDictionary(zcs, dict, dictSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + else + { + /* Dictionary is cleared if !cdict */ + nuint err_code = ZSTD_CCtx_refCDict(zcs, cdict); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /* ZSTD_initCStream_usingCDict_advanced() : + * same as ZSTD_initCStream_usingCDict(), with control over frame parameters */ + public static nuint ZSTD_initCStream_usingCDict_advanced( + ZSTD_CCtx_s* zcs, + ZSTD_CDict_s* cdict, + ZSTD_frameParameters fParams, + ulong pledgedSrcSize + ) + { + { + nuint err_code = ZSTD_CCtx_reset(zcs, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + zcs->requestedParams.fParams = fParams; + { + nuint err_code = ZSTD_CCtx_refCDict(zcs, cdict); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /* note : cdict must outlive compression session */ + public static nuint ZSTD_initCStream_usingCDict(ZSTD_CCtx_s* zcs, ZSTD_CDict_s* cdict) + { + { + nuint err_code = ZSTD_CCtx_reset(zcs, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_refCDict(zcs, cdict); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /* ZSTD_initCStream_advanced() : + * pledgedSrcSize must be exact. + * if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. + * dict is loaded with default parameters ZSTD_dct_auto and ZSTD_dlm_byCopy. */ + public static nuint ZSTD_initCStream_advanced( + ZSTD_CCtx_s* zcs, + void* dict, + nuint dictSize, + ZSTD_parameters @params, + ulong pss + ) + { + /* for compatibility with older programs relying on this behavior. + * Users should now specify ZSTD_CONTENTSIZE_UNKNOWN. + * This line will be removed in the future. + */ + ulong pledgedSrcSize = + pss == 0 && @params.fParams.contentSizeFlag == 0 ? unchecked(0UL - 1) : pss; + { + nuint err_code = ZSTD_CCtx_reset(zcs, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_checkCParams(@params.cParams); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ZSTD_CCtxParams_setZstdParams(&zcs->requestedParams, &@params); + { + nuint err_code = ZSTD_CCtx_loadDictionary(zcs, dict, dictSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /*! ZSTD_initCStream_usingDict() : + * This function is DEPRECATED, and is equivalent to: + * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); + * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel); + * ZSTD_CCtx_loadDictionary(zcs, dict, dictSize); + * + * Creates of an internal CDict (incompatible with static CCtx), except if + * dict == NULL or dictSize < 8, in which case no dict is used. + * Note: dict is loaded with ZSTD_dct_auto (treated as a full zstd dictionary if + * it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy. + * This prototype will generate compilation warnings. + */ + public static nuint ZSTD_initCStream_usingDict( + ZSTD_CCtx_s* zcs, + void* dict, + nuint dictSize, + int compressionLevel + ) + { + { + nuint err_code = ZSTD_CCtx_reset(zcs, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + zcs, + ZSTD_cParameter.ZSTD_c_compressionLevel, + compressionLevel + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_loadDictionary(zcs, dict, dictSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /*! ZSTD_initCStream_srcSize() : + * This function is DEPRECATED, and equivalent to: + * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); + * ZSTD_CCtx_refCDict(zcs, NULL); // clear the dictionary (if any) + * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel); + * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); + * + * pledgedSrcSize must be correct. If it is not known at init time, use + * ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, + * "0" also disables frame content size field. It may be enabled in the future. + * This prototype will generate compilation warnings. + */ + public static nuint ZSTD_initCStream_srcSize(ZSTD_CCtx_s* zcs, int compressionLevel, ulong pss) + { + /* temporary : 0 interpreted as "unknown" during transition period. + * Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN. + * 0 will be interpreted as "empty" in the future. + */ + ulong pledgedSrcSize = pss == 0 ? unchecked(0UL - 1) : pss; + { + nuint err_code = ZSTD_CCtx_reset(zcs, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_refCDict(zcs, null); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + zcs, + ZSTD_cParameter.ZSTD_c_compressionLevel, + compressionLevel + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /*! + * Equivalent to: + * + * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); + * ZSTD_CCtx_refCDict(zcs, NULL); // clear the dictionary (if any) + * ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel); + * + * Note that ZSTD_initCStream() clears any previously set dictionary. Use the new API + * to compress with a dictionary. + */ + public static nuint ZSTD_initCStream(ZSTD_CCtx_s* zcs, int compressionLevel) + { + { + nuint err_code = ZSTD_CCtx_reset(zcs, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_refCDict(zcs, null); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_CCtx_setParameter( + zcs, + ZSTD_cParameter.ZSTD_c_compressionLevel, + compressionLevel + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /*====== Compression ======*/ + private static nuint ZSTD_nextInputSizeHint(ZSTD_CCtx_s* cctx) + { + if (cctx->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable) + { + return cctx->blockSizeMax - cctx->stableIn_notConsumed; + } + + assert(cctx->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered); + { + nuint hintInSize = cctx->inBuffTarget - cctx->inBuffPos; + if (hintInSize == 0) + hintInSize = cctx->blockSizeMax; + return hintInSize; + } + } + + /** ZSTD_compressStream_generic(): + * internal function for all *compressStream*() variants + * @return : hint size for next input to complete ongoing block */ + private static nuint ZSTD_compressStream_generic( + ZSTD_CCtx_s* zcs, + ZSTD_outBuffer_s* output, + ZSTD_inBuffer_s* input, + ZSTD_EndDirective flushMode + ) + { + assert(input != null); + sbyte* istart = (sbyte*)input->src; + sbyte* iend = istart != null ? istart + input->size : istart; + sbyte* ip = istart != null ? istart + input->pos : istart; + assert(output != null); + sbyte* ostart = (sbyte*)output->dst; + sbyte* oend = ostart != null ? ostart + output->size : ostart; + sbyte* op = ostart != null ? ostart + output->pos : ostart; + uint someMoreWork = 1; + assert(zcs != null); + if (zcs->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable) + { + assert(input->pos >= zcs->stableIn_notConsumed); + input->pos -= zcs->stableIn_notConsumed; + if (ip != null) + ip -= zcs->stableIn_notConsumed; + zcs->stableIn_notConsumed = 0; + } + +#if DEBUG + if (zcs->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered) + { + assert(zcs->inBuff != null); + assert(zcs->inBuffSize > 0); + } +#endif + +#if DEBUG + if (zcs->appliedParams.outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered) + { + assert(zcs->outBuff != null); + assert(zcs->outBuffSize > 0); + } +#endif + +#if DEBUG + if (input->src == null) + assert(input->size == 0); +#endif + assert(input->pos <= input->size); +#if DEBUG + if (output->dst == null) + assert(output->size == 0); +#endif + assert(output->pos <= output->size); + assert((uint)flushMode <= (uint)ZSTD_EndDirective.ZSTD_e_end); + while (someMoreWork != 0) + { + switch (zcs->streamStage) + { + case ZSTD_cStreamStage.zcss_init: + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_init_missing)); + case ZSTD_cStreamStage.zcss_load: + if ( + flushMode == ZSTD_EndDirective.ZSTD_e_end + && ( + (nuint)(oend - op) >= ZSTD_compressBound((nuint)(iend - ip)) + || zcs->appliedParams.outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable + ) + && zcs->inBuffPos == 0 + ) + { + /* shortcut to compression pass directly into output buffer */ + nuint cSize = ZSTD_compressEnd_public( + zcs, + op, + (nuint)(oend - op), + ip, + (nuint)(iend - ip) + ); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ip = iend; + op += cSize; + zcs->frameEnded = 1; + ZSTD_CCtx_reset(zcs, ZSTD_ResetDirective.ZSTD_reset_session_only); + someMoreWork = 0; + break; + } + + if (zcs->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered) + { + nuint toLoad = zcs->inBuffTarget - zcs->inBuffPos; + nuint loaded = ZSTD_limitCopy( + zcs->inBuff + zcs->inBuffPos, + toLoad, + ip, + (nuint)(iend - ip) + ); + zcs->inBuffPos += loaded; + if (ip != null) + ip += loaded; + if ( + flushMode == ZSTD_EndDirective.ZSTD_e_continue + && zcs->inBuffPos < zcs->inBuffTarget + ) + { + someMoreWork = 0; + break; + } + + if ( + flushMode == ZSTD_EndDirective.ZSTD_e_flush + && zcs->inBuffPos == zcs->inToCompress + ) + { + someMoreWork = 0; + break; + } + } + else + { + assert(zcs->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable); + if ( + flushMode == ZSTD_EndDirective.ZSTD_e_continue + && (nuint)(iend - ip) < zcs->blockSizeMax + ) + { + zcs->stableIn_notConsumed = (nuint)(iend - ip); + ip = iend; + someMoreWork = 0; + break; + } + + if (flushMode == ZSTD_EndDirective.ZSTD_e_flush && ip == iend) + { + someMoreWork = 0; + break; + } + } + + { + int inputBuffered = + zcs->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered + ? 1 + : 0; + void* cDst; + nuint cSize; + nuint oSize = (nuint)(oend - op); + nuint iSize = + inputBuffered != 0 ? zcs->inBuffPos - zcs->inToCompress + : (nuint)(iend - ip) < zcs->blockSizeMax ? (nuint)(iend - ip) + : zcs->blockSizeMax; + if ( + oSize >= ZSTD_compressBound(iSize) + || zcs->appliedParams.outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable + ) + cDst = op; + else + { + cDst = zcs->outBuff; + oSize = zcs->outBuffSize; + } + + if (inputBuffered != 0) + { + uint lastBlock = + flushMode == ZSTD_EndDirective.ZSTD_e_end && ip == iend ? 1U : 0U; + cSize = + lastBlock != 0 + ? ZSTD_compressEnd_public( + zcs, + cDst, + oSize, + zcs->inBuff + zcs->inToCompress, + iSize + ) + : ZSTD_compressContinue_public( + zcs, + cDst, + oSize, + zcs->inBuff + zcs->inToCompress, + iSize + ); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + zcs->frameEnded = lastBlock; + zcs->inBuffTarget = zcs->inBuffPos + zcs->blockSizeMax; + if (zcs->inBuffTarget > zcs->inBuffSize) + { + zcs->inBuffPos = 0; + zcs->inBuffTarget = zcs->blockSizeMax; + } + +#if DEBUG + if (lastBlock == 0) + assert(zcs->inBuffTarget <= zcs->inBuffSize); +#endif + zcs->inToCompress = zcs->inBuffPos; + } + else + { + uint lastBlock = + flushMode == ZSTD_EndDirective.ZSTD_e_end && ip + iSize == iend + ? 1U + : 0U; + cSize = + lastBlock != 0 + ? ZSTD_compressEnd_public(zcs, cDst, oSize, ip, iSize) + : ZSTD_compressContinue_public(zcs, cDst, oSize, ip, iSize); + if (ip != null) + ip += iSize; + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + zcs->frameEnded = lastBlock; +#if DEBUG + if (lastBlock != 0) + assert(ip == iend); +#endif + } + + if (cDst == op) + { + op += cSize; + if (zcs->frameEnded != 0) + { + someMoreWork = 0; + ZSTD_CCtx_reset(zcs, ZSTD_ResetDirective.ZSTD_reset_session_only); + } + + break; + } + + zcs->outBuffContentSize = cSize; + zcs->outBuffFlushedSize = 0; + zcs->streamStage = ZSTD_cStreamStage.zcss_flush; + } + + goto case ZSTD_cStreamStage.zcss_flush; + case ZSTD_cStreamStage.zcss_flush: + assert(zcs->appliedParams.outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered); + + { + nuint toFlush = zcs->outBuffContentSize - zcs->outBuffFlushedSize; + nuint flushed = ZSTD_limitCopy( + op, + (nuint)(oend - op), + zcs->outBuff + zcs->outBuffFlushedSize, + toFlush + ); + if (flushed != 0) + op += flushed; + zcs->outBuffFlushedSize += flushed; + if (toFlush != flushed) + { + assert(op == oend); + someMoreWork = 0; + break; + } + + zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0; + if (zcs->frameEnded != 0) + { + someMoreWork = 0; + ZSTD_CCtx_reset(zcs, ZSTD_ResetDirective.ZSTD_reset_session_only); + break; + } + + zcs->streamStage = ZSTD_cStreamStage.zcss_load; + break; + } + + default: + assert(0 != 0); + break; + } + } + + input->pos = (nuint)(ip - istart); + output->pos = (nuint)(op - ostart); + if (zcs->frameEnded != 0) + return 0; + return ZSTD_nextInputSizeHint(zcs); + } + + private static nuint ZSTD_nextInputSizeHint_MTorST(ZSTD_CCtx_s* cctx) + { + if (cctx->appliedParams.nbWorkers >= 1) + { + assert(cctx->mtctx != null); + return ZSTDMT_nextInputSizeHint(cctx->mtctx); + } + + return ZSTD_nextInputSizeHint(cctx); + } + + /*! + * Alternative for ZSTD_compressStream2(zcs, output, input, ZSTD_e_continue). + * NOTE: The return value is different. ZSTD_compressStream() returns a hint for + * the next read size (if non-zero and not an error). ZSTD_compressStream2() + * returns the minimum nb of bytes left to flush (if non-zero and not an error). + */ + public static nuint ZSTD_compressStream( + ZSTD_CCtx_s* zcs, + ZSTD_outBuffer_s* output, + ZSTD_inBuffer_s* input + ) + { + { + nuint err_code = ZSTD_compressStream2( + zcs, + output, + input, + ZSTD_EndDirective.ZSTD_e_continue + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return ZSTD_nextInputSizeHint_MTorST(zcs); + } + + /* After a compression call set the expected input/output buffer. + * This is validated at the start of the next compression call. + */ + private static void ZSTD_setBufferExpectations( + ZSTD_CCtx_s* cctx, + ZSTD_outBuffer_s* output, + ZSTD_inBuffer_s* input + ) + { + if (cctx->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable) + { + cctx->expectedInBuffer = *input; + } + + if (cctx->appliedParams.outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable) + { + cctx->expectedOutBufferSize = output->size - output->pos; + } + } + + /* Validate that the input/output buffers match the expectations set by + * ZSTD_setBufferExpectations. + */ + private static nuint ZSTD_checkBufferStability( + ZSTD_CCtx_s* cctx, + ZSTD_outBuffer_s* output, + ZSTD_inBuffer_s* input, + ZSTD_EndDirective endOp + ) + { + if (cctx->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable) + { + ZSTD_inBuffer_s expect = cctx->expectedInBuffer; + if (expect.src != input->src || expect.pos != input->pos) + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stabilityCondition_notRespected) + ); + } + + if (cctx->appliedParams.outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable) + { + nuint outBufferSize = output->size - output->pos; + if (cctx->expectedOutBufferSize != outBufferSize) + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stabilityCondition_notRespected) + ); + } + + return 0; + } + + /* + * If @endOp == ZSTD_e_end, @inSize becomes pledgedSrcSize. + * Otherwise, it's ignored. + * @return: 0 on success, or a ZSTD_error code otherwise. + */ + private static nuint ZSTD_CCtx_init_compressStream2( + ZSTD_CCtx_s* cctx, + ZSTD_EndDirective endOp, + nuint inSize + ) + { + ZSTD_CCtx_params_s @params = cctx->requestedParams; + ZSTD_prefixDict_s prefixDict = cctx->prefixDict; + { + /* Init the local dict if present. */ + nuint err_code = ZSTD_initLocalDict(cctx); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + cctx->prefixDict = new ZSTD_prefixDict_s(); + assert(prefixDict.dict == null || cctx->cdict == null); + if (cctx->cdict != null && cctx->localDict.cdict == null) + { + @params.compressionLevel = cctx->cdict->compressionLevel; + } + + if (endOp == ZSTD_EndDirective.ZSTD_e_end) + cctx->pledgedSrcSizePlusOne = inSize + 1; + { + nuint dictSize = + prefixDict.dict != null ? prefixDict.dictSize + : cctx->cdict != null ? cctx->cdict->dictContentSize + : 0; + ZSTD_CParamMode_e mode = ZSTD_getCParamMode( + cctx->cdict, + &@params, + cctx->pledgedSrcSizePlusOne - 1 + ); + @params.cParams = ZSTD_getCParamsFromCCtxParams( + &@params, + cctx->pledgedSrcSizePlusOne - 1, + dictSize, + mode + ); + } + + @params.postBlockSplitter = ZSTD_resolveBlockSplitterMode( + @params.postBlockSplitter, + &@params.cParams + ); + @params.ldmParams.enableLdm = ZSTD_resolveEnableLdm( + @params.ldmParams.enableLdm, + &@params.cParams + ); + @params.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode( + @params.useRowMatchFinder, + &@params.cParams + ); + @params.validateSequences = ZSTD_resolveExternalSequenceValidation( + @params.validateSequences + ); + @params.maxBlockSize = ZSTD_resolveMaxBlockSize(@params.maxBlockSize); + @params.searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch( + @params.searchForExternalRepcodes, + @params.compressionLevel + ); + if (ZSTD_hasExtSeqProd(&@params) != 0 && @params.nbWorkers >= 1) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_combination_unsupported) + ); + } + + if (cctx->pledgedSrcSizePlusOne - 1 <= 512 * (1 << 10)) + { + @params.nbWorkers = 0; + } + + if (@params.nbWorkers > 0) + { + if (cctx->mtctx == null) + { + cctx->mtctx = ZSTDMT_createCCtx_advanced( + (uint)@params.nbWorkers, + cctx->customMem, + cctx->pool + ); + if (cctx->mtctx == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + } + + { + nuint err_code = ZSTDMT_initCStream_internal( + cctx->mtctx, + prefixDict.dict, + prefixDict.dictSize, + prefixDict.dictContentType, + cctx->cdict, + @params, + cctx->pledgedSrcSizePlusOne - 1 + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + cctx->dictID = cctx->cdict != null ? cctx->cdict->dictID : 0; + cctx->dictContentSize = + cctx->cdict != null ? cctx->cdict->dictContentSize : prefixDict.dictSize; + cctx->consumedSrcSize = 0; + cctx->producedCSize = 0; + cctx->streamStage = ZSTD_cStreamStage.zcss_load; + cctx->appliedParams = @params; + } + else + { + ulong pledgedSrcSize = cctx->pledgedSrcSizePlusOne - 1; + assert(!ERR_isError(ZSTD_checkCParams(@params.cParams))); + { + nuint err_code = ZSTD_compressBegin_internal( + cctx, + prefixDict.dict, + prefixDict.dictSize, + prefixDict.dictContentType, + ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast, + cctx->cdict, + &@params, + pledgedSrcSize, + ZSTD_buffered_policy_e.ZSTDb_buffered + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(cctx->appliedParams.nbWorkers == 0); + cctx->inToCompress = 0; + cctx->inBuffPos = 0; + if (cctx->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered) + { + cctx->inBuffTarget = + cctx->blockSizeMax + (nuint)(cctx->blockSizeMax == pledgedSrcSize ? 1 : 0); + } + else + { + cctx->inBuffTarget = 0; + } + + cctx->outBuffContentSize = cctx->outBuffFlushedSize = 0; + cctx->streamStage = ZSTD_cStreamStage.zcss_load; + cctx->frameEnded = 0; + } + + return 0; + } + + /* @return provides a minimum amount of data remaining to be flushed from internal buffers + */ + public static nuint ZSTD_compressStream2( + ZSTD_CCtx_s* cctx, + ZSTD_outBuffer_s* output, + ZSTD_inBuffer_s* input, + ZSTD_EndDirective endOp + ) + { + if (output->pos > output->size) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (input->pos > input->size) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if ((uint)endOp > (uint)ZSTD_EndDirective.ZSTD_e_end) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + assert(cctx != null); + if (cctx->streamStage == ZSTD_cStreamStage.zcss_init) + { + /* no obligation to start from pos==0 */ + nuint inputSize = input->size - input->pos; + nuint totalInputSize = inputSize + cctx->stableIn_notConsumed; + if ( + cctx->requestedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable + && endOp == ZSTD_EndDirective.ZSTD_e_continue + && totalInputSize < 1 << 17 + ) + { + if (cctx->stableIn_notConsumed != 0) + { + if (input->src != cctx->expectedInBuffer.src) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stabilityCondition_notRespected) + ); + } + + if (input->pos != cctx->expectedInBuffer.size) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stabilityCondition_notRespected) + ); + } + } + + input->pos = input->size; + cctx->expectedInBuffer = *input; + cctx->stableIn_notConsumed += inputSize; + return (nuint)(cctx->requestedParams.format == ZSTD_format_e.ZSTD_f_zstd1 ? 6 : 2); + } + + { + nuint err_code = ZSTD_CCtx_init_compressStream2(cctx, endOp, totalInputSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ZSTD_setBufferExpectations(cctx, output, input); + } + + { + /* end of transparent initialization stage */ + nuint err_code = ZSTD_checkBufferStability(cctx, output, input, endOp); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (cctx->appliedParams.nbWorkers > 0) + { + nuint flushMin; + if (cctx->cParamsChanged != 0) + { + ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams); + cctx->cParamsChanged = 0; + } + + if (cctx->stableIn_notConsumed != 0) + { + assert(cctx->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable); + assert(input->pos >= cctx->stableIn_notConsumed); + input->pos -= cctx->stableIn_notConsumed; + cctx->stableIn_notConsumed = 0; + } + + for (; ; ) + { + nuint ipos = input->pos; + nuint opos = output->pos; + flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp); + cctx->consumedSrcSize += input->pos - ipos; + cctx->producedCSize += output->pos - opos; + if (ERR_isError(flushMin) || endOp == ZSTD_EndDirective.ZSTD_e_end && flushMin == 0) + { + if (flushMin == 0) + ZSTD_CCtx_trace(cctx, 0); + ZSTD_CCtx_reset(cctx, ZSTD_ResetDirective.ZSTD_reset_session_only); + } + + { + nuint err_code = flushMin; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (endOp == ZSTD_EndDirective.ZSTD_e_continue) + { + if ( + input->pos != ipos + || output->pos != opos + || input->pos == input->size + || output->pos == output->size + ) + break; + } + else + { + assert( + endOp == ZSTD_EndDirective.ZSTD_e_flush + || endOp == ZSTD_EndDirective.ZSTD_e_end + ); + if (flushMin == 0 || output->pos == output->size) + break; + } + } + + assert( + endOp == ZSTD_EndDirective.ZSTD_e_continue + || flushMin == 0 + || output->pos == output->size + ); + ZSTD_setBufferExpectations(cctx, output, input); + return flushMin; + } + + { + nuint err_code = ZSTD_compressStream_generic(cctx, output, input, endOp); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ZSTD_setBufferExpectations(cctx, output, input); + return cctx->outBuffContentSize - cctx->outBuffFlushedSize; + } + + /*! ZSTD_compressStream2_simpleArgs() : + * Same as ZSTD_compressStream2(), + * but using only integral types as arguments. + * This variant might be helpful for binders from dynamic languages + * which have troubles handling structures containing memory pointers. + */ + public static nuint ZSTD_compressStream2_simpleArgs( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + nuint* dstPos, + void* src, + nuint srcSize, + nuint* srcPos, + ZSTD_EndDirective endOp + ) + { + ZSTD_outBuffer_s output; + ZSTD_inBuffer_s input; + output.dst = dst; + output.size = dstCapacity; + output.pos = *dstPos; + input.src = src; + input.size = srcSize; + input.pos = *srcPos; + { + nuint cErr = ZSTD_compressStream2(cctx, &output, &input, endOp); + *dstPos = output.pos; + *srcPos = input.pos; + return cErr; + } + } + + /*! ZSTD_compress2() : + * Behave the same as ZSTD_compressCCtx(), but compression parameters are set using the advanced API. + * (note that this entry point doesn't even expose a compression level parameter). + * ZSTD_compress2() always starts a new frame. + * Should cctx hold data from a previously unfinished frame, everything about it is forgotten. + * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*() + * - The function is always blocking, returns when compression is completed. + * NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have + * enough space to successfully compress the data, though it is possible it fails for other reasons. + * @return : compressed size written into `dst` (<= `dstCapacity), + * or an error code if it fails (which can be tested using ZSTD_isError()). + */ + public static nuint ZSTD_compress2( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + ZSTD_bufferMode_e originalInBufferMode = cctx->requestedParams.inBufferMode; + ZSTD_bufferMode_e originalOutBufferMode = cctx->requestedParams.outBufferMode; + ZSTD_CCtx_reset(cctx, ZSTD_ResetDirective.ZSTD_reset_session_only); + cctx->requestedParams.inBufferMode = ZSTD_bufferMode_e.ZSTD_bm_stable; + cctx->requestedParams.outBufferMode = ZSTD_bufferMode_e.ZSTD_bm_stable; + { + nuint oPos = 0; + nuint iPos = 0; + nuint result = ZSTD_compressStream2_simpleArgs( + cctx, + dst, + dstCapacity, + &oPos, + src, + srcSize, + &iPos, + ZSTD_EndDirective.ZSTD_e_end + ); + cctx->requestedParams.inBufferMode = originalInBufferMode; + cctx->requestedParams.outBufferMode = originalOutBufferMode; + { + nuint err_code = result; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (result != 0) + { + assert(oPos == dstCapacity); + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + assert(iPos == srcSize); + return oPos; + } + } + + /* ZSTD_validateSequence() : + * @offBase : must use the format required by ZSTD_storeSeq() + * @returns a ZSTD error code if sequence is not valid + */ + private static nuint ZSTD_validateSequence( + uint offBase, + uint matchLength, + uint minMatch, + nuint posInSrc, + uint windowLog, + nuint dictSize, + int useSequenceProducer + ) + { + uint windowSize = 1U << (int)windowLog; + /* posInSrc represents the amount of data the decoder would decode up to this point. + * As long as the amount of data decoded is less than or equal to window size, offsets may be + * larger than the total length of output decoded in order to reference the dict, even larger than + * window size. After output surpasses windowSize, we're limited to windowSize offsets again. + */ + nuint offsetBound = posInSrc > windowSize ? windowSize : posInSrc + dictSize; + nuint matchLenLowerBound = (nuint)(minMatch == 3 || useSequenceProducer != 0 ? 3 : 4); + { + assert(offsetBound > 0); + if (offBase > offsetBound + 3) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid) + ); + } + } + + if (matchLength < matchLenLowerBound) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid)); + } + + return 0; + } + + /* Returns an offset code, given a sequence's raw offset, the ongoing repcode array, and whether litLength == 0 */ + private static uint ZSTD_finalizeOffBase(uint rawOffset, uint* rep, uint ll0) + { + assert(rawOffset > 0); + uint offBase = rawOffset + 3; + if (ll0 == 0 && rawOffset == rep[0]) + { + assert(1 >= 1); + assert(1 <= 3); + offBase = 1; + } + else if (rawOffset == rep[1]) + { + assert(2 - ll0 >= 1); + assert(2 - ll0 <= 3); + offBase = 2 - ll0; + } + else if (rawOffset == rep[2]) + { + assert(3 - ll0 >= 1); + assert(3 - ll0 <= 3); + offBase = 3 - ll0; + } + else if (ll0 != 0 && rawOffset == rep[0] - 1) + { + assert(3 >= 1); + assert(3 <= 3); + offBase = 3; + } + + return offBase; + } + + /* This function scans through an array of ZSTD_Sequence, + * storing the sequences it reads, until it reaches a block delimiter. + * Note that the block delimiter includes the last literals of the block. + * @blockSize must be == sum(sequence_lengths). + * @returns @blockSize on success, and a ZSTD_error otherwise. + */ + private static nuint ZSTD_transferSequences_wBlockDelim( + ZSTD_CCtx_s* cctx, + ZSTD_SequencePosition* seqPos, + ZSTD_Sequence* inSeqs, + nuint inSeqsSize, + void* src, + nuint blockSize, + ZSTD_paramSwitch_e externalRepSearch + ) + { + uint idx = seqPos->idx; + uint startIdx = idx; + byte* ip = (byte*)src; + byte* iend = ip + blockSize; + repcodes_s updatedRepcodes; + uint dictSize; + if (cctx->cdict != null) + { + dictSize = (uint)cctx->cdict->dictContentSize; + } + else if (cctx->prefixDict.dict != null) + { + dictSize = (uint)cctx->prefixDict.dictSize; + } + else + { + dictSize = 0; + } + + memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, (uint)sizeof(repcodes_s)); + for (; idx < inSeqsSize && (inSeqs[idx].matchLength != 0 || inSeqs[idx].offset != 0); ++idx) + { + uint litLength = inSeqs[idx].litLength; + uint matchLength = inSeqs[idx].matchLength; + uint offBase; + if (externalRepSearch == ZSTD_paramSwitch_e.ZSTD_ps_disable) + { + assert(inSeqs[idx].offset > 0); + offBase = inSeqs[idx].offset + 3; + } + else + { + uint ll0 = litLength == 0 ? 1U : 0U; + offBase = ZSTD_finalizeOffBase(inSeqs[idx].offset, updatedRepcodes.rep, ll0); + ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0); + } + + if (cctx->appliedParams.validateSequences != 0) + { + seqPos->posInSrc += litLength + matchLength; + { + nuint err_code = ZSTD_validateSequence( + offBase, + matchLength, + cctx->appliedParams.cParams.minMatch, + seqPos->posInSrc, + cctx->appliedParams.cParams.windowLog, + dictSize, + ZSTD_hasExtSeqProd(&cctx->appliedParams) + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + } + + if (idx - seqPos->idx >= cctx->seqStore.maxNbSeq) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid) + ); + } + + ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offBase, matchLength); + ip += matchLength + litLength; + } + + if (idx == inSeqsSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid)); + } + + assert(externalRepSearch != ZSTD_paramSwitch_e.ZSTD_ps_auto); + assert(idx >= startIdx); + if (externalRepSearch == ZSTD_paramSwitch_e.ZSTD_ps_disable && idx != startIdx) + { + uint* rep = updatedRepcodes.rep; + /* index of last non-block-delimiter sequence */ + uint lastSeqIdx = idx - 1; + if (lastSeqIdx >= startIdx + 2) + { + rep[2] = inSeqs[lastSeqIdx - 2].offset; + rep[1] = inSeqs[lastSeqIdx - 1].offset; + rep[0] = inSeqs[lastSeqIdx].offset; + } + else if (lastSeqIdx == startIdx + 1) + { + rep[2] = rep[0]; + rep[1] = inSeqs[lastSeqIdx - 1].offset; + rep[0] = inSeqs[lastSeqIdx].offset; + } + else + { + assert(lastSeqIdx == startIdx); + rep[2] = rep[1]; + rep[1] = rep[0]; + rep[0] = inSeqs[lastSeqIdx].offset; + } + } + + memcpy(cctx->blockState.nextCBlock->rep, updatedRepcodes.rep, (uint)sizeof(repcodes_s)); + if (inSeqs[idx].litLength != 0) + { + ZSTD_storeLastLiterals(&cctx->seqStore, ip, inSeqs[idx].litLength); + ip += inSeqs[idx].litLength; + seqPos->posInSrc += inSeqs[idx].litLength; + } + + if (ip != iend) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid)); + } + + seqPos->idx = idx + 1; + return blockSize; + } + + /* + * This function attempts to scan through @blockSize bytes in @src + * represented by the sequences in @inSeqs, + * storing any (partial) sequences. + * + * Occasionally, we may want to reduce the actual number of bytes consumed from @src + * to avoid splitting a match, notably if it would produce a match smaller than MINMATCH. + * + * @returns the number of bytes consumed from @src, necessarily <= @blockSize. + * Otherwise, it may return a ZSTD error if something went wrong. + */ + private static nuint ZSTD_transferSequences_noDelim( + ZSTD_CCtx_s* cctx, + ZSTD_SequencePosition* seqPos, + ZSTD_Sequence* inSeqs, + nuint inSeqsSize, + void* src, + nuint blockSize, + ZSTD_paramSwitch_e externalRepSearch + ) + { + uint idx = seqPos->idx; + uint startPosInSequence = seqPos->posInSequence; + uint endPosInSequence = seqPos->posInSequence + (uint)blockSize; + nuint dictSize; + byte* istart = (byte*)src; + byte* ip = istart; + /* May be adjusted if we decide to process fewer than blockSize bytes */ + byte* iend = istart + blockSize; + repcodes_s updatedRepcodes; + uint bytesAdjustment = 0; + uint finalMatchSplit = 0; + if (cctx->cdict != null) + { + dictSize = cctx->cdict->dictContentSize; + } + else if (cctx->prefixDict.dict != null) + { + dictSize = cctx->prefixDict.dictSize; + } + else + { + dictSize = 0; + } + + memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, (uint)sizeof(repcodes_s)); + while (endPosInSequence != 0 && idx < inSeqsSize && finalMatchSplit == 0) + { + ZSTD_Sequence currSeq = inSeqs[idx]; + uint litLength = currSeq.litLength; + uint matchLength = currSeq.matchLength; + uint rawOffset = currSeq.offset; + uint offBase; + if (endPosInSequence >= currSeq.litLength + currSeq.matchLength) + { + if (startPosInSequence >= litLength) + { + startPosInSequence -= litLength; + litLength = 0; + matchLength -= startPosInSequence; + } + else + { + litLength -= startPosInSequence; + } + + endPosInSequence -= currSeq.litLength + currSeq.matchLength; + startPosInSequence = 0; + } + else + { + if (endPosInSequence > litLength) + { + uint firstHalfMatchLength; + litLength = + startPosInSequence >= litLength ? 0 : litLength - startPosInSequence; + firstHalfMatchLength = endPosInSequence - startPosInSequence - litLength; + if ( + matchLength > blockSize + && firstHalfMatchLength >= cctx->appliedParams.cParams.minMatch + ) + { + /* Only ever split the match if it is larger than the block size */ + uint secondHalfMatchLength = + currSeq.matchLength + currSeq.litLength - endPosInSequence; + if (secondHalfMatchLength < cctx->appliedParams.cParams.minMatch) + { + endPosInSequence -= + cctx->appliedParams.cParams.minMatch - secondHalfMatchLength; + bytesAdjustment = + cctx->appliedParams.cParams.minMatch - secondHalfMatchLength; + firstHalfMatchLength -= bytesAdjustment; + } + + matchLength = firstHalfMatchLength; + finalMatchSplit = 1; + } + else + { + bytesAdjustment = endPosInSequence - currSeq.litLength; + endPosInSequence = currSeq.litLength; + break; + } + } + else + { + break; + } + } + + { + uint ll0 = litLength == 0 ? 1U : 0U; + offBase = ZSTD_finalizeOffBase(rawOffset, updatedRepcodes.rep, ll0); + ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0); + } + + if (cctx->appliedParams.validateSequences != 0) + { + seqPos->posInSrc += litLength + matchLength; + { + nuint err_code = ZSTD_validateSequence( + offBase, + matchLength, + cctx->appliedParams.cParams.minMatch, + seqPos->posInSrc, + cctx->appliedParams.cParams.windowLog, + dictSize, + ZSTD_hasExtSeqProd(&cctx->appliedParams) + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + } + + if (idx - seqPos->idx >= cctx->seqStore.maxNbSeq) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid) + ); + } + + ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offBase, matchLength); + ip += matchLength + litLength; + if (finalMatchSplit == 0) + idx++; + } + + assert( + idx == inSeqsSize || endPosInSequence <= inSeqs[idx].litLength + inSeqs[idx].matchLength + ); + seqPos->idx = idx; + seqPos->posInSequence = endPosInSequence; + memcpy(cctx->blockState.nextCBlock->rep, updatedRepcodes.rep, (uint)sizeof(repcodes_s)); + iend -= bytesAdjustment; + if (ip != iend) + { + /* Store any last literals */ + uint lastLLSize = (uint)(iend - ip); + assert(ip <= iend); + ZSTD_storeLastLiterals(&cctx->seqStore, ip, lastLLSize); + seqPos->posInSrc += lastLLSize; + } + + return (nuint)(iend - istart); + } + + private static void* ZSTD_selectSequenceCopier(ZSTD_sequenceFormat_e mode) + { + assert( + ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_experimentalParam11, (int)mode) != 0 + ); + if (mode == ZSTD_sequenceFormat_e.ZSTD_sf_explicitBlockDelimiters) + { + return (delegate* managed< + ZSTD_CCtx_s*, + ZSTD_SequencePosition*, + ZSTD_Sequence*, + nuint, + void*, + nuint, + ZSTD_paramSwitch_e, + nuint>)(&ZSTD_transferSequences_wBlockDelim); + } + + assert(mode == ZSTD_sequenceFormat_e.ZSTD_sf_noBlockDelimiters); + return (delegate* managed< + ZSTD_CCtx_s*, + ZSTD_SequencePosition*, + ZSTD_Sequence*, + nuint, + void*, + nuint, + ZSTD_paramSwitch_e, + nuint>)(&ZSTD_transferSequences_noDelim); + } + + /* Discover the size of next block by searching for the delimiter. + * Note that a block delimiter **must** exist in this mode, + * otherwise it's an input error. + * The block size retrieved will be later compared to ensure it remains within bounds */ + private static nuint blockSize_explicitDelimiter( + ZSTD_Sequence* inSeqs, + nuint inSeqsSize, + ZSTD_SequencePosition seqPos + ) + { + int end = 0; + nuint blockSize = 0; + nuint spos = seqPos.idx; + assert(spos <= inSeqsSize); + while (spos < inSeqsSize) + { + end = inSeqs[spos].offset == 0 ? 1 : 0; + blockSize += inSeqs[spos].litLength + inSeqs[spos].matchLength; + if (end != 0) + { + if (inSeqs[spos].matchLength != 0) + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid) + ); + break; + } + + spos++; + } + + if (end == 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid)); + return blockSize; + } + + private static nuint determine_blockSize( + ZSTD_sequenceFormat_e mode, + nuint blockSize, + nuint remaining, + ZSTD_Sequence* inSeqs, + nuint inSeqsSize, + ZSTD_SequencePosition seqPos + ) + { + if (mode == ZSTD_sequenceFormat_e.ZSTD_sf_noBlockDelimiters) + { + return remaining < blockSize ? remaining : blockSize; + } + + assert(mode == ZSTD_sequenceFormat_e.ZSTD_sf_explicitBlockDelimiters); + { + nuint explicitBlockSize = blockSize_explicitDelimiter(inSeqs, inSeqsSize, seqPos); + { + nuint err_code = explicitBlockSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (explicitBlockSize > blockSize) + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid) + ); + if (explicitBlockSize > remaining) + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid) + ); + return explicitBlockSize; + } + } + + /* Compress all provided sequences, block-by-block. + * + * Returns the cumulative size of all compressed blocks (including their headers), + * otherwise a ZSTD error. + */ + private static nuint ZSTD_compressSequences_internal( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + ZSTD_Sequence* inSeqs, + nuint inSeqsSize, + void* src, + nuint srcSize + ) + { + nuint cSize = 0; + nuint remaining = srcSize; + ZSTD_SequencePosition seqPos = new ZSTD_SequencePosition + { + idx = 0, + posInSequence = 0, + posInSrc = 0, + }; + byte* ip = (byte*)src; + byte* op = (byte*)dst; + void* sequenceCopier = ZSTD_selectSequenceCopier(cctx->appliedParams.blockDelimiters); + if (remaining == 0) + { + /* last block */ + uint cBlockHeader24 = 1 + ((uint)blockType_e.bt_raw << 1); + if (dstCapacity < 4) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + MEM_writeLE32(op, cBlockHeader24); + op += ZSTD_blockHeaderSize; + dstCapacity -= ZSTD_blockHeaderSize; + cSize += ZSTD_blockHeaderSize; + } + + while (remaining != 0) + { + nuint compressedSeqsSize; + nuint cBlockSize; + nuint blockSize = determine_blockSize( + cctx->appliedParams.blockDelimiters, + cctx->blockSizeMax, + remaining, + inSeqs, + inSeqsSize, + seqPos + ); + uint lastBlock = blockSize == remaining ? 1U : 0U; + { + nuint err_code = blockSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(blockSize <= remaining); + ZSTD_resetSeqStore(&cctx->seqStore); + blockSize = ( + (delegate* managed< + ZSTD_CCtx_s*, + ZSTD_SequencePosition*, + ZSTD_Sequence*, + nuint, + void*, + nuint, + ZSTD_paramSwitch_e, + nuint>)sequenceCopier + )( + cctx, + &seqPos, + inSeqs, + inSeqsSize, + ip, + blockSize, + cctx->appliedParams.searchForExternalRepcodes + ); + { + nuint err_code = blockSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (blockSize < (nuint)(1 + 1) + ZSTD_blockHeaderSize + 1 + 1) + { + cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock); + { + nuint err_code = cBlockSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + cSize += cBlockSize; + ip += blockSize; + op += cBlockSize; + remaining -= blockSize; + dstCapacity -= cBlockSize; + continue; + } + + if (dstCapacity < ZSTD_blockHeaderSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + compressedSeqsSize = ZSTD_entropyCompressSeqStore( + &cctx->seqStore, + &cctx->blockState.prevCBlock->entropy, + &cctx->blockState.nextCBlock->entropy, + &cctx->appliedParams, + op + ZSTD_blockHeaderSize, + dstCapacity - ZSTD_blockHeaderSize, + blockSize, + cctx->tmpWorkspace, + cctx->tmpWkspSize, + cctx->bmi2 + ); + { + nuint err_code = compressedSeqsSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if ( + cctx->isFirstBlock == 0 + && ZSTD_maybeRLE(&cctx->seqStore) != 0 + && ZSTD_isRLE(ip, blockSize) != 0 + ) + { + compressedSeqsSize = 1; + } + + if (compressedSeqsSize == 0) + { + cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock); + { + nuint err_code = cBlockSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + } + else if (compressedSeqsSize == 1) + { + cBlockSize = ZSTD_rleCompressBlock(op, dstCapacity, *ip, blockSize, lastBlock); + { + nuint err_code = cBlockSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + } + else + { + uint cBlockHeader; + ZSTD_blockState_confirmRepcodesAndEntropyTables(&cctx->blockState); + if ( + cctx->blockState.prevCBlock->entropy.fse.offcode_repeatMode + == FSE_repeat.FSE_repeat_valid + ) + cctx->blockState.prevCBlock->entropy.fse.offcode_repeatMode = + FSE_repeat.FSE_repeat_check; + cBlockHeader = + lastBlock + + ((uint)blockType_e.bt_compressed << 1) + + (uint)(compressedSeqsSize << 3); + MEM_writeLE24(op, cBlockHeader); + cBlockSize = ZSTD_blockHeaderSize + compressedSeqsSize; + } + + cSize += cBlockSize; + if (lastBlock != 0) + { + break; + } + else + { + ip += blockSize; + op += cBlockSize; + remaining -= blockSize; + dstCapacity -= cBlockSize; + cctx->isFirstBlock = 0; + } + } + + return cSize; + } + + /*! ZSTD_compressSequences() : + * Compress an array of ZSTD_Sequence, associated with @src buffer, into dst. + * @src contains the entire input (not just the literals). + * If @srcSize > sum(sequence.length), the remaining bytes are considered all literals + * If a dictionary is included, then the cctx should reference the dict (see: ZSTD_CCtx_refCDict(), ZSTD_CCtx_loadDictionary(), etc.). + * The entire source is compressed into a single frame. + * + * The compression behavior changes based on cctx params. In particular: + * If ZSTD_c_blockDelimiters == ZSTD_sf_noBlockDelimiters, the array of ZSTD_Sequence is expected to contain + * no block delimiters (defined in ZSTD_Sequence). Block boundaries are roughly determined based on + * the block size derived from the cctx, and sequences may be split. This is the default setting. + * + * If ZSTD_c_blockDelimiters == ZSTD_sf_explicitBlockDelimiters, the array of ZSTD_Sequence is expected to contain + * valid block delimiters (defined in ZSTD_Sequence). Behavior is undefined if no block delimiters are provided. + * + * When ZSTD_c_blockDelimiters == ZSTD_sf_explicitBlockDelimiters, it's possible to decide generating repcodes + * using the advanced parameter ZSTD_c_repcodeResolution. Repcodes will improve compression ratio, though the benefit + * can vary greatly depending on Sequences. On the other hand, repcode resolution is an expensive operation. + * By default, it's disabled at low (<10) compression levels, and enabled above the threshold (>=10). + * ZSTD_c_repcodeResolution makes it possible to directly manage this processing in either direction. + * + * If ZSTD_c_validateSequences == 0, this function blindly accepts the Sequences provided. Invalid Sequences cause undefined + * behavior. If ZSTD_c_validateSequences == 1, then the function will detect invalid Sequences (see doc/zstd_compression_format.md for + * specifics regarding offset/matchlength requirements) and then bail out and return an error. + * + * In addition to the two adjustable experimental params, there are other important cctx params. + * - ZSTD_c_minMatch MUST be set as less than or equal to the smallest match generated by the match finder. It has a minimum value of ZSTD_MINMATCH_MIN. + * - ZSTD_c_compressionLevel accordingly adjusts the strength of the entropy coder, as it would in typical compression. + * - ZSTD_c_windowLog affects offset validation: this function will return an error at higher debug levels if a provided offset + * is larger than what the spec allows for a given window log and dictionary (if present). See: doc/zstd_compression_format.md + * + * Note: Repcodes are, as of now, always re-calculated within this function, ZSTD_Sequence.rep is effectively unused. + * Dev Note: Once ability to ingest repcodes become available, the explicit block delims mode must respect those repcodes exactly, + * and cannot emit an RLE block that disagrees with the repcode history. + * @return : final compressed size, or a ZSTD error code. + */ + public static nuint ZSTD_compressSequences( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + ZSTD_Sequence* inSeqs, + nuint inSeqsSize, + void* src, + nuint srcSize + ) + { + byte* op = (byte*)dst; + nuint cSize = 0; + assert(cctx != null); + { + nuint err_code = ZSTD_CCtx_init_compressStream2( + cctx, + ZSTD_EndDirective.ZSTD_e_end, + srcSize + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint frameHeaderSize = ZSTD_writeFrameHeader( + op, + dstCapacity, + &cctx->appliedParams, + srcSize, + cctx->dictID + ); + op += frameHeaderSize; + assert(frameHeaderSize <= dstCapacity); + dstCapacity -= frameHeaderSize; + cSize += frameHeaderSize; + } + + if (cctx->appliedParams.fParams.checksumFlag != 0 && srcSize != 0) + { + ZSTD_XXH64_update(&cctx->xxhState, src, srcSize); + } + + { + nuint cBlocksSize = ZSTD_compressSequences_internal( + cctx, + op, + dstCapacity, + inSeqs, + inSeqsSize, + src, + srcSize + ); + { + nuint err_code = cBlocksSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + cSize += cBlocksSize; + assert(cBlocksSize <= dstCapacity); + dstCapacity -= cBlocksSize; + } + + if (cctx->appliedParams.fParams.checksumFlag != 0) + { + uint checksum = (uint)ZSTD_XXH64_digest(&cctx->xxhState); + if (dstCapacity < 4) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + MEM_writeLE32((sbyte*)dst + cSize, checksum); + cSize += 4; + } + + return cSize; + } + + private static nuint convertSequences_noRepcodes( + SeqDef_s* dstSeqs, + ZSTD_Sequence* inSeqs, + nuint nbSequences + ) + { + nuint longLen = 0; + nuint n; + for (n = 0; n < nbSequences; n++) + { + assert(inSeqs[n].offset > 0); + dstSeqs[n].offBase = inSeqs[n].offset + 3; + dstSeqs[n].litLength = (ushort)inSeqs[n].litLength; + dstSeqs[n].mlBase = (ushort)(inSeqs[n].matchLength - 3); + if (inSeqs[n].matchLength > 65535 + 3) + { + assert(longLen == 0); + longLen = n + 1; + } + + if (inSeqs[n].litLength > 65535) + { + assert(longLen == 0); + longLen = n + nbSequences + 1; + } + } + + return longLen; + } + + /* + * Precondition: Sequences must end on an explicit Block Delimiter + * @return: 0 on success, or an error code. + * Note: Sequence validation functionality has been disabled (removed). + * This is helpful to generate a lean main pipeline, improving performance. + * It may be re-inserted later. + */ + private static nuint ZSTD_convertBlockSequences( + ZSTD_CCtx_s* cctx, + ZSTD_Sequence* inSeqs, + nuint nbSequences, + int repcodeResolution + ) + { + repcodes_s updatedRepcodes; + nuint seqNb = 0; + if (nbSequences >= cctx->seqStore.maxNbSeq) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid)); + } + + memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, (uint)sizeof(repcodes_s)); + assert(nbSequences >= 1); + assert(inSeqs[nbSequences - 1].matchLength == 0); + assert(inSeqs[nbSequences - 1].offset == 0); + if (repcodeResolution == 0) + { + nuint longl = convertSequences_noRepcodes( + cctx->seqStore.sequencesStart, + inSeqs, + nbSequences - 1 + ); + cctx->seqStore.sequences = cctx->seqStore.sequencesStart + nbSequences - 1; + if (longl != 0) + { + assert(cctx->seqStore.longLengthType == ZSTD_longLengthType_e.ZSTD_llt_none); + if (longl <= nbSequences - 1) + { + cctx->seqStore.longLengthType = ZSTD_longLengthType_e.ZSTD_llt_matchLength; + cctx->seqStore.longLengthPos = (uint)(longl - 1); + } + else + { + assert(longl <= 2 * (nbSequences - 1)); + cctx->seqStore.longLengthType = ZSTD_longLengthType_e.ZSTD_llt_literalLength; + cctx->seqStore.longLengthPos = (uint)(longl - (nbSequences - 1) - 1); + } + } + } + else + { + for (seqNb = 0; seqNb < nbSequences - 1; seqNb++) + { + uint litLength = inSeqs[seqNb].litLength; + uint matchLength = inSeqs[seqNb].matchLength; + uint ll0 = litLength == 0 ? 1U : 0U; + uint offBase = ZSTD_finalizeOffBase(inSeqs[seqNb].offset, updatedRepcodes.rep, ll0); + ZSTD_storeSeqOnly(&cctx->seqStore, litLength, offBase, matchLength); + ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0); + } + } + + if (repcodeResolution == 0 && nbSequences > 1) + { + uint* rep = updatedRepcodes.rep; + if (nbSequences >= 4) + { + /* index of last full sequence */ + uint lastSeqIdx = (uint)nbSequences - 2; + rep[2] = inSeqs[lastSeqIdx - 2].offset; + rep[1] = inSeqs[lastSeqIdx - 1].offset; + rep[0] = inSeqs[lastSeqIdx].offset; + } + else if (nbSequences == 3) + { + rep[2] = rep[0]; + rep[1] = inSeqs[0].offset; + rep[0] = inSeqs[1].offset; + } + else + { + assert(nbSequences == 2); + rep[2] = rep[1]; + rep[1] = rep[0]; + rep[0] = inSeqs[0].offset; + } + } + + memcpy(cctx->blockState.nextCBlock->rep, updatedRepcodes.rep, (uint)sizeof(repcodes_s)); + return 0; + } + + private static BlockSummary ZSTD_get1BlockSummary(ZSTD_Sequence* seqs, nuint nbSeqs) + { + nuint totalMatchSize = 0; + nuint litSize = 0; + nuint n; + assert(seqs != null); + for (n = 0; n < nbSeqs; n++) + { + totalMatchSize += seqs[n].matchLength; + litSize += seqs[n].litLength; + if (seqs[n].matchLength == 0) + { + assert(seqs[n].offset == 0); + break; + } + } + + if (n == nbSeqs) + { + BlockSummary bs; + System.Runtime.CompilerServices.Unsafe.SkipInit(out bs); + bs.nbSequences = unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid) + ); + return bs; + } + + { + BlockSummary bs; + bs.nbSequences = n + 1; + bs.blockSize = litSize + totalMatchSize; + bs.litSize = litSize; + return bs; + } + } + + private static nuint ZSTD_compressSequencesAndLiterals_internal( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + ZSTD_Sequence* inSeqs, + nuint nbSequences, + void* literals, + nuint litSize, + nuint srcSize + ) + { + nuint remaining = srcSize; + nuint cSize = 0; + byte* op = (byte*)dst; + int repcodeResolution = + cctx->appliedParams.searchForExternalRepcodes == ZSTD_paramSwitch_e.ZSTD_ps_enable + ? 1 + : 0; + assert(cctx->appliedParams.searchForExternalRepcodes != ZSTD_paramSwitch_e.ZSTD_ps_auto); + if (nbSequences == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid)); + } + + if (nbSequences == 1 && inSeqs[0].litLength == 0) + { + /* last block */ + uint cBlockHeader24 = 1 + ((uint)blockType_e.bt_raw << 1); + if (dstCapacity < 3) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + MEM_writeLE24(op, cBlockHeader24); + op += ZSTD_blockHeaderSize; + dstCapacity -= ZSTD_blockHeaderSize; + cSize += ZSTD_blockHeaderSize; + } + + while (nbSequences != 0) + { + nuint compressedSeqsSize, + cBlockSize, + conversionStatus; + BlockSummary block = ZSTD_get1BlockSummary(inSeqs, nbSequences); + uint lastBlock = block.nbSequences == nbSequences ? 1U : 0U; + { + nuint err_code = block.nbSequences; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(block.nbSequences <= nbSequences); + if (block.litSize > litSize) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid) + ); + } + + ZSTD_resetSeqStore(&cctx->seqStore); + conversionStatus = ZSTD_convertBlockSequences( + cctx, + inSeqs, + block.nbSequences, + repcodeResolution + ); + { + nuint err_code = conversionStatus; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + inSeqs += block.nbSequences; + nbSequences -= block.nbSequences; + remaining -= block.blockSize; + if (dstCapacity < ZSTD_blockHeaderSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + compressedSeqsSize = ZSTD_entropyCompressSeqStore_internal( + op + ZSTD_blockHeaderSize, + dstCapacity - ZSTD_blockHeaderSize, + literals, + block.litSize, + &cctx->seqStore, + &cctx->blockState.prevCBlock->entropy, + &cctx->blockState.nextCBlock->entropy, + &cctx->appliedParams, + cctx->tmpWorkspace, + cctx->tmpWkspSize, + cctx->bmi2 + ); + { + nuint err_code = compressedSeqsSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (compressedSeqsSize > cctx->blockSizeMax) + compressedSeqsSize = 0; + litSize -= block.litSize; + literals = (sbyte*)literals + block.litSize; + if (compressedSeqsSize == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_cannotProduce_uncompressedBlock) + ); + } + else + { + uint cBlockHeader; + assert(compressedSeqsSize > 1); + ZSTD_blockState_confirmRepcodesAndEntropyTables(&cctx->blockState); + if ( + cctx->blockState.prevCBlock->entropy.fse.offcode_repeatMode + == FSE_repeat.FSE_repeat_valid + ) + cctx->blockState.prevCBlock->entropy.fse.offcode_repeatMode = + FSE_repeat.FSE_repeat_check; + cBlockHeader = + lastBlock + + ((uint)blockType_e.bt_compressed << 1) + + (uint)(compressedSeqsSize << 3); + MEM_writeLE24(op, cBlockHeader); + cBlockSize = ZSTD_blockHeaderSize + compressedSeqsSize; + } + + cSize += cBlockSize; + op += cBlockSize; + dstCapacity -= cBlockSize; + cctx->isFirstBlock = 0; + if (lastBlock != 0) + { + assert(nbSequences == 0); + break; + } + } + + if (litSize != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid)); + } + + if (remaining != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_externalSequences_invalid)); + } + + return cSize; + } + + /*! ZSTD_compressSequencesAndLiterals() : + * This is a variant of ZSTD_compressSequences() which, + * instead of receiving (src,srcSize) as input parameter, receives (literals,litSize), + * aka all the literals, already extracted and laid out into a single continuous buffer. + * This can be useful if the process generating the sequences also happens to generate the buffer of literals, + * thus skipping an extraction + caching stage. + * It's a speed optimization, useful when the right conditions are met, + * but it also features the following limitations: + * - Only supports explicit delimiter mode + * - Currently does not support Sequences validation (so input Sequences are trusted) + * - Not compatible with frame checksum, which must be disabled + * - If any block is incompressible, will fail and return an error + * - @litSize must be == sum of all @.litLength fields in @inSeqs. Any discrepancy will generate an error. + * - @litBufCapacity is the size of the underlying buffer into which literals are written, starting at address @literals. + * @litBufCapacity must be at least 8 bytes larger than @litSize. + * - @decompressedSize must be correct, and correspond to the sum of all Sequences. Any discrepancy will generate an error. + * @return : final compressed size, or a ZSTD error code. + */ + public static nuint ZSTD_compressSequencesAndLiterals( + ZSTD_CCtx_s* cctx, + void* dst, + nuint dstCapacity, + ZSTD_Sequence* inSeqs, + nuint inSeqsSize, + void* literals, + nuint litSize, + nuint litCapacity, + nuint decompressedSize + ) + { + byte* op = (byte*)dst; + nuint cSize = 0; + assert(cctx != null); + if (litCapacity < litSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_workSpace_tooSmall)); + } + + { + nuint err_code = ZSTD_CCtx_init_compressStream2( + cctx, + ZSTD_EndDirective.ZSTD_e_end, + decompressedSize + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (cctx->appliedParams.blockDelimiters == ZSTD_sequenceFormat_e.ZSTD_sf_noBlockDelimiters) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_unsupported)); + } + + if (cctx->appliedParams.validateSequences != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported)); + } + + if (cctx->appliedParams.fParams.checksumFlag != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_unsupported)); + } + + { + nuint frameHeaderSize = ZSTD_writeFrameHeader( + op, + dstCapacity, + &cctx->appliedParams, + decompressedSize, + cctx->dictID + ); + op += frameHeaderSize; + assert(frameHeaderSize <= dstCapacity); + dstCapacity -= frameHeaderSize; + cSize += frameHeaderSize; + } + + { + nuint cBlocksSize = ZSTD_compressSequencesAndLiterals_internal( + cctx, + op, + dstCapacity, + inSeqs, + inSeqsSize, + literals, + litSize, + decompressedSize + ); + { + nuint err_code = cBlocksSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + cSize += cBlocksSize; + assert(cBlocksSize <= dstCapacity); + dstCapacity -= cBlocksSize; + } + + return cSize; + } + + /*====== Finalize ======*/ + private static ZSTD_inBuffer_s inBuffer_forEndFlush(ZSTD_CCtx_s* zcs) + { + ZSTD_inBuffer_s nullInput = new ZSTD_inBuffer_s + { + src = null, + size = 0, + pos = 0, + }; + int stableInput = + zcs->appliedParams.inBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable ? 1 : 0; + return stableInput != 0 ? zcs->expectedInBuffer : nullInput; + } + + /*! ZSTD_flushStream() : + * @return : amount of data remaining to flush */ + public static nuint ZSTD_flushStream(ZSTD_CCtx_s* zcs, ZSTD_outBuffer_s* output) + { + ZSTD_inBuffer_s input = inBuffer_forEndFlush(zcs); + input.size = input.pos; + return ZSTD_compressStream2(zcs, output, &input, ZSTD_EndDirective.ZSTD_e_flush); + } + + /*! Equivalent to ZSTD_compressStream2(zcs, output, &emptyInput, ZSTD_e_end). */ + public static nuint ZSTD_endStream(ZSTD_CCtx_s* zcs, ZSTD_outBuffer_s* output) + { + ZSTD_inBuffer_s input = inBuffer_forEndFlush(zcs); + nuint remainingToFlush = ZSTD_compressStream2( + zcs, + output, + &input, + ZSTD_EndDirective.ZSTD_e_end + ); + { + nuint err_code = remainingToFlush; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (zcs->appliedParams.nbWorkers > 0) + return remainingToFlush; + { + nuint lastBlockSize = (nuint)(zcs->frameEnded != 0 ? 0 : 3); + nuint checksumSize = (nuint)( + zcs->frameEnded != 0 ? 0 : zcs->appliedParams.fParams.checksumFlag * 4 + ); + nuint toFlush = remainingToFlush + lastBlockSize + checksumSize; + return toFlush; + } + } + + public static int ZSTD_maxCLevel() + { + return 22; + } + + public static int ZSTD_minCLevel() + { + return -(1 << 17); + } + + public static int ZSTD_defaultCLevel() + { + return 3; + } + + private static ZSTD_compressionParameters ZSTD_dedicatedDictSearch_getCParams( + int compressionLevel, + nuint dictSize + ) + { + ZSTD_compressionParameters cParams = ZSTD_getCParams_internal( + compressionLevel, + 0, + dictSize, + ZSTD_CParamMode_e.ZSTD_cpm_createCDict + ); + switch (cParams.strategy) + { + case ZSTD_strategy.ZSTD_fast: + case ZSTD_strategy.ZSTD_dfast: + break; + case ZSTD_strategy.ZSTD_greedy: + case ZSTD_strategy.ZSTD_lazy: + case ZSTD_strategy.ZSTD_lazy2: + cParams.hashLog += 2; + break; + case ZSTD_strategy.ZSTD_btlazy2: + case ZSTD_strategy.ZSTD_btopt: + case ZSTD_strategy.ZSTD_btultra: + case ZSTD_strategy.ZSTD_btultra2: + break; + } + + return cParams; + } + + private static int ZSTD_dedicatedDictSearch_isSupported(ZSTD_compressionParameters* cParams) + { + return + cParams->strategy >= ZSTD_strategy.ZSTD_greedy + && cParams->strategy <= ZSTD_strategy.ZSTD_lazy2 + && cParams->hashLog > cParams->chainLog + && cParams->chainLog <= 24 + ? 1 + : 0; + } + + /** + * Reverses the adjustment applied to cparams when enabling dedicated dict + * search. This is used to recover the params set to be used in the working + * context. (Otherwise, those tables would also grow.) + */ + private static void ZSTD_dedicatedDictSearch_revertCParams(ZSTD_compressionParameters* cParams) + { + switch (cParams->strategy) + { + case ZSTD_strategy.ZSTD_fast: + case ZSTD_strategy.ZSTD_dfast: + break; + case ZSTD_strategy.ZSTD_greedy: + case ZSTD_strategy.ZSTD_lazy: + case ZSTD_strategy.ZSTD_lazy2: + cParams->hashLog -= 2; + if (cParams->hashLog < 6) + { + cParams->hashLog = 6; + } + + break; + case ZSTD_strategy.ZSTD_btlazy2: + case ZSTD_strategy.ZSTD_btopt: + case ZSTD_strategy.ZSTD_btultra: + case ZSTD_strategy.ZSTD_btultra2: + break; + } + } + + private static ulong ZSTD_getCParamRowSize( + ulong srcSizeHint, + nuint dictSize, + ZSTD_CParamMode_e mode + ) + { + switch (mode) + { + case ZSTD_CParamMode_e.ZSTD_cpm_unknown: + case ZSTD_CParamMode_e.ZSTD_cpm_noAttachDict: + case ZSTD_CParamMode_e.ZSTD_cpm_createCDict: + break; + case ZSTD_CParamMode_e.ZSTD_cpm_attachDict: + dictSize = 0; + break; + default: + assert(0 != 0); + break; + } + + { + int unknown = srcSizeHint == unchecked(0UL - 1) ? 1 : 0; + nuint addedSize = (nuint)(unknown != 0 && dictSize > 0 ? 500 : 0); + return unknown != 0 && dictSize == 0 + ? unchecked(0UL - 1) + : srcSizeHint + dictSize + addedSize; + } + } + + /*! ZSTD_getCParams_internal() : + * @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize. + * Note: srcSizeHint 0 means 0, use ZSTD_CONTENTSIZE_UNKNOWN for unknown. + * Use dictSize == 0 for unknown or unused. + * Note: `mode` controls how we treat the `dictSize`. See docs for `ZSTD_CParamMode_e`. */ + private static ZSTD_compressionParameters ZSTD_getCParams_internal( + int compressionLevel, + ulong srcSizeHint, + nuint dictSize, + ZSTD_CParamMode_e mode + ) + { + ulong rSize = ZSTD_getCParamRowSize(srcSizeHint, dictSize, mode); + uint tableID = (uint)( + (rSize <= 256 * (1 << 10) ? 1 : 0) + + (rSize <= 128 * (1 << 10) ? 1 : 0) + + (rSize <= 16 * (1 << 10) ? 1 : 0) + ); + int row; + if (compressionLevel == 0) + row = 3; + else if (compressionLevel < 0) + row = 0; + else if (compressionLevel > 22) + row = 22; + else + row = compressionLevel; + { + ZSTD_compressionParameters cp = ZSTD_defaultCParameters[tableID][row]; + if (compressionLevel < 0) + { + int clampedCompressionLevel = + ZSTD_minCLevel() > compressionLevel ? ZSTD_minCLevel() : compressionLevel; + cp.targetLength = (uint)-clampedCompressionLevel; + } + + return ZSTD_adjustCParams_internal( + cp, + srcSizeHint, + dictSize, + mode, + ZSTD_paramSwitch_e.ZSTD_ps_auto + ); + } + } + + /*! ZSTD_getCParams() : + * @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize. + * Size values are optional, provide 0 if not known or unused */ + public static ZSTD_compressionParameters ZSTD_getCParams( + int compressionLevel, + ulong srcSizeHint, + nuint dictSize + ) + { + if (srcSizeHint == 0) + srcSizeHint = unchecked(0UL - 1); + return ZSTD_getCParams_internal( + compressionLevel, + srcSizeHint, + dictSize, + ZSTD_CParamMode_e.ZSTD_cpm_unknown + ); + } + + /*! ZSTD_getParams() : + * same idea as ZSTD_getCParams() + * @return a `ZSTD_parameters` structure (instead of `ZSTD_compressionParameters`). + * Fields of `ZSTD_frameParameters` are set to default values */ + private static ZSTD_parameters ZSTD_getParams_internal( + int compressionLevel, + ulong srcSizeHint, + nuint dictSize, + ZSTD_CParamMode_e mode + ) + { + ZSTD_parameters @params; + ZSTD_compressionParameters cParams = ZSTD_getCParams_internal( + compressionLevel, + srcSizeHint, + dictSize, + mode + ); + @params = new ZSTD_parameters { cParams = cParams }; + @params.fParams.contentSizeFlag = 1; + return @params; + } + + /*! ZSTD_getParams() : + * same idea as ZSTD_getCParams() + * @return a `ZSTD_parameters` structure (instead of `ZSTD_compressionParameters`). + * Fields of `ZSTD_frameParameters` are set to default values */ + public static ZSTD_parameters ZSTD_getParams( + int compressionLevel, + ulong srcSizeHint, + nuint dictSize + ) + { + if (srcSizeHint == 0) + srcSizeHint = unchecked(0UL - 1); + return ZSTD_getParams_internal( + compressionLevel, + srcSizeHint, + dictSize, + ZSTD_CParamMode_e.ZSTD_cpm_unknown + ); + } + + /*! ZSTD_registerSequenceProducer() : + * Instruct zstd to use a block-level external sequence producer function. + * + * The sequenceProducerState must be initialized by the caller, and the caller is + * responsible for managing its lifetime. This parameter is sticky across + * compressions. It will remain set until the user explicitly resets compression + * parameters. + * + * Sequence producer registration is considered to be an "advanced parameter", + * part of the "advanced API". This means it will only have an effect on compression + * APIs which respect advanced parameters, such as compress2() and compressStream2(). + * Older compression APIs such as compressCCtx(), which predate the introduction of + * "advanced parameters", will ignore any external sequence producer setting. + * + * The sequence producer can be "cleared" by registering a NULL function pointer. This + * removes all limitations described above in the "LIMITATIONS" section of the API docs. + * + * The user is strongly encouraged to read the full API documentation (above) before + * calling this function. */ + public static void ZSTD_registerSequenceProducer( + ZSTD_CCtx_s* zc, + void* extSeqProdState, + void* extSeqProdFunc + ) + { + assert(zc != null); + ZSTD_CCtxParams_registerSequenceProducer( + &zc->requestedParams, + extSeqProdState, + extSeqProdFunc + ); + } + + /*! ZSTD_CCtxParams_registerSequenceProducer() : + * Same as ZSTD_registerSequenceProducer(), but operates on ZSTD_CCtx_params. + * This is used for accurate size estimation with ZSTD_estimateCCtxSize_usingCCtxParams(), + * which is needed when creating a ZSTD_CCtx with ZSTD_initStaticCCtx(). + * + * If you are using the external sequence producer API in a scenario where ZSTD_initStaticCCtx() + * is required, then this function is for you. Otherwise, you probably don't need it. + * + * See tests/zstreamtest.c for example usage. */ + public static void ZSTD_CCtxParams_registerSequenceProducer( + ZSTD_CCtx_params_s* @params, + void* extSeqProdState, + void* extSeqProdFunc + ) + { + assert(@params != null); + if (extSeqProdFunc != null) + { + @params->extSeqProdFunc = extSeqProdFunc; + @params->extSeqProdState = extSeqProdState; + } + else + { + @params->extSeqProdFunc = null; + @params->extSeqProdState = null; + } + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressInternal.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressInternal.cs new file mode 100644 index 00000000..2e836fb1 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressInternal.cs @@ -0,0 +1,1444 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /** + * Returns the ZSTD_SequenceLength for the given sequences. It handles the decoding of long sequences + * indicated by longLengthPos and longLengthType, and adds MINMATCH back to matchLength. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ZSTD_SequenceLength ZSTD_getSequenceLength(SeqStore_t* seqStore, SeqDef_s* seq) + { + ZSTD_SequenceLength seqLen; + seqLen.litLength = seq->litLength; + seqLen.matchLength = (uint)(seq->mlBase + 3); + if (seqStore->longLengthPos == (uint)(seq - seqStore->sequencesStart)) + { + if (seqStore->longLengthType == ZSTD_longLengthType_e.ZSTD_llt_literalLength) + { + seqLen.litLength += 0x10000; + } + + if (seqStore->longLengthType == ZSTD_longLengthType_e.ZSTD_llt_matchLength) + { + seqLen.matchLength += 0x10000; + } + } + + return seqLen; + } + + private static readonly RawSeqStore_t kNullRawSeqStore = new RawSeqStore_t( + seq: null, + pos: 0, + posInSequence: 0, + size: 0, + capacity: 0 + ); +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_LL_Code => + new byte[64] + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 16, + 17, + 17, + 18, + 18, + 19, + 19, + 20, + 20, + 20, + 20, + 21, + 21, + 21, + 21, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + }; + private static byte* LL_Code => + (byte*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_LL_Code) + ); +#else + + private static readonly byte* LL_Code = GetArrayPointer( + new byte[64] + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 16, + 17, + 17, + 18, + 18, + 19, + 19, + 20, + 20, + 20, + 20, + 21, + 21, + 21, + 21, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + } + ); +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_LLcode(uint litLength) + { + const uint LL_deltaCode = 19; + return litLength > 63 ? ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; + } + +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_ML_Code => + new byte[128] + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 32, + 33, + 33, + 34, + 34, + 35, + 35, + 36, + 36, + 36, + 36, + 37, + 37, + 37, + 37, + 38, + 38, + 38, + 38, + 38, + 38, + 38, + 38, + 39, + 39, + 39, + 39, + 39, + 39, + 39, + 39, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + }; + private static byte* ML_Code => + (byte*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_ML_Code) + ); +#else + + private static readonly byte* ML_Code = GetArrayPointer( + new byte[128] + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 32, + 33, + 33, + 34, + 34, + 35, + 35, + 36, + 36, + 36, + 36, + 37, + 37, + 37, + 37, + 38, + 38, + 38, + 38, + 38, + 38, + 38, + 38, + 39, + 39, + 39, + 39, + 39, + 39, + 39, + 39, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 41, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + } + ); +#endif + /* ZSTD_MLcode() : + * note : mlBase = matchLength - MINMATCH; + * because it's the format it's stored in seqStore->sequences */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_MLcode(uint mlBase) + { + const uint ML_deltaCode = 36; + return mlBase > 127 ? ZSTD_highbit32(mlBase) + ML_deltaCode : ML_Code[mlBase]; + } + + /* ZSTD_cParam_withinBounds: + * @return 1 if value is within cParam bounds, + * 0 otherwise */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_cParam_withinBounds(ZSTD_cParameter cParam, int value) + { + ZSTD_bounds bounds = ZSTD_cParam_getBounds(cParam); + if (ERR_isError(bounds.error)) + return 0; + if (value < bounds.lowerBound) + return 0; + if (value > bounds.upperBound) + return 0; + return 1; + } + + /* ZSTD_selectAddr: + * @return index >= lowLimit ? candidate : backup, + * tries to force branchless codegen. */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte* ZSTD_selectAddr(uint index, uint lowLimit, byte* candidate, byte* backup) + { + return index >= lowLimit ? candidate : backup; + } + + /* ZSTD_noCompressBlock() : + * Writes uncompressed block to dst buffer from given src. + * Returns the size of the block */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_noCompressBlock( + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + uint lastBlock + ) + { + uint cBlockHeader24 = lastBlock + ((uint)blockType_e.bt_raw << 1) + (uint)(srcSize << 3); + if (srcSize + ZSTD_blockHeaderSize > dstCapacity) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + MEM_writeLE24(dst, cBlockHeader24); + memcpy((byte*)dst + ZSTD_blockHeaderSize, src, (uint)srcSize); + return ZSTD_blockHeaderSize + srcSize; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_rleCompressBlock( + void* dst, + nuint dstCapacity, + byte src, + nuint srcSize, + uint lastBlock + ) + { + byte* op = (byte*)dst; + uint cBlockHeader = lastBlock + ((uint)blockType_e.bt_rle << 1) + (uint)(srcSize << 3); + if (dstCapacity < 4) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + MEM_writeLE24(op, cBlockHeader); + op[3] = src; + return 4; + } + + /* ZSTD_minGain() : + * minimum compression required + * to generate a compress block or a compressed literals section. + * note : use same formula for both situations */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_minGain(nuint srcSize, ZSTD_strategy strat) + { + uint minlog = strat >= ZSTD_strategy.ZSTD_btultra ? (uint)strat - 1 : 6; + assert(ZSTD_cParam_withinBounds(ZSTD_cParameter.ZSTD_c_strategy, (int)strat) != 0); + return (srcSize >> (int)minlog) + 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_literalsCompressionIsDisabled(ZSTD_CCtx_params_s* cctxParams) + { + switch (cctxParams->literalCompressionMode) + { + case ZSTD_paramSwitch_e.ZSTD_ps_enable: + return 0; + case ZSTD_paramSwitch_e.ZSTD_ps_disable: + return 1; + default: + assert(0 != 0); + goto case ZSTD_paramSwitch_e.ZSTD_ps_auto; + case ZSTD_paramSwitch_e.ZSTD_ps_auto: + return + cctxParams->cParams.strategy == ZSTD_strategy.ZSTD_fast + && cctxParams->cParams.targetLength > 0 + ? 1 + : 0; + } + } + + /*! ZSTD_safecopyLiterals() : + * memcpy() function that won't read beyond more than WILDCOPY_OVERLENGTH bytes past ilimit_w. + * Only called when the sequence ends past ilimit_w, so it only needs to be optimized for single + * large copies. + */ + private static void ZSTD_safecopyLiterals(byte* op, byte* ip, byte* iend, byte* ilimit_w) + { + assert(iend > ilimit_w); + if (ip <= ilimit_w) + { + ZSTD_wildcopy(op, ip, (nint)(ilimit_w - ip), ZSTD_overlap_e.ZSTD_no_overlap); + op += ilimit_w - ip; + ip = ilimit_w; + } + + while (ip < iend) + *op++ = *ip++; + } + + /*! ZSTD_storeSeqOnly() : + * Store a sequence (litlen, litPtr, offBase and matchLength) into SeqStore_t. + * Literals themselves are not copied, but @litPtr is updated. + * @offBase : Users should employ macros REPCODE_TO_OFFBASE() and OFFSET_TO_OFFBASE(). + * @matchLength : must be >= MINMATCH + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_storeSeqOnly( + SeqStore_t* seqStorePtr, + nuint litLength, + uint offBase, + nuint matchLength + ) + { + assert( + (nuint)(seqStorePtr->sequences - seqStorePtr->sequencesStart) < seqStorePtr->maxNbSeq + ); + assert(litLength <= 1 << 17); + if (litLength > 0xFFFF) + { + assert(seqStorePtr->longLengthType == ZSTD_longLengthType_e.ZSTD_llt_none); + seqStorePtr->longLengthType = ZSTD_longLengthType_e.ZSTD_llt_literalLength; + seqStorePtr->longLengthPos = (uint)( + seqStorePtr->sequences - seqStorePtr->sequencesStart + ); + } + + seqStorePtr->sequences[0].litLength = (ushort)litLength; + seqStorePtr->sequences[0].offBase = offBase; + assert(matchLength <= 1 << 17); + assert(matchLength >= 3); + { + nuint mlBase = matchLength - 3; + if (mlBase > 0xFFFF) + { + assert(seqStorePtr->longLengthType == ZSTD_longLengthType_e.ZSTD_llt_none); + seqStorePtr->longLengthType = ZSTD_longLengthType_e.ZSTD_llt_matchLength; + seqStorePtr->longLengthPos = (uint)( + seqStorePtr->sequences - seqStorePtr->sequencesStart + ); + } + + seqStorePtr->sequences[0].mlBase = (ushort)mlBase; + } + + seqStorePtr->sequences++; + } + + /*! ZSTD_storeSeq() : + * Store a sequence (litlen, litPtr, offBase and matchLength) into SeqStore_t. + * @offBase : Users should employ macros REPCODE_TO_OFFBASE() and OFFSET_TO_OFFBASE(). + * @matchLength : must be >= MINMATCH + * Allowed to over-read literals up to litLimit. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_storeSeq( + SeqStore_t* seqStorePtr, + nuint litLength, + byte* literals, + byte* litLimit, + uint offBase, + nuint matchLength + ) + { + byte* litLimit_w = litLimit - 32; + byte* litEnd = literals + litLength; + assert( + (nuint)(seqStorePtr->sequences - seqStorePtr->sequencesStart) < seqStorePtr->maxNbSeq + ); + assert(seqStorePtr->maxNbLit <= 128 * (1 << 10)); + assert(seqStorePtr->lit + litLength <= seqStorePtr->litStart + seqStorePtr->maxNbLit); + assert(literals + litLength <= litLimit); + if (litEnd <= litLimit_w) + { + ZSTD_copy16(seqStorePtr->lit, literals); + if (litLength > 16) + { + ZSTD_wildcopy( + seqStorePtr->lit + 16, + literals + 16, + (nint)litLength - 16, + ZSTD_overlap_e.ZSTD_no_overlap + ); + } + } + else + { + ZSTD_safecopyLiterals(seqStorePtr->lit, literals, litEnd, litLimit_w); + } + + seqStorePtr->lit += litLength; + ZSTD_storeSeqOnly(seqStorePtr, litLength, offBase, matchLength); + } + + /* ZSTD_updateRep() : + * updates in-place @rep (array of repeat offsets) + * @offBase : sum-type, using numeric representation of ZSTD_storeSeq() + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_updateRep(uint* rep, uint offBase, uint ll0) + { + if (offBase > 3) + { + rep[2] = rep[1]; + rep[1] = rep[0]; + assert(offBase > 3); + rep[0] = offBase - 3; + } + else + { + assert(1 <= offBase && offBase <= 3); + uint repCode = offBase - 1 + ll0; + if (repCode > 0) + { + uint currentOffset = repCode == 3 ? rep[0] - 1 : rep[repCode]; + rep[2] = repCode >= 2 ? rep[1] : rep[2]; + rep[1] = rep[0]; + rep[0] = currentOffset; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static repcodes_s ZSTD_newRep(uint* rep, uint offBase, uint ll0) + { + repcodes_s newReps; + memcpy(&newReps, rep, (uint)sizeof(repcodes_s)); + ZSTD_updateRep(newReps.rep, offBase, ll0); + return newReps; + } + + /*-************************************* + * Match length counter + ***************************************/ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_count(byte* pIn, byte* pMatch, byte* pInLimit) + { + byte* pStart = pIn; + byte* pInLoopLimit = pInLimit - (sizeof(nuint) - 1); + if (pIn < pInLoopLimit) + { + { + nuint diff = MEM_readST(pMatch) ^ MEM_readST(pIn); + if (diff != 0) + return ZSTD_NbCommonBytes(diff); + } + + pIn += sizeof(nuint); + pMatch += sizeof(nuint); + while (pIn < pInLoopLimit) + { + nuint diff = MEM_readST(pMatch) ^ MEM_readST(pIn); + if (diff == 0) + { + pIn += sizeof(nuint); + pMatch += sizeof(nuint); + continue; + } + + pIn += ZSTD_NbCommonBytes(diff); + return (nuint)(pIn - pStart); + } + } + + if (MEM_64bits && pIn < pInLimit - 3 && MEM_read32(pMatch) == MEM_read32(pIn)) + { + pIn += 4; + pMatch += 4; + } + + if (pIn < pInLimit - 1 && MEM_read16(pMatch) == MEM_read16(pIn)) + { + pIn += 2; + pMatch += 2; + } + + if (pIn < pInLimit && *pMatch == *pIn) + pIn++; + return (nuint)(pIn - pStart); + } + + /** ZSTD_count_2segments() : + * can count match length with `ip` & `match` in 2 different segments. + * convention : on reaching mEnd, match count continue starting from iStart + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_count_2segments( + byte* ip, + byte* match, + byte* iEnd, + byte* mEnd, + byte* iStart + ) + { + byte* vEnd = ip + (mEnd - match) < iEnd ? ip + (mEnd - match) : iEnd; + nuint matchLength = ZSTD_count(ip, match, vEnd); + if (match + matchLength != mEnd) + return matchLength; + return matchLength + ZSTD_count(ip + matchLength, iStart, iEnd); + } + + private const uint prime3bytes = 506832829U; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_hash3(uint u, uint h, uint s) + { + assert(h <= 32); + return ((u << 32 - 24) * prime3bytes ^ s) >> (int)(32 - h); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash3Ptr(void* ptr, uint h) + { + return ZSTD_hash3(MEM_readLE32(ptr), h, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash3PtrS(void* ptr, uint h, uint s) + { + return ZSTD_hash3(MEM_readLE32(ptr), h, s); + } + + private const uint prime4bytes = 2654435761U; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_hash4(uint u, uint h, uint s) + { + assert(h <= 32); + return (u * prime4bytes ^ s) >> (int)(32 - h); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash4Ptr(void* ptr, uint h) + { + return ZSTD_hash4(MEM_readLE32(ptr), h, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash4PtrS(void* ptr, uint h, uint s) + { + return ZSTD_hash4(MEM_readLE32(ptr), h, s); + } + + private const ulong prime5bytes = 889523592379UL; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash5(ulong u, uint h, ulong s) + { + assert(h <= 64); + return (nuint)(((u << 64 - 40) * prime5bytes ^ s) >> (int)(64 - h)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash5Ptr(void* p, uint h) + { + return ZSTD_hash5(MEM_readLE64(p), h, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash5PtrS(void* p, uint h, ulong s) + { + return ZSTD_hash5(MEM_readLE64(p), h, s); + } + + private const ulong prime6bytes = 227718039650203UL; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash6(ulong u, uint h, ulong s) + { + assert(h <= 64); + return (nuint)(((u << 64 - 48) * prime6bytes ^ s) >> (int)(64 - h)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash6Ptr(void* p, uint h) + { + return ZSTD_hash6(MEM_readLE64(p), h, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash6PtrS(void* p, uint h, ulong s) + { + return ZSTD_hash6(MEM_readLE64(p), h, s); + } + + private const ulong prime7bytes = 58295818150454627UL; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash7(ulong u, uint h, ulong s) + { + assert(h <= 64); + return (nuint)(((u << 64 - 56) * prime7bytes ^ s) >> (int)(64 - h)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash7Ptr(void* p, uint h) + { + return ZSTD_hash7(MEM_readLE64(p), h, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash7PtrS(void* p, uint h, ulong s) + { + return ZSTD_hash7(MEM_readLE64(p), h, s); + } + + private const ulong prime8bytes = 0xCF1BBCDCB7A56463UL; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash8(ulong u, uint h, ulong s) + { + assert(h <= 64); + return (nuint)((u * prime8bytes ^ s) >> (int)(64 - h)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash8Ptr(void* p, uint h) + { + return ZSTD_hash8(MEM_readLE64(p), h, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hash8PtrS(void* p, uint h, ulong s) + { + return ZSTD_hash8(MEM_readLE64(p), h, s); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hashPtr(void* p, uint hBits, uint mls) + { + assert(hBits <= 32); + if (mls == 5) + return ZSTD_hash5Ptr(p, hBits); + if (mls == 6) + return ZSTD_hash6Ptr(p, hBits); + if (mls == 7) + return ZSTD_hash7Ptr(p, hBits); + if (mls == 8) + return ZSTD_hash8Ptr(p, hBits); + return ZSTD_hash4Ptr(p, hBits); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_hashPtrSalted(void* p, uint hBits, uint mls, ulong hashSalt) + { + assert(hBits <= 32); + if (mls == 5) + return ZSTD_hash5PtrS(p, hBits, hashSalt); + if (mls == 6) + return ZSTD_hash6PtrS(p, hBits, hashSalt); + if (mls == 7) + return ZSTD_hash7PtrS(p, hBits, hashSalt); + if (mls == 8) + return ZSTD_hash8PtrS(p, hBits, hashSalt); + return ZSTD_hash4PtrS(p, hBits, (uint)hashSalt); + } + + /** ZSTD_ipow() : + * Return base^exponent. + */ + private static ulong ZSTD_ipow(ulong @base, ulong exponent) + { + ulong power = 1; + while (exponent != 0) + { + if ((exponent & 1) != 0) + power *= @base; + exponent >>= 1; + @base *= @base; + } + + return power; + } + + /** ZSTD_rollingHash_append() : + * Add the buffer to the hash value. + */ + private static ulong ZSTD_rollingHash_append(ulong hash, void* buf, nuint size) + { + byte* istart = (byte*)buf; + nuint pos; + for (pos = 0; pos < size; ++pos) + { + hash *= prime8bytes; + hash += (ulong)(istart[pos] + 10); + } + + return hash; + } + + /** ZSTD_rollingHash_compute() : + * Compute the rolling hash value of the buffer. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong ZSTD_rollingHash_compute(void* buf, nuint size) + { + return ZSTD_rollingHash_append(0, buf, size); + } + + /** ZSTD_rollingHash_primePower() : + * Compute the primePower to be passed to ZSTD_rollingHash_rotate() for a hash + * over a window of length bytes. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong ZSTD_rollingHash_primePower(uint length) + { + return ZSTD_ipow(prime8bytes, length - 1); + } + + /** ZSTD_rollingHash_rotate() : + * Rotate the rolling hash by one byte. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong ZSTD_rollingHash_rotate( + ulong hash, + byte toRemove, + byte toAdd, + ulong primePower + ) + { + hash -= (ulong)(toRemove + 10) * primePower; + hash *= prime8bytes; + hash += (ulong)(toAdd + 10); + return hash; + } + + /** + * ZSTD_window_clear(): + * Clears the window containing the history by simply setting it to empty. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_window_clear(ZSTD_window_t* window) + { + nuint endT = (nuint)(window->nextSrc - window->@base); + uint end = (uint)endT; + window->lowLimit = end; + window->dictLimit = end; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_window_isEmpty(ZSTD_window_t window) + { + return window.dictLimit == 2 && window.lowLimit == 2 && window.nextSrc - window.@base == 2 + ? 1U + : 0U; + } + + /** + * ZSTD_window_hasExtDict(): + * Returns non-zero if the window has a non-empty extDict. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_window_hasExtDict(ZSTD_window_t window) + { + return window.lowLimit < window.dictLimit ? 1U : 0U; + } + + /** + * ZSTD_matchState_dictMode(): + * Inspects the provided matchState and figures out what dictMode should be + * passed to the compressor. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ZSTD_dictMode_e ZSTD_matchState_dictMode(ZSTD_MatchState_t* ms) + { + return ZSTD_window_hasExtDict(ms->window) != 0 ? ZSTD_dictMode_e.ZSTD_extDict + : ms->dictMatchState != null + ? ms->dictMatchState->dedicatedDictSearch != 0 + ? ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + : ZSTD_dictMode_e.ZSTD_dictMatchState + : ZSTD_dictMode_e.ZSTD_noDict; + } + + /** + * ZSTD_window_canOverflowCorrect(): + * Returns non-zero if the indices are large enough for overflow correction + * to work correctly without impacting compression ratio. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_window_canOverflowCorrect( + ZSTD_window_t window, + uint cycleLog, + uint maxDist, + uint loadedDictEnd, + void* src + ) + { + uint cycleSize = 1U << (int)cycleLog; + uint curr = (uint)((byte*)src - window.@base); + uint minIndexToOverflowCorrect = + cycleSize + (maxDist > cycleSize ? maxDist : cycleSize) + 2; + /* Adjust the min index to backoff the overflow correction frequency, + * so we don't waste too much CPU in overflow correction. If this + * computation overflows we don't really care, we just need to make + * sure it is at least minIndexToOverflowCorrect. + */ + uint adjustment = window.nbOverflowCorrections + 1; + uint adjustedIndex = + minIndexToOverflowCorrect * adjustment > minIndexToOverflowCorrect + ? minIndexToOverflowCorrect * adjustment + : minIndexToOverflowCorrect; + uint indexLargeEnough = curr > adjustedIndex ? 1U : 0U; + /* Only overflow correct early if the dictionary is invalidated already, + * so we don't hurt compression ratio. + */ + uint dictionaryInvalidated = curr > maxDist + loadedDictEnd ? 1U : 0U; + return indexLargeEnough != 0 && dictionaryInvalidated != 0 ? 1U : 0U; + } + + /** + * ZSTD_window_needOverflowCorrection(): + * Returns non-zero if the indices are getting too large and need overflow + * protection. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_window_needOverflowCorrection( + ZSTD_window_t window, + uint cycleLog, + uint maxDist, + uint loadedDictEnd, + void* src, + void* srcEnd + ) + { + uint curr = (uint)((byte*)srcEnd - window.@base); + return curr > (MEM_64bits ? 3500U * (1 << 20) : 2000U * (1 << 20)) ? 1U : 0U; + } + + /** + * ZSTD_window_correctOverflow(): + * Reduces the indices to protect from index overflow. + * Returns the correction made to the indices, which must be applied to every + * stored index. + * + * The least significant cycleLog bits of the indices must remain the same, + * which may be 0. Every index up to maxDist in the past must be valid. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_window_correctOverflow( + ZSTD_window_t* window, + uint cycleLog, + uint maxDist, + void* src + ) + { + /* preemptive overflow correction: + * 1. correction is large enough: + * lowLimit > (3<<29) ==> current > 3<<29 + 1< (3<<29 + 1< (3<<29) - (1< (3<<29) - (1<<30) (NOTE: chainLog <= 30) + * > 1<<29 + * + * 2. (ip+ZSTD_CHUNKSIZE_MAX - cctx->base) doesn't overflow: + * After correction, current is less than (1<base < 1<<32. + * 3. (cctx->lowLimit + 1< 3<<29 + 1<@base); + uint currentCycle = curr & cycleMask; + /* Ensure newCurrent - maxDist >= ZSTD_WINDOW_START_INDEX. */ + uint currentCycleCorrection = + currentCycle < 2 + ? cycleSize > 2 + ? cycleSize + : 2 + : 0; + uint newCurrent = + currentCycle + currentCycleCorrection + (maxDist > cycleSize ? maxDist : cycleSize); + uint correction = curr - newCurrent; + assert((maxDist & maxDist - 1) == 0); + assert((curr & cycleMask) == (newCurrent & cycleMask)); + assert(curr > newCurrent); + { + assert(correction > 1 << 28); + } + + window->@base += correction; + window->dictBase += correction; + if (window->lowLimit < correction + 2) + { + window->lowLimit = 2; + } + else + { + window->lowLimit -= correction; + } + + if (window->dictLimit < correction + 2) + { + window->dictLimit = 2; + } + else + { + window->dictLimit -= correction; + } + + assert(newCurrent >= maxDist); + assert(newCurrent - maxDist >= 2); + assert(window->lowLimit <= newCurrent); + assert(window->dictLimit <= newCurrent); + ++window->nbOverflowCorrections; + return correction; + } + + /** + * ZSTD_window_enforceMaxDist(): + * Updates lowLimit so that: + * (srcEnd - base) - lowLimit == maxDist + loadedDictEnd + * + * It ensures index is valid as long as index >= lowLimit. + * This must be called before a block compression call. + * + * loadedDictEnd is only defined if a dictionary is in use for current compression. + * As the name implies, loadedDictEnd represents the index at end of dictionary. + * The value lies within context's referential, it can be directly compared to blockEndIdx. + * + * If loadedDictEndPtr is NULL, no dictionary is in use, and we use loadedDictEnd == 0. + * If loadedDictEndPtr is not NULL, we set it to zero after updating lowLimit. + * This is because dictionaries are allowed to be referenced fully + * as long as the last byte of the dictionary is in the window. + * Once input has progressed beyond window size, dictionary cannot be referenced anymore. + * + * In normal dict mode, the dictionary lies between lowLimit and dictLimit. + * In dictMatchState mode, lowLimit and dictLimit are the same, + * and the dictionary is below them. + * forceWindow and dictMatchState are therefore incompatible. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_window_enforceMaxDist( + ZSTD_window_t* window, + void* blockEnd, + uint maxDist, + uint* loadedDictEndPtr, + ZSTD_MatchState_t** dictMatchStatePtr + ) + { + uint blockEndIdx = (uint)((byte*)blockEnd - window->@base); + uint loadedDictEnd = loadedDictEndPtr != null ? *loadedDictEndPtr : 0; + if (blockEndIdx > maxDist + loadedDictEnd) + { + uint newLowLimit = blockEndIdx - maxDist; + if (window->lowLimit < newLowLimit) + window->lowLimit = newLowLimit; + if (window->dictLimit < window->lowLimit) + { + window->dictLimit = window->lowLimit; + } + + if (loadedDictEndPtr != null) + *loadedDictEndPtr = 0; + if (dictMatchStatePtr != null) + *dictMatchStatePtr = null; + } + } + + /* Similar to ZSTD_window_enforceMaxDist(), + * but only invalidates dictionary + * when input progresses beyond window size. + * assumption : loadedDictEndPtr and dictMatchStatePtr are valid (non NULL) + * loadedDictEnd uses same referential as window->base + * maxDist is the window size */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_checkDictValidity( + ZSTD_window_t* window, + void* blockEnd, + uint maxDist, + uint* loadedDictEndPtr, + ZSTD_MatchState_t** dictMatchStatePtr + ) + { + assert(loadedDictEndPtr != null); + assert(dictMatchStatePtr != null); + { + uint blockEndIdx = (uint)((byte*)blockEnd - window->@base); + uint loadedDictEnd = *loadedDictEndPtr; + assert(blockEndIdx >= loadedDictEnd); + if (blockEndIdx > loadedDictEnd + maxDist || loadedDictEnd != window->dictLimit) + { + *loadedDictEndPtr = 0; + *dictMatchStatePtr = null; + } + } + } + +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_stringToByte_20_00 => new byte[] { 32, 0 }; + private static byte* stringToByte_20_00 => + (byte*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_stringToByte_20_00) + ); +#else + + private static readonly byte* stringToByte_20_00 = GetArrayPointer(new byte[] { 32, 0 }); +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_window_init(ZSTD_window_t* window) + { + *window = new ZSTD_window_t + { + @base = stringToByte_20_00, + dictBase = stringToByte_20_00, + dictLimit = 2, + lowLimit = 2, + nextSrc = stringToByte_20_00 + 2, + nbOverflowCorrections = 0, + }; + } + + /** + * ZSTD_window_update(): + * Updates the window by appending [src, src + srcSize) to the window. + * If it is not contiguous, the current prefix becomes the extDict, and we + * forget about the extDict. Handles overlap of the prefix and extDict. + * Returns non-zero if the segment is contiguous. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_window_update( + ZSTD_window_t* window, + void* src, + nuint srcSize, + int forceNonContiguous + ) + { + byte* ip = (byte*)src; + uint contiguous = 1; + if (srcSize == 0) + return contiguous; + assert(window->@base != null); + assert(window->dictBase != null); + if (src != window->nextSrc || forceNonContiguous != 0) + { + /* not contiguous */ + nuint distanceFromBase = (nuint)(window->nextSrc - window->@base); + window->lowLimit = window->dictLimit; + assert(distanceFromBase == (uint)distanceFromBase); + window->dictLimit = (uint)distanceFromBase; + window->dictBase = window->@base; + window->@base = ip - distanceFromBase; + if (window->dictLimit - window->lowLimit < 8) + window->lowLimit = window->dictLimit; + contiguous = 0; + } + + window->nextSrc = ip + srcSize; + if ( + ip + srcSize > window->dictBase + window->lowLimit + && ip < window->dictBase + window->dictLimit + ) + { + nuint highInputIdx = (nuint)(ip + srcSize - window->dictBase); + uint lowLimitMax = + highInputIdx > window->dictLimit ? window->dictLimit : (uint)highInputIdx; + assert(highInputIdx < 0xffffffff); + window->lowLimit = lowLimitMax; + } + + return contiguous; + } + + /** + * Returns the lowest allowed match index. It may either be in the ext-dict or the prefix. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_getLowestMatchIndex(ZSTD_MatchState_t* ms, uint curr, uint windowLog) + { + uint maxDistance = 1U << (int)windowLog; + uint lowestValid = ms->window.lowLimit; + uint withinWindow = curr - lowestValid > maxDistance ? curr - maxDistance : lowestValid; + uint isDictionary = ms->loadedDictEnd != 0 ? 1U : 0U; + /* When using a dictionary the entire dictionary is valid if a single byte of the dictionary + * is within the window. We invalidate the dictionary (and set loadedDictEnd to 0) when it isn't + * valid for the entire block. So this check is sufficient to find the lowest valid match index. + */ + uint matchLowest = isDictionary != 0 ? lowestValid : withinWindow; + return matchLowest; + } + + /** + * Returns the lowest allowed match index in the prefix. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_getLowestPrefixIndex(ZSTD_MatchState_t* ms, uint curr, uint windowLog) + { + uint maxDistance = 1U << (int)windowLog; + uint lowestValid = ms->window.dictLimit; + uint withinWindow = curr - lowestValid > maxDistance ? curr - maxDistance : lowestValid; + uint isDictionary = ms->loadedDictEnd != 0 ? 1U : 0U; + /* When computing the lowest prefix index we need to take the dictionary into account to handle + * the edge case where the dictionary and the source are contiguous in memory. + */ + uint matchLowest = isDictionary != 0 ? lowestValid : withinWindow; + return matchLowest; + } + + /* index_safety_check: + * intentional underflow : ensure repIndex isn't overlapping dict + prefix + * @return 1 if values are not overlapping, + * 0 otherwise */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_index_overlap_check(uint prefixLowestIndex, uint repIndex) + { + return prefixLowestIndex - 1 - repIndex >= 3 ? 1 : 0; + } + + /* Helper function for ZSTD_fillHashTable and ZSTD_fillDoubleHashTable. + * Unpacks hashAndTag into (hash, tag), then packs (index, tag) into hashTable[hash]. */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_writeTaggedIndex(uint* hashTable, nuint hashAndTag, uint index) + { + nuint hash = hashAndTag >> 8; + uint tag = (uint)(hashAndTag & (1U << 8) - 1); + assert(index >> 32 - 8 == 0); + hashTable[hash] = index << 8 | tag; + } + + /* Helper function for short cache matchfinders. + * Unpacks tag1 and tag2 from lower bits of packedTag1 and packedTag2, then checks if the tags match. */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_comparePackedTags(nuint packedTag1, nuint packedTag2) + { + uint tag1 = (uint)(packedTag1 & (1U << 8) - 1); + uint tag2 = (uint)(packedTag2 & (1U << 8) - 1); + return tag1 == tag2 ? 1 : 0; + } + + /* Returns 1 if an external sequence producer is registered, otherwise returns 0. */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_hasExtSeqProd(ZSTD_CCtx_params_s* @params) + { + return @params->extSeqProdFunc != null ? 1 : 0; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressLiterals.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressLiterals.cs new file mode 100644 index 00000000..a4f92ab3 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressLiterals.cs @@ -0,0 +1,314 @@ +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /* ************************************************************** + * Literals compression - special cases + ****************************************************************/ + private static nuint ZSTD_noCompressLiterals( + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + byte* ostart = (byte*)dst; + uint flSize = (uint)(1 + (srcSize > 31 ? 1 : 0) + (srcSize > 4095 ? 1 : 0)); + if (srcSize + flSize > dstCapacity) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + switch (flSize) + { + case 1: + ostart[0] = (byte)((uint)SymbolEncodingType_e.set_basic + (srcSize << 3)); + break; + case 2: + MEM_writeLE16( + ostart, + (ushort)((uint)SymbolEncodingType_e.set_basic + (1 << 2) + (srcSize << 4)) + ); + break; + case 3: + MEM_writeLE32( + ostart, + (uint)((uint)SymbolEncodingType_e.set_basic + (3 << 2) + (srcSize << 4)) + ); + break; + default: + assert(0 != 0); + break; + } + + memcpy(ostart + flSize, src, (uint)srcSize); + return srcSize + flSize; + } + + private static int allBytesIdentical(void* src, nuint srcSize) + { + assert(srcSize >= 1); + assert(src != null); + { + byte b = ((byte*)src)[0]; + nuint p; + for (p = 1; p < srcSize; p++) + { + if (((byte*)src)[p] != b) + return 0; + } + + return 1; + } + } + + /* ZSTD_compressRleLiteralsBlock() : + * Conditions : + * - All bytes in @src are identical + * - dstCapacity >= 4 */ + private static nuint ZSTD_compressRleLiteralsBlock( + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + byte* ostart = (byte*)dst; + uint flSize = (uint)(1 + (srcSize > 31 ? 1 : 0) + (srcSize > 4095 ? 1 : 0)); + assert(dstCapacity >= 4); + assert(allBytesIdentical(src, srcSize) != 0); + switch (flSize) + { + case 1: + ostart[0] = (byte)((uint)SymbolEncodingType_e.set_rle + (srcSize << 3)); + break; + case 2: + MEM_writeLE16( + ostart, + (ushort)((uint)SymbolEncodingType_e.set_rle + (1 << 2) + (srcSize << 4)) + ); + break; + case 3: + MEM_writeLE32( + ostart, + (uint)((uint)SymbolEncodingType_e.set_rle + (3 << 2) + (srcSize << 4)) + ); + break; + default: + assert(0 != 0); + break; + } + + ostart[flSize] = *(byte*)src; + return flSize + 1; + } + + /* ZSTD_minLiteralsToCompress() : + * returns minimal amount of literals + * for literal compression to even be attempted. + * Minimum is made tighter as compression strategy increases. + */ + private static nuint ZSTD_minLiteralsToCompress(ZSTD_strategy strategy, HUF_repeat huf_repeat) + { + assert((int)strategy >= 0); + assert((int)strategy <= 9); + { + int shift = 9 - (int)strategy < 3 ? 9 - (int)strategy : 3; + nuint mintc = huf_repeat == HUF_repeat.HUF_repeat_valid ? 6 : (nuint)8 << shift; + return mintc; + } + } + + /* ZSTD_compressLiterals(): + * @entropyWorkspace: must be aligned on 4-bytes boundaries + * @entropyWorkspaceSize : must be >= HUF_WORKSPACE_SIZE + * @suspectUncompressible: sampling checks, to potentially skip huffman coding + */ + private static nuint ZSTD_compressLiterals( + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + void* entropyWorkspace, + nuint entropyWorkspaceSize, + ZSTD_hufCTables_t* prevHuf, + ZSTD_hufCTables_t* nextHuf, + ZSTD_strategy strategy, + int disableLiteralCompression, + int suspectUncompressible, + int bmi2 + ) + { + nuint lhSize = (nuint)( + 3 + (srcSize >= 1 * (1 << 10) ? 1 : 0) + (srcSize >= 16 * (1 << 10) ? 1 : 0) + ); + byte* ostart = (byte*)dst; + uint singleStream = srcSize < 256 ? 1U : 0U; + SymbolEncodingType_e hType = SymbolEncodingType_e.set_compressed; + nuint cLitSize; + memcpy(nextHuf, prevHuf, (uint)sizeof(ZSTD_hufCTables_t)); + if (disableLiteralCompression != 0) + return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); + if (srcSize < ZSTD_minLiteralsToCompress(strategy, prevHuf->repeatMode)) + return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); + if (dstCapacity < lhSize + 1) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + { + HUF_repeat repeat = prevHuf->repeatMode; + int flags = + 0 + | (bmi2 != 0 ? (int)HUF_flags_e.HUF_flags_bmi2 : 0) + | ( + strategy < ZSTD_strategy.ZSTD_lazy && srcSize <= 1024 + ? (int)HUF_flags_e.HUF_flags_preferRepeat + : 0 + ) + | ( + strategy >= ZSTD_strategy.ZSTD_btultra + ? (int)HUF_flags_e.HUF_flags_optimalDepth + : 0 + ) + | ( + suspectUncompressible != 0 + ? (int)HUF_flags_e.HUF_flags_suspectUncompressible + : 0 + ); + void* huf_compress; + if (repeat == HUF_repeat.HUF_repeat_valid && lhSize == 3) + singleStream = 1; + huf_compress = + singleStream != 0 + ? (delegate* managed< + void*, + nuint, + void*, + nuint, + uint, + uint, + void*, + nuint, + nuint*, + HUF_repeat*, + int, + nuint>)(&HUF_compress1X_repeat) + : (delegate* managed< + void*, + nuint, + void*, + nuint, + uint, + uint, + void*, + nuint, + nuint*, + HUF_repeat*, + int, + nuint>)(&HUF_compress4X_repeat); + cLitSize = ( + (delegate* managed< + void*, + nuint, + void*, + nuint, + uint, + uint, + void*, + nuint, + nuint*, + HUF_repeat*, + int, + nuint>)huf_compress + )( + ostart + lhSize, + dstCapacity - lhSize, + src, + srcSize, + 255, + 11, + entropyWorkspace, + entropyWorkspaceSize, + &nextHuf->CTable.e0, + &repeat, + flags + ); + if (repeat != HUF_repeat.HUF_repeat_none) + { + hType = SymbolEncodingType_e.set_repeat; + } + } + + { + nuint minGain = ZSTD_minGain(srcSize, strategy); + if (cLitSize == 0 || cLitSize >= srcSize - minGain || ERR_isError(cLitSize)) + { + memcpy(nextHuf, prevHuf, (uint)sizeof(ZSTD_hufCTables_t)); + return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); + } + } + + if (cLitSize == 1) + { + if (srcSize >= 8 || allBytesIdentical(src, srcSize) != 0) + { + memcpy(nextHuf, prevHuf, (uint)sizeof(ZSTD_hufCTables_t)); + return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); + } + } + + if (hType == SymbolEncodingType_e.set_compressed) + { + nextHuf->repeatMode = HUF_repeat.HUF_repeat_check; + } + + switch (lhSize) + { + case 3: +#if DEBUG + if (singleStream == 0) + assert(srcSize >= 6); + +#endif + { + uint lhc = + (uint)hType + + ((singleStream == 0 ? 1U : 0U) << 2) + + ((uint)srcSize << 4) + + ((uint)cLitSize << 14); + MEM_writeLE24(ostart, lhc); + break; + } + + case 4: + assert(srcSize >= 6); + + { + uint lhc = + (uint)(hType + (2 << 2)) + ((uint)srcSize << 4) + ((uint)cLitSize << 18); + MEM_writeLE32(ostart, lhc); + break; + } + + case 5: + assert(srcSize >= 6); + + { + uint lhc = + (uint)(hType + (3 << 2)) + ((uint)srcSize << 4) + ((uint)cLitSize << 22); + MEM_writeLE32(ostart, lhc); + ostart[4] = (byte)(cLitSize >> 10); + break; + } + + default: + assert(0 != 0); + break; + } + + return lhSize + cLitSize; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressSequences.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressSequences.cs new file mode 100644 index 00000000..e0fd5eee --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressSequences.cs @@ -0,0 +1,1211 @@ +using System; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_kInverseProbabilityLog256 => + new uint[256] + { + 0, + 2048, + 1792, + 1642, + 1536, + 1453, + 1386, + 1329, + 1280, + 1236, + 1197, + 1162, + 1130, + 1100, + 1073, + 1047, + 1024, + 1001, + 980, + 960, + 941, + 923, + 906, + 889, + 874, + 859, + 844, + 830, + 817, + 804, + 791, + 779, + 768, + 756, + 745, + 734, + 724, + 714, + 704, + 694, + 685, + 676, + 667, + 658, + 650, + 642, + 633, + 626, + 618, + 610, + 603, + 595, + 588, + 581, + 574, + 567, + 561, + 554, + 548, + 542, + 535, + 529, + 523, + 517, + 512, + 506, + 500, + 495, + 489, + 484, + 478, + 473, + 468, + 463, + 458, + 453, + 448, + 443, + 438, + 434, + 429, + 424, + 420, + 415, + 411, + 407, + 402, + 398, + 394, + 390, + 386, + 382, + 377, + 373, + 370, + 366, + 362, + 358, + 354, + 350, + 347, + 343, + 339, + 336, + 332, + 329, + 325, + 322, + 318, + 315, + 311, + 308, + 305, + 302, + 298, + 295, + 292, + 289, + 286, + 282, + 279, + 276, + 273, + 270, + 267, + 264, + 261, + 258, + 256, + 253, + 250, + 247, + 244, + 241, + 239, + 236, + 233, + 230, + 228, + 225, + 222, + 220, + 217, + 215, + 212, + 209, + 207, + 204, + 202, + 199, + 197, + 194, + 192, + 190, + 187, + 185, + 182, + 180, + 178, + 175, + 173, + 171, + 168, + 166, + 164, + 162, + 159, + 157, + 155, + 153, + 151, + 149, + 146, + 144, + 142, + 140, + 138, + 136, + 134, + 132, + 130, + 128, + 126, + 123, + 121, + 119, + 117, + 115, + 114, + 112, + 110, + 108, + 106, + 104, + 102, + 100, + 98, + 96, + 94, + 93, + 91, + 89, + 87, + 85, + 83, + 82, + 80, + 78, + 76, + 74, + 73, + 71, + 69, + 67, + 66, + 64, + 62, + 61, + 59, + 57, + 55, + 54, + 52, + 50, + 49, + 47, + 46, + 44, + 42, + 41, + 39, + 37, + 36, + 34, + 33, + 31, + 30, + 28, + 26, + 25, + 23, + 22, + 20, + 19, + 17, + 16, + 14, + 13, + 11, + 10, + 8, + 7, + 5, + 4, + 2, + 1, + }; + private static uint* kInverseProbabilityLog256 => + (uint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_kInverseProbabilityLog256) + ); +#else + + private static readonly uint* kInverseProbabilityLog256 = GetArrayPointer( + new uint[256] + { + 0, + 2048, + 1792, + 1642, + 1536, + 1453, + 1386, + 1329, + 1280, + 1236, + 1197, + 1162, + 1130, + 1100, + 1073, + 1047, + 1024, + 1001, + 980, + 960, + 941, + 923, + 906, + 889, + 874, + 859, + 844, + 830, + 817, + 804, + 791, + 779, + 768, + 756, + 745, + 734, + 724, + 714, + 704, + 694, + 685, + 676, + 667, + 658, + 650, + 642, + 633, + 626, + 618, + 610, + 603, + 595, + 588, + 581, + 574, + 567, + 561, + 554, + 548, + 542, + 535, + 529, + 523, + 517, + 512, + 506, + 500, + 495, + 489, + 484, + 478, + 473, + 468, + 463, + 458, + 453, + 448, + 443, + 438, + 434, + 429, + 424, + 420, + 415, + 411, + 407, + 402, + 398, + 394, + 390, + 386, + 382, + 377, + 373, + 370, + 366, + 362, + 358, + 354, + 350, + 347, + 343, + 339, + 336, + 332, + 329, + 325, + 322, + 318, + 315, + 311, + 308, + 305, + 302, + 298, + 295, + 292, + 289, + 286, + 282, + 279, + 276, + 273, + 270, + 267, + 264, + 261, + 258, + 256, + 253, + 250, + 247, + 244, + 241, + 239, + 236, + 233, + 230, + 228, + 225, + 222, + 220, + 217, + 215, + 212, + 209, + 207, + 204, + 202, + 199, + 197, + 194, + 192, + 190, + 187, + 185, + 182, + 180, + 178, + 175, + 173, + 171, + 168, + 166, + 164, + 162, + 159, + 157, + 155, + 153, + 151, + 149, + 146, + 144, + 142, + 140, + 138, + 136, + 134, + 132, + 130, + 128, + 126, + 123, + 121, + 119, + 117, + 115, + 114, + 112, + 110, + 108, + 106, + 104, + 102, + 100, + 98, + 96, + 94, + 93, + 91, + 89, + 87, + 85, + 83, + 82, + 80, + 78, + 76, + 74, + 73, + 71, + 69, + 67, + 66, + 64, + 62, + 61, + 59, + 57, + 55, + 54, + 52, + 50, + 49, + 47, + 46, + 44, + 42, + 41, + 39, + 37, + 36, + 34, + 33, + 31, + 30, + 28, + 26, + 25, + 23, + 22, + 20, + 19, + 17, + 16, + 14, + 13, + 11, + 10, + 8, + 7, + 5, + 4, + 2, + 1, + } + ); +#endif + + private static uint ZSTD_getFSEMaxSymbolValue(uint* ctable) + { + void* ptr = ctable; + ushort* u16ptr = (ushort*)ptr; + uint maxSymbolValue = MEM_read16(u16ptr + 1); + return maxSymbolValue; + } + + /** + * Returns true if we should use ncount=-1 else we should + * use ncount=1 for low probability symbols instead. + */ + private static uint ZSTD_useLowProbCount(nuint nbSeq) + { + return nbSeq >= 2048 ? 1U : 0U; + } + + /** + * Returns the cost in bytes of encoding the normalized count header. + * Returns an error if any of the helper functions return an error. + */ + private static nuint ZSTD_NCountCost(uint* count, uint max, nuint nbSeq, uint FSELog) + { + byte* wksp = stackalloc byte[512]; + short* norm = stackalloc short[53]; + uint tableLog = FSE_optimalTableLog(FSELog, nbSeq, max); + { + nuint err_code = FSE_normalizeCount( + norm, + tableLog, + count, + nbSeq, + max, + ZSTD_useLowProbCount(nbSeq) + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return FSE_writeNCount(wksp, sizeof(byte) * 512, norm, max, tableLog); + } + + /** + * Returns the cost in bits of encoding the distribution described by count + * using the entropy bound. + */ + private static nuint ZSTD_entropyCost(uint* count, uint max, nuint total) + { + uint cost = 0; + uint s; + assert(total > 0); + for (s = 0; s <= max; ++s) + { + uint norm = (uint)(256 * count[s] / total); + if (count[s] != 0 && norm == 0) + norm = 1; + assert(count[s] < total); + cost += count[s] * kInverseProbabilityLog256[norm]; + } + + return cost >> 8; + } + + /** + * Returns the cost in bits of encoding the distribution in count using ctable. + * Returns an error if ctable cannot represent all the symbols in count. + */ + private static nuint ZSTD_fseBitCost(uint* ctable, uint* count, uint max) + { + const uint kAccuracyLog = 8; + nuint cost = 0; + uint s; + FSE_CState_t cstate; + FSE_initCState(&cstate, ctable); + if (ZSTD_getFSEMaxSymbolValue(ctable) < max) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + + for (s = 0; s <= max; ++s) + { + uint tableLog = cstate.stateLog; + uint badCost = tableLog + 1 << (int)kAccuracyLog; + uint bitCost = FSE_bitCost(cstate.symbolTT, tableLog, s, kAccuracyLog); + if (count[s] == 0) + continue; + if (bitCost >= badCost) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + + cost += (nuint)count[s] * bitCost; + } + + return cost >> (int)kAccuracyLog; + } + + /** + * Returns the cost in bits of encoding the distribution in count using the + * table described by norm. The max symbol support by norm is assumed >= max. + * norm must be valid for every symbol with non-zero probability in count. + */ + private static nuint ZSTD_crossEntropyCost(short* norm, uint accuracyLog, uint* count, uint max) + { + uint shift = 8 - accuracyLog; + nuint cost = 0; + uint s; + assert(accuracyLog <= 8); + for (s = 0; s <= max; ++s) + { + uint normAcc = norm[s] != -1 ? (uint)norm[s] : 1; + uint norm256 = normAcc << (int)shift; + assert(norm256 > 0); + assert(norm256 < 256); + cost += count[s] * kInverseProbabilityLog256[norm256]; + } + + return cost >> 8; + } + + private static SymbolEncodingType_e ZSTD_selectEncodingType( + FSE_repeat* repeatMode, + uint* count, + uint max, + nuint mostFrequent, + nuint nbSeq, + uint FSELog, + uint* prevCTable, + short* defaultNorm, + uint defaultNormLog, + ZSTD_DefaultPolicy_e isDefaultAllowed, + ZSTD_strategy strategy + ) + { + if (mostFrequent == nbSeq) + { + *repeatMode = FSE_repeat.FSE_repeat_none; + if (isDefaultAllowed != default && nbSeq <= 2) + { + return SymbolEncodingType_e.set_basic; + } + + return SymbolEncodingType_e.set_rle; + } + + if (strategy < ZSTD_strategy.ZSTD_lazy) + { + if (isDefaultAllowed != default) + { + const nuint staticFse_nbSeq_max = 1000; + nuint mult = (nuint)(10 - strategy); + const nuint baseLog = 3; + /* 28-36 for offset, 56-72 for lengths */ + nuint dynamicFse_nbSeq_min = + ((nuint)1 << (int)defaultNormLog) * mult >> (int)baseLog; + assert(defaultNormLog >= 5 && defaultNormLog <= 6); + assert(mult <= 9 && mult >= 7); + if (*repeatMode == FSE_repeat.FSE_repeat_valid && nbSeq < staticFse_nbSeq_max) + { + return SymbolEncodingType_e.set_repeat; + } + + if ( + nbSeq < dynamicFse_nbSeq_min + || mostFrequent < nbSeq >> (int)(defaultNormLog - 1) + ) + { + *repeatMode = FSE_repeat.FSE_repeat_none; + return SymbolEncodingType_e.set_basic; + } + } + } + else + { + nuint basicCost = + isDefaultAllowed != default + ? ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, count, max) + : unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + nuint repeatCost = + *repeatMode != FSE_repeat.FSE_repeat_none + ? ZSTD_fseBitCost(prevCTable, count, max) + : unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + nuint NCountCost = ZSTD_NCountCost(count, max, nbSeq, FSELog); + nuint compressedCost = (NCountCost << 3) + ZSTD_entropyCost(count, max, nbSeq); +#if DEBUG + if (isDefaultAllowed != default) + { + assert(!ERR_isError(basicCost)); + assert(!(*repeatMode == FSE_repeat.FSE_repeat_valid && ERR_isError(repeatCost))); + } +#endif + + assert(!ERR_isError(NCountCost)); + assert(compressedCost < unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_maxCode))); + if (basicCost <= repeatCost && basicCost <= compressedCost) + { + assert(isDefaultAllowed != default); + *repeatMode = FSE_repeat.FSE_repeat_none; + return SymbolEncodingType_e.set_basic; + } + + if (repeatCost <= compressedCost) + { + assert(!ERR_isError(repeatCost)); + return SymbolEncodingType_e.set_repeat; + } + + assert(compressedCost < basicCost && compressedCost < repeatCost); + } + + *repeatMode = FSE_repeat.FSE_repeat_check; + return SymbolEncodingType_e.set_compressed; + } + + private static nuint ZSTD_buildCTable( + void* dst, + nuint dstCapacity, + uint* nextCTable, + uint FSELog, + SymbolEncodingType_e type, + uint* count, + uint max, + byte* codeTable, + nuint nbSeq, + short* defaultNorm, + uint defaultNormLog, + uint defaultMax, + uint* prevCTable, + nuint prevCTableSize, + void* entropyWorkspace, + nuint entropyWorkspaceSize + ) + { + byte* op = (byte*)dst; + byte* oend = op + dstCapacity; + switch (type) + { + case SymbolEncodingType_e.set_rle: + { + nuint err_code = FSE_buildCTable_rle(nextCTable, (byte)max); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (dstCapacity == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + *op = codeTable[0]; + return 1; + case SymbolEncodingType_e.set_repeat: + memcpy(nextCTable, prevCTable, (uint)prevCTableSize); + return 0; + case SymbolEncodingType_e.set_basic: + { + /* note : could be pre-calculated */ + nuint err_code = FSE_buildCTable_wksp( + nextCTable, + defaultNorm, + defaultMax, + defaultNormLog, + entropyWorkspace, + entropyWorkspaceSize + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + case SymbolEncodingType_e.set_compressed: + { + ZSTD_BuildCTableWksp* wksp = (ZSTD_BuildCTableWksp*)entropyWorkspace; + nuint nbSeq_1 = nbSeq; + uint tableLog = FSE_optimalTableLog(FSELog, nbSeq, max); + if (count[codeTable[nbSeq - 1]] > 1) + { + count[codeTable[nbSeq - 1]]--; + nbSeq_1--; + } + + assert(nbSeq_1 > 1); + assert(entropyWorkspaceSize >= (nuint)sizeof(ZSTD_BuildCTableWksp)); + { + nuint err_code = FSE_normalizeCount( + wksp->norm, + tableLog, + count, + nbSeq_1, + max, + ZSTD_useLowProbCount(nbSeq_1) + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(oend >= op); + { + /* overflow protected */ + nuint NCountSize = FSE_writeNCount( + op, + (nuint)(oend - op), + wksp->norm, + max, + tableLog + ); + { + nuint err_code = NCountSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = FSE_buildCTable_wksp( + nextCTable, + wksp->norm, + max, + tableLog, + wksp->wksp, + sizeof(uint) * 285 + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return NCountSize; + } + } + + default: + assert(0 != 0); + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + } + + private static nuint ZSTD_encodeSequences_body( + void* dst, + nuint dstCapacity, + uint* CTable_MatchLength, + byte* mlCodeTable, + uint* CTable_OffsetBits, + byte* ofCodeTable, + uint* CTable_LitLength, + byte* llCodeTable, + SeqDef_s* sequences, + nuint nbSeq, + int longOffsets + ) + { + BIT_CStream_t blockStream; + System.Runtime.CompilerServices.Unsafe.SkipInit(out blockStream); + FSE_CState_t stateMatchLength; + System.Runtime.CompilerServices.Unsafe.SkipInit(out stateMatchLength); + FSE_CState_t stateOffsetBits; + System.Runtime.CompilerServices.Unsafe.SkipInit(out stateOffsetBits); + FSE_CState_t stateLitLength; + System.Runtime.CompilerServices.Unsafe.SkipInit(out stateLitLength); + if (ERR_isError(BIT_initCStream(ref blockStream, dst, dstCapacity))) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + nuint blockStream_bitContainer = blockStream.bitContainer; + uint blockStream_bitPos = blockStream.bitPos; + sbyte* blockStream_ptr = blockStream.ptr; + sbyte* blockStream_endPtr = blockStream.endPtr; + FSE_initCState2(ref stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq - 1]); + FSE_initCState2(ref stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq - 1]); + FSE_initCState2(ref stateLitLength, CTable_LitLength, llCodeTable[nbSeq - 1]); + BIT_addBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + sequences[nbSeq - 1].litLength, + LL_bits[llCodeTable[nbSeq - 1]] + ); + if (MEM_32bits) + BIT_flushBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr + ); + BIT_addBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + sequences[nbSeq - 1].mlBase, + ML_bits[mlCodeTable[nbSeq - 1]] + ); + if (MEM_32bits) + BIT_flushBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr + ); + if (longOffsets != 0) + { + uint ofBits = ofCodeTable[nbSeq - 1]; + uint extraBits = + ofBits + - ( + ofBits < (uint)(MEM_32bits ? 25 : 57) - 1 + ? ofBits + : (uint)(MEM_32bits ? 25 : 57) - 1 + ); + if (extraBits != 0) + { + BIT_addBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + sequences[nbSeq - 1].offBase, + extraBits + ); + BIT_flushBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr + ); + } + + BIT_addBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + sequences[nbSeq - 1].offBase >> (int)extraBits, + ofBits - extraBits + ); + } + else + { + BIT_addBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + sequences[nbSeq - 1].offBase, + ofCodeTable[nbSeq - 1] + ); + } + + BIT_flushBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr + ); + { + nuint n; + for (n = nbSeq - 2; n < nbSeq; n--) + { + byte llCode = llCodeTable[n]; + byte ofCode = ofCodeTable[n]; + byte mlCode = mlCodeTable[n]; + uint llBits = LL_bits[llCode]; + uint ofBits = ofCode; + uint mlBits = ML_bits[mlCode]; + FSE_encodeSymbol( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref stateOffsetBits, + ofCode + ); + FSE_encodeSymbol( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref stateMatchLength, + mlCode + ); + if (MEM_32bits) + BIT_flushBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr + ); + FSE_encodeSymbol( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref stateLitLength, + llCode + ); + if (MEM_32bits || ofBits + mlBits + llBits >= 64 - 7 - (9 + 9 + 8)) + BIT_flushBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr + ); + BIT_addBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + sequences[n].litLength, + llBits + ); + if (MEM_32bits && llBits + mlBits > 24) + BIT_flushBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr + ); + BIT_addBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + sequences[n].mlBase, + mlBits + ); + if (MEM_32bits || ofBits + mlBits + llBits > 56) + BIT_flushBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr + ); + if (longOffsets != 0) + { + uint extraBits = + ofBits + - ( + ofBits < (uint)(MEM_32bits ? 25 : 57) - 1 + ? ofBits + : (uint)(MEM_32bits ? 25 : 57) - 1 + ); + if (extraBits != 0) + { + BIT_addBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + sequences[n].offBase, + extraBits + ); + BIT_flushBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr + ); + } + + BIT_addBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + sequences[n].offBase >> (int)extraBits, + ofBits - extraBits + ); + } + else + { + BIT_addBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + sequences[n].offBase, + ofBits + ); + } + + BIT_flushBits( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr + ); + } + } + + FSE_flushCState( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr, + ref stateMatchLength + ); + FSE_flushCState( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr, + ref stateOffsetBits + ); + FSE_flushCState( + ref blockStream_bitContainer, + ref blockStream_bitPos, + ref blockStream_ptr, + blockStream_endPtr, + ref stateLitLength + ); + { + nuint streamSize = BIT_closeCStream( + ref blockStream_bitContainer, + ref blockStream_bitPos, + blockStream_ptr, + blockStream_endPtr, + blockStream.startPtr + ); + if (streamSize == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + return streamSize; + } + } + + private static nuint ZSTD_encodeSequences_default( + void* dst, + nuint dstCapacity, + uint* CTable_MatchLength, + byte* mlCodeTable, + uint* CTable_OffsetBits, + byte* ofCodeTable, + uint* CTable_LitLength, + byte* llCodeTable, + SeqDef_s* sequences, + nuint nbSeq, + int longOffsets + ) + { + return ZSTD_encodeSequences_body( + dst, + dstCapacity, + CTable_MatchLength, + mlCodeTable, + CTable_OffsetBits, + ofCodeTable, + CTable_LitLength, + llCodeTable, + sequences, + nbSeq, + longOffsets + ); + } + + private static nuint ZSTD_encodeSequences( + void* dst, + nuint dstCapacity, + uint* CTable_MatchLength, + byte* mlCodeTable, + uint* CTable_OffsetBits, + byte* ofCodeTable, + uint* CTable_LitLength, + byte* llCodeTable, + SeqDef_s* sequences, + nuint nbSeq, + int longOffsets, + int bmi2 + ) + { + return ZSTD_encodeSequences_default( + dst, + dstCapacity, + CTable_MatchLength, + mlCodeTable, + CTable_OffsetBits, + ofCodeTable, + CTable_LitLength, + llCodeTable, + sequences, + nbSeq, + longOffsets + ); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressSuperblock.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressSuperblock.cs new file mode 100644 index 00000000..8a79453b --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCompressSuperblock.cs @@ -0,0 +1,976 @@ +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /** ZSTD_compressSubBlock_literal() : + * Compresses literals section for a sub-block. + * When we have to write the Huffman table we will sometimes choose a header + * size larger than necessary. This is because we have to pick the header size + * before we know the table size + compressed size, so we have a bound on the + * table size. If we guessed incorrectly, we fall back to uncompressed literals. + * + * We write the header when writeEntropy=1 and set entropyWritten=1 when we succeeded + * in writing the header, otherwise it is set to 0. + * + * hufMetadata->hType has literals block type info. + * If it is set_basic, all sub-blocks literals section will be Raw_Literals_Block. + * If it is set_rle, all sub-blocks literals section will be RLE_Literals_Block. + * If it is set_compressed, first sub-block's literals section will be Compressed_Literals_Block + * If it is set_compressed, first sub-block's literals section will be Treeless_Literals_Block + * and the following sub-blocks' literals sections will be Treeless_Literals_Block. + * @return : compressed size of literals section of a sub-block + * Or 0 if unable to compress. + * Or error code */ + private static nuint ZSTD_compressSubBlock_literal( + nuint* hufTable, + ZSTD_hufCTablesMetadata_t* hufMetadata, + byte* literals, + nuint litSize, + void* dst, + nuint dstSize, + int bmi2, + int writeEntropy, + int* entropyWritten + ) + { + nuint header = (nuint)(writeEntropy != 0 ? 200 : 0); + nuint lhSize = (nuint)( + 3 + + (litSize >= 1 * (1 << 10) - header ? 1 : 0) + + (litSize >= 16 * (1 << 10) - header ? 1 : 0) + ); + byte* ostart = (byte*)dst; + byte* oend = ostart + dstSize; + byte* op = ostart + lhSize; + uint singleStream = lhSize == 3 ? 1U : 0U; + SymbolEncodingType_e hType = + writeEntropy != 0 ? hufMetadata->hType : SymbolEncodingType_e.set_repeat; + nuint cLitSize = 0; + *entropyWritten = 0; + if (litSize == 0 || hufMetadata->hType == SymbolEncodingType_e.set_basic) + { + return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize); + } + else if (hufMetadata->hType == SymbolEncodingType_e.set_rle) + { + return ZSTD_compressRleLiteralsBlock(dst, dstSize, literals, litSize); + } + + assert(litSize > 0); + assert( + hufMetadata->hType == SymbolEncodingType_e.set_compressed + || hufMetadata->hType == SymbolEncodingType_e.set_repeat + ); + if (writeEntropy != 0 && hufMetadata->hType == SymbolEncodingType_e.set_compressed) + { + memcpy(op, hufMetadata->hufDesBuffer, (uint)hufMetadata->hufDesSize); + op += hufMetadata->hufDesSize; + cLitSize += hufMetadata->hufDesSize; + } + + { + int flags = bmi2 != 0 ? (int)HUF_flags_e.HUF_flags_bmi2 : 0; + nuint cSize = + singleStream != 0 + ? HUF_compress1X_usingCTable( + op, + (nuint)(oend - op), + literals, + litSize, + hufTable, + flags + ) + : HUF_compress4X_usingCTable( + op, + (nuint)(oend - op), + literals, + litSize, + hufTable, + flags + ); + op += cSize; + cLitSize += cSize; + if (cSize == 0 || ERR_isError(cSize)) + { + return 0; + } + + if (writeEntropy == 0 && cLitSize >= litSize) + { + return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize); + } + + if ( + lhSize + < (nuint)( + 3 + (cLitSize >= 1 * (1 << 10) ? 1 : 0) + (cLitSize >= 16 * (1 << 10) ? 1 : 0) + ) + ) + { + assert(cLitSize > litSize); + return ZSTD_noCompressLiterals(dst, dstSize, literals, litSize); + } + } + + switch (lhSize) + { + case 3: + { + uint lhc = + (uint)hType + + ((singleStream == 0 ? 1U : 0U) << 2) + + ((uint)litSize << 4) + + ((uint)cLitSize << 14); + MEM_writeLE24(ostart, lhc); + break; + } + + case 4: + { + uint lhc = (uint)(hType + (2 << 2)) + ((uint)litSize << 4) + ((uint)cLitSize << 18); + MEM_writeLE32(ostart, lhc); + break; + } + + case 5: + { + uint lhc = (uint)(hType + (3 << 2)) + ((uint)litSize << 4) + ((uint)cLitSize << 22); + MEM_writeLE32(ostart, lhc); + ostart[4] = (byte)(cLitSize >> 10); + break; + } + + default: + assert(0 != 0); + break; + } + + *entropyWritten = 1; + return (nuint)(op - ostart); + } + + private static nuint ZSTD_seqDecompressedSize( + SeqStore_t* seqStore, + SeqDef_s* sequences, + nuint nbSeqs, + nuint litSize, + int lastSubBlock + ) + { + nuint matchLengthSum = 0; + nuint litLengthSum = 0; + nuint n; + for (n = 0; n < nbSeqs; n++) + { + ZSTD_SequenceLength seqLen = ZSTD_getSequenceLength(seqStore, sequences + n); + litLengthSum += seqLen.litLength; + matchLengthSum += seqLen.matchLength; + } + + if (lastSubBlock == 0) + assert(litLengthSum == litSize); + else + assert(litLengthSum <= litSize); + return matchLengthSum + litSize; + } + + /** ZSTD_compressSubBlock_sequences() : + * Compresses sequences section for a sub-block. + * fseMetadata->llType, fseMetadata->ofType, and fseMetadata->mlType have + * symbol compression modes for the super-block. + * The first successfully compressed block will have these in its header. + * We set entropyWritten=1 when we succeed in compressing the sequences. + * The following sub-blocks will always have repeat mode. + * @return : compressed size of sequences section of a sub-block + * Or 0 if it is unable to compress + * Or error code. */ + private static nuint ZSTD_compressSubBlock_sequences( + ZSTD_fseCTables_t* fseTables, + ZSTD_fseCTablesMetadata_t* fseMetadata, + SeqDef_s* sequences, + nuint nbSeq, + byte* llCode, + byte* mlCode, + byte* ofCode, + ZSTD_CCtx_params_s* cctxParams, + void* dst, + nuint dstCapacity, + int bmi2, + int writeEntropy, + int* entropyWritten + ) + { + int longOffsets = cctxParams->cParams.windowLog > (uint)(MEM_32bits ? 25 : 57) ? 1 : 0; + byte* ostart = (byte*)dst; + byte* oend = ostart + dstCapacity; + byte* op = ostart; + byte* seqHead; + *entropyWritten = 0; + if (oend - op < 3 + 1) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (nbSeq < 128) + *op++ = (byte)nbSeq; + else if (nbSeq < 0x7F00) + { + op[0] = (byte)((nbSeq >> 8) + 0x80); + op[1] = (byte)nbSeq; + op += 2; + } + else + { + op[0] = 0xFF; + MEM_writeLE16(op + 1, (ushort)(nbSeq - 0x7F00)); + op += 3; + } + + if (nbSeq == 0) + { + return (nuint)(op - ostart); + } + + seqHead = op++; + if (writeEntropy != 0) + { + uint LLtype = (uint)fseMetadata->llType; + uint Offtype = (uint)fseMetadata->ofType; + uint MLtype = (uint)fseMetadata->mlType; + *seqHead = (byte)((LLtype << 6) + (Offtype << 4) + (MLtype << 2)); + memcpy(op, fseMetadata->fseTablesBuffer, (uint)fseMetadata->fseTablesSize); + op += fseMetadata->fseTablesSize; + } + else + { + uint repeat = (uint)SymbolEncodingType_e.set_repeat; + *seqHead = (byte)((repeat << 6) + (repeat << 4) + (repeat << 2)); + } + + { + nuint bitstreamSize = ZSTD_encodeSequences( + op, + (nuint)(oend - op), + fseTables->matchlengthCTable, + mlCode, + fseTables->offcodeCTable, + ofCode, + fseTables->litlengthCTable, + llCode, + sequences, + nbSeq, + longOffsets, + bmi2 + ); + { + nuint err_code = bitstreamSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + op += bitstreamSize; + if ( + writeEntropy != 0 + && fseMetadata->lastCountSize != 0 + && fseMetadata->lastCountSize + bitstreamSize < 4 + ) + { + assert(fseMetadata->lastCountSize + bitstreamSize == 3); + return 0; + } + } + + if (op - seqHead < 4) + { + return 0; + } + + *entropyWritten = 1; + return (nuint)(op - ostart); + } + + /** ZSTD_compressSubBlock() : + * Compresses a single sub-block. + * @return : compressed size of the sub-block + * Or 0 if it failed to compress. */ + private static nuint ZSTD_compressSubBlock( + ZSTD_entropyCTables_t* entropy, + ZSTD_entropyCTablesMetadata_t* entropyMetadata, + SeqDef_s* sequences, + nuint nbSeq, + byte* literals, + nuint litSize, + byte* llCode, + byte* mlCode, + byte* ofCode, + ZSTD_CCtx_params_s* cctxParams, + void* dst, + nuint dstCapacity, + int bmi2, + int writeLitEntropy, + int writeSeqEntropy, + int* litEntropyWritten, + int* seqEntropyWritten, + uint lastBlock + ) + { + byte* ostart = (byte*)dst; + byte* oend = ostart + dstCapacity; + byte* op = ostart + ZSTD_blockHeaderSize; + { + nuint cLitSize = ZSTD_compressSubBlock_literal( + &entropy->huf.CTable.e0, + &entropyMetadata->hufMetadata, + literals, + litSize, + op, + (nuint)(oend - op), + bmi2, + writeLitEntropy, + litEntropyWritten + ); + { + nuint err_code = cLitSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (cLitSize == 0) + return 0; + op += cLitSize; + } + + { + nuint cSeqSize = ZSTD_compressSubBlock_sequences( + &entropy->fse, + &entropyMetadata->fseMetadata, + sequences, + nbSeq, + llCode, + mlCode, + ofCode, + cctxParams, + op, + (nuint)(oend - op), + bmi2, + writeSeqEntropy, + seqEntropyWritten + ); + { + nuint err_code = cSeqSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (cSeqSize == 0) + return 0; + op += cSeqSize; + } + + { + nuint cSize = (nuint)(op - ostart) - ZSTD_blockHeaderSize; + uint cBlockHeader24 = + lastBlock + ((uint)blockType_e.bt_compressed << 1) + (uint)(cSize << 3); + MEM_writeLE24(ostart, cBlockHeader24); + } + + return (nuint)(op - ostart); + } + + private static nuint ZSTD_estimateSubBlockSize_literal( + byte* literals, + nuint litSize, + ZSTD_hufCTables_t* huf, + ZSTD_hufCTablesMetadata_t* hufMetadata, + void* workspace, + nuint wkspSize, + int writeEntropy + ) + { + uint* countWksp = (uint*)workspace; + uint maxSymbolValue = 255; + /* Use hard coded size of 3 bytes */ + nuint literalSectionHeaderSize = 3; + if (hufMetadata->hType == SymbolEncodingType_e.set_basic) + return litSize; + else if (hufMetadata->hType == SymbolEncodingType_e.set_rle) + return 1; + else if ( + hufMetadata->hType == SymbolEncodingType_e.set_compressed + || hufMetadata->hType == SymbolEncodingType_e.set_repeat + ) + { + nuint largest = HIST_count_wksp( + countWksp, + &maxSymbolValue, + literals, + litSize, + workspace, + wkspSize + ); + if (ERR_isError(largest)) + return litSize; + { + nuint cLitSizeEstimate = HUF_estimateCompressedSize( + &huf->CTable.e0, + countWksp, + maxSymbolValue + ); + if (writeEntropy != 0) + cLitSizeEstimate += hufMetadata->hufDesSize; + return cLitSizeEstimate + literalSectionHeaderSize; + } + } + + assert(0 != 0); + return 0; + } + + private static nuint ZSTD_estimateSubBlockSize_symbolType( + SymbolEncodingType_e type, + byte* codeTable, + uint maxCode, + nuint nbSeq, + uint* fseCTable, + byte* additionalBits, + short* defaultNorm, + uint defaultNormLog, + uint defaultMax, + void* workspace, + nuint wkspSize + ) + { + uint* countWksp = (uint*)workspace; + byte* ctp = codeTable; + byte* ctStart = ctp; + byte* ctEnd = ctStart + nbSeq; + nuint cSymbolTypeSizeEstimateInBits = 0; + uint max = maxCode; + HIST_countFast_wksp(countWksp, &max, codeTable, nbSeq, workspace, wkspSize); + if (type == SymbolEncodingType_e.set_basic) + { + assert(max <= defaultMax); + cSymbolTypeSizeEstimateInBits = + max <= defaultMax + ? ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, countWksp, max) + : unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + else if (type == SymbolEncodingType_e.set_rle) + { + cSymbolTypeSizeEstimateInBits = 0; + } + else if ( + type == SymbolEncodingType_e.set_compressed + || type == SymbolEncodingType_e.set_repeat + ) + { + cSymbolTypeSizeEstimateInBits = ZSTD_fseBitCost(fseCTable, countWksp, max); + } + + if (ERR_isError(cSymbolTypeSizeEstimateInBits)) + return nbSeq * 10; + while (ctp < ctEnd) + { + if (additionalBits != null) + cSymbolTypeSizeEstimateInBits += additionalBits[*ctp]; + else + cSymbolTypeSizeEstimateInBits += *ctp; + ctp++; + } + + return cSymbolTypeSizeEstimateInBits / 8; + } + + private static nuint ZSTD_estimateSubBlockSize_sequences( + byte* ofCodeTable, + byte* llCodeTable, + byte* mlCodeTable, + nuint nbSeq, + ZSTD_fseCTables_t* fseTables, + ZSTD_fseCTablesMetadata_t* fseMetadata, + void* workspace, + nuint wkspSize, + int writeEntropy + ) + { + /* Use hard coded size of 3 bytes */ + const nuint sequencesSectionHeaderSize = 3; + nuint cSeqSizeEstimate = 0; + if (nbSeq == 0) + return sequencesSectionHeaderSize; + cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType( + fseMetadata->ofType, + ofCodeTable, + 31, + nbSeq, + fseTables->offcodeCTable, + null, + OF_defaultNorm, + OF_defaultNormLog, + 28, + workspace, + wkspSize + ); + cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType( + fseMetadata->llType, + llCodeTable, + 35, + nbSeq, + fseTables->litlengthCTable, + LL_bits, + LL_defaultNorm, + LL_defaultNormLog, + 35, + workspace, + wkspSize + ); + cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType( + fseMetadata->mlType, + mlCodeTable, + 52, + nbSeq, + fseTables->matchlengthCTable, + ML_bits, + ML_defaultNorm, + ML_defaultNormLog, + 52, + workspace, + wkspSize + ); + if (writeEntropy != 0) + cSeqSizeEstimate += fseMetadata->fseTablesSize; + return cSeqSizeEstimate + sequencesSectionHeaderSize; + } + + private static EstimatedBlockSize ZSTD_estimateSubBlockSize( + byte* literals, + nuint litSize, + byte* ofCodeTable, + byte* llCodeTable, + byte* mlCodeTable, + nuint nbSeq, + ZSTD_entropyCTables_t* entropy, + ZSTD_entropyCTablesMetadata_t* entropyMetadata, + void* workspace, + nuint wkspSize, + int writeLitEntropy, + int writeSeqEntropy + ) + { + EstimatedBlockSize ebs; + ebs.estLitSize = ZSTD_estimateSubBlockSize_literal( + literals, + litSize, + &entropy->huf, + &entropyMetadata->hufMetadata, + workspace, + wkspSize, + writeLitEntropy + ); + ebs.estBlockSize = ZSTD_estimateSubBlockSize_sequences( + ofCodeTable, + llCodeTable, + mlCodeTable, + nbSeq, + &entropy->fse, + &entropyMetadata->fseMetadata, + workspace, + wkspSize, + writeSeqEntropy + ); + ebs.estBlockSize += ebs.estLitSize + ZSTD_blockHeaderSize; + return ebs; + } + + private static int ZSTD_needSequenceEntropyTables(ZSTD_fseCTablesMetadata_t* fseMetadata) + { + if ( + fseMetadata->llType == SymbolEncodingType_e.set_compressed + || fseMetadata->llType == SymbolEncodingType_e.set_rle + ) + return 1; + if ( + fseMetadata->mlType == SymbolEncodingType_e.set_compressed + || fseMetadata->mlType == SymbolEncodingType_e.set_rle + ) + return 1; + if ( + fseMetadata->ofType == SymbolEncodingType_e.set_compressed + || fseMetadata->ofType == SymbolEncodingType_e.set_rle + ) + return 1; + return 0; + } + + private static nuint countLiterals(SeqStore_t* seqStore, SeqDef_s* sp, nuint seqCount) + { + nuint n, + total = 0; + assert(sp != null); + for (n = 0; n < seqCount; n++) + { + total += ZSTD_getSequenceLength(seqStore, sp + n).litLength; + } + + return total; + } + + private static nuint sizeBlockSequences( + SeqDef_s* sp, + nuint nbSeqs, + nuint targetBudget, + nuint avgLitCost, + nuint avgSeqCost, + int firstSubBlock + ) + { + nuint n, + budget = 0, + inSize = 0; + /* generous estimate */ + nuint headerSize = (nuint)firstSubBlock * 120 * 256; + assert(firstSubBlock == 0 || firstSubBlock == 1); + budget += headerSize; + budget += sp[0].litLength * avgLitCost + avgSeqCost; + if (budget > targetBudget) + return 1; + inSize = (nuint)(sp[0].litLength + (sp[0].mlBase + 3)); + for (n = 1; n < nbSeqs; n++) + { + nuint currentCost = sp[n].litLength * avgLitCost + avgSeqCost; + budget += currentCost; + inSize += (nuint)(sp[n].litLength + (sp[n].mlBase + 3)); + if (budget > targetBudget && budget < inSize * 256) + break; + } + + return n; + } + + /** ZSTD_compressSubBlock_multi() : + * Breaks super-block into multiple sub-blocks and compresses them. + * Entropy will be written into the first block. + * The following blocks use repeat_mode to compress. + * Sub-blocks are all compressed, except the last one when beneficial. + * @return : compressed size of the super block (which features multiple ZSTD blocks) + * or 0 if it failed to compress. */ + private static nuint ZSTD_compressSubBlock_multi( + SeqStore_t* seqStorePtr, + ZSTD_compressedBlockState_t* prevCBlock, + ZSTD_compressedBlockState_t* nextCBlock, + ZSTD_entropyCTablesMetadata_t* entropyMetadata, + ZSTD_CCtx_params_s* cctxParams, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + int bmi2, + uint lastBlock, + void* workspace, + nuint wkspSize + ) + { + SeqDef_s* sstart = seqStorePtr->sequencesStart; + SeqDef_s* send = seqStorePtr->sequences; + /* tracks progresses within seqStorePtr->sequences */ + SeqDef_s* sp = sstart; + nuint nbSeqs = (nuint)(send - sstart); + byte* lstart = seqStorePtr->litStart; + byte* lend = seqStorePtr->lit; + byte* lp = lstart; + nuint nbLiterals = (nuint)(lend - lstart); + byte* ip = (byte*)src; + byte* iend = ip + srcSize; + byte* ostart = (byte*)dst; + byte* oend = ostart + dstCapacity; + byte* op = ostart; + byte* llCodePtr = seqStorePtr->llCode; + byte* mlCodePtr = seqStorePtr->mlCode; + byte* ofCodePtr = seqStorePtr->ofCode; + /* enforce minimum size, to reduce undesirable side effects */ + const nuint minTarget = 1340; + nuint targetCBlockSize = + minTarget > cctxParams->targetCBlockSize ? minTarget : cctxParams->targetCBlockSize; + int writeLitEntropy = + entropyMetadata->hufMetadata.hType == SymbolEncodingType_e.set_compressed ? 1 : 0; + int writeSeqEntropy = 1; + if (nbSeqs > 0) + { + EstimatedBlockSize ebs = ZSTD_estimateSubBlockSize( + lp, + nbLiterals, + ofCodePtr, + llCodePtr, + mlCodePtr, + nbSeqs, + &nextCBlock->entropy, + entropyMetadata, + workspace, + wkspSize, + writeLitEntropy, + writeSeqEntropy + ); + /* quick estimation */ + nuint avgLitCost = nbLiterals != 0 ? ebs.estLitSize * 256 / nbLiterals : 256; + nuint avgSeqCost = (ebs.estBlockSize - ebs.estLitSize) * 256 / nbSeqs; + nuint nbSubBlocks = + (ebs.estBlockSize + targetCBlockSize / 2) / targetCBlockSize > 1 + ? (ebs.estBlockSize + targetCBlockSize / 2) / targetCBlockSize + : 1; + nuint n, + avgBlockBudget, + blockBudgetSupp = 0; + avgBlockBudget = ebs.estBlockSize * 256 / nbSubBlocks; + if (ebs.estBlockSize > srcSize) + return 0; + assert(nbSubBlocks > 0); + for (n = 0; n < nbSubBlocks - 1; n++) + { + /* determine nb of sequences for current sub-block + nbLiterals from next sequence */ + nuint seqCount = sizeBlockSequences( + sp, + (nuint)(send - sp), + avgBlockBudget + blockBudgetSupp, + avgLitCost, + avgSeqCost, + n == 0 ? 1 : 0 + ); + assert(seqCount <= (nuint)(send - sp)); + if (sp + seqCount == send) + break; + assert(seqCount > 0); + { + int litEntropyWritten = 0; + int seqEntropyWritten = 0; + nuint litSize = countLiterals(seqStorePtr, sp, seqCount); + nuint decompressedSize = ZSTD_seqDecompressedSize( + seqStorePtr, + sp, + seqCount, + litSize, + 0 + ); + nuint cSize = ZSTD_compressSubBlock( + &nextCBlock->entropy, + entropyMetadata, + sp, + seqCount, + lp, + litSize, + llCodePtr, + mlCodePtr, + ofCodePtr, + cctxParams, + op, + (nuint)(oend - op), + bmi2, + writeLitEntropy, + writeSeqEntropy, + &litEntropyWritten, + &seqEntropyWritten, + 0 + ); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (cSize > 0 && cSize < decompressedSize) + { + assert(ip + decompressedSize <= iend); + ip += decompressedSize; + lp += litSize; + op += cSize; + llCodePtr += seqCount; + mlCodePtr += seqCount; + ofCodePtr += seqCount; + if (litEntropyWritten != 0) + { + writeLitEntropy = 0; + } + + if (seqEntropyWritten != 0) + { + writeSeqEntropy = 0; + } + + sp += seqCount; + blockBudgetSupp = 0; + } + } + } + } + + { + int litEntropyWritten = 0; + int seqEntropyWritten = 0; + nuint litSize = (nuint)(lend - lp); + nuint seqCount = (nuint)(send - sp); + nuint decompressedSize = ZSTD_seqDecompressedSize( + seqStorePtr, + sp, + seqCount, + litSize, + 1 + ); + nuint cSize = ZSTD_compressSubBlock( + &nextCBlock->entropy, + entropyMetadata, + sp, + seqCount, + lp, + litSize, + llCodePtr, + mlCodePtr, + ofCodePtr, + cctxParams, + op, + (nuint)(oend - op), + bmi2, + writeLitEntropy, + writeSeqEntropy, + &litEntropyWritten, + &seqEntropyWritten, + lastBlock + ); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (cSize > 0 && cSize < decompressedSize) + { + assert(ip + decompressedSize <= iend); + ip += decompressedSize; + lp += litSize; + op += cSize; + llCodePtr += seqCount; + mlCodePtr += seqCount; + ofCodePtr += seqCount; + if (litEntropyWritten != 0) + { + writeLitEntropy = 0; + } + + if (seqEntropyWritten != 0) + { + writeSeqEntropy = 0; + } + + sp += seqCount; + } + } + + if (writeLitEntropy != 0) + { + memcpy( + &nextCBlock->entropy.huf, + &prevCBlock->entropy.huf, + (uint)sizeof(ZSTD_hufCTables_t) + ); + } + + if ( + writeSeqEntropy != 0 + && ZSTD_needSequenceEntropyTables(&entropyMetadata->fseMetadata) != 0 + ) + { + return 0; + } + + if (ip < iend) + { + /* some data left : last part of the block sent uncompressed */ + nuint rSize = (nuint)(iend - ip); + nuint cSize = ZSTD_noCompressBlock(op, (nuint)(oend - op), ip, rSize, lastBlock); + { + nuint err_code = cSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(cSize != 0); + op += cSize; + if (sp < send) + { + SeqDef_s* seq; + repcodes_s rep; + memcpy(&rep, prevCBlock->rep, (uint)sizeof(repcodes_s)); + for (seq = sstart; seq < sp; ++seq) + { + ZSTD_updateRep( + rep.rep, + seq->offBase, + ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0 ? 1U : 0U + ); + } + + memcpy(nextCBlock->rep, &rep, (uint)sizeof(repcodes_s)); + } + } + + return (nuint)(op - ostart); + } + + /* ZSTD_compressSuperBlock() : + * Used to compress a super block when targetCBlockSize is being used. + * The given block will be compressed into multiple sub blocks that are around targetCBlockSize. */ + private static nuint ZSTD_compressSuperBlock( + ZSTD_CCtx_s* zc, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + uint lastBlock + ) + { + ZSTD_entropyCTablesMetadata_t entropyMetadata; + { + nuint err_code = ZSTD_buildBlockEntropyStats( + &zc->seqStore, + &zc->blockState.prevCBlock->entropy, + &zc->blockState.nextCBlock->entropy, + &zc->appliedParams, + &entropyMetadata, + zc->tmpWorkspace, + zc->tmpWkspSize + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return ZSTD_compressSubBlock_multi( + &zc->seqStore, + zc->blockState.prevCBlock, + zc->blockState.nextCBlock, + &entropyMetadata, + &zc->appliedParams, + dst, + dstCapacity, + src, + srcSize, + zc->bmi2, + lastBlock, + zc->tmpWorkspace, + zc->tmpWkspSize + ); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCwksp.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCwksp.cs new file mode 100644 index 00000000..236c94d4 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdCwksp.cs @@ -0,0 +1,581 @@ +using System.Diagnostics; +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Conditional("DEBUG")] + private static void ZSTD_cwksp_assert_internal_consistency(ZSTD_cwksp* ws) + { + assert(ws->workspace <= ws->objectEnd); + assert(ws->objectEnd <= ws->tableEnd); + assert(ws->objectEnd <= ws->tableValidEnd); + assert(ws->tableEnd <= ws->allocStart); + assert(ws->tableValidEnd <= ws->allocStart); + assert(ws->allocStart <= ws->workspaceEnd); + assert(ws->initOnceStart <= ZSTD_cwksp_initialAllocStart(ws)); + assert(ws->workspace <= ws->initOnceStart); + } + + /** + * Align must be a power of 2. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_cwksp_align(nuint size, nuint align) + { + nuint mask = align - 1; + assert(ZSTD_isPower2(align) != 0); + return size + mask & ~mask; + } + + /** + * Use this to determine how much space in the workspace we will consume to + * allocate this object. (Normally it should be exactly the size of the object, + * but under special conditions, like ASAN, where we pad each object, it might + * be larger.) + * + * Since tables aren't currently redzoned, you don't need to call through this + * to figure out how much space you need for the matchState tables. Everything + * else is though. + * + * Do not use for sizing aligned buffers. Instead, use ZSTD_cwksp_aligned64_alloc_size(). + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_cwksp_alloc_size(nuint size) + { + if (size == 0) + return 0; + return size; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_cwksp_aligned_alloc_size(nuint size, nuint alignment) + { + return ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(size, alignment)); + } + + /** + * Returns an adjusted alloc size that is the nearest larger multiple of 64 bytes. + * Used to determine the number of bytes required for a given "aligned". + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_cwksp_aligned64_alloc_size(nuint size) + { + return ZSTD_cwksp_aligned_alloc_size(size, 64); + } + + /** + * Returns the amount of additional space the cwksp must allocate + * for internal purposes (currently only alignment). + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_cwksp_slack_space_required() + { + /* For alignment, the wksp will always allocate an additional 2*ZSTD_CWKSP_ALIGNMENT_BYTES + * bytes to align the beginning of tables section and end of buffers; + */ + const nuint slackSpace = 64 * 2; + return slackSpace; + } + + /** + * Return the number of additional bytes required to align a pointer to the given number of bytes. + * alignBytes must be a power of two. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_cwksp_bytes_to_align_ptr(void* ptr, nuint alignBytes) + { + nuint alignBytesMask = alignBytes - 1; + nuint bytes = alignBytes - ((nuint)ptr & alignBytesMask) & alignBytesMask; + assert(ZSTD_isPower2(alignBytes) != 0); + assert(bytes < alignBytes); + return bytes; + } + + /** + * Returns the initial value for allocStart which is used to determine the position from + * which we can allocate from the end of the workspace. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void* ZSTD_cwksp_initialAllocStart(ZSTD_cwksp* ws) + { + sbyte* endPtr = (sbyte*)ws->workspaceEnd; + assert(ZSTD_isPower2(64) != 0); + endPtr = endPtr - (nuint)endPtr % 64; + return endPtr; + } + + /** + * Internal function. Do not use directly. + * Reserves the given number of bytes within the aligned/buffer segment of the wksp, + * which counts from the end of the wksp (as opposed to the object/table segment). + * + * Returns a pointer to the beginning of that space. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void* ZSTD_cwksp_reserve_internal_buffer_space(ZSTD_cwksp* ws, nuint bytes) + { + void* alloc = (byte*)ws->allocStart - bytes; + void* bottom = ws->tableEnd; + ZSTD_cwksp_assert_internal_consistency(ws); + assert(alloc >= bottom); + if (alloc < bottom) + { + ws->allocFailed = 1; + return null; + } + + if (alloc < ws->tableValidEnd) + { + ws->tableValidEnd = alloc; + } + + ws->allocStart = alloc; + return alloc; + } + + /** + * Moves the cwksp to the next phase, and does any necessary allocations. + * cwksp initialization must necessarily go through each phase in order. + * Returns a 0 on success, or zstd error + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_cwksp_internal_advance_phase( + ZSTD_cwksp* ws, + ZSTD_cwksp_alloc_phase_e phase + ) + { + assert(phase >= ws->phase); + if (phase > ws->phase) + { + if ( + ws->phase < ZSTD_cwksp_alloc_phase_e.ZSTD_cwksp_alloc_aligned_init_once + && phase >= ZSTD_cwksp_alloc_phase_e.ZSTD_cwksp_alloc_aligned_init_once + ) + { + ws->tableValidEnd = ws->objectEnd; + ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws); + { + void* alloc = ws->objectEnd; + nuint bytesToAlign = ZSTD_cwksp_bytes_to_align_ptr(alloc, 64); + void* objectEnd = (byte*)alloc + bytesToAlign; + if (objectEnd > ws->workspaceEnd) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation) + ); + } + + ws->objectEnd = objectEnd; + ws->tableEnd = objectEnd; + if (ws->tableValidEnd < ws->tableEnd) + { + ws->tableValidEnd = ws->tableEnd; + } + } + } + + ws->phase = phase; + ZSTD_cwksp_assert_internal_consistency(ws); + } + + return 0; + } + + /** + * Returns whether this object/buffer/etc was allocated in this workspace. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_cwksp_owns_buffer(ZSTD_cwksp* ws, void* ptr) + { + return ptr != null && ws->workspace <= ptr && ptr < ws->workspaceEnd ? 1 : 0; + } + + /** + * Internal function. Do not use directly. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void* ZSTD_cwksp_reserve_internal( + ZSTD_cwksp* ws, + nuint bytes, + ZSTD_cwksp_alloc_phase_e phase + ) + { + void* alloc; + if (ERR_isError(ZSTD_cwksp_internal_advance_phase(ws, phase)) || bytes == 0) + { + return null; + } + + alloc = ZSTD_cwksp_reserve_internal_buffer_space(ws, bytes); + return alloc; + } + + /** + * Reserves and returns unaligned memory. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte* ZSTD_cwksp_reserve_buffer(ZSTD_cwksp* ws, nuint bytes) + { + return (byte*)ZSTD_cwksp_reserve_internal( + ws, + bytes, + ZSTD_cwksp_alloc_phase_e.ZSTD_cwksp_alloc_buffers + ); + } + + /** + * Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes). + * This memory has been initialized at least once in the past. + * This doesn't mean it has been initialized this time, and it might contain data from previous + * operations. + * The main usage is for algorithms that might need read access into uninitialized memory. + * The algorithm must maintain safety under these conditions and must make sure it doesn't + * leak any of the past data (directly or in side channels). + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void* ZSTD_cwksp_reserve_aligned_init_once(ZSTD_cwksp* ws, nuint bytes) + { + nuint alignedBytes = ZSTD_cwksp_align(bytes, 64); + void* ptr = ZSTD_cwksp_reserve_internal( + ws, + alignedBytes, + ZSTD_cwksp_alloc_phase_e.ZSTD_cwksp_alloc_aligned_init_once + ); + assert(((nuint)ptr & 64 - 1) == 0); + if (ptr != null && ptr < ws->initOnceStart) + { + memset( + ptr, + 0, + (uint)( + (nuint)((byte*)ws->initOnceStart - (byte*)ptr) < alignedBytes + ? (nuint)((byte*)ws->initOnceStart - (byte*)ptr) + : alignedBytes + ) + ); + ws->initOnceStart = ptr; + } + + return ptr; + } + + /** + * Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes). + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void* ZSTD_cwksp_reserve_aligned64(ZSTD_cwksp* ws, nuint bytes) + { + void* ptr = ZSTD_cwksp_reserve_internal( + ws, + ZSTD_cwksp_align(bytes, 64), + ZSTD_cwksp_alloc_phase_e.ZSTD_cwksp_alloc_aligned + ); + assert(((nuint)ptr & 64 - 1) == 0); + return ptr; + } + + /** + * Aligned on 64 bytes. These buffers have the special property that + * their values remain constrained, allowing us to reuse them without + * memset()-ing them. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, nuint bytes) + { + ZSTD_cwksp_alloc_phase_e phase = + ZSTD_cwksp_alloc_phase_e.ZSTD_cwksp_alloc_aligned_init_once; + void* alloc; + void* end; + void* top; + if (ws->phase < phase) + { + if (ERR_isError(ZSTD_cwksp_internal_advance_phase(ws, phase))) + { + return null; + } + } + + alloc = ws->tableEnd; + end = (byte*)alloc + bytes; + top = ws->allocStart; + assert((bytes & sizeof(uint) - 1) == 0); + ZSTD_cwksp_assert_internal_consistency(ws); + assert(end <= top); + if (end > top) + { + ws->allocFailed = 1; + return null; + } + + ws->tableEnd = end; + assert((bytes & 64 - 1) == 0); + assert(((nuint)alloc & 64 - 1) == 0); + return alloc; + } + + /** + * Aligned on sizeof(void*). + * Note : should happen only once, at workspace first initialization + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void* ZSTD_cwksp_reserve_object(ZSTD_cwksp* ws, nuint bytes) + { + nuint roundedBytes = ZSTD_cwksp_align(bytes, (nuint)sizeof(void*)); + void* alloc = ws->objectEnd; + void* end = (byte*)alloc + roundedBytes; + assert((nuint)alloc % (nuint)sizeof(void*) == 0); + assert(bytes % (nuint)sizeof(void*) == 0); + ZSTD_cwksp_assert_internal_consistency(ws); + if ( + ws->phase != ZSTD_cwksp_alloc_phase_e.ZSTD_cwksp_alloc_objects + || end > ws->workspaceEnd + ) + { + ws->allocFailed = 1; + return null; + } + + ws->objectEnd = end; + ws->tableEnd = end; + ws->tableValidEnd = end; + return alloc; + } + + /** + * with alignment control + * Note : should happen only once, at workspace first initialization + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void* ZSTD_cwksp_reserve_object_aligned( + ZSTD_cwksp* ws, + nuint byteSize, + nuint alignment + ) + { + nuint mask = alignment - 1; + nuint surplus = alignment > (nuint)sizeof(void*) ? alignment - (nuint)sizeof(void*) : 0; + void* start = ZSTD_cwksp_reserve_object(ws, byteSize + surplus); + if (start == null) + return null; + if (surplus == 0) + return start; + assert(ZSTD_isPower2(alignment) != 0); + return (void*)((nuint)start + surplus & ~mask); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_cwksp_mark_tables_dirty(ZSTD_cwksp* ws) + { + assert(ws->tableValidEnd >= ws->objectEnd); + assert(ws->tableValidEnd <= ws->allocStart); + ws->tableValidEnd = ws->objectEnd; + ZSTD_cwksp_assert_internal_consistency(ws); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_cwksp_mark_tables_clean(ZSTD_cwksp* ws) + { + assert(ws->tableValidEnd >= ws->objectEnd); + assert(ws->tableValidEnd <= ws->allocStart); + if (ws->tableValidEnd < ws->tableEnd) + { + ws->tableValidEnd = ws->tableEnd; + } + + ZSTD_cwksp_assert_internal_consistency(ws); + } + + /** + * Zero the part of the allocated tables not already marked clean. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_cwksp_clean_tables(ZSTD_cwksp* ws) + { + assert(ws->tableValidEnd >= ws->objectEnd); + assert(ws->tableValidEnd <= ws->allocStart); + if (ws->tableValidEnd < ws->tableEnd) + { + memset( + ws->tableValidEnd, + 0, + (uint)(nuint)((byte*)ws->tableEnd - (byte*)ws->tableValidEnd) + ); + } + + ZSTD_cwksp_mark_tables_clean(ws); + } + + /** + * Invalidates table allocations. + * All other allocations remain valid. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_cwksp_clear_tables(ZSTD_cwksp* ws) + { + ws->tableEnd = ws->objectEnd; + ZSTD_cwksp_assert_internal_consistency(ws); + } + + /** + * Invalidates all buffer, aligned, and table allocations. + * Object allocations remain valid. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_cwksp_clear(ZSTD_cwksp* ws) + { + ws->tableEnd = ws->objectEnd; + ws->allocStart = ZSTD_cwksp_initialAllocStart(ws); + ws->allocFailed = 0; + if (ws->phase > ZSTD_cwksp_alloc_phase_e.ZSTD_cwksp_alloc_aligned_init_once) + { + ws->phase = ZSTD_cwksp_alloc_phase_e.ZSTD_cwksp_alloc_aligned_init_once; + } + + ZSTD_cwksp_assert_internal_consistency(ws); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_cwksp_sizeof(ZSTD_cwksp* ws) + { + return (nuint)((byte*)ws->workspaceEnd - (byte*)ws->workspace); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_cwksp_used(ZSTD_cwksp* ws) + { + return (nuint)((byte*)ws->tableEnd - (byte*)ws->workspace) + + (nuint)((byte*)ws->workspaceEnd - (byte*)ws->allocStart); + } + + /** + * The provided workspace takes ownership of the buffer [start, start+size). + * Any existing values in the workspace are ignored (the previously managed + * buffer, if present, must be separately freed). + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_cwksp_init( + ZSTD_cwksp* ws, + void* start, + nuint size, + ZSTD_cwksp_static_alloc_e isStatic + ) + { + assert(((nuint)start & (nuint)(sizeof(void*) - 1)) == 0); + ws->workspace = start; + ws->workspaceEnd = (byte*)start + size; + ws->objectEnd = ws->workspace; + ws->tableValidEnd = ws->objectEnd; + ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws); + ws->phase = ZSTD_cwksp_alloc_phase_e.ZSTD_cwksp_alloc_objects; + ws->isStatic = isStatic; + ZSTD_cwksp_clear(ws); + ws->workspaceOversizedDuration = 0; + ZSTD_cwksp_assert_internal_consistency(ws); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_cwksp_create(ZSTD_cwksp* ws, nuint size, ZSTD_customMem customMem) + { + void* workspace = ZSTD_customMalloc(size, customMem); + if (workspace == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + ZSTD_cwksp_init(ws, workspace, size, ZSTD_cwksp_static_alloc_e.ZSTD_cwksp_dynamic_alloc); + return 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_cwksp_free(ZSTD_cwksp* ws, ZSTD_customMem customMem) + { + void* ptr = ws->workspace; + *ws = new ZSTD_cwksp(); + ZSTD_customFree(ptr, customMem); + } + + /** + * Moves the management of a workspace from one cwksp to another. The src cwksp + * is left in an invalid state (src must be re-init()'ed before it's used again). + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_cwksp_move(ZSTD_cwksp* dst, ZSTD_cwksp* src) + { + *dst = *src; + *src = new ZSTD_cwksp(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_cwksp_reserve_failed(ZSTD_cwksp* ws) + { + return ws->allocFailed; + } + + /* ZSTD_alignmentSpaceWithinBounds() : + * Returns if the estimated space needed for a wksp is within an acceptable limit of the + * actual amount of space used. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_cwksp_estimated_space_within_bounds( + ZSTD_cwksp* ws, + nuint estimatedSpace + ) + { + return + estimatedSpace - ZSTD_cwksp_slack_space_required() <= ZSTD_cwksp_used(ws) + && ZSTD_cwksp_used(ws) <= estimatedSpace + ? 1 + : 0; + } + + /*-************************************* + * Functions + ***************************************/ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_cwksp_available_space(ZSTD_cwksp* ws) + { + return (nuint)((byte*)ws->allocStart - (byte*)ws->tableEnd); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_cwksp_check_available(ZSTD_cwksp* ws, nuint additionalNeededSpace) + { + return ZSTD_cwksp_available_space(ws) >= additionalNeededSpace ? 1 : 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_cwksp_check_too_large(ZSTD_cwksp* ws, nuint additionalNeededSpace) + { + return ZSTD_cwksp_check_available(ws, additionalNeededSpace * 3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_cwksp_check_wasteful(ZSTD_cwksp* ws, nuint additionalNeededSpace) + { + return + ZSTD_cwksp_check_too_large(ws, additionalNeededSpace) != 0 + && ws->workspaceOversizedDuration > 128 + ? 1 + : 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_cwksp_bump_oversized_duration( + ZSTD_cwksp* ws, + nuint additionalNeededSpace + ) + { + if (ZSTD_cwksp_check_too_large(ws, additionalNeededSpace) != 0) + { + ws->workspaceOversizedDuration++; + } + else + { + ws->workspaceOversizedDuration = 0; + } + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDdict.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDdict.cs new file mode 100644 index 00000000..ce79c0f1 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDdict.cs @@ -0,0 +1,292 @@ +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /* note: several prototypes are already published in `zstd.h` : + * ZSTD_createDDict() + * ZSTD_createDDict_byReference() + * ZSTD_createDDict_advanced() + * ZSTD_freeDDict() + * ZSTD_initStaticDDict() + * ZSTD_sizeof_DDict() + * ZSTD_estimateDDictSize() + * ZSTD_getDictID_fromDict() + */ + private static void* ZSTD_DDict_dictContent(ZSTD_DDict_s* ddict) + { + assert(ddict != null); + return ddict->dictContent; + } + + private static nuint ZSTD_DDict_dictSize(ZSTD_DDict_s* ddict) + { + assert(ddict != null); + return ddict->dictSize; + } + + private static void ZSTD_copyDDictParameters(ZSTD_DCtx_s* dctx, ZSTD_DDict_s* ddict) + { + assert(dctx != null); + assert(ddict != null); + dctx->dictID = ddict->dictID; + dctx->prefixStart = ddict->dictContent; + dctx->virtualStart = ddict->dictContent; + dctx->dictEnd = (byte*)ddict->dictContent + ddict->dictSize; + dctx->previousDstEnd = dctx->dictEnd; + if (ddict->entropyPresent != 0) + { + dctx->litEntropy = 1; + dctx->fseEntropy = 1; + dctx->LLTptr = &ddict->entropy.LLTable.e0; + dctx->MLTptr = &ddict->entropy.MLTable.e0; + dctx->OFTptr = &ddict->entropy.OFTable.e0; + dctx->HUFptr = ddict->entropy.hufTable; + dctx->entropy.rep[0] = ddict->entropy.rep[0]; + dctx->entropy.rep[1] = ddict->entropy.rep[1]; + dctx->entropy.rep[2] = ddict->entropy.rep[2]; + } + else + { + dctx->litEntropy = 0; + dctx->fseEntropy = 0; + } + } + + private static nuint ZSTD_loadEntropy_intoDDict( + ZSTD_DDict_s* ddict, + ZSTD_dictContentType_e dictContentType + ) + { + ddict->dictID = 0; + ddict->entropyPresent = 0; + if (dictContentType == ZSTD_dictContentType_e.ZSTD_dct_rawContent) + return 0; + if (ddict->dictSize < 8) + { + if (dictContentType == ZSTD_dictContentType_e.ZSTD_dct_fullDict) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + return 0; + } + + { + uint magic = MEM_readLE32(ddict->dictContent); + if (magic != 0xEC30A437) + { + if (dictContentType == ZSTD_dictContentType_e.ZSTD_dct_fullDict) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + return 0; + } + } + + ddict->dictID = MEM_readLE32((sbyte*)ddict->dictContent + 4); + if (ERR_isError(ZSTD_loadDEntropy(&ddict->entropy, ddict->dictContent, ddict->dictSize))) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + ddict->entropyPresent = 1; + return 0; + } + + private static nuint ZSTD_initDDict_internal( + ZSTD_DDict_s* ddict, + void* dict, + nuint dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictContentType_e dictContentType + ) + { + if (dictLoadMethod == ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef || dict == null || dictSize == 0) + { + ddict->dictBuffer = null; + ddict->dictContent = dict; + if (dict == null) + dictSize = 0; + } + else + { + void* internalBuffer = ZSTD_customMalloc(dictSize, ddict->cMem); + ddict->dictBuffer = internalBuffer; + ddict->dictContent = internalBuffer; + if (internalBuffer == null) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + memcpy(internalBuffer, dict, (uint)dictSize); + } + + ddict->dictSize = dictSize; + ddict->entropy.hufTable[0] = 12 * 0x1000001; + { + /* parse dictionary content */ + nuint err_code = ZSTD_loadEntropy_intoDDict(ddict, dictContentType); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + public static ZSTD_DDict_s* ZSTD_createDDict_advanced( + void* dict, + nuint dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictContentType_e dictContentType, + ZSTD_customMem customMem + ) + { + if (((customMem.customAlloc == null ? 1 : 0) ^ (customMem.customFree == null ? 1 : 0)) != 0) + return null; + { + ZSTD_DDict_s* ddict = (ZSTD_DDict_s*)ZSTD_customMalloc( + (nuint)sizeof(ZSTD_DDict_s), + customMem + ); + if (ddict == null) + return null; + ddict->cMem = customMem; + { + nuint initResult = ZSTD_initDDict_internal( + ddict, + dict, + dictSize, + dictLoadMethod, + dictContentType + ); + if (ERR_isError(initResult)) + { + ZSTD_freeDDict(ddict); + return null; + } + } + + return ddict; + } + } + + /*! ZSTD_createDDict() : + * Create a digested dictionary, to start decompression without startup delay. + * `dict` content is copied inside DDict. + * Consequently, `dict` can be released after `ZSTD_DDict` creation */ + public static ZSTD_DDict_s* ZSTD_createDDict(void* dict, nuint dictSize) + { + ZSTD_customMem allocator = new ZSTD_customMem + { + customAlloc = null, + customFree = null, + opaque = null, + }; + return ZSTD_createDDict_advanced( + dict, + dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byCopy, + ZSTD_dictContentType_e.ZSTD_dct_auto, + allocator + ); + } + + /*! ZSTD_createDDict_byReference() : + * Create a digested dictionary, to start decompression without startup delay. + * Dictionary content is simply referenced, it will be accessed during decompression. + * Warning : dictBuffer must outlive DDict (DDict must be freed before dictBuffer) */ + public static ZSTD_DDict_s* ZSTD_createDDict_byReference(void* dictBuffer, nuint dictSize) + { + ZSTD_customMem allocator = new ZSTD_customMem + { + customAlloc = null, + customFree = null, + opaque = null, + }; + return ZSTD_createDDict_advanced( + dictBuffer, + dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef, + ZSTD_dictContentType_e.ZSTD_dct_auto, + allocator + ); + } + + public static ZSTD_DDict_s* ZSTD_initStaticDDict( + void* sBuffer, + nuint sBufferSize, + void* dict, + nuint dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictContentType_e dictContentType + ) + { + nuint neededSpace = + (nuint)sizeof(ZSTD_DDict_s) + + (dictLoadMethod == ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef ? 0 : dictSize); + ZSTD_DDict_s* ddict = (ZSTD_DDict_s*)sBuffer; + assert(sBuffer != null); + assert(dict != null); + if (((nuint)sBuffer & 7) != 0) + return null; + if (sBufferSize < neededSpace) + return null; + if (dictLoadMethod == ZSTD_dictLoadMethod_e.ZSTD_dlm_byCopy) + { + memcpy(ddict + 1, dict, (uint)dictSize); + dict = ddict + 1; + } + + if ( + ERR_isError( + ZSTD_initDDict_internal( + ddict, + dict, + dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef, + dictContentType + ) + ) + ) + return null; + return ddict; + } + + /*! ZSTD_freeDDict() : + * Function frees memory allocated with ZSTD_createDDict() + * If a NULL pointer is passed, no operation is performed. */ + public static nuint ZSTD_freeDDict(ZSTD_DDict_s* ddict) + { + if (ddict == null) + return 0; + { + ZSTD_customMem cMem = ddict->cMem; + ZSTD_customFree(ddict->dictBuffer, cMem); + ZSTD_customFree(ddict, cMem); + return 0; + } + } + + /*! ZSTD_estimateDDictSize() : + * Estimate amount of memory that will be needed to create a dictionary for decompression. + * Note : dictionary created by reference using ZSTD_dlm_byRef are smaller */ + public static nuint ZSTD_estimateDDictSize(nuint dictSize, ZSTD_dictLoadMethod_e dictLoadMethod) + { + return (nuint)sizeof(ZSTD_DDict_s) + + (dictLoadMethod == ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef ? 0 : dictSize); + } + + public static nuint ZSTD_sizeof_DDict(ZSTD_DDict_s* ddict) + { + if (ddict == null) + return 0; + return (nuint)sizeof(ZSTD_DDict_s) + (ddict->dictBuffer != null ? ddict->dictSize : 0); + } + + /*! ZSTD_getDictID_fromDDict() : + * Provides the dictID of the dictionary loaded into `ddict`. + * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty. + * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */ + public static uint ZSTD_getDictID_fromDDict(ZSTD_DDict_s* ddict) + { + if (ddict == null) + return 0; + return ddict->dictID; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompress.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompress.cs new file mode 100644 index 00000000..ab20f04c --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompress.cs @@ -0,0 +1,3420 @@ +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /* Hash function to determine starting position of dict insertion within the table + * Returns an index between [0, hashSet->ddictPtrTableSize] + */ + private static nuint ZSTD_DDictHashSet_getIndex(ZSTD_DDictHashSet* hashSet, uint dictID) + { + ulong hash = ZSTD_XXH64(&dictID, sizeof(uint), 0); + return (nuint)(hash & hashSet->ddictPtrTableSize - 1); + } + + /* Adds DDict to a hashset without resizing it. + * If inserting a DDict with a dictID that already exists in the set, replaces the one in the set. + * Returns 0 if successful, or a zstd error code if something went wrong. + */ + private static nuint ZSTD_DDictHashSet_emplaceDDict( + ZSTD_DDictHashSet* hashSet, + ZSTD_DDict_s* ddict + ) + { + uint dictID = ZSTD_getDictID_fromDDict(ddict); + nuint idx = ZSTD_DDictHashSet_getIndex(hashSet, dictID); + nuint idxRangeMask = hashSet->ddictPtrTableSize - 1; + if (hashSet->ddictPtrCount == hashSet->ddictPtrTableSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + + while (hashSet->ddictPtrTable[idx] != null) + { + if (ZSTD_getDictID_fromDDict(hashSet->ddictPtrTable[idx]) == dictID) + { + hashSet->ddictPtrTable[idx] = ddict; + return 0; + } + + idx &= idxRangeMask; + idx++; + } + + hashSet->ddictPtrTable[idx] = ddict; + hashSet->ddictPtrCount++; + return 0; + } + + /* Expands hash table by factor of DDICT_HASHSET_RESIZE_FACTOR and + * rehashes all values, allocates new table, frees old table. + * Returns 0 on success, otherwise a zstd error code. + */ + private static nuint ZSTD_DDictHashSet_expand( + ZSTD_DDictHashSet* hashSet, + ZSTD_customMem customMem + ) + { + nuint newTableSize = hashSet->ddictPtrTableSize * 2; + ZSTD_DDict_s** newTable = (ZSTD_DDict_s**)ZSTD_customCalloc( + (nuint)sizeof(ZSTD_DDict_s*) * newTableSize, + customMem + ); + ZSTD_DDict_s** oldTable = hashSet->ddictPtrTable; + nuint oldTableSize = hashSet->ddictPtrTableSize; + nuint i; + if (newTable == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + hashSet->ddictPtrTable = newTable; + hashSet->ddictPtrTableSize = newTableSize; + hashSet->ddictPtrCount = 0; + for (i = 0; i < oldTableSize; ++i) + { + if (oldTable[i] != null) + { + nuint err_code = ZSTD_DDictHashSet_emplaceDDict(hashSet, oldTable[i]); + if (ERR_isError(err_code)) + { + return err_code; + } + } + } + + ZSTD_customFree(oldTable, customMem); + return 0; + } + + /* Fetches a DDict with the given dictID + * Returns the ZSTD_DDict* with the requested dictID. If it doesn't exist, then returns NULL. + */ + private static ZSTD_DDict_s* ZSTD_DDictHashSet_getDDict(ZSTD_DDictHashSet* hashSet, uint dictID) + { + nuint idx = ZSTD_DDictHashSet_getIndex(hashSet, dictID); + nuint idxRangeMask = hashSet->ddictPtrTableSize - 1; + for (; ; ) + { + nuint currDictID = ZSTD_getDictID_fromDDict(hashSet->ddictPtrTable[idx]); + if (currDictID == dictID || currDictID == 0) + { + break; + } + else + { + idx &= idxRangeMask; + idx++; + } + } + + return hashSet->ddictPtrTable[idx]; + } + + /* Allocates space for and returns a ddict hash set + * The hash set's ZSTD_DDict* table has all values automatically set to NULL to begin with. + * Returns NULL if allocation failed. + */ + private static ZSTD_DDictHashSet* ZSTD_createDDictHashSet(ZSTD_customMem customMem) + { + ZSTD_DDictHashSet* ret = (ZSTD_DDictHashSet*)ZSTD_customMalloc( + (nuint)sizeof(ZSTD_DDictHashSet), + customMem + ); + if (ret == null) + return null; + ret->ddictPtrTable = (ZSTD_DDict_s**)ZSTD_customCalloc( + (nuint)(64 * sizeof(ZSTD_DDict_s*)), + customMem + ); + if (ret->ddictPtrTable == null) + { + ZSTD_customFree(ret, customMem); + return null; + } + + ret->ddictPtrTableSize = 64; + ret->ddictPtrCount = 0; + return ret; + } + + /* Frees the table of ZSTD_DDict* within a hashset, then frees the hashset itself. + * Note: The ZSTD_DDict* within the table are NOT freed. + */ + private static void ZSTD_freeDDictHashSet(ZSTD_DDictHashSet* hashSet, ZSTD_customMem customMem) + { + if (hashSet != null && hashSet->ddictPtrTable != null) + { + ZSTD_customFree(hashSet->ddictPtrTable, customMem); + } + + if (hashSet != null) + { + ZSTD_customFree(hashSet, customMem); + } + } + + /* Public function: Adds a DDict into the ZSTD_DDictHashSet, possibly triggering a resize of the hash set. + * Returns 0 on success, or a ZSTD error. + */ + private static nuint ZSTD_DDictHashSet_addDDict( + ZSTD_DDictHashSet* hashSet, + ZSTD_DDict_s* ddict, + ZSTD_customMem customMem + ) + { + if (hashSet->ddictPtrCount * 4 / hashSet->ddictPtrTableSize * 3 != 0) + { + nuint err_code = ZSTD_DDictHashSet_expand(hashSet, customMem); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_DDictHashSet_emplaceDDict(hashSet, ddict); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return 0; + } + + /*-************************************************************* + * Context management + ***************************************************************/ + public static nuint ZSTD_sizeof_DCtx(ZSTD_DCtx_s* dctx) + { + if (dctx == null) + return 0; + return (nuint)sizeof(ZSTD_DCtx_s) + + ZSTD_sizeof_DDict(dctx->ddictLocal) + + dctx->inBuffSize + + dctx->outBuffSize; + } + + public static nuint ZSTD_estimateDCtxSize() + { + return (nuint)sizeof(ZSTD_DCtx_s); + } + + private static nuint ZSTD_startingInputLength(ZSTD_format_e format) + { + nuint startingInputLength = (nuint)(format == ZSTD_format_e.ZSTD_f_zstd1 ? 5 : 1); + assert( + format == ZSTD_format_e.ZSTD_f_zstd1 || format == ZSTD_format_e.ZSTD_f_zstd1_magicless + ); + return startingInputLength; + } + + private static void ZSTD_DCtx_resetParameters(ZSTD_DCtx_s* dctx) + { + assert(dctx->streamStage == ZSTD_dStreamStage.zdss_init); + dctx->format = ZSTD_format_e.ZSTD_f_zstd1; + dctx->maxWindowSize = ((uint)1 << 27) + 1; + dctx->outBufferMode = ZSTD_bufferMode_e.ZSTD_bm_buffered; + dctx->forceIgnoreChecksum = ZSTD_forceIgnoreChecksum_e.ZSTD_d_validateChecksum; + dctx->refMultipleDDicts = ZSTD_refMultipleDDicts_e.ZSTD_rmd_refSingleDDict; + dctx->disableHufAsm = 0; + dctx->maxBlockSizeParam = 0; + } + + private static void ZSTD_initDCtx_internal(ZSTD_DCtx_s* dctx) + { + dctx->staticSize = 0; + dctx->ddict = null; + dctx->ddictLocal = null; + dctx->dictEnd = null; + dctx->ddictIsCold = 0; + dctx->dictUses = ZSTD_dictUses_e.ZSTD_dont_use; + dctx->inBuff = null; + dctx->inBuffSize = 0; + dctx->outBuffSize = 0; + dctx->streamStage = ZSTD_dStreamStage.zdss_init; + dctx->noForwardProgress = 0; + dctx->oversizedDuration = 0; + dctx->isFrameDecompression = 1; + dctx->ddictSet = null; + ZSTD_DCtx_resetParameters(dctx); + } + + public static ZSTD_DCtx_s* ZSTD_initStaticDCtx(void* workspace, nuint workspaceSize) + { + ZSTD_DCtx_s* dctx = (ZSTD_DCtx_s*)workspace; + if (((nuint)workspace & 7) != 0) + return null; + if (workspaceSize < (nuint)sizeof(ZSTD_DCtx_s)) + return null; + ZSTD_initDCtx_internal(dctx); + dctx->staticSize = workspaceSize; + dctx->inBuff = (sbyte*)(dctx + 1); + return dctx; + } + + private static ZSTD_DCtx_s* ZSTD_createDCtx_internal(ZSTD_customMem customMem) + { + if (((customMem.customAlloc == null ? 1 : 0) ^ (customMem.customFree == null ? 1 : 0)) != 0) + return null; + { + ZSTD_DCtx_s* dctx = (ZSTD_DCtx_s*)ZSTD_customMalloc( + (nuint)sizeof(ZSTD_DCtx_s), + customMem + ); + if (dctx == null) + return null; + dctx->customMem = customMem; + ZSTD_initDCtx_internal(dctx); + return dctx; + } + } + + public static ZSTD_DCtx_s* ZSTD_createDCtx_advanced(ZSTD_customMem customMem) + { + return ZSTD_createDCtx_internal(customMem); + } + + public static ZSTD_DCtx_s* ZSTD_createDCtx() + { + return ZSTD_createDCtx_internal(ZSTD_defaultCMem); + } + + private static void ZSTD_clearDict(ZSTD_DCtx_s* dctx) + { + ZSTD_freeDDict(dctx->ddictLocal); + dctx->ddictLocal = null; + dctx->ddict = null; + dctx->dictUses = ZSTD_dictUses_e.ZSTD_dont_use; + } + + public static nuint ZSTD_freeDCtx(ZSTD_DCtx_s* dctx) + { + if (dctx == null) + return 0; + if (dctx->staticSize != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + { + ZSTD_customMem cMem = dctx->customMem; + ZSTD_clearDict(dctx); + ZSTD_customFree(dctx->inBuff, cMem); + dctx->inBuff = null; + if (dctx->ddictSet != null) + { + ZSTD_freeDDictHashSet(dctx->ddictSet, cMem); + dctx->ddictSet = null; + } + + ZSTD_customFree(dctx, cMem); + return 0; + } + } + + /* no longer useful */ + public static void ZSTD_copyDCtx(ZSTD_DCtx_s* dstDCtx, ZSTD_DCtx_s* srcDCtx) + { + nuint toCopy = (nuint)((sbyte*)&dstDCtx->inBuff - (sbyte*)dstDCtx); + memcpy(dstDCtx, srcDCtx, (uint)toCopy); + } + + /* Given a dctx with a digested frame params, re-selects the correct ZSTD_DDict based on + * the requested dict ID from the frame. If there exists a reference to the correct ZSTD_DDict, then + * accordingly sets the ddict to be used to decompress the frame. + * + * If no DDict is found, then no action is taken, and the ZSTD_DCtx::ddict remains as-is. + * + * ZSTD_d_refMultipleDDicts must be enabled for this function to be called. + */ + private static void ZSTD_DCtx_selectFrameDDict(ZSTD_DCtx_s* dctx) + { + assert(dctx->refMultipleDDicts != default && dctx->ddictSet != null); + if (dctx->ddict != null) + { + ZSTD_DDict_s* frameDDict = ZSTD_DDictHashSet_getDDict( + dctx->ddictSet, + dctx->fParams.dictID + ); + if (frameDDict != null) + { + ZSTD_clearDict(dctx); + dctx->dictID = dctx->fParams.dictID; + dctx->ddict = frameDDict; + dctx->dictUses = ZSTD_dictUses_e.ZSTD_use_indefinitely; + } + } + } + + /*! ZSTD_isFrame() : + * Tells if the content of `buffer` starts with a valid Frame Identifier. + * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0. + * Note 2 : Legacy Frame Identifiers are considered valid only if Legacy Support is enabled. + * Note 3 : Skippable Frame Identifiers are considered valid. */ + public static uint ZSTD_isFrame(void* buffer, nuint size) + { + if (size < 4) + return 0; + { + uint magic = MEM_readLE32(buffer); + if (magic == 0xFD2FB528) + return 1; + if ((magic & 0xFFFFFFF0) == 0x184D2A50) + return 1; + } + + return 0; + } + + /*! ZSTD_isSkippableFrame() : + * Tells if the content of `buffer` starts with a valid Frame Identifier for a skippable frame. + * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0. + */ + public static uint ZSTD_isSkippableFrame(void* buffer, nuint size) + { + if (size < 4) + return 0; + { + uint magic = MEM_readLE32(buffer); + if ((magic & 0xFFFFFFF0) == 0x184D2A50) + return 1; + } + + return 0; + } + + /** ZSTD_frameHeaderSize_internal() : + * srcSize must be large enough to reach header size fields. + * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless. + * @return : size of the Frame Header + * or an error code, which can be tested with ZSTD_isError() */ + private static nuint ZSTD_frameHeaderSize_internal( + void* src, + nuint srcSize, + ZSTD_format_e format + ) + { + nuint minInputSize = ZSTD_startingInputLength(format); + if (srcSize < minInputSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + { + byte fhd = ((byte*)src)[minInputSize - 1]; + uint dictID = (uint)(fhd & 3); + uint singleSegment = (uint)(fhd >> 5 & 1); + uint fcsId = (uint)(fhd >> 6); + return minInputSize + + (nuint)(singleSegment == 0 ? 1 : 0) + + ZSTD_did_fieldSize[dictID] + + ZSTD_fcs_fieldSize[fcsId] + + (nuint)(singleSegment != 0 && fcsId == 0 ? 1 : 0); + } + } + + /** ZSTD_frameHeaderSize() : + * srcSize must be >= ZSTD_frameHeaderSize_prefix. + * @return : size of the Frame Header, + * or an error code (if srcSize is too small) */ + public static nuint ZSTD_frameHeaderSize(void* src, nuint srcSize) + { + return ZSTD_frameHeaderSize_internal(src, srcSize, ZSTD_format_e.ZSTD_f_zstd1); + } + + /** ZSTD_getFrameHeader_advanced() : + * decode Frame Header, or require larger `srcSize`. + * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless + * @return : 0, `zfhPtr` is correctly filled, + * >0, `srcSize` is too small, value is wanted `srcSize` amount, + ** or an error code, which can be tested using ZSTD_isError() */ + public static nuint ZSTD_getFrameHeader_advanced( + ZSTD_frameHeader* zfhPtr, + void* src, + nuint srcSize, + ZSTD_format_e format + ) + { + byte* ip = (byte*)src; + nuint minInputSize = ZSTD_startingInputLength(format); + if (srcSize > 0) + { + if (src == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + } + + if (srcSize < minInputSize) + { + if (srcSize > 0 && format != ZSTD_format_e.ZSTD_f_zstd1_magicless) + { + /* when receiving less than @minInputSize bytes, + * control these bytes at least correspond to a supported magic number + * in order to error out early if they don't. + **/ + nuint toCopy = 4 < srcSize ? 4 : srcSize; + byte* hbuf = stackalloc byte[4]; + MEM_writeLE32(hbuf, 0xFD2FB528); + assert(src != null); + memcpy(hbuf, src, (uint)toCopy); + if (MEM_readLE32(hbuf) != 0xFD2FB528) + { + MEM_writeLE32(hbuf, 0x184D2A50); + memcpy(hbuf, src, (uint)toCopy); + if ((MEM_readLE32(hbuf) & 0xFFFFFFF0) != 0x184D2A50) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_prefix_unknown)); + } + } + } + + return minInputSize; + } + + *zfhPtr = new ZSTD_frameHeader(); + if (format != ZSTD_format_e.ZSTD_f_zstd1_magicless && MEM_readLE32(src) != 0xFD2FB528) + { + if ((MEM_readLE32(src) & 0xFFFFFFF0) == 0x184D2A50) + { + if (srcSize < 8) + return 8; + *zfhPtr = new ZSTD_frameHeader + { + frameType = ZSTD_frameType_e.ZSTD_skippableFrame, + dictID = MEM_readLE32(src) - 0x184D2A50, + headerSize = 8, + frameContentSize = MEM_readLE32((sbyte*)src + 4), + }; + return 0; + } + + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_prefix_unknown)); + } + + { + nuint fhsize = ZSTD_frameHeaderSize_internal(src, srcSize, format); + if (srcSize < fhsize) + return fhsize; + zfhPtr->headerSize = (uint)fhsize; + } + + { + byte fhdByte = ip[minInputSize - 1]; + nuint pos = minInputSize; + uint dictIDSizeCode = (uint)(fhdByte & 3); + uint checksumFlag = (uint)(fhdByte >> 2 & 1); + uint singleSegment = (uint)(fhdByte >> 5 & 1); + uint fcsID = (uint)(fhdByte >> 6); + ulong windowSize = 0; + uint dictID = 0; + ulong frameContentSize = unchecked(0UL - 1); + if ((fhdByte & 0x08) != 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_unsupported) + ); + } + + if (singleSegment == 0) + { + byte wlByte = ip[pos++]; + uint windowLog = (uint)((wlByte >> 3) + 10); + if (windowLog > (uint)(sizeof(nuint) == 4 ? 30 : 31)) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_windowTooLarge) + ); + } + + windowSize = 1UL << (int)windowLog; + windowSize += (windowSize >> 3) * (ulong)(wlByte & 7); + } + + switch (dictIDSizeCode) + { + default: + assert(0 != 0); + goto case 0; + case 0: + break; + case 1: + dictID = ip[pos]; + pos++; + break; + case 2: + dictID = MEM_readLE16(ip + pos); + pos += 2; + break; + case 3: + dictID = MEM_readLE32(ip + pos); + pos += 4; + break; + } + + switch (fcsID) + { + default: + assert(0 != 0); + goto case 0; + case 0: + if (singleSegment != 0) + frameContentSize = ip[pos]; + break; + case 1: + frameContentSize = (ulong)(MEM_readLE16(ip + pos) + 256); + break; + case 2: + frameContentSize = MEM_readLE32(ip + pos); + break; + case 3: + frameContentSize = MEM_readLE64(ip + pos); + break; + } + + if (singleSegment != 0) + windowSize = frameContentSize; + zfhPtr->frameType = ZSTD_frameType_e.ZSTD_frame; + zfhPtr->frameContentSize = frameContentSize; + zfhPtr->windowSize = windowSize; + zfhPtr->blockSizeMax = (uint)(windowSize < 1 << 17 ? windowSize : 1 << 17); + zfhPtr->dictID = dictID; + zfhPtr->checksumFlag = checksumFlag; + } + + return 0; + } + + /** ZSTD_getFrameHeader() : + * decode Frame Header, or require larger `srcSize`. + * note : this function does not consume input, it only reads it. + * @return : 0, `zfhPtr` is correctly filled, + * >0, `srcSize` is too small, value is wanted `srcSize` amount, + * or an error code, which can be tested using ZSTD_isError() */ + public static nuint ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, void* src, nuint srcSize) + { + return ZSTD_getFrameHeader_advanced(zfhPtr, src, srcSize, ZSTD_format_e.ZSTD_f_zstd1); + } + + /** ZSTD_getFrameContentSize() : + * compatible with legacy mode + * @return : decompressed size of the single frame pointed to be `src` if known, otherwise + * - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined + * - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) */ + public static ulong ZSTD_getFrameContentSize(void* src, nuint srcSize) + { + ZSTD_frameHeader zfh; + if (ZSTD_getFrameHeader(&zfh, src, srcSize) != 0) + return unchecked(0UL - 2); + if (zfh.frameType == ZSTD_frameType_e.ZSTD_skippableFrame) + { + return 0; + } + else + { + return zfh.frameContentSize; + } + } + + private static nuint readSkippableFrameSize(void* src, nuint srcSize) + { + const nuint skippableHeaderSize = 8; + uint sizeU32; + if (srcSize < 8) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + sizeU32 = MEM_readLE32((byte*)src + 4); + if (sizeU32 + 8 < sizeU32) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_unsupported)); + } + + { + nuint skippableSize = skippableHeaderSize + sizeU32; + if (skippableSize > srcSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + return skippableSize; + } + } + + /*! ZSTD_readSkippableFrame() : + * Retrieves content of a skippable frame, and writes it to dst buffer. + * + * The parameter magicVariant will receive the magicVariant that was supplied when the frame was written, + * i.e. magicNumber - ZSTD_MAGIC_SKIPPABLE_START. This can be NULL if the caller is not interested + * in the magicVariant. + * + * Returns an error if destination buffer is not large enough, or if this is not a valid skippable frame. + * + * @return : number of bytes written or a ZSTD error. + */ + public static nuint ZSTD_readSkippableFrame( + void* dst, + nuint dstCapacity, + uint* magicVariant, + void* src, + nuint srcSize + ) + { + if (srcSize < 8) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + { + uint magicNumber = MEM_readLE32(src); + nuint skippableFrameSize = readSkippableFrameSize(src, srcSize); + nuint skippableContentSize = skippableFrameSize - 8; + if (ZSTD_isSkippableFrame(src, srcSize) == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_unsupported) + ); + } + + if (skippableFrameSize < 8 || skippableFrameSize > srcSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if (skippableContentSize > dstCapacity) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (skippableContentSize > 0 && dst != null) + memcpy(dst, (byte*)src + 8, (uint)skippableContentSize); + if (magicVariant != null) + *magicVariant = magicNumber - 0x184D2A50; + return skippableContentSize; + } + } + + /** ZSTD_findDecompressedSize() : + * `srcSize` must be the exact length of some number of ZSTD compressed and/or + * skippable frames + * note: compatible with legacy mode + * @return : decompressed size of the frames contained */ + public static ulong ZSTD_findDecompressedSize(void* src, nuint srcSize) + { + ulong totalDstSize = 0; + while (srcSize >= ZSTD_startingInputLength(ZSTD_format_e.ZSTD_f_zstd1)) + { + uint magicNumber = MEM_readLE32(src); + if ((magicNumber & 0xFFFFFFF0) == 0x184D2A50) + { + nuint skippableSize = readSkippableFrameSize(src, srcSize); + if (ERR_isError(skippableSize)) + return unchecked(0UL - 2); + assert(skippableSize <= srcSize); + src = (byte*)src + skippableSize; + srcSize -= skippableSize; + continue; + } + + { + ulong fcs = ZSTD_getFrameContentSize(src, srcSize); + if (fcs >= unchecked(0UL - 2)) + return fcs; + if (totalDstSize + fcs < totalDstSize) + return unchecked(0UL - 2); + totalDstSize += fcs; + } + + { + nuint frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize); + if (ERR_isError(frameSrcSize)) + return unchecked(0UL - 2); + assert(frameSrcSize <= srcSize); + src = (byte*)src + frameSrcSize; + srcSize -= frameSrcSize; + } + } + + if (srcSize != 0) + return unchecked(0UL - 2); + return totalDstSize; + } + + /** ZSTD_getDecompressedSize() : + * compatible with legacy mode + * @return : decompressed size if known, 0 otherwise + note : 0 can mean any of the following : + - frame content is empty + - decompressed size field is not present in frame header + - frame header unknown / not supported + - frame header not complete (`srcSize` too small) */ + public static ulong ZSTD_getDecompressedSize(void* src, nuint srcSize) + { + ulong ret = ZSTD_getFrameContentSize(src, srcSize); + return ret >= unchecked(0UL - 2) ? 0 : ret; + } + + /** ZSTD_decodeFrameHeader() : + * `headerSize` must be the size provided by ZSTD_frameHeaderSize(). + * If multiple DDict references are enabled, also will choose the correct DDict to use. + * @return : 0 if success, or an error code, which can be tested using ZSTD_isError() */ + private static nuint ZSTD_decodeFrameHeader(ZSTD_DCtx_s* dctx, void* src, nuint headerSize) + { + nuint result = ZSTD_getFrameHeader_advanced(&dctx->fParams, src, headerSize, dctx->format); + if (ERR_isError(result)) + return result; + if (result > 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if ( + dctx->refMultipleDDicts == ZSTD_refMultipleDDicts_e.ZSTD_rmd_refMultipleDDicts + && dctx->ddictSet != null + ) + { + ZSTD_DCtx_selectFrameDDict(dctx); + } + + if (dctx->fParams.dictID != 0 && dctx->dictID != dctx->fParams.dictID) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_wrong)); + } + + dctx->validateChecksum = (uint)( + dctx->fParams.checksumFlag != 0 && dctx->forceIgnoreChecksum == default ? 1 : 0 + ); + if (dctx->validateChecksum != 0) + ZSTD_XXH64_reset(&dctx->xxhState, 0); + dctx->processedCSize += headerSize; + return 0; + } + + private static ZSTD_frameSizeInfo ZSTD_errorFrameSizeInfo(nuint ret) + { + ZSTD_frameSizeInfo frameSizeInfo; + System.Runtime.CompilerServices.Unsafe.SkipInit(out frameSizeInfo); + frameSizeInfo.compressedSize = ret; + frameSizeInfo.decompressedBound = unchecked(0UL - 2); + return frameSizeInfo; + } + + private static ZSTD_frameSizeInfo ZSTD_findFrameSizeInfo( + void* src, + nuint srcSize, + ZSTD_format_e format + ) + { + ZSTD_frameSizeInfo frameSizeInfo; + frameSizeInfo = new ZSTD_frameSizeInfo(); + if ( + format == ZSTD_format_e.ZSTD_f_zstd1 + && srcSize >= 8 + && (MEM_readLE32(src) & 0xFFFFFFF0) == 0x184D2A50 + ) + { + frameSizeInfo.compressedSize = readSkippableFrameSize(src, srcSize); + assert( + ERR_isError(frameSizeInfo.compressedSize) || frameSizeInfo.compressedSize <= srcSize + ); + return frameSizeInfo; + } + else + { + byte* ip = (byte*)src; + byte* ipstart = ip; + nuint remainingSize = srcSize; + nuint nbBlocks = 0; + ZSTD_frameHeader zfh; + { + nuint ret = ZSTD_getFrameHeader_advanced(&zfh, src, srcSize, format); + if (ERR_isError(ret)) + return ZSTD_errorFrameSizeInfo(ret); + if (ret > 0) + return ZSTD_errorFrameSizeInfo( + unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)) + ); + } + + ip += zfh.headerSize; + remainingSize -= zfh.headerSize; + while (true) + { + blockProperties_t blockProperties; + nuint cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); + if (ERR_isError(cBlockSize)) + return ZSTD_errorFrameSizeInfo(cBlockSize); + if (ZSTD_blockHeaderSize + cBlockSize > remainingSize) + return ZSTD_errorFrameSizeInfo( + unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)) + ); + ip += ZSTD_blockHeaderSize + cBlockSize; + remainingSize -= ZSTD_blockHeaderSize + cBlockSize; + nbBlocks++; + if (blockProperties.lastBlock != 0) + break; + } + + if (zfh.checksumFlag != 0) + { + if (remainingSize < 4) + return ZSTD_errorFrameSizeInfo( + unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)) + ); + ip += 4; + } + + frameSizeInfo.nbBlocks = nbBlocks; + frameSizeInfo.compressedSize = (nuint)(ip - ipstart); + frameSizeInfo.decompressedBound = + zfh.frameContentSize != unchecked(0UL - 1) + ? zfh.frameContentSize + : (ulong)nbBlocks * zfh.blockSizeMax; + return frameSizeInfo; + } + } + + private static nuint ZSTD_findFrameCompressedSize_advanced( + void* src, + nuint srcSize, + ZSTD_format_e format + ) + { + ZSTD_frameSizeInfo frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize, format); + return frameSizeInfo.compressedSize; + } + + /** ZSTD_findFrameCompressedSize() : + * See docs in zstd.h + * Note: compatible with legacy mode */ + public static nuint ZSTD_findFrameCompressedSize(void* src, nuint srcSize) + { + return ZSTD_findFrameCompressedSize_advanced(src, srcSize, ZSTD_format_e.ZSTD_f_zstd1); + } + + /** ZSTD_decompressBound() : + * compatible with legacy mode + * `src` must point to the start of a ZSTD frame or a skippable frame + * `srcSize` must be at least as large as the frame contained + * @return : the maximum decompressed size of the compressed source + */ + public static ulong ZSTD_decompressBound(void* src, nuint srcSize) + { + ulong bound = 0; + while (srcSize > 0) + { + ZSTD_frameSizeInfo frameSizeInfo = ZSTD_findFrameSizeInfo( + src, + srcSize, + ZSTD_format_e.ZSTD_f_zstd1 + ); + nuint compressedSize = frameSizeInfo.compressedSize; + ulong decompressedBound = frameSizeInfo.decompressedBound; + if (ERR_isError(compressedSize) || decompressedBound == unchecked(0UL - 2)) + return unchecked(0UL - 2); + assert(srcSize >= compressedSize); + src = (byte*)src + compressedSize; + srcSize -= compressedSize; + bound += decompressedBound; + } + + return bound; + } + + /*! ZSTD_decompressionMargin() : + * Zstd supports in-place decompression, where the input and output buffers overlap. + * In this case, the output buffer must be at least (Margin + Output_Size) bytes large, + * and the input buffer must be at the end of the output buffer. + * + * _______________________ Output Buffer ________________________ + * | | + * | ____ Input Buffer ____| + * | | | + * v v v + * |---------------------------------------|-----------|----------| + * ^ ^ ^ + * |___________________ Output_Size ___________________|_ Margin _| + * + * NOTE: See also ZSTD_DECOMPRESSION_MARGIN(). + * NOTE: This applies only to single-pass decompression through ZSTD_decompress() or + * ZSTD_decompressDCtx(). + * NOTE: This function supports multi-frame input. + * + * @param src The compressed frame(s) + * @param srcSize The size of the compressed frame(s) + * @returns The decompression margin or an error that can be checked with ZSTD_isError(). + */ + public static nuint ZSTD_decompressionMargin(void* src, nuint srcSize) + { + nuint margin = 0; + uint maxBlockSize = 0; + while (srcSize > 0) + { + ZSTD_frameSizeInfo frameSizeInfo = ZSTD_findFrameSizeInfo( + src, + srcSize, + ZSTD_format_e.ZSTD_f_zstd1 + ); + nuint compressedSize = frameSizeInfo.compressedSize; + ulong decompressedBound = frameSizeInfo.decompressedBound; + ZSTD_frameHeader zfh; + { + nuint err_code = ZSTD_getFrameHeader(&zfh, src, srcSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (ERR_isError(compressedSize) || decompressedBound == unchecked(0UL - 2)) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + if (zfh.frameType == ZSTD_frameType_e.ZSTD_frame) + { + margin += zfh.headerSize; + margin += (nuint)(zfh.checksumFlag != 0 ? 4 : 0); + margin += 3 * frameSizeInfo.nbBlocks; + maxBlockSize = maxBlockSize > zfh.blockSizeMax ? maxBlockSize : zfh.blockSizeMax; + } + else + { + assert(zfh.frameType == ZSTD_frameType_e.ZSTD_skippableFrame); + margin += compressedSize; + } + + assert(srcSize >= compressedSize); + src = (byte*)src + compressedSize; + srcSize -= compressedSize; + } + + margin += maxBlockSize; + return margin; + } + + /** ZSTD_insertBlock() : + * insert `src` block into `dctx` history. Useful to track uncompressed blocks. */ + public static nuint ZSTD_insertBlock(ZSTD_DCtx_s* dctx, void* blockStart, nuint blockSize) + { + ZSTD_checkContinuity(dctx, blockStart, blockSize); + dctx->previousDstEnd = (sbyte*)blockStart + blockSize; + return blockSize; + } + + private static nuint ZSTD_copyRawBlock(void* dst, nuint dstCapacity, void* src, nuint srcSize) + { + if (srcSize > dstCapacity) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (dst == null) + { + if (srcSize == 0) + return 0; + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstBuffer_null)); + } + + memmove(dst, src, srcSize); + return srcSize; + } + + private static nuint ZSTD_setRleBlock(void* dst, nuint dstCapacity, byte b, nuint regenSize) + { + if (regenSize > dstCapacity) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (dst == null) + { + if (regenSize == 0) + return 0; + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstBuffer_null)); + } + + memset(dst, b, (uint)regenSize); + return regenSize; + } + + private static void ZSTD_DCtx_trace_end( + ZSTD_DCtx_s* dctx, + ulong uncompressedSize, + ulong compressedSize, + int streaming + ) { } + + /*! ZSTD_decompressFrame() : + * @dctx must be properly initialized + * will update *srcPtr and *srcSizePtr, + * to make *srcPtr progress by one frame. */ + private static nuint ZSTD_decompressFrame( + ZSTD_DCtx_s* dctx, + void* dst, + nuint dstCapacity, + void** srcPtr, + nuint* srcSizePtr + ) + { + byte* istart = (byte*)*srcPtr; + byte* ip = istart; + byte* ostart = (byte*)dst; + byte* oend = dstCapacity != 0 ? ostart + dstCapacity : ostart; + byte* op = ostart; + nuint remainingSrcSize = *srcSizePtr; + if ( + remainingSrcSize + < (nuint)(dctx->format == ZSTD_format_e.ZSTD_f_zstd1 ? 6 : 2) + ZSTD_blockHeaderSize + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + { + nuint frameHeaderSize = ZSTD_frameHeaderSize_internal( + ip, + (nuint)(dctx->format == ZSTD_format_e.ZSTD_f_zstd1 ? 5 : 1), + dctx->format + ); + if (ERR_isError(frameHeaderSize)) + return frameHeaderSize; + if (remainingSrcSize < frameHeaderSize + ZSTD_blockHeaderSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + { + nuint err_code = ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ip += frameHeaderSize; + remainingSrcSize -= frameHeaderSize; + } + + if (dctx->maxBlockSizeParam != 0) + dctx->fParams.blockSizeMax = + dctx->fParams.blockSizeMax < (uint)dctx->maxBlockSizeParam + ? dctx->fParams.blockSizeMax + : (uint)dctx->maxBlockSizeParam; + while (true) + { + byte* oBlockEnd = oend; + nuint decodedSize; + blockProperties_t blockProperties; + nuint cBlockSize = ZSTD_getcBlockSize(ip, remainingSrcSize, &blockProperties); + if (ERR_isError(cBlockSize)) + return cBlockSize; + ip += ZSTD_blockHeaderSize; + remainingSrcSize -= ZSTD_blockHeaderSize; + if (cBlockSize > remainingSrcSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if (ip >= op && ip < oBlockEnd) + { + oBlockEnd = op + (ip - op); + } + + switch (blockProperties.blockType) + { + case blockType_e.bt_compressed: + assert(dctx->isFrameDecompression == 1); + decodedSize = ZSTD_decompressBlock_internal( + dctx, + op, + (nuint)(oBlockEnd - op), + ip, + cBlockSize, + streaming_operation.not_streaming + ); + break; + case blockType_e.bt_raw: + decodedSize = ZSTD_copyRawBlock(op, (nuint)(oend - op), ip, cBlockSize); + break; + case blockType_e.bt_rle: + decodedSize = ZSTD_setRleBlock( + op, + (nuint)(oBlockEnd - op), + *ip, + blockProperties.origSize + ); + break; + case blockType_e.bt_reserved: + default: + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + { + nuint err_code = decodedSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (dctx->validateChecksum != 0) + { + ZSTD_XXH64_update(&dctx->xxhState, op, decodedSize); + } + + if (decodedSize != 0) + { + op += decodedSize; + } + + assert(ip != null); + ip += cBlockSize; + remainingSrcSize -= cBlockSize; + if (blockProperties.lastBlock != 0) + break; + } + + if (dctx->fParams.frameContentSize != unchecked(0UL - 1)) + { + if ((ulong)(op - ostart) != dctx->fParams.frameContentSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + } + + if (dctx->fParams.checksumFlag != 0) + { + if (remainingSrcSize < 4) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_checksum_wrong)); + } + + if (dctx->forceIgnoreChecksum == default) + { + uint checkCalc = (uint)ZSTD_XXH64_digest(&dctx->xxhState); + uint checkRead; + checkRead = MEM_readLE32(ip); + if (checkRead != checkCalc) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_checksum_wrong)); + } + } + + ip += 4; + remainingSrcSize -= 4; + } + + ZSTD_DCtx_trace_end(dctx, (ulong)(op - ostart), (ulong)(ip - istart), 0); + *srcPtr = ip; + *srcSizePtr = remainingSrcSize; + return (nuint)(op - ostart); + } + + private static nuint ZSTD_decompressMultiFrame( + ZSTD_DCtx_s* dctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + void* dict, + nuint dictSize, + ZSTD_DDict_s* ddict + ) + { + void* dststart = dst; + int moreThan1Frame = 0; + assert(dict == null || ddict == null); + if (ddict != null) + { + dict = ZSTD_DDict_dictContent(ddict); + dictSize = ZSTD_DDict_dictSize(ddict); + } + + while (srcSize >= ZSTD_startingInputLength(dctx->format)) + { + if (dctx->format == ZSTD_format_e.ZSTD_f_zstd1 && srcSize >= 4) + { + uint magicNumber = MEM_readLE32(src); + if ((magicNumber & 0xFFFFFFF0) == 0x184D2A50) + { + /* skippable frame detected : skip it */ + nuint skippableSize = readSkippableFrameSize(src, srcSize); + { + nuint err_code = skippableSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(skippableSize <= srcSize); + src = (byte*)src + skippableSize; + srcSize -= skippableSize; + continue; + } + } + + if (ddict != null) + { + /* we were called from ZSTD_decompress_usingDDict */ + nuint err_code = ZSTD_decompressBegin_usingDDict(dctx, ddict); + if (ERR_isError(err_code)) + { + return err_code; + } + } + else + { + /* this will initialize correctly with no dict if dict == NULL, so + * use this in all cases but ddict */ + nuint err_code = ZSTD_decompressBegin_usingDict(dctx, dict, dictSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + ZSTD_checkContinuity(dctx, dst, dstCapacity); + { + nuint res = ZSTD_decompressFrame(dctx, dst, dstCapacity, &src, &srcSize); + if ( + ZSTD_getErrorCode(res) == ZSTD_ErrorCode.ZSTD_error_prefix_unknown + && moreThan1Frame == 1 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if (ERR_isError(res)) + return res; + assert(res <= dstCapacity); + if (res != 0) + dst = (byte*)dst + res; + dstCapacity -= res; + } + + moreThan1Frame = 1; + } + + if (srcSize != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + return (nuint)((byte*)dst - (byte*)dststart); + } + + /*! ZSTD_decompress_usingDict() : + * Decompression using a known Dictionary. + * Dictionary must be identical to the one used during compression. + * Note : This function loads the dictionary, resulting in significant startup delay. + * It's intended for a dictionary used only once. + * Note : When `dict == NULL || dictSize < 8` no dictionary is used. */ + public static nuint ZSTD_decompress_usingDict( + ZSTD_DCtx_s* dctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + void* dict, + nuint dictSize + ) + { + return ZSTD_decompressMultiFrame( + dctx, + dst, + dstCapacity, + src, + srcSize, + dict, + dictSize, + null + ); + } + + private static ZSTD_DDict_s* ZSTD_getDDict(ZSTD_DCtx_s* dctx) + { + switch (dctx->dictUses) + { + default: + assert(0 != 0); + goto case ZSTD_dictUses_e.ZSTD_dont_use; + case ZSTD_dictUses_e.ZSTD_dont_use: + ZSTD_clearDict(dctx); + return null; + case ZSTD_dictUses_e.ZSTD_use_indefinitely: + return dctx->ddict; + case ZSTD_dictUses_e.ZSTD_use_once: + dctx->dictUses = ZSTD_dictUses_e.ZSTD_dont_use; + return dctx->ddict; + } + } + + /*! ZSTD_decompressDCtx() : + * Same as ZSTD_decompress(), + * requires an allocated ZSTD_DCtx. + * Compatible with sticky parameters (see below). + */ + public static nuint ZSTD_decompressDCtx( + ZSTD_DCtx_s* dctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + return ZSTD_decompress_usingDDict( + dctx, + dst, + dstCapacity, + src, + srcSize, + ZSTD_getDDict(dctx) + ); + } + + /*! ZSTD_decompress() : + * `compressedSize` : must be the _exact_ size of some number of compressed and/or skippable frames. + * Multiple compressed frames can be decompressed at once with this method. + * The result will be the concatenation of all decompressed frames, back to back. + * `dstCapacity` is an upper bound of originalSize to regenerate. + * First frame's decompressed size can be extracted using ZSTD_getFrameContentSize(). + * If maximum upper bound isn't known, prefer using streaming mode to decompress data. + * @return : the number of bytes decompressed into `dst` (<= `dstCapacity`), + * or an errorCode if it fails (which can be tested using ZSTD_isError()). */ + public static nuint ZSTD_decompress(void* dst, nuint dstCapacity, void* src, nuint srcSize) + { + nuint regenSize; + ZSTD_DCtx_s* dctx = ZSTD_createDCtx_internal(ZSTD_defaultCMem); + if (dctx == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + regenSize = ZSTD_decompressDCtx(dctx, dst, dstCapacity, src, srcSize); + ZSTD_freeDCtx(dctx); + return regenSize; + } + + /*-************************************** + * Advanced Streaming Decompression API + * Bufferless and synchronous + ****************************************/ + public static nuint ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx_s* dctx) + { + return dctx->expected; + } + + /** + * Similar to ZSTD_nextSrcSizeToDecompress(), but when a block input can be streamed, we + * allow taking a partial block as the input. Currently only raw uncompressed blocks can + * be streamed. + * + * For blocks that can be streamed, this allows us to reduce the latency until we produce + * output, and avoid copying the input. + * + * @param inputSize - The total amount of input that the caller currently has. + */ + private static nuint ZSTD_nextSrcSizeToDecompressWithInputSize( + ZSTD_DCtx_s* dctx, + nuint inputSize + ) + { + if ( + !( + dctx->stage == ZSTD_dStage.ZSTDds_decompressBlock + || dctx->stage == ZSTD_dStage.ZSTDds_decompressLastBlock + ) + ) + return dctx->expected; + if (dctx->bType != blockType_e.bt_raw) + return dctx->expected; + return inputSize <= 1 ? 1 + : inputSize <= dctx->expected ? inputSize + : dctx->expected; + } + + public static ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx_s* dctx) + { + switch (dctx->stage) + { + default: + assert(0 != 0); + goto case ZSTD_dStage.ZSTDds_getFrameHeaderSize; + case ZSTD_dStage.ZSTDds_getFrameHeaderSize: + case ZSTD_dStage.ZSTDds_decodeFrameHeader: + return ZSTD_nextInputType_e.ZSTDnit_frameHeader; + case ZSTD_dStage.ZSTDds_decodeBlockHeader: + return ZSTD_nextInputType_e.ZSTDnit_blockHeader; + case ZSTD_dStage.ZSTDds_decompressBlock: + return ZSTD_nextInputType_e.ZSTDnit_block; + case ZSTD_dStage.ZSTDds_decompressLastBlock: + return ZSTD_nextInputType_e.ZSTDnit_lastBlock; + case ZSTD_dStage.ZSTDds_checkChecksum: + return ZSTD_nextInputType_e.ZSTDnit_checksum; + case ZSTD_dStage.ZSTDds_decodeSkippableHeader: + case ZSTD_dStage.ZSTDds_skipFrame: + return ZSTD_nextInputType_e.ZSTDnit_skippableFrame; + } + } + + private static int ZSTD_isSkipFrame(ZSTD_DCtx_s* dctx) + { + return dctx->stage == ZSTD_dStage.ZSTDds_skipFrame ? 1 : 0; + } + + /** ZSTD_decompressContinue() : + * srcSize : must be the exact nb of bytes expected (see ZSTD_nextSrcSizeToDecompress()) + * @return : nb of bytes generated into `dst` (necessarily <= `dstCapacity) + * or an error code, which can be tested using ZSTD_isError() */ + public static nuint ZSTD_decompressContinue( + ZSTD_DCtx_s* dctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + if (srcSize != ZSTD_nextSrcSizeToDecompressWithInputSize(dctx, srcSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + ZSTD_checkContinuity(dctx, dst, dstCapacity); + dctx->processedCSize += srcSize; + switch (dctx->stage) + { + case ZSTD_dStage.ZSTDds_getFrameHeaderSize: + assert(src != null); + if (dctx->format == ZSTD_format_e.ZSTD_f_zstd1) + { + assert(srcSize >= 4); + if ((MEM_readLE32(src) & 0xFFFFFFF0) == 0x184D2A50) + { + memcpy(dctx->headerBuffer, src, (uint)srcSize); + dctx->expected = 8 - srcSize; + dctx->stage = ZSTD_dStage.ZSTDds_decodeSkippableHeader; + return 0; + } + } + + dctx->headerSize = ZSTD_frameHeaderSize_internal(src, srcSize, dctx->format); + if (ERR_isError(dctx->headerSize)) + return dctx->headerSize; + memcpy(dctx->headerBuffer, src, (uint)srcSize); + dctx->expected = dctx->headerSize - srcSize; + dctx->stage = ZSTD_dStage.ZSTDds_decodeFrameHeader; + return 0; + case ZSTD_dStage.ZSTDds_decodeFrameHeader: + assert(src != null); + memcpy(dctx->headerBuffer + (dctx->headerSize - srcSize), src, (uint)srcSize); + + { + nuint err_code = ZSTD_decodeFrameHeader( + dctx, + dctx->headerBuffer, + dctx->headerSize + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + dctx->expected = ZSTD_blockHeaderSize; + dctx->stage = ZSTD_dStage.ZSTDds_decodeBlockHeader; + return 0; + case ZSTD_dStage.ZSTDds_decodeBlockHeader: + { + blockProperties_t bp; + nuint cBlockSize = ZSTD_getcBlockSize(src, ZSTD_blockHeaderSize, &bp); + if (ERR_isError(cBlockSize)) + return cBlockSize; + if (cBlockSize > dctx->fParams.blockSizeMax) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + dctx->expected = cBlockSize; + dctx->bType = bp.blockType; + dctx->rleSize = bp.origSize; + if (cBlockSize != 0) + { + dctx->stage = + bp.lastBlock != 0 + ? ZSTD_dStage.ZSTDds_decompressLastBlock + : ZSTD_dStage.ZSTDds_decompressBlock; + return 0; + } + + if (bp.lastBlock != 0) + { + if (dctx->fParams.checksumFlag != 0) + { + dctx->expected = 4; + dctx->stage = ZSTD_dStage.ZSTDds_checkChecksum; + } + else + { + dctx->expected = 0; + dctx->stage = ZSTD_dStage.ZSTDds_getFrameHeaderSize; + } + } + else + { + dctx->expected = ZSTD_blockHeaderSize; + dctx->stage = ZSTD_dStage.ZSTDds_decodeBlockHeader; + } + + return 0; + } + + case ZSTD_dStage.ZSTDds_decompressLastBlock: + case ZSTD_dStage.ZSTDds_decompressBlock: + { + nuint rSize; + switch (dctx->bType) + { + case blockType_e.bt_compressed: + assert(dctx->isFrameDecompression == 1); + rSize = ZSTD_decompressBlock_internal( + dctx, + dst, + dstCapacity, + src, + srcSize, + streaming_operation.is_streaming + ); + dctx->expected = 0; + break; + case blockType_e.bt_raw: + assert(srcSize <= dctx->expected); + rSize = ZSTD_copyRawBlock(dst, dstCapacity, src, srcSize); + + { + nuint err_code = rSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(rSize == srcSize); + dctx->expected -= rSize; + break; + case blockType_e.bt_rle: + rSize = ZSTD_setRleBlock(dst, dstCapacity, *(byte*)src, dctx->rleSize); + dctx->expected = 0; + break; + case blockType_e.bt_reserved: + default: + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + { + nuint err_code = rSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (rSize > dctx->fParams.blockSizeMax) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + dctx->decodedSize += rSize; + if (dctx->validateChecksum != 0) + ZSTD_XXH64_update(&dctx->xxhState, dst, rSize); + dctx->previousDstEnd = (sbyte*)dst + rSize; + if (dctx->expected > 0) + { + return rSize; + } + + if (dctx->stage == ZSTD_dStage.ZSTDds_decompressLastBlock) + { + if ( + dctx->fParams.frameContentSize != unchecked(0UL - 1) + && dctx->decodedSize != dctx->fParams.frameContentSize + ) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + if (dctx->fParams.checksumFlag != 0) + { + dctx->expected = 4; + dctx->stage = ZSTD_dStage.ZSTDds_checkChecksum; + } + else + { + ZSTD_DCtx_trace_end(dctx, dctx->decodedSize, dctx->processedCSize, 1); + dctx->expected = 0; + dctx->stage = ZSTD_dStage.ZSTDds_getFrameHeaderSize; + } + } + else + { + dctx->stage = ZSTD_dStage.ZSTDds_decodeBlockHeader; + dctx->expected = ZSTD_blockHeaderSize; + } + + return rSize; + } + + case ZSTD_dStage.ZSTDds_checkChecksum: + assert(srcSize == 4); + + { + if (dctx->validateChecksum != 0) + { + uint h32 = (uint)ZSTD_XXH64_digest(&dctx->xxhState); + uint check32 = MEM_readLE32(src); + if (check32 != h32) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_checksum_wrong) + ); + } + } + + ZSTD_DCtx_trace_end(dctx, dctx->decodedSize, dctx->processedCSize, 1); + dctx->expected = 0; + dctx->stage = ZSTD_dStage.ZSTDds_getFrameHeaderSize; + return 0; + } + + case ZSTD_dStage.ZSTDds_decodeSkippableHeader: + assert(src != null); + assert(srcSize <= 8); + assert(dctx->format != ZSTD_format_e.ZSTD_f_zstd1_magicless); + memcpy(dctx->headerBuffer + (8 - srcSize), src, (uint)srcSize); + dctx->expected = MEM_readLE32(dctx->headerBuffer + 4); + dctx->stage = ZSTD_dStage.ZSTDds_skipFrame; + return 0; + case ZSTD_dStage.ZSTDds_skipFrame: + dctx->expected = 0; + dctx->stage = ZSTD_dStage.ZSTDds_getFrameHeaderSize; + return 0; + default: + assert(0 != 0); + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + } + + private static nuint ZSTD_refDictContent(ZSTD_DCtx_s* dctx, void* dict, nuint dictSize) + { + dctx->dictEnd = dctx->previousDstEnd; + dctx->virtualStart = + (sbyte*)dict - ((sbyte*)dctx->previousDstEnd - (sbyte*)dctx->prefixStart); + dctx->prefixStart = dict; + dctx->previousDstEnd = (sbyte*)dict + dictSize; + return 0; + } + + /*! ZSTD_loadDEntropy() : + * dict : must point at beginning of a valid zstd dictionary. + * @return : size of entropy tables read */ + private static nuint ZSTD_loadDEntropy( + ZSTD_entropyDTables_t* entropy, + void* dict, + nuint dictSize + ) + { + byte* dictPtr = (byte*)dict; + byte* dictEnd = dictPtr + dictSize; + if (dictSize <= 8) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + assert(MEM_readLE32(dict) == 0xEC30A437); + dictPtr += 8; + { + /* use fse tables as temporary workspace; implies fse tables are grouped together */ + void* workspace = &entropy->LLTable; + nuint workspaceSize = (nuint)( + sizeof(ZSTD_seqSymbol) * 513 + + sizeof(ZSTD_seqSymbol) * 257 + + sizeof(ZSTD_seqSymbol) * 513 + ); + nuint hSize = HUF_readDTableX2_wksp( + entropy->hufTable, + dictPtr, + (nuint)(dictEnd - dictPtr), + workspace, + workspaceSize, + 0 + ); + if (ERR_isError(hSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + dictPtr += hSize; + } + + { + short* offcodeNCount = stackalloc short[32]; + uint offcodeMaxValue = 31, + offcodeLog; + nuint offcodeHeaderSize = FSE_readNCount( + offcodeNCount, + &offcodeMaxValue, + &offcodeLog, + dictPtr, + (nuint)(dictEnd - dictPtr) + ); + if (ERR_isError(offcodeHeaderSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if (offcodeMaxValue > 31) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if (offcodeLog > 8) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + ZSTD_buildFSETable( + &entropy->OFTable.e0, + offcodeNCount, + offcodeMaxValue, + OF_base, + OF_bits, + offcodeLog, + entropy->workspace, + sizeof(uint) * 157, + 0 + ); + dictPtr += offcodeHeaderSize; + } + + { + short* matchlengthNCount = stackalloc short[53]; + uint matchlengthMaxValue = 52, + matchlengthLog; + nuint matchlengthHeaderSize = FSE_readNCount( + matchlengthNCount, + &matchlengthMaxValue, + &matchlengthLog, + dictPtr, + (nuint)(dictEnd - dictPtr) + ); + if (ERR_isError(matchlengthHeaderSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if (matchlengthMaxValue > 52) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if (matchlengthLog > 9) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + ZSTD_buildFSETable( + &entropy->MLTable.e0, + matchlengthNCount, + matchlengthMaxValue, + ML_base, + ML_bits, + matchlengthLog, + entropy->workspace, + sizeof(uint) * 157, + 0 + ); + dictPtr += matchlengthHeaderSize; + } + + { + short* litlengthNCount = stackalloc short[36]; + uint litlengthMaxValue = 35, + litlengthLog; + nuint litlengthHeaderSize = FSE_readNCount( + litlengthNCount, + &litlengthMaxValue, + &litlengthLog, + dictPtr, + (nuint)(dictEnd - dictPtr) + ); + if (ERR_isError(litlengthHeaderSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if (litlengthMaxValue > 35) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + if (litlengthLog > 9) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + ZSTD_buildFSETable( + &entropy->LLTable.e0, + litlengthNCount, + litlengthMaxValue, + LL_base, + LL_bits, + litlengthLog, + entropy->workspace, + sizeof(uint) * 157, + 0 + ); + dictPtr += litlengthHeaderSize; + } + + if (dictPtr + 12 > dictEnd) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + { + int i; + nuint dictContentSize = (nuint)(dictEnd - (dictPtr + 12)); + for (i = 0; i < 3; i++) + { + uint rep = MEM_readLE32(dictPtr); + dictPtr += 4; + if (rep == 0 || rep > dictContentSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + entropy->rep[i] = rep; + } + } + + return (nuint)(dictPtr - (byte*)dict); + } + + private static nuint ZSTD_decompress_insertDictionary( + ZSTD_DCtx_s* dctx, + void* dict, + nuint dictSize + ) + { + if (dictSize < 8) + return ZSTD_refDictContent(dctx, dict, dictSize); + { + uint magic = MEM_readLE32(dict); + if (magic != 0xEC30A437) + { + return ZSTD_refDictContent(dctx, dict, dictSize); + } + } + + dctx->dictID = MEM_readLE32((sbyte*)dict + 4); + { + nuint eSize = ZSTD_loadDEntropy(&dctx->entropy, dict, dictSize); + if (ERR_isError(eSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + dict = (sbyte*)dict + eSize; + dictSize -= eSize; + } + + dctx->litEntropy = dctx->fseEntropy = 1; + return ZSTD_refDictContent(dctx, dict, dictSize); + } + + public static nuint ZSTD_decompressBegin(ZSTD_DCtx_s* dctx) + { + assert(dctx != null); + dctx->expected = ZSTD_startingInputLength(dctx->format); + dctx->stage = ZSTD_dStage.ZSTDds_getFrameHeaderSize; + dctx->processedCSize = 0; + dctx->decodedSize = 0; + dctx->previousDstEnd = null; + dctx->prefixStart = null; + dctx->virtualStart = null; + dctx->dictEnd = null; + dctx->entropy.hufTable[0] = 12 * 0x1000001; + dctx->litEntropy = dctx->fseEntropy = 0; + dctx->dictID = 0; + dctx->bType = blockType_e.bt_reserved; + dctx->isFrameDecompression = 1; + memcpy(dctx->entropy.rep, repStartValue, sizeof(uint) * 3); + dctx->LLTptr = &dctx->entropy.LLTable.e0; + dctx->MLTptr = &dctx->entropy.MLTable.e0; + dctx->OFTptr = &dctx->entropy.OFTable.e0; + dctx->HUFptr = dctx->entropy.hufTable; + return 0; + } + + public static nuint ZSTD_decompressBegin_usingDict( + ZSTD_DCtx_s* dctx, + void* dict, + nuint dictSize + ) + { + { + nuint err_code = ZSTD_decompressBegin(dctx); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (dict != null && dictSize != 0) + if (ERR_isError(ZSTD_decompress_insertDictionary(dctx, dict, dictSize))) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted)); + } + + return 0; + } + + /* ====== ZSTD_DDict ====== */ + public static nuint ZSTD_decompressBegin_usingDDict(ZSTD_DCtx_s* dctx, ZSTD_DDict_s* ddict) + { + assert(dctx != null); + if (ddict != null) + { + sbyte* dictStart = (sbyte*)ZSTD_DDict_dictContent(ddict); + nuint dictSize = ZSTD_DDict_dictSize(ddict); + void* dictEnd = dictStart + dictSize; + dctx->ddictIsCold = dctx->dictEnd != dictEnd ? 1 : 0; + } + + { + nuint err_code = ZSTD_decompressBegin(dctx); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (ddict != null) + { + ZSTD_copyDDictParameters(dctx, ddict); + } + + return 0; + } + + /*! ZSTD_getDictID_fromDict() : + * Provides the dictID stored within dictionary. + * if @return == 0, the dictionary is not conformant with Zstandard specification. + * It can still be loaded, but as a content-only dictionary. */ + public static uint ZSTD_getDictID_fromDict(void* dict, nuint dictSize) + { + if (dictSize < 8) + return 0; + if (MEM_readLE32(dict) != 0xEC30A437) + return 0; + return MEM_readLE32((sbyte*)dict + 4); + } + + /*! ZSTD_getDictID_fromFrame() : + * Provides the dictID required to decompress frame stored within `src`. + * If @return == 0, the dictID could not be decoded. + * This could for one of the following reasons : + * - The frame does not require a dictionary (most common case). + * - The frame was built with dictID intentionally removed. + * Needed dictionary is a hidden piece of information. + * Note : this use case also happens when using a non-conformant dictionary. + * - `srcSize` is too small, and as a result, frame header could not be decoded. + * Note : possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`. + * - This is not a Zstandard frame. + * When identifying the exact failure cause, it's possible to use + * ZSTD_getFrameHeader(), which will provide a more precise error code. */ + public static uint ZSTD_getDictID_fromFrame(void* src, nuint srcSize) + { + ZSTD_frameHeader zfp = new ZSTD_frameHeader + { + frameContentSize = 0, + windowSize = 0, + blockSizeMax = 0, + frameType = ZSTD_frameType_e.ZSTD_frame, + headerSize = 0, + dictID = 0, + checksumFlag = 0, + _reserved1 = 0, + _reserved2 = 0, + }; + nuint hError = ZSTD_getFrameHeader(&zfp, src, srcSize); + if (ERR_isError(hError)) + return 0; + return zfp.dictID; + } + + /*! ZSTD_decompress_usingDDict() : + * Decompression using a pre-digested Dictionary + * Use dictionary without significant overhead. */ + public static nuint ZSTD_decompress_usingDDict( + ZSTD_DCtx_s* dctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + ZSTD_DDict_s* ddict + ) + { + return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, null, 0, ddict); + } + + /*===================================== + * Streaming decompression + *====================================*/ + public static ZSTD_DCtx_s* ZSTD_createDStream() + { + return ZSTD_createDCtx_internal(ZSTD_defaultCMem); + } + + public static ZSTD_DCtx_s* ZSTD_initStaticDStream(void* workspace, nuint workspaceSize) + { + return ZSTD_initStaticDCtx(workspace, workspaceSize); + } + + public static ZSTD_DCtx_s* ZSTD_createDStream_advanced(ZSTD_customMem customMem) + { + return ZSTD_createDCtx_internal(customMem); + } + + public static nuint ZSTD_freeDStream(ZSTD_DCtx_s* zds) + { + return ZSTD_freeDCtx(zds); + } + + /* *** Initialization *** */ + public static nuint ZSTD_DStreamInSize() + { + return (nuint)(1 << 17) + ZSTD_blockHeaderSize; + } + + public static nuint ZSTD_DStreamOutSize() + { + return 1 << 17; + } + + /*! ZSTD_DCtx_loadDictionary_advanced() : + * Same as ZSTD_DCtx_loadDictionary(), + * but gives direct control over + * how to load the dictionary (by copy ? by reference ?) + * and how to interpret it (automatic ? force raw mode ? full mode only ?). */ + public static nuint ZSTD_DCtx_loadDictionary_advanced( + ZSTD_DCtx_s* dctx, + void* dict, + nuint dictSize, + ZSTD_dictLoadMethod_e dictLoadMethod, + ZSTD_dictContentType_e dictContentType + ) + { + if (dctx->streamStage != ZSTD_dStreamStage.zdss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + ZSTD_clearDict(dctx); + if (dict != null && dictSize != 0) + { + dctx->ddictLocal = ZSTD_createDDict_advanced( + dict, + dictSize, + dictLoadMethod, + dictContentType, + dctx->customMem + ); + if (dctx->ddictLocal == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + dctx->ddict = dctx->ddictLocal; + dctx->dictUses = ZSTD_dictUses_e.ZSTD_use_indefinitely; + } + + return 0; + } + + /*! ZSTD_DCtx_loadDictionary_byReference() : + * Same as ZSTD_DCtx_loadDictionary(), + * but references `dict` content instead of copying it into `dctx`. + * This saves memory if `dict` remains around., + * However, it's imperative that `dict` remains accessible (and unmodified) while being used, so it must outlive decompression. */ + public static nuint ZSTD_DCtx_loadDictionary_byReference( + ZSTD_DCtx_s* dctx, + void* dict, + nuint dictSize + ) + { + return ZSTD_DCtx_loadDictionary_advanced( + dctx, + dict, + dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef, + ZSTD_dictContentType_e.ZSTD_dct_auto + ); + } + + /*! ZSTD_DCtx_loadDictionary() : Requires v1.4.0+ + * Create an internal DDict from dict buffer, to be used to decompress all future frames. + * The dictionary remains valid for all future frames, until explicitly invalidated, or + * a new dictionary is loaded. + * @result : 0, or an error code (which can be tested with ZSTD_isError()). + * Special : Adding a NULL (or 0-size) dictionary invalidates any previous dictionary, + * meaning "return to no-dictionary mode". + * Note 1 : Loading a dictionary involves building tables, + * which has a non-negligible impact on CPU usage and latency. + * It's recommended to "load once, use many times", to amortize the cost + * Note 2 :`dict` content will be copied internally, so `dict` can be released after loading. + * Use ZSTD_DCtx_loadDictionary_byReference() to reference dictionary content instead. + * Note 3 : Use ZSTD_DCtx_loadDictionary_advanced() to take control of + * how dictionary content is loaded and interpreted. + */ + public static nuint ZSTD_DCtx_loadDictionary(ZSTD_DCtx_s* dctx, void* dict, nuint dictSize) + { + return ZSTD_DCtx_loadDictionary_advanced( + dctx, + dict, + dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byCopy, + ZSTD_dictContentType_e.ZSTD_dct_auto + ); + } + + /*! ZSTD_DCtx_refPrefix_advanced() : + * Same as ZSTD_DCtx_refPrefix(), but gives finer control over + * how to interpret prefix content (automatic ? force raw mode (default) ? full mode only ?) */ + public static nuint ZSTD_DCtx_refPrefix_advanced( + ZSTD_DCtx_s* dctx, + void* prefix, + nuint prefixSize, + ZSTD_dictContentType_e dictContentType + ) + { + { + nuint err_code = ZSTD_DCtx_loadDictionary_advanced( + dctx, + prefix, + prefixSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef, + dictContentType + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + dctx->dictUses = ZSTD_dictUses_e.ZSTD_use_once; + return 0; + } + + /*! ZSTD_DCtx_refPrefix() : Requires v1.4.0+ + * Reference a prefix (single-usage dictionary) to decompress next frame. + * This is the reverse operation of ZSTD_CCtx_refPrefix(), + * and must use the same prefix as the one used during compression. + * Prefix is **only used once**. Reference is discarded at end of frame. + * End of frame is reached when ZSTD_decompressStream() returns 0. + * @result : 0, or an error code (which can be tested with ZSTD_isError()). + * Note 1 : Adding any prefix (including NULL) invalidates any previously set prefix or dictionary + * Note 2 : Prefix buffer is referenced. It **must** outlive decompression. + * Prefix buffer must remain unmodified up to the end of frame, + * reached when ZSTD_decompressStream() returns 0. + * Note 3 : By default, the prefix is treated as raw content (ZSTD_dct_rawContent). + * Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode (Experimental section) + * Note 4 : Referencing a raw content prefix has almost no cpu nor memory cost. + * A full dictionary is more costly, as it requires building tables. + */ + public static nuint ZSTD_DCtx_refPrefix(ZSTD_DCtx_s* dctx, void* prefix, nuint prefixSize) + { + return ZSTD_DCtx_refPrefix_advanced( + dctx, + prefix, + prefixSize, + ZSTD_dictContentType_e.ZSTD_dct_rawContent + ); + } + + /* ZSTD_initDStream_usingDict() : + * return : expected size, aka ZSTD_startingInputLength(). + * this function cannot fail */ + public static nuint ZSTD_initDStream_usingDict(ZSTD_DCtx_s* zds, void* dict, nuint dictSize) + { + { + nuint err_code = ZSTD_DCtx_reset(zds, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_DCtx_loadDictionary(zds, dict, dictSize); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return ZSTD_startingInputLength(zds->format); + } + + /* note : this variant can't fail */ + public static nuint ZSTD_initDStream(ZSTD_DCtx_s* zds) + { + { + nuint err_code = ZSTD_DCtx_reset(zds, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_DCtx_refDDict(zds, null); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return ZSTD_startingInputLength(zds->format); + } + + /* ZSTD_initDStream_usingDDict() : + * ddict will just be referenced, and must outlive decompression session + * this function cannot fail */ + public static nuint ZSTD_initDStream_usingDDict(ZSTD_DCtx_s* dctx, ZSTD_DDict_s* ddict) + { + { + nuint err_code = ZSTD_DCtx_reset(dctx, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + { + nuint err_code = ZSTD_DCtx_refDDict(dctx, ddict); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return ZSTD_startingInputLength(dctx->format); + } + + /* ZSTD_resetDStream() : + * return : expected size, aka ZSTD_startingInputLength(). + * this function cannot fail */ + public static nuint ZSTD_resetDStream(ZSTD_DCtx_s* dctx) + { + { + nuint err_code = ZSTD_DCtx_reset(dctx, ZSTD_ResetDirective.ZSTD_reset_session_only); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return ZSTD_startingInputLength(dctx->format); + } + + /*! ZSTD_DCtx_refDDict() : Requires v1.4.0+ + * Reference a prepared dictionary, to be used to decompress next frames. + * The dictionary remains active for decompression of future frames using same DCtx. + * + * If called with ZSTD_d_refMultipleDDicts enabled, repeated calls of this function + * will store the DDict references in a table, and the DDict used for decompression + * will be determined at decompression time, as per the dict ID in the frame. + * The memory for the table is allocated on the first call to refDDict, and can be + * freed with ZSTD_freeDCtx(). + * + * If called with ZSTD_d_refMultipleDDicts disabled (the default), only one dictionary + * will be managed, and referencing a dictionary effectively "discards" any previous one. + * + * @result : 0, or an error code (which can be tested with ZSTD_isError()). + * Special: referencing a NULL DDict means "return to no-dictionary mode". + * Note 2 : DDict is just referenced, its lifetime must outlive its usage from DCtx. + */ + public static nuint ZSTD_DCtx_refDDict(ZSTD_DCtx_s* dctx, ZSTD_DDict_s* ddict) + { + if (dctx->streamStage != ZSTD_dStreamStage.zdss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + ZSTD_clearDict(dctx); + if (ddict != null) + { + dctx->ddict = ddict; + dctx->dictUses = ZSTD_dictUses_e.ZSTD_use_indefinitely; + if (dctx->refMultipleDDicts == ZSTD_refMultipleDDicts_e.ZSTD_rmd_refMultipleDDicts) + { + if (dctx->ddictSet == null) + { + dctx->ddictSet = ZSTD_createDDictHashSet(dctx->customMem); + if (dctx->ddictSet == null) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation) + ); + } + } + + assert(dctx->staticSize == 0); + { + nuint err_code = ZSTD_DDictHashSet_addDDict( + dctx->ddictSet, + ddict, + dctx->customMem + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + } + } + + return 0; + } + + /* ZSTD_DCtx_setMaxWindowSize() : + * note : no direct equivalence in ZSTD_DCtx_setParameter, + * since this version sets windowSize, and the other sets windowLog */ + public static nuint ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx_s* dctx, nuint maxWindowSize) + { + ZSTD_bounds bounds = ZSTD_dParam_getBounds(ZSTD_dParameter.ZSTD_d_windowLogMax); + nuint min = (nuint)1 << bounds.lowerBound; + nuint max = (nuint)1 << bounds.upperBound; + if (dctx->streamStage != ZSTD_dStreamStage.zdss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + if (maxWindowSize < min) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + if (maxWindowSize > max) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound)); + } + + dctx->maxWindowSize = maxWindowSize; + return 0; + } + + /*! ZSTD_DCtx_setFormat() : + * This function is REDUNDANT. Prefer ZSTD_DCtx_setParameter(). + * Instruct the decoder context about what kind of data to decode next. + * This instruction is mandatory to decode data without a fully-formed header, + * such ZSTD_f_zstd1_magicless for example. + * @return : 0, or an error code (which can be tested using ZSTD_isError()). */ + public static nuint ZSTD_DCtx_setFormat(ZSTD_DCtx_s* dctx, ZSTD_format_e format) + { + return ZSTD_DCtx_setParameter(dctx, ZSTD_dParameter.ZSTD_d_experimentalParam1, (int)format); + } + + /*! ZSTD_dParam_getBounds() : + * All parameters must belong to an interval with lower and upper bounds, + * otherwise they will either trigger an error or be automatically clamped. + * @return : a structure, ZSTD_bounds, which contains + * - an error status field, which must be tested using ZSTD_isError() + * - both lower and upper bounds, inclusive + */ + public static ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam) + { + ZSTD_bounds bounds = new ZSTD_bounds + { + error = 0, + lowerBound = 0, + upperBound = 0, + }; + switch (dParam) + { + case ZSTD_dParameter.ZSTD_d_windowLogMax: + bounds.lowerBound = 10; + bounds.upperBound = sizeof(nuint) == 4 ? 30 : 31; + return bounds; + case ZSTD_dParameter.ZSTD_d_experimentalParam1: + bounds.lowerBound = (int)ZSTD_format_e.ZSTD_f_zstd1; + bounds.upperBound = (int)ZSTD_format_e.ZSTD_f_zstd1_magicless; + return bounds; + case ZSTD_dParameter.ZSTD_d_experimentalParam2: + bounds.lowerBound = (int)ZSTD_bufferMode_e.ZSTD_bm_buffered; + bounds.upperBound = (int)ZSTD_bufferMode_e.ZSTD_bm_stable; + return bounds; + case ZSTD_dParameter.ZSTD_d_experimentalParam3: + bounds.lowerBound = (int)ZSTD_forceIgnoreChecksum_e.ZSTD_d_validateChecksum; + bounds.upperBound = (int)ZSTD_forceIgnoreChecksum_e.ZSTD_d_ignoreChecksum; + return bounds; + case ZSTD_dParameter.ZSTD_d_experimentalParam4: + bounds.lowerBound = (int)ZSTD_refMultipleDDicts_e.ZSTD_rmd_refSingleDDict; + bounds.upperBound = (int)ZSTD_refMultipleDDicts_e.ZSTD_rmd_refMultipleDDicts; + return bounds; + case ZSTD_dParameter.ZSTD_d_experimentalParam5: + bounds.lowerBound = 0; + bounds.upperBound = 1; + return bounds; + case ZSTD_dParameter.ZSTD_d_experimentalParam6: + bounds.lowerBound = 1 << 10; + bounds.upperBound = 1 << 17; + return bounds; + default: + break; + } + + bounds.error = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported)); + return bounds; + } + + /* ZSTD_dParam_withinBounds: + * @return 1 if value is within dParam bounds, + * 0 otherwise */ + private static int ZSTD_dParam_withinBounds(ZSTD_dParameter dParam, int value) + { + ZSTD_bounds bounds = ZSTD_dParam_getBounds(dParam); + if (ERR_isError(bounds.error)) + return 0; + if (value < bounds.lowerBound) + return 0; + if (value > bounds.upperBound) + return 0; + return 1; + } + + /*! ZSTD_DCtx_getParameter() : + * Get the requested decompression parameter value, selected by enum ZSTD_dParameter, + * and store it into int* value. + * @return : 0, or an error code (which can be tested with ZSTD_isError()). + */ + public static nuint ZSTD_DCtx_getParameter(ZSTD_DCtx_s* dctx, ZSTD_dParameter param, int* value) + { + switch (param) + { + case ZSTD_dParameter.ZSTD_d_windowLogMax: + *value = (int)ZSTD_highbit32((uint)dctx->maxWindowSize); + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam1: + *value = (int)dctx->format; + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam2: + *value = (int)dctx->outBufferMode; + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam3: + *value = (int)dctx->forceIgnoreChecksum; + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam4: + *value = (int)dctx->refMultipleDDicts; + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam5: + *value = dctx->disableHufAsm; + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam6: + *value = dctx->maxBlockSizeParam; + return 0; + default: + break; + } + + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported)); + } + + /*! ZSTD_DCtx_setParameter() : + * Set one compression parameter, selected by enum ZSTD_dParameter. + * All parameters have valid bounds. Bounds can be queried using ZSTD_dParam_getBounds(). + * Providing a value beyond bound will either clamp it, or trigger an error (depending on parameter). + * Setting a parameter is only possible during frame initialization (before starting decompression). + * @return : 0, or an error code (which can be tested using ZSTD_isError()). + */ + public static nuint ZSTD_DCtx_setParameter(ZSTD_DCtx_s* dctx, ZSTD_dParameter dParam, int value) + { + if (dctx->streamStage != ZSTD_dStreamStage.zdss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + switch (dParam) + { + case ZSTD_dParameter.ZSTD_d_windowLogMax: + if (value == 0) + value = 27; + + { + if (ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_windowLogMax, value) == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + } + + dctx->maxWindowSize = (nuint)1 << value; + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam1: + { + if ( + ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam1, value) + == 0 + ) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + } + + dctx->format = (ZSTD_format_e)value; + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam2: + { + if ( + ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam2, value) + == 0 + ) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + } + + dctx->outBufferMode = (ZSTD_bufferMode_e)value; + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam3: + { + if ( + ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam3, value) + == 0 + ) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + } + + dctx->forceIgnoreChecksum = (ZSTD_forceIgnoreChecksum_e)value; + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam4: + { + if ( + ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam4, value) + == 0 + ) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + } + + if (dctx->staticSize != 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported) + ); + } + + dctx->refMultipleDDicts = (ZSTD_refMultipleDDicts_e)value; + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam5: + { + if ( + ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam5, value) + == 0 + ) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + } + + dctx->disableHufAsm = value != 0 ? 1 : 0; + return 0; + case ZSTD_dParameter.ZSTD_d_experimentalParam6: + if (value != 0) + { + if ( + ZSTD_dParam_withinBounds(ZSTD_dParameter.ZSTD_d_experimentalParam6, value) + == 0 + ) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_outOfBound) + ); + } + } + + dctx->maxBlockSizeParam = value; + return 0; + default: + break; + } + + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_parameter_unsupported)); + } + + /*! ZSTD_DCtx_reset() : + * Return a DCtx to clean state. + * Session and parameters can be reset jointly or separately. + * Parameters can only be reset when no active frame is being decompressed. + * @return : 0, or an error code, which can be tested with ZSTD_isError() + */ + public static nuint ZSTD_DCtx_reset(ZSTD_DCtx_s* dctx, ZSTD_ResetDirective reset) + { + if ( + reset == ZSTD_ResetDirective.ZSTD_reset_session_only + || reset == ZSTD_ResetDirective.ZSTD_reset_session_and_parameters + ) + { + dctx->streamStage = ZSTD_dStreamStage.zdss_init; + dctx->noForwardProgress = 0; + dctx->isFrameDecompression = 1; + } + + if ( + reset == ZSTD_ResetDirective.ZSTD_reset_parameters + || reset == ZSTD_ResetDirective.ZSTD_reset_session_and_parameters + ) + { + if (dctx->streamStage != ZSTD_dStreamStage.zdss_init) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + ZSTD_clearDict(dctx); + ZSTD_DCtx_resetParameters(dctx); + } + + return 0; + } + + public static nuint ZSTD_sizeof_DStream(ZSTD_DCtx_s* dctx) + { + return ZSTD_sizeof_DCtx(dctx); + } + + private static nuint ZSTD_decodingBufferSize_internal( + ulong windowSize, + ulong frameContentSize, + nuint blockSizeMax + ) + { + nuint blockSize = + (nuint)(windowSize < 1 << 17 ? windowSize : 1 << 17) < blockSizeMax + ? (nuint)(windowSize < 1 << 17 ? windowSize : 1 << 17) + : blockSizeMax; + /* We need blockSize + WILDCOPY_OVERLENGTH worth of buffer so that if a block + * ends at windowSize + WILDCOPY_OVERLENGTH + 1 bytes, we can start writing + * the block at the beginning of the output buffer, and maintain a full window. + * + * We need another blockSize worth of buffer so that we can store split + * literals at the end of the block without overwriting the extDict window. + */ + ulong neededRBSize = windowSize + blockSize * 2 + 32 * 2; + ulong neededSize = frameContentSize < neededRBSize ? frameContentSize : neededRBSize; + nuint minRBSize = (nuint)neededSize; + if (minRBSize != neededSize) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_windowTooLarge) + ); + } + + return minRBSize; + } + + /*===== Buffer-less streaming decompression functions =====*/ + public static nuint ZSTD_decodingBufferSize_min(ulong windowSize, ulong frameContentSize) + { + return ZSTD_decodingBufferSize_internal(windowSize, frameContentSize, 1 << 17); + } + + public static nuint ZSTD_estimateDStreamSize(nuint windowSize) + { + nuint blockSize = windowSize < 1 << 17 ? windowSize : 1 << 17; + /* no block can be larger */ + nuint inBuffSize = blockSize; + nuint outBuffSize = ZSTD_decodingBufferSize_min(windowSize, unchecked(0UL - 1)); + return ZSTD_estimateDCtxSize() + inBuffSize + outBuffSize; + } + + public static nuint ZSTD_estimateDStreamSize_fromFrame(void* src, nuint srcSize) + { + /* note : should be user-selectable, but requires an additional parameter (or a dctx) */ + uint windowSizeMax = 1U << (sizeof(nuint) == 4 ? 30 : 31); + ZSTD_frameHeader zfh; + nuint err = ZSTD_getFrameHeader(&zfh, src, srcSize); + if (ERR_isError(err)) + return err; + if (err > 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if (zfh.windowSize > windowSizeMax) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_windowTooLarge) + ); + } + + return ZSTD_estimateDStreamSize((nuint)zfh.windowSize); + } + + /* ***** Decompression ***** */ + private static int ZSTD_DCtx_isOverflow( + ZSTD_DCtx_s* zds, + nuint neededInBuffSize, + nuint neededOutBuffSize + ) + { + return zds->inBuffSize + zds->outBuffSize >= (neededInBuffSize + neededOutBuffSize) * 3 + ? 1 + : 0; + } + + private static void ZSTD_DCtx_updateOversizedDuration( + ZSTD_DCtx_s* zds, + nuint neededInBuffSize, + nuint neededOutBuffSize + ) + { + if (ZSTD_DCtx_isOverflow(zds, neededInBuffSize, neededOutBuffSize) != 0) + zds->oversizedDuration++; + else + zds->oversizedDuration = 0; + } + + private static int ZSTD_DCtx_isOversizedTooLong(ZSTD_DCtx_s* zds) + { + return zds->oversizedDuration >= 128 ? 1 : 0; + } + + /* Checks that the output buffer hasn't changed if ZSTD_obm_stable is used. */ + private static nuint ZSTD_checkOutBuffer(ZSTD_DCtx_s* zds, ZSTD_outBuffer_s* output) + { + ZSTD_outBuffer_s expect = zds->expectedOutBuffer; + if (zds->outBufferMode != ZSTD_bufferMode_e.ZSTD_bm_stable) + return 0; + if (zds->streamStage == ZSTD_dStreamStage.zdss_init) + return 0; + if (expect.dst == output->dst && expect.pos == output->pos && expect.size == output->size) + return 0; + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstBuffer_wrong)); + } + + /* Calls ZSTD_decompressContinue() with the right parameters for ZSTD_decompressStream() + * and updates the stage and the output buffer state. This call is extracted so it can be + * used both when reading directly from the ZSTD_inBuffer, and in buffered input mode. + * NOTE: You must break after calling this function since the streamStage is modified. + */ + private static nuint ZSTD_decompressContinueStream( + ZSTD_DCtx_s* zds, + sbyte** op, + sbyte* oend, + void* src, + nuint srcSize + ) + { + int isSkipFrame = ZSTD_isSkipFrame(zds); + if (zds->outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered) + { + nuint dstSize = isSkipFrame != 0 ? 0 : zds->outBuffSize - zds->outStart; + nuint decodedSize = ZSTD_decompressContinue( + zds, + zds->outBuff + zds->outStart, + dstSize, + src, + srcSize + ); + { + nuint err_code = decodedSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (decodedSize == 0 && isSkipFrame == 0) + { + zds->streamStage = ZSTD_dStreamStage.zdss_read; + } + else + { + zds->outEnd = zds->outStart + decodedSize; + zds->streamStage = ZSTD_dStreamStage.zdss_flush; + } + } + else + { + /* Write directly into the output buffer */ + nuint dstSize = isSkipFrame != 0 ? 0 : (nuint)(oend - *op); + nuint decodedSize = ZSTD_decompressContinue(zds, *op, dstSize, src, srcSize); + { + nuint err_code = decodedSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + *op += decodedSize; + zds->streamStage = ZSTD_dStreamStage.zdss_read; + assert(*op <= oend); + assert(zds->outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable); + } + + return 0; + } + + /*! ZSTD_decompressStream() : + * Streaming decompression function. + * Call repetitively to consume full input updating it as necessary. + * Function will update both input and output `pos` fields exposing current state via these fields: + * - `input.pos < input.size`, some input remaining and caller should provide remaining input + * on the next call. + * - `output.pos < output.size`, decoder flushed internal output buffer. + * - `output.pos == output.size`, unflushed data potentially present in the internal buffers, + * check ZSTD_decompressStream() @return value, + * if > 0, invoke it again to flush remaining data to output. + * Note : with no additional input, amount of data flushed <= ZSTD_BLOCKSIZE_MAX. + * + * @return : 0 when a frame is completely decoded and fully flushed, + * or an error code, which can be tested using ZSTD_isError(), + * or any other value > 0, which means there is some decoding or flushing to do to complete current frame. + * + * Note: when an operation returns with an error code, the @zds state may be left in undefined state. + * It's UB to invoke `ZSTD_decompressStream()` on such a state. + * In order to re-use such a state, it must be first reset, + * which can be done explicitly (`ZSTD_DCtx_reset()`), + * or is implied for operations starting some new decompression job (`ZSTD_initDStream`, `ZSTD_decompressDCtx()`, `ZSTD_decompress_usingDict()`) + */ + public static nuint ZSTD_decompressStream( + ZSTD_DCtx_s* zds, + ZSTD_outBuffer_s* output, + ZSTD_inBuffer_s* input + ) + { + sbyte* src = (sbyte*)input->src; + sbyte* istart = input->pos != 0 ? src + input->pos : src; + sbyte* iend = input->size != 0 ? src + input->size : src; + sbyte* ip = istart; + sbyte* dst = (sbyte*)output->dst; + sbyte* ostart = output->pos != 0 ? dst + output->pos : dst; + sbyte* oend = output->size != 0 ? dst + output->size : dst; + sbyte* op = ostart; + uint someMoreWork = 1; + assert(zds != null); + if (input->pos > input->size) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if (output->pos > output->size) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + { + nuint err_code = ZSTD_checkOutBuffer(zds, output); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + while (someMoreWork != 0) + { + switch (zds->streamStage) + { + case ZSTD_dStreamStage.zdss_init: + zds->streamStage = ZSTD_dStreamStage.zdss_loadHeader; + zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; + zds->hostageByte = 0; + zds->expectedOutBuffer = *output; + goto case ZSTD_dStreamStage.zdss_loadHeader; + case ZSTD_dStreamStage.zdss_loadHeader: + { + nuint hSize = ZSTD_getFrameHeader_advanced( + &zds->fParams, + zds->headerBuffer, + zds->lhSize, + zds->format + ); + if (zds->refMultipleDDicts != default && zds->ddictSet != null) + { + ZSTD_DCtx_selectFrameDDict(zds); + } + + if (ERR_isError(hSize)) + { + return hSize; + } + + if (hSize != 0) + { + /* if hSize!=0, hSize > zds->lhSize */ + nuint toLoad = hSize - zds->lhSize; + nuint remainingInput = (nuint)(iend - ip); + assert(iend >= ip); + if (toLoad > remainingInput) + { + if (remainingInput > 0) + { + memcpy( + zds->headerBuffer + zds->lhSize, + ip, + (uint)remainingInput + ); + zds->lhSize += remainingInput; + } + + input->pos = input->size; + { + /* check first few bytes */ + nuint err_code = ZSTD_getFrameHeader_advanced( + &zds->fParams, + zds->headerBuffer, + zds->lhSize, + zds->format + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + return ( + (nuint)(zds->format == ZSTD_format_e.ZSTD_f_zstd1 ? 6 : 2) + > hSize + ? (nuint)( + zds->format == ZSTD_format_e.ZSTD_f_zstd1 ? 6 : 2 + ) + : hSize + ) + - zds->lhSize + + ZSTD_blockHeaderSize; + } + + assert(ip != null); + memcpy(zds->headerBuffer + zds->lhSize, ip, (uint)toLoad); + zds->lhSize = hSize; + ip += toLoad; + break; + } + } + + if ( + zds->fParams.frameContentSize != unchecked(0UL - 1) + && zds->fParams.frameType != ZSTD_frameType_e.ZSTD_skippableFrame + && (nuint)(oend - op) >= zds->fParams.frameContentSize + ) + { + nuint cSize = ZSTD_findFrameCompressedSize_advanced( + istart, + (nuint)(iend - istart), + zds->format + ); + if (cSize <= (nuint)(iend - istart)) + { + /* shortcut : using single-pass mode */ + nuint decompressedSize = ZSTD_decompress_usingDDict( + zds, + op, + (nuint)(oend - op), + istart, + cSize, + ZSTD_getDDict(zds) + ); + if (ERR_isError(decompressedSize)) + return decompressedSize; + assert(istart != null); + ip = istart + cSize; + op = op != null ? op + decompressedSize : op; + zds->expected = 0; + zds->streamStage = ZSTD_dStreamStage.zdss_init; + someMoreWork = 0; + break; + } + } + + if ( + zds->outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_stable + && zds->fParams.frameType != ZSTD_frameType_e.ZSTD_skippableFrame + && zds->fParams.frameContentSize != unchecked(0UL - 1) + && (nuint)(oend - op) < zds->fParams.frameContentSize + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + { + nuint err_code = ZSTD_decompressBegin_usingDDict(zds, ZSTD_getDDict(zds)); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if ( + zds->format == ZSTD_format_e.ZSTD_f_zstd1 + && (MEM_readLE32(zds->headerBuffer) & 0xFFFFFFF0) == 0x184D2A50 + ) + { + zds->expected = MEM_readLE32(zds->headerBuffer + 4); + zds->stage = ZSTD_dStage.ZSTDds_skipFrame; + } + else + { + { + nuint err_code = ZSTD_decodeFrameHeader( + zds, + zds->headerBuffer, + zds->lhSize + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + zds->expected = ZSTD_blockHeaderSize; + zds->stage = ZSTD_dStage.ZSTDds_decodeBlockHeader; + } + + zds->fParams.windowSize = + zds->fParams.windowSize > 1U << 10 ? zds->fParams.windowSize : 1U << 10; + if (zds->fParams.windowSize > zds->maxWindowSize) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_frameParameter_windowTooLarge) + ); + } + + if (zds->maxBlockSizeParam != 0) + zds->fParams.blockSizeMax = + zds->fParams.blockSizeMax < (uint)zds->maxBlockSizeParam + ? zds->fParams.blockSizeMax + : (uint)zds->maxBlockSizeParam; + + { + /* frame checksum */ + nuint neededInBuffSize = + zds->fParams.blockSizeMax > 4 ? zds->fParams.blockSizeMax : 4; + nuint neededOutBuffSize = + zds->outBufferMode == ZSTD_bufferMode_e.ZSTD_bm_buffered + ? ZSTD_decodingBufferSize_internal( + zds->fParams.windowSize, + zds->fParams.frameContentSize, + zds->fParams.blockSizeMax + ) + : 0; + ZSTD_DCtx_updateOversizedDuration(zds, neededInBuffSize, neededOutBuffSize); + { + int tooSmall = + zds->inBuffSize < neededInBuffSize + || zds->outBuffSize < neededOutBuffSize + ? 1 + : 0; + int tooLarge = ZSTD_DCtx_isOversizedTooLong(zds); + if (tooSmall != 0 || tooLarge != 0) + { + nuint bufferSize = neededInBuffSize + neededOutBuffSize; + if (zds->staticSize != 0) + { + assert(zds->staticSize >= (nuint)sizeof(ZSTD_DCtx_s)); + if (bufferSize > zds->staticSize - (nuint)sizeof(ZSTD_DCtx_s)) + { + return unchecked( + (nuint)( + -(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation + ) + ); + } + } + else + { + ZSTD_customFree(zds->inBuff, zds->customMem); + zds->inBuffSize = 0; + zds->outBuffSize = 0; + zds->inBuff = (sbyte*)ZSTD_customMalloc( + bufferSize, + zds->customMem + ); + if (zds->inBuff == null) + { + return unchecked( + (nuint)( + -(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation + ) + ); + } + } + + zds->inBuffSize = neededInBuffSize; + zds->outBuff = zds->inBuff + zds->inBuffSize; + zds->outBuffSize = neededOutBuffSize; + } + } + } + + zds->streamStage = ZSTD_dStreamStage.zdss_read; + goto case ZSTD_dStreamStage.zdss_read; + case ZSTD_dStreamStage.zdss_read: + { + nuint neededInSize = ZSTD_nextSrcSizeToDecompressWithInputSize( + zds, + (nuint)(iend - ip) + ); + if (neededInSize == 0) + { + zds->streamStage = ZSTD_dStreamStage.zdss_init; + someMoreWork = 0; + break; + } + + if ((nuint)(iend - ip) >= neededInSize) + { + { + nuint err_code = ZSTD_decompressContinueStream( + zds, + &op, + oend, + ip, + neededInSize + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + assert(ip != null); + ip += neededInSize; + break; + } + } + + if (ip == iend) + { + someMoreWork = 0; + break; + } + + zds->streamStage = ZSTD_dStreamStage.zdss_load; + goto case ZSTD_dStreamStage.zdss_load; + case ZSTD_dStreamStage.zdss_load: + { + nuint neededInSize = ZSTD_nextSrcSizeToDecompress(zds); + nuint toLoad = neededInSize - zds->inPos; + int isSkipFrame = ZSTD_isSkipFrame(zds); + nuint loadedSize; + assert( + neededInSize + == ZSTD_nextSrcSizeToDecompressWithInputSize(zds, (nuint)(iend - ip)) + ); + if (isSkipFrame != 0) + { + loadedSize = toLoad < (nuint)(iend - ip) ? toLoad : (nuint)(iend - ip); + } + else + { + if (toLoad > zds->inBuffSize - zds->inPos) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + loadedSize = ZSTD_limitCopy( + zds->inBuff + zds->inPos, + toLoad, + ip, + (nuint)(iend - ip) + ); + } + + if (loadedSize != 0) + { + ip += loadedSize; + zds->inPos += loadedSize; + } + + if (loadedSize < toLoad) + { + someMoreWork = 0; + break; + } + + zds->inPos = 0; + { + nuint err_code = ZSTD_decompressContinueStream( + zds, + &op, + oend, + zds->inBuff, + neededInSize + ); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + break; + } + + case ZSTD_dStreamStage.zdss_flush: + { + nuint toFlushSize = zds->outEnd - zds->outStart; + nuint flushedSize = ZSTD_limitCopy( + op, + (nuint)(oend - op), + zds->outBuff + zds->outStart, + toFlushSize + ); + op = op != null ? op + flushedSize : op; + zds->outStart += flushedSize; + if (flushedSize == toFlushSize) + { + zds->streamStage = ZSTD_dStreamStage.zdss_read; + if ( + zds->outBuffSize < zds->fParams.frameContentSize + && zds->outStart + zds->fParams.blockSizeMax > zds->outBuffSize + ) + { + zds->outStart = zds->outEnd = 0; + } + + break; + } + } + + someMoreWork = 0; + break; + default: + assert(0 != 0); + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + } + + input->pos = (nuint)(ip - (sbyte*)input->src); + output->pos = (nuint)(op - (sbyte*)output->dst); + zds->expectedOutBuffer = *output; + if (ip == istart && op == ostart) + { + zds->noForwardProgress++; + if (zds->noForwardProgress >= 16) + { + if (op == oend) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_noForwardProgress_destFull) + ); + } + + if (ip == iend) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_noForwardProgress_inputEmpty) + ); + } + + assert(0 != 0); + } + } + else + { + zds->noForwardProgress = 0; + } + + { + nuint nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zds); + if (nextSrcSizeHint == 0) + { + if (zds->outEnd == zds->outStart) + { + if (zds->hostageByte != 0) + { + if (input->pos >= input->size) + { + zds->streamStage = ZSTD_dStreamStage.zdss_read; + return 1; + } + + input->pos++; + } + + return 0; + } + + if (zds->hostageByte == 0) + { + input->pos--; + zds->hostageByte = 1; + } + + return 1; + } + + nextSrcSizeHint += + ZSTD_blockHeaderSize + * (nuint)(ZSTD_nextInputType(zds) == ZSTD_nextInputType_e.ZSTDnit_block ? 1 : 0); + assert(zds->inPos <= nextSrcSizeHint); + nextSrcSizeHint -= zds->inPos; + return nextSrcSizeHint; + } + } + + /*! ZSTD_decompressStream_simpleArgs() : + * Same as ZSTD_decompressStream(), + * but using only integral types as arguments. + * This can be helpful for binders from dynamic languages + * which have troubles handling structures containing memory pointers. + */ + public static nuint ZSTD_decompressStream_simpleArgs( + ZSTD_DCtx_s* dctx, + void* dst, + nuint dstCapacity, + nuint* dstPos, + void* src, + nuint srcSize, + nuint* srcPos + ) + { + ZSTD_outBuffer_s output; + ZSTD_inBuffer_s input; + output.dst = dst; + output.size = dstCapacity; + output.pos = *dstPos; + input.src = src; + input.size = srcSize; + input.pos = *srcPos; + { + nuint cErr = ZSTD_decompressStream(dctx, &output, &input); + *dstPos = output.pos; + *srcPos = input.pos; + return cErr; + } + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompressBlock.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompressBlock.cs new file mode 100644 index 00000000..fd4168b5 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompressBlock.cs @@ -0,0 +1,3218 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /*_******************************************************* + * Memory operations + **********************************************************/ + private static void ZSTD_copy4(void* dst, void* src) + { + memcpy(dst, src, 4); + } + + /*-************************************************************* + * Block decoding + ***************************************************************/ + private static nuint ZSTD_blockSizeMax(ZSTD_DCtx_s* dctx) + { + nuint blockSizeMax = dctx->isFrameDecompression != 0 ? dctx->fParams.blockSizeMax : 1 << 17; + assert(blockSizeMax <= 1 << 17); + return blockSizeMax; + } + + /*! ZSTD_getcBlockSize() : + * Provides the size of compressed block from block header `src` */ + private static nuint ZSTD_getcBlockSize(void* src, nuint srcSize, blockProperties_t* bpPtr) + { + if (srcSize < ZSTD_blockHeaderSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + { + uint cBlockHeader = MEM_readLE24(src); + uint cSize = cBlockHeader >> 3; + bpPtr->lastBlock = cBlockHeader & 1; + bpPtr->blockType = (blockType_e)(cBlockHeader >> 1 & 3); + bpPtr->origSize = cSize; + if (bpPtr->blockType == blockType_e.bt_rle) + return 1; + if (bpPtr->blockType == blockType_e.bt_reserved) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + return cSize; + } + } + + /* Allocate buffer for literals, either overlapping current dst, or split between dst and litExtraBuffer, or stored entirely within litExtraBuffer */ + private static void ZSTD_allocateLiteralsBuffer( + ZSTD_DCtx_s* dctx, + void* dst, + nuint dstCapacity, + nuint litSize, + streaming_operation streaming, + nuint expectedWriteSize, + uint splitImmediately + ) + { + nuint blockSizeMax = ZSTD_blockSizeMax(dctx); + assert(litSize <= blockSizeMax); + assert(dctx->isFrameDecompression != 0 || streaming == streaming_operation.not_streaming); + assert(expectedWriteSize <= blockSizeMax); + if ( + streaming == streaming_operation.not_streaming + && dstCapacity > blockSizeMax + 32 + litSize + 32 + ) + { + dctx->litBuffer = (byte*)dst + blockSizeMax + 32; + dctx->litBufferEnd = dctx->litBuffer + litSize; + dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_in_dst; + } + else if (litSize <= 1 << 16) + { + dctx->litBuffer = dctx->litExtraBuffer; + dctx->litBufferEnd = dctx->litBuffer + litSize; + dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst; + } + else + { + assert(blockSizeMax > 1 << 16); + if (splitImmediately != 0) + { + dctx->litBuffer = (byte*)dst + expectedWriteSize - litSize + (1 << 16) - 32; + dctx->litBufferEnd = dctx->litBuffer + litSize - (1 << 16); + } + else + { + dctx->litBuffer = (byte*)dst + expectedWriteSize - litSize; + dctx->litBufferEnd = (byte*)dst + expectedWriteSize; + } + + dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_split; + assert(dctx->litBufferEnd <= (byte*)dst + expectedWriteSize); + } + } + + /*! ZSTD_decodeLiteralsBlock() : + * Where it is possible to do so without being stomped by the output during decompression, the literals block will be stored + * in the dstBuffer. If there is room to do so, it will be stored in full in the excess dst space after where the current + * block will be output. Otherwise it will be stored at the end of the current dst blockspace, with a small portion being + * stored in dctx->litExtraBuffer to help keep it "ahead" of the current output write. + * + * @return : nb of bytes read from src (< srcSize ) + * note : symbol not declared but exposed for fullbench */ + private static nuint ZSTD_decodeLiteralsBlock( + ZSTD_DCtx_s* dctx, + void* src, + nuint srcSize, + void* dst, + nuint dstCapacity, + streaming_operation streaming + ) + { + if (srcSize < 1 + 1) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + { + byte* istart = (byte*)src; + SymbolEncodingType_e litEncType = (SymbolEncodingType_e)(istart[0] & 3); + nuint blockSizeMax = ZSTD_blockSizeMax(dctx); + switch (litEncType) + { + case SymbolEncodingType_e.set_repeat: + if (dctx->litEntropy == 0) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dictionary_corrupted) + ); + } + + goto case SymbolEncodingType_e.set_compressed; + case SymbolEncodingType_e.set_compressed: + if (srcSize < 5) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + { + nuint lhSize, + litSize, + litCSize; + uint singleStream = 0; + uint lhlCode = (uint)(istart[0] >> 2 & 3); + uint lhc = MEM_readLE32(istart); + nuint hufSuccess; + nuint expectedWriteSize = + blockSizeMax < dstCapacity ? blockSizeMax : dstCapacity; + int flags = + 0 + | (ZSTD_DCtx_get_bmi2(dctx) != 0 ? (int)HUF_flags_e.HUF_flags_bmi2 : 0) + | ( + dctx->disableHufAsm != 0 ? (int)HUF_flags_e.HUF_flags_disableAsm : 0 + ); + switch (lhlCode) + { + case 0: + case 1: + default: + singleStream = lhlCode == 0 ? 1U : 0U; + lhSize = 3; + litSize = lhc >> 4 & 0x3FF; + litCSize = lhc >> 14 & 0x3FF; + break; + case 2: + lhSize = 4; + litSize = lhc >> 4 & 0x3FFF; + litCSize = lhc >> 18; + break; + case 3: + lhSize = 5; + litSize = lhc >> 4 & 0x3FFFF; + litCSize = (lhc >> 22) + ((nuint)istart[4] << 10); + break; + } + + if (litSize > 0 && dst == null) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall) + ); + } + + if (litSize > blockSizeMax) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + if (singleStream == 0) + if (litSize < 6) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_literals_headerWrong) + ); + } + + if (litCSize + lhSize > srcSize) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + if (expectedWriteSize < litSize) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall) + ); + } + + ZSTD_allocateLiteralsBuffer( + dctx, + dst, + dstCapacity, + litSize, + streaming, + expectedWriteSize, + 0 + ); + if (dctx->ddictIsCold != 0 && litSize > 768) + { + sbyte* _ptr = (sbyte*)dctx->HUFptr; + const nuint _size = sizeof(uint) * 4097; + nuint _pos; + for (_pos = 0; _pos < _size; _pos += 64) + { +#if NETCOREAPP3_0_OR_GREATER + if (System.Runtime.Intrinsics.X86.Sse.IsSupported) + { + System.Runtime.Intrinsics.X86.Sse.Prefetch1(_ptr + _pos); + } +#endif + } + } + + if (litEncType == SymbolEncodingType_e.set_repeat) + { + if (singleStream != 0) + { + hufSuccess = HUF_decompress1X_usingDTable( + dctx->litBuffer, + litSize, + istart + lhSize, + litCSize, + dctx->HUFptr, + flags + ); + } + else + { + assert(litSize >= 6); + hufSuccess = HUF_decompress4X_usingDTable( + dctx->litBuffer, + litSize, + istart + lhSize, + litCSize, + dctx->HUFptr, + flags + ); + } + } + else + { + if (singleStream != 0) + { + hufSuccess = HUF_decompress1X1_DCtx_wksp( + dctx->entropy.hufTable, + dctx->litBuffer, + litSize, + istart + lhSize, + litCSize, + dctx->workspace, + sizeof(uint) * 640, + flags + ); + } + else + { + hufSuccess = HUF_decompress4X_hufOnly_wksp( + dctx->entropy.hufTable, + dctx->litBuffer, + litSize, + istart + lhSize, + litCSize, + dctx->workspace, + sizeof(uint) * 640, + flags + ); + } + } + + if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split) + { + assert(litSize > 1 << 16); + memcpy(dctx->litExtraBuffer, dctx->litBufferEnd - (1 << 16), 1 << 16); + memmove( + dctx->litBuffer + (1 << 16) - 32, + dctx->litBuffer, + litSize - (1 << 16) + ); + dctx->litBuffer += (1 << 16) - 32; + dctx->litBufferEnd -= 32; + assert(dctx->litBufferEnd <= (byte*)dst + blockSizeMax); + } + + if (ERR_isError(hufSuccess)) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + dctx->litPtr = dctx->litBuffer; + dctx->litSize = litSize; + dctx->litEntropy = 1; + if (litEncType == SymbolEncodingType_e.set_compressed) + dctx->HUFptr = dctx->entropy.hufTable; + return litCSize + lhSize; + } + + case SymbolEncodingType_e.set_basic: + { + nuint litSize, + lhSize; + uint lhlCode = (uint)(istart[0] >> 2 & 3); + nuint expectedWriteSize = + blockSizeMax < dstCapacity ? blockSizeMax : dstCapacity; + switch (lhlCode) + { + case 0: + case 2: + default: + lhSize = 1; + litSize = (nuint)(istart[0] >> 3); + break; + case 1: + lhSize = 2; + litSize = (nuint)(MEM_readLE16(istart) >> 4); + break; + case 3: + lhSize = 3; + if (srcSize < 3) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + litSize = MEM_readLE24(istart) >> 4; + break; + } + + if (litSize > 0 && dst == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (litSize > blockSizeMax) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + if (expectedWriteSize < litSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + ZSTD_allocateLiteralsBuffer( + dctx, + dst, + dstCapacity, + litSize, + streaming, + expectedWriteSize, + 1 + ); + if (lhSize + litSize + 32 > srcSize) + { + if (litSize + lhSize > srcSize) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split) + { + memcpy(dctx->litBuffer, istart + lhSize, (uint)(litSize - (1 << 16))); + memcpy( + dctx->litExtraBuffer, + istart + lhSize + litSize - (1 << 16), + 1 << 16 + ); + } + else + { + memcpy(dctx->litBuffer, istart + lhSize, (uint)litSize); + } + + dctx->litPtr = dctx->litBuffer; + dctx->litSize = litSize; + return lhSize + litSize; + } + + dctx->litPtr = istart + lhSize; + dctx->litSize = litSize; + dctx->litBufferEnd = dctx->litPtr + litSize; + dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst; + return lhSize + litSize; + } + + case SymbolEncodingType_e.set_rle: + { + uint lhlCode = (uint)(istart[0] >> 2 & 3); + nuint litSize, + lhSize; + nuint expectedWriteSize = + blockSizeMax < dstCapacity ? blockSizeMax : dstCapacity; + switch (lhlCode) + { + case 0: + case 2: + default: + lhSize = 1; + litSize = (nuint)(istart[0] >> 3); + break; + case 1: + lhSize = 2; + if (srcSize < 3) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + litSize = (nuint)(MEM_readLE16(istart) >> 4); + break; + case 3: + lhSize = 3; + if (srcSize < 4) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + litSize = MEM_readLE24(istart) >> 4; + break; + } + + if (litSize > 0 && dst == null) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (litSize > blockSizeMax) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + } + + if (expectedWriteSize < litSize) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + ZSTD_allocateLiteralsBuffer( + dctx, + dst, + dstCapacity, + litSize, + streaming, + expectedWriteSize, + 1 + ); + if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split) + { + memset(dctx->litBuffer, istart[lhSize], (uint)(litSize - (1 << 16))); + memset(dctx->litExtraBuffer, istart[lhSize], 1 << 16); + } + else + { + memset(dctx->litBuffer, istart[lhSize], (uint)litSize); + } + + dctx->litPtr = dctx->litBuffer; + dctx->litSize = litSize; + return lhSize + 1; + } + + default: + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + } + } + + /* Hidden declaration for fullbench */ + private static nuint ZSTD_decodeLiteralsBlock_wrapper( + ZSTD_DCtx_s* dctx, + void* src, + nuint srcSize, + void* dst, + nuint dstCapacity + ) + { + dctx->isFrameDecompression = 0; + return ZSTD_decodeLiteralsBlock( + dctx, + src, + srcSize, + dst, + dstCapacity, + streaming_operation.not_streaming + ); + } + + private static readonly ZSTD_seqSymbol* LL_defaultDTable = GetArrayPointer( + new ZSTD_seqSymbol[65] + { + new ZSTD_seqSymbol(nextState: 1, nbAdditionalBits: 1, nbBits: 1, baseValue: 6), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 4, baseValue: 0), + new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 0, nbBits: 4, baseValue: 0), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 1), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 3), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 4), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 6), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 7), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 9), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 10), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 12), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 14), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 5, baseValue: 16), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 5, baseValue: 20), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 5, baseValue: 22), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 2, nbBits: 5, baseValue: 28), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 3, nbBits: 5, baseValue: 32), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 4, nbBits: 5, baseValue: 48), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 6, nbBits: 5, baseValue: 64), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 7, nbBits: 5, baseValue: 128), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 8, nbBits: 6, baseValue: 256), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 10, nbBits: 6, baseValue: 1024), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 12, nbBits: 6, baseValue: 4096), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 4, baseValue: 0), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 4, baseValue: 1), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 2), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 4), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 5), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 7), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 8), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 10), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 11), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 13), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 1, nbBits: 5, baseValue: 16), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 5, baseValue: 18), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 1, nbBits: 5, baseValue: 22), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 2, nbBits: 5, baseValue: 24), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 3, nbBits: 5, baseValue: 32), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 3, nbBits: 5, baseValue: 40), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 6, nbBits: 4, baseValue: 64), + new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 6, nbBits: 4, baseValue: 64), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 7, nbBits: 5, baseValue: 128), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 9, nbBits: 6, baseValue: 512), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 11, nbBits: 6, baseValue: 2048), + new ZSTD_seqSymbol(nextState: 48, nbAdditionalBits: 0, nbBits: 4, baseValue: 0), + new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 0, nbBits: 4, baseValue: 1), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 2), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 3), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 5), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 6), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 8), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 9), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 11), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 12), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 15), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 1, nbBits: 5, baseValue: 18), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 1, nbBits: 5, baseValue: 20), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 2, nbBits: 5, baseValue: 24), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 2, nbBits: 5, baseValue: 28), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 3, nbBits: 5, baseValue: 40), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 4, nbBits: 5, baseValue: 48), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 16, nbBits: 6, baseValue: 65536), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 15, nbBits: 6, baseValue: 32768), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 14, nbBits: 6, baseValue: 16384), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 13, nbBits: 6, baseValue: 8192), + } + ); + private static readonly ZSTD_seqSymbol* OF_defaultDTable = GetArrayPointer( + new ZSTD_seqSymbol[33] + { + new ZSTD_seqSymbol(nextState: 1, nbAdditionalBits: 1, nbBits: 1, baseValue: 5), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 0), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 6, nbBits: 4, baseValue: 61), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 9, nbBits: 5, baseValue: 509), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 15, nbBits: 5, baseValue: 32765), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 21, nbBits: 5, baseValue: 2097149), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 3, nbBits: 5, baseValue: 5), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 7, nbBits: 4, baseValue: 125), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 12, nbBits: 5, baseValue: 4093), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 18, nbBits: 5, baseValue: 262141), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 23, nbBits: 5, baseValue: 8388605), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 5, nbBits: 5, baseValue: 29), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 8, nbBits: 4, baseValue: 253), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 14, nbBits: 5, baseValue: 16381), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 20, nbBits: 5, baseValue: 1048573), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 2, nbBits: 5, baseValue: 1), + new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 7, nbBits: 4, baseValue: 125), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 11, nbBits: 5, baseValue: 2045), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 17, nbBits: 5, baseValue: 131069), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 22, nbBits: 5, baseValue: 4194301), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 4, nbBits: 5, baseValue: 13), + new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 8, nbBits: 4, baseValue: 253), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 13, nbBits: 5, baseValue: 8189), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 19, nbBits: 5, baseValue: 524285), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 5, baseValue: 1), + new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 6, nbBits: 4, baseValue: 61), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 10, nbBits: 5, baseValue: 1021), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 16, nbBits: 5, baseValue: 65533), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 28, nbBits: 5, baseValue: 268435453), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 27, nbBits: 5, baseValue: 134217725), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 26, nbBits: 5, baseValue: 67108861), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 25, nbBits: 5, baseValue: 33554429), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 24, nbBits: 5, baseValue: 16777213), + } + ); + private static readonly ZSTD_seqSymbol* ML_defaultDTable = GetArrayPointer( + new ZSTD_seqSymbol[65] + { + new ZSTD_seqSymbol(nextState: 1, nbAdditionalBits: 1, nbBits: 1, baseValue: 6), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 3), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 4, baseValue: 4), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 5), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 6), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 8), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 9), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 11), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 13), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 16), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 19), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 22), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 25), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 28), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 31), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 34), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 6, baseValue: 37), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 6, baseValue: 41), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 2, nbBits: 6, baseValue: 47), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 3, nbBits: 6, baseValue: 59), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 4, nbBits: 6, baseValue: 83), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 7, nbBits: 6, baseValue: 131), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 9, nbBits: 6, baseValue: 515), + new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 0, nbBits: 4, baseValue: 4), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 4, baseValue: 5), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 6), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 7), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 9), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 5, baseValue: 10), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 12), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 15), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 18), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 21), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 24), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 27), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 30), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 33), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 6, baseValue: 35), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 1, nbBits: 6, baseValue: 39), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 2, nbBits: 6, baseValue: 43), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 3, nbBits: 6, baseValue: 51), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 4, nbBits: 6, baseValue: 67), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 5, nbBits: 6, baseValue: 99), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 8, nbBits: 6, baseValue: 259), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 4, baseValue: 4), + new ZSTD_seqSymbol(nextState: 48, nbAdditionalBits: 0, nbBits: 4, baseValue: 4), + new ZSTD_seqSymbol(nextState: 16, nbAdditionalBits: 0, nbBits: 4, baseValue: 5), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 7), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 8), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 10), + new ZSTD_seqSymbol(nextState: 32, nbAdditionalBits: 0, nbBits: 5, baseValue: 11), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 14), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 17), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 20), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 23), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 26), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 29), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 0, nbBits: 6, baseValue: 32), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 16, nbBits: 6, baseValue: 65539), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 15, nbBits: 6, baseValue: 32771), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 14, nbBits: 6, baseValue: 16387), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 13, nbBits: 6, baseValue: 8195), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 12, nbBits: 6, baseValue: 4099), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 11, nbBits: 6, baseValue: 2051), + new ZSTD_seqSymbol(nextState: 0, nbAdditionalBits: 10, nbBits: 6, baseValue: 1027), + } + ); + + private static void ZSTD_buildSeqTable_rle(ZSTD_seqSymbol* dt, uint baseValue, byte nbAddBits) + { + void* ptr = dt; + ZSTD_seqSymbol_header* DTableH = (ZSTD_seqSymbol_header*)ptr; + ZSTD_seqSymbol* cell = dt + 1; + DTableH->tableLog = 0; + DTableH->fastMode = 0; + cell->nbBits = 0; + cell->nextState = 0; + assert(nbAddBits < 255); + cell->nbAdditionalBits = nbAddBits; + cell->baseValue = baseValue; + } + + /* ZSTD_buildFSETable() : + * generate FSE decoding table for one symbol (ll, ml or off) + * cannot fail if input is valid => + * all inputs are presumed validated at this stage */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_buildFSETable_body( + ZSTD_seqSymbol* dt, + short* normalizedCounter, + uint maxSymbolValue, + uint* baseValue, + byte* nbAdditionalBits, + uint tableLog, + void* wksp, + nuint wkspSize + ) + { + ZSTD_seqSymbol* tableDecode = dt + 1; + uint maxSV1 = maxSymbolValue + 1; + uint tableSize = (uint)(1 << (int)tableLog); + ushort* symbolNext = (ushort*)wksp; + byte* spread = (byte*)(symbolNext + 52 + 1); + uint highThreshold = tableSize - 1; + assert(maxSymbolValue <= 52); + assert(tableLog <= 9); + assert(wkspSize >= sizeof(short) * (52 + 1) + (1U << 9) + sizeof(ulong)); + { + ZSTD_seqSymbol_header DTableH; + DTableH.tableLog = tableLog; + DTableH.fastMode = 1; + { + short largeLimit = (short)(1 << (int)(tableLog - 1)); + uint s; + for (s = 0; s < maxSV1; s++) + { + if (normalizedCounter[s] == -1) + { + tableDecode[highThreshold--].baseValue = s; + symbolNext[s] = 1; + } + else + { + if (normalizedCounter[s] >= largeLimit) + DTableH.fastMode = 0; + assert(normalizedCounter[s] >= 0); + symbolNext[s] = (ushort)normalizedCounter[s]; + } + } + } + + memcpy(dt, &DTableH, (uint)sizeof(ZSTD_seqSymbol_header)); + } + + assert(tableSize <= 512); + if (highThreshold == tableSize - 1) + { + nuint tableMask = tableSize - 1; + nuint step = (tableSize >> 1) + (tableSize >> 3) + 3; + { + const ulong add = 0x0101010101010101UL; + nuint pos = 0; + ulong sv = 0; + uint s; + for (s = 0; s < maxSV1; ++s, sv += add) + { + int i; + int n = normalizedCounter[s]; + MEM_write64(spread + pos, sv); + for (i = 8; i < n; i += 8) + { + MEM_write64(spread + pos + i, sv); + } + + assert(n >= 0); + pos += (nuint)n; + } + } + + { + nuint position = 0; + nuint s; + const nuint unroll = 2; + assert(tableSize % unroll == 0); + for (s = 0; s < tableSize; s += unroll) + { + nuint u; + for (u = 0; u < unroll; ++u) + { + nuint uPosition = position + u * step & tableMask; + tableDecode[uPosition].baseValue = spread[s + u]; + } + + position = position + unroll * step & tableMask; + } + + assert(position == 0); + } + } + else + { + uint tableMask = tableSize - 1; + uint step = (tableSize >> 1) + (tableSize >> 3) + 3; + uint s, + position = 0; + for (s = 0; s < maxSV1; s++) + { + int i; + int n = normalizedCounter[s]; + for (i = 0; i < n; i++) + { + tableDecode[position].baseValue = s; + position = position + step & tableMask; + while (position > highThreshold) + position = position + step & tableMask; + } + } + + assert(position == 0); + } + + { + uint u; + for (u = 0; u < tableSize; u++) + { + uint symbol = tableDecode[u].baseValue; + uint nextState = symbolNext[symbol]++; + tableDecode[u].nbBits = (byte)(tableLog - ZSTD_highbit32(nextState)); + tableDecode[u].nextState = (ushort)( + (nextState << tableDecode[u].nbBits) - tableSize + ); + assert(nbAdditionalBits[symbol] < 255); + tableDecode[u].nbAdditionalBits = nbAdditionalBits[symbol]; + tableDecode[u].baseValue = baseValue[symbol]; + } + } + } + + /* Avoids the FORCE_INLINE of the _body() function. */ + private static void ZSTD_buildFSETable_body_default( + ZSTD_seqSymbol* dt, + short* normalizedCounter, + uint maxSymbolValue, + uint* baseValue, + byte* nbAdditionalBits, + uint tableLog, + void* wksp, + nuint wkspSize + ) + { + ZSTD_buildFSETable_body( + dt, + normalizedCounter, + maxSymbolValue, + baseValue, + nbAdditionalBits, + tableLog, + wksp, + wkspSize + ); + } + + /* ZSTD_buildFSETable() : + * generate FSE decoding table for one symbol (ll, ml or off) + * this function must be called with valid parameters only + * (dt is large enough, normalizedCounter distribution total is a power of 2, max is within range, etc.) + * in which case it cannot fail. + * The workspace must be 4-byte aligned and at least ZSTD_BUILD_FSE_TABLE_WKSP_SIZE bytes, which is + * defined in zstd_decompress_internal.h. + * Internal use only. + */ + private static void ZSTD_buildFSETable( + ZSTD_seqSymbol* dt, + short* normalizedCounter, + uint maxSymbolValue, + uint* baseValue, + byte* nbAdditionalBits, + uint tableLog, + void* wksp, + nuint wkspSize, + int bmi2 + ) + { + ZSTD_buildFSETable_body_default( + dt, + normalizedCounter, + maxSymbolValue, + baseValue, + nbAdditionalBits, + tableLog, + wksp, + wkspSize + ); + } + + /*! ZSTD_buildSeqTable() : + * @return : nb bytes read from src, + * or an error code if it fails */ + private static nuint ZSTD_buildSeqTable( + ZSTD_seqSymbol* DTableSpace, + ZSTD_seqSymbol** DTablePtr, + SymbolEncodingType_e type, + uint max, + uint maxLog, + void* src, + nuint srcSize, + uint* baseValue, + byte* nbAdditionalBits, + ZSTD_seqSymbol* defaultTable, + uint flagRepeatTable, + int ddictIsCold, + int nbSeq, + uint* wksp, + nuint wkspSize, + int bmi2 + ) + { + switch (type) + { + case SymbolEncodingType_e.set_rle: + if (srcSize == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if (*(byte*)src > max) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + { + uint symbol = *(byte*)src; + uint baseline = baseValue[symbol]; + byte nbBits = nbAdditionalBits[symbol]; + ZSTD_buildSeqTable_rle(DTableSpace, baseline, nbBits); + } + + *DTablePtr = DTableSpace; + return 1; + case SymbolEncodingType_e.set_basic: + *DTablePtr = defaultTable; + return 0; + case SymbolEncodingType_e.set_repeat: + if (flagRepeatTable == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + if (ddictIsCold != 0 && nbSeq > 24) + { + void* pStart = *DTablePtr; + nuint pSize = (nuint)(sizeof(ZSTD_seqSymbol) * (1 + (1 << (int)maxLog))); + { + sbyte* _ptr = (sbyte*)pStart; + nuint _size = pSize; + nuint _pos; + for (_pos = 0; _pos < _size; _pos += 64) + { +#if NETCOREAPP3_0_OR_GREATER + if (System.Runtime.Intrinsics.X86.Sse.IsSupported) + { + System.Runtime.Intrinsics.X86.Sse.Prefetch1(_ptr + _pos); + } +#endif + } + } + } + + return 0; + case SymbolEncodingType_e.set_compressed: + { + uint tableLog; + short* norm = stackalloc short[53]; + nuint headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize); + if (ERR_isError(headerSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + if (tableLog > maxLog) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + ZSTD_buildFSETable( + DTableSpace, + norm, + max, + baseValue, + nbAdditionalBits, + tableLog, + wksp, + wkspSize, + bmi2 + ); + *DTablePtr = DTableSpace; + return headerSize; + } + + default: + assert(0 != 0); + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_GENERIC)); + } + } + + /*! ZSTD_decodeSeqHeaders() : + * decode sequence header from src */ + /* Used by: zstd_decompress_block, fullbench */ + private static nuint ZSTD_decodeSeqHeaders( + ZSTD_DCtx_s* dctx, + int* nbSeqPtr, + void* src, + nuint srcSize + ) + { + byte* istart = (byte*)src; + byte* iend = istart + srcSize; + byte* ip = istart; + int nbSeq; + if (srcSize < 1) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + nbSeq = *ip++; + if (nbSeq > 0x7F) + { + if (nbSeq == 0xFF) + { + if (ip + 2 > iend) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + nbSeq = MEM_readLE16(ip) + 0x7F00; + ip += 2; + } + else + { + if (ip >= iend) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + nbSeq = (nbSeq - 0x80 << 8) + *ip++; + } + } + + *nbSeqPtr = nbSeq; + if (nbSeq == 0) + { + if (ip != iend) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + return (nuint)(ip - istart); + } + + if (ip + 1 > iend) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + if ((*ip & 3) != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + { + SymbolEncodingType_e LLtype = (SymbolEncodingType_e)(*ip >> 6); + SymbolEncodingType_e OFtype = (SymbolEncodingType_e)(*ip >> 4 & 3); + SymbolEncodingType_e MLtype = (SymbolEncodingType_e)(*ip >> 2 & 3); + ip++; + { + nuint llhSize = ZSTD_buildSeqTable( + &dctx->entropy.LLTable.e0, + &dctx->LLTptr, + LLtype, + 35, + 9, + ip, + (nuint)(iend - ip), + LL_base, + LL_bits, + LL_defaultDTable, + dctx->fseEntropy, + dctx->ddictIsCold, + nbSeq, + dctx->workspace, + sizeof(uint) * 640, + ZSTD_DCtx_get_bmi2(dctx) + ); + if (ERR_isError(llhSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + ip += llhSize; + } + + { + nuint ofhSize = ZSTD_buildSeqTable( + &dctx->entropy.OFTable.e0, + &dctx->OFTptr, + OFtype, + 31, + 8, + ip, + (nuint)(iend - ip), + OF_base, + OF_bits, + OF_defaultDTable, + dctx->fseEntropy, + dctx->ddictIsCold, + nbSeq, + dctx->workspace, + sizeof(uint) * 640, + ZSTD_DCtx_get_bmi2(dctx) + ); + if (ERR_isError(ofhSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + ip += ofhSize; + } + + { + nuint mlhSize = ZSTD_buildSeqTable( + &dctx->entropy.MLTable.e0, + &dctx->MLTptr, + MLtype, + 52, + 9, + ip, + (nuint)(iend - ip), + ML_base, + ML_bits, + ML_defaultDTable, + dctx->fseEntropy, + dctx->ddictIsCold, + nbSeq, + dctx->workspace, + sizeof(uint) * 640, + ZSTD_DCtx_get_bmi2(dctx) + ); + if (ERR_isError(mlhSize)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + ip += mlhSize; + } + } + + return (nuint)(ip - istart); + } + +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_dec32table => new uint[8] { 0, 1, 2, 1, 4, 4, 4, 4 }; + private static uint* dec32table => + (uint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_dec32table) + ); +#else + + private static readonly uint* dec32table = GetArrayPointer( + new uint[8] { 0, 1, 2, 1, 4, 4, 4, 4 } + ); +#endif +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_dec64table => new int[8] { 8, 8, 8, 7, 8, 9, 10, 11 }; + private static int* dec64table => + (int*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_dec64table) + ); +#else + + private static readonly int* dec64table = GetArrayPointer( + new int[8] { 8, 8, 8, 7, 8, 9, 10, 11 } + ); +#endif + /*! ZSTD_overlapCopy8() : + * Copies 8 bytes from ip to op and updates op and ip where ip <= op. + * If the offset is < 8 then the offset is spread to at least 8 bytes. + * + * Precondition: *ip <= *op + * Postcondition: *op - *op >= 8 + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_overlapCopy8(byte** op, byte** ip, nuint offset) + { + assert(*ip <= *op); + if (offset < 8) + { + int sub2 = dec64table[offset]; + (*op)[0] = (*ip)[0]; + (*op)[1] = (*ip)[1]; + (*op)[2] = (*ip)[2]; + (*op)[3] = (*ip)[3]; + *ip += dec32table[offset]; + ZSTD_copy4(*op + 4, *ip); + *ip -= sub2; + } + else + { + ZSTD_copy8(*op, *ip); + } + + *ip += 8; + *op += 8; + assert(*op - *ip >= 8); + } + + /*! ZSTD_safecopy() : + * Specialized version of memcpy() that is allowed to READ up to WILDCOPY_OVERLENGTH past the input buffer + * and write up to 16 bytes past oend_w (op >= oend_w is allowed). + * This function is only called in the uncommon case where the sequence is near the end of the block. It + * should be fast for a single long sequence, but can be slow for several short sequences. + * + * @param ovtype controls the overlap detection + * - ZSTD_no_overlap: The source and destination are guaranteed to be at least WILDCOPY_VECLEN bytes apart. + * - ZSTD_overlap_src_before_dst: The src and dst may overlap and may be any distance apart. + * The src buffer must be before the dst buffer. + */ + private static void ZSTD_safecopy( + byte* op, + byte* oend_w, + byte* ip, + nint length, + ZSTD_overlap_e ovtype + ) + { + nint diff = (nint)(op - ip); + byte* oend = op + length; + assert( + ovtype == ZSTD_overlap_e.ZSTD_no_overlap && (diff <= -8 || diff >= 8 || op >= oend_w) + || ovtype == ZSTD_overlap_e.ZSTD_overlap_src_before_dst && diff >= 0 + ); + if (length < 8) + { + while (op < oend) + *op++ = *ip++; + return; + } + + if (ovtype == ZSTD_overlap_e.ZSTD_overlap_src_before_dst) + { + assert(length >= 8); + ZSTD_overlapCopy8(&op, &ip, (nuint)diff); + length -= 8; + assert(op - ip >= 8); + assert(op <= oend); + } + + if (oend <= oend_w) + { + ZSTD_wildcopy(op, ip, length, ovtype); + return; + } + + if (op <= oend_w) + { + assert(oend > oend_w); + ZSTD_wildcopy(op, ip, (nint)(oend_w - op), ovtype); + ip += oend_w - op; + op += oend_w - op; + } + + while (op < oend) + *op++ = *ip++; + } + + /* ZSTD_safecopyDstBeforeSrc(): + * This version allows overlap with dst before src, or handles the non-overlap case with dst after src + * Kept separate from more common ZSTD_safecopy case to avoid performance impact to the safecopy common case */ + private static void ZSTD_safecopyDstBeforeSrc(byte* op, byte* ip, nint length) + { + nint diff = (nint)(op - ip); + byte* oend = op + length; + if (length < 8 || diff > -8) + { + while (op < oend) + *op++ = *ip++; + return; + } + + if (op <= oend - 32 && diff < -16) + { + ZSTD_wildcopy(op, ip, (nint)(oend - 32 - op), ZSTD_overlap_e.ZSTD_no_overlap); + ip += oend - 32 - op; + op += oend - 32 - op; + } + + while (op < oend) + *op++ = *ip++; + } + + /* ZSTD_execSequenceEnd(): + * This version handles cases that are near the end of the output buffer. It requires + * more careful checks to make sure there is no overflow. By separating out these hard + * and unlikely cases, we can speed up the common cases. + * + * NOTE: This function needs to be fast for a single long sequence, but doesn't need + * to be optimized for many small sequences, since those fall into ZSTD_execSequence(). + */ + private static nuint ZSTD_execSequenceEnd( + byte* op, + byte* oend, + seq_t sequence, + byte** litPtr, + byte* litLimit, + byte* prefixStart, + byte* virtualStart, + byte* dictEnd + ) + { + byte* oLitEnd = op + sequence.litLength; + nuint sequenceLength = sequence.litLength + sequence.matchLength; + byte* iLitEnd = *litPtr + sequence.litLength; + byte* match = oLitEnd - sequence.offset; + byte* oend_w = oend - 32; + if (sequenceLength > (nuint)(oend - op)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (sequence.litLength > (nuint)(litLimit - *litPtr)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + assert(op < op + sequenceLength); + assert(oLitEnd < op + sequenceLength); + ZSTD_safecopy( + op, + oend_w, + *litPtr, + (nint)sequence.litLength, + ZSTD_overlap_e.ZSTD_no_overlap + ); + op = oLitEnd; + *litPtr = iLitEnd; + if (sequence.offset > (nuint)(oLitEnd - prefixStart)) + { + if (sequence.offset > (nuint)(oLitEnd - virtualStart)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + match = dictEnd - (prefixStart - match); + if (match + sequence.matchLength <= dictEnd) + { + memmove(oLitEnd, match, sequence.matchLength); + return sequenceLength; + } + + { + nuint length1 = (nuint)(dictEnd - match); + memmove(oLitEnd, match, length1); + op = oLitEnd + length1; + sequence.matchLength -= length1; + match = prefixStart; + } + } + + ZSTD_safecopy( + op, + oend_w, + match, + (nint)sequence.matchLength, + ZSTD_overlap_e.ZSTD_overlap_src_before_dst + ); + return sequenceLength; + } + + /* ZSTD_execSequenceEndSplitLitBuffer(): + * This version is intended to be used during instances where the litBuffer is still split. It is kept separate to avoid performance impact for the good case. + */ + private static nuint ZSTD_execSequenceEndSplitLitBuffer( + byte* op, + byte* oend, + byte* oend_w, + seq_t sequence, + byte** litPtr, + byte* litLimit, + byte* prefixStart, + byte* virtualStart, + byte* dictEnd + ) + { + byte* oLitEnd = op + sequence.litLength; + nuint sequenceLength = sequence.litLength + sequence.matchLength; + byte* iLitEnd = *litPtr + sequence.litLength; + byte* match = oLitEnd - sequence.offset; + if (sequenceLength > (nuint)(oend - op)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (sequence.litLength > (nuint)(litLimit - *litPtr)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + assert(op < op + sequenceLength); + assert(oLitEnd < op + sequenceLength); + if (op > *litPtr && op < *litPtr + sequence.litLength) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + ZSTD_safecopyDstBeforeSrc(op, *litPtr, (nint)sequence.litLength); + op = oLitEnd; + *litPtr = iLitEnd; + if (sequence.offset > (nuint)(oLitEnd - prefixStart)) + { + if (sequence.offset > (nuint)(oLitEnd - virtualStart)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + match = dictEnd - (prefixStart - match); + if (match + sequence.matchLength <= dictEnd) + { + memmove(oLitEnd, match, sequence.matchLength); + return sequenceLength; + } + + { + nuint length1 = (nuint)(dictEnd - match); + memmove(oLitEnd, match, length1); + op = oLitEnd + length1; + sequence.matchLength -= length1; + match = prefixStart; + } + } + + ZSTD_safecopy( + op, + oend_w, + match, + (nint)sequence.matchLength, + ZSTD_overlap_e.ZSTD_overlap_src_before_dst + ); + return sequenceLength; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_execSequence( + byte* op, + byte* oend, + seq_t sequence, + byte** litPtr, + byte* litLimit, + byte* prefixStart, + byte* virtualStart, + byte* dictEnd + ) + { + var sequence_litLength = sequence.litLength; + var sequence_matchLength = sequence.matchLength; + var sequence_offset = sequence.offset; + byte* oLitEnd = op + sequence_litLength; + nuint sequenceLength = sequence_litLength + sequence_matchLength; + /* risk : address space overflow (32-bits) */ + byte* oMatchEnd = op + sequenceLength; + /* risk : address space underflow on oend=NULL */ + byte* oend_w = oend - 32; + byte* iLitEnd = *litPtr + sequence_litLength; + byte* match = oLitEnd - sequence_offset; + assert(op != null); + assert(oend_w < oend); + if ( + iLitEnd > litLimit + || oMatchEnd > oend_w + || MEM_32bits && (nuint)(oend - op) < sequenceLength + 32 + ) + return ZSTD_execSequenceEnd( + op, + oend, + new seq_t + { + litLength = sequence_litLength, + matchLength = sequence_matchLength, + offset = sequence_offset, + }, + litPtr, + litLimit, + prefixStart, + virtualStart, + dictEnd + ); + assert(op <= oLitEnd); + assert(oLitEnd < oMatchEnd); + assert(oMatchEnd <= oend); + assert(iLitEnd <= litLimit); + assert(oLitEnd <= oend_w); + assert(oMatchEnd <= oend_w); + assert(32 >= 16); + ZSTD_copy16(op, *litPtr); + if (sequence_litLength > 16) + { + ZSTD_wildcopy( + op + 16, + *litPtr + 16, + (nint)(sequence_litLength - 16), + ZSTD_overlap_e.ZSTD_no_overlap + ); + } + + op = oLitEnd; + *litPtr = iLitEnd; + if (sequence_offset > (nuint)(oLitEnd - prefixStart)) + { + if (sequence_offset > (nuint)(oLitEnd - virtualStart)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + match = dictEnd + (match - prefixStart); + if (match + sequence_matchLength <= dictEnd) + { + memmove(oLitEnd, match, sequence_matchLength); + return sequenceLength; + } + + { + nuint length1 = (nuint)(dictEnd - match); + memmove(oLitEnd, match, length1); + op = oLitEnd + length1; + sequence_matchLength -= length1; + match = prefixStart; + } + } + + assert(op <= oMatchEnd); + assert(oMatchEnd <= oend_w); + assert(match >= prefixStart); + assert(sequence_matchLength >= 1); + if (sequence_offset >= 16) + { + ZSTD_wildcopy(op, match, (nint)sequence_matchLength, ZSTD_overlap_e.ZSTD_no_overlap); + return sequenceLength; + } + + assert(sequence_offset < 16); + ZSTD_overlapCopy8(ref op, ref match, sequence_offset); + if (sequence_matchLength > 8) + { + assert(op < oMatchEnd); + ZSTD_wildcopy( + op, + match, + (nint)sequence_matchLength - 8, + ZSTD_overlap_e.ZSTD_overlap_src_before_dst + ); + } + + return sequenceLength; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_execSequenceSplitLitBuffer( + byte* op, + byte* oend, + byte* oend_w, + seq_t sequence, + byte** litPtr, + byte* litLimit, + byte* prefixStart, + byte* virtualStart, + byte* dictEnd + ) + { + byte* oLitEnd = op + sequence.litLength; + nuint sequenceLength = sequence.litLength + sequence.matchLength; + /* risk : address space overflow (32-bits) */ + byte* oMatchEnd = op + sequenceLength; + byte* iLitEnd = *litPtr + sequence.litLength; + byte* match = oLitEnd - sequence.offset; + assert(op != null); + assert(oend_w < oend); + if ( + iLitEnd > litLimit + || oMatchEnd > oend_w + || MEM_32bits && (nuint)(oend - op) < sequenceLength + 32 + ) + return ZSTD_execSequenceEndSplitLitBuffer( + op, + oend, + oend_w, + sequence, + litPtr, + litLimit, + prefixStart, + virtualStart, + dictEnd + ); + assert(op <= oLitEnd); + assert(oLitEnd < oMatchEnd); + assert(oMatchEnd <= oend); + assert(iLitEnd <= litLimit); + assert(oLitEnd <= oend_w); + assert(oMatchEnd <= oend_w); + assert(32 >= 16); + ZSTD_copy16(op, *litPtr); + if (sequence.litLength > 16) + { + ZSTD_wildcopy( + op + 16, + *litPtr + 16, + (nint)(sequence.litLength - 16), + ZSTD_overlap_e.ZSTD_no_overlap + ); + } + + op = oLitEnd; + *litPtr = iLitEnd; + if (sequence.offset > (nuint)(oLitEnd - prefixStart)) + { + if (sequence.offset > (nuint)(oLitEnd - virtualStart)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + match = dictEnd + (match - prefixStart); + if (match + sequence.matchLength <= dictEnd) + { + memmove(oLitEnd, match, sequence.matchLength); + return sequenceLength; + } + + { + nuint length1 = (nuint)(dictEnd - match); + memmove(oLitEnd, match, length1); + op = oLitEnd + length1; + sequence.matchLength -= length1; + match = prefixStart; + } + } + + assert(op <= oMatchEnd); + assert(oMatchEnd <= oend_w); + assert(match >= prefixStart); + assert(sequence.matchLength >= 1); + if (sequence.offset >= 16) + { + ZSTD_wildcopy(op, match, (nint)sequence.matchLength, ZSTD_overlap_e.ZSTD_no_overlap); + return sequenceLength; + } + + assert(sequence.offset < 16); + ZSTD_overlapCopy8(&op, &match, sequence.offset); + if (sequence.matchLength > 8) + { + assert(op < oMatchEnd); + ZSTD_wildcopy( + op, + match, + (nint)sequence.matchLength - 8, + ZSTD_overlap_e.ZSTD_overlap_src_before_dst + ); + } + + return sequenceLength; + } + + private static void ZSTD_initFseState( + ZSTD_fseState* DStatePtr, + BIT_DStream_t* bitD, + ZSTD_seqSymbol* dt + ) + { + void* ptr = dt; + ZSTD_seqSymbol_header* DTableH = (ZSTD_seqSymbol_header*)ptr; + DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); + BIT_reloadDStream(bitD); + DStatePtr->table = dt + 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_updateFseStateWithDInfo( + ZSTD_fseState* DStatePtr, + BIT_DStream_t* bitD, + ushort nextState, + uint nbBits + ) + { + nuint lowBits = BIT_readBits(bitD, nbBits); + DStatePtr->state = nextState + lowBits; + } + + /** + * ZSTD_decodeSequence(): + * @p longOffsets : tells the decoder to reload more bit while decoding large offsets + * only used in 32-bit mode + * @return : Sequence (litL + matchL + offset) + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static seq_t ZSTD_decodeSequence( + seqState_t* seqState, + ZSTD_longOffset_e longOffsets, + int isLastSeq + ) + { + seq_t seq; + ZSTD_seqSymbol* llDInfo = seqState->stateLL.table + seqState->stateLL.state; + ZSTD_seqSymbol* mlDInfo = seqState->stateML.table + seqState->stateML.state; + ZSTD_seqSymbol* ofDInfo = seqState->stateOffb.table + seqState->stateOffb.state; + seq.matchLength = mlDInfo->baseValue; + seq.litLength = llDInfo->baseValue; + { + uint ofBase = ofDInfo->baseValue; + byte llBits = llDInfo->nbAdditionalBits; + byte mlBits = mlDInfo->nbAdditionalBits; + byte ofBits = ofDInfo->nbAdditionalBits; + byte totalBits = (byte)(llBits + mlBits + ofBits); + ushort llNext = llDInfo->nextState; + ushort mlNext = mlDInfo->nextState; + ushort ofNext = ofDInfo->nextState; + uint llnbBits = llDInfo->nbBits; + uint mlnbBits = mlDInfo->nbBits; + uint ofnbBits = ofDInfo->nbBits; + assert(llBits <= 16); + assert(mlBits <= 16); + assert(ofBits <= 31); + { + nuint offset; + if (ofBits > 1) + { + if (MEM_32bits && longOffsets != default && ofBits >= 25) + { + /* Always read extra bits, this keeps the logic simple, + * avoids branches, and avoids accidentally reading 0 bits. + */ + const uint extraBits = 30 - 25; + offset = + ofBase + + ( + BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) + << (int)extraBits + ); + BIT_reloadDStream(&seqState->DStream); + offset += BIT_readBitsFast(&seqState->DStream, extraBits); + } + else + { + offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits); + if (MEM_32bits) + BIT_reloadDStream(&seqState->DStream); + } + + seqState->prevOffset.e2 = seqState->prevOffset.e1; + seqState->prevOffset.e1 = seqState->prevOffset.e0; + seqState->prevOffset.e0 = offset; + } + else + { + uint ll0 = llDInfo->baseValue == 0 ? 1U : 0U; + if (ofBits == 0) + { + offset = (&seqState->prevOffset.e0)[ll0]; + seqState->prevOffset.e1 = (&seqState->prevOffset.e0)[ll0 == 0 ? 1 : 0]; + seqState->prevOffset.e0 = offset; + } + else + { + offset = ofBase + ll0 + BIT_readBitsFast(&seqState->DStream, 1); + { + nuint temp = + offset == 3 + ? seqState->prevOffset.e0 - 1 + : (&seqState->prevOffset.e0)[offset]; + temp -= temp == 0 ? 1U : 0U; + if (offset != 1) + seqState->prevOffset.e2 = seqState->prevOffset.e1; + seqState->prevOffset.e1 = seqState->prevOffset.e0; + seqState->prevOffset.e0 = offset = temp; + } + } + } + + seq.offset = offset; + } + + if (mlBits > 0) + seq.matchLength += BIT_readBitsFast(&seqState->DStream, mlBits); + if (MEM_32bits && mlBits + llBits >= 25 - (30 - 25)) + BIT_reloadDStream(&seqState->DStream); + if (MEM_64bits && totalBits >= 57 - (9 + 9 + 8)) + BIT_reloadDStream(&seqState->DStream); + if (llBits > 0) + seq.litLength += BIT_readBitsFast(&seqState->DStream, llBits); + if (MEM_32bits) + BIT_reloadDStream(&seqState->DStream); + if (isLastSeq == 0) + { + ZSTD_updateFseStateWithDInfo( + &seqState->stateLL, + &seqState->DStream, + llNext, + llnbBits + ); + ZSTD_updateFseStateWithDInfo( + &seqState->stateML, + &seqState->DStream, + mlNext, + mlnbBits + ); + if (MEM_32bits) + BIT_reloadDStream(&seqState->DStream); + ZSTD_updateFseStateWithDInfo( + &seqState->stateOffb, + &seqState->DStream, + ofNext, + ofnbBits + ); + BIT_reloadDStream(&seqState->DStream); + } + } + + return seq; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_decompressSequences_bodySplitLitBuffer( + ZSTD_DCtx_s* dctx, + void* dst, + nuint maxDstSize, + void* seqStart, + nuint seqSize, + int nbSeq, + ZSTD_longOffset_e isLongOffset + ) + { + byte* ip = (byte*)seqStart; + byte* iend = ip + seqSize; + byte* ostart = (byte*)dst; + byte* oend = ZSTD_maybeNullPtrAdd(ostart, (nint)maxDstSize); + byte* op = ostart; + byte* litPtr = dctx->litPtr; + byte* litBufferEnd = dctx->litBufferEnd; + byte* prefixStart = (byte*)dctx->prefixStart; + byte* vBase = (byte*)dctx->virtualStart; + byte* dictEnd = (byte*)dctx->dictEnd; + if (nbSeq != 0) + { + seqState_t seqState; + dctx->fseEntropy = 1; + { + uint i; + for (i = 0; i < 3; i++) + (&seqState.prevOffset.e0)[i] = dctx->entropy.rep[i]; + } + + if (ERR_isError(BIT_initDStream(&seqState.DStream, ip, (nuint)(iend - ip)))) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); + ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); + ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); + assert(dst != null); + { + /* some static analyzer believe that @sequence is not initialized (it necessarily is, since for(;;) loop as at least one iteration) */ + seq_t sequence = new seq_t + { + litLength = 0, + matchLength = 0, + offset = 0, + }; + for (; nbSeq != 0; nbSeq--) + { + sequence = ZSTD_decodeSequence(&seqState, isLongOffset, nbSeq == 1 ? 1 : 0); + if (litPtr + sequence.litLength > dctx->litBufferEnd) + break; + { + nuint oneSeqSize = ZSTD_execSequenceSplitLitBuffer( + op, + oend, + litPtr + sequence.litLength - 32, + sequence, + &litPtr, + litBufferEnd, + prefixStart, + vBase, + dictEnd + ); + if (ERR_isError(oneSeqSize)) + return oneSeqSize; + op += oneSeqSize; + } + } + + if (nbSeq > 0) + { + nuint leftoverLit = (nuint)(dctx->litBufferEnd - litPtr); + if (leftoverLit != 0) + { + if (leftoverLit > (nuint)(oend - op)) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall) + ); + } + + ZSTD_safecopyDstBeforeSrc(op, litPtr, (nint)leftoverLit); + sequence.litLength -= leftoverLit; + op += leftoverLit; + } + + litPtr = dctx->litExtraBuffer; + litBufferEnd = dctx->litExtraBuffer + (1 << 16); + dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst; + { + nuint oneSeqSize = ZSTD_execSequence( + op, + oend, + sequence, + &litPtr, + litBufferEnd, + prefixStart, + vBase, + dictEnd + ); + if (ERR_isError(oneSeqSize)) + return oneSeqSize; + op += oneSeqSize; + } + + nbSeq--; + } + } + + if (nbSeq > 0) + { + for (; nbSeq != 0; nbSeq--) + { + seq_t sequence = ZSTD_decodeSequence( + &seqState, + isLongOffset, + nbSeq == 1 ? 1 : 0 + ); + nuint oneSeqSize = ZSTD_execSequence( + op, + oend, + sequence, + &litPtr, + litBufferEnd, + prefixStart, + vBase, + dictEnd + ); + if (ERR_isError(oneSeqSize)) + return oneSeqSize; + op += oneSeqSize; + } + } + + if (nbSeq != 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + if (BIT_endOfDStream(&seqState.DStream) == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + { + uint i; + for (i = 0; i < 3; i++) + dctx->entropy.rep[i] = (uint)(&seqState.prevOffset.e0)[i]; + } + } + + if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split) + { + /* split hasn't been reached yet, first get dst then copy litExtraBuffer */ + nuint lastLLSize = (nuint)(litBufferEnd - litPtr); + if (lastLLSize > (nuint)(oend - op)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (op != null) + { + memmove(op, litPtr, lastLLSize); + op += lastLLSize; + } + + litPtr = dctx->litExtraBuffer; + litBufferEnd = dctx->litExtraBuffer + (1 << 16); + dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst; + } + + { + nuint lastLLSize = (nuint)(litBufferEnd - litPtr); + if (lastLLSize > (nuint)(oend - op)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (op != null) + { + memcpy(op, litPtr, (uint)lastLLSize); + op += lastLLSize; + } + } + + return (nuint)(op - ostart); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_decompressSequences_body( + ZSTD_DCtx_s* dctx, + void* dst, + nuint maxDstSize, + void* seqStart, + nuint seqSize, + int nbSeq, + ZSTD_longOffset_e isLongOffset + ) + { + // HACK, force nbSeq to stack (better register usage) + System.Threading.Volatile.Read(ref nbSeq); + byte* ip = (byte*)seqStart; + byte* iend = ip + seqSize; + byte* ostart = (byte*)dst; + byte* oend = + dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_not_in_dst + ? ZSTD_maybeNullPtrAdd(ostart, (nint)maxDstSize) + : dctx->litBuffer; + byte* op = ostart; + byte* litPtr = dctx->litPtr; + byte* litEnd = litPtr + dctx->litSize; + byte* prefixStart = (byte*)dctx->prefixStart; + byte* vBase = (byte*)dctx->virtualStart; + byte* dictEnd = (byte*)dctx->dictEnd; + if (nbSeq != 0) + { + seqState_t seqState; + System.Runtime.CompilerServices.Unsafe.SkipInit(out seqState); + dctx->fseEntropy = 1; + { + uint i; + for (i = 0; i < 3; i++) + System.Runtime.CompilerServices.Unsafe.Add(ref seqState.prevOffset.e0, (int)i) = + dctx->entropy.rep[i]; + } + + if (ERR_isError(BIT_initDStream(ref seqState.DStream, ip, (nuint)(iend - ip)))) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + ZSTD_initFseState(ref seqState.stateLL, ref seqState.DStream, dctx->LLTptr); + ZSTD_initFseState(ref seqState.stateOffb, ref seqState.DStream, dctx->OFTptr); + ZSTD_initFseState(ref seqState.stateML, ref seqState.DStream, dctx->MLTptr); + assert(dst != null); + nuint seqState_DStream_bitContainer = seqState.DStream.bitContainer; + uint seqState_DStream_bitsConsumed = seqState.DStream.bitsConsumed; + sbyte* seqState_DStream_ptr = seqState.DStream.ptr; + sbyte* seqState_DStream_start = seqState.DStream.start; + sbyte* seqState_DStream_limitPtr = seqState.DStream.limitPtr; + for (; nbSeq != 0; nbSeq--) + { + nuint sequence_litLength; + nuint sequence_matchLength; + nuint sequence_offset; + ZSTD_seqSymbol* llDInfo = seqState.stateLL.table + seqState.stateLL.state; + ZSTD_seqSymbol* mlDInfo = seqState.stateML.table + seqState.stateML.state; + ZSTD_seqSymbol* ofDInfo = seqState.stateOffb.table + seqState.stateOffb.state; + sequence_matchLength = mlDInfo->baseValue; + sequence_litLength = llDInfo->baseValue; + { + uint ofBase = ofDInfo->baseValue; + byte llBits = llDInfo->nbAdditionalBits; + byte mlBits = mlDInfo->nbAdditionalBits; + byte ofBits = ofDInfo->nbAdditionalBits; + byte totalBits = (byte)(llBits + mlBits + ofBits); + ushort llNext = llDInfo->nextState; + ushort mlNext = mlDInfo->nextState; + ushort ofNext = ofDInfo->nextState; + uint llnbBits = llDInfo->nbBits; + uint mlnbBits = mlDInfo->nbBits; + uint ofnbBits = ofDInfo->nbBits; + assert(llBits <= 16); + assert(mlBits <= 16); + assert(ofBits <= 31); + { + nuint offset; + if (ofBits > 1) + { + if (MEM_32bits && isLongOffset != default && ofBits >= 25) + { + /* Always read extra bits, this keeps the logic simple, + * avoids branches, and avoids accidentally reading 0 bits. + */ + const uint extraBits = 30 - 25; + offset = + ofBase + + ( + BIT_readBitsFast( + seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + ofBits - extraBits + ) << (int)extraBits + ); + BIT_reloadDStream( + ref seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + ref seqState_DStream_ptr, + seqState_DStream_start, + seqState_DStream_limitPtr + ); + offset += BIT_readBitsFast( + seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + extraBits + ); + } + else + { + offset = + ofBase + + BIT_readBitsFast( + seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + ofBits + ); + if (MEM_32bits) + BIT_reloadDStream( + ref seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + ref seqState_DStream_ptr, + seqState_DStream_start, + seqState_DStream_limitPtr + ); + } + + seqState.prevOffset.e2 = seqState.prevOffset.e1; + seqState.prevOffset.e1 = seqState.prevOffset.e0; + seqState.prevOffset.e0 = offset; + } + else + { + uint ll0 = llDInfo->baseValue == 0 ? 1U : 0U; + if (ofBits == 0) + { + offset = System.Runtime.CompilerServices.Unsafe.Add( + ref seqState.prevOffset.e0, + (int)ll0 + ); + seqState.prevOffset.e1 = System.Runtime.CompilerServices.Unsafe.Add( + ref seqState.prevOffset.e0, + ll0 == 0 ? 1 : 0 + ); + seqState.prevOffset.e0 = offset; + } + else + { + offset = + ofBase + + ll0 + + BIT_readBitsFast( + seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + 1 + ); + { + nuint temp = + offset == 3 + ? seqState.prevOffset.e0 - 1 + : System.Runtime.CompilerServices.Unsafe.Add( + ref seqState.prevOffset.e0, + (int)offset + ); + temp -= temp == 0 ? 1U : 0U; + if (offset != 1) + seqState.prevOffset.e2 = seqState.prevOffset.e1; + seqState.prevOffset.e1 = seqState.prevOffset.e0; + seqState.prevOffset.e0 = offset = temp; + } + } + } + + sequence_offset = offset; + } + + if (mlBits > 0) + sequence_matchLength += BIT_readBitsFast( + seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + mlBits + ); + if (MEM_32bits && mlBits + llBits >= 25 - (30 - 25)) + BIT_reloadDStream( + ref seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + ref seqState_DStream_ptr, + seqState_DStream_start, + seqState_DStream_limitPtr + ); + if (MEM_64bits && totalBits >= 57 - (9 + 9 + 8)) + BIT_reloadDStream( + ref seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + ref seqState_DStream_ptr, + seqState_DStream_start, + seqState_DStream_limitPtr + ); + if (llBits > 0) + sequence_litLength += BIT_readBitsFast( + seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + llBits + ); + if (MEM_32bits) + BIT_reloadDStream( + ref seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + ref seqState_DStream_ptr, + seqState_DStream_start, + seqState_DStream_limitPtr + ); + if ((nbSeq == 1 ? 1 : 0) == 0) + { + ZSTD_updateFseStateWithDInfo( + ref seqState.stateLL, + seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + llNext, + llnbBits + ); + ZSTD_updateFseStateWithDInfo( + ref seqState.stateML, + seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + mlNext, + mlnbBits + ); + if (MEM_32bits) + BIT_reloadDStream( + ref seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + ref seqState_DStream_ptr, + seqState_DStream_start, + seqState_DStream_limitPtr + ); + ZSTD_updateFseStateWithDInfo( + ref seqState.stateOffb, + seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + ofNext, + ofnbBits + ); + BIT_reloadDStream( + ref seqState_DStream_bitContainer, + ref seqState_DStream_bitsConsumed, + ref seqState_DStream_ptr, + seqState_DStream_start, + seqState_DStream_limitPtr + ); + } + } + + nuint oneSeqSize; + { + byte* oLitEnd = op + sequence_litLength; + oneSeqSize = sequence_litLength + sequence_matchLength; + /* risk : address space overflow (32-bits) */ + byte* oMatchEnd = op + oneSeqSize; + /* risk : address space underflow on oend=NULL */ + byte* oend_w = oend - 32; + byte* iLitEnd = litPtr + sequence_litLength; + byte* match = oLitEnd - sequence_offset; + assert(op != null); + assert(oend_w < oend); + if ( + iLitEnd > litEnd + || oMatchEnd > oend_w + || MEM_32bits && (nuint)(oend - op) < oneSeqSize + 32 + ) + { + oneSeqSize = ZSTD_execSequenceEnd( + op, + oend, + new seq_t + { + litLength = sequence_litLength, + matchLength = sequence_matchLength, + offset = sequence_offset, + }, + &litPtr, + litEnd, + prefixStart, + vBase, + dictEnd + ); + goto returnOneSeqSize; + } + + assert(op <= oLitEnd); + assert(oLitEnd < oMatchEnd); + assert(oMatchEnd <= oend); + assert(iLitEnd <= litEnd); + assert(oLitEnd <= oend_w); + assert(oMatchEnd <= oend_w); + assert(32 >= 16); + ZSTD_copy16(op, litPtr); + if (sequence_litLength > 16) + { + ZSTD_wildcopy( + op + 16, + litPtr + 16, + (nint)(sequence_litLength - 16), + ZSTD_overlap_e.ZSTD_no_overlap + ); + } + + byte* opInner = oLitEnd; + litPtr = iLitEnd; + if (sequence_offset > (nuint)(oLitEnd - prefixStart)) + { + if (sequence_offset > (nuint)(oLitEnd - vBase)) + { + oneSeqSize = unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected) + ); + goto returnOneSeqSize; + } + + match = dictEnd + (match - prefixStart); + if (match + sequence_matchLength <= dictEnd) + { + memmove(oLitEnd, match, sequence_matchLength); + goto returnOneSeqSize; + } + + { + nuint length1 = (nuint)(dictEnd - match); + memmove(oLitEnd, match, length1); + opInner = oLitEnd + length1; + sequence_matchLength -= length1; + match = prefixStart; + } + } + + assert(opInner <= oMatchEnd); + assert(oMatchEnd <= oend_w); + assert(match >= prefixStart); + assert(sequence_matchLength >= 1); + if (sequence_offset >= 16) + { + ZSTD_wildcopy( + opInner, + match, + (nint)sequence_matchLength, + ZSTD_overlap_e.ZSTD_no_overlap + ); + goto returnOneSeqSize; + } + + assert(sequence_offset < 16); + ZSTD_overlapCopy8(ref opInner, ref match, sequence_offset); + if (sequence_matchLength > 8) + { + assert(opInner < oMatchEnd); + ZSTD_wildcopy( + opInner, + match, + (nint)sequence_matchLength - 8, + ZSTD_overlap_e.ZSTD_overlap_src_before_dst + ); + } + + returnOneSeqSize: + ; + } + + if (ERR_isError(oneSeqSize)) + return oneSeqSize; + op += oneSeqSize; + } + + assert(nbSeq == 0); + if ( + BIT_endOfDStream( + seqState_DStream_bitsConsumed, + seqState_DStream_ptr, + seqState_DStream_start + ) == 0 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + { + uint i; + for (i = 0; i < 3; i++) + dctx->entropy.rep[i] = (uint) + System.Runtime.CompilerServices.Unsafe.Add( + ref seqState.prevOffset.e0, + (int)i + ); + } + } + + { + nuint lastLLSize = (nuint)(litEnd - litPtr); + if (lastLLSize > (nuint)(oend - op)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (op != null) + { + memcpy(op, litPtr, (uint)lastLLSize); + op += lastLLSize; + } + } + + return (nuint)(op - ostart); + } + + private static nuint ZSTD_decompressSequences_default( + ZSTD_DCtx_s* dctx, + void* dst, + nuint maxDstSize, + void* seqStart, + nuint seqSize, + int nbSeq, + ZSTD_longOffset_e isLongOffset + ) + { + return ZSTD_decompressSequences_body( + dctx, + dst, + maxDstSize, + seqStart, + seqSize, + nbSeq, + isLongOffset + ); + } + + private static nuint ZSTD_decompressSequencesSplitLitBuffer_default( + ZSTD_DCtx_s* dctx, + void* dst, + nuint maxDstSize, + void* seqStart, + nuint seqSize, + int nbSeq, + ZSTD_longOffset_e isLongOffset + ) + { + return ZSTD_decompressSequences_bodySplitLitBuffer( + dctx, + dst, + maxDstSize, + seqStart, + seqSize, + nbSeq, + isLongOffset + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_prefetchMatch( + nuint prefetchPos, + seq_t sequence, + byte* prefixStart, + byte* dictEnd + ) + { + prefetchPos += sequence.litLength; + { + byte* matchBase = sequence.offset > prefetchPos ? dictEnd : prefixStart; + /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted. + * No consequence though : memory address is only used for prefetching, not for dereferencing */ + byte* match = ZSTD_wrappedPtrSub( + ZSTD_wrappedPtrAdd(matchBase, (nint)prefetchPos), + (nint)sequence.offset + ); +#if NETCOREAPP3_0_OR_GREATER + if (System.Runtime.Intrinsics.X86.Sse.IsSupported) + { + System.Runtime.Intrinsics.X86.Sse.Prefetch0(match); + System.Runtime.Intrinsics.X86.Sse.Prefetch0(match + 64); + } +#endif + } + + return prefetchPos + sequence.matchLength; + } + + /* This decoding function employs prefetching + * to reduce latency impact of cache misses. + * It's generally employed when block contains a significant portion of long-distance matches + * or when coupled with a "cold" dictionary */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_decompressSequencesLong_body( + ZSTD_DCtx_s* dctx, + void* dst, + nuint maxDstSize, + void* seqStart, + nuint seqSize, + int nbSeq, + ZSTD_longOffset_e isLongOffset + ) + { + byte* ip = (byte*)seqStart; + byte* iend = ip + seqSize; + byte* ostart = (byte*)dst; + byte* oend = + dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_in_dst + ? dctx->litBuffer + : ZSTD_maybeNullPtrAdd(ostart, (nint)maxDstSize); + byte* op = ostart; + byte* litPtr = dctx->litPtr; + byte* litBufferEnd = dctx->litBufferEnd; + byte* prefixStart = (byte*)dctx->prefixStart; + byte* dictStart = (byte*)dctx->virtualStart; + byte* dictEnd = (byte*)dctx->dictEnd; + if (nbSeq != 0) + { + seq_t* sequences = stackalloc seq_t[8]; + int seqAdvance = nbSeq < 8 ? nbSeq : 8; + seqState_t seqState; + int seqNb; + /* track position relative to prefixStart */ + nuint prefetchPos = (nuint)(op - prefixStart); + dctx->fseEntropy = 1; + { + int i; + for (i = 0; i < 3; i++) + (&seqState.prevOffset.e0)[i] = dctx->entropy.rep[i]; + } + + assert(dst != null); + assert(iend >= ip); + if (ERR_isError(BIT_initDStream(&seqState.DStream, ip, (nuint)(iend - ip)))) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); + ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); + ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); + for (seqNb = 0; seqNb < seqAdvance; seqNb++) + { + seq_t sequence = ZSTD_decodeSequence( + &seqState, + isLongOffset, + seqNb == nbSeq - 1 ? 1 : 0 + ); + prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd); + sequences[seqNb] = sequence; + } + + for (; seqNb < nbSeq; seqNb++) + { + seq_t sequence = ZSTD_decodeSequence( + &seqState, + isLongOffset, + seqNb == nbSeq - 1 ? 1 : 0 + ); + if ( + dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split + && litPtr + sequences[seqNb - 8 & 8 - 1].litLength > dctx->litBufferEnd + ) + { + /* lit buffer is reaching split point, empty out the first buffer and transition to litExtraBuffer */ + nuint leftoverLit = (nuint)(dctx->litBufferEnd - litPtr); + if (leftoverLit != 0) + { + if (leftoverLit > (nuint)(oend - op)) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall) + ); + } + + ZSTD_safecopyDstBeforeSrc(op, litPtr, (nint)leftoverLit); + sequences[seqNb - 8 & 8 - 1].litLength -= leftoverLit; + op += leftoverLit; + } + + litPtr = dctx->litExtraBuffer; + litBufferEnd = dctx->litExtraBuffer + (1 << 16); + dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst; + { + nuint oneSeqSize = ZSTD_execSequence( + op, + oend, + sequences[seqNb - 8 & 8 - 1], + &litPtr, + litBufferEnd, + prefixStart, + dictStart, + dictEnd + ); + if (ERR_isError(oneSeqSize)) + return oneSeqSize; + prefetchPos = ZSTD_prefetchMatch( + prefetchPos, + sequence, + prefixStart, + dictEnd + ); + sequences[seqNb & 8 - 1] = sequence; + op += oneSeqSize; + } + } + else + { + /* lit buffer is either wholly contained in first or second split, or not split at all*/ + nuint oneSeqSize = + dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split + ? ZSTD_execSequenceSplitLitBuffer( + op, + oend, + litPtr + sequences[seqNb - 8 & 8 - 1].litLength - 32, + sequences[seqNb - 8 & 8 - 1], + &litPtr, + litBufferEnd, + prefixStart, + dictStart, + dictEnd + ) + : ZSTD_execSequence( + op, + oend, + sequences[seqNb - 8 & 8 - 1], + &litPtr, + litBufferEnd, + prefixStart, + dictStart, + dictEnd + ); + if (ERR_isError(oneSeqSize)) + return oneSeqSize; + prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd); + sequences[seqNb & 8 - 1] = sequence; + op += oneSeqSize; + } + } + + if (BIT_endOfDStream(&seqState.DStream) == 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_corruption_detected)); + } + + seqNb -= seqAdvance; + for (; seqNb < nbSeq; seqNb++) + { + seq_t* sequence = &sequences[seqNb & 8 - 1]; + if ( + dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split + && litPtr + sequence->litLength > dctx->litBufferEnd + ) + { + nuint leftoverLit = (nuint)(dctx->litBufferEnd - litPtr); + if (leftoverLit != 0) + { + if (leftoverLit > (nuint)(oend - op)) + { + return unchecked( + (nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall) + ); + } + + ZSTD_safecopyDstBeforeSrc(op, litPtr, (nint)leftoverLit); + sequence->litLength -= leftoverLit; + op += leftoverLit; + } + + litPtr = dctx->litExtraBuffer; + litBufferEnd = dctx->litExtraBuffer + (1 << 16); + dctx->litBufferLocation = ZSTD_litLocation_e.ZSTD_not_in_dst; + { + nuint oneSeqSize = ZSTD_execSequence( + op, + oend, + *sequence, + &litPtr, + litBufferEnd, + prefixStart, + dictStart, + dictEnd + ); + if (ERR_isError(oneSeqSize)) + return oneSeqSize; + op += oneSeqSize; + } + } + else + { + nuint oneSeqSize = + dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split + ? ZSTD_execSequenceSplitLitBuffer( + op, + oend, + litPtr + sequence->litLength - 32, + *sequence, + &litPtr, + litBufferEnd, + prefixStart, + dictStart, + dictEnd + ) + : ZSTD_execSequence( + op, + oend, + *sequence, + &litPtr, + litBufferEnd, + prefixStart, + dictStart, + dictEnd + ); + if (ERR_isError(oneSeqSize)) + return oneSeqSize; + op += oneSeqSize; + } + } + + { + uint i; + for (i = 0; i < 3; i++) + dctx->entropy.rep[i] = (uint)(&seqState.prevOffset.e0)[i]; + } + } + + if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split) + { + nuint lastLLSize = (nuint)(litBufferEnd - litPtr); + if (lastLLSize > (nuint)(oend - op)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (op != null) + { + memmove(op, litPtr, lastLLSize); + op += lastLLSize; + } + + litPtr = dctx->litExtraBuffer; + litBufferEnd = dctx->litExtraBuffer + (1 << 16); + } + + { + nuint lastLLSize = (nuint)(litBufferEnd - litPtr); + if (lastLLSize > (nuint)(oend - op)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if (op != null) + { + memmove(op, litPtr, lastLLSize); + op += lastLLSize; + } + } + + return (nuint)(op - ostart); + } + + private static nuint ZSTD_decompressSequencesLong_default( + ZSTD_DCtx_s* dctx, + void* dst, + nuint maxDstSize, + void* seqStart, + nuint seqSize, + int nbSeq, + ZSTD_longOffset_e isLongOffset + ) + { + return ZSTD_decompressSequencesLong_body( + dctx, + dst, + maxDstSize, + seqStart, + seqSize, + nbSeq, + isLongOffset + ); + } + + private static nuint ZSTD_decompressSequences( + ZSTD_DCtx_s* dctx, + void* dst, + nuint maxDstSize, + void* seqStart, + nuint seqSize, + int nbSeq, + ZSTD_longOffset_e isLongOffset + ) + { + return ZSTD_decompressSequences_default( + dctx, + dst, + maxDstSize, + seqStart, + seqSize, + nbSeq, + isLongOffset + ); + } + + private static nuint ZSTD_decompressSequencesSplitLitBuffer( + ZSTD_DCtx_s* dctx, + void* dst, + nuint maxDstSize, + void* seqStart, + nuint seqSize, + int nbSeq, + ZSTD_longOffset_e isLongOffset + ) + { + return ZSTD_decompressSequencesSplitLitBuffer_default( + dctx, + dst, + maxDstSize, + seqStart, + seqSize, + nbSeq, + isLongOffset + ); + } + + /* ZSTD_decompressSequencesLong() : + * decompression function triggered when a minimum share of offsets is considered "long", + * aka out of cache. + * note : "long" definition seems overloaded here, sometimes meaning "wider than bitstream register", and sometimes meaning "farther than memory cache distance". + * This function will try to mitigate main memory latency through the use of prefetching */ + private static nuint ZSTD_decompressSequencesLong( + ZSTD_DCtx_s* dctx, + void* dst, + nuint maxDstSize, + void* seqStart, + nuint seqSize, + int nbSeq, + ZSTD_longOffset_e isLongOffset + ) + { + return ZSTD_decompressSequencesLong_default( + dctx, + dst, + maxDstSize, + seqStart, + seqSize, + nbSeq, + isLongOffset + ); + } + + /** + * @returns The total size of the history referenceable by zstd, including + * both the prefix and the extDict. At @p op any offset larger than this + * is invalid. + */ + private static nuint ZSTD_totalHistorySize(byte* op, byte* virtualStart) + { + return (nuint)(op - virtualStart); + } + + /* ZSTD_getOffsetInfo() : + * condition : offTable must be valid + * @return : "share" of long offsets (arbitrarily defined as > (1<<23)) + * compared to maximum possible of (1< table[u].nbAdditionalBits + ? info.maxNbAdditionalBits + : table[u].nbAdditionalBits; + if (table[u].nbAdditionalBits > 22) + info.longOffsetShare += 1; + } + + assert(tableLog <= 8); + info.longOffsetShare <<= (int)(8 - tableLog); + } + + return info; + } + + /** + * @returns The maximum offset we can decode in one read of our bitstream, without + * reloading more bits in the middle of the offset bits read. Any offsets larger + * than this must use the long offset decoder. + */ + private static nuint ZSTD_maxShortOffset() + { + if (MEM_64bits) + { + return unchecked((nuint)(-1)); + } + else + { + /* The maximum offBase is (1 << (STREAM_ACCUMULATOR_MIN + 1)) - 1. + * This offBase would require STREAM_ACCUMULATOR_MIN extra bits. + * Then we have to subtract ZSTD_REP_NUM to get the maximum possible offset. + */ + nuint maxOffbase = ((nuint)1 << (int)((uint)(MEM_32bits ? 25 : 57) + 1)) - 1; + nuint maxOffset = maxOffbase - 3; + assert(ZSTD_highbit32((uint)maxOffbase) == (uint)(MEM_32bits ? 25 : 57)); + return maxOffset; + } + } + + /* ZSTD_decompressBlock_internal() : + * decompress block, starting at `src`, + * into destination buffer `dst`. + * @return : decompressed block size, + * or an error code (which can be tested using ZSTD_isError()) + */ + private static nuint ZSTD_decompressBlock_internal( + ZSTD_DCtx_s* dctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize, + streaming_operation streaming + ) + { + byte* ip = (byte*)src; + if (srcSize > ZSTD_blockSizeMax(dctx)) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_srcSize_wrong)); + } + + { + nuint litCSize = ZSTD_decodeLiteralsBlock( + dctx, + src, + srcSize, + dst, + dstCapacity, + streaming + ); + if (ERR_isError(litCSize)) + return litCSize; + ip += litCSize; + srcSize -= litCSize; + } + + { + /* Compute the maximum block size, which must also work when !frame and fParams are unset. + * Additionally, take the min with dstCapacity to ensure that the totalHistorySize fits in a size_t. + */ + nuint blockSizeMax = + dstCapacity < ZSTD_blockSizeMax(dctx) ? dstCapacity : ZSTD_blockSizeMax(dctx); + nuint totalHistorySize = ZSTD_totalHistorySize( + ZSTD_maybeNullPtrAdd((byte*)dst, (nint)blockSizeMax), + (byte*)dctx->virtualStart + ); + /* isLongOffset must be true if there are long offsets. + * Offsets are long if they are larger than ZSTD_maxShortOffset(). + * We don't expect that to be the case in 64-bit mode. + * + * We check here to see if our history is large enough to allow long offsets. + * If it isn't, then we can't possible have (valid) long offsets. If the offset + * is invalid, then it is okay to read it incorrectly. + * + * If isLongOffsets is true, then we will later check our decoding table to see + * if it is even possible to generate long offsets. + */ + ZSTD_longOffset_e isLongOffset = (ZSTD_longOffset_e)( + MEM_32bits && totalHistorySize > ZSTD_maxShortOffset() ? 1 : 0 + ); + int usePrefetchDecoder = dctx->ddictIsCold; + int nbSeq; + nuint seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, srcSize); + if (ERR_isError(seqHSize)) + return seqHSize; + ip += seqHSize; + srcSize -= seqHSize; + if ((dst == null || dstCapacity == 0) && nbSeq > 0) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if ( + MEM_64bits + && sizeof(nuint) == sizeof(void*) + && unchecked((nuint)(-1)) - (nuint)dst < 1 << 20 + ) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + } + + if ( + isLongOffset != default + || usePrefetchDecoder == 0 && totalHistorySize > 1U << 24 && nbSeq > 8 + ) + { + ZSTD_OffsetInfo info = ZSTD_getOffsetInfo(dctx->OFTptr, nbSeq); + if ( + isLongOffset != default + && info.maxNbAdditionalBits <= (uint)(MEM_32bits ? 25 : 57) + ) + { + isLongOffset = ZSTD_longOffset_e.ZSTD_lo_isRegularOffset; + } + + if (usePrefetchDecoder == 0) + { + /* heuristic values, correspond to 2.73% and 7.81% */ + uint minShare = (uint)(MEM_64bits ? 7 : 20); + usePrefetchDecoder = info.longOffsetShare >= minShare ? 1 : 0; + } + } + + dctx->ddictIsCold = 0; + if (usePrefetchDecoder != 0) + { + return ZSTD_decompressSequencesLong( + dctx, + dst, + dstCapacity, + ip, + srcSize, + nbSeq, + isLongOffset + ); + } + + if (dctx->litBufferLocation == ZSTD_litLocation_e.ZSTD_split) + return ZSTD_decompressSequencesSplitLitBuffer( + dctx, + dst, + dstCapacity, + ip, + srcSize, + nbSeq, + isLongOffset + ); + else + return ZSTD_decompressSequences( + dctx, + dst, + dstCapacity, + ip, + srcSize, + nbSeq, + isLongOffset + ); + } + } + + /*! ZSTD_checkContinuity() : + * check if next `dst` follows previous position, where decompression ended. + * If yes, do nothing (continue on current segment). + * If not, classify previous segment as "external dictionary", and start a new segment. + * This function cannot fail. */ + private static void ZSTD_checkContinuity(ZSTD_DCtx_s* dctx, void* dst, nuint dstSize) + { + if (dst != dctx->previousDstEnd && dstSize > 0) + { + dctx->dictEnd = dctx->previousDstEnd; + dctx->virtualStart = + (sbyte*)dst - ((sbyte*)dctx->previousDstEnd - (sbyte*)dctx->prefixStart); + dctx->prefixStart = dst; + dctx->previousDstEnd = dst; + } + } + + /* Internal definition of ZSTD_decompressBlock() to avoid deprecation warnings. */ + private static nuint ZSTD_decompressBlock_deprecated( + ZSTD_DCtx_s* dctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + nuint dSize; + dctx->isFrameDecompression = 0; + ZSTD_checkContinuity(dctx, dst, dstCapacity); + dSize = ZSTD_decompressBlock_internal( + dctx, + dst, + dstCapacity, + src, + srcSize, + streaming_operation.not_streaming + ); + { + nuint err_code = dSize; + if (ERR_isError(err_code)) + { + return err_code; + } + } + + dctx->previousDstEnd = (sbyte*)dst + dSize; + return dSize; + } + + /* NOTE: Must just wrap ZSTD_decompressBlock_deprecated() */ + public static nuint ZSTD_decompressBlock( + ZSTD_DCtx_s* dctx, + void* dst, + nuint dstCapacity, + void* src, + nuint srcSize + ) + { + return ZSTD_decompressBlock_deprecated(dctx, dst, dstCapacity, src, srcSize); + } + + private static void ZSTD_initFseState( + ref ZSTD_fseState DStatePtr, + ref BIT_DStream_t bitD, + ZSTD_seqSymbol* dt + ) + { + void* ptr = dt; + ZSTD_seqSymbol_header* DTableH = (ZSTD_seqSymbol_header*)ptr; + DStatePtr.state = BIT_readBits(bitD.bitContainer, ref bitD.bitsConsumed, DTableH->tableLog); + BIT_reloadDStream( + ref bitD.bitContainer, + ref bitD.bitsConsumed, + ref bitD.ptr, + bitD.start, + bitD.limitPtr + ); + DStatePtr.table = dt + 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_updateFseStateWithDInfo( + ref ZSTD_fseState DStatePtr, + nuint bitD_bitContainer, + ref uint bitD_bitsConsumed, + ushort nextState, + uint nbBits + ) + { + nuint lowBits = BIT_readBits(bitD_bitContainer, ref bitD_bitsConsumed, nbBits); + DStatePtr.state = nextState + lowBits; + } + + /*! ZSTD_overlapCopy8() : + * Copies 8 bytes from ip to op and updates op and ip where ip <= op. + * If the offset is < 8 then the offset is spread to at least 8 bytes. + * + * Precondition: *ip <= *op + * Postcondition: *op - *op >= 8 + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_overlapCopy8(ref byte* op, ref byte* ip, nuint offset) + { + assert(ip <= op); + if (offset < 8) + { + int sub2 = dec64table[offset]; + op[0] = ip[0]; + op[1] = ip[1]; + op[2] = ip[2]; + op[3] = ip[3]; + ip += dec32table[offset]; + ZSTD_copy4(op + 4, ip); + ip -= sub2; + } + else + { + ZSTD_copy8(op, ip); + } + + ip += 8; + op += 8; + assert(op - ip >= 8); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompressInternal.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompressInternal.cs new file mode 100644 index 00000000..670f5846 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDecompressInternal.cs @@ -0,0 +1,394 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_LL_base => + new uint[36] + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 18, + 20, + 22, + 24, + 28, + 32, + 40, + 48, + 64, + 0x80, + 0x100, + 0x200, + 0x400, + 0x800, + 0x1000, + 0x2000, + 0x4000, + 0x8000, + 0x10000, + }; + private static uint* LL_base => + (uint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_LL_base) + ); +#else + + private static readonly uint* LL_base = GetArrayPointer( + new uint[36] + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 18, + 20, + 22, + 24, + 28, + 32, + 40, + 48, + 64, + 0x80, + 0x100, + 0x200, + 0x400, + 0x800, + 0x1000, + 0x2000, + 0x4000, + 0x8000, + 0x10000, + } + ); +#endif +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_OF_base => + new uint[32] + { + 0, + 1, + 1, + 5, + 0xD, + 0x1D, + 0x3D, + 0x7D, + 0xFD, + 0x1FD, + 0x3FD, + 0x7FD, + 0xFFD, + 0x1FFD, + 0x3FFD, + 0x7FFD, + 0xFFFD, + 0x1FFFD, + 0x3FFFD, + 0x7FFFD, + 0xFFFFD, + 0x1FFFFD, + 0x3FFFFD, + 0x7FFFFD, + 0xFFFFFD, + 0x1FFFFFD, + 0x3FFFFFD, + 0x7FFFFFD, + 0xFFFFFFD, + 0x1FFFFFFD, + 0x3FFFFFFD, + 0x7FFFFFFD, + }; + private static uint* OF_base => + (uint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_OF_base) + ); +#else + + private static readonly uint* OF_base = GetArrayPointer( + new uint[32] + { + 0, + 1, + 1, + 5, + 0xD, + 0x1D, + 0x3D, + 0x7D, + 0xFD, + 0x1FD, + 0x3FD, + 0x7FD, + 0xFFD, + 0x1FFD, + 0x3FFD, + 0x7FFD, + 0xFFFD, + 0x1FFFD, + 0x3FFFD, + 0x7FFFD, + 0xFFFFD, + 0x1FFFFD, + 0x3FFFFD, + 0x7FFFFD, + 0xFFFFFD, + 0x1FFFFFD, + 0x3FFFFFD, + 0x7FFFFFD, + 0xFFFFFFD, + 0x1FFFFFFD, + 0x3FFFFFFD, + 0x7FFFFFFD, + } + ); +#endif +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_OF_bits => + new byte[32] + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + }; + private static byte* OF_bits => + (byte*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_OF_bits) + ); +#else + + private static readonly byte* OF_bits = GetArrayPointer( + new byte[32] + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + } + ); +#endif +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_ML_base => + new uint[53] + { + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 37, + 39, + 41, + 43, + 47, + 51, + 59, + 67, + 83, + 99, + 0x83, + 0x103, + 0x203, + 0x403, + 0x803, + 0x1003, + 0x2003, + 0x4003, + 0x8003, + 0x10003, + }; + private static uint* ML_base => + (uint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_ML_base) + ); +#else + + private static readonly uint* ML_base = GetArrayPointer( + new uint[53] + { + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 37, + 39, + 41, + 43, + 47, + 51, + 59, + 67, + 83, + 99, + 0x83, + 0x103, + 0x203, + 0x403, + 0x803, + 0x1003, + 0x2003, + 0x4003, + 0x8003, + 0x10003, + } + ); +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_DCtx_get_bmi2(ZSTD_DCtx_s* dctx) + { + return 0; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDoubleFast.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDoubleFast.cs new file mode 100644 index 00000000..55d383ec --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdDoubleFast.cs @@ -0,0 +1,1118 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + private static void ZSTD_fillDoubleHashTableForCDict( + ZSTD_MatchState_t* ms, + void* end, + ZSTD_dictTableLoadMethod_e dtlm + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashLarge = ms->hashTable; + uint hBitsL = cParams->hashLog + 8; + uint mls = cParams->minMatch; + uint* hashSmall = ms->chainTable; + uint hBitsS = cParams->chainLog + 8; + byte* @base = ms->window.@base; + byte* ip = @base + ms->nextToUpdate; + byte* iend = (byte*)end - 8; + const uint fastHashFillStep = 3; + for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) + { + uint curr = (uint)(ip - @base); + uint i; + for (i = 0; i < fastHashFillStep; ++i) + { + nuint smHashAndTag = ZSTD_hashPtr(ip + i, hBitsS, mls); + nuint lgHashAndTag = ZSTD_hashPtr(ip + i, hBitsL, 8); + if (i == 0) + { + ZSTD_writeTaggedIndex(hashSmall, smHashAndTag, curr + i); + } + + if (i == 0 || hashLarge[lgHashAndTag >> 8] == 0) + { + ZSTD_writeTaggedIndex(hashLarge, lgHashAndTag, curr + i); + } + + if (dtlm == ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast) + break; + } + } + } + + private static void ZSTD_fillDoubleHashTableForCCtx( + ZSTD_MatchState_t* ms, + void* end, + ZSTD_dictTableLoadMethod_e dtlm + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashLarge = ms->hashTable; + uint hBitsL = cParams->hashLog; + uint mls = cParams->minMatch; + uint* hashSmall = ms->chainTable; + uint hBitsS = cParams->chainLog; + byte* @base = ms->window.@base; + byte* ip = @base + ms->nextToUpdate; + byte* iend = (byte*)end - 8; + const uint fastHashFillStep = 3; + for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) + { + uint curr = (uint)(ip - @base); + uint i; + for (i = 0; i < fastHashFillStep; ++i) + { + nuint smHash = ZSTD_hashPtr(ip + i, hBitsS, mls); + nuint lgHash = ZSTD_hashPtr(ip + i, hBitsL, 8); + if (i == 0) + hashSmall[smHash] = curr + i; + if (i == 0 || hashLarge[lgHash] == 0) + hashLarge[lgHash] = curr + i; + if (dtlm == ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast) + break; + } + } + } + + private static void ZSTD_fillDoubleHashTable( + ZSTD_MatchState_t* ms, + void* end, + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp + ) + { + if (tfp == ZSTD_tableFillPurpose_e.ZSTD_tfp_forCDict) + { + ZSTD_fillDoubleHashTableForCDict(ms, end, dtlm); + } + else + { + ZSTD_fillDoubleHashTableForCCtx(ms, end, dtlm); + } + } + +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_dummy => + new byte[10] { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xe2, 0xb4 }; + private static byte* dummy => + (byte*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_dummy) + ); +#else + + private static readonly byte* dummy = GetArrayPointer( + new byte[10] { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xe2, 0xb4 } + ); +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_compressBlock_doubleFast_noDict_generic( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize, + uint mls + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashLong = ms->hashTable; + uint hBitsL = cParams->hashLog; + uint* hashSmall = ms->chainTable; + uint hBitsS = cParams->chainLog; + byte* @base = ms->window.@base; + byte* istart = (byte*)src; + byte* anchor = istart; + uint endIndex = (uint)((nuint)(istart - @base) + srcSize); + /* presumes that, if there is a dictionary, it must be using Attach mode */ + uint prefixLowestIndex = ZSTD_getLowestPrefixIndex(ms, endIndex, cParams->windowLog); + byte* prefixLowest = @base + prefixLowestIndex; + byte* iend = istart + srcSize; + byte* ilimit = iend - 8; + uint offset_1 = rep[0], + offset_2 = rep[1]; + uint offsetSaved1 = 0, + offsetSaved2 = 0; + nuint mLength; + uint offset; + uint curr; + /* how many positions to search before increasing step size */ + const nuint kStepIncr = 1 << 8; + /* the position at which to increment the step size if no match is found */ + byte* nextStep; + /* the current step size */ + nuint step; + /* the long hash at ip */ + nuint hl0; + /* the long hash at ip1 */ + nuint hl1; + /* the long match index for ip */ + uint idxl0; + /* the long match index for ip1 */ + uint idxl1; + /* the long match for ip */ + byte* matchl0; + /* the short match for ip */ + byte* matchs0; + /* the long match for ip1 */ + byte* matchl1; + /* matchs0 or safe address */ + byte* matchs0_safe; + /* the current position */ + byte* ip = istart; + /* the next position */ + byte* ip1; + ip += ip - prefixLowest == 0 ? 1 : 0; + { + uint current = (uint)(ip - @base); + uint windowLow = ZSTD_getLowestPrefixIndex(ms, current, cParams->windowLog); + uint maxRep = current - windowLow; + if (offset_2 > maxRep) + { + offsetSaved2 = offset_2; + offset_2 = 0; + } + + if (offset_1 > maxRep) + { + offsetSaved1 = offset_1; + offset_1 = 0; + } + } + + while (true) + { + step = 1; + nextStep = ip + kStepIncr; + ip1 = ip + step; + if (ip1 > ilimit) + { + goto _cleanup; + } + + hl0 = ZSTD_hashPtr(ip, hBitsL, 8); + idxl0 = hashLong[hl0]; + matchl0 = @base + idxl0; + do + { + nuint hs0 = ZSTD_hashPtr(ip, hBitsS, mls); + uint idxs0 = hashSmall[hs0]; + curr = (uint)(ip - @base); + matchs0 = @base + idxs0; + hashLong[hl0] = hashSmall[hs0] = curr; + if (offset_1 > 0 && MEM_read32(ip + 1 - offset_1) == MEM_read32(ip + 1)) + { + mLength = ZSTD_count(ip + 1 + 4, ip + 1 + 4 - offset_1, iend) + 4; + ip++; + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, (nuint)(ip - anchor), anchor, iend, 1, mLength); + goto _match_stored; + } + + hl1 = ZSTD_hashPtr(ip1, hBitsL, 8); + { + byte* matchl0_safe = ZSTD_selectAddr( + idxl0, + prefixLowestIndex, + matchl0, + &dummy[0] + ); + if (MEM_read64(matchl0_safe) == MEM_read64(ip) && matchl0_safe == matchl0) + { + mLength = ZSTD_count(ip + 8, matchl0 + 8, iend) + 8; + offset = (uint)(ip - matchl0); + while (ip > anchor && matchl0 > prefixLowest && ip[-1] == matchl0[-1]) + { + ip--; + matchl0--; + mLength++; + } + + goto _match_found; + } + } + + idxl1 = hashLong[hl1]; + matchl1 = @base + idxl1; + matchs0_safe = ZSTD_selectAddr(idxs0, prefixLowestIndex, matchs0, &dummy[0]); + if (MEM_read32(matchs0_safe) == MEM_read32(ip) && matchs0_safe == matchs0) + { + goto _search_next_long; + } + + if (ip1 >= nextStep) + { +#if NETCOREAPP3_0_OR_GREATER + if (System.Runtime.Intrinsics.X86.Sse.IsSupported) + { + System.Runtime.Intrinsics.X86.Sse.Prefetch0(ip1 + 64); + System.Runtime.Intrinsics.X86.Sse.Prefetch0(ip1 + 128); + } +#endif + + step++; + nextStep += kStepIncr; + } + + ip = ip1; + ip1 += step; + hl0 = hl1; + idxl0 = idxl1; + matchl0 = matchl1; + } while (ip1 <= ilimit); + _cleanup: + offsetSaved2 = offsetSaved1 != 0 && offset_1 != 0 ? offsetSaved1 : offsetSaved2; + rep[0] = offset_1 != 0 ? offset_1 : offsetSaved1; + rep[1] = offset_2 != 0 ? offset_2 : offsetSaved2; + return (nuint)(iend - anchor); + _search_next_long: + mLength = ZSTD_count(ip + 4, matchs0 + 4, iend) + 4; + offset = (uint)(ip - matchs0); + if (idxl1 > prefixLowestIndex && MEM_read64(matchl1) == MEM_read64(ip1)) + { + nuint l1len = ZSTD_count(ip1 + 8, matchl1 + 8, iend) + 8; + if (l1len > mLength) + { + ip = ip1; + mLength = l1len; + offset = (uint)(ip - matchl1); + matchs0 = matchl1; + } + } + + while (ip > anchor && matchs0 > prefixLowest && ip[-1] == matchs0[-1]) + { + ip--; + matchs0--; + mLength++; + } + + _match_found: + offset_2 = offset_1; + offset_1 = offset; + if (step < 4) + { + hashLong[hl1] = (uint)(ip1 - @base); + } + + assert(offset > 0); + ZSTD_storeSeq(seqStore, (nuint)(ip - anchor), anchor, iend, offset + 3, mLength); + _match_stored: + ip += mLength; + anchor = ip; + if (ip <= ilimit) + { + { + uint indexToInsert = curr + 2; + hashLong[ZSTD_hashPtr(@base + indexToInsert, hBitsL, 8)] = indexToInsert; + hashLong[ZSTD_hashPtr(ip - 2, hBitsL, 8)] = (uint)(ip - 2 - @base); + hashSmall[ZSTD_hashPtr(@base + indexToInsert, hBitsS, mls)] = indexToInsert; + hashSmall[ZSTD_hashPtr(ip - 1, hBitsS, mls)] = (uint)(ip - 1 - @base); + } + + while (ip <= ilimit && offset_2 > 0 && MEM_read32(ip) == MEM_read32(ip - offset_2)) + { + /* store sequence */ + nuint rLength = ZSTD_count(ip + 4, ip + 4 - offset_2, iend) + 4; + /* swap offset_2 <=> offset_1 */ + uint tmpOff = offset_2; + offset_2 = offset_1; + offset_1 = tmpOff; + hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (uint)(ip - @base); + hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (uint)(ip - @base); + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, 0, anchor, iend, 1, rLength); + ip += rLength; + anchor = ip; + continue; + } + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_compressBlock_doubleFast_dictMatchState_generic( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize, + uint mls + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashLong = ms->hashTable; + uint hBitsL = cParams->hashLog; + uint* hashSmall = ms->chainTable; + uint hBitsS = cParams->chainLog; + byte* @base = ms->window.@base; + byte* istart = (byte*)src; + byte* ip = istart; + byte* anchor = istart; + uint endIndex = (uint)((nuint)(istart - @base) + srcSize); + /* presumes that, if there is a dictionary, it must be using Attach mode */ + uint prefixLowestIndex = ZSTD_getLowestPrefixIndex(ms, endIndex, cParams->windowLog); + byte* prefixLowest = @base + prefixLowestIndex; + byte* iend = istart + srcSize; + byte* ilimit = iend - 8; + uint offset_1 = rep[0], + offset_2 = rep[1]; + ZSTD_MatchState_t* dms = ms->dictMatchState; + ZSTD_compressionParameters* dictCParams = &dms->cParams; + uint* dictHashLong = dms->hashTable; + uint* dictHashSmall = dms->chainTable; + uint dictStartIndex = dms->window.dictLimit; + byte* dictBase = dms->window.@base; + byte* dictStart = dictBase + dictStartIndex; + byte* dictEnd = dms->window.nextSrc; + uint dictIndexDelta = prefixLowestIndex - (uint)(dictEnd - dictBase); + uint dictHBitsL = dictCParams->hashLog + 8; + uint dictHBitsS = dictCParams->chainLog + 8; + uint dictAndPrefixLength = (uint)(ip - prefixLowest + (dictEnd - dictStart)); + assert(ms->window.dictLimit + (1U << (int)cParams->windowLog) >= endIndex); + if (ms->prefetchCDictTables != 0) + { + nuint hashTableBytes = ((nuint)1 << (int)dictCParams->hashLog) * sizeof(uint); + nuint chainTableBytes = ((nuint)1 << (int)dictCParams->chainLog) * sizeof(uint); + { + sbyte* _ptr = (sbyte*)dictHashLong; + nuint _size = hashTableBytes; + nuint _pos; + for (_pos = 0; _pos < _size; _pos += 64) + { +#if NETCOREAPP3_0_OR_GREATER + if (System.Runtime.Intrinsics.X86.Sse.IsSupported) + { + System.Runtime.Intrinsics.X86.Sse.Prefetch1(_ptr + _pos); + } +#endif + } + } + + { + sbyte* _ptr = (sbyte*)dictHashSmall; + nuint _size = chainTableBytes; + nuint _pos; + for (_pos = 0; _pos < _size; _pos += 64) + { +#if NETCOREAPP3_0_OR_GREATER + if (System.Runtime.Intrinsics.X86.Sse.IsSupported) + { + System.Runtime.Intrinsics.X86.Sse.Prefetch1(_ptr + _pos); + } +#endif + } + } + } + + ip += dictAndPrefixLength == 0 ? 1 : 0; + assert(offset_1 <= dictAndPrefixLength); + assert(offset_2 <= dictAndPrefixLength); + while (ip < ilimit) + { + nuint mLength; + uint offset; + nuint h2 = ZSTD_hashPtr(ip, hBitsL, 8); + nuint h = ZSTD_hashPtr(ip, hBitsS, mls); + nuint dictHashAndTagL = ZSTD_hashPtr(ip, dictHBitsL, 8); + nuint dictHashAndTagS = ZSTD_hashPtr(ip, dictHBitsS, mls); + uint dictMatchIndexAndTagL = dictHashLong[dictHashAndTagL >> 8]; + uint dictMatchIndexAndTagS = dictHashSmall[dictHashAndTagS >> 8]; + int dictTagsMatchL = ZSTD_comparePackedTags(dictMatchIndexAndTagL, dictHashAndTagL); + int dictTagsMatchS = ZSTD_comparePackedTags(dictMatchIndexAndTagS, dictHashAndTagS); + uint curr = (uint)(ip - @base); + uint matchIndexL = hashLong[h2]; + uint matchIndexS = hashSmall[h]; + byte* matchLong = @base + matchIndexL; + byte* match = @base + matchIndexS; + uint repIndex = curr + 1 - offset_1; + byte* repMatch = + repIndex < prefixLowestIndex + ? dictBase + (repIndex - dictIndexDelta) + : @base + repIndex; + hashLong[h2] = hashSmall[h] = curr; + if ( + ZSTD_index_overlap_check(prefixLowestIndex, repIndex) != 0 + && MEM_read32(repMatch) == MEM_read32(ip + 1) + ) + { + byte* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; + mLength = + ZSTD_count_2segments(ip + 1 + 4, repMatch + 4, iend, repMatchEnd, prefixLowest) + + 4; + ip++; + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, (nuint)(ip - anchor), anchor, iend, 1, mLength); + goto _match_stored; + } + + if (matchIndexL >= prefixLowestIndex && MEM_read64(matchLong) == MEM_read64(ip)) + { + mLength = ZSTD_count(ip + 8, matchLong + 8, iend) + 8; + offset = (uint)(ip - matchLong); + while (ip > anchor && matchLong > prefixLowest && ip[-1] == matchLong[-1]) + { + ip--; + matchLong--; + mLength++; + } + + goto _match_found; + } + else if (dictTagsMatchL != 0) + { + /* check dictMatchState long match */ + uint dictMatchIndexL = dictMatchIndexAndTagL >> 8; + byte* dictMatchL = dictBase + dictMatchIndexL; + assert(dictMatchL < dictEnd); + if (dictMatchL > dictStart && MEM_read64(dictMatchL) == MEM_read64(ip)) + { + mLength = + ZSTD_count_2segments(ip + 8, dictMatchL + 8, iend, dictEnd, prefixLowest) + + 8; + offset = curr - dictMatchIndexL - dictIndexDelta; + while (ip > anchor && dictMatchL > dictStart && ip[-1] == dictMatchL[-1]) + { + ip--; + dictMatchL--; + mLength++; + } + + goto _match_found; + } + } + + if (matchIndexS > prefixLowestIndex) + { + if (MEM_read32(match) == MEM_read32(ip)) + { + goto _search_next_long; + } + } + else if (dictTagsMatchS != 0) + { + /* check dictMatchState short match */ + uint dictMatchIndexS = dictMatchIndexAndTagS >> 8; + match = dictBase + dictMatchIndexS; + matchIndexS = dictMatchIndexS + dictIndexDelta; + if (match > dictStart && MEM_read32(match) == MEM_read32(ip)) + { + goto _search_next_long; + } + } + + ip += (ip - anchor >> 8) + 1; + continue; + _search_next_long: + { + nuint hl3 = ZSTD_hashPtr(ip + 1, hBitsL, 8); + nuint dictHashAndTagL3 = ZSTD_hashPtr(ip + 1, dictHBitsL, 8); + uint matchIndexL3 = hashLong[hl3]; + uint dictMatchIndexAndTagL3 = dictHashLong[dictHashAndTagL3 >> 8]; + int dictTagsMatchL3 = ZSTD_comparePackedTags( + dictMatchIndexAndTagL3, + dictHashAndTagL3 + ); + byte* matchL3 = @base + matchIndexL3; + hashLong[hl3] = curr + 1; + if (matchIndexL3 >= prefixLowestIndex && MEM_read64(matchL3) == MEM_read64(ip + 1)) + { + mLength = ZSTD_count(ip + 9, matchL3 + 8, iend) + 8; + ip++; + offset = (uint)(ip - matchL3); + while (ip > anchor && matchL3 > prefixLowest && ip[-1] == matchL3[-1]) + { + ip--; + matchL3--; + mLength++; + } + + goto _match_found; + } + else if (dictTagsMatchL3 != 0) + { + /* check dict long +1 match */ + uint dictMatchIndexL3 = dictMatchIndexAndTagL3 >> 8; + byte* dictMatchL3 = dictBase + dictMatchIndexL3; + assert(dictMatchL3 < dictEnd); + if (dictMatchL3 > dictStart && MEM_read64(dictMatchL3) == MEM_read64(ip + 1)) + { + mLength = + ZSTD_count_2segments( + ip + 1 + 8, + dictMatchL3 + 8, + iend, + dictEnd, + prefixLowest + ) + 8; + ip++; + offset = curr + 1 - dictMatchIndexL3 - dictIndexDelta; + while (ip > anchor && dictMatchL3 > dictStart && ip[-1] == dictMatchL3[-1]) + { + ip--; + dictMatchL3--; + mLength++; + } + + goto _match_found; + } + } + } + + if (matchIndexS < prefixLowestIndex) + { + mLength = ZSTD_count_2segments(ip + 4, match + 4, iend, dictEnd, prefixLowest) + 4; + offset = curr - matchIndexS; + while (ip > anchor && match > dictStart && ip[-1] == match[-1]) + { + ip--; + match--; + mLength++; + } + } + else + { + mLength = ZSTD_count(ip + 4, match + 4, iend) + 4; + offset = (uint)(ip - match); + while (ip > anchor && match > prefixLowest && ip[-1] == match[-1]) + { + ip--; + match--; + mLength++; + } + } + + _match_found: + offset_2 = offset_1; + offset_1 = offset; + assert(offset > 0); + ZSTD_storeSeq(seqStore, (nuint)(ip - anchor), anchor, iend, offset + 3, mLength); + _match_stored: + ip += mLength; + anchor = ip; + if (ip <= ilimit) + { + { + uint indexToInsert = curr + 2; + hashLong[ZSTD_hashPtr(@base + indexToInsert, hBitsL, 8)] = indexToInsert; + hashLong[ZSTD_hashPtr(ip - 2, hBitsL, 8)] = (uint)(ip - 2 - @base); + hashSmall[ZSTD_hashPtr(@base + indexToInsert, hBitsS, mls)] = indexToInsert; + hashSmall[ZSTD_hashPtr(ip - 1, hBitsS, mls)] = (uint)(ip - 1 - @base); + } + + while (ip <= ilimit) + { + uint current2 = (uint)(ip - @base); + uint repIndex2 = current2 - offset_2; + byte* repMatch2 = + repIndex2 < prefixLowestIndex + ? dictBase + repIndex2 - dictIndexDelta + : @base + repIndex2; + if ( + ZSTD_index_overlap_check(prefixLowestIndex, repIndex2) != 0 + && MEM_read32(repMatch2) == MEM_read32(ip) + ) + { + byte* repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend; + nuint repLength2 = + ZSTD_count_2segments(ip + 4, repMatch2 + 4, iend, repEnd2, prefixLowest) + + 4; + /* swap offset_2 <=> offset_1 */ + uint tmpOffset = offset_2; + offset_2 = offset_1; + offset_1 = tmpOffset; + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, 0, anchor, iend, 1, repLength2); + hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; + hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; + ip += repLength2; + anchor = ip; + continue; + } + + break; + } + } + } + + rep[0] = offset_1; + rep[1] = offset_2; + return (nuint)(iend - anchor); + } + + private static nuint ZSTD_compressBlock_doubleFast_noDict_4( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_noDict_generic(ms, seqStore, rep, src, srcSize, 4); + } + + private static nuint ZSTD_compressBlock_doubleFast_noDict_5( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_noDict_generic(ms, seqStore, rep, src, srcSize, 5); + } + + private static nuint ZSTD_compressBlock_doubleFast_noDict_6( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_noDict_generic(ms, seqStore, rep, src, srcSize, 6); + } + + private static nuint ZSTD_compressBlock_doubleFast_noDict_7( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_noDict_generic(ms, seqStore, rep, src, srcSize, 7); + } + + private static nuint ZSTD_compressBlock_doubleFast_dictMatchState_4( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_dictMatchState_generic( + ms, + seqStore, + rep, + src, + srcSize, + 4 + ); + } + + private static nuint ZSTD_compressBlock_doubleFast_dictMatchState_5( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_dictMatchState_generic( + ms, + seqStore, + rep, + src, + srcSize, + 5 + ); + } + + private static nuint ZSTD_compressBlock_doubleFast_dictMatchState_6( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_dictMatchState_generic( + ms, + seqStore, + rep, + src, + srcSize, + 6 + ); + } + + private static nuint ZSTD_compressBlock_doubleFast_dictMatchState_7( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_dictMatchState_generic( + ms, + seqStore, + rep, + src, + srcSize, + 7 + ); + } + + private static nuint ZSTD_compressBlock_doubleFast( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + uint mls = ms->cParams.minMatch; + switch (mls) + { + default: + case 4: + return ZSTD_compressBlock_doubleFast_noDict_4(ms, seqStore, rep, src, srcSize); + case 5: + return ZSTD_compressBlock_doubleFast_noDict_5(ms, seqStore, rep, src, srcSize); + case 6: + return ZSTD_compressBlock_doubleFast_noDict_6(ms, seqStore, rep, src, srcSize); + case 7: + return ZSTD_compressBlock_doubleFast_noDict_7(ms, seqStore, rep, src, srcSize); + } + } + + private static nuint ZSTD_compressBlock_doubleFast_dictMatchState( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + uint mls = ms->cParams.minMatch; + switch (mls) + { + default: + case 4: + return ZSTD_compressBlock_doubleFast_dictMatchState_4( + ms, + seqStore, + rep, + src, + srcSize + ); + case 5: + return ZSTD_compressBlock_doubleFast_dictMatchState_5( + ms, + seqStore, + rep, + src, + srcSize + ); + case 6: + return ZSTD_compressBlock_doubleFast_dictMatchState_6( + ms, + seqStore, + rep, + src, + srcSize + ); + case 7: + return ZSTD_compressBlock_doubleFast_dictMatchState_7( + ms, + seqStore, + rep, + src, + srcSize + ); + } + } + + private static nuint ZSTD_compressBlock_doubleFast_extDict_generic( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize, + uint mls + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashLong = ms->hashTable; + uint hBitsL = cParams->hashLog; + uint* hashSmall = ms->chainTable; + uint hBitsS = cParams->chainLog; + byte* istart = (byte*)src; + byte* ip = istart; + byte* anchor = istart; + byte* iend = istart + srcSize; + byte* ilimit = iend - 8; + byte* @base = ms->window.@base; + uint endIndex = (uint)((nuint)(istart - @base) + srcSize); + uint lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, cParams->windowLog); + uint dictStartIndex = lowLimit; + uint dictLimit = ms->window.dictLimit; + uint prefixStartIndex = dictLimit > lowLimit ? dictLimit : lowLimit; + byte* prefixStart = @base + prefixStartIndex; + byte* dictBase = ms->window.dictBase; + byte* dictStart = dictBase + dictStartIndex; + byte* dictEnd = dictBase + prefixStartIndex; + uint offset_1 = rep[0], + offset_2 = rep[1]; + if (prefixStartIndex == dictStartIndex) + return ZSTD_compressBlock_doubleFast(ms, seqStore, rep, src, srcSize); + while (ip < ilimit) + { + nuint hSmall = ZSTD_hashPtr(ip, hBitsS, mls); + uint matchIndex = hashSmall[hSmall]; + byte* matchBase = matchIndex < prefixStartIndex ? dictBase : @base; + byte* match = matchBase + matchIndex; + nuint hLong = ZSTD_hashPtr(ip, hBitsL, 8); + uint matchLongIndex = hashLong[hLong]; + byte* matchLongBase = matchLongIndex < prefixStartIndex ? dictBase : @base; + byte* matchLong = matchLongBase + matchLongIndex; + uint curr = (uint)(ip - @base); + /* offset_1 expected <= curr +1 */ + uint repIndex = curr + 1 - offset_1; + byte* repBase = repIndex < prefixStartIndex ? dictBase : @base; + byte* repMatch = repBase + repIndex; + nuint mLength; + hashSmall[hSmall] = hashLong[hLong] = curr; + if ( + ( + ZSTD_index_overlap_check(prefixStartIndex, repIndex) + & (offset_1 <= curr + 1 - dictStartIndex ? 1 : 0) + ) != 0 + && MEM_read32(repMatch) == MEM_read32(ip + 1) + ) + { + byte* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; + mLength = + ZSTD_count_2segments(ip + 1 + 4, repMatch + 4, iend, repMatchEnd, prefixStart) + + 4; + ip++; + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, (nuint)(ip - anchor), anchor, iend, 1, mLength); + } + else + { + if (matchLongIndex > dictStartIndex && MEM_read64(matchLong) == MEM_read64(ip)) + { + byte* matchEnd = matchLongIndex < prefixStartIndex ? dictEnd : iend; + byte* lowMatchPtr = matchLongIndex < prefixStartIndex ? dictStart : prefixStart; + uint offset; + mLength = + ZSTD_count_2segments(ip + 8, matchLong + 8, iend, matchEnd, prefixStart) + + 8; + offset = curr - matchLongIndex; + while (ip > anchor && matchLong > lowMatchPtr && ip[-1] == matchLong[-1]) + { + ip--; + matchLong--; + mLength++; + } + + offset_2 = offset_1; + offset_1 = offset; + assert(offset > 0); + ZSTD_storeSeq( + seqStore, + (nuint)(ip - anchor), + anchor, + iend, + offset + 3, + mLength + ); + } + else if (matchIndex > dictStartIndex && MEM_read32(match) == MEM_read32(ip)) + { + nuint h3 = ZSTD_hashPtr(ip + 1, hBitsL, 8); + uint matchIndex3 = hashLong[h3]; + byte* match3Base = matchIndex3 < prefixStartIndex ? dictBase : @base; + byte* match3 = match3Base + matchIndex3; + uint offset; + hashLong[h3] = curr + 1; + if (matchIndex3 > dictStartIndex && MEM_read64(match3) == MEM_read64(ip + 1)) + { + byte* matchEnd = matchIndex3 < prefixStartIndex ? dictEnd : iend; + byte* lowMatchPtr = + matchIndex3 < prefixStartIndex ? dictStart : prefixStart; + mLength = + ZSTD_count_2segments(ip + 9, match3 + 8, iend, matchEnd, prefixStart) + + 8; + ip++; + offset = curr + 1 - matchIndex3; + while (ip > anchor && match3 > lowMatchPtr && ip[-1] == match3[-1]) + { + ip--; + match3--; + mLength++; + } + } + else + { + byte* matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend; + byte* lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart; + mLength = + ZSTD_count_2segments(ip + 4, match + 4, iend, matchEnd, prefixStart) + + 4; + offset = curr - matchIndex; + while (ip > anchor && match > lowMatchPtr && ip[-1] == match[-1]) + { + ip--; + match--; + mLength++; + } + } + + offset_2 = offset_1; + offset_1 = offset; + assert(offset > 0); + ZSTD_storeSeq( + seqStore, + (nuint)(ip - anchor), + anchor, + iend, + offset + 3, + mLength + ); + } + else + { + ip += (ip - anchor >> 8) + 1; + continue; + } + } + + ip += mLength; + anchor = ip; + if (ip <= ilimit) + { + { + uint indexToInsert = curr + 2; + hashLong[ZSTD_hashPtr(@base + indexToInsert, hBitsL, 8)] = indexToInsert; + hashLong[ZSTD_hashPtr(ip - 2, hBitsL, 8)] = (uint)(ip - 2 - @base); + hashSmall[ZSTD_hashPtr(@base + indexToInsert, hBitsS, mls)] = indexToInsert; + hashSmall[ZSTD_hashPtr(ip - 1, hBitsS, mls)] = (uint)(ip - 1 - @base); + } + + while (ip <= ilimit) + { + uint current2 = (uint)(ip - @base); + uint repIndex2 = current2 - offset_2; + byte* repMatch2 = + repIndex2 < prefixStartIndex ? dictBase + repIndex2 : @base + repIndex2; + if ( + ( + ZSTD_index_overlap_check(prefixStartIndex, repIndex2) + & (offset_2 <= current2 - dictStartIndex ? 1 : 0) + ) != 0 + && MEM_read32(repMatch2) == MEM_read32(ip) + ) + { + byte* repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; + nuint repLength2 = + ZSTD_count_2segments(ip + 4, repMatch2 + 4, iend, repEnd2, prefixStart) + + 4; + /* swap offset_2 <=> offset_1 */ + uint tmpOffset = offset_2; + offset_2 = offset_1; + offset_1 = tmpOffset; + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, 0, anchor, iend, 1, repLength2); + hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; + hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; + ip += repLength2; + anchor = ip; + continue; + } + + break; + } + } + } + + rep[0] = offset_1; + rep[1] = offset_2; + return (nuint)(iend - anchor); + } + + private static nuint ZSTD_compressBlock_doubleFast_extDict_4( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 4); + } + + private static nuint ZSTD_compressBlock_doubleFast_extDict_5( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 5); + } + + private static nuint ZSTD_compressBlock_doubleFast_extDict_6( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 6); + } + + private static nuint ZSTD_compressBlock_doubleFast_extDict_7( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 7); + } + + private static nuint ZSTD_compressBlock_doubleFast_extDict( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + uint mls = ms->cParams.minMatch; + switch (mls) + { + default: + case 4: + return ZSTD_compressBlock_doubleFast_extDict_4(ms, seqStore, rep, src, srcSize); + case 5: + return ZSTD_compressBlock_doubleFast_extDict_5(ms, seqStore, rep, src, srcSize); + case 6: + return ZSTD_compressBlock_doubleFast_extDict_6(ms, seqStore, rep, src, srcSize); + case 7: + return ZSTD_compressBlock_doubleFast_extDict_7(ms, seqStore, rep, src, srcSize); + } + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdFast.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdFast.cs new file mode 100644 index 00000000..518954b6 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdFast.cs @@ -0,0 +1,1209 @@ +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + private static void ZSTD_fillHashTableForCDict( + ZSTD_MatchState_t* ms, + void* end, + ZSTD_dictTableLoadMethod_e dtlm + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashTable = ms->hashTable; + uint hBits = cParams->hashLog + 8; + uint mls = cParams->minMatch; + byte* @base = ms->window.@base; + byte* ip = @base + ms->nextToUpdate; + byte* iend = (byte*)end - 8; + const uint fastHashFillStep = 3; + assert(dtlm == ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_full); + for (; ip + fastHashFillStep < iend + 2; ip += fastHashFillStep) + { + uint curr = (uint)(ip - @base); + { + nuint hashAndTag = ZSTD_hashPtr(ip, hBits, mls); + ZSTD_writeTaggedIndex(hashTable, hashAndTag, curr); + } + + if (dtlm == ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast) + continue; + { + uint p; + for (p = 1; p < fastHashFillStep; ++p) + { + nuint hashAndTag = ZSTD_hashPtr(ip + p, hBits, mls); + if (hashTable[hashAndTag >> 8] == 0) + { + ZSTD_writeTaggedIndex(hashTable, hashAndTag, curr + p); + } + } + } + } + } + + private static void ZSTD_fillHashTableForCCtx( + ZSTD_MatchState_t* ms, + void* end, + ZSTD_dictTableLoadMethod_e dtlm + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashTable = ms->hashTable; + uint hBits = cParams->hashLog; + uint mls = cParams->minMatch; + byte* @base = ms->window.@base; + byte* ip = @base + ms->nextToUpdate; + byte* iend = (byte*)end - 8; + const uint fastHashFillStep = 3; + assert(dtlm == ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast); + for (; ip + fastHashFillStep < iend + 2; ip += fastHashFillStep) + { + uint curr = (uint)(ip - @base); + nuint hash0 = ZSTD_hashPtr(ip, hBits, mls); + hashTable[hash0] = curr; + if (dtlm == ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast) + continue; + { + uint p; + for (p = 1; p < fastHashFillStep; ++p) + { + nuint hash = ZSTD_hashPtr(ip + p, hBits, mls); + if (hashTable[hash] == 0) + { + hashTable[hash] = curr + p; + } + } + } + } + } + + private static void ZSTD_fillHashTable( + ZSTD_MatchState_t* ms, + void* end, + ZSTD_dictTableLoadMethod_e dtlm, + ZSTD_tableFillPurpose_e tfp + ) + { + if (tfp == ZSTD_tableFillPurpose_e.ZSTD_tfp_forCDict) + { + ZSTD_fillHashTableForCDict(ms, end, dtlm); + } + else + { + ZSTD_fillHashTableForCCtx(ms, end, dtlm); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_match4Found_cmov( + byte* currentPtr, + byte* matchAddress, + uint matchIdx, + uint idxLowLimit + ) + { + /* currentIdx >= lowLimit is a (somewhat) unpredictable branch. + * However expression below compiles into conditional move. + */ + byte* mvalAddr = ZSTD_selectAddr(matchIdx, idxLowLimit, matchAddress, dummy); + if (MEM_read32(currentPtr) != MEM_read32(mvalAddr)) + return 0; + return matchIdx >= idxLowLimit ? 1 : 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_match4Found_branch( + byte* currentPtr, + byte* matchAddress, + uint matchIdx, + uint idxLowLimit + ) + { + /* using a branch instead of a cmov, + * because it's faster in scenarios where matchIdx >= idxLowLimit is generally true, + * aka almost all candidates are within range */ + uint mval; + if (matchIdx >= idxLowLimit) + { + mval = MEM_read32(matchAddress); + } + else + { + mval = MEM_read32(currentPtr) ^ 1; + } + + return MEM_read32(currentPtr) == mval ? 1 : 0; + } + + /** + * If you squint hard enough (and ignore repcodes), the search operation at any + * given position is broken into 4 stages: + * + * 1. Hash (map position to hash value via input read) + * 2. Lookup (map hash val to index via hashtable read) + * 3. Load (map index to value at that position via input read) + * 4. Compare + * + * Each of these steps involves a memory read at an address which is computed + * from the previous step. This means these steps must be sequenced and their + * latencies are cumulative. + * + * Rather than do 1->2->3->4 sequentially for a single position before moving + * onto the next, this implementation interleaves these operations across the + * next few positions: + * + * R = Repcode Read & Compare + * H = Hash + * T = Table Lookup + * M = Match Read & Compare + * + * Pos | Time --> + * ----+------------------- + * N | ... M + * N+1 | ... TM + * N+2 | R H T M + * N+3 | H TM + * N+4 | R H T M + * N+5 | H ... + * N+6 | R ... + * + * This is very much analogous to the pipelining of execution in a CPU. And just + * like a CPU, we have to dump the pipeline when we find a match (i.e., take a + * branch). + * + * When this happens, we throw away our current state, and do the following prep + * to re-enter the loop: + * + * Pos | Time --> + * ----+------------------- + * N | H T + * N+1 | H + * + * This is also the work we do at the beginning to enter the loop initially. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_compressBlock_fast_noDict_generic( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize, + uint mls, + int useCmov + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashTable = ms->hashTable; + uint hlog = cParams->hashLog; + /* min 2 */ + nuint stepSize = cParams->targetLength + (uint)(cParams->targetLength == 0 ? 1 : 0) + 1; + byte* @base = ms->window.@base; + byte* istart = (byte*)src; + uint endIndex = (uint)((nuint)(istart - @base) + srcSize); + uint prefixStartIndex = ZSTD_getLowestPrefixIndex(ms, endIndex, cParams->windowLog); + byte* prefixStart = @base + prefixStartIndex; + byte* iend = istart + srcSize; + byte* ilimit = iend - 8; + byte* anchor = istart; + byte* ip0 = istart; + byte* ip1; + byte* ip2; + byte* ip3; + uint current0; + uint rep_offset1 = rep[0]; + uint rep_offset2 = rep[1]; + uint offsetSaved1 = 0, + offsetSaved2 = 0; + /* hash for ip0 */ + nuint hash0; + /* hash for ip1 */ + nuint hash1; + /* match idx for ip0 */ + uint matchIdx; + uint offcode; + byte* match0; + nuint mLength; + /* ip0 and ip1 are always adjacent. The targetLength skipping and + * uncompressibility acceleration is applied to every other position, + * matching the behavior of #1562. step therefore represents the gap + * between pairs of positions, from ip0 to ip2 or ip1 to ip3. */ + nuint step; + byte* nextStep; + const nuint kStepIncr = 1 << 8 - 1; + void* matchFound = + useCmov != 0 + ? (delegate* managed)(&ZSTD_match4Found_cmov) + : (delegate* managed)(&ZSTD_match4Found_branch); + ip0 += ip0 == prefixStart ? 1 : 0; + { + uint curr = (uint)(ip0 - @base); + uint windowLow = ZSTD_getLowestPrefixIndex(ms, curr, cParams->windowLog); + uint maxRep = curr - windowLow; + if (rep_offset2 > maxRep) + { + offsetSaved2 = rep_offset2; + rep_offset2 = 0; + } + + if (rep_offset1 > maxRep) + { + offsetSaved1 = rep_offset1; + rep_offset1 = 0; + } + } + + _start: + step = stepSize; + nextStep = ip0 + kStepIncr; + ip1 = ip0 + 1; + ip2 = ip0 + step; + ip3 = ip2 + 1; + if (ip3 >= ilimit) + { + goto _cleanup; + } + + hash0 = ZSTD_hashPtr(ip0, hlog, mls); + hash1 = ZSTD_hashPtr(ip1, hlog, mls); + matchIdx = hashTable[hash0]; + do + { + /* load repcode match for ip[2]*/ + uint rval = MEM_read32(ip2 - rep_offset1); + current0 = (uint)(ip0 - @base); + hashTable[hash0] = current0; + if (MEM_read32(ip2) == rval && rep_offset1 > 0) + { + ip0 = ip2; + match0 = ip0 - rep_offset1; + mLength = ip0[-1] == match0[-1] ? 1U : 0U; + ip0 -= mLength; + match0 -= mLength; + assert(1 >= 1); + assert(1 <= 3); + offcode = 1; + mLength += 4; + hashTable[hash1] = (uint)(ip1 - @base); + goto _match; + } + + if ( + ((delegate* managed)matchFound)( + ip0, + @base + matchIdx, + matchIdx, + prefixStartIndex + ) != 0 + ) + { + hashTable[hash1] = (uint)(ip1 - @base); + goto _offset; + } + + matchIdx = hashTable[hash1]; + hash0 = hash1; + hash1 = ZSTD_hashPtr(ip2, hlog, mls); + ip0 = ip1; + ip1 = ip2; + ip2 = ip3; + current0 = (uint)(ip0 - @base); + hashTable[hash0] = current0; + if ( + ((delegate* managed)matchFound)( + ip0, + @base + matchIdx, + matchIdx, + prefixStartIndex + ) != 0 + ) + { + if (step <= 4) + { + hashTable[hash1] = (uint)(ip1 - @base); + } + + goto _offset; + } + + matchIdx = hashTable[hash1]; + hash0 = hash1; + hash1 = ZSTD_hashPtr(ip2, hlog, mls); + ip0 = ip1; + ip1 = ip2; + ip2 = ip0 + step; + ip3 = ip1 + step; + if (ip2 >= nextStep) + { + step++; +#if NETCOREAPP3_0_OR_GREATER + if (System.Runtime.Intrinsics.X86.Sse.IsSupported) + { + System.Runtime.Intrinsics.X86.Sse.Prefetch0(ip1 + 64); + System.Runtime.Intrinsics.X86.Sse.Prefetch0(ip1 + 128); + } +#endif + + nextStep += kStepIncr; + } + } while (ip3 < ilimit); + _cleanup: + offsetSaved2 = offsetSaved1 != 0 && rep_offset1 != 0 ? offsetSaved1 : offsetSaved2; + rep[0] = rep_offset1 != 0 ? rep_offset1 : offsetSaved1; + rep[1] = rep_offset2 != 0 ? rep_offset2 : offsetSaved2; + return (nuint)(iend - anchor); + _offset: + match0 = @base + matchIdx; + rep_offset2 = rep_offset1; + rep_offset1 = (uint)(ip0 - match0); + assert(rep_offset1 > 0); + offcode = rep_offset1 + 3; + mLength = 4; + while (ip0 > anchor && match0 > prefixStart && ip0[-1] == match0[-1]) + { + ip0--; + match0--; + mLength++; + } + + _match: + mLength += ZSTD_count(ip0 + mLength, match0 + mLength, iend); + ZSTD_storeSeq(seqStore, (nuint)(ip0 - anchor), anchor, iend, offcode, mLength); + ip0 += mLength; + anchor = ip0; + if (ip0 <= ilimit) + { + assert(@base + current0 + 2 > istart); + hashTable[ZSTD_hashPtr(@base + current0 + 2, hlog, mls)] = current0 + 2; + hashTable[ZSTD_hashPtr(ip0 - 2, hlog, mls)] = (uint)(ip0 - 2 - @base); + if (rep_offset2 > 0) + { + while (ip0 <= ilimit && MEM_read32(ip0) == MEM_read32(ip0 - rep_offset2)) + { + /* store sequence */ + nuint rLength = ZSTD_count(ip0 + 4, ip0 + 4 - rep_offset2, iend) + 4; + { + /* swap rep_offset2 <=> rep_offset1 */ + uint tmpOff = rep_offset2; + rep_offset2 = rep_offset1; + rep_offset1 = tmpOff; + } + + hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (uint)(ip0 - @base); + ip0 += rLength; + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, 0, anchor, iend, 1, rLength); + anchor = ip0; + continue; + } + } + } + + goto _start; + } + + private static nuint ZSTD_compressBlock_fast_noDict_4_1( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_noDict_generic(ms, seqStore, rep, src, srcSize, 4, 1); + } + + private static nuint ZSTD_compressBlock_fast_noDict_5_1( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_noDict_generic(ms, seqStore, rep, src, srcSize, 5, 1); + } + + private static nuint ZSTD_compressBlock_fast_noDict_6_1( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_noDict_generic(ms, seqStore, rep, src, srcSize, 6, 1); + } + + private static nuint ZSTD_compressBlock_fast_noDict_7_1( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_noDict_generic(ms, seqStore, rep, src, srcSize, 7, 1); + } + + private static nuint ZSTD_compressBlock_fast_noDict_4_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_noDict_generic(ms, seqStore, rep, src, srcSize, 4, 0); + } + + private static nuint ZSTD_compressBlock_fast_noDict_5_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_noDict_generic(ms, seqStore, rep, src, srcSize, 5, 0); + } + + private static nuint ZSTD_compressBlock_fast_noDict_6_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_noDict_generic(ms, seqStore, rep, src, srcSize, 6, 0); + } + + private static nuint ZSTD_compressBlock_fast_noDict_7_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_noDict_generic(ms, seqStore, rep, src, srcSize, 7, 0); + } + + private static nuint ZSTD_compressBlock_fast( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + uint mml = ms->cParams.minMatch; + /* use cmov when "candidate in range" branch is likely unpredictable */ + int useCmov = ms->cParams.windowLog < 19 ? 1 : 0; + assert(ms->dictMatchState == null); + if (useCmov != 0) + { + switch (mml) + { + default: + case 4: + return ZSTD_compressBlock_fast_noDict_4_1(ms, seqStore, rep, src, srcSize); + case 5: + return ZSTD_compressBlock_fast_noDict_5_1(ms, seqStore, rep, src, srcSize); + case 6: + return ZSTD_compressBlock_fast_noDict_6_1(ms, seqStore, rep, src, srcSize); + case 7: + return ZSTD_compressBlock_fast_noDict_7_1(ms, seqStore, rep, src, srcSize); + } + } + else + { + switch (mml) + { + default: + case 4: + return ZSTD_compressBlock_fast_noDict_4_0(ms, seqStore, rep, src, srcSize); + case 5: + return ZSTD_compressBlock_fast_noDict_5_0(ms, seqStore, rep, src, srcSize); + case 6: + return ZSTD_compressBlock_fast_noDict_6_0(ms, seqStore, rep, src, srcSize); + case 7: + return ZSTD_compressBlock_fast_noDict_7_0(ms, seqStore, rep, src, srcSize); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_compressBlock_fast_dictMatchState_generic( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize, + uint mls, + uint hasStep + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashTable = ms->hashTable; + uint hlog = cParams->hashLog; + /* support stepSize of 0 */ + uint stepSize = cParams->targetLength + (uint)(cParams->targetLength == 0 ? 1 : 0); + byte* @base = ms->window.@base; + byte* istart = (byte*)src; + byte* ip0 = istart; + /* we assert below that stepSize >= 1 */ + byte* ip1 = ip0 + stepSize; + byte* anchor = istart; + uint prefixStartIndex = ms->window.dictLimit; + byte* prefixStart = @base + prefixStartIndex; + byte* iend = istart + srcSize; + byte* ilimit = iend - 8; + uint offset_1 = rep[0], + offset_2 = rep[1]; + ZSTD_MatchState_t* dms = ms->dictMatchState; + ZSTD_compressionParameters* dictCParams = &dms->cParams; + uint* dictHashTable = dms->hashTable; + uint dictStartIndex = dms->window.dictLimit; + byte* dictBase = dms->window.@base; + byte* dictStart = dictBase + dictStartIndex; + byte* dictEnd = dms->window.nextSrc; + uint dictIndexDelta = prefixStartIndex - (uint)(dictEnd - dictBase); + uint dictAndPrefixLength = (uint)(istart - prefixStart + dictEnd - dictStart); + uint dictHBits = dictCParams->hashLog + 8; + /* if a dictionary is still attached, it necessarily means that + * it is within window size. So we just check it. */ + uint maxDistance = 1U << (int)cParams->windowLog; + uint endIndex = (uint)((nuint)(istart - @base) + srcSize); + assert(endIndex - prefixStartIndex <= maxDistance); + assert(prefixStartIndex >= (uint)(dictEnd - dictBase)); + if (ms->prefetchCDictTables != 0) + { + nuint hashTableBytes = ((nuint)1 << (int)dictCParams->hashLog) * sizeof(uint); + { + sbyte* _ptr = (sbyte*)dictHashTable; + nuint _size = hashTableBytes; + nuint _pos; + for (_pos = 0; _pos < _size; _pos += 64) + { +#if NETCOREAPP3_0_OR_GREATER + if (System.Runtime.Intrinsics.X86.Sse.IsSupported) + { + System.Runtime.Intrinsics.X86.Sse.Prefetch1(_ptr + _pos); + } +#endif + } + } + } + + ip0 += dictAndPrefixLength == 0 ? 1 : 0; + assert(offset_1 <= dictAndPrefixLength); + assert(offset_2 <= dictAndPrefixLength); + assert(stepSize >= 1); + while (ip1 <= ilimit) + { + nuint mLength; + nuint hash0 = ZSTD_hashPtr(ip0, hlog, mls); + nuint dictHashAndTag0 = ZSTD_hashPtr(ip0, dictHBits, mls); + uint dictMatchIndexAndTag = dictHashTable[dictHashAndTag0 >> 8]; + int dictTagsMatch = ZSTD_comparePackedTags(dictMatchIndexAndTag, dictHashAndTag0); + uint matchIndex = hashTable[hash0]; + uint curr = (uint)(ip0 - @base); + nuint step = stepSize; + const nuint kStepIncr = 1 << 8; + byte* nextStep = ip0 + kStepIncr; + while (true) + { + byte* match = @base + matchIndex; + uint repIndex = curr + 1 - offset_1; + byte* repMatch = + repIndex < prefixStartIndex + ? dictBase + (repIndex - dictIndexDelta) + : @base + repIndex; + nuint hash1 = ZSTD_hashPtr(ip1, hlog, mls); + nuint dictHashAndTag1 = ZSTD_hashPtr(ip1, dictHBits, mls); + hashTable[hash0] = curr; + if ( + ZSTD_index_overlap_check(prefixStartIndex, repIndex) != 0 + && MEM_read32(repMatch) == MEM_read32(ip0 + 1) + ) + { + byte* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend; + mLength = + ZSTD_count_2segments( + ip0 + 1 + 4, + repMatch + 4, + iend, + repMatchEnd, + prefixStart + ) + 4; + ip0++; + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, (nuint)(ip0 - anchor), anchor, iend, 1, mLength); + break; + } + + if (dictTagsMatch != 0) + { + /* Found a possible dict match */ + uint dictMatchIndex = dictMatchIndexAndTag >> 8; + byte* dictMatch = dictBase + dictMatchIndex; + if (dictMatchIndex > dictStartIndex && MEM_read32(dictMatch) == MEM_read32(ip0)) + { + if (matchIndex <= prefixStartIndex) + { + uint offset = curr - dictMatchIndex - dictIndexDelta; + mLength = + ZSTD_count_2segments( + ip0 + 4, + dictMatch + 4, + iend, + dictEnd, + prefixStart + ) + 4; + while ( + ip0 > anchor && dictMatch > dictStart && ip0[-1] == dictMatch[-1] + ) + { + ip0--; + dictMatch--; + mLength++; + } + + offset_2 = offset_1; + offset_1 = offset; + assert(offset > 0); + ZSTD_storeSeq( + seqStore, + (nuint)(ip0 - anchor), + anchor, + iend, + offset + 3, + mLength + ); + break; + } + } + } + + if (ZSTD_match4Found_cmov(ip0, match, matchIndex, prefixStartIndex) != 0) + { + /* found a regular match of size >= 4 */ + uint offset = (uint)(ip0 - match); + mLength = ZSTD_count(ip0 + 4, match + 4, iend) + 4; + while (ip0 > anchor && match > prefixStart && ip0[-1] == match[-1]) + { + ip0--; + match--; + mLength++; + } + + offset_2 = offset_1; + offset_1 = offset; + assert(offset > 0); + ZSTD_storeSeq( + seqStore, + (nuint)(ip0 - anchor), + anchor, + iend, + offset + 3, + mLength + ); + break; + } + + dictMatchIndexAndTag = dictHashTable[dictHashAndTag1 >> 8]; + dictTagsMatch = ZSTD_comparePackedTags(dictMatchIndexAndTag, dictHashAndTag1); + matchIndex = hashTable[hash1]; + if (ip1 >= nextStep) + { + step++; + nextStep += kStepIncr; + } + + ip0 = ip1; + ip1 = ip1 + step; + if (ip1 > ilimit) + goto _cleanup; + curr = (uint)(ip0 - @base); + hash0 = hash1; + } + + assert(mLength != 0); + ip0 += mLength; + anchor = ip0; + if (ip0 <= ilimit) + { + assert(@base + curr + 2 > istart); + hashTable[ZSTD_hashPtr(@base + curr + 2, hlog, mls)] = curr + 2; + hashTable[ZSTD_hashPtr(ip0 - 2, hlog, mls)] = (uint)(ip0 - 2 - @base); + while (ip0 <= ilimit) + { + uint current2 = (uint)(ip0 - @base); + uint repIndex2 = current2 - offset_2; + byte* repMatch2 = + repIndex2 < prefixStartIndex + ? dictBase - dictIndexDelta + repIndex2 + : @base + repIndex2; + if ( + ZSTD_index_overlap_check(prefixStartIndex, repIndex2) != 0 + && MEM_read32(repMatch2) == MEM_read32(ip0) + ) + { + byte* repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; + nuint repLength2 = + ZSTD_count_2segments(ip0 + 4, repMatch2 + 4, iend, repEnd2, prefixStart) + + 4; + /* swap offset_2 <=> offset_1 */ + uint tmpOffset = offset_2; + offset_2 = offset_1; + offset_1 = tmpOffset; + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, 0, anchor, iend, 1, repLength2); + hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = current2; + ip0 += repLength2; + anchor = ip0; + continue; + } + + break; + } + } + + assert(ip0 == anchor); + ip1 = ip0 + stepSize; + } + + _cleanup: + rep[0] = offset_1; + rep[1] = offset_2; + return (nuint)(iend - anchor); + } + + private static nuint ZSTD_compressBlock_fast_dictMatchState_4_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_dictMatchState_generic( + ms, + seqStore, + rep, + src, + srcSize, + 4, + 0 + ); + } + + private static nuint ZSTD_compressBlock_fast_dictMatchState_5_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_dictMatchState_generic( + ms, + seqStore, + rep, + src, + srcSize, + 5, + 0 + ); + } + + private static nuint ZSTD_compressBlock_fast_dictMatchState_6_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_dictMatchState_generic( + ms, + seqStore, + rep, + src, + srcSize, + 6, + 0 + ); + } + + private static nuint ZSTD_compressBlock_fast_dictMatchState_7_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_dictMatchState_generic( + ms, + seqStore, + rep, + src, + srcSize, + 7, + 0 + ); + } + + private static nuint ZSTD_compressBlock_fast_dictMatchState( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + uint mls = ms->cParams.minMatch; + assert(ms->dictMatchState != null); + switch (mls) + { + default: + case 4: + return ZSTD_compressBlock_fast_dictMatchState_4_0(ms, seqStore, rep, src, srcSize); + case 5: + return ZSTD_compressBlock_fast_dictMatchState_5_0(ms, seqStore, rep, src, srcSize); + case 6: + return ZSTD_compressBlock_fast_dictMatchState_6_0(ms, seqStore, rep, src, srcSize); + case 7: + return ZSTD_compressBlock_fast_dictMatchState_7_0(ms, seqStore, rep, src, srcSize); + } + } + + private static nuint ZSTD_compressBlock_fast_extDict_generic( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize, + uint mls, + uint hasStep + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashTable = ms->hashTable; + uint hlog = cParams->hashLog; + /* support stepSize of 0 */ + nuint stepSize = cParams->targetLength + (uint)(cParams->targetLength == 0 ? 1 : 0) + 1; + byte* @base = ms->window.@base; + byte* dictBase = ms->window.dictBase; + byte* istart = (byte*)src; + byte* anchor = istart; + uint endIndex = (uint)((nuint)(istart - @base) + srcSize); + uint lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, cParams->windowLog); + uint dictStartIndex = lowLimit; + byte* dictStart = dictBase + dictStartIndex; + uint dictLimit = ms->window.dictLimit; + uint prefixStartIndex = dictLimit < lowLimit ? lowLimit : dictLimit; + byte* prefixStart = @base + prefixStartIndex; + byte* dictEnd = dictBase + prefixStartIndex; + byte* iend = istart + srcSize; + byte* ilimit = iend - 8; + uint offset_1 = rep[0], + offset_2 = rep[1]; + uint offsetSaved1 = 0, + offsetSaved2 = 0; + byte* ip0 = istart; + byte* ip1; + byte* ip2; + byte* ip3; + uint current0; + /* hash for ip0 */ + nuint hash0; + /* hash for ip1 */ + nuint hash1; + /* match idx for ip0 */ + uint idx; + /* base pointer for idx */ + byte* idxBase; + uint offcode; + byte* match0; + nuint mLength; + /* initialize to avoid warning, assert != 0 later */ + byte* matchEnd = null; + nuint step; + byte* nextStep; + const nuint kStepIncr = 1 << 8 - 1; + if (prefixStartIndex == dictStartIndex) + return ZSTD_compressBlock_fast(ms, seqStore, rep, src, srcSize); + { + uint curr = (uint)(ip0 - @base); + uint maxRep = curr - dictStartIndex; + if (offset_2 >= maxRep) + { + offsetSaved2 = offset_2; + offset_2 = 0; + } + + if (offset_1 >= maxRep) + { + offsetSaved1 = offset_1; + offset_1 = 0; + } + } + + _start: + step = stepSize; + nextStep = ip0 + kStepIncr; + ip1 = ip0 + 1; + ip2 = ip0 + step; + ip3 = ip2 + 1; + if (ip3 >= ilimit) + { + goto _cleanup; + } + + hash0 = ZSTD_hashPtr(ip0, hlog, mls); + hash1 = ZSTD_hashPtr(ip1, hlog, mls); + idx = hashTable[hash0]; + idxBase = idx < prefixStartIndex ? dictBase : @base; + do + { + { + uint current2 = (uint)(ip2 - @base); + uint repIndex = current2 - offset_1; + byte* repBase = repIndex < prefixStartIndex ? dictBase : @base; + uint rval; + if (prefixStartIndex - repIndex >= 4 && offset_1 > 0) + { + rval = MEM_read32(repBase + repIndex); + } + else + { + rval = MEM_read32(ip2) ^ 1; + } + + current0 = (uint)(ip0 - @base); + hashTable[hash0] = current0; + if (MEM_read32(ip2) == rval) + { + ip0 = ip2; + match0 = repBase + repIndex; + matchEnd = repIndex < prefixStartIndex ? dictEnd : iend; + assert(match0 != prefixStart && match0 != dictStart); + mLength = ip0[-1] == match0[-1] ? 1U : 0U; + ip0 -= mLength; + match0 -= mLength; + assert(1 >= 1); + assert(1 <= 3); + offcode = 1; + mLength += 4; + goto _match; + } + } + + { + uint mval = idx >= dictStartIndex ? MEM_read32(idxBase + idx) : MEM_read32(ip0) ^ 1; + if (MEM_read32(ip0) == mval) + { + goto _offset; + } + } + + idx = hashTable[hash1]; + idxBase = idx < prefixStartIndex ? dictBase : @base; + hash0 = hash1; + hash1 = ZSTD_hashPtr(ip2, hlog, mls); + ip0 = ip1; + ip1 = ip2; + ip2 = ip3; + current0 = (uint)(ip0 - @base); + hashTable[hash0] = current0; + { + uint mval = idx >= dictStartIndex ? MEM_read32(idxBase + idx) : MEM_read32(ip0) ^ 1; + if (MEM_read32(ip0) == mval) + { + goto _offset; + } + } + + idx = hashTable[hash1]; + idxBase = idx < prefixStartIndex ? dictBase : @base; + hash0 = hash1; + hash1 = ZSTD_hashPtr(ip2, hlog, mls); + ip0 = ip1; + ip1 = ip2; + ip2 = ip0 + step; + ip3 = ip1 + step; + if (ip2 >= nextStep) + { + step++; +#if NETCOREAPP3_0_OR_GREATER + if (System.Runtime.Intrinsics.X86.Sse.IsSupported) + { + System.Runtime.Intrinsics.X86.Sse.Prefetch0(ip1 + 64); + System.Runtime.Intrinsics.X86.Sse.Prefetch0(ip1 + 128); + } +#endif + + nextStep += kStepIncr; + } + } while (ip3 < ilimit); + _cleanup: + offsetSaved2 = offsetSaved1 != 0 && offset_1 != 0 ? offsetSaved1 : offsetSaved2; + rep[0] = offset_1 != 0 ? offset_1 : offsetSaved1; + rep[1] = offset_2 != 0 ? offset_2 : offsetSaved2; + return (nuint)(iend - anchor); + _offset: + { + uint offset = current0 - idx; + byte* lowMatchPtr = idx < prefixStartIndex ? dictStart : prefixStart; + matchEnd = idx < prefixStartIndex ? dictEnd : iend; + match0 = idxBase + idx; + offset_2 = offset_1; + offset_1 = offset; + assert(offset > 0); + offcode = offset + 3; + mLength = 4; + while (ip0 > anchor && match0 > lowMatchPtr && ip0[-1] == match0[-1]) + { + ip0--; + match0--; + mLength++; + } + } + + _match: + assert(matchEnd != null); + mLength += ZSTD_count_2segments( + ip0 + mLength, + match0 + mLength, + iend, + matchEnd, + prefixStart + ); + ZSTD_storeSeq(seqStore, (nuint)(ip0 - anchor), anchor, iend, offcode, mLength); + ip0 += mLength; + anchor = ip0; + if (ip1 < ip0) + { + hashTable[hash1] = (uint)(ip1 - @base); + } + + if (ip0 <= ilimit) + { + assert(@base + current0 + 2 > istart); + hashTable[ZSTD_hashPtr(@base + current0 + 2, hlog, mls)] = current0 + 2; + hashTable[ZSTD_hashPtr(ip0 - 2, hlog, mls)] = (uint)(ip0 - 2 - @base); + while (ip0 <= ilimit) + { + uint repIndex2 = (uint)(ip0 - @base) - offset_2; + byte* repMatch2 = + repIndex2 < prefixStartIndex ? dictBase + repIndex2 : @base + repIndex2; + if ( + (ZSTD_index_overlap_check(prefixStartIndex, repIndex2) & (offset_2 > 0 ? 1 : 0)) + != 0 + && MEM_read32(repMatch2) == MEM_read32(ip0) + ) + { + byte* repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend; + nuint repLength2 = + ZSTD_count_2segments(ip0 + 4, repMatch2 + 4, iend, repEnd2, prefixStart) + + 4; + { + /* swap offset_2 <=> offset_1 */ + uint tmpOffset = offset_2; + offset_2 = offset_1; + offset_1 = tmpOffset; + } + + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, 0, anchor, iend, 1, repLength2); + hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (uint)(ip0 - @base); + ip0 += repLength2; + anchor = ip0; + continue; + } + + break; + } + } + + goto _start; + } + + private static nuint ZSTD_compressBlock_fast_extDict_4_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 4, 0); + } + + private static nuint ZSTD_compressBlock_fast_extDict_5_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 5, 0); + } + + private static nuint ZSTD_compressBlock_fast_extDict_6_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 6, 0); + } + + private static nuint ZSTD_compressBlock_fast_extDict_7_0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 7, 0); + } + + private static nuint ZSTD_compressBlock_fast_extDict( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + uint mls = ms->cParams.minMatch; + assert(ms->dictMatchState == null); + switch (mls) + { + default: + case 4: + return ZSTD_compressBlock_fast_extDict_4_0(ms, seqStore, rep, src, srcSize); + case 5: + return ZSTD_compressBlock_fast_extDict_5_0(ms, seqStore, rep, src, srcSize); + case 6: + return ZSTD_compressBlock_fast_extDict_6_0(ms, seqStore, rep, src, srcSize); + case 7: + return ZSTD_compressBlock_fast_extDict_7_0(ms, seqStore, rep, src, srcSize); + } + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdInternal.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdInternal.cs new file mode 100644 index 00000000..bea3765f --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdInternal.cs @@ -0,0 +1,638 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; +#if NETCOREAPP3_0_OR_GREATER +using System.Runtime.Intrinsics.X86; +#endif +#if NET5_0_OR_GREATER +using System.Runtime.Intrinsics.Arm; +#endif + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_repStartValue => new uint[3] { 1, 4, 8 }; + private static uint* repStartValue => + (uint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_repStartValue) + ); +#else + + private static readonly uint* repStartValue = GetArrayPointer(new uint[3] { 1, 4, 8 }); +#endif + private static readonly nuint* ZSTD_fcs_fieldSize = GetArrayPointer( + new nuint[4] { 0, 2, 4, 8 } + ); + private static readonly nuint* ZSTD_did_fieldSize = GetArrayPointer( + new nuint[4] { 0, 1, 2, 4 } + ); + private const uint ZSTD_blockHeaderSize = 3; +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_LL_bits => + new byte[36] + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + }; + private static byte* LL_bits => + (byte*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_LL_bits) + ); +#else + + private static readonly byte* LL_bits = GetArrayPointer( + new byte[36] + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + } + ); +#endif +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_LL_defaultNorm => + new short[36] + { + 4, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 2, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + }; + private static short* LL_defaultNorm => + (short*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_LL_defaultNorm) + ); +#else + + private static readonly short* LL_defaultNorm = GetArrayPointer( + new short[36] + { + 4, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 2, + 1, + 1, + 1, + 1, + 1, + (short)(-1), + (short)(-1), + (short)(-1), + (short)(-1), + } + ); +#endif + private const uint LL_defaultNormLog = 6; +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_ML_bits => + new byte[53] + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + }; + private static byte* ML_bits => + (byte*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_ML_bits) + ); +#else + + private static readonly byte* ML_bits = GetArrayPointer( + new byte[53] + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + } + ); +#endif +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_ML_defaultNorm => + new short[53] + { + 1, + 4, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + }; + private static short* ML_defaultNorm => + (short*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_ML_defaultNorm) + ); +#else + + private static readonly short* ML_defaultNorm = GetArrayPointer( + new short[53] + { + 1, + 4, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + (short)(-1), + (short)(-1), + (short)(-1), + (short)(-1), + (short)(-1), + (short)(-1), + (short)(-1), + } + ); +#endif + private const uint ML_defaultNormLog = 6; +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_OF_defaultNorm => + new short[29] + { + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + }; + private static short* OF_defaultNorm => + (short*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_OF_defaultNorm) + ); +#else + + private static readonly short* OF_defaultNorm = GetArrayPointer( + new short[29] + { + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + (short)(-1), + (short)(-1), + (short)(-1), + (short)(-1), + (short)(-1), + } + ); +#endif + private const uint OF_defaultNormLog = 5; + + /*-******************************************* + * Shared functions to include for inlining + *********************************************/ + private static void ZSTD_copy8(void* dst, void* src) + { + memcpy(dst, src, 8); + } + + /* Need to use memmove here since the literal buffer can now be located within + the dst buffer. In circumstances where the op "catches up" to where the + literal buffer is, there can be partial overlaps in this call on the final + copy if the literal is being shifted by less than 16 bytes. */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_copy16(void* dst, void* src) + { +#if NET5_0_OR_GREATER + if (AdvSimd.IsSupported) + { + AdvSimd.Store((byte*)dst, AdvSimd.LoadVector128((byte*)src)); + } + else +#endif +#if NETCOREAPP3_0_OR_GREATER + if (Sse2.IsSupported) + { + Sse2.Store((byte*)dst, Sse2.LoadVector128((byte*)src)); + } + else +#endif + { + var v1 = System.Runtime.CompilerServices.Unsafe.ReadUnaligned((ulong*)src); + var v2 = System.Runtime.CompilerServices.Unsafe.ReadUnaligned((ulong*)src + 1); + System.Runtime.CompilerServices.Unsafe.WriteUnaligned((ulong*)dst, v1); + System.Runtime.CompilerServices.Unsafe.WriteUnaligned((ulong*)dst + 1, v2); + } + } + + /*! ZSTD_wildcopy() : + * Custom version of ZSTD_memcpy(), can over read/write up to WILDCOPY_OVERLENGTH bytes (if length==0) + * @param ovtype controls the overlap detection + * - ZSTD_no_overlap: The source and destination are guaranteed to be at least WILDCOPY_VECLEN bytes apart. + * - ZSTD_overlap_src_before_dst: The src and dst may overlap, but they MUST be at least 8 bytes apart. + * The src buffer must be before the dst buffer. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_wildcopy(void* dst, void* src, nint length, ZSTD_overlap_e ovtype) + { + nint diff = (nint)((byte*)dst - (byte*)src); + byte* ip = (byte*)src; + byte* op = (byte*)dst; + byte* oend = op + length; + if (ovtype == ZSTD_overlap_e.ZSTD_overlap_src_before_dst && diff < 16) + { + do + { + ZSTD_copy8(op, ip); + op += 8; + ip += 8; + } while (op < oend); + } + else + { + assert(diff >= 16 || diff <= -16); + ZSTD_copy16(op, ip); + if (16 >= length) + return; + op += 16; + ip += 16; + do + { + { + ZSTD_copy16(op, ip); + op += 16; + ip += 16; + } + + { + ZSTD_copy16(op, ip); + op += 16; + ip += 16; + } + } while (op < oend); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_limitCopy(void* dst, nuint dstCapacity, void* src, nuint srcSize) + { + nuint length = dstCapacity < srcSize ? dstCapacity : srcSize; + if (length > 0) + { + memcpy(dst, src, (uint)length); + } + + return length; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLazy.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLazy.cs new file mode 100644 index 00000000..2256ffbf --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLazy.cs @@ -0,0 +1,4661 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; +#if NETCOREAPP3_0_OR_GREATER +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +#endif +#if NET5_0_OR_GREATER +using System.Runtime.Intrinsics.Arm; +#endif + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /*-************************************* + * Binary Tree search + ***************************************/ + private static void ZSTD_updateDUBT(ZSTD_MatchState_t* ms, byte* ip, byte* iend, uint mls) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashTable = ms->hashTable; + uint hashLog = cParams->hashLog; + uint* bt = ms->chainTable; + uint btLog = cParams->chainLog - 1; + uint btMask = (uint)((1 << (int)btLog) - 1); + byte* @base = ms->window.@base; + uint target = (uint)(ip - @base); + uint idx = ms->nextToUpdate; + assert(ip + 8 <= iend); + assert(idx >= ms->window.dictLimit); + for (; idx < target; idx++) + { + /* assumption : ip + 8 <= iend */ + nuint h = ZSTD_hashPtr(@base + idx, hashLog, mls); + uint matchIndex = hashTable[h]; + uint* nextCandidatePtr = bt + 2 * (idx & btMask); + uint* sortMarkPtr = nextCandidatePtr + 1; + hashTable[h] = idx; + *nextCandidatePtr = matchIndex; + *sortMarkPtr = 1; + } + + ms->nextToUpdate = target; + } + + /** ZSTD_insertDUBT1() : + * sort one already inserted but unsorted position + * assumption : curr >= btlow == (curr - btmask) + * doesn't fail */ + private static void ZSTD_insertDUBT1( + ZSTD_MatchState_t* ms, + uint curr, + byte* inputEnd, + uint nbCompares, + uint btLow, + ZSTD_dictMode_e dictMode + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* bt = ms->chainTable; + uint btLog = cParams->chainLog - 1; + uint btMask = (uint)((1 << (int)btLog) - 1); + nuint commonLengthSmaller = 0, + commonLengthLarger = 0; + byte* @base = ms->window.@base; + byte* dictBase = ms->window.dictBase; + uint dictLimit = ms->window.dictLimit; + byte* ip = curr >= dictLimit ? @base + curr : dictBase + curr; + byte* iend = curr >= dictLimit ? inputEnd : dictBase + dictLimit; + byte* dictEnd = dictBase + dictLimit; + byte* prefixStart = @base + dictLimit; + byte* match; + uint* smallerPtr = bt + 2 * (curr & btMask); + uint* largerPtr = smallerPtr + 1; + /* this candidate is unsorted : next sorted candidate is reached through *smallerPtr, while *largerPtr contains previous unsorted candidate (which is already saved and can be overwritten) */ + uint matchIndex = *smallerPtr; + /* to be nullified at the end */ + uint dummy32; + uint windowValid = ms->window.lowLimit; + uint maxDistance = 1U << (int)cParams->windowLog; + uint windowLow = curr - windowValid > maxDistance ? curr - maxDistance : windowValid; + assert(curr >= btLow); + assert(ip < iend); + for (; nbCompares != 0 && matchIndex > windowLow; --nbCompares) + { + uint* nextPtr = bt + 2 * (matchIndex & btMask); + /* guaranteed minimum nb of common bytes */ + nuint matchLength = + commonLengthSmaller < commonLengthLarger ? commonLengthSmaller : commonLengthLarger; + assert(matchIndex < curr); + if ( + dictMode != ZSTD_dictMode_e.ZSTD_extDict + || matchIndex + matchLength >= dictLimit + || curr < dictLimit + ) + { + byte* mBase = + dictMode != ZSTD_dictMode_e.ZSTD_extDict + || matchIndex + matchLength >= dictLimit + ? @base + : dictBase; + assert(matchIndex + matchLength >= dictLimit || curr < dictLimit); + match = mBase + matchIndex; + matchLength += ZSTD_count(ip + matchLength, match + matchLength, iend); + } + else + { + match = dictBase + matchIndex; + matchLength += ZSTD_count_2segments( + ip + matchLength, + match + matchLength, + iend, + dictEnd, + prefixStart + ); + if (matchIndex + matchLength >= dictLimit) + match = @base + matchIndex; + } + + if (ip + matchLength == iend) + { + break; + } + + if (match[matchLength] < ip[matchLength]) + { + *smallerPtr = matchIndex; + commonLengthSmaller = matchLength; + if (matchIndex <= btLow) + { + smallerPtr = &dummy32; + break; + } + + smallerPtr = nextPtr + 1; + matchIndex = nextPtr[1]; + } + else + { + *largerPtr = matchIndex; + commonLengthLarger = matchLength; + if (matchIndex <= btLow) + { + largerPtr = &dummy32; + break; + } + + largerPtr = nextPtr; + matchIndex = nextPtr[0]; + } + } + + *smallerPtr = *largerPtr = 0; + } + + private static nuint ZSTD_DUBT_findBetterDictMatch( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iend, + nuint* offsetPtr, + nuint bestLength, + uint nbCompares, + uint mls, + ZSTD_dictMode_e dictMode + ) + { + ZSTD_MatchState_t* dms = ms->dictMatchState; + ZSTD_compressionParameters* dmsCParams = &dms->cParams; + uint* dictHashTable = dms->hashTable; + uint hashLog = dmsCParams->hashLog; + nuint h = ZSTD_hashPtr(ip, hashLog, mls); + uint dictMatchIndex = dictHashTable[h]; + byte* @base = ms->window.@base; + byte* prefixStart = @base + ms->window.dictLimit; + uint curr = (uint)(ip - @base); + byte* dictBase = dms->window.@base; + byte* dictEnd = dms->window.nextSrc; + uint dictHighLimit = (uint)(dms->window.nextSrc - dms->window.@base); + uint dictLowLimit = dms->window.lowLimit; + uint dictIndexDelta = ms->window.lowLimit - dictHighLimit; + uint* dictBt = dms->chainTable; + uint btLog = dmsCParams->chainLog - 1; + uint btMask = (uint)((1 << (int)btLog) - 1); + uint btLow = btMask >= dictHighLimit - dictLowLimit ? dictLowLimit : dictHighLimit - btMask; + nuint commonLengthSmaller = 0, + commonLengthLarger = 0; + assert(dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState); + for (; nbCompares != 0 && dictMatchIndex > dictLowLimit; --nbCompares) + { + uint* nextPtr = dictBt + 2 * (dictMatchIndex & btMask); + /* guaranteed minimum nb of common bytes */ + nuint matchLength = + commonLengthSmaller < commonLengthLarger ? commonLengthSmaller : commonLengthLarger; + byte* match = dictBase + dictMatchIndex; + matchLength += ZSTD_count_2segments( + ip + matchLength, + match + matchLength, + iend, + dictEnd, + prefixStart + ); + if (dictMatchIndex + matchLength >= dictHighLimit) + match = @base + dictMatchIndex + dictIndexDelta; + if (matchLength > bestLength) + { + uint matchIndex = dictMatchIndex + dictIndexDelta; + if ( + 4 * (int)(matchLength - bestLength) + > (int)( + ZSTD_highbit32(curr - matchIndex + 1) + - ZSTD_highbit32((uint)offsetPtr[0] + 1) + ) + ) + { + bestLength = matchLength; + assert(curr - matchIndex > 0); + *offsetPtr = curr - matchIndex + 3; + } + + if (ip + matchLength == iend) + { + break; + } + } + + if (match[matchLength] < ip[matchLength]) + { + if (dictMatchIndex <= btLow) + { + break; + } + + commonLengthSmaller = matchLength; + dictMatchIndex = nextPtr[1]; + } + else + { + if (dictMatchIndex <= btLow) + { + break; + } + + commonLengthLarger = matchLength; + dictMatchIndex = nextPtr[0]; + } + } + + if (bestLength >= 3) + { + assert(*offsetPtr > 3); + uint mIndex = curr - (uint)(*offsetPtr - 3); + } + + return bestLength; + } + + private static nuint ZSTD_DUBT_findBestMatch( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iend, + nuint* offBasePtr, + uint mls, + ZSTD_dictMode_e dictMode + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashTable = ms->hashTable; + uint hashLog = cParams->hashLog; + nuint h = ZSTD_hashPtr(ip, hashLog, mls); + uint matchIndex = hashTable[h]; + byte* @base = ms->window.@base; + uint curr = (uint)(ip - @base); + uint windowLow = ZSTD_getLowestMatchIndex(ms, curr, cParams->windowLog); + uint* bt = ms->chainTable; + uint btLog = cParams->chainLog - 1; + uint btMask = (uint)((1 << (int)btLog) - 1); + uint btLow = btMask >= curr ? 0 : curr - btMask; + uint unsortLimit = btLow > windowLow ? btLow : windowLow; + uint* nextCandidate = bt + 2 * (matchIndex & btMask); + uint* unsortedMark = bt + 2 * (matchIndex & btMask) + 1; + uint nbCompares = 1U << (int)cParams->searchLog; + uint nbCandidates = nbCompares; + uint previousCandidate = 0; + assert(ip <= iend - 8); + assert(dictMode != ZSTD_dictMode_e.ZSTD_dedicatedDictSearch); + while (matchIndex > unsortLimit && *unsortedMark == 1 && nbCandidates > 1) + { + *unsortedMark = previousCandidate; + previousCandidate = matchIndex; + matchIndex = *nextCandidate; + nextCandidate = bt + 2 * (matchIndex & btMask); + unsortedMark = bt + 2 * (matchIndex & btMask) + 1; + nbCandidates--; + } + + if (matchIndex > unsortLimit && *unsortedMark == 1) + { + *nextCandidate = *unsortedMark = 0; + } + + matchIndex = previousCandidate; + while (matchIndex != 0) + { + uint* nextCandidateIdxPtr = bt + 2 * (matchIndex & btMask) + 1; + uint nextCandidateIdx = *nextCandidateIdxPtr; + ZSTD_insertDUBT1(ms, matchIndex, iend, nbCandidates, unsortLimit, dictMode); + matchIndex = nextCandidateIdx; + nbCandidates++; + } + + { + nuint commonLengthSmaller = 0, + commonLengthLarger = 0; + byte* dictBase = ms->window.dictBase; + uint dictLimit = ms->window.dictLimit; + byte* dictEnd = dictBase + dictLimit; + byte* prefixStart = @base + dictLimit; + uint* smallerPtr = bt + 2 * (curr & btMask); + uint* largerPtr = bt + 2 * (curr & btMask) + 1; + uint matchEndIdx = curr + 8 + 1; + /* to be nullified at the end */ + uint dummy32; + nuint bestLength = 0; + matchIndex = hashTable[h]; + hashTable[h] = curr; + for (; nbCompares != 0 && matchIndex > windowLow; --nbCompares) + { + uint* nextPtr = bt + 2 * (matchIndex & btMask); + /* guaranteed minimum nb of common bytes */ + nuint matchLength = + commonLengthSmaller < commonLengthLarger + ? commonLengthSmaller + : commonLengthLarger; + byte* match; + if ( + dictMode != ZSTD_dictMode_e.ZSTD_extDict + || matchIndex + matchLength >= dictLimit + ) + { + match = @base + matchIndex; + matchLength += ZSTD_count(ip + matchLength, match + matchLength, iend); + } + else + { + match = dictBase + matchIndex; + matchLength += ZSTD_count_2segments( + ip + matchLength, + match + matchLength, + iend, + dictEnd, + prefixStart + ); + if (matchIndex + matchLength >= dictLimit) + match = @base + matchIndex; + } + + if (matchLength > bestLength) + { + if (matchLength > matchEndIdx - matchIndex) + matchEndIdx = matchIndex + (uint)matchLength; + if ( + 4 * (int)(matchLength - bestLength) + > (int)( + ZSTD_highbit32(curr - matchIndex + 1) + - ZSTD_highbit32((uint)*offBasePtr) + ) + ) + { + bestLength = matchLength; + assert(curr - matchIndex > 0); + *offBasePtr = curr - matchIndex + 3; + } + + if (ip + matchLength == iend) + { + if (dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState) + { + nbCompares = 0; + } + + break; + } + } + + if (match[matchLength] < ip[matchLength]) + { + *smallerPtr = matchIndex; + commonLengthSmaller = matchLength; + if (matchIndex <= btLow) + { + smallerPtr = &dummy32; + break; + } + + smallerPtr = nextPtr + 1; + matchIndex = nextPtr[1]; + } + else + { + *largerPtr = matchIndex; + commonLengthLarger = matchLength; + if (matchIndex <= btLow) + { + largerPtr = &dummy32; + break; + } + + largerPtr = nextPtr; + matchIndex = nextPtr[0]; + } + } + + *smallerPtr = *largerPtr = 0; + assert(nbCompares <= 1U << (sizeof(nuint) == 4 ? 30 : 31) - 1); + if (dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState && nbCompares != 0) + { + bestLength = ZSTD_DUBT_findBetterDictMatch( + ms, + ip, + iend, + offBasePtr, + bestLength, + nbCompares, + mls, + dictMode + ); + } + + assert(matchEndIdx > curr + 8); + ms->nextToUpdate = matchEndIdx - 8; + if (bestLength >= 3) + { + assert(*offBasePtr > 3); + uint mIndex = curr - (uint)(*offBasePtr - 3); + } + + return bestLength; + } + } + + /** ZSTD_BtFindBestMatch() : Tree updater, providing best match */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_BtFindBestMatch( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr, + uint mls, + ZSTD_dictMode_e dictMode + ) + { + if (ip < ms->window.@base + ms->nextToUpdate) + return 0; + ZSTD_updateDUBT(ms, ip, iLimit, mls); + return ZSTD_DUBT_findBestMatch(ms, ip, iLimit, offBasePtr, mls, dictMode); + } + + /*********************************** + * Dedicated dict search + ***********************************/ + private static void ZSTD_dedicatedDictSearch_lazy_loadDictionary( + ZSTD_MatchState_t* ms, + byte* ip + ) + { + byte* @base = ms->window.@base; + uint target = (uint)(ip - @base); + uint* hashTable = ms->hashTable; + uint* chainTable = ms->chainTable; + uint chainSize = (uint)(1 << (int)ms->cParams.chainLog); + uint idx = ms->nextToUpdate; + uint minChain = chainSize < target - idx ? target - chainSize : idx; + const uint bucketSize = 1 << 2; + uint cacheSize = bucketSize - 1; + uint chainAttempts = (uint)(1 << (int)ms->cParams.searchLog) - cacheSize; + uint chainLimit = chainAttempts > 255 ? 255 : chainAttempts; + /* We know the hashtable is oversized by a factor of `bucketSize`. + * We are going to temporarily pretend `bucketSize == 1`, keeping only a + * single entry. We will use the rest of the space to construct a temporary + * chaintable. + */ + uint hashLog = ms->cParams.hashLog - 2; + uint* tmpHashTable = hashTable; + uint* tmpChainTable = hashTable + ((nuint)1 << (int)hashLog); + uint tmpChainSize = (uint)((1 << 2) - 1) << (int)hashLog; + uint tmpMinChain = tmpChainSize < target ? target - tmpChainSize : idx; + uint hashIdx; + assert(ms->cParams.chainLog <= 24); + assert(ms->cParams.hashLog > ms->cParams.chainLog); + assert(idx != 0); + assert(tmpMinChain <= minChain); + for (; idx < target; idx++) + { + uint h = (uint)ZSTD_hashPtr(@base + idx, hashLog, ms->cParams.minMatch); + if (idx >= tmpMinChain) + { + tmpChainTable[idx - tmpMinChain] = hashTable[h]; + } + + tmpHashTable[h] = idx; + } + + { + uint chainPos = 0; + for (hashIdx = 0; hashIdx < 1U << (int)hashLog; hashIdx++) + { + uint count; + uint countBeyondMinChain = 0; + uint i = tmpHashTable[hashIdx]; + for (count = 0; i >= tmpMinChain && count < cacheSize; count++) + { + if (i < minChain) + { + countBeyondMinChain++; + } + + i = tmpChainTable[i - tmpMinChain]; + } + + if (count == cacheSize) + { + for (count = 0; count < chainLimit; ) + { + if (i < minChain) + { + if (i == 0 || ++countBeyondMinChain > cacheSize) + { + break; + } + } + + chainTable[chainPos++] = i; + count++; + if (i < tmpMinChain) + { + break; + } + + i = tmpChainTable[i - tmpMinChain]; + } + } + else + { + count = 0; + } + + if (count != 0) + { + tmpHashTable[hashIdx] = (chainPos - count << 8) + count; + } + else + { + tmpHashTable[hashIdx] = 0; + } + } + + assert(chainPos <= chainSize); + } + + for (hashIdx = (uint)(1 << (int)hashLog); hashIdx != 0; ) + { + uint bucketIdx = --hashIdx << 2; + uint chainPackedPointer = tmpHashTable[hashIdx]; + uint i; + for (i = 0; i < cacheSize; i++) + { + hashTable[bucketIdx + i] = 0; + } + + hashTable[bucketIdx + bucketSize - 1] = chainPackedPointer; + } + + for (idx = ms->nextToUpdate; idx < target; idx++) + { + uint h = (uint)ZSTD_hashPtr(@base + idx, hashLog, ms->cParams.minMatch) << 2; + uint i; + for (i = cacheSize - 1; i != 0; i--) + hashTable[h + i] = hashTable[h + i - 1]; + hashTable[h] = idx; + } + + ms->nextToUpdate = target; + } + + /* Returns the longest match length found in the dedicated dict search structure. + * If none are longer than the argument ml, then ml will be returned. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_dedicatedDictSearch_lazy_search( + nuint* offsetPtr, + nuint ml, + uint nbAttempts, + ZSTD_MatchState_t* dms, + byte* ip, + byte* iLimit, + byte* prefixStart, + uint curr, + uint dictLimit, + nuint ddsIdx + ) + { + uint ddsLowestIndex = dms->window.dictLimit; + byte* ddsBase = dms->window.@base; + byte* ddsEnd = dms->window.nextSrc; + uint ddsSize = (uint)(ddsEnd - ddsBase); + uint ddsIndexDelta = dictLimit - ddsSize; + const uint bucketSize = 1 << 2; + uint bucketLimit = nbAttempts < bucketSize - 1 ? nbAttempts : bucketSize - 1; + uint ddsAttempt; + uint matchIndex; + for (ddsAttempt = 0; ddsAttempt < bucketSize - 1; ddsAttempt++) + { +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(ddsBase + dms->hashTable[ddsIdx + ddsAttempt]); + } +#endif + } + + { + uint chainPackedPointer = dms->hashTable[ddsIdx + bucketSize - 1]; + uint chainIndex = chainPackedPointer >> 8; +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(&dms->chainTable[chainIndex]); + } +#endif + } + + for (ddsAttempt = 0; ddsAttempt < bucketLimit; ddsAttempt++) + { + nuint currentMl = 0; + byte* match; + matchIndex = dms->hashTable[ddsIdx + ddsAttempt]; + match = ddsBase + matchIndex; + if (matchIndex == 0) + { + return ml; + } + + assert(matchIndex >= ddsLowestIndex); + assert(match + 4 <= ddsEnd); + if (MEM_read32(match) == MEM_read32(ip)) + { + currentMl = + ZSTD_count_2segments(ip + 4, match + 4, iLimit, ddsEnd, prefixStart) + 4; + } + + if (currentMl > ml) + { + ml = currentMl; + assert(curr - (matchIndex + ddsIndexDelta) > 0); + *offsetPtr = curr - (matchIndex + ddsIndexDelta) + 3; + if (ip + currentMl == iLimit) + { + return ml; + } + } + } + + { + uint chainPackedPointer = dms->hashTable[ddsIdx + bucketSize - 1]; + uint chainIndex = chainPackedPointer >> 8; + uint chainLength = chainPackedPointer & 0xFF; + uint chainAttempts = nbAttempts - ddsAttempt; + uint chainLimit = chainAttempts > chainLength ? chainLength : chainAttempts; + uint chainAttempt; + for (chainAttempt = 0; chainAttempt < chainLimit; chainAttempt++) + { +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(ddsBase + dms->chainTable[chainIndex + chainAttempt]); + } +#endif + } + + for (chainAttempt = 0; chainAttempt < chainLimit; chainAttempt++, chainIndex++) + { + nuint currentMl = 0; + byte* match; + matchIndex = dms->chainTable[chainIndex]; + match = ddsBase + matchIndex; + assert(matchIndex >= ddsLowestIndex); + assert(match + 4 <= ddsEnd); + if (MEM_read32(match) == MEM_read32(ip)) + { + currentMl = + ZSTD_count_2segments(ip + 4, match + 4, iLimit, ddsEnd, prefixStart) + 4; + } + + if (currentMl > ml) + { + ml = currentMl; + assert(curr - (matchIndex + ddsIndexDelta) > 0); + *offsetPtr = curr - (matchIndex + ddsIndexDelta) + 3; + if (ip + currentMl == iLimit) + break; + } + } + } + + return ml; + } + + /* Update chains up to ip (excluded) + Assumption : always within prefix (i.e. not within extDict) */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_insertAndFindFirstIndex_internal( + ZSTD_MatchState_t* ms, + ZSTD_compressionParameters* cParams, + byte* ip, + uint mls, + uint lazySkipping + ) + { + uint* hashTable = ms->hashTable; + uint hashLog = cParams->hashLog; + uint* chainTable = ms->chainTable; + uint chainMask = (uint)((1 << (int)cParams->chainLog) - 1); + byte* @base = ms->window.@base; + uint target = (uint)(ip - @base); + uint idx = ms->nextToUpdate; + while (idx < target) + { + nuint h = ZSTD_hashPtr(@base + idx, hashLog, mls); + chainTable[idx & chainMask] = hashTable[h]; + hashTable[h] = idx; + idx++; + if (lazySkipping != 0) + break; + } + + ms->nextToUpdate = target; + return hashTable[ZSTD_hashPtr(ip, hashLog, mls)]; + } + + private static uint ZSTD_insertAndFindFirstIndex(ZSTD_MatchState_t* ms, byte* ip) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch, 0); + } + + /* inlining is important to hardwire a hot branch (template emulation) */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_HcFindBestMatch( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr, + uint mls, + ZSTD_dictMode_e dictMode + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* chainTable = ms->chainTable; + uint chainSize = (uint)(1 << (int)cParams->chainLog); + uint chainMask = chainSize - 1; + byte* @base = ms->window.@base; + byte* dictBase = ms->window.dictBase; + uint dictLimit = ms->window.dictLimit; + byte* prefixStart = @base + dictLimit; + byte* dictEnd = dictBase + dictLimit; + uint curr = (uint)(ip - @base); + uint maxDistance = 1U << (int)cParams->windowLog; + uint lowestValid = ms->window.lowLimit; + uint withinMaxDistance = + curr - lowestValid > maxDistance ? curr - maxDistance : lowestValid; + uint isDictionary = ms->loadedDictEnd != 0 ? 1U : 0U; + uint lowLimit = isDictionary != 0 ? lowestValid : withinMaxDistance; + uint minChain = curr > chainSize ? curr - chainSize : 0; + uint nbAttempts = 1U << (int)cParams->searchLog; + nuint ml = 4 - 1; + ZSTD_MatchState_t* dms = ms->dictMatchState; + uint ddsHashLog = + dictMode == ZSTD_dictMode_e.ZSTD_dedicatedDictSearch ? dms->cParams.hashLog - 2 : 0; + nuint ddsIdx = + dictMode == ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ? ZSTD_hashPtr(ip, ddsHashLog, mls) << 2 + : 0; + uint matchIndex; + if (dictMode == ZSTD_dictMode_e.ZSTD_dedicatedDictSearch) + { + uint* entry = &dms->hashTable[ddsIdx]; +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(entry); + } +#endif + } + + matchIndex = ZSTD_insertAndFindFirstIndex_internal( + ms, + cParams, + ip, + mls, + (uint)ms->lazySkipping + ); + for (; matchIndex >= lowLimit && nbAttempts > 0; nbAttempts--) + { + nuint currentMl = 0; + if (dictMode != ZSTD_dictMode_e.ZSTD_extDict || matchIndex >= dictLimit) + { + byte* match = @base + matchIndex; + assert(matchIndex >= dictLimit); + if (MEM_read32(match + ml - 3) == MEM_read32(ip + ml - 3)) + currentMl = ZSTD_count(ip, match, iLimit); + } + else + { + byte* match = dictBase + matchIndex; + assert(match + 4 <= dictEnd); + if (MEM_read32(match) == MEM_read32(ip)) + currentMl = + ZSTD_count_2segments(ip + 4, match + 4, iLimit, dictEnd, prefixStart) + 4; + } + + if (currentMl > ml) + { + ml = currentMl; + assert(curr - matchIndex > 0); + *offsetPtr = curr - matchIndex + 3; + if (ip + currentMl == iLimit) + break; + } + + if (matchIndex <= minChain) + break; + matchIndex = chainTable[matchIndex & chainMask]; + } + + assert(nbAttempts <= 1U << (sizeof(nuint) == 4 ? 30 : 31) - 1); + if (dictMode == ZSTD_dictMode_e.ZSTD_dedicatedDictSearch) + { + ml = ZSTD_dedicatedDictSearch_lazy_search( + offsetPtr, + ml, + nbAttempts, + dms, + ip, + iLimit, + prefixStart, + curr, + dictLimit, + ddsIdx + ); + } + else if (dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState) + { + uint* dmsChainTable = dms->chainTable; + uint dmsChainSize = (uint)(1 << (int)dms->cParams.chainLog); + uint dmsChainMask = dmsChainSize - 1; + uint dmsLowestIndex = dms->window.dictLimit; + byte* dmsBase = dms->window.@base; + byte* dmsEnd = dms->window.nextSrc; + uint dmsSize = (uint)(dmsEnd - dmsBase); + uint dmsIndexDelta = dictLimit - dmsSize; + uint dmsMinChain = dmsSize > dmsChainSize ? dmsSize - dmsChainSize : 0; + matchIndex = dms->hashTable[ZSTD_hashPtr(ip, dms->cParams.hashLog, mls)]; + for (; matchIndex >= dmsLowestIndex && nbAttempts > 0; nbAttempts--) + { + nuint currentMl = 0; + byte* match = dmsBase + matchIndex; + assert(match + 4 <= dmsEnd); + if (MEM_read32(match) == MEM_read32(ip)) + currentMl = + ZSTD_count_2segments(ip + 4, match + 4, iLimit, dmsEnd, prefixStart) + 4; + if (currentMl > ml) + { + ml = currentMl; + assert(curr > matchIndex + dmsIndexDelta); + assert(curr - (matchIndex + dmsIndexDelta) > 0); + *offsetPtr = curr - (matchIndex + dmsIndexDelta) + 3; + if (ip + currentMl == iLimit) + break; + } + + if (matchIndex <= dmsMinChain) + break; + matchIndex = dmsChainTable[matchIndex & dmsChainMask]; + } + } + + return ml; + } + + /* ZSTD_VecMask_next(): + * Starting from the LSB, returns the idx of the next non-zero bit. + * Basically counting the nb of trailing zeroes. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_VecMask_next(ulong val) + { + assert(val != 0); + return (uint)BitOperations.TrailingZeroCount(val); + } + + /* ZSTD_row_nextIndex(): + * Returns the next index to insert at within a tagTable row, and updates the "head" + * value to reflect the update. Essentially cycles backwards from [1, {entries per row}) + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_row_nextIndex(byte* tagRow, uint rowMask) + { + uint next = (uint)(*tagRow - 1) & rowMask; + next += next == 0 ? rowMask : 0; + *tagRow = (byte)next; + return next; + } + + /* ZSTD_isAligned(): + * Checks that a pointer is aligned to "align" bytes which must be a power of 2. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ZSTD_isAligned(void* ptr, nuint align) + { + assert((align & align - 1) == 0); + return ((nuint)ptr & align - 1) == 0 ? 1 : 0; + } + + /* ZSTD_row_prefetch(): + * Performs prefetching for the hashTable and tagTable at a given row. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_row_prefetch(uint* hashTable, byte* tagTable, uint relRow, uint rowLog) + { +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(hashTable + relRow); + } +#endif + + if (rowLog >= 5) + { +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(hashTable + relRow + 16); + } +#endif + } + +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(tagTable + relRow); + } +#endif + + if (rowLog == 6) + { +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(tagTable + relRow + 32); + } +#endif + } + + assert(rowLog == 4 || rowLog == 5 || rowLog == 6); + assert(ZSTD_isAligned(hashTable + relRow, 64) != 0); + assert(ZSTD_isAligned(tagTable + relRow, (nuint)1 << (int)rowLog) != 0); + } + + /* ZSTD_row_fillHashCache(): + * Fill up the hash cache starting at idx, prefetching up to ZSTD_ROW_HASH_CACHE_SIZE entries, + * but not beyond iLimit. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_row_fillHashCache( + ZSTD_MatchState_t* ms, + byte* @base, + uint rowLog, + uint mls, + uint idx, + byte* iLimit + ) + { + uint* hashTable = ms->hashTable; + byte* tagTable = ms->tagTable; + uint hashLog = ms->rowHashLog; + uint maxElemsToPrefetch = @base + idx > iLimit ? 0 : (uint)(iLimit - (@base + idx) + 1); + uint lim = idx + (8 < maxElemsToPrefetch ? 8 : maxElemsToPrefetch); + for (; idx < lim; ++idx) + { + uint hash = (uint)ZSTD_hashPtrSalted(@base + idx, hashLog + 8, mls, ms->hashSalt); + uint row = hash >> 8 << (int)rowLog; + ZSTD_row_prefetch(hashTable, tagTable, row, rowLog); + ms->hashCache[idx & 8 - 1] = hash; + } + } + + /* ZSTD_row_nextCachedHash(): + * Returns the hash of base + idx, and replaces the hash in the hash cache with the byte at + * base + idx + ZSTD_ROW_HASH_CACHE_SIZE. Also prefetches the appropriate rows from hashTable and tagTable. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_row_nextCachedHash( + uint* cache, + uint* hashTable, + byte* tagTable, + byte* @base, + uint idx, + uint hashLog, + uint rowLog, + uint mls, + ulong hashSalt + ) + { + uint newHash = (uint)ZSTD_hashPtrSalted(@base + idx + 8, hashLog + 8, mls, hashSalt); + uint row = newHash >> 8 << (int)rowLog; + ZSTD_row_prefetch(hashTable, tagTable, row, rowLog); + { + uint hash = cache[idx & 8 - 1]; + cache[idx & 8 - 1] = newHash; + return hash; + } + } + + /* ZSTD_row_update_internalImpl(): + * Updates the hash table with positions starting from updateStartIdx until updateEndIdx. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_row_update_internalImpl( + ZSTD_MatchState_t* ms, + uint updateStartIdx, + uint updateEndIdx, + uint mls, + uint rowLog, + uint rowMask, + uint useCache + ) + { + uint* hashTable = ms->hashTable; + byte* tagTable = ms->tagTable; + uint hashLog = ms->rowHashLog; + byte* @base = ms->window.@base; + for (; updateStartIdx < updateEndIdx; ++updateStartIdx) + { + uint hash = + useCache != 0 + ? ZSTD_row_nextCachedHash( + ms->hashCache, + hashTable, + tagTable, + @base, + updateStartIdx, + hashLog, + rowLog, + mls, + ms->hashSalt + ) + : (uint)ZSTD_hashPtrSalted( + @base + updateStartIdx, + hashLog + 8, + mls, + ms->hashSalt + ); + uint relRow = hash >> 8 << (int)rowLog; + uint* row = hashTable + relRow; + byte* tagRow = tagTable + relRow; + uint pos = ZSTD_row_nextIndex(tagRow, rowMask); + assert( + hash == ZSTD_hashPtrSalted(@base + updateStartIdx, hashLog + 8, mls, ms->hashSalt) + ); + tagRow[pos] = (byte)(hash & (1U << 8) - 1); + row[pos] = updateStartIdx; + } + } + + /* ZSTD_row_update_internal(): + * Inserts the byte at ip into the appropriate position in the hash table, and updates ms->nextToUpdate. + * Skips sections of long matches as is necessary. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_row_update_internal( + ZSTD_MatchState_t* ms, + byte* ip, + uint mls, + uint rowLog, + uint rowMask, + uint useCache + ) + { + uint idx = ms->nextToUpdate; + byte* @base = ms->window.@base; + uint target = (uint)(ip - @base); + const uint kSkipThreshold = 384; + const uint kMaxMatchStartPositionsToUpdate = 96; + const uint kMaxMatchEndPositionsToUpdate = 32; + if (useCache != 0) + { + if (target - idx > kSkipThreshold) + { + uint bound = idx + kMaxMatchStartPositionsToUpdate; + ZSTD_row_update_internalImpl(ms, idx, bound, mls, rowLog, rowMask, useCache); + idx = target - kMaxMatchEndPositionsToUpdate; + ZSTD_row_fillHashCache(ms, @base, rowLog, mls, idx, ip + 1); + } + } + + assert(target >= idx); + ZSTD_row_update_internalImpl(ms, idx, target, mls, rowLog, rowMask, useCache); + ms->nextToUpdate = target; + } + + /* ZSTD_row_update(): + * External wrapper for ZSTD_row_update_internal(). Used for filling the hashtable during dictionary + * processing. + */ + private static void ZSTD_row_update(ZSTD_MatchState_t* ms, byte* ip) + { + uint rowLog = + ms->cParams.searchLog <= 4 ? 4 + : ms->cParams.searchLog <= 6 ? ms->cParams.searchLog + : 6; + uint rowMask = (1U << (int)rowLog) - 1; + /* mls caps out at 6 */ + uint mls = ms->cParams.minMatch < 6 ? ms->cParams.minMatch : 6; + ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 0); + } + + /* Returns the mask width of bits group of which will be set to 1. Given not all + * architectures have easy movemask instruction, this helps to iterate over + * groups of bits easier and faster. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_row_matchMaskGroupWidth(uint rowEntries) + { + assert(rowEntries == 16 || rowEntries == 32 || rowEntries == 64); + assert(rowEntries <= 64); +#if NET5_0_OR_GREATER + if (AdvSimd.IsSupported && BitConverter.IsLittleEndian) + { + if (rowEntries == 16) + return 4; +#if NET9_0_OR_GREATER + if (AdvSimd.Arm64.IsSupported) + { + if (rowEntries == 32) + return 2; + if (rowEntries == 64) + return 1; + } +#endif + } +#endif + return 1; + } + +#if NETCOREAPP3_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong ZSTD_row_getSSEMask(int nbChunks, byte* src, byte tag, uint head) + { + Vector128 comparisonMask = Vector128.Create(tag); + assert(nbChunks is 1 or 2 or 4); + if (nbChunks == 1) + { + Vector128 chunk0 = Sse2.LoadVector128(src); + Vector128 equalMask0 = Sse2.CompareEqual(chunk0, comparisonMask); + int matches0 = Sse2.MoveMask(equalMask0); + return BitOperations.RotateRight((ushort)matches0, (int)head); + } + + if (nbChunks == 2) + { + Vector128 chunk0 = Sse2.LoadVector128(src); + Vector128 equalMask0 = Sse2.CompareEqual(chunk0, comparisonMask); + int matches0 = Sse2.MoveMask(equalMask0); + Vector128 chunk1 = Sse2.LoadVector128(src + 16); + Vector128 equalMask1 = Sse2.CompareEqual(chunk1, comparisonMask); + int matches1 = Sse2.MoveMask(equalMask1); + return BitOperations.RotateRight((uint)matches1 << 16 | (uint)matches0, (int)head); + } + + { + Vector128 chunk0 = Sse2.LoadVector128(src); + Vector128 equalMask0 = Sse2.CompareEqual(chunk0, comparisonMask); + int matches0 = Sse2.MoveMask(equalMask0); + Vector128 chunk1 = Sse2.LoadVector128(src + 16 * 1); + Vector128 equalMask1 = Sse2.CompareEqual(chunk1, comparisonMask); + int matches1 = Sse2.MoveMask(equalMask1); + Vector128 chunk2 = Sse2.LoadVector128(src + 16 * 2); + Vector128 equalMask2 = Sse2.CompareEqual(chunk2, comparisonMask); + int matches2 = Sse2.MoveMask(equalMask2); + Vector128 chunk3 = Sse2.LoadVector128(src + 16 * 3); + Vector128 equalMask3 = Sse2.CompareEqual(chunk3, comparisonMask); + int matches3 = Sse2.MoveMask(equalMask3); + return BitOperations.RotateRight( + (ulong)matches3 << 48 + | (ulong)matches2 << 32 + | (ulong)matches1 << 16 + | (uint)matches0, + (int)head + ); + } + } +#endif + + /* Returns a ZSTD_VecMask (U64) that has the nth group (determined by + * ZSTD_row_matchMaskGroupWidth) of bits set to 1 if the newly-computed "tag" + * matches the hash at the nth position in a row of the tagTable. + * Each row is a circular buffer beginning at the value of "headGrouped". So we + * must rotate the "matches" bitfield to match up with the actual layout of the + * entries within the hashTable */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong ZSTD_row_getMatchMask( + byte* tagRow, + byte tag, + uint headGrouped, + uint rowEntries + ) + { + byte* src = tagRow; + assert(rowEntries == 16 || rowEntries == 32 || rowEntries == 64); + assert(rowEntries <= 64); + assert(ZSTD_row_matchMaskGroupWidth(rowEntries) * rowEntries <= sizeof(ulong) * 8); +#if NETCOREAPP3_0_OR_GREATER + if (Sse2.IsSupported) + { + return ZSTD_row_getSSEMask((int)(rowEntries / 16), src, tag, headGrouped); + } +#endif + +#if NET5_0_OR_GREATER + if (AdvSimd.IsSupported && BitConverter.IsLittleEndian) + { + if (rowEntries == 16) + { + /* vshrn_n_u16 shifts by 4 every u16 and narrows to 8 lower bits. + * After that groups of 4 bits represent the equalMask. We lower + * all bits except the highest in these groups by doing AND with + * 0x88 = 0b10001000. + */ + Vector128 chunk = AdvSimd.LoadVector128(src); + Vector128 equalMask = AdvSimd + .CompareEqual(chunk, AdvSimd.DuplicateToVector128(tag)) + .As(); + Vector64 res = AdvSimd.ShiftRightLogicalNarrowingLower(equalMask, 4); + ulong matches = res.As().GetElement(0); + return BitOperations.RotateRight(matches, (int)headGrouped) & 0x8888888888888888; + } + else if (rowEntries == 32) + { +#if NET9_0_OR_GREATER + if (AdvSimd.Arm64.IsSupported) + { + /* Same idea as with rowEntries == 16 but doing AND with + * 0x55 = 0b01010101. + */ + (Vector128 chunk0, Vector128 chunk1) = + AdvSimd.Arm64.Load2xVector128AndUnzip((ushort*)src); + Vector128 dup = AdvSimd.DuplicateToVector128(tag); + Vector64 t0 = AdvSimd.ShiftRightLogicalNarrowingLower( + AdvSimd.CompareEqual(chunk0.As(), dup).As(), + 6 + ); + Vector64 t1 = AdvSimd.ShiftRightLogicalNarrowingLower( + AdvSimd.CompareEqual(chunk1.As(), dup).As(), + 6 + ); + Vector64 res = AdvSimd.ShiftLeftAndInsert(t0, t1, 4); + ulong matches = res.As().GetElement(0); + return BitOperations.RotateRight(matches, (int)headGrouped) + & 0x5555555555555555; + } +#endif + } + else + { /* rowEntries == 64 */ +#if NET9_0_OR_GREATER + if (AdvSimd.Arm64.IsSupported) + { + ( + Vector128 chunk0, + Vector128 chunk1, + Vector128 chunk2, + Vector128 chunk3 + ) = AdvSimd.Arm64.Load4xVector128AndUnzip(src); + Vector128 dup = AdvSimd.DuplicateToVector128(tag); + Vector128 cmp0 = AdvSimd.CompareEqual(chunk0, dup); + Vector128 cmp1 = AdvSimd.CompareEqual(chunk1, dup); + Vector128 cmp2 = AdvSimd.CompareEqual(chunk2, dup); + Vector128 cmp3 = AdvSimd.CompareEqual(chunk3, dup); + + Vector128 t0 = AdvSimd.ShiftRightAndInsert(cmp1, cmp0, 1); + Vector128 t1 = AdvSimd.ShiftRightAndInsert(cmp3, cmp2, 1); + Vector128 t2 = AdvSimd.ShiftRightAndInsert(t1, t0, 2); + Vector128 t3 = AdvSimd.ShiftRightAndInsert(t2, t2, 4); + Vector64 t4 = AdvSimd.ShiftRightLogicalNarrowingLower( + t3.As(), + 4 + ); + ulong matches = t4.As().GetElement(0); + return BitOperations.RotateRight(matches, (int)headGrouped); + } +#endif + } + } +#endif + + { + nuint chunkSize = (nuint)sizeof(nuint); + nuint shiftAmount = chunkSize * 8 - chunkSize; + nuint xFF = ~(nuint)0; + nuint x01 = xFF / 0xFF; + nuint x80 = x01 << 7; + nuint splatChar = tag * x01; + ulong matches = 0; + int i = (int)(rowEntries - chunkSize); + assert(sizeof(nuint) == 4 || sizeof(nuint) == 8); + if (BitConverter.IsLittleEndian) + { + nuint extractMagic = xFF / 0x7F >> (int)chunkSize; + do + { + nuint chunk = MEM_readST(&src[i]); + chunk ^= splatChar; + chunk = ((chunk | x80) - x01 | chunk) & x80; + matches <<= (int)chunkSize; + matches |= chunk * extractMagic >> (int)shiftAmount; + i -= (int)chunkSize; + } while (i >= 0); + } + else + { + nuint msb = xFF ^ xFF >> 1; + nuint extractMagic = msb / 0x1FF | msb; + do + { + nuint chunk = MEM_readST(&src[i]); + chunk ^= splatChar; + chunk = ((chunk | x80) - x01 | chunk) & x80; + matches <<= (int)chunkSize; + matches |= (chunk >> 7) * extractMagic >> (int)shiftAmount; + i -= (int)chunkSize; + } while (i >= 0); + } + + matches = ~matches; + if (rowEntries == 16) + { + return BitOperations.RotateRight((ushort)matches, (int)headGrouped); + } + else if (rowEntries == 32) + { + return BitOperations.RotateRight((uint)matches, (int)headGrouped); + } + else + { + return BitOperations.RotateRight(matches, (int)headGrouped); + } + } + } + + /* The high-level approach of the SIMD row based match finder is as follows: + * - Figure out where to insert the new entry: + * - Generate a hash for current input position and split it into a one byte of tag and `rowHashLog` bits of index. + * - The hash is salted by a value that changes on every context reset, so when the same table is used + * we will avoid collisions that would otherwise slow us down by introducing phantom matches. + * - The hashTable is effectively split into groups or "rows" of 15 or 31 entries of U32, and the index determines + * which row to insert into. + * - Determine the correct position within the row to insert the entry into. Each row of 15 or 31 can + * be considered as a circular buffer with a "head" index that resides in the tagTable (overall 16 or 32 bytes + * per row). + * - Use SIMD to efficiently compare the tags in the tagTable to the 1-byte tag calculated for the position and + * generate a bitfield that we can cycle through to check the collisions in the hash table. + * - Pick the longest match. + * - Insert the tag into the equivalent row and position in the tagTable. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_RowFindBestMatch( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr, + uint mls, + ZSTD_dictMode_e dictMode, + uint rowLog + ) + { + uint* hashTable = ms->hashTable; + byte* tagTable = ms->tagTable; + uint* hashCache = ms->hashCache; + uint hashLog = ms->rowHashLog; + ZSTD_compressionParameters* cParams = &ms->cParams; + byte* @base = ms->window.@base; + byte* dictBase = ms->window.dictBase; + uint dictLimit = ms->window.dictLimit; + byte* prefixStart = @base + dictLimit; + byte* dictEnd = dictBase + dictLimit; + uint curr = (uint)(ip - @base); + uint maxDistance = 1U << (int)cParams->windowLog; + uint lowestValid = ms->window.lowLimit; + uint withinMaxDistance = + curr - lowestValid > maxDistance ? curr - maxDistance : lowestValid; + uint isDictionary = ms->loadedDictEnd != 0 ? 1U : 0U; + uint lowLimit = isDictionary != 0 ? lowestValid : withinMaxDistance; + uint rowEntries = 1U << (int)rowLog; + uint rowMask = rowEntries - 1; + /* nb of searches is capped at nb entries per row */ + uint cappedSearchLog = cParams->searchLog < rowLog ? cParams->searchLog : rowLog; + uint groupWidth = ZSTD_row_matchMaskGroupWidth(rowEntries); + ulong hashSalt = ms->hashSalt; + uint nbAttempts = 1U << (int)cappedSearchLog; + nuint ml = 4 - 1; + uint hash; + /* DMS/DDS variables that may be referenced laster */ + ZSTD_MatchState_t* dms = ms->dictMatchState; + /* Initialize the following variables to satisfy static analyzer */ + nuint ddsIdx = 0; + /* cctx hash tables are limited in searches, but allow extra searches into DDS */ + uint ddsExtraAttempts = 0; + uint dmsTag = 0; + uint* dmsRow = null; + byte* dmsTagRow = null; + if (dictMode == ZSTD_dictMode_e.ZSTD_dedicatedDictSearch) + { + uint ddsHashLog = dms->cParams.hashLog - 2; + { + ddsIdx = ZSTD_hashPtr(ip, ddsHashLog, mls) << 2; +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(&dms->hashTable[ddsIdx]); + } +#endif + } + + ddsExtraAttempts = + cParams->searchLog > rowLog ? 1U << (int)(cParams->searchLog - rowLog) : 0; + } + + if (dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState) + { + /* Prefetch DMS rows */ + uint* dmsHashTable = dms->hashTable; + byte* dmsTagTable = dms->tagTable; + uint dmsHash = (uint)ZSTD_hashPtr(ip, dms->rowHashLog + 8, mls); + uint dmsRelRow = dmsHash >> 8 << (int)rowLog; + dmsTag = dmsHash & (1U << 8) - 1; + dmsTagRow = dmsTagTable + dmsRelRow; + dmsRow = dmsHashTable + dmsRelRow; + ZSTD_row_prefetch(dmsHashTable, dmsTagTable, dmsRelRow, rowLog); + } + + if (ms->lazySkipping == 0) + { + ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 1); + hash = ZSTD_row_nextCachedHash( + hashCache, + hashTable, + tagTable, + @base, + curr, + hashLog, + rowLog, + mls, + hashSalt + ); + } + else + { + hash = (uint)ZSTD_hashPtrSalted(ip, hashLog + 8, mls, hashSalt); + ms->nextToUpdate = curr; + } + + ms->hashSaltEntropy += hash; + { + uint relRow = hash >> 8 << (int)rowLog; + uint tag = hash & (1U << 8) - 1; + uint* row = hashTable + relRow; + byte* tagRow = tagTable + relRow; + uint headGrouped = (*tagRow & rowMask) * groupWidth; + uint* matchBuffer = stackalloc uint[64]; + nuint numMatches = 0; + nuint currMatch = 0; + ulong matches = ZSTD_row_getMatchMask(tagRow, (byte)tag, headGrouped, rowEntries); + for (; matches > 0 && nbAttempts > 0; matches &= matches - 1) + { + uint matchPos = (headGrouped + ZSTD_VecMask_next(matches)) / groupWidth & rowMask; + uint matchIndex = row[matchPos]; + if (matchPos == 0) + continue; + assert(numMatches < rowEntries); + if (matchIndex < lowLimit) + break; + if (dictMode != ZSTD_dictMode_e.ZSTD_extDict || matchIndex >= dictLimit) + { +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(@base + matchIndex); + } +#endif + } + else + { +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(dictBase + matchIndex); + } +#endif + } + + matchBuffer[numMatches++] = matchIndex; + --nbAttempts; + } + + { + uint pos = ZSTD_row_nextIndex(tagRow, rowMask); + tagRow[pos] = (byte)tag; + row[pos] = ms->nextToUpdate++; + } + + for (; currMatch < numMatches; ++currMatch) + { + uint matchIndex = matchBuffer[currMatch]; + nuint currentMl = 0; + assert(matchIndex < curr); + assert(matchIndex >= lowLimit); + if (dictMode != ZSTD_dictMode_e.ZSTD_extDict || matchIndex >= dictLimit) + { + byte* match = @base + matchIndex; + assert(matchIndex >= dictLimit); + if (MEM_read32(match + ml - 3) == MEM_read32(ip + ml - 3)) + currentMl = ZSTD_count(ip, match, iLimit); + } + else + { + byte* match = dictBase + matchIndex; + assert(match + 4 <= dictEnd); + if (MEM_read32(match) == MEM_read32(ip)) + currentMl = + ZSTD_count_2segments(ip + 4, match + 4, iLimit, dictEnd, prefixStart) + + 4; + } + + if (currentMl > ml) + { + ml = currentMl; + assert(curr - matchIndex > 0); + *offsetPtr = curr - matchIndex + 3; + if (ip + currentMl == iLimit) + break; + } + } + } + + assert(nbAttempts <= 1U << (sizeof(nuint) == 4 ? 30 : 31) - 1); + if (dictMode == ZSTD_dictMode_e.ZSTD_dedicatedDictSearch) + { + ml = ZSTD_dedicatedDictSearch_lazy_search( + offsetPtr, + ml, + nbAttempts + ddsExtraAttempts, + dms, + ip, + iLimit, + prefixStart, + curr, + dictLimit, + ddsIdx + ); + } + else if (dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState) + { + /* TODO: Measure and potentially add prefetching to DMS */ + uint dmsLowestIndex = dms->window.dictLimit; + byte* dmsBase = dms->window.@base; + byte* dmsEnd = dms->window.nextSrc; + uint dmsSize = (uint)(dmsEnd - dmsBase); + uint dmsIndexDelta = dictLimit - dmsSize; + { + uint headGrouped = (*dmsTagRow & rowMask) * groupWidth; + uint* matchBuffer = stackalloc uint[64]; + nuint numMatches = 0; + nuint currMatch = 0; + ulong matches = ZSTD_row_getMatchMask( + dmsTagRow, + (byte)dmsTag, + headGrouped, + rowEntries + ); + for (; matches > 0 && nbAttempts > 0; matches &= matches - 1) + { + uint matchPos = + (headGrouped + ZSTD_VecMask_next(matches)) / groupWidth & rowMask; + uint matchIndex = dmsRow[matchPos]; + if (matchPos == 0) + continue; + if (matchIndex < dmsLowestIndex) + break; +#if NETCOREAPP3_0_OR_GREATER + if (Sse.IsSupported) + { + Sse.Prefetch0(dmsBase + matchIndex); + } +#endif + + matchBuffer[numMatches++] = matchIndex; + --nbAttempts; + } + + for (; currMatch < numMatches; ++currMatch) + { + uint matchIndex = matchBuffer[currMatch]; + nuint currentMl = 0; + assert(matchIndex >= dmsLowestIndex); + assert(matchIndex < curr); + { + byte* match = dmsBase + matchIndex; + assert(match + 4 <= dmsEnd); + if (MEM_read32(match) == MEM_read32(ip)) + currentMl = + ZSTD_count_2segments(ip + 4, match + 4, iLimit, dmsEnd, prefixStart) + + 4; + } + + if (currentMl > ml) + { + ml = currentMl; + assert(curr > matchIndex + dmsIndexDelta); + assert(curr - (matchIndex + dmsIndexDelta) > 0); + *offsetPtr = curr - (matchIndex + dmsIndexDelta) + 3; + if (ip + currentMl == iLimit) + break; + } + } + } + } + + return ml; + } + + /* Generate row search fns for each combination of (dictMode, mls, rowLog) */ + private static nuint ZSTD_RowFindBestMatch_noDict_4_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMode_e.ZSTD_noDict, 4); + } + + private static nuint ZSTD_RowFindBestMatch_noDict_4_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMode_e.ZSTD_noDict, 5); + } + + private static nuint ZSTD_RowFindBestMatch_noDict_4_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMode_e.ZSTD_noDict, 6); + } + + private static nuint ZSTD_RowFindBestMatch_noDict_5_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMode_e.ZSTD_noDict, 4); + } + + private static nuint ZSTD_RowFindBestMatch_noDict_5_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMode_e.ZSTD_noDict, 5); + } + + private static nuint ZSTD_RowFindBestMatch_noDict_5_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMode_e.ZSTD_noDict, 6); + } + + private static nuint ZSTD_RowFindBestMatch_noDict_6_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMode_e.ZSTD_noDict, 4); + } + + private static nuint ZSTD_RowFindBestMatch_noDict_6_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMode_e.ZSTD_noDict, 5); + } + + private static nuint ZSTD_RowFindBestMatch_noDict_6_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMode_e.ZSTD_noDict, 6); + } + + private static nuint ZSTD_RowFindBestMatch_extDict_4_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMode_e.ZSTD_extDict, 4); + } + + private static nuint ZSTD_RowFindBestMatch_extDict_4_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMode_e.ZSTD_extDict, 5); + } + + private static nuint ZSTD_RowFindBestMatch_extDict_4_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMode_e.ZSTD_extDict, 6); + } + + private static nuint ZSTD_RowFindBestMatch_extDict_5_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMode_e.ZSTD_extDict, 4); + } + + private static nuint ZSTD_RowFindBestMatch_extDict_5_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMode_e.ZSTD_extDict, 5); + } + + private static nuint ZSTD_RowFindBestMatch_extDict_5_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMode_e.ZSTD_extDict, 6); + } + + private static nuint ZSTD_RowFindBestMatch_extDict_6_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMode_e.ZSTD_extDict, 4); + } + + private static nuint ZSTD_RowFindBestMatch_extDict_6_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMode_e.ZSTD_extDict, 5); + } + + private static nuint ZSTD_RowFindBestMatch_extDict_6_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMode_e.ZSTD_extDict, 6); + } + + private static nuint ZSTD_RowFindBestMatch_dictMatchState_4_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 4, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 4 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dictMatchState_4_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 4, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 5 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dictMatchState_4_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 4, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 6 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dictMatchState_5_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 5, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 4 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dictMatchState_5_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 5, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 5 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dictMatchState_5_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 5, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 6 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dictMatchState_6_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 6, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 4 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dictMatchState_6_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 6, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 5 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dictMatchState_6_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 6, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 6 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dedicatedDictSearch_4_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 4, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch, + 4 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dedicatedDictSearch_4_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 4, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch, + 5 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dedicatedDictSearch_4_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 4, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch, + 6 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dedicatedDictSearch_5_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 5, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch, + 4 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dedicatedDictSearch_5_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 5, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch, + 5 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dedicatedDictSearch_5_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 5, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch, + 6 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dedicatedDictSearch_6_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 4 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 6, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch, + 4 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dedicatedDictSearch_6_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 5 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 6, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch, + 5 + ); + } + + private static nuint ZSTD_RowFindBestMatch_dedicatedDictSearch_6_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + assert( + ( + 4 > (6 < ms->cParams.searchLog ? 6 : ms->cParams.searchLog) ? 4 + : 6 < ms->cParams.searchLog ? 6 + : ms->cParams.searchLog + ) == 6 + ); + return ZSTD_RowFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 6, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch, + 6 + ); + } + + /* Generate binary Tree search fns for each combination of (dictMode, mls) */ + private static nuint ZSTD_BtFindBestMatch_noDict_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + return ZSTD_BtFindBestMatch(ms, ip, iLimit, offBasePtr, 4, ZSTD_dictMode_e.ZSTD_noDict); + } + + private static nuint ZSTD_BtFindBestMatch_noDict_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + return ZSTD_BtFindBestMatch(ms, ip, iLimit, offBasePtr, 5, ZSTD_dictMode_e.ZSTD_noDict); + } + + private static nuint ZSTD_BtFindBestMatch_noDict_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + return ZSTD_BtFindBestMatch(ms, ip, iLimit, offBasePtr, 6, ZSTD_dictMode_e.ZSTD_noDict); + } + + private static nuint ZSTD_BtFindBestMatch_extDict_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + return ZSTD_BtFindBestMatch(ms, ip, iLimit, offBasePtr, 4, ZSTD_dictMode_e.ZSTD_extDict); + } + + private static nuint ZSTD_BtFindBestMatch_extDict_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + return ZSTD_BtFindBestMatch(ms, ip, iLimit, offBasePtr, 5, ZSTD_dictMode_e.ZSTD_extDict); + } + + private static nuint ZSTD_BtFindBestMatch_extDict_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + return ZSTD_BtFindBestMatch(ms, ip, iLimit, offBasePtr, 6, ZSTD_dictMode_e.ZSTD_extDict); + } + + private static nuint ZSTD_BtFindBestMatch_dictMatchState_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + return ZSTD_BtFindBestMatch( + ms, + ip, + iLimit, + offBasePtr, + 4, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_BtFindBestMatch_dictMatchState_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + return ZSTD_BtFindBestMatch( + ms, + ip, + iLimit, + offBasePtr, + 5, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_BtFindBestMatch_dictMatchState_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + return ZSTD_BtFindBestMatch( + ms, + ip, + iLimit, + offBasePtr, + 6, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_BtFindBestMatch_dedicatedDictSearch_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + return ZSTD_BtFindBestMatch( + ms, + ip, + iLimit, + offBasePtr, + 4, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + private static nuint ZSTD_BtFindBestMatch_dedicatedDictSearch_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + return ZSTD_BtFindBestMatch( + ms, + ip, + iLimit, + offBasePtr, + 5, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + private static nuint ZSTD_BtFindBestMatch_dedicatedDictSearch_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offBasePtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + return ZSTD_BtFindBestMatch( + ms, + ip, + iLimit, + offBasePtr, + 6, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + /* Generate hash chain search fns for each combination of (dictMode, mls) */ + private static nuint ZSTD_HcFindBestMatch_noDict_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + return ZSTD_HcFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMode_e.ZSTD_noDict); + } + + private static nuint ZSTD_HcFindBestMatch_noDict_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + return ZSTD_HcFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMode_e.ZSTD_noDict); + } + + private static nuint ZSTD_HcFindBestMatch_noDict_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + return ZSTD_HcFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMode_e.ZSTD_noDict); + } + + private static nuint ZSTD_HcFindBestMatch_extDict_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + return ZSTD_HcFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMode_e.ZSTD_extDict); + } + + private static nuint ZSTD_HcFindBestMatch_extDict_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + return ZSTD_HcFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMode_e.ZSTD_extDict); + } + + private static nuint ZSTD_HcFindBestMatch_extDict_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + return ZSTD_HcFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMode_e.ZSTD_extDict); + } + + private static nuint ZSTD_HcFindBestMatch_dictMatchState_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + return ZSTD_HcFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 4, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_HcFindBestMatch_dictMatchState_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + return ZSTD_HcFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 5, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_HcFindBestMatch_dictMatchState_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + return ZSTD_HcFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 6, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_HcFindBestMatch_dedicatedDictSearch_4( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 4 + ); + return ZSTD_HcFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 4, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + private static nuint ZSTD_HcFindBestMatch_dedicatedDictSearch_5( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 5 + ); + return ZSTD_HcFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 5, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + private static nuint ZSTD_HcFindBestMatch_dedicatedDictSearch_6( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iLimit, + nuint* offsetPtr + ) + { + assert( + ( + 4 > (6 < ms->cParams.minMatch ? 6 : ms->cParams.minMatch) ? 4 + : 6 < ms->cParams.minMatch ? 6 + : ms->cParams.minMatch + ) == 6 + ); + return ZSTD_HcFindBestMatch( + ms, + ip, + iLimit, + offsetPtr, + 6, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + /** + * Searches for the longest match at @p ip. + * Dispatches to the correct implementation function based on the + * (searchMethod, dictMode, mls, rowLog). We use switch statements + * here instead of using an indirect function call through a function + * pointer because after Spectre and Meltdown mitigations, indirect + * function calls can be very costly, especially in the kernel. + * + * NOTE: dictMode and searchMethod should be templated, so those switch + * statements should be optimized out. Only the mls & rowLog switches + * should be left. + * + * @param ms The match state. + * @param ip The position to search at. + * @param iend The end of the input data. + * @param[out] offsetPtr Stores the match offset into this pointer. + * @param mls The minimum search length, in the range [4, 6]. + * @param rowLog The row log (if applicable), in the range [4, 6]. + * @param searchMethod The search method to use (templated). + * @param dictMode The dictMode (templated). + * + * @returns The length of the longest match found, or < mls if no match is found. + * If a match is found its offset is stored in @p offsetPtr. + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_searchMax( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iend, + nuint* offsetPtr, + uint mls, + uint rowLog, + searchMethod_e searchMethod, + ZSTD_dictMode_e dictMode + ) + { + if (dictMode == ZSTD_dictMode_e.ZSTD_noDict) + { + if (searchMethod == searchMethod_e.search_rowHash) + { + if (mls == 4) + { + if (rowLog == 4) + return ZSTD_RowFindBestMatch_noDict_4_4(ms, ip, iend, offsetPtr); + return rowLog == 5 + ? ZSTD_RowFindBestMatch_noDict_4_5(ms, ip, iend, offsetPtr) + : ZSTD_RowFindBestMatch_noDict_4_6(ms, ip, iend, offsetPtr); + } + + if (mls == 5) + { + if (rowLog == 4) + return ZSTD_RowFindBestMatch_noDict_5_4(ms, ip, iend, offsetPtr); + return rowLog == 5 + ? ZSTD_RowFindBestMatch_noDict_5_5(ms, ip, iend, offsetPtr) + : ZSTD_RowFindBestMatch_noDict_5_6(ms, ip, iend, offsetPtr); + } + + if (rowLog == 4) + return ZSTD_RowFindBestMatch_noDict_6_4(ms, ip, iend, offsetPtr); + return rowLog == 5 + ? ZSTD_RowFindBestMatch_noDict_6_5(ms, ip, iend, offsetPtr) + : ZSTD_RowFindBestMatch_noDict_6_6(ms, ip, iend, offsetPtr); + } + + if (searchMethod == searchMethod_e.search_hashChain) + { + if (mls == 4) + return ZSTD_HcFindBestMatch_noDict_4(ms, ip, iend, offsetPtr); + return mls == 5 + ? ZSTD_HcFindBestMatch_noDict_5(ms, ip, iend, offsetPtr) + : ZSTD_HcFindBestMatch_noDict_6(ms, ip, iend, offsetPtr); + } + + // searchMethod_e.search_binaryTree + if (mls == 4) + return ZSTD_BtFindBestMatch_noDict_4(ms, ip, iend, offsetPtr); + return mls == 5 + ? ZSTD_BtFindBestMatch_noDict_5(ms, ip, iend, offsetPtr) + : ZSTD_BtFindBestMatch_noDict_6(ms, ip, iend, offsetPtr); + } + + if (dictMode == ZSTD_dictMode_e.ZSTD_extDict) + { + if (searchMethod == searchMethod_e.search_rowHash) + { + if (mls == 4) + { + if (rowLog == 4) + return ZSTD_RowFindBestMatch_extDict_4_4(ms, ip, iend, offsetPtr); + if (rowLog == 5) + return ZSTD_RowFindBestMatch_extDict_4_5(ms, ip, iend, offsetPtr); + return ZSTD_RowFindBestMatch_extDict_4_6(ms, ip, iend, offsetPtr); + } + + if (mls == 5) + { + if (rowLog == 4) + return ZSTD_RowFindBestMatch_extDict_5_4(ms, ip, iend, offsetPtr); + if (rowLog == 5) + return ZSTD_RowFindBestMatch_extDict_5_5(ms, ip, iend, offsetPtr); + return ZSTD_RowFindBestMatch_extDict_5_6(ms, ip, iend, offsetPtr); + } + + if (mls == 6) + { + if (rowLog == 4) + return ZSTD_RowFindBestMatch_extDict_6_4(ms, ip, iend, offsetPtr); + if (rowLog == 5) + return ZSTD_RowFindBestMatch_extDict_6_5(ms, ip, iend, offsetPtr); + return ZSTD_RowFindBestMatch_extDict_6_6(ms, ip, iend, offsetPtr); + } + } + + if (searchMethod == searchMethod_e.search_hashChain) + { + if (mls == 4) + return ZSTD_HcFindBestMatch_extDict_4(ms, ip, iend, offsetPtr); + if (mls == 5) + return ZSTD_HcFindBestMatch_extDict_5(ms, ip, iend, offsetPtr); + return ZSTD_HcFindBestMatch_extDict_6(ms, ip, iend, offsetPtr); + } + + // searchMethod_e.search_binaryTree + if (mls == 4) + return ZSTD_BtFindBestMatch_extDict_4(ms, ip, iend, offsetPtr); + if (mls == 5) + return ZSTD_BtFindBestMatch_extDict_5(ms, ip, iend, offsetPtr); + return ZSTD_BtFindBestMatch_extDict_6(ms, ip, iend, offsetPtr); + } + + if (dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState) + { + if (searchMethod == searchMethod_e.search_rowHash) + { + if (mls == 4) + { + if (rowLog == 4) + return ZSTD_RowFindBestMatch_dictMatchState_4_4(ms, ip, iend, offsetPtr); + if (rowLog == 5) + return ZSTD_RowFindBestMatch_dictMatchState_4_5(ms, ip, iend, offsetPtr); + return ZSTD_RowFindBestMatch_dictMatchState_4_6(ms, ip, iend, offsetPtr); + } + + if (mls == 5) + { + if (rowLog == 4) + return ZSTD_RowFindBestMatch_dictMatchState_5_4(ms, ip, iend, offsetPtr); + if (rowLog == 5) + return ZSTD_RowFindBestMatch_dictMatchState_5_5(ms, ip, iend, offsetPtr); + return ZSTD_RowFindBestMatch_dictMatchState_5_6(ms, ip, iend, offsetPtr); + } + + if (mls == 6) + { + if (rowLog == 4) + return ZSTD_RowFindBestMatch_dictMatchState_6_4(ms, ip, iend, offsetPtr); + if (rowLog == 5) + return ZSTD_RowFindBestMatch_dictMatchState_6_5(ms, ip, iend, offsetPtr); + return ZSTD_RowFindBestMatch_dictMatchState_6_6(ms, ip, iend, offsetPtr); + } + } + + if (searchMethod == searchMethod_e.search_hashChain) + { + if (mls == 4) + return ZSTD_HcFindBestMatch_dictMatchState_4(ms, ip, iend, offsetPtr); + if (mls == 5) + return ZSTD_HcFindBestMatch_dictMatchState_5(ms, ip, iend, offsetPtr); + return ZSTD_HcFindBestMatch_dictMatchState_6(ms, ip, iend, offsetPtr); + } + + // search_binaryTree + if (mls == 4) + return ZSTD_BtFindBestMatch_dictMatchState_4(ms, ip, iend, offsetPtr); + if (mls == 5) + return ZSTD_BtFindBestMatch_dictMatchState_5(ms, ip, iend, offsetPtr); + return ZSTD_BtFindBestMatch_dictMatchState_6(ms, ip, iend, offsetPtr); + } + + if (searchMethod == searchMethod_e.search_rowHash) + { + if (mls == 4) + { + if (rowLog == 4) + return ZSTD_RowFindBestMatch_dedicatedDictSearch_4_4(ms, ip, iend, offsetPtr); + if (rowLog == 5) + return ZSTD_RowFindBestMatch_dedicatedDictSearch_4_5(ms, ip, iend, offsetPtr); + return ZSTD_RowFindBestMatch_dedicatedDictSearch_4_6(ms, ip, iend, offsetPtr); + } + + if (mls == 5) + { + if (rowLog == 4) + return ZSTD_RowFindBestMatch_dedicatedDictSearch_5_4(ms, ip, iend, offsetPtr); + if (rowLog == 5) + return ZSTD_RowFindBestMatch_dedicatedDictSearch_5_5(ms, ip, iend, offsetPtr); + return ZSTD_RowFindBestMatch_dedicatedDictSearch_5_6(ms, ip, iend, offsetPtr); + } + + if (mls == 6) + { + if (rowLog == 4) + return ZSTD_RowFindBestMatch_dedicatedDictSearch_6_4(ms, ip, iend, offsetPtr); + if (rowLog == 5) + return ZSTD_RowFindBestMatch_dedicatedDictSearch_6_5(ms, ip, iend, offsetPtr); + return ZSTD_RowFindBestMatch_dedicatedDictSearch_6_6(ms, ip, iend, offsetPtr); + } + } + + if (searchMethod == searchMethod_e.search_hashChain) + { + if (mls == 4) + return ZSTD_HcFindBestMatch_dedicatedDictSearch_4(ms, ip, iend, offsetPtr); + if (mls == 5) + return ZSTD_HcFindBestMatch_dedicatedDictSearch_5(ms, ip, iend, offsetPtr); + return ZSTD_HcFindBestMatch_dedicatedDictSearch_6(ms, ip, iend, offsetPtr); + } + + // searchMethod_e.search_binaryTree + if (mls == 4) + return ZSTD_BtFindBestMatch_dedicatedDictSearch_4(ms, ip, iend, offsetPtr); + if (mls == 5) + return ZSTD_BtFindBestMatch_dedicatedDictSearch_5(ms, ip, iend, offsetPtr); + return ZSTD_BtFindBestMatch_dedicatedDictSearch_6(ms, ip, iend, offsetPtr); + } + + /* ******************************* + * Common parser - lazy strategy + *********************************/ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_compressBlock_lazy_generic( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize, + searchMethod_e searchMethod, + uint depth, + ZSTD_dictMode_e dictMode + ) + { + byte* istart = (byte*)src; + byte* ip = istart; + byte* anchor = istart; + byte* iend = istart + srcSize; + byte* ilimit = searchMethod == searchMethod_e.search_rowHash ? iend - 8 - 8 : iend - 8; + byte* @base = ms->window.@base; + uint prefixLowestIndex = ms->window.dictLimit; + byte* prefixLowest = @base + prefixLowestIndex; + uint mls = + ms->cParams.minMatch <= 4 ? 4 + : ms->cParams.minMatch <= 6 ? ms->cParams.minMatch + : 6; + uint rowLog = + ms->cParams.searchLog <= 4 ? 4 + : ms->cParams.searchLog <= 6 ? ms->cParams.searchLog + : 6; + uint offset_1 = rep[0], + offset_2 = rep[1]; + uint offsetSaved1 = 0, + offsetSaved2 = 0; + int isDMS = dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState ? 1 : 0; + int isDDS = dictMode == ZSTD_dictMode_e.ZSTD_dedicatedDictSearch ? 1 : 0; + int isDxS = isDMS != 0 || isDDS != 0 ? 1 : 0; + ZSTD_MatchState_t* dms = ms->dictMatchState; + uint dictLowestIndex = isDxS != 0 ? dms->window.dictLimit : 0; + byte* dictBase = isDxS != 0 ? dms->window.@base : null; + byte* dictLowest = isDxS != 0 ? dictBase + dictLowestIndex : null; + byte* dictEnd = isDxS != 0 ? dms->window.nextSrc : null; + uint dictIndexDelta = isDxS != 0 ? prefixLowestIndex - (uint)(dictEnd - dictBase) : 0; + uint dictAndPrefixLength = (uint)(ip - prefixLowest + (dictEnd - dictLowest)); + ip += dictAndPrefixLength == 0 ? 1 : 0; + if (dictMode == ZSTD_dictMode_e.ZSTD_noDict) + { + uint curr = (uint)(ip - @base); + uint windowLow = ZSTD_getLowestPrefixIndex(ms, curr, ms->cParams.windowLog); + uint maxRep = curr - windowLow; + if (offset_2 > maxRep) + { + offsetSaved2 = offset_2; + offset_2 = 0; + } + + if (offset_1 > maxRep) + { + offsetSaved1 = offset_1; + offset_1 = 0; + } + } + +#if DEBUG + if (isDxS != 0) + { + assert(offset_1 <= dictAndPrefixLength); + assert(offset_2 <= dictAndPrefixLength); + } +#endif + + ms->lazySkipping = 0; + if (searchMethod == searchMethod_e.search_rowHash) + { + ZSTD_row_fillHashCache(ms, @base, rowLog, mls, ms->nextToUpdate, ilimit); + } + + while (ip < ilimit) + { + nuint matchLength = 0; + assert(1 >= 1); + assert(1 <= 3); + nuint offBase = 1; + byte* start = ip + 1; + if (isDxS != 0) + { + uint repIndex = (uint)(ip - @base) + 1 - offset_1; + byte* repMatch = + ( + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState + || dictMode == ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ) + && repIndex < prefixLowestIndex + ? dictBase + (repIndex - dictIndexDelta) + : @base + repIndex; + if ( + ZSTD_index_overlap_check(prefixLowestIndex, repIndex) != 0 + && MEM_read32(repMatch) == MEM_read32(ip + 1) + ) + { + byte* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; + matchLength = + ZSTD_count_2segments( + ip + 1 + 4, + repMatch + 4, + iend, + repMatchEnd, + prefixLowest + ) + 4; + if (depth == 0) + goto _storeSequence; + } + } + + if ( + dictMode == ZSTD_dictMode_e.ZSTD_noDict + && offset_1 > 0 + && MEM_read32(ip + 1 - offset_1) == MEM_read32(ip + 1) + ) + { + matchLength = ZSTD_count(ip + 1 + 4, ip + 1 + 4 - offset_1, iend) + 4; + if (depth == 0) + goto _storeSequence; + } + + { + nuint offbaseFound = 999999999; + nuint ml2 = ZSTD_searchMax( + ms, + ip, + iend, + &offbaseFound, + mls, + rowLog, + searchMethod, + dictMode + ); + if (ml2 > matchLength) + { + matchLength = ml2; + start = ip; + offBase = offbaseFound; + } + } + + if (matchLength < 4) + { + /* jump faster over incompressible sections */ + nuint step = ((nuint)(ip - anchor) >> 8) + 1; + ip += step; + ms->lazySkipping = step > 8 ? 1 : 0; + continue; + } + + if (depth >= 1) + while (ip < ilimit) + { + ip++; + if ( + dictMode == ZSTD_dictMode_e.ZSTD_noDict + && offBase != 0 + && offset_1 > 0 + && MEM_read32(ip) == MEM_read32(ip - offset_1) + ) + { + nuint mlRep = ZSTD_count(ip + 4, ip + 4 - offset_1, iend) + 4; + int gain2 = (int)(mlRep * 3); + int gain1 = (int)(matchLength * 3 - ZSTD_highbit32((uint)offBase) + 1); + if (mlRep >= 4 && gain2 > gain1) + { + matchLength = mlRep; + assert(1 >= 1); + assert(1 <= 3); + offBase = 1; + start = ip; + } + } + + if (isDxS != 0) + { + uint repIndex = (uint)(ip - @base) - offset_1; + byte* repMatch = + repIndex < prefixLowestIndex + ? dictBase + (repIndex - dictIndexDelta) + : @base + repIndex; + if ( + ZSTD_index_overlap_check(prefixLowestIndex, repIndex) != 0 + && MEM_read32(repMatch) == MEM_read32(ip) + ) + { + byte* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; + nuint mlRep = + ZSTD_count_2segments( + ip + 4, + repMatch + 4, + iend, + repMatchEnd, + prefixLowest + ) + 4; + int gain2 = (int)(mlRep * 3); + int gain1 = (int)(matchLength * 3 - ZSTD_highbit32((uint)offBase) + 1); + if (mlRep >= 4 && gain2 > gain1) + { + matchLength = mlRep; + assert(1 >= 1); + assert(1 <= 3); + offBase = 1; + start = ip; + } + } + } + + { + nuint ofbCandidate = 999999999; + nuint ml2 = ZSTD_searchMax( + ms, + ip, + iend, + &ofbCandidate, + mls, + rowLog, + searchMethod, + dictMode + ); + /* raw approx */ + int gain2 = (int)(ml2 * 4 - ZSTD_highbit32((uint)ofbCandidate)); + int gain1 = (int)(matchLength * 4 - ZSTD_highbit32((uint)offBase) + 4); + if (ml2 >= 4 && gain2 > gain1) + { + matchLength = ml2; + offBase = ofbCandidate; + start = ip; + continue; + } + } + + if (depth == 2 && ip < ilimit) + { + ip++; + if ( + dictMode == ZSTD_dictMode_e.ZSTD_noDict + && offBase != 0 + && offset_1 > 0 + && MEM_read32(ip) == MEM_read32(ip - offset_1) + ) + { + nuint mlRep = ZSTD_count(ip + 4, ip + 4 - offset_1, iend) + 4; + int gain2 = (int)(mlRep * 4); + int gain1 = (int)(matchLength * 4 - ZSTD_highbit32((uint)offBase) + 1); + if (mlRep >= 4 && gain2 > gain1) + { + matchLength = mlRep; + assert(1 >= 1); + assert(1 <= 3); + offBase = 1; + start = ip; + } + } + + if (isDxS != 0) + { + uint repIndex = (uint)(ip - @base) - offset_1; + byte* repMatch = + repIndex < prefixLowestIndex + ? dictBase + (repIndex - dictIndexDelta) + : @base + repIndex; + if ( + ZSTD_index_overlap_check(prefixLowestIndex, repIndex) != 0 + && MEM_read32(repMatch) == MEM_read32(ip) + ) + { + byte* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; + nuint mlRep = + ZSTD_count_2segments( + ip + 4, + repMatch + 4, + iend, + repMatchEnd, + prefixLowest + ) + 4; + int gain2 = (int)(mlRep * 4); + int gain1 = (int)( + matchLength * 4 - ZSTD_highbit32((uint)offBase) + 1 + ); + if (mlRep >= 4 && gain2 > gain1) + { + matchLength = mlRep; + assert(1 >= 1); + assert(1 <= 3); + offBase = 1; + start = ip; + } + } + } + + { + nuint ofbCandidate = 999999999; + nuint ml2 = ZSTD_searchMax( + ms, + ip, + iend, + &ofbCandidate, + mls, + rowLog, + searchMethod, + dictMode + ); + /* raw approx */ + int gain2 = (int)(ml2 * 4 - ZSTD_highbit32((uint)ofbCandidate)); + int gain1 = (int)(matchLength * 4 - ZSTD_highbit32((uint)offBase) + 7); + if (ml2 >= 4 && gain2 > gain1) + { + matchLength = ml2; + offBase = ofbCandidate; + start = ip; + continue; + } + } + } + + break; + } + + if (offBase > 3) + { + if (dictMode == ZSTD_dictMode_e.ZSTD_noDict) + { + assert(offBase > 3); + assert(offBase > 3); + while ( + start > anchor + && start - (offBase - 3) > prefixLowest + && start[-1] == (start - (offBase - 3))[-1] + ) + { + start--; + matchLength++; + } + } + + if (isDxS != 0) + { + assert(offBase > 3); + uint matchIndex = (uint)((nuint)(start - @base) - (offBase - 3)); + byte* match = + matchIndex < prefixLowestIndex + ? dictBase + matchIndex - dictIndexDelta + : @base + matchIndex; + byte* mStart = matchIndex < prefixLowestIndex ? dictLowest : prefixLowest; + while (start > anchor && match > mStart && start[-1] == match[-1]) + { + start--; + match--; + matchLength++; + } + } + + offset_2 = offset_1; + assert(offBase > 3); + offset_1 = (uint)(offBase - 3); + } + + _storeSequence: + { + nuint litLength = (nuint)(start - anchor); + ZSTD_storeSeq(seqStore, litLength, anchor, iend, (uint)offBase, matchLength); + anchor = ip = start + matchLength; + } + + if (ms->lazySkipping != 0) + { + if (searchMethod == searchMethod_e.search_rowHash) + { + ZSTD_row_fillHashCache(ms, @base, rowLog, mls, ms->nextToUpdate, ilimit); + } + + ms->lazySkipping = 0; + } + + if (isDxS != 0) + { + while (ip <= ilimit) + { + uint current2 = (uint)(ip - @base); + uint repIndex = current2 - offset_2; + byte* repMatch = + repIndex < prefixLowestIndex + ? dictBase - dictIndexDelta + repIndex + : @base + repIndex; + if ( + ZSTD_index_overlap_check(prefixLowestIndex, repIndex) != 0 + && MEM_read32(repMatch) == MEM_read32(ip) + ) + { + byte* repEnd2 = repIndex < prefixLowestIndex ? dictEnd : iend; + matchLength = + ZSTD_count_2segments(ip + 4, repMatch + 4, iend, repEnd2, prefixLowest) + + 4; + offBase = offset_2; + offset_2 = offset_1; + offset_1 = (uint)offBase; + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, 0, anchor, iend, 1, matchLength); + ip += matchLength; + anchor = ip; + continue; + } + + break; + } + } + + if (dictMode == ZSTD_dictMode_e.ZSTD_noDict) + { + while (ip <= ilimit && offset_2 > 0 && MEM_read32(ip) == MEM_read32(ip - offset_2)) + { + matchLength = ZSTD_count(ip + 4, ip + 4 - offset_2, iend) + 4; + offBase = offset_2; + offset_2 = offset_1; + offset_1 = (uint)offBase; + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, 0, anchor, iend, 1, matchLength); + ip += matchLength; + anchor = ip; + continue; + } + } + } + + offsetSaved2 = offsetSaved1 != 0 && offset_1 != 0 ? offsetSaved1 : offsetSaved2; + rep[0] = offset_1 != 0 ? offset_1 : offsetSaved1; + rep[1] = offset_2 != 0 ? offset_2 : offsetSaved2; + return (nuint)(iend - anchor); + } + + private static nuint ZSTD_compressBlock_greedy( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 0, + ZSTD_dictMode_e.ZSTD_noDict + ); + } + + private static nuint ZSTD_compressBlock_greedy_dictMatchState( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 0, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_compressBlock_greedy_dedicatedDictSearch( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 0, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + private static nuint ZSTD_compressBlock_greedy_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 0, + ZSTD_dictMode_e.ZSTD_noDict + ); + } + + private static nuint ZSTD_compressBlock_greedy_dictMatchState_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 0, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_compressBlock_greedy_dedicatedDictSearch_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 0, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + private static nuint ZSTD_compressBlock_lazy( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 1, + ZSTD_dictMode_e.ZSTD_noDict + ); + } + + private static nuint ZSTD_compressBlock_lazy_dictMatchState( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 1, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_compressBlock_lazy_dedicatedDictSearch( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 1, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + private static nuint ZSTD_compressBlock_lazy_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 1, + ZSTD_dictMode_e.ZSTD_noDict + ); + } + + private static nuint ZSTD_compressBlock_lazy_dictMatchState_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 1, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_compressBlock_lazy_dedicatedDictSearch_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 1, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + private static nuint ZSTD_compressBlock_lazy2( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 2, + ZSTD_dictMode_e.ZSTD_noDict + ); + } + + private static nuint ZSTD_compressBlock_lazy2_dictMatchState( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 2, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_compressBlock_lazy2_dedicatedDictSearch( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 2, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + private static nuint ZSTD_compressBlock_lazy2_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 2, + ZSTD_dictMode_e.ZSTD_noDict + ); + } + + private static nuint ZSTD_compressBlock_lazy2_dictMatchState_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 2, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_compressBlock_lazy2_dedicatedDictSearch_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 2, + ZSTD_dictMode_e.ZSTD_dedicatedDictSearch + ); + } + + private static nuint ZSTD_compressBlock_btlazy2( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_binaryTree, + 2, + ZSTD_dictMode_e.ZSTD_noDict + ); + } + + private static nuint ZSTD_compressBlock_btlazy2_dictMatchState( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_binaryTree, + 2, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_compressBlock_lazy_extDict_generic( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize, + searchMethod_e searchMethod, + uint depth + ) + { + byte* istart = (byte*)src; + byte* ip = istart; + byte* anchor = istart; + byte* iend = istart + srcSize; + byte* ilimit = searchMethod == searchMethod_e.search_rowHash ? iend - 8 - 8 : iend - 8; + byte* @base = ms->window.@base; + uint dictLimit = ms->window.dictLimit; + byte* prefixStart = @base + dictLimit; + byte* dictBase = ms->window.dictBase; + byte* dictEnd = dictBase + dictLimit; + byte* dictStart = dictBase + ms->window.lowLimit; + uint windowLog = ms->cParams.windowLog; + uint mls = + ms->cParams.minMatch <= 4 ? 4 + : ms->cParams.minMatch <= 6 ? ms->cParams.minMatch + : 6; + uint rowLog = + ms->cParams.searchLog <= 4 ? 4 + : ms->cParams.searchLog <= 6 ? ms->cParams.searchLog + : 6; + uint offset_1 = rep[0], + offset_2 = rep[1]; + ms->lazySkipping = 0; + ip += ip == prefixStart ? 1 : 0; + if (searchMethod == searchMethod_e.search_rowHash) + { + ZSTD_row_fillHashCache(ms, @base, rowLog, mls, ms->nextToUpdate, ilimit); + } + + while (ip < ilimit) + { + nuint matchLength = 0; + assert(1 >= 1); + assert(1 <= 3); + nuint offBase = 1; + byte* start = ip + 1; + uint curr = (uint)(ip - @base); + { + uint windowLow = ZSTD_getLowestMatchIndex(ms, curr + 1, windowLog); + uint repIndex = curr + 1 - offset_1; + byte* repBase = repIndex < dictLimit ? dictBase : @base; + byte* repMatch = repBase + repIndex; + if ( + ( + ZSTD_index_overlap_check(dictLimit, repIndex) + & (offset_1 <= curr + 1 - windowLow ? 1 : 0) + ) != 0 + ) + if (MEM_read32(ip + 1) == MEM_read32(repMatch)) + { + /* repcode detected we should take it */ + byte* repEnd = repIndex < dictLimit ? dictEnd : iend; + matchLength = + ZSTD_count_2segments( + ip + 1 + 4, + repMatch + 4, + iend, + repEnd, + prefixStart + ) + 4; + if (depth == 0) + goto _storeSequence; + } + } + + { + nuint ofbCandidate = 999999999; + nuint ml2 = ZSTD_searchMax( + ms, + ip, + iend, + &ofbCandidate, + mls, + rowLog, + searchMethod, + ZSTD_dictMode_e.ZSTD_extDict + ); + if (ml2 > matchLength) + { + matchLength = ml2; + start = ip; + offBase = ofbCandidate; + } + } + + if (matchLength < 4) + { + nuint step = (nuint)(ip - anchor) >> 8; + ip += step + 1; + ms->lazySkipping = step > 8 ? 1 : 0; + continue; + } + + if (depth >= 1) + while (ip < ilimit) + { + ip++; + curr++; + if (offBase != 0) + { + uint windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog); + uint repIndex = curr - offset_1; + byte* repBase = repIndex < dictLimit ? dictBase : @base; + byte* repMatch = repBase + repIndex; + if ( + ( + ZSTD_index_overlap_check(dictLimit, repIndex) + & (offset_1 <= curr - windowLow ? 1 : 0) + ) != 0 + ) + if (MEM_read32(ip) == MEM_read32(repMatch)) + { + /* repcode detected */ + byte* repEnd = repIndex < dictLimit ? dictEnd : iend; + nuint repLength = + ZSTD_count_2segments( + ip + 4, + repMatch + 4, + iend, + repEnd, + prefixStart + ) + 4; + int gain2 = (int)(repLength * 3); + int gain1 = (int)( + matchLength * 3 - ZSTD_highbit32((uint)offBase) + 1 + ); + if (repLength >= 4 && gain2 > gain1) + { + matchLength = repLength; + assert(1 >= 1); + assert(1 <= 3); + offBase = 1; + start = ip; + } + } + } + + { + nuint ofbCandidate = 999999999; + nuint ml2 = ZSTD_searchMax( + ms, + ip, + iend, + &ofbCandidate, + mls, + rowLog, + searchMethod, + ZSTD_dictMode_e.ZSTD_extDict + ); + /* raw approx */ + int gain2 = (int)(ml2 * 4 - ZSTD_highbit32((uint)ofbCandidate)); + int gain1 = (int)(matchLength * 4 - ZSTD_highbit32((uint)offBase) + 4); + if (ml2 >= 4 && gain2 > gain1) + { + matchLength = ml2; + offBase = ofbCandidate; + start = ip; + continue; + } + } + + if (depth == 2 && ip < ilimit) + { + ip++; + curr++; + if (offBase != 0) + { + uint windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog); + uint repIndex = curr - offset_1; + byte* repBase = repIndex < dictLimit ? dictBase : @base; + byte* repMatch = repBase + repIndex; + if ( + ( + ZSTD_index_overlap_check(dictLimit, repIndex) + & (offset_1 <= curr - windowLow ? 1 : 0) + ) != 0 + ) + if (MEM_read32(ip) == MEM_read32(repMatch)) + { + /* repcode detected */ + byte* repEnd = repIndex < dictLimit ? dictEnd : iend; + nuint repLength = + ZSTD_count_2segments( + ip + 4, + repMatch + 4, + iend, + repEnd, + prefixStart + ) + 4; + int gain2 = (int)(repLength * 4); + int gain1 = (int)( + matchLength * 4 - ZSTD_highbit32((uint)offBase) + 1 + ); + if (repLength >= 4 && gain2 > gain1) + { + matchLength = repLength; + assert(1 >= 1); + assert(1 <= 3); + offBase = 1; + start = ip; + } + } + } + + { + nuint ofbCandidate = 999999999; + nuint ml2 = ZSTD_searchMax( + ms, + ip, + iend, + &ofbCandidate, + mls, + rowLog, + searchMethod, + ZSTD_dictMode_e.ZSTD_extDict + ); + /* raw approx */ + int gain2 = (int)(ml2 * 4 - ZSTD_highbit32((uint)ofbCandidate)); + int gain1 = (int)(matchLength * 4 - ZSTD_highbit32((uint)offBase) + 7); + if (ml2 >= 4 && gain2 > gain1) + { + matchLength = ml2; + offBase = ofbCandidate; + start = ip; + continue; + } + } + } + + break; + } + + if (offBase > 3) + { + assert(offBase > 3); + uint matchIndex = (uint)((nuint)(start - @base) - (offBase - 3)); + byte* match = matchIndex < dictLimit ? dictBase + matchIndex : @base + matchIndex; + byte* mStart = matchIndex < dictLimit ? dictStart : prefixStart; + while (start > anchor && match > mStart && start[-1] == match[-1]) + { + start--; + match--; + matchLength++; + } + + offset_2 = offset_1; + assert(offBase > 3); + offset_1 = (uint)(offBase - 3); + } + + _storeSequence: + { + nuint litLength = (nuint)(start - anchor); + ZSTD_storeSeq(seqStore, litLength, anchor, iend, (uint)offBase, matchLength); + anchor = ip = start + matchLength; + } + + if (ms->lazySkipping != 0) + { + if (searchMethod == searchMethod_e.search_rowHash) + { + ZSTD_row_fillHashCache(ms, @base, rowLog, mls, ms->nextToUpdate, ilimit); + } + + ms->lazySkipping = 0; + } + + while (ip <= ilimit) + { + uint repCurrent = (uint)(ip - @base); + uint windowLow = ZSTD_getLowestMatchIndex(ms, repCurrent, windowLog); + uint repIndex = repCurrent - offset_2; + byte* repBase = repIndex < dictLimit ? dictBase : @base; + byte* repMatch = repBase + repIndex; + if ( + ( + ZSTD_index_overlap_check(dictLimit, repIndex) + & (offset_2 <= repCurrent - windowLow ? 1 : 0) + ) != 0 + ) + if (MEM_read32(ip) == MEM_read32(repMatch)) + { + /* repcode detected we should take it */ + byte* repEnd = repIndex < dictLimit ? dictEnd : iend; + matchLength = + ZSTD_count_2segments(ip + 4, repMatch + 4, iend, repEnd, prefixStart) + + 4; + offBase = offset_2; + offset_2 = offset_1; + offset_1 = (uint)offBase; + assert(1 >= 1); + assert(1 <= 3); + ZSTD_storeSeq(seqStore, 0, anchor, iend, 1, matchLength); + ip += matchLength; + anchor = ip; + continue; + } + + break; + } + } + + rep[0] = offset_1; + rep[1] = offset_2; + return (nuint)(iend - anchor); + } + + private static nuint ZSTD_compressBlock_greedy_extDict( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_extDict_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 0 + ); + } + + private static nuint ZSTD_compressBlock_greedy_extDict_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_extDict_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 0 + ); + } + + private static nuint ZSTD_compressBlock_lazy_extDict( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_extDict_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 1 + ); + } + + private static nuint ZSTD_compressBlock_lazy_extDict_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_extDict_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 1 + ); + } + + private static nuint ZSTD_compressBlock_lazy2_extDict( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_extDict_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_hashChain, + 2 + ); + } + + private static nuint ZSTD_compressBlock_lazy2_extDict_row( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_extDict_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_rowHash, + 2 + ); + } + + private static nuint ZSTD_compressBlock_btlazy2_extDict( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_lazy_extDict_generic( + ms, + seqStore, + rep, + src, + srcSize, + searchMethod_e.search_binaryTree, + 2 + ); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLdm.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLdm.cs new file mode 100644 index 00000000..eb634979 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLdm.cs @@ -0,0 +1,940 @@ +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /** ZSTD_ldm_gear_init(): + * + * Initializes the rolling hash state such that it will honor the + * settings in params. */ + private static void ZSTD_ldm_gear_init(ldmRollingHashState_t* state, ldmParams_t* @params) + { + uint maxBitsInMask = @params->minMatchLength < 64 ? @params->minMatchLength : 64; + uint hashRateLog = @params->hashRateLog; + state->rolling = ~(uint)0; + if (hashRateLog > 0 && hashRateLog <= maxBitsInMask) + { + state->stopMask = + ((ulong)1 << (int)hashRateLog) - 1 << (int)(maxBitsInMask - hashRateLog); + } + else + { + state->stopMask = ((ulong)1 << (int)hashRateLog) - 1; + } + } + + /** ZSTD_ldm_gear_reset() + * Feeds [data, data + minMatchLength) into the hash without registering any + * splits. This effectively resets the hash state. This is used when skipping + * over data, either at the beginning of a block, or skipping sections. + */ + private static void ZSTD_ldm_gear_reset( + ldmRollingHashState_t* state, + byte* data, + nuint minMatchLength + ) + { + ulong hash = state->rolling; + nuint n = 0; + while (n + 3 < minMatchLength) + { + { + hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; + n += 1; + } + + { + hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; + n += 1; + } + + { + hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; + n += 1; + } + + { + hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; + n += 1; + } + } + + while (n < minMatchLength) + { + hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; + n += 1; + } + } + + /** ZSTD_ldm_gear_feed(): + * + * Registers in the splits array all the split points found in the first + * size bytes following the data pointer. This function terminates when + * either all the data has been processed or LDM_BATCH_SIZE splits are + * present in the splits array. + * + * Precondition: The splits array must not be full. + * Returns: The number of bytes processed. */ + private static nuint ZSTD_ldm_gear_feed( + ldmRollingHashState_t* state, + byte* data, + nuint size, + nuint* splits, + uint* numSplits + ) + { + nuint n; + ulong hash, + mask; + hash = state->rolling; + mask = state->stopMask; + n = 0; + while (n + 3 < size) + { + { + hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; + n += 1; + if ((hash & mask) == 0) + { + splits[*numSplits] = n; + *numSplits += 1; + if (*numSplits == 64) + goto done; + } + } + + { + hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; + n += 1; + if ((hash & mask) == 0) + { + splits[*numSplits] = n; + *numSplits += 1; + if (*numSplits == 64) + goto done; + } + } + + { + hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; + n += 1; + if ((hash & mask) == 0) + { + splits[*numSplits] = n; + *numSplits += 1; + if (*numSplits == 64) + goto done; + } + } + + { + hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; + n += 1; + if ((hash & mask) == 0) + { + splits[*numSplits] = n; + *numSplits += 1; + if (*numSplits == 64) + goto done; + } + } + } + + while (n < size) + { + hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; + n += 1; + if ((hash & mask) == 0) + { + splits[*numSplits] = n; + *numSplits += 1; + if (*numSplits == 64) + goto done; + } + } + + done: + state->rolling = hash; + return n; + } + + /** ZSTD_ldm_adjustParameters() : + * If the params->hashRateLog is not set, set it to its default value based on + * windowLog and params->hashLog. + * + * Ensures that params->bucketSizeLog is <= params->hashLog (setting it to + * params->hashLog if it is not). + * + * Ensures that the minMatchLength >= targetLength during optimal parsing. + */ + private static void ZSTD_ldm_adjustParameters( + ldmParams_t* @params, + ZSTD_compressionParameters* cParams + ) + { + @params->windowLog = cParams->windowLog; + if (@params->hashRateLog == 0) + { + if (@params->hashLog > 0) + { + assert( + @params->hashLog + <= (uint)( + (sizeof(nuint) == 4 ? 30 : 31) < 30 + ? sizeof(nuint) == 4 + ? 30 + : 31 + : 30 + ) + ); + if (@params->windowLog > @params->hashLog) + { + @params->hashRateLog = @params->windowLog - @params->hashLog; + } + } + else + { + assert(1 <= (int)cParams->strategy && (int)cParams->strategy <= 9); + @params->hashRateLog = (uint)(7 - (int)cParams->strategy / 3); + } + } + + if (@params->hashLog == 0) + { + @params->hashLog = + @params->windowLog - @params->hashRateLog <= 6 ? 6 + : @params->windowLog - @params->hashRateLog + <= (uint)( + (sizeof(nuint) == 4 ? 30 : 31) < 30 + ? sizeof(nuint) == 4 + ? 30 + : 31 + : 30 + ) + ? @params->windowLog - @params->hashRateLog + : (uint)( + (sizeof(nuint) == 4 ? 30 : 31) < 30 + ? sizeof(nuint) == 4 + ? 30 + : 31 + : 30 + ); + } + + if (@params->minMatchLength == 0) + { + @params->minMatchLength = 64; + if (cParams->strategy >= ZSTD_strategy.ZSTD_btultra) + @params->minMatchLength /= 2; + } + + if (@params->bucketSizeLog == 0) + { + assert(1 <= (int)cParams->strategy && (int)cParams->strategy <= 9); + @params->bucketSizeLog = + (uint)cParams->strategy <= 4 ? 4 + : (uint)cParams->strategy <= 8 ? (uint)cParams->strategy + : 8; + } + + @params->bucketSizeLog = + @params->bucketSizeLog < @params->hashLog ? @params->bucketSizeLog : @params->hashLog; + } + + /** ZSTD_ldm_getTableSize() : + * Estimate the space needed for long distance matching tables or 0 if LDM is + * disabled. + */ + private static nuint ZSTD_ldm_getTableSize(ldmParams_t @params) + { + nuint ldmHSize = (nuint)1 << (int)@params.hashLog; + nuint ldmBucketSizeLog = + @params.bucketSizeLog < @params.hashLog ? @params.bucketSizeLog : @params.hashLog; + nuint ldmBucketSize = (nuint)1 << (int)(@params.hashLog - ldmBucketSizeLog); + nuint totalSize = + ZSTD_cwksp_alloc_size(ldmBucketSize) + + ZSTD_cwksp_alloc_size(ldmHSize * (nuint)sizeof(ldmEntry_t)); + return @params.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable ? totalSize : 0; + } + + /** ZSTD_ldm_getSeqSpace() : + * Return an upper bound on the number of sequences that can be produced by + * the long distance matcher, or 0 if LDM is disabled. + */ + private static nuint ZSTD_ldm_getMaxNbSeq(ldmParams_t @params, nuint maxChunkSize) + { + return @params.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable + ? maxChunkSize / @params.minMatchLength + : 0; + } + + /** ZSTD_ldm_getBucket() : + * Returns a pointer to the start of the bucket associated with hash. */ + private static ldmEntry_t* ZSTD_ldm_getBucket( + ldmState_t* ldmState, + nuint hash, + uint bucketSizeLog + ) + { + return ldmState->hashTable + (hash << (int)bucketSizeLog); + } + + /** ZSTD_ldm_insertEntry() : + * Insert the entry with corresponding hash into the hash table */ + private static void ZSTD_ldm_insertEntry( + ldmState_t* ldmState, + nuint hash, + ldmEntry_t entry, + uint bucketSizeLog + ) + { + byte* pOffset = ldmState->bucketOffsets + hash; + uint offset = *pOffset; + *(ZSTD_ldm_getBucket(ldmState, hash, bucketSizeLog) + offset) = entry; + *pOffset = (byte)(offset + 1 & (1U << (int)bucketSizeLog) - 1); + } + + /** ZSTD_ldm_countBackwardsMatch() : + * Returns the number of bytes that match backwards before pIn and pMatch. + * + * We count only bytes where pMatch >= pBase and pIn >= pAnchor. */ + private static nuint ZSTD_ldm_countBackwardsMatch( + byte* pIn, + byte* pAnchor, + byte* pMatch, + byte* pMatchBase + ) + { + nuint matchLength = 0; + while (pIn > pAnchor && pMatch > pMatchBase && pIn[-1] == pMatch[-1]) + { + pIn--; + pMatch--; + matchLength++; + } + + return matchLength; + } + + /** ZSTD_ldm_countBackwardsMatch_2segments() : + * Returns the number of bytes that match backwards from pMatch, + * even with the backwards match spanning 2 different segments. + * + * On reaching `pMatchBase`, start counting from mEnd */ + private static nuint ZSTD_ldm_countBackwardsMatch_2segments( + byte* pIn, + byte* pAnchor, + byte* pMatch, + byte* pMatchBase, + byte* pExtDictStart, + byte* pExtDictEnd + ) + { + nuint matchLength = ZSTD_ldm_countBackwardsMatch(pIn, pAnchor, pMatch, pMatchBase); + if (pMatch - matchLength != pMatchBase || pMatchBase == pExtDictStart) + { + return matchLength; + } + + matchLength += ZSTD_ldm_countBackwardsMatch( + pIn - matchLength, + pAnchor, + pExtDictEnd, + pExtDictStart + ); + return matchLength; + } + + /** ZSTD_ldm_fillFastTables() : + * + * Fills the relevant tables for the ZSTD_fast and ZSTD_dfast strategies. + * This is similar to ZSTD_loadDictionaryContent. + * + * The tables for the other strategies are filled within their + * block compressors. */ + private static nuint ZSTD_ldm_fillFastTables(ZSTD_MatchState_t* ms, void* end) + { + byte* iend = (byte*)end; + switch (ms->cParams.strategy) + { + case ZSTD_strategy.ZSTD_fast: + ZSTD_fillHashTable( + ms, + iend, + ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast, + ZSTD_tableFillPurpose_e.ZSTD_tfp_forCCtx + ); + break; + case ZSTD_strategy.ZSTD_dfast: + ZSTD_fillDoubleHashTable( + ms, + iend, + ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast, + ZSTD_tableFillPurpose_e.ZSTD_tfp_forCCtx + ); + break; + case ZSTD_strategy.ZSTD_greedy: + case ZSTD_strategy.ZSTD_lazy: + case ZSTD_strategy.ZSTD_lazy2: + case ZSTD_strategy.ZSTD_btlazy2: + case ZSTD_strategy.ZSTD_btopt: + case ZSTD_strategy.ZSTD_btultra: + case ZSTD_strategy.ZSTD_btultra2: + break; + default: + assert(0 != 0); + break; + } + + return 0; + } + + private static void ZSTD_ldm_fillHashTable( + ldmState_t* ldmState, + byte* ip, + byte* iend, + ldmParams_t* @params + ) + { + uint minMatchLength = @params->minMatchLength; + uint bucketSizeLog = @params->bucketSizeLog; + uint hBits = @params->hashLog - bucketSizeLog; + byte* @base = ldmState->window.@base; + byte* istart = ip; + ldmRollingHashState_t hashState; + nuint* splits = &ldmState->splitIndices.e0; + uint numSplits; + ZSTD_ldm_gear_init(&hashState, @params); + while (ip < iend) + { + nuint hashed; + uint n; + numSplits = 0; + hashed = ZSTD_ldm_gear_feed(&hashState, ip, (nuint)(iend - ip), splits, &numSplits); + for (n = 0; n < numSplits; n++) + { + if (ip + splits[n] >= istart + minMatchLength) + { + byte* split = ip + splits[n] - minMatchLength; + ulong xxhash = ZSTD_XXH64(split, minMatchLength, 0); + uint hash = (uint)(xxhash & ((uint)1 << (int)hBits) - 1); + ldmEntry_t entry; + entry.offset = (uint)(split - @base); + entry.checksum = (uint)(xxhash >> 32); + ZSTD_ldm_insertEntry(ldmState, hash, entry, @params->bucketSizeLog); + } + } + + ip += hashed; + } + } + + /** ZSTD_ldm_limitTableUpdate() : + * + * Sets cctx->nextToUpdate to a position corresponding closer to anchor + * if it is far way + * (after a long match, only update tables a limited amount). */ + private static void ZSTD_ldm_limitTableUpdate(ZSTD_MatchState_t* ms, byte* anchor) + { + uint curr = (uint)(anchor - ms->window.@base); + if (curr > ms->nextToUpdate + 1024) + { + ms->nextToUpdate = + curr + - (512 < curr - ms->nextToUpdate - 1024 ? 512 : curr - ms->nextToUpdate - 1024); + } + } + + private static nuint ZSTD_ldm_generateSequences_internal( + ldmState_t* ldmState, + RawSeqStore_t* rawSeqStore, + ldmParams_t* @params, + void* src, + nuint srcSize + ) + { + /* LDM parameters */ + int extDict = (int)ZSTD_window_hasExtDict(ldmState->window); + uint minMatchLength = @params->minMatchLength; + uint entsPerBucket = 1U << (int)@params->bucketSizeLog; + uint hBits = @params->hashLog - @params->bucketSizeLog; + /* Prefix and extDict parameters */ + uint dictLimit = ldmState->window.dictLimit; + uint lowestIndex = extDict != 0 ? ldmState->window.lowLimit : dictLimit; + byte* @base = ldmState->window.@base; + byte* dictBase = extDict != 0 ? ldmState->window.dictBase : null; + byte* dictStart = extDict != 0 ? dictBase + lowestIndex : null; + byte* dictEnd = extDict != 0 ? dictBase + dictLimit : null; + byte* lowPrefixPtr = @base + dictLimit; + /* Input bounds */ + byte* istart = (byte*)src; + byte* iend = istart + srcSize; + byte* ilimit = iend - 8; + /* Input positions */ + byte* anchor = istart; + byte* ip = istart; + /* Rolling hash state */ + ldmRollingHashState_t hashState; + /* Arrays for staged-processing */ + nuint* splits = &ldmState->splitIndices.e0; + ldmMatchCandidate_t* candidates = &ldmState->matchCandidates.e0; + uint numSplits; + if (srcSize < minMatchLength) + return (nuint)(iend - anchor); + ZSTD_ldm_gear_init(&hashState, @params); + ZSTD_ldm_gear_reset(&hashState, ip, minMatchLength); + ip += minMatchLength; + while (ip < ilimit) + { + nuint hashed; + uint n; + numSplits = 0; + hashed = ZSTD_ldm_gear_feed(&hashState, ip, (nuint)(ilimit - ip), splits, &numSplits); + for (n = 0; n < numSplits; n++) + { + byte* split = ip + splits[n] - minMatchLength; + ulong xxhash = ZSTD_XXH64(split, minMatchLength, 0); + uint hash = (uint)(xxhash & ((uint)1 << (int)hBits) - 1); + candidates[n].split = split; + candidates[n].hash = hash; + candidates[n].checksum = (uint)(xxhash >> 32); + candidates[n].bucket = ZSTD_ldm_getBucket(ldmState, hash, @params->bucketSizeLog); +#if NETCOREAPP3_0_OR_GREATER + if (System.Runtime.Intrinsics.X86.Sse.IsSupported) + { + System.Runtime.Intrinsics.X86.Sse.Prefetch0(candidates[n].bucket); + } +#endif + } + + for (n = 0; n < numSplits; n++) + { + nuint forwardMatchLength = 0, + backwardMatchLength = 0, + bestMatchLength = 0, + mLength; + uint offset; + byte* split = candidates[n].split; + uint checksum = candidates[n].checksum; + uint hash = candidates[n].hash; + ldmEntry_t* bucket = candidates[n].bucket; + ldmEntry_t* cur; + ldmEntry_t* bestEntry = null; + ldmEntry_t newEntry; + newEntry.offset = (uint)(split - @base); + newEntry.checksum = checksum; + if (split < anchor) + { + ZSTD_ldm_insertEntry(ldmState, hash, newEntry, @params->bucketSizeLog); + continue; + } + + for (cur = bucket; cur < bucket + entsPerBucket; cur++) + { + nuint curForwardMatchLength, + curBackwardMatchLength, + curTotalMatchLength; + if (cur->checksum != checksum || cur->offset <= lowestIndex) + { + continue; + } + + if (extDict != 0) + { + byte* curMatchBase = cur->offset < dictLimit ? dictBase : @base; + byte* pMatch = curMatchBase + cur->offset; + byte* matchEnd = cur->offset < dictLimit ? dictEnd : iend; + byte* lowMatchPtr = cur->offset < dictLimit ? dictStart : lowPrefixPtr; + curForwardMatchLength = ZSTD_count_2segments( + split, + pMatch, + iend, + matchEnd, + lowPrefixPtr + ); + if (curForwardMatchLength < minMatchLength) + { + continue; + } + + curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch_2segments( + split, + anchor, + pMatch, + lowMatchPtr, + dictStart, + dictEnd + ); + } + else + { + byte* pMatch = @base + cur->offset; + curForwardMatchLength = ZSTD_count(split, pMatch, iend); + if (curForwardMatchLength < minMatchLength) + { + continue; + } + + curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch( + split, + anchor, + pMatch, + lowPrefixPtr + ); + } + + curTotalMatchLength = curForwardMatchLength + curBackwardMatchLength; + if (curTotalMatchLength > bestMatchLength) + { + bestMatchLength = curTotalMatchLength; + forwardMatchLength = curForwardMatchLength; + backwardMatchLength = curBackwardMatchLength; + bestEntry = cur; + } + } + + if (bestEntry == null) + { + ZSTD_ldm_insertEntry(ldmState, hash, newEntry, @params->bucketSizeLog); + continue; + } + + offset = (uint)(split - @base) - bestEntry->offset; + mLength = forwardMatchLength + backwardMatchLength; + { + rawSeq* seq = rawSeqStore->seq + rawSeqStore->size; + if (rawSeqStore->size == rawSeqStore->capacity) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_dstSize_tooSmall)); + seq->litLength = (uint)(split - backwardMatchLength - anchor); + seq->matchLength = (uint)mLength; + seq->offset = offset; + rawSeqStore->size++; + } + + ZSTD_ldm_insertEntry(ldmState, hash, newEntry, @params->bucketSizeLog); + anchor = split + forwardMatchLength; + if (anchor > ip + hashed) + { + ZSTD_ldm_gear_reset(&hashState, anchor - minMatchLength, minMatchLength); + ip = anchor - hashed; + break; + } + } + + ip += hashed; + } + + return (nuint)(iend - anchor); + } + + /*! ZSTD_ldm_reduceTable() : + * reduce table indexes by `reducerValue` */ + private static void ZSTD_ldm_reduceTable(ldmEntry_t* table, uint size, uint reducerValue) + { + uint u; + for (u = 0; u < size; u++) + { + if (table[u].offset < reducerValue) + table[u].offset = 0; + else + table[u].offset -= reducerValue; + } + } + + /** + * ZSTD_ldm_generateSequences(): + * + * Generates the sequences using the long distance match finder. + * Generates long range matching sequences in `sequences`, which parse a prefix + * of the source. `sequences` must be large enough to store every sequence, + * which can be checked with `ZSTD_ldm_getMaxNbSeq()`. + * @returns 0 or an error code. + * + * NOTE: The user must have called ZSTD_window_update() for all of the input + * they have, even if they pass it to ZSTD_ldm_generateSequences() in chunks. + * NOTE: This function returns an error if it runs out of space to store + * sequences. + */ + private static nuint ZSTD_ldm_generateSequences( + ldmState_t* ldmState, + RawSeqStore_t* sequences, + ldmParams_t* @params, + void* src, + nuint srcSize + ) + { + uint maxDist = 1U << (int)@params->windowLog; + byte* istart = (byte*)src; + byte* iend = istart + srcSize; + const nuint kMaxChunkSize = 1 << 20; + nuint nbChunks = srcSize / kMaxChunkSize + (nuint)(srcSize % kMaxChunkSize != 0 ? 1 : 0); + nuint chunk; + nuint leftoverSize = 0; + assert( + unchecked((uint)-1) - (MEM_64bits ? 3500U * (1 << 20) : 2000U * (1 << 20)) + >= kMaxChunkSize + ); + assert(ldmState->window.nextSrc >= (byte*)src + srcSize); + assert(sequences->pos <= sequences->size); + assert(sequences->size <= sequences->capacity); + for (chunk = 0; chunk < nbChunks && sequences->size < sequences->capacity; ++chunk) + { + byte* chunkStart = istart + chunk * kMaxChunkSize; + nuint remaining = (nuint)(iend - chunkStart); + byte* chunkEnd = remaining < kMaxChunkSize ? iend : chunkStart + kMaxChunkSize; + nuint chunkSize = (nuint)(chunkEnd - chunkStart); + nuint newLeftoverSize; + nuint prevSize = sequences->size; + assert(chunkStart < iend); + if ( + ZSTD_window_needOverflowCorrection( + ldmState->window, + 0, + maxDist, + ldmState->loadedDictEnd, + chunkStart, + chunkEnd + ) != 0 + ) + { + uint ldmHSize = 1U << (int)@params->hashLog; + uint correction = ZSTD_window_correctOverflow( + &ldmState->window, + 0, + maxDist, + chunkStart + ); + ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction); + ldmState->loadedDictEnd = 0; + } + + ZSTD_window_enforceMaxDist( + &ldmState->window, + chunkEnd, + maxDist, + &ldmState->loadedDictEnd, + null + ); + newLeftoverSize = ZSTD_ldm_generateSequences_internal( + ldmState, + sequences, + @params, + chunkStart, + chunkSize + ); + if (ERR_isError(newLeftoverSize)) + return newLeftoverSize; + if (prevSize < sequences->size) + { + sequences->seq[prevSize].litLength += (uint)leftoverSize; + leftoverSize = newLeftoverSize; + } + else + { + assert(newLeftoverSize == chunkSize); + leftoverSize += chunkSize; + } + } + + return 0; + } + + /** + * ZSTD_ldm_skipSequences(): + * + * Skip past `srcSize` bytes worth of sequences in `rawSeqStore`. + * Avoids emitting matches less than `minMatch` bytes. + * Must be called for data that is not passed to ZSTD_ldm_blockCompress(). + */ + private static void ZSTD_ldm_skipSequences( + RawSeqStore_t* rawSeqStore, + nuint srcSize, + uint minMatch + ) + { + while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) + { + rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos; + if (srcSize <= seq->litLength) + { + seq->litLength -= (uint)srcSize; + return; + } + + srcSize -= seq->litLength; + seq->litLength = 0; + if (srcSize < seq->matchLength) + { + seq->matchLength -= (uint)srcSize; + if (seq->matchLength < minMatch) + { + if (rawSeqStore->pos + 1 < rawSeqStore->size) + { + seq[1].litLength += seq[0].matchLength; + } + + rawSeqStore->pos++; + } + + return; + } + + srcSize -= seq->matchLength; + seq->matchLength = 0; + rawSeqStore->pos++; + } + } + + /** + * If the sequence length is longer than remaining then the sequence is split + * between this block and the next. + * + * Returns the current sequence to handle, or if the rest of the block should + * be literals, it returns a sequence with offset == 0. + */ + private static rawSeq maybeSplitSequence( + RawSeqStore_t* rawSeqStore, + uint remaining, + uint minMatch + ) + { + rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos]; + assert(sequence.offset > 0); + if (remaining >= sequence.litLength + sequence.matchLength) + { + rawSeqStore->pos++; + return sequence; + } + + if (remaining <= sequence.litLength) + { + sequence.offset = 0; + } + else if (remaining < sequence.litLength + sequence.matchLength) + { + sequence.matchLength = remaining - sequence.litLength; + if (sequence.matchLength < minMatch) + { + sequence.offset = 0; + } + } + + ZSTD_ldm_skipSequences(rawSeqStore, remaining, minMatch); + return sequence; + } + + /* ZSTD_ldm_skipRawSeqStoreBytes(): + * Moves forward in rawSeqStore by nbBytes, updating fields 'pos' and 'posInSequence'. + * Not to be used in conjunction with ZSTD_ldm_skipSequences(). + * Must be called for data with is not passed to ZSTD_ldm_blockCompress(). + */ + private static void ZSTD_ldm_skipRawSeqStoreBytes(RawSeqStore_t* rawSeqStore, nuint nbBytes) + { + uint currPos = (uint)(rawSeqStore->posInSequence + nbBytes); + while (currPos != 0 && rawSeqStore->pos < rawSeqStore->size) + { + rawSeq currSeq = rawSeqStore->seq[rawSeqStore->pos]; + if (currPos >= currSeq.litLength + currSeq.matchLength) + { + currPos -= currSeq.litLength + currSeq.matchLength; + rawSeqStore->pos++; + } + else + { + rawSeqStore->posInSequence = currPos; + break; + } + } + + if (currPos == 0 || rawSeqStore->pos == rawSeqStore->size) + { + rawSeqStore->posInSequence = 0; + } + } + + /** + * ZSTD_ldm_blockCompress(): + * + * Compresses a block using the predefined sequences, along with a secondary + * block compressor. The literals section of every sequence is passed to the + * secondary block compressor, and those sequences are interspersed with the + * predefined sequences. Returns the length of the last literals. + * Updates `rawSeqStore.pos` to indicate how many sequences have been consumed. + * `rawSeqStore.seq` may also be updated to split the last sequence between two + * blocks. + * @return The length of the last literals. + * + * NOTE: The source must be at most the maximum block size, but the predefined + * sequences can be any size, and may be longer than the block. In the case that + * they are longer than the block, the last sequences may need to be split into + * two. We handle that case correctly, and update `rawSeqStore` appropriately. + * NOTE: This function does not return any errors. + */ + private static nuint ZSTD_ldm_blockCompress( + RawSeqStore_t* rawSeqStore, + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + ZSTD_paramSwitch_e useRowMatchFinder, + void* src, + nuint srcSize + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint minMatch = cParams->minMatch; + ZSTD_BlockCompressor_f blockCompressor = ZSTD_selectBlockCompressor( + cParams->strategy, + useRowMatchFinder, + ZSTD_matchState_dictMode(ms) + ); + /* Input bounds */ + byte* istart = (byte*)src; + byte* iend = istart + srcSize; + /* Input positions */ + byte* ip = istart; + if (cParams->strategy >= ZSTD_strategy.ZSTD_btopt) + { + nuint lastLLSize; + ms->ldmSeqStore = rawSeqStore; + lastLLSize = blockCompressor(ms, seqStore, rep, src, srcSize); + ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore, srcSize); + return lastLLSize; + } + + assert(rawSeqStore->pos <= rawSeqStore->size); + assert(rawSeqStore->size <= rawSeqStore->capacity); + while (rawSeqStore->pos < rawSeqStore->size && ip < iend) + { + /* maybeSplitSequence updates rawSeqStore->pos */ + rawSeq sequence = maybeSplitSequence(rawSeqStore, (uint)(iend - ip), minMatch); + if (sequence.offset == 0) + break; + assert(ip + sequence.litLength + sequence.matchLength <= iend); + ZSTD_ldm_limitTableUpdate(ms, ip); + ZSTD_ldm_fillFastTables(ms, ip); + { + int i; + nuint newLitLength = blockCompressor(ms, seqStore, rep, ip, sequence.litLength); + ip += sequence.litLength; + for (i = 3 - 1; i > 0; i--) + rep[i] = rep[i - 1]; + rep[0] = sequence.offset; + assert(sequence.offset > 0); + ZSTD_storeSeq( + seqStore, + newLitLength, + ip - newLitLength, + iend, + sequence.offset + 3, + sequence.matchLength + ); + ip += sequence.matchLength; + } + } + + ZSTD_ldm_limitTableUpdate(ms, ip); + ZSTD_ldm_fillFastTables(ms, ip); + return blockCompressor(ms, seqStore, rep, ip, (nuint)(iend - ip)); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLdmGeartab.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLdmGeartab.cs new file mode 100644 index 00000000..5edf1197 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdLdmGeartab.cs @@ -0,0 +1,539 @@ +using System; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_ZSTD_ldm_gearTab => + new ulong[256] + { + 0xf5b8f72c5f77775c, + 0x84935f266b7ac412, + 0xb647ada9ca730ccc, + 0xb065bb4b114fb1de, + 0x34584e7e8c3a9fd0, + 0x4e97e17c6ae26b05, + 0x3a03d743bc99a604, + 0xcecd042422c4044f, + 0x76de76c58524259e, + 0x9c8528f65badeaca, + 0x86563706e2097529, + 0x2902475fa375d889, + 0xafb32a9739a5ebe6, + 0xce2714da3883e639, + 0x21eaf821722e69e, + 0x37b628620b628, + 0x49a8d455d88caf5, + 0x8556d711e6958140, + 0x4f7ae74fc605c1f, + 0x829f0c3468bd3a20, + 0x4ffdc885c625179e, + 0x8473de048a3daf1b, + 0x51008822b05646b2, + 0x69d75d12b2d1cc5f, + 0x8c9d4a19159154bc, + 0xc3cc10f4abbd4003, + 0xd06ddc1cecb97391, + 0xbe48e6e7ed80302e, + 0x3481db31cee03547, + 0xacc3f67cdaa1d210, + 0x65cb771d8c7f96cc, + 0x8eb27177055723dd, + 0xc789950d44cd94be, + 0x934feadc3700b12b, + 0x5e485f11edbdf182, + 0x1e2e2a46fd64767a, + 0x2969ca71d82efa7c, + 0x9d46e9935ebbba2e, + 0xe056b67e05e6822b, + 0x94d73f55739d03a0, + 0xcd7010bdb69b5a03, + 0x455ef9fcd79b82f4, + 0x869cb54a8749c161, + 0x38d1a4fa6185d225, + 0xb475166f94bbe9bb, + 0xa4143548720959f1, + 0x7aed4780ba6b26ba, + 0xd0ce264439e02312, + 0x84366d746078d508, + 0xa8ce973c72ed17be, + 0x21c323a29a430b01, + 0x9962d617e3af80ee, + 0xab0ce91d9c8cf75b, + 0x530e8ee6d19a4dbc, + 0x2ef68c0cf53f5d72, + 0xc03a681640a85506, + 0x496e4e9f9c310967, + 0x78580472b59b14a0, + 0x273824c23b388577, + 0x66bf923ad45cb553, + 0x47ae1a5a2492ba86, + 0x35e304569e229659, + 0x4765182a46870b6f, + 0x6cbab625e9099412, + 0xddac9a2e598522c1, + 0x7172086e666624f2, + 0xdf5003ca503b7837, + 0x88c0c1db78563d09, + 0x58d51865acfc289d, + 0x177671aec65224f1, + 0xfb79d8a241e967d7, + 0x2be1e101cad9a49a, + 0x6625682f6e29186b, + 0x399553457ac06e50, + 0x35dffb4c23abb74, + 0x429db2591f54aade, + 0xc52802a8037d1009, + 0x6acb27381f0b25f3, + 0xf45e2551ee4f823b, + 0x8b0ea2d99580c2f7, + 0x3bed519cbcb4e1e1, + 0xff452823dbb010a, + 0x9d42ed614f3dd267, + 0x5b9313c06257c57b, + 0xa114b8008b5e1442, + 0xc1fe311c11c13d4b, + 0x66e8763ea34c5568, + 0x8b982af1c262f05d, + 0xee8876faaa75fbb7, + 0x8a62a4d0d172bb2a, + 0xc13d94a3b7449a97, + 0x6dbbba9dc15d037c, + 0xc786101f1d92e0f1, + 0xd78681a907a0b79b, + 0xf61aaf2962c9abb9, + 0x2cfd16fcd3cb7ad9, + 0x868c5b6744624d21, + 0x25e650899c74ddd7, + 0xba042af4a7c37463, + 0x4eb1a539465a3eca, + 0xbe09dbf03b05d5ca, + 0x774e5a362b5472ba, + 0x47a1221229d183cd, + 0x504b0ca18ef5a2df, + 0xdffbdfbde2456eb9, + 0x46cd2b2fbee34634, + 0xf2aef8fe819d98c3, + 0x357f5276d4599d61, + 0x24a5483879c453e3, + 0x88026889192b4b9, + 0x28da96671782dbec, + 0x4ef37c40588e9aaa, + 0x8837b90651bc9fb3, + 0xc164f741d3f0e5d6, + 0xbc135a0a704b70ba, + 0x69cd868f7622ada, + 0xbc37ba89e0b9c0ab, + 0x47c14a01323552f6, + 0x4f00794bacee98bb, + 0x7107de7d637a69d5, + 0x88af793bb6f2255e, + 0xf3c6466b8799b598, + 0xc288c616aa7f3b59, + 0x81ca63cf42fca3fd, + 0x88d85ace36a2674b, + 0xd056bd3792389e7, + 0xe55c396c4e9dd32d, + 0xbefb504571e6c0a6, + 0x96ab32115e91e8cc, + 0xbf8acb18de8f38d1, + 0x66dae58801672606, + 0x833b6017872317fb, + 0xb87c16f2d1c92864, + 0xdb766a74e58b669c, + 0x89659f85c61417be, + 0xc8daad856011ea0c, + 0x76a4b565b6fe7eae, + 0xa469d085f6237312, + 0xaaf0365683a3e96c, + 0x4dbb746f8424f7b8, + 0x638755af4e4acc1, + 0x3d7807f5bde64486, + 0x17be6d8f5bbb7639, + 0x903f0cd44dc35dc, + 0x67b672eafdf1196c, + 0xa676ff93ed4c82f1, + 0x521d1004c5053d9d, + 0x37ba9ad09ccc9202, + 0x84e54d297aacfb51, + 0xa0b4b776a143445, + 0x820d471e20b348e, + 0x1874383cb83d46dc, + 0x97edeec7a1efe11c, + 0xb330e50b1bdc42aa, + 0x1dd91955ce70e032, + 0xa514cdb88f2939d5, + 0x2791233fd90db9d3, + 0x7b670a4cc50f7a9b, + 0x77c07d2a05c6dfa5, + 0xe3778b6646d0a6fa, + 0xb39c8eda47b56749, + 0x933ed448addbef28, + 0xaf846af6ab7d0bf4, + 0xe5af208eb666e49, + 0x5e6622f73534cd6a, + 0x297daeca42ef5b6e, + 0x862daef3d35539a6, + 0xe68722498f8e1ea9, + 0x981c53093dc0d572, + 0xfa09b0bfbf86fbf5, + 0x30b1e96166219f15, + 0x70e7d466bdc4fb83, + 0x5a66736e35f2a8e9, + 0xcddb59d2b7c1baef, + 0xd6c7d247d26d8996, + 0xea4e39eac8de1ba3, + 0x539c8bb19fa3aff2, + 0x9f90e4c5fd508d8, + 0xa34e5956fbaf3385, + 0x2e2f8e151d3ef375, + 0x173691e9b83faec1, + 0xb85a8d56bf016379, + 0x8382381267408ae3, + 0xb90f901bbdc0096d, + 0x7c6ad32933bcec65, + 0x76bb5e2f2c8ad595, + 0x390f851a6cf46d28, + 0xc3e6064da1c2da72, + 0xc52a0c101cfa5389, + 0xd78eaf84a3fbc530, + 0x3781b9e2288b997e, + 0x73c2f6dea83d05c4, + 0x4228e364c5b5ed7, + 0x9d7a3edf0da43911, + 0x8edcfeda24686756, + 0x5e7667a7b7a9b3a1, + 0x4c4f389fa143791d, + 0xb08bc1023da7cddc, + 0x7ab4be3ae529b1cc, + 0x754e6132dbe74ff9, + 0x71635442a839df45, + 0x2f6fb1643fbe52de, + 0x961e0a42cf7a8177, + 0xf3b45d83d89ef2ea, + 0xee3de4cf4a6e3e9b, + 0xcd6848542c3295e7, + 0xe4cee1664c78662f, + 0x9947548b474c68c4, + 0x25d73777a5ed8b0b, + 0xc915b1d636b7fc, + 0x21c2ba75d9b0d2da, + 0x5f6b5dcf608a64a1, + 0xdcf333255ff9570c, + 0x633b922418ced4ee, + 0xc136dde0b004b34a, + 0x58cc83b05d4b2f5a, + 0x5eb424dda28e42d2, + 0x62df47369739cd98, + 0xb4e0b42485e4ce17, + 0x16e1f0c1f9a8d1e7, + 0x8ec3916707560ebf, + 0x62ba6e2df2cc9db3, + 0xcbf9f4ff77d83a16, + 0x78d9d7d07d2bbcc4, + 0xef554ce1e02c41f4, + 0x8d7581127eccf94d, + 0xa9b53336cb3c8a05, + 0x38c42c0bf45c4f91, + 0x640893cdf4488863, + 0x80ec34bc575ea568, + 0x39f324f5b48eaa40, + 0xe9d9ed1f8eff527f, + 0x9224fc058cc5a214, + 0xbaba00b04cfe7741, + 0x309a9f120fcf52af, + 0xa558f3ec65626212, + 0x424bec8b7adabe2f, + 0x41622513a6aea433, + 0xb88da2d5324ca798, + 0xd287733b245528a4, + 0x9a44697e6d68aec3, + 0x7b1093be2f49bb28, + 0x50bbec632e3d8aad, + 0x6cd90723e1ea8283, + 0x897b9e7431b02bf3, + 0x219efdcb338a7047, + 0x3b0311f0a27c0656, + 0xdb17bf91c0db96e7, + 0x8cd4fd6b4e85a5b2, + 0xfab071054ba6409d, + 0x40d6fe831fa9dfd9, + 0xaf358debad7d791e, + 0xeb8d0e25a65e3e58, + 0xbbcbd3df14e08580, + 0xcf751f27ecdab2b, + 0x2b4da14f2613d8f4, + }; + private static ulong* ZSTD_ldm_gearTab => + (ulong*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_ZSTD_ldm_gearTab) + ); +#else + + private static readonly ulong* ZSTD_ldm_gearTab = GetArrayPointer( + new ulong[256] + { + 0xf5b8f72c5f77775c, + 0x84935f266b7ac412, + 0xb647ada9ca730ccc, + 0xb065bb4b114fb1de, + 0x34584e7e8c3a9fd0, + 0x4e97e17c6ae26b05, + 0x3a03d743bc99a604, + 0xcecd042422c4044f, + 0x76de76c58524259e, + 0x9c8528f65badeaca, + 0x86563706e2097529, + 0x2902475fa375d889, + 0xafb32a9739a5ebe6, + 0xce2714da3883e639, + 0x21eaf821722e69e, + 0x37b628620b628, + 0x49a8d455d88caf5, + 0x8556d711e6958140, + 0x4f7ae74fc605c1f, + 0x829f0c3468bd3a20, + 0x4ffdc885c625179e, + 0x8473de048a3daf1b, + 0x51008822b05646b2, + 0x69d75d12b2d1cc5f, + 0x8c9d4a19159154bc, + 0xc3cc10f4abbd4003, + 0xd06ddc1cecb97391, + 0xbe48e6e7ed80302e, + 0x3481db31cee03547, + 0xacc3f67cdaa1d210, + 0x65cb771d8c7f96cc, + 0x8eb27177055723dd, + 0xc789950d44cd94be, + 0x934feadc3700b12b, + 0x5e485f11edbdf182, + 0x1e2e2a46fd64767a, + 0x2969ca71d82efa7c, + 0x9d46e9935ebbba2e, + 0xe056b67e05e6822b, + 0x94d73f55739d03a0, + 0xcd7010bdb69b5a03, + 0x455ef9fcd79b82f4, + 0x869cb54a8749c161, + 0x38d1a4fa6185d225, + 0xb475166f94bbe9bb, + 0xa4143548720959f1, + 0x7aed4780ba6b26ba, + 0xd0ce264439e02312, + 0x84366d746078d508, + 0xa8ce973c72ed17be, + 0x21c323a29a430b01, + 0x9962d617e3af80ee, + 0xab0ce91d9c8cf75b, + 0x530e8ee6d19a4dbc, + 0x2ef68c0cf53f5d72, + 0xc03a681640a85506, + 0x496e4e9f9c310967, + 0x78580472b59b14a0, + 0x273824c23b388577, + 0x66bf923ad45cb553, + 0x47ae1a5a2492ba86, + 0x35e304569e229659, + 0x4765182a46870b6f, + 0x6cbab625e9099412, + 0xddac9a2e598522c1, + 0x7172086e666624f2, + 0xdf5003ca503b7837, + 0x88c0c1db78563d09, + 0x58d51865acfc289d, + 0x177671aec65224f1, + 0xfb79d8a241e967d7, + 0x2be1e101cad9a49a, + 0x6625682f6e29186b, + 0x399553457ac06e50, + 0x35dffb4c23abb74, + 0x429db2591f54aade, + 0xc52802a8037d1009, + 0x6acb27381f0b25f3, + 0xf45e2551ee4f823b, + 0x8b0ea2d99580c2f7, + 0x3bed519cbcb4e1e1, + 0xff452823dbb010a, + 0x9d42ed614f3dd267, + 0x5b9313c06257c57b, + 0xa114b8008b5e1442, + 0xc1fe311c11c13d4b, + 0x66e8763ea34c5568, + 0x8b982af1c262f05d, + 0xee8876faaa75fbb7, + 0x8a62a4d0d172bb2a, + 0xc13d94a3b7449a97, + 0x6dbbba9dc15d037c, + 0xc786101f1d92e0f1, + 0xd78681a907a0b79b, + 0xf61aaf2962c9abb9, + 0x2cfd16fcd3cb7ad9, + 0x868c5b6744624d21, + 0x25e650899c74ddd7, + 0xba042af4a7c37463, + 0x4eb1a539465a3eca, + 0xbe09dbf03b05d5ca, + 0x774e5a362b5472ba, + 0x47a1221229d183cd, + 0x504b0ca18ef5a2df, + 0xdffbdfbde2456eb9, + 0x46cd2b2fbee34634, + 0xf2aef8fe819d98c3, + 0x357f5276d4599d61, + 0x24a5483879c453e3, + 0x88026889192b4b9, + 0x28da96671782dbec, + 0x4ef37c40588e9aaa, + 0x8837b90651bc9fb3, + 0xc164f741d3f0e5d6, + 0xbc135a0a704b70ba, + 0x69cd868f7622ada, + 0xbc37ba89e0b9c0ab, + 0x47c14a01323552f6, + 0x4f00794bacee98bb, + 0x7107de7d637a69d5, + 0x88af793bb6f2255e, + 0xf3c6466b8799b598, + 0xc288c616aa7f3b59, + 0x81ca63cf42fca3fd, + 0x88d85ace36a2674b, + 0xd056bd3792389e7, + 0xe55c396c4e9dd32d, + 0xbefb504571e6c0a6, + 0x96ab32115e91e8cc, + 0xbf8acb18de8f38d1, + 0x66dae58801672606, + 0x833b6017872317fb, + 0xb87c16f2d1c92864, + 0xdb766a74e58b669c, + 0x89659f85c61417be, + 0xc8daad856011ea0c, + 0x76a4b565b6fe7eae, + 0xa469d085f6237312, + 0xaaf0365683a3e96c, + 0x4dbb746f8424f7b8, + 0x638755af4e4acc1, + 0x3d7807f5bde64486, + 0x17be6d8f5bbb7639, + 0x903f0cd44dc35dc, + 0x67b672eafdf1196c, + 0xa676ff93ed4c82f1, + 0x521d1004c5053d9d, + 0x37ba9ad09ccc9202, + 0x84e54d297aacfb51, + 0xa0b4b776a143445, + 0x820d471e20b348e, + 0x1874383cb83d46dc, + 0x97edeec7a1efe11c, + 0xb330e50b1bdc42aa, + 0x1dd91955ce70e032, + 0xa514cdb88f2939d5, + 0x2791233fd90db9d3, + 0x7b670a4cc50f7a9b, + 0x77c07d2a05c6dfa5, + 0xe3778b6646d0a6fa, + 0xb39c8eda47b56749, + 0x933ed448addbef28, + 0xaf846af6ab7d0bf4, + 0xe5af208eb666e49, + 0x5e6622f73534cd6a, + 0x297daeca42ef5b6e, + 0x862daef3d35539a6, + 0xe68722498f8e1ea9, + 0x981c53093dc0d572, + 0xfa09b0bfbf86fbf5, + 0x30b1e96166219f15, + 0x70e7d466bdc4fb83, + 0x5a66736e35f2a8e9, + 0xcddb59d2b7c1baef, + 0xd6c7d247d26d8996, + 0xea4e39eac8de1ba3, + 0x539c8bb19fa3aff2, + 0x9f90e4c5fd508d8, + 0xa34e5956fbaf3385, + 0x2e2f8e151d3ef375, + 0x173691e9b83faec1, + 0xb85a8d56bf016379, + 0x8382381267408ae3, + 0xb90f901bbdc0096d, + 0x7c6ad32933bcec65, + 0x76bb5e2f2c8ad595, + 0x390f851a6cf46d28, + 0xc3e6064da1c2da72, + 0xc52a0c101cfa5389, + 0xd78eaf84a3fbc530, + 0x3781b9e2288b997e, + 0x73c2f6dea83d05c4, + 0x4228e364c5b5ed7, + 0x9d7a3edf0da43911, + 0x8edcfeda24686756, + 0x5e7667a7b7a9b3a1, + 0x4c4f389fa143791d, + 0xb08bc1023da7cddc, + 0x7ab4be3ae529b1cc, + 0x754e6132dbe74ff9, + 0x71635442a839df45, + 0x2f6fb1643fbe52de, + 0x961e0a42cf7a8177, + 0xf3b45d83d89ef2ea, + 0xee3de4cf4a6e3e9b, + 0xcd6848542c3295e7, + 0xe4cee1664c78662f, + 0x9947548b474c68c4, + 0x25d73777a5ed8b0b, + 0xc915b1d636b7fc, + 0x21c2ba75d9b0d2da, + 0x5f6b5dcf608a64a1, + 0xdcf333255ff9570c, + 0x633b922418ced4ee, + 0xc136dde0b004b34a, + 0x58cc83b05d4b2f5a, + 0x5eb424dda28e42d2, + 0x62df47369739cd98, + 0xb4e0b42485e4ce17, + 0x16e1f0c1f9a8d1e7, + 0x8ec3916707560ebf, + 0x62ba6e2df2cc9db3, + 0xcbf9f4ff77d83a16, + 0x78d9d7d07d2bbcc4, + 0xef554ce1e02c41f4, + 0x8d7581127eccf94d, + 0xa9b53336cb3c8a05, + 0x38c42c0bf45c4f91, + 0x640893cdf4488863, + 0x80ec34bc575ea568, + 0x39f324f5b48eaa40, + 0xe9d9ed1f8eff527f, + 0x9224fc058cc5a214, + 0xbaba00b04cfe7741, + 0x309a9f120fcf52af, + 0xa558f3ec65626212, + 0x424bec8b7adabe2f, + 0x41622513a6aea433, + 0xb88da2d5324ca798, + 0xd287733b245528a4, + 0x9a44697e6d68aec3, + 0x7b1093be2f49bb28, + 0x50bbec632e3d8aad, + 0x6cd90723e1ea8283, + 0x897b9e7431b02bf3, + 0x219efdcb338a7047, + 0x3b0311f0a27c0656, + 0xdb17bf91c0db96e7, + 0x8cd4fd6b4e85a5b2, + 0xfab071054ba6409d, + 0x40d6fe831fa9dfd9, + 0xaf358debad7d791e, + 0xeb8d0e25a65e3e58, + 0xbbcbd3df14e08580, + 0xcf751f27ecdab2b, + 0x2b4da14f2613d8f4, + } + ); +#endif +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdOpt.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdOpt.cs new file mode 100644 index 00000000..4b05bb17 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdOpt.cs @@ -0,0 +1,2254 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /* ZSTD_bitWeight() : + * provide estimated "cost" of a stat in full bits only */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_bitWeight(uint stat) + { + return ZSTD_highbit32(stat + 1) * (1 << 8); + } + + /* ZSTD_fracWeight() : + * provide fractional-bit "cost" of a stat, + * using linear interpolation approximation */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_fracWeight(uint rawStat) + { + uint stat = rawStat + 1; + uint hb = ZSTD_highbit32(stat); + uint BWeight = hb * (1 << 8); + /* Fweight was meant for "Fractional weight" + * but it's effectively a value between 1 and 2 + * using fixed point arithmetic */ + uint FWeight = stat << 8 >> (int)hb; + uint weight = BWeight + FWeight; + assert(hb + 8 < 31); + return weight; + } + + private static int ZSTD_compressedLiterals(optState_t* optPtr) + { + return optPtr->literalCompressionMode != ZSTD_paramSwitch_e.ZSTD_ps_disable ? 1 : 0; + } + + private static void ZSTD_setBasePrices(optState_t* optPtr, int optLevel) + { + if (ZSTD_compressedLiterals(optPtr) != 0) + optPtr->litSumBasePrice = + optLevel != 0 ? ZSTD_fracWeight(optPtr->litSum) : ZSTD_bitWeight(optPtr->litSum); + optPtr->litLengthSumBasePrice = + optLevel != 0 + ? ZSTD_fracWeight(optPtr->litLengthSum) + : ZSTD_bitWeight(optPtr->litLengthSum); + optPtr->matchLengthSumBasePrice = + optLevel != 0 + ? ZSTD_fracWeight(optPtr->matchLengthSum) + : ZSTD_bitWeight(optPtr->matchLengthSum); + optPtr->offCodeSumBasePrice = + optLevel != 0 + ? ZSTD_fracWeight(optPtr->offCodeSum) + : ZSTD_bitWeight(optPtr->offCodeSum); + } + + private static uint sum_u32(uint* table, nuint nbElts) + { + nuint n; + uint total = 0; + for (n = 0; n < nbElts; n++) + { + total += table[n]; + } + + return total; + } + + private static uint ZSTD_downscaleStats( + uint* table, + uint lastEltIndex, + uint shift, + base_directive_e base1 + ) + { + uint s, + sum = 0; + assert(shift < 30); + for (s = 0; s < lastEltIndex + 1; s++) + { + uint @base = (uint)( + base1 != default ? 1 + : table[s] > 0 ? 1 + : 0 + ); + uint newStat = @base + (table[s] >> (int)shift); + sum += newStat; + table[s] = newStat; + } + + return sum; + } + + /* ZSTD_scaleStats() : + * reduce all elt frequencies in table if sum too large + * return the resulting sum of elements */ + private static uint ZSTD_scaleStats(uint* table, uint lastEltIndex, uint logTarget) + { + uint prevsum = sum_u32(table, lastEltIndex + 1); + uint factor = prevsum >> (int)logTarget; + assert(logTarget < 30); + if (factor <= 1) + return prevsum; + return ZSTD_downscaleStats( + table, + lastEltIndex, + ZSTD_highbit32(factor), + base_directive_e.base_1guaranteed + ); + } + +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_baseLLfreqs => + new uint[36] + { + 4, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + }; + private static uint* baseLLfreqs => + (uint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_baseLLfreqs) + ); +#else + + private static readonly uint* baseLLfreqs = GetArrayPointer( + new uint[36] + { + 4, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + } + ); +#endif +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_baseOFCfreqs => + new uint[32] + { + 6, + 2, + 1, + 1, + 2, + 3, + 4, + 4, + 4, + 3, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + }; + private static uint* baseOFCfreqs => + (uint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_baseOFCfreqs) + ); +#else + + private static readonly uint* baseOFCfreqs = GetArrayPointer( + new uint[32] + { + 6, + 2, + 1, + 1, + 2, + 3, + 4, + 4, + 4, + 3, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + } + ); +#endif + /* ZSTD_rescaleFreqs() : + * if first block (detected by optPtr->litLengthSum == 0) : init statistics + * take hints from dictionary if there is one + * and init from zero if there is none, + * using src for literals stats, and baseline stats for sequence symbols + * otherwise downscale existing stats, to be used as seed for next block. + */ + private static void ZSTD_rescaleFreqs( + optState_t* optPtr, + byte* src, + nuint srcSize, + int optLevel + ) + { + int compressedLiterals = ZSTD_compressedLiterals(optPtr); + optPtr->priceType = ZSTD_OptPrice_e.zop_dynamic; + if (optPtr->litLengthSum == 0) + { + if (srcSize <= 8) + { + optPtr->priceType = ZSTD_OptPrice_e.zop_predef; + } + + assert(optPtr->symbolCosts != null); + if (optPtr->symbolCosts->huf.repeatMode == HUF_repeat.HUF_repeat_valid) + { + optPtr->priceType = ZSTD_OptPrice_e.zop_dynamic; + if (compressedLiterals != 0) + { + /* generate literals statistics from huffman table */ + uint lit; + assert(optPtr->litFreq != null); + optPtr->litSum = 0; + for (lit = 0; lit <= (1 << 8) - 1; lit++) + { + /* scale to 2K */ + const uint scaleLog = 11; + uint bitCost = HUF_getNbBitsFromCTable( + &optPtr->symbolCosts->huf.CTable.e0, + lit + ); + assert(bitCost <= scaleLog); + optPtr->litFreq[lit] = (uint)( + bitCost != 0 ? 1 << (int)(scaleLog - bitCost) : 1 + ); + optPtr->litSum += optPtr->litFreq[lit]; + } + } + + { + uint ll; + FSE_CState_t llstate; + FSE_initCState(&llstate, optPtr->symbolCosts->fse.litlengthCTable); + optPtr->litLengthSum = 0; + for (ll = 0; ll <= 35; ll++) + { + /* scale to 1K */ + const uint scaleLog = 10; + uint bitCost = FSE_getMaxNbBits(llstate.symbolTT, ll); + assert(bitCost < scaleLog); + optPtr->litLengthFreq[ll] = (uint)( + bitCost != 0 ? 1 << (int)(scaleLog - bitCost) : 1 + ); + optPtr->litLengthSum += optPtr->litLengthFreq[ll]; + } + } + + { + uint ml; + FSE_CState_t mlstate; + FSE_initCState(&mlstate, optPtr->symbolCosts->fse.matchlengthCTable); + optPtr->matchLengthSum = 0; + for (ml = 0; ml <= 52; ml++) + { + const uint scaleLog = 10; + uint bitCost = FSE_getMaxNbBits(mlstate.symbolTT, ml); + assert(bitCost < scaleLog); + optPtr->matchLengthFreq[ml] = (uint)( + bitCost != 0 ? 1 << (int)(scaleLog - bitCost) : 1 + ); + optPtr->matchLengthSum += optPtr->matchLengthFreq[ml]; + } + } + + { + uint of; + FSE_CState_t ofstate; + FSE_initCState(&ofstate, optPtr->symbolCosts->fse.offcodeCTable); + optPtr->offCodeSum = 0; + for (of = 0; of <= 31; of++) + { + const uint scaleLog = 10; + uint bitCost = FSE_getMaxNbBits(ofstate.symbolTT, of); + assert(bitCost < scaleLog); + optPtr->offCodeFreq[of] = (uint)( + bitCost != 0 ? 1 << (int)(scaleLog - bitCost) : 1 + ); + optPtr->offCodeSum += optPtr->offCodeFreq[of]; + } + } + } + else + { + assert(optPtr->litFreq != null); + if (compressedLiterals != 0) + { + /* base initial cost of literals on direct frequency within src */ + uint lit = (1 << 8) - 1; + HIST_count_simple(optPtr->litFreq, &lit, src, srcSize); + optPtr->litSum = ZSTD_downscaleStats( + optPtr->litFreq, + (1 << 8) - 1, + 8, + base_directive_e.base_0possible + ); + } + + { + memcpy(optPtr->litLengthFreq, baseLLfreqs, sizeof(uint) * 36); + optPtr->litLengthSum = sum_u32(baseLLfreqs, 35 + 1); + } + + { + uint ml; + for (ml = 0; ml <= 52; ml++) + optPtr->matchLengthFreq[ml] = 1; + } + + optPtr->matchLengthSum = 52 + 1; + { + memcpy(optPtr->offCodeFreq, baseOFCfreqs, sizeof(uint) * 32); + optPtr->offCodeSum = sum_u32(baseOFCfreqs, 31 + 1); + } + } + } + else + { + if (compressedLiterals != 0) + optPtr->litSum = ZSTD_scaleStats(optPtr->litFreq, (1 << 8) - 1, 12); + optPtr->litLengthSum = ZSTD_scaleStats(optPtr->litLengthFreq, 35, 11); + optPtr->matchLengthSum = ZSTD_scaleStats(optPtr->matchLengthFreq, 52, 11); + optPtr->offCodeSum = ZSTD_scaleStats(optPtr->offCodeFreq, 31, 11); + } + + ZSTD_setBasePrices(optPtr, optLevel); + } + + /* ZSTD_rawLiteralsCost() : + * price of literals (only) in specified segment (which length can be 0). + * does not include price of literalLength symbol */ + private static uint ZSTD_rawLiteralsCost( + byte* literals, + uint litLength, + optState_t* optPtr, + int optLevel + ) + { + if (litLength == 0) + return 0; + if (ZSTD_compressedLiterals(optPtr) == 0) + return (litLength << 3) * (1 << 8); + if (optPtr->priceType == ZSTD_OptPrice_e.zop_predef) + return litLength * 6 * (1 << 8); + { + uint price = optPtr->litSumBasePrice * litLength; + uint litPriceMax = optPtr->litSumBasePrice - (1 << 8); + uint u; + assert(optPtr->litSumBasePrice >= 1 << 8); + for (u = 0; u < litLength; u++) + { + uint litPrice = + optLevel != 0 + ? ZSTD_fracWeight(optPtr->litFreq[literals[u]]) + : ZSTD_bitWeight(optPtr->litFreq[literals[u]]); + if (litPrice > litPriceMax) + litPrice = litPriceMax; + price -= litPrice; + } + + return price; + } + } + + /* ZSTD_litLengthPrice() : + * cost of literalLength symbol */ + private static uint ZSTD_litLengthPrice(uint litLength, optState_t* optPtr, int optLevel) + { + assert(litLength <= 1 << 17); + if (optPtr->priceType == ZSTD_OptPrice_e.zop_predef) + return optLevel != 0 ? ZSTD_fracWeight(litLength) : ZSTD_bitWeight(litLength); + if (litLength == 1 << 17) + return (1 << 8) + ZSTD_litLengthPrice((1 << 17) - 1, optPtr, optLevel); + { + uint llCode = ZSTD_LLcode(litLength); + return (uint)(LL_bits[llCode] * (1 << 8)) + + optPtr->litLengthSumBasePrice + - ( + optLevel != 0 + ? ZSTD_fracWeight(optPtr->litLengthFreq[llCode]) + : ZSTD_bitWeight(optPtr->litLengthFreq[llCode]) + ); + } + } + + /* ZSTD_getMatchPrice() : + * Provides the cost of the match part (offset + matchLength) of a sequence. + * Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence. + * @offBase : sumtype, representing an offset or a repcode, and using numeric representation of ZSTD_storeSeq() + * @optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) + */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_getMatchPrice( + uint offBase, + uint matchLength, + optState_t* optPtr, + int optLevel + ) + { + uint price; + uint offCode = ZSTD_highbit32(offBase); + uint mlBase = matchLength - 3; + assert(matchLength >= 3); + if (optPtr->priceType == ZSTD_OptPrice_e.zop_predef) + return (optLevel != 0 ? ZSTD_fracWeight(mlBase) : ZSTD_bitWeight(mlBase)) + + (16 + offCode) * (1 << 8); + price = + offCode * (1 << 8) + + ( + optPtr->offCodeSumBasePrice + - ( + optLevel != 0 + ? ZSTD_fracWeight(optPtr->offCodeFreq[offCode]) + : ZSTD_bitWeight(optPtr->offCodeFreq[offCode]) + ) + ); + if (optLevel < 2 && offCode >= 20) + price += (offCode - 19) * 2 * (1 << 8); + { + uint mlCode = ZSTD_MLcode(mlBase); + price += + (uint)(ML_bits[mlCode] * (1 << 8)) + + ( + optPtr->matchLengthSumBasePrice + - ( + optLevel != 0 + ? ZSTD_fracWeight(optPtr->matchLengthFreq[mlCode]) + : ZSTD_bitWeight(optPtr->matchLengthFreq[mlCode]) + ) + ); + } + + price += (1 << 8) / 5; + return price; + } + + /* ZSTD_updateStats() : + * assumption : literals + litLength <= iend */ + private static void ZSTD_updateStats( + optState_t* optPtr, + uint litLength, + byte* literals, + uint offBase, + uint matchLength + ) + { + if (ZSTD_compressedLiterals(optPtr) != 0) + { + uint u; + for (u = 0; u < litLength; u++) + optPtr->litFreq[literals[u]] += 2; + optPtr->litSum += litLength * 2; + } + + { + uint llCode = ZSTD_LLcode(litLength); + optPtr->litLengthFreq[llCode]++; + optPtr->litLengthSum++; + } + + { + uint offCode = ZSTD_highbit32(offBase); + assert(offCode <= 31); + optPtr->offCodeFreq[offCode]++; + optPtr->offCodeSum++; + } + + { + uint mlBase = matchLength - 3; + uint mlCode = ZSTD_MLcode(mlBase); + optPtr->matchLengthFreq[mlCode]++; + optPtr->matchLengthSum++; + } + } + + /* ZSTD_readMINMATCH() : + * function safe only for comparisons + * assumption : memPtr must be at least 4 bytes before end of buffer */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_readMINMATCH(void* memPtr, uint length) + { + switch (length) + { + default: + case 4: + return MEM_read32(memPtr); + case 3: + if (BitConverter.IsLittleEndian) + return MEM_read32(memPtr) << 8; + else + return MEM_read32(memPtr) >> 8; + } + } + + /* Update hashTable3 up to ip (excluded) + Assumption : always within prefix (i.e. not within extDict) */ + private static uint ZSTD_insertAndFindFirstIndexHash3( + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip + ) + { + uint* hashTable3 = ms->hashTable3; + uint hashLog3 = ms->hashLog3; + byte* @base = ms->window.@base; + uint idx = *nextToUpdate3; + uint target = (uint)(ip - @base); + nuint hash3 = ZSTD_hash3Ptr(ip, hashLog3); + assert(hashLog3 > 0); + while (idx < target) + { + hashTable3[ZSTD_hash3Ptr(@base + idx, hashLog3)] = idx; + idx++; + } + + *nextToUpdate3 = target; + return hashTable3[hash3]; + } + + /*-************************************* + * Binary Tree search + ***************************************/ + /** ZSTD_insertBt1() : add one or multiple positions to tree. + * @param ip assumed <= iend-8 . + * @param target The target of ZSTD_updateTree_internal() - we are filling to this position + * @return : nb of positions added */ + private static uint ZSTD_insertBt1( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iend, + uint target, + uint mls, + int extDict + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint* hashTable = ms->hashTable; + uint hashLog = cParams->hashLog; + nuint h = ZSTD_hashPtr(ip, hashLog, mls); + uint* bt = ms->chainTable; + uint btLog = cParams->chainLog - 1; + uint btMask = (uint)((1 << (int)btLog) - 1); + uint matchIndex = hashTable[h]; + nuint commonLengthSmaller = 0, + commonLengthLarger = 0; + byte* @base = ms->window.@base; + byte* dictBase = ms->window.dictBase; + uint dictLimit = ms->window.dictLimit; + byte* dictEnd = dictBase + dictLimit; + byte* prefixStart = @base + dictLimit; + byte* match; + uint curr = (uint)(ip - @base); + uint btLow = btMask >= curr ? 0 : curr - btMask; + uint* smallerPtr = bt + 2 * (curr & btMask); + uint* largerPtr = smallerPtr + 1; + /* to be nullified at the end */ + uint dummy32; + /* windowLow is based on target because + * we only need positions that will be in the window at the end of the tree update. + */ + uint windowLow = ZSTD_getLowestMatchIndex(ms, target, cParams->windowLog); + uint matchEndIdx = curr + 8 + 1; + nuint bestLength = 8; + uint nbCompares = 1U << (int)cParams->searchLog; + assert(curr <= target); + assert(ip <= iend - 8); + hashTable[h] = curr; + assert(windowLow > 0); + for (; nbCompares != 0 && matchIndex >= windowLow; --nbCompares) + { + uint* nextPtr = bt + 2 * (matchIndex & btMask); + /* guaranteed minimum nb of common bytes */ + nuint matchLength = + commonLengthSmaller < commonLengthLarger ? commonLengthSmaller : commonLengthLarger; + assert(matchIndex < curr); + if (extDict == 0 || matchIndex + matchLength >= dictLimit) + { + assert(matchIndex + matchLength >= dictLimit); + match = @base + matchIndex; + matchLength += ZSTD_count(ip + matchLength, match + matchLength, iend); + } + else + { + match = dictBase + matchIndex; + matchLength += ZSTD_count_2segments( + ip + matchLength, + match + matchLength, + iend, + dictEnd, + prefixStart + ); + if (matchIndex + matchLength >= dictLimit) + match = @base + matchIndex; + } + + if (matchLength > bestLength) + { + bestLength = matchLength; + if (matchLength > matchEndIdx - matchIndex) + matchEndIdx = matchIndex + (uint)matchLength; + } + + if (ip + matchLength == iend) + { + break; + } + + if (match[matchLength] < ip[matchLength]) + { + *smallerPtr = matchIndex; + commonLengthSmaller = matchLength; + if (matchIndex <= btLow) + { + smallerPtr = &dummy32; + break; + } + + smallerPtr = nextPtr + 1; + matchIndex = nextPtr[1]; + } + else + { + *largerPtr = matchIndex; + commonLengthLarger = matchLength; + if (matchIndex <= btLow) + { + largerPtr = &dummy32; + break; + } + + largerPtr = nextPtr; + matchIndex = nextPtr[0]; + } + } + + *smallerPtr = *largerPtr = 0; + { + uint positions = 0; + if (bestLength > 384) + positions = 192 < (uint)(bestLength - 384) ? 192 : (uint)(bestLength - 384); + assert(matchEndIdx > curr + 8); + return positions > matchEndIdx - (curr + 8) ? positions : matchEndIdx - (curr + 8); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ZSTD_updateTree_internal( + ZSTD_MatchState_t* ms, + byte* ip, + byte* iend, + uint mls, + ZSTD_dictMode_e dictMode + ) + { + byte* @base = ms->window.@base; + uint target = (uint)(ip - @base); + uint idx = ms->nextToUpdate; + while (idx < target) + { + uint forward = ZSTD_insertBt1( + ms, + @base + idx, + iend, + target, + mls, + dictMode == ZSTD_dictMode_e.ZSTD_extDict ? 1 : 0 + ); + assert(idx < idx + forward); + idx += forward; + } + + assert((nuint)(ip - @base) <= unchecked((uint)-1)); + assert((nuint)(iend - @base) <= unchecked((uint)-1)); + ms->nextToUpdate = target; + } + + /* used in ZSTD_loadDictionaryContent() */ + private static void ZSTD_updateTree(ZSTD_MatchState_t* ms, byte* ip, byte* iend) + { + ZSTD_updateTree_internal(ms, ip, iend, ms->cParams.minMatch, ZSTD_dictMode_e.ZSTD_noDict); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_insertBtAndGetAllMatches( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iLimit, + ZSTD_dictMode_e dictMode, + uint* rep, + uint ll0, + uint lengthToBeat, + uint mls + ) + { + ZSTD_compressionParameters* cParams = &ms->cParams; + uint sufficient_len = + cParams->targetLength < (1 << 12) - 1 ? cParams->targetLength : (1 << 12) - 1; + byte* @base = ms->window.@base; + uint curr = (uint)(ip - @base); + uint hashLog = cParams->hashLog; + uint minMatch = (uint)(mls == 3 ? 3 : 4); + uint* hashTable = ms->hashTable; + nuint h = ZSTD_hashPtr(ip, hashLog, mls); + uint matchIndex = hashTable[h]; + uint* bt = ms->chainTable; + uint btLog = cParams->chainLog - 1; + uint btMask = (1U << (int)btLog) - 1; + nuint commonLengthSmaller = 0, + commonLengthLarger = 0; + byte* dictBase = ms->window.dictBase; + uint dictLimit = ms->window.dictLimit; + byte* dictEnd = dictBase + dictLimit; + byte* prefixStart = @base + dictLimit; + uint btLow = btMask >= curr ? 0 : curr - btMask; + uint windowLow = ZSTD_getLowestMatchIndex(ms, curr, cParams->windowLog); + uint matchLow = windowLow != 0 ? windowLow : 1; + uint* smallerPtr = bt + 2 * (curr & btMask); + uint* largerPtr = bt + 2 * (curr & btMask) + 1; + /* farthest referenced position of any match => detects repetitive patterns */ + uint matchEndIdx = curr + 8 + 1; + /* to be nullified at the end */ + uint dummy32; + uint mnum = 0; + uint nbCompares = 1U << (int)cParams->searchLog; + ZSTD_MatchState_t* dms = + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState ? ms->dictMatchState : null; + ZSTD_compressionParameters* dmsCParams = + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState ? &dms->cParams : null; + byte* dmsBase = dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState ? dms->window.@base : null; + byte* dmsEnd = dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState ? dms->window.nextSrc : null; + uint dmsHighLimit = + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState ? (uint)(dmsEnd - dmsBase) : 0; + uint dmsLowLimit = + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState ? dms->window.lowLimit : 0; + uint dmsIndexDelta = + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState ? windowLow - dmsHighLimit : 0; + uint dmsHashLog = + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState ? dmsCParams->hashLog : hashLog; + uint dmsBtLog = + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState ? dmsCParams->chainLog - 1 : btLog; + uint dmsBtMask = + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState ? (1U << (int)dmsBtLog) - 1 : 0; + uint dmsBtLow = + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState + && dmsBtMask < dmsHighLimit - dmsLowLimit + ? dmsHighLimit - dmsBtMask + : dmsLowLimit; + nuint bestLength = lengthToBeat - 1; + assert(ll0 <= 1); + { + uint lastR = 3 + ll0; + uint repCode; + for (repCode = ll0; repCode < lastR; repCode++) + { + uint repOffset = repCode == 3 ? rep[0] - 1 : rep[repCode]; + uint repIndex = curr - repOffset; + uint repLen = 0; + assert(curr >= dictLimit); + if (repOffset - 1 < curr - dictLimit) + { + if ( + repIndex >= windowLow + && ZSTD_readMINMATCH(ip, minMatch) + == ZSTD_readMINMATCH(ip - repOffset, minMatch) + ) + { + repLen = + (uint)ZSTD_count(ip + minMatch, ip + minMatch - repOffset, iLimit) + + minMatch; + } + } + else + { + byte* repMatch = + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState + ? dmsBase + repIndex - dmsIndexDelta + : dictBase + repIndex; + assert(curr >= windowLow); + if ( + dictMode == ZSTD_dictMode_e.ZSTD_extDict + && ( + (repOffset - 1 < curr - windowLow ? 1 : 0) + & ZSTD_index_overlap_check(dictLimit, repIndex) + ) != 0 + && ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch) + ) + { + repLen = + (uint)ZSTD_count_2segments( + ip + minMatch, + repMatch + minMatch, + iLimit, + dictEnd, + prefixStart + ) + minMatch; + } + + if ( + dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState + && ( + (repOffset - 1 < curr - (dmsLowLimit + dmsIndexDelta) ? 1 : 0) + & ZSTD_index_overlap_check(dictLimit, repIndex) + ) != 0 + && ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch) + ) + { + repLen = + (uint)ZSTD_count_2segments( + ip + minMatch, + repMatch + minMatch, + iLimit, + dmsEnd, + prefixStart + ) + minMatch; + } + } + + if (repLen > bestLength) + { + bestLength = repLen; + assert(repCode - ll0 + 1 >= 1); + assert(repCode - ll0 + 1 <= 3); + matches[mnum].off = repCode - ll0 + 1; + matches[mnum].len = repLen; + mnum++; + if (repLen > sufficient_len || ip + repLen == iLimit) + { + return mnum; + } + } + } + } + + if (mls == 3 && bestLength < mls) + { + uint matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, nextToUpdate3, ip); + if (matchIndex3 >= matchLow && curr - matchIndex3 < 1 << 18) + { + nuint mlen; + if ( + dictMode == ZSTD_dictMode_e.ZSTD_noDict + || dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState + || matchIndex3 >= dictLimit + ) + { + byte* match = @base + matchIndex3; + mlen = ZSTD_count(ip, match, iLimit); + } + else + { + byte* match = dictBase + matchIndex3; + mlen = ZSTD_count_2segments(ip, match, iLimit, dictEnd, prefixStart); + } + + if (mlen >= mls) + { + bestLength = mlen; + assert(curr > matchIndex3); + assert(mnum == 0); + assert(curr - matchIndex3 > 0); + matches[0].off = curr - matchIndex3 + 3; + matches[0].len = (uint)mlen; + mnum = 1; + if (mlen > sufficient_len || ip + mlen == iLimit) + { + ms->nextToUpdate = curr + 1; + return 1; + } + } + } + } + + hashTable[h] = curr; + for (; nbCompares != 0 && matchIndex >= matchLow; --nbCompares) + { + uint* nextPtr = bt + 2 * (matchIndex & btMask); + byte* match; + /* guaranteed minimum nb of common bytes */ + nuint matchLength = + commonLengthSmaller < commonLengthLarger ? commonLengthSmaller : commonLengthLarger; + assert(curr > matchIndex); + if ( + dictMode == ZSTD_dictMode_e.ZSTD_noDict + || dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState + || matchIndex + matchLength >= dictLimit + ) + { + assert(matchIndex + matchLength >= dictLimit); + match = @base + matchIndex; +#if DEBUG + if (matchIndex >= dictLimit) + assert(memcmp(match, ip, matchLength) == 0); +#endif + matchLength += ZSTD_count(ip + matchLength, match + matchLength, iLimit); + } + else + { + match = dictBase + matchIndex; + assert(memcmp(match, ip, matchLength) == 0); + matchLength += ZSTD_count_2segments( + ip + matchLength, + match + matchLength, + iLimit, + dictEnd, + prefixStart + ); + if (matchIndex + matchLength >= dictLimit) + match = @base + matchIndex; + } + + if (matchLength > bestLength) + { + assert(matchEndIdx > matchIndex); + if (matchLength > matchEndIdx - matchIndex) + matchEndIdx = matchIndex + (uint)matchLength; + bestLength = matchLength; + assert(curr - matchIndex > 0); + matches[mnum].off = curr - matchIndex + 3; + matches[mnum].len = (uint)matchLength; + mnum++; + if (matchLength > 1 << 12 || ip + matchLength == iLimit) + { + if (dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState) + nbCompares = 0; + break; + } + } + + if (match[matchLength] < ip[matchLength]) + { + *smallerPtr = matchIndex; + commonLengthSmaller = matchLength; + if (matchIndex <= btLow) + { + smallerPtr = &dummy32; + break; + } + + smallerPtr = nextPtr + 1; + matchIndex = nextPtr[1]; + } + else + { + *largerPtr = matchIndex; + commonLengthLarger = matchLength; + if (matchIndex <= btLow) + { + largerPtr = &dummy32; + break; + } + + largerPtr = nextPtr; + matchIndex = nextPtr[0]; + } + } + + *smallerPtr = *largerPtr = 0; + assert(nbCompares <= 1U << (sizeof(nuint) == 4 ? 30 : 31) - 1); + if (dictMode == ZSTD_dictMode_e.ZSTD_dictMatchState && nbCompares != 0) + { + nuint dmsH = ZSTD_hashPtr(ip, dmsHashLog, mls); + uint dictMatchIndex = dms->hashTable[dmsH]; + uint* dmsBt = dms->chainTable; + commonLengthSmaller = commonLengthLarger = 0; + for (; nbCompares != 0 && dictMatchIndex > dmsLowLimit; --nbCompares) + { + uint* nextPtr = dmsBt + 2 * (dictMatchIndex & dmsBtMask); + /* guaranteed minimum nb of common bytes */ + nuint matchLength = + commonLengthSmaller < commonLengthLarger + ? commonLengthSmaller + : commonLengthLarger; + byte* match = dmsBase + dictMatchIndex; + matchLength += ZSTD_count_2segments( + ip + matchLength, + match + matchLength, + iLimit, + dmsEnd, + prefixStart + ); + if (dictMatchIndex + matchLength >= dmsHighLimit) + match = @base + dictMatchIndex + dmsIndexDelta; + if (matchLength > bestLength) + { + matchIndex = dictMatchIndex + dmsIndexDelta; + if (matchLength > matchEndIdx - matchIndex) + matchEndIdx = matchIndex + (uint)matchLength; + bestLength = matchLength; + assert(curr - matchIndex > 0); + matches[mnum].off = curr - matchIndex + 3; + matches[mnum].len = (uint)matchLength; + mnum++; + if (matchLength > 1 << 12 || ip + matchLength == iLimit) + { + break; + } + } + + if (dictMatchIndex <= dmsBtLow) + { + break; + } + + if (match[matchLength] < ip[matchLength]) + { + commonLengthSmaller = matchLength; + dictMatchIndex = nextPtr[1]; + } + else + { + commonLengthLarger = matchLength; + dictMatchIndex = nextPtr[0]; + } + } + } + + assert(matchEndIdx > curr + 8); + ms->nextToUpdate = matchEndIdx - 8; + return mnum; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ZSTD_btGetAllMatches_internal( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat, + ZSTD_dictMode_e dictMode, + uint mls + ) + { + assert( + ( + ms->cParams.minMatch <= 3 ? 3 + : ms->cParams.minMatch <= 6 ? ms->cParams.minMatch + : 6 + ) == mls + ); + if (ip < ms->window.@base + ms->nextToUpdate) + return 0; + ZSTD_updateTree_internal(ms, ip, iHighLimit, mls, dictMode); + return ZSTD_insertBtAndGetAllMatches( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + dictMode, + rep, + ll0, + lengthToBeat, + mls + ); + } + + private static uint ZSTD_btGetAllMatches_noDict_3( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_noDict, + 3 + ); + } + + private static uint ZSTD_btGetAllMatches_noDict_4( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_noDict, + 4 + ); + } + + private static uint ZSTD_btGetAllMatches_noDict_5( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_noDict, + 5 + ); + } + + private static uint ZSTD_btGetAllMatches_noDict_6( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_noDict, + 6 + ); + } + + private static uint ZSTD_btGetAllMatches_extDict_3( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_extDict, + 3 + ); + } + + private static uint ZSTD_btGetAllMatches_extDict_4( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_extDict, + 4 + ); + } + + private static uint ZSTD_btGetAllMatches_extDict_5( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_extDict, + 5 + ); + } + + private static uint ZSTD_btGetAllMatches_extDict_6( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_extDict, + 6 + ); + } + + private static uint ZSTD_btGetAllMatches_dictMatchState_3( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 3 + ); + } + + private static uint ZSTD_btGetAllMatches_dictMatchState_4( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 4 + ); + } + + private static uint ZSTD_btGetAllMatches_dictMatchState_5( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 5 + ); + } + + private static uint ZSTD_btGetAllMatches_dictMatchState_6( + ZSTD_match_t* matches, + ZSTD_MatchState_t* ms, + uint* nextToUpdate3, + byte* ip, + byte* iHighLimit, + uint* rep, + uint ll0, + uint lengthToBeat + ) + { + return ZSTD_btGetAllMatches_internal( + matches, + ms, + nextToUpdate3, + ip, + iHighLimit, + rep, + ll0, + lengthToBeat, + ZSTD_dictMode_e.ZSTD_dictMatchState, + 6 + ); + } + + private static readonly ZSTD_getAllMatchesFn[][] getAllMatchesFns = new ZSTD_getAllMatchesFn[ + 3 + ][] + { + new ZSTD_getAllMatchesFn[4] + { + ZSTD_btGetAllMatches_noDict_3, + ZSTD_btGetAllMatches_noDict_4, + ZSTD_btGetAllMatches_noDict_5, + ZSTD_btGetAllMatches_noDict_6, + }, + new ZSTD_getAllMatchesFn[4] + { + ZSTD_btGetAllMatches_extDict_3, + ZSTD_btGetAllMatches_extDict_4, + ZSTD_btGetAllMatches_extDict_5, + ZSTD_btGetAllMatches_extDict_6, + }, + new ZSTD_getAllMatchesFn[4] + { + ZSTD_btGetAllMatches_dictMatchState_3, + ZSTD_btGetAllMatches_dictMatchState_4, + ZSTD_btGetAllMatches_dictMatchState_5, + ZSTD_btGetAllMatches_dictMatchState_6, + }, + }; + + private static ZSTD_getAllMatchesFn ZSTD_selectBtGetAllMatches( + ZSTD_MatchState_t* ms, + ZSTD_dictMode_e dictMode + ) + { + uint mls = + ms->cParams.minMatch <= 3 ? 3 + : ms->cParams.minMatch <= 6 ? ms->cParams.minMatch + : 6; + assert((uint)dictMode < 3); + assert(mls - 3 < 4); + return getAllMatchesFns[(int)dictMode][mls - 3]; + } + + /* ZSTD_optLdm_skipRawSeqStoreBytes(): + * Moves forward in @rawSeqStore by @nbBytes, + * which will update the fields 'pos' and 'posInSequence'. + */ + private static void ZSTD_optLdm_skipRawSeqStoreBytes(RawSeqStore_t* rawSeqStore, nuint nbBytes) + { + uint currPos = (uint)(rawSeqStore->posInSequence + nbBytes); + while (currPos != 0 && rawSeqStore->pos < rawSeqStore->size) + { + rawSeq currSeq = rawSeqStore->seq[rawSeqStore->pos]; + if (currPos >= currSeq.litLength + currSeq.matchLength) + { + currPos -= currSeq.litLength + currSeq.matchLength; + rawSeqStore->pos++; + } + else + { + rawSeqStore->posInSequence = currPos; + break; + } + } + + if (currPos == 0 || rawSeqStore->pos == rawSeqStore->size) + { + rawSeqStore->posInSequence = 0; + } + } + + /* ZSTD_opt_getNextMatchAndUpdateSeqStore(): + * Calculates the beginning and end of the next match in the current block. + * Updates 'pos' and 'posInSequence' of the ldmSeqStore. + */ + private static void ZSTD_opt_getNextMatchAndUpdateSeqStore( + ZSTD_optLdm_t* optLdm, + uint currPosInBlock, + uint blockBytesRemaining + ) + { + rawSeq currSeq; + uint currBlockEndPos; + uint literalsBytesRemaining; + uint matchBytesRemaining; + if (optLdm->seqStore.size == 0 || optLdm->seqStore.pos >= optLdm->seqStore.size) + { + optLdm->startPosInBlock = 0xffffffff; + optLdm->endPosInBlock = 0xffffffff; + return; + } + + currSeq = optLdm->seqStore.seq[optLdm->seqStore.pos]; + assert(optLdm->seqStore.posInSequence <= currSeq.litLength + currSeq.matchLength); + currBlockEndPos = currPosInBlock + blockBytesRemaining; + literalsBytesRemaining = + optLdm->seqStore.posInSequence < currSeq.litLength + ? currSeq.litLength - (uint)optLdm->seqStore.posInSequence + : 0; + matchBytesRemaining = + literalsBytesRemaining == 0 + ? currSeq.matchLength - ((uint)optLdm->seqStore.posInSequence - currSeq.litLength) + : currSeq.matchLength; + if (literalsBytesRemaining >= blockBytesRemaining) + { + optLdm->startPosInBlock = 0xffffffff; + optLdm->endPosInBlock = 0xffffffff; + ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, blockBytesRemaining); + return; + } + + optLdm->startPosInBlock = currPosInBlock + literalsBytesRemaining; + optLdm->endPosInBlock = optLdm->startPosInBlock + matchBytesRemaining; + optLdm->offset = currSeq.offset; + if (optLdm->endPosInBlock > currBlockEndPos) + { + optLdm->endPosInBlock = currBlockEndPos; + ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, currBlockEndPos - currPosInBlock); + } + else + { + ZSTD_optLdm_skipRawSeqStoreBytes( + &optLdm->seqStore, + literalsBytesRemaining + matchBytesRemaining + ); + } + } + + /* ZSTD_optLdm_maybeAddMatch(): + * Adds a match if it's long enough, + * based on it's 'matchStartPosInBlock' and 'matchEndPosInBlock', + * into 'matches'. Maintains the correct ordering of 'matches'. + */ + private static void ZSTD_optLdm_maybeAddMatch( + ZSTD_match_t* matches, + uint* nbMatches, + ZSTD_optLdm_t* optLdm, + uint currPosInBlock, + uint minMatch + ) + { + uint posDiff = currPosInBlock - optLdm->startPosInBlock; + /* Note: ZSTD_match_t actually contains offBase and matchLength (before subtracting MINMATCH) */ + uint candidateMatchLength = optLdm->endPosInBlock - optLdm->startPosInBlock - posDiff; + if ( + currPosInBlock < optLdm->startPosInBlock + || currPosInBlock >= optLdm->endPosInBlock + || candidateMatchLength < minMatch + ) + { + return; + } + + if ( + *nbMatches == 0 + || candidateMatchLength > matches[*nbMatches - 1].len && *nbMatches < 1 << 12 + ) + { + assert(optLdm->offset > 0); + uint candidateOffBase = optLdm->offset + 3; + matches[*nbMatches].len = candidateMatchLength; + matches[*nbMatches].off = candidateOffBase; + (*nbMatches)++; + } + } + + /* ZSTD_optLdm_processMatchCandidate(): + * Wrapper function to update ldm seq store and call ldm functions as necessary. + */ + private static void ZSTD_optLdm_processMatchCandidate( + ZSTD_optLdm_t* optLdm, + ZSTD_match_t* matches, + uint* nbMatches, + uint currPosInBlock, + uint remainingBytes, + uint minMatch + ) + { + if (optLdm->seqStore.size == 0 || optLdm->seqStore.pos >= optLdm->seqStore.size) + { + return; + } + + if (currPosInBlock >= optLdm->endPosInBlock) + { + if (currPosInBlock > optLdm->endPosInBlock) + { + /* The position at which ZSTD_optLdm_processMatchCandidate() is called is not necessarily + * at the end of a match from the ldm seq store, and will often be some bytes + * over beyond matchEndPosInBlock. As such, we need to correct for these "overshoots" + */ + uint posOvershoot = currPosInBlock - optLdm->endPosInBlock; + ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, posOvershoot); + } + + ZSTD_opt_getNextMatchAndUpdateSeqStore(optLdm, currPosInBlock, remainingBytes); + } + + ZSTD_optLdm_maybeAddMatch(matches, nbMatches, optLdm, currPosInBlock, minMatch); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ZSTD_compressBlock_opt_generic( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize, + int optLevel, + ZSTD_dictMode_e dictMode + ) + { + optState_t* optStatePtr = &ms->opt; + byte* istart = (byte*)src; + byte* ip = istart; + byte* anchor = istart; + byte* iend = istart + srcSize; + byte* ilimit = iend - 8; + byte* @base = ms->window.@base; + byte* prefixStart = @base + ms->window.dictLimit; + ZSTD_compressionParameters* cParams = &ms->cParams; + ZSTD_getAllMatchesFn getAllMatches = ZSTD_selectBtGetAllMatches(ms, dictMode); + uint sufficient_len = + cParams->targetLength < (1 << 12) - 1 ? cParams->targetLength : (1 << 12) - 1; + uint minMatch = (uint)(cParams->minMatch == 3 ? 3 : 4); + uint nextToUpdate3 = ms->nextToUpdate; + ZSTD_optimal_t* opt = optStatePtr->priceTable; + ZSTD_match_t* matches = optStatePtr->matchTable; + ZSTD_optimal_t lastStretch; + ZSTD_optLdm_t optLdm; + lastStretch = new ZSTD_optimal_t(); + optLdm.seqStore = ms->ldmSeqStore != null ? *ms->ldmSeqStore : kNullRawSeqStore; + optLdm.endPosInBlock = optLdm.startPosInBlock = optLdm.offset = 0; + ZSTD_opt_getNextMatchAndUpdateSeqStore(&optLdm, (uint)(ip - istart), (uint)(iend - ip)); + assert(optLevel <= 2); + ZSTD_rescaleFreqs(optStatePtr, (byte*)src, srcSize, optLevel); + ip += ip == prefixStart ? 1 : 0; + while (ip < ilimit) + { + uint cur, + last_pos = 0; + { + uint litlen = (uint)(ip - anchor); + uint ll0 = litlen == 0 ? 1U : 0U; + uint nbMatches = getAllMatches( + matches, + ms, + &nextToUpdate3, + ip, + iend, + rep, + ll0, + minMatch + ); + ZSTD_optLdm_processMatchCandidate( + &optLdm, + matches, + &nbMatches, + (uint)(ip - istart), + (uint)(iend - ip), + minMatch + ); + if (nbMatches == 0) + { + ip++; + continue; + } + + opt[0].mlen = 0; + opt[0].litlen = litlen; + opt[0].price = (int)ZSTD_litLengthPrice(litlen, optStatePtr, optLevel); + memcpy(&opt[0].rep[0], rep, sizeof(uint) * 3); + { + uint maxML = matches[nbMatches - 1].len; + uint maxOffBase = matches[nbMatches - 1].off; + if (maxML > sufficient_len) + { + lastStretch.litlen = 0; + lastStretch.mlen = maxML; + lastStretch.off = maxOffBase; + cur = 0; + last_pos = maxML; + goto _shortestPath; + } + } + + assert(opt[0].price >= 0); + { + uint pos; + uint matchNb; + for (pos = 1; pos < minMatch; pos++) + { + opt[pos].price = 1 << 30; + opt[pos].mlen = 0; + opt[pos].litlen = litlen + pos; + } + + for (matchNb = 0; matchNb < nbMatches; matchNb++) + { + uint offBase = matches[matchNb].off; + uint end = matches[matchNb].len; + for (; pos <= end; pos++) + { + int matchPrice = (int)ZSTD_getMatchPrice( + offBase, + pos, + optStatePtr, + optLevel + ); + int sequencePrice = opt[0].price + matchPrice; + opt[pos].mlen = pos; + opt[pos].off = offBase; + opt[pos].litlen = 0; + opt[pos].price = + sequencePrice + (int)ZSTD_litLengthPrice(0, optStatePtr, optLevel); + } + } + + last_pos = pos - 1; + opt[pos].price = 1 << 30; + } + } + + for (cur = 1; cur <= last_pos; cur++) + { + byte* inr = ip + cur; + assert(cur <= 1 << 12); + { + uint litlen = opt[cur - 1].litlen + 1; + int price = + opt[cur - 1].price + + (int)ZSTD_rawLiteralsCost(ip + cur - 1, 1, optStatePtr, optLevel) + + ( + (int)ZSTD_litLengthPrice(litlen, optStatePtr, optLevel) + - (int)ZSTD_litLengthPrice(litlen - 1, optStatePtr, optLevel) + ); + assert(price < 1000000000); + if (price <= opt[cur].price) + { + ZSTD_optimal_t prevMatch = opt[cur]; + opt[cur] = opt[cur - 1]; + opt[cur].litlen = litlen; + opt[cur].price = price; + if ( + optLevel >= 1 + && prevMatch.litlen == 0 + && (int)ZSTD_litLengthPrice(1, optStatePtr, optLevel) + - (int)ZSTD_litLengthPrice(1 - 1, optStatePtr, optLevel) + < 0 + && ip + cur < iend + ) + { + /* check next position, in case it would be cheaper */ + int with1literal = + prevMatch.price + + (int)ZSTD_rawLiteralsCost(ip + cur, 1, optStatePtr, optLevel) + + ( + (int)ZSTD_litLengthPrice(1, optStatePtr, optLevel) + - (int)ZSTD_litLengthPrice(1 - 1, optStatePtr, optLevel) + ); + int withMoreLiterals = + price + + (int)ZSTD_rawLiteralsCost(ip + cur, 1, optStatePtr, optLevel) + + ( + (int)ZSTD_litLengthPrice(litlen + 1, optStatePtr, optLevel) + - (int)ZSTD_litLengthPrice( + litlen + 1 - 1, + optStatePtr, + optLevel + ) + ); + if ( + with1literal < withMoreLiterals + && with1literal < opt[cur + 1].price + ) + { + /* update offset history - before it disappears */ + uint prev = cur - prevMatch.mlen; + repcodes_s newReps = ZSTD_newRep( + opt[prev].rep, + prevMatch.off, + opt[prev].litlen == 0 ? 1U : 0U + ); + assert(cur >= prevMatch.mlen); + opt[cur + 1] = prevMatch; + memcpy(opt[cur + 1].rep, &newReps, (uint)sizeof(repcodes_s)); + opt[cur + 1].litlen = 1; + opt[cur + 1].price = with1literal; + if (last_pos < cur + 1) + last_pos = cur + 1; + } + } + } + } + + assert(cur >= opt[cur].mlen); + if (opt[cur].litlen == 0) + { + /* just finished a match => alter offset history */ + uint prev = cur - opt[cur].mlen; + repcodes_s newReps = ZSTD_newRep( + opt[prev].rep, + opt[cur].off, + opt[prev].litlen == 0 ? 1U : 0U + ); + memcpy(opt[cur].rep, &newReps, (uint)sizeof(repcodes_s)); + } + + if (inr > ilimit) + continue; + if (cur == last_pos) + break; + if (optLevel == 0 && opt[cur + 1].price <= opt[cur].price + (1 << 8) / 2) + { + continue; + } + + assert(opt[cur].price >= 0); + { + uint ll0 = opt[cur].litlen == 0 ? 1U : 0U; + int previousPrice = opt[cur].price; + int basePrice = + previousPrice + (int)ZSTD_litLengthPrice(0, optStatePtr, optLevel); + uint nbMatches = getAllMatches( + matches, + ms, + &nextToUpdate3, + inr, + iend, + opt[cur].rep, + ll0, + minMatch + ); + uint matchNb; + ZSTD_optLdm_processMatchCandidate( + &optLdm, + matches, + &nbMatches, + (uint)(inr - istart), + (uint)(iend - inr), + minMatch + ); + if (nbMatches == 0) + { + continue; + } + + { + uint longestML = matches[nbMatches - 1].len; + if ( + longestML > sufficient_len + || cur + longestML >= 1 << 12 + || ip + cur + longestML >= iend + ) + { + lastStretch.mlen = longestML; + lastStretch.off = matches[nbMatches - 1].off; + lastStretch.litlen = 0; + last_pos = cur + longestML; + goto _shortestPath; + } + } + + for (matchNb = 0; matchNb < nbMatches; matchNb++) + { + uint offset = matches[matchNb].off; + uint lastML = matches[matchNb].len; + uint startML = matchNb > 0 ? matches[matchNb - 1].len + 1 : minMatch; + uint mlen; + for (mlen = lastML; mlen >= startML; mlen--) + { + uint pos = cur + mlen; + int price = + basePrice + + (int)ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel); + if (pos > last_pos || price < opt[pos].price) + { + while (last_pos < pos) + { + last_pos++; + opt[last_pos].price = 1 << 30; + opt[last_pos].litlen = 0 == 0 ? 1U : 0U; + } + + opt[pos].mlen = mlen; + opt[pos].off = offset; + opt[pos].litlen = 0; + opt[pos].price = price; + } + else + { + if (optLevel == 0) + break; + } + } + } + } + + opt[last_pos + 1].price = 1 << 30; + } + + lastStretch = opt[last_pos]; + assert(cur >= lastStretch.mlen); + cur = last_pos - lastStretch.mlen; + _shortestPath: + assert(opt[0].mlen == 0); + assert(last_pos >= lastStretch.mlen); + assert(cur == last_pos - lastStretch.mlen); + if (lastStretch.mlen == 0) + { + assert(lastStretch.litlen == (uint)(ip - anchor) + last_pos); + ip += last_pos; + continue; + } + + assert(lastStretch.off > 0); + if (lastStretch.litlen == 0) + { + /* finishing on a match : update offset history */ + repcodes_s reps = ZSTD_newRep( + opt[cur].rep, + lastStretch.off, + opt[cur].litlen == 0 ? 1U : 0U + ); + memcpy(rep, &reps, (uint)sizeof(repcodes_s)); + } + else + { + memcpy(rep, lastStretch.rep, (uint)sizeof(repcodes_s)); + assert(cur >= lastStretch.litlen); + cur -= lastStretch.litlen; + } + + { + uint storeEnd = cur + 2; + uint storeStart = storeEnd; + uint stretchPos = cur; + assert(storeEnd < (1 << 12) + 3); + if (lastStretch.litlen > 0) + { + opt[storeEnd].litlen = lastStretch.litlen; + opt[storeEnd].mlen = 0; + storeStart = storeEnd - 1; + opt[storeStart] = lastStretch; + } + + { + opt[storeEnd] = lastStretch; + storeStart = storeEnd; + } + + while (true) + { + ZSTD_optimal_t nextStretch = opt[stretchPos]; + opt[storeStart].litlen = nextStretch.litlen; + if (nextStretch.mlen == 0) + { + break; + } + + storeStart--; + opt[storeStart] = nextStretch; + assert(nextStretch.litlen + nextStretch.mlen <= stretchPos); + stretchPos -= nextStretch.litlen + nextStretch.mlen; + } + + { + uint storePos; + for (storePos = storeStart; storePos <= storeEnd; storePos++) + { + uint llen = opt[storePos].litlen; + uint mlen = opt[storePos].mlen; + uint offBase = opt[storePos].off; + uint advance = llen + mlen; + if (mlen == 0) + { + assert(storePos == storeEnd); + ip = anchor + llen; + continue; + } + + assert(anchor + llen <= iend); + ZSTD_updateStats(optStatePtr, llen, anchor, offBase, mlen); + ZSTD_storeSeq(seqStore, llen, anchor, iend, offBase, mlen); + anchor += advance; + ip = anchor; + } + } + + ZSTD_setBasePrices(optStatePtr, optLevel); + } + } + + return (nuint)(iend - anchor); + } + + private static nuint ZSTD_compressBlock_opt0( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize, + ZSTD_dictMode_e dictMode + ) + { + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0, dictMode); + } + + private static nuint ZSTD_compressBlock_opt2( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize, + ZSTD_dictMode_e dictMode + ) + { + return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2, dictMode); + } + + private static nuint ZSTD_compressBlock_btopt( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_opt0( + ms, + seqStore, + rep, + src, + srcSize, + ZSTD_dictMode_e.ZSTD_noDict + ); + } + + /* ZSTD_initStats_ultra(): + * make a first compression pass, just to seed stats with more accurate starting values. + * only works on first block, with no dictionary and no ldm. + * this function cannot error out, its narrow contract must be respected. + */ + private static void ZSTD_initStats_ultra( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + /* updated rep codes will sink here */ + uint* tmpRep = stackalloc uint[3]; + memcpy(tmpRep, rep, sizeof(uint) * 3); + assert(ms->opt.litLengthSum == 0); + assert(seqStore->sequences == seqStore->sequencesStart); + assert(ms->window.dictLimit == ms->window.lowLimit); + assert(ms->window.dictLimit - ms->nextToUpdate <= 1); + ZSTD_compressBlock_opt2(ms, seqStore, tmpRep, src, srcSize, ZSTD_dictMode_e.ZSTD_noDict); + ZSTD_resetSeqStore(seqStore); + ms->window.@base -= srcSize; + ms->window.dictLimit += (uint)srcSize; + ms->window.lowLimit = ms->window.dictLimit; + ms->nextToUpdate = ms->window.dictLimit; + } + + private static nuint ZSTD_compressBlock_btultra( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_opt2( + ms, + seqStore, + rep, + src, + srcSize, + ZSTD_dictMode_e.ZSTD_noDict + ); + } + + /* note : no btultra2 variant for extDict nor dictMatchState, + * because btultra2 is not meant to work with dictionaries + * and is only specific for the first block (no prefix) */ + private static nuint ZSTD_compressBlock_btultra2( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + uint curr = (uint)((byte*)src - ms->window.@base); + assert(srcSize <= 1 << 17); + if ( + ms->opt.litLengthSum == 0 + && seqStore->sequences == seqStore->sequencesStart + && ms->window.dictLimit == ms->window.lowLimit + && curr == ms->window.dictLimit + && srcSize > 8 + ) + { + ZSTD_initStats_ultra(ms, seqStore, rep, src, srcSize); + } + + return ZSTD_compressBlock_opt2( + ms, + seqStore, + rep, + src, + srcSize, + ZSTD_dictMode_e.ZSTD_noDict + ); + } + + private static nuint ZSTD_compressBlock_btopt_dictMatchState( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_opt0( + ms, + seqStore, + rep, + src, + srcSize, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_compressBlock_btopt_extDict( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_opt0( + ms, + seqStore, + rep, + src, + srcSize, + ZSTD_dictMode_e.ZSTD_extDict + ); + } + + private static nuint ZSTD_compressBlock_btultra_dictMatchState( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_opt2( + ms, + seqStore, + rep, + src, + srcSize, + ZSTD_dictMode_e.ZSTD_dictMatchState + ); + } + + private static nuint ZSTD_compressBlock_btultra_extDict( + ZSTD_MatchState_t* ms, + SeqStore_t* seqStore, + uint* rep, + void* src, + nuint srcSize + ) + { + return ZSTD_compressBlock_opt2( + ms, + seqStore, + rep, + src, + srcSize, + ZSTD_dictMode_e.ZSTD_extDict + ); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdPresplit.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdPresplit.cs new file mode 100644 index 00000000..e0d13921 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdPresplit.cs @@ -0,0 +1,295 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + /* for hashLog > 8, hash 2 bytes. + * for hashLog == 8, just take the byte, no hashing. + * The speed of this method relies on compile-time constant propagation */ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint hash2(void* p, uint hashLog) + { + assert(hashLog >= 8); + if (hashLog == 8) + return ((byte*)p)[0]; + assert(hashLog <= 10); + return MEM_read16(p) * 0x9e3779b9 >> (int)(32 - hashLog); + } + + private static void initStats(FPStats* fpstats) + { + *fpstats = new FPStats(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void addEvents_generic( + Fingerprint* fp, + void* src, + nuint srcSize, + nuint samplingRate, + uint hashLog + ) + { + sbyte* p = (sbyte*)src; + nuint limit = srcSize - 2 + 1; + nuint n; + assert(srcSize >= 2); + for (n = 0; n < limit; n += samplingRate) + { + fp->events[hash2(p + n, hashLog)]++; + } + + fp->nbEvents += limit / samplingRate; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void recordFingerprint_generic( + Fingerprint* fp, + void* src, + nuint srcSize, + nuint samplingRate, + uint hashLog + ) + { + memset(fp, 0, (uint)(sizeof(uint) * ((nuint)1 << (int)hashLog))); + fp->nbEvents = 0; + addEvents_generic(fp, src, srcSize, samplingRate, hashLog); + } + + private static void ZSTD_recordFingerprint_1(Fingerprint* fp, void* src, nuint srcSize) + { + recordFingerprint_generic(fp, src, srcSize, 1, 10); + } + + private static void ZSTD_recordFingerprint_5(Fingerprint* fp, void* src, nuint srcSize) + { + recordFingerprint_generic(fp, src, srcSize, 5, 10); + } + + private static void ZSTD_recordFingerprint_11(Fingerprint* fp, void* src, nuint srcSize) + { + recordFingerprint_generic(fp, src, srcSize, 11, 9); + } + + private static void ZSTD_recordFingerprint_43(Fingerprint* fp, void* src, nuint srcSize) + { + recordFingerprint_generic(fp, src, srcSize, 43, 8); + } + + private static ulong abs64(long s64) + { + return (ulong)(s64 < 0 ? -s64 : s64); + } + + private static ulong fpDistance(Fingerprint* fp1, Fingerprint* fp2, uint hashLog) + { + ulong distance = 0; + nuint n; + assert(hashLog <= 10); + for (n = 0; n < (nuint)1 << (int)hashLog; n++) + { + distance += abs64( + fp1->events[n] * (long)fp2->nbEvents - fp2->events[n] * (long)fp1->nbEvents + ); + } + + return distance; + } + + /* Compare newEvents with pastEvents + * return 1 when considered "too different" + */ + private static int compareFingerprints( + Fingerprint* @ref, + Fingerprint* newfp, + int penalty, + uint hashLog + ) + { + assert(@ref->nbEvents > 0); + assert(newfp->nbEvents > 0); + { + ulong p50 = @ref->nbEvents * (ulong)newfp->nbEvents; + ulong deviation = fpDistance(@ref, newfp, hashLog); + ulong threshold = p50 * (ulong)(16 - 2 + penalty) / 16; + return deviation >= threshold ? 1 : 0; + } + } + + private static void mergeEvents(Fingerprint* acc, Fingerprint* newfp) + { + nuint n; + for (n = 0; n < 1 << 10; n++) + { + acc->events[n] += newfp->events[n]; + } + + acc->nbEvents += newfp->nbEvents; + } + + private static void flushEvents(FPStats* fpstats) + { + nuint n; + for (n = 0; n < 1 << 10; n++) + { + fpstats->pastEvents.events[n] = fpstats->newEvents.events[n]; + } + + fpstats->pastEvents.nbEvents = fpstats->newEvents.nbEvents; + fpstats->newEvents = new Fingerprint(); + } + + private static void removeEvents(Fingerprint* acc, Fingerprint* slice) + { + nuint n; + for (n = 0; n < 1 << 10; n++) + { + assert(acc->events[n] >= slice->events[n]); + acc->events[n] -= slice->events[n]; + } + + acc->nbEvents -= slice->nbEvents; + } + + private static readonly void*[] records_fs = new void*[4] + { + (delegate* managed)(&ZSTD_recordFingerprint_43), + (delegate* managed)(&ZSTD_recordFingerprint_11), + (delegate* managed)(&ZSTD_recordFingerprint_5), + (delegate* managed)(&ZSTD_recordFingerprint_1), + }; +#if NET7_0_OR_GREATER + private static ReadOnlySpan Span_hashParams => new uint[4] { 8, 9, 10, 10 }; + private static uint* hashParams => + (uint*) + System.Runtime.CompilerServices.Unsafe.AsPointer( + ref MemoryMarshal.GetReference(Span_hashParams) + ); +#else + + private static readonly uint* hashParams = GetArrayPointer(new uint[4] { 8, 9, 10, 10 }); +#endif + + private static nuint ZSTD_splitBlock_byChunks( + void* blockStart, + nuint blockSize, + int level, + void* workspace, + nuint wkspSize + ) + { + assert(0 <= level && level <= 3); + void* record_f = records_fs[level]; + FPStats* fpstats = (FPStats*)workspace; + sbyte* p = (sbyte*)blockStart; + int penalty = 3; + nuint pos = 0; + assert(blockSize == 128 << 10); + assert(workspace != null); + assert((nuint)workspace % (nuint)Math.Max(sizeof(uint), sizeof(ulong)) == 0); + assert(wkspSize >= (nuint)sizeof(FPStats)); + initStats(fpstats); + ((delegate* managed)record_f)( + &fpstats->pastEvents, + p, + 8 << 10 + ); + for (pos = 8 << 10; pos <= blockSize - (8 << 10); pos += 8 << 10) + { + ((delegate* managed)record_f)( + &fpstats->newEvents, + p + pos, + 8 << 10 + ); + if ( + compareFingerprints( + &fpstats->pastEvents, + &fpstats->newEvents, + penalty, + hashParams[level] + ) != 0 + ) + { + return pos; + } + else + { + mergeEvents(&fpstats->pastEvents, &fpstats->newEvents); + if (penalty > 0) + penalty--; + } + } + + assert(pos == blockSize); + return blockSize; + } + + /* ZSTD_splitBlock_fromBorders(): very fast strategy : + * compare fingerprint from beginning and end of the block, + * derive from their difference if it's preferable to split in the middle, + * repeat the process a second time, for finer grained decision. + * 3 times did not brought improvements, so I stopped at 2. + * Benefits are good enough for a cheap heuristic. + * More accurate splitting saves more, but speed impact is also more perceptible. + * For better accuracy, use more elaborate variant *_byChunks. + */ + private static nuint ZSTD_splitBlock_fromBorders( + void* blockStart, + nuint blockSize, + void* workspace, + nuint wkspSize + ) + { + FPStats* fpstats = (FPStats*)workspace; + Fingerprint* middleEvents = (Fingerprint*)(void*)((sbyte*)workspace + 512 * sizeof(uint)); + assert(blockSize == 128 << 10); + assert(workspace != null); + assert((nuint)workspace % (nuint)Math.Max(sizeof(uint), sizeof(ulong)) == 0); + assert(wkspSize >= (nuint)sizeof(FPStats)); + initStats(fpstats); + HIST_add(fpstats->pastEvents.events, blockStart, 512); + HIST_add(fpstats->newEvents.events, (sbyte*)blockStart + blockSize - 512, 512); + fpstats->pastEvents.nbEvents = fpstats->newEvents.nbEvents = 512; + if (compareFingerprints(&fpstats->pastEvents, &fpstats->newEvents, 0, 8) == 0) + return blockSize; + HIST_add(middleEvents->events, (sbyte*)blockStart + blockSize / 2 - 512 / 2, 512); + middleEvents->nbEvents = 512; + { + ulong distFromBegin = fpDistance(&fpstats->pastEvents, middleEvents, 8); + ulong distFromEnd = fpDistance(&fpstats->newEvents, middleEvents, 8); + const ulong minDistance = 512 * 512 / 3; + if (abs64((long)distFromBegin - (long)distFromEnd) < minDistance) + return 64 * (1 << 10); + return (nuint)(distFromBegin > distFromEnd ? 32 * (1 << 10) : 96 * (1 << 10)); + } + } + + /* ZSTD_splitBlock(): + * @level must be a value between 0 and 4. + * higher levels spend more energy to detect block boundaries. + * @workspace must be aligned for size_t. + * @wkspSize must be at least >= ZSTD_SLIPBLOCK_WORKSPACESIZE + * note: + * For the time being, this function only accepts full 128 KB blocks. + * Therefore, @blockSize must be == 128 KB. + * While this could be extended to smaller sizes in the future, + * it is not yet clear if this would be useful. TBD. + */ + private static nuint ZSTD_splitBlock( + void* blockStart, + nuint blockSize, + int level, + void* workspace, + nuint wkspSize + ) + { + assert(0 <= level && level <= 4); + if (level == 0) + return ZSTD_splitBlock_fromBorders(blockStart, blockSize, workspace, wkspSize); + return ZSTD_splitBlock_byChunks(blockStart, blockSize, level - 1, workspace, wkspSize); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdmtCompress.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdmtCompress.cs new file mode 100644 index 00000000..2a21c323 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ZstdmtCompress.cs @@ -0,0 +1,1894 @@ +using System.Runtime.CompilerServices; +using static SharpCompress.Compressors.ZStandard.UnsafeHelper; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public static unsafe partial class Methods +{ + private static readonly buffer_s g_nullBuffer = new buffer_s(start: null, capacity: 0); + + private static void ZSTDMT_freeBufferPool(ZSTDMT_bufferPool_s* bufPool) + { + if (bufPool == null) + return; + if (bufPool->buffers != null) + { + uint u; + for (u = 0; u < bufPool->totalBuffers; u++) + { + ZSTD_customFree(bufPool->buffers[u].start, bufPool->cMem); + } + + ZSTD_customFree(bufPool->buffers, bufPool->cMem); + } + + SynchronizationWrapper.Free(&bufPool->poolMutex); + ZSTD_customFree(bufPool, bufPool->cMem); + } + + private static ZSTDMT_bufferPool_s* ZSTDMT_createBufferPool( + uint maxNbBuffers, + ZSTD_customMem cMem + ) + { + ZSTDMT_bufferPool_s* bufPool = (ZSTDMT_bufferPool_s*)ZSTD_customCalloc( + (nuint)sizeof(ZSTDMT_bufferPool_s), + cMem + ); + if (bufPool == null) + return null; + SynchronizationWrapper.Init(&bufPool->poolMutex); + bufPool->buffers = (buffer_s*)ZSTD_customCalloc( + maxNbBuffers * (uint)sizeof(buffer_s), + cMem + ); + if (bufPool->buffers == null) + { + ZSTDMT_freeBufferPool(bufPool); + return null; + } + + bufPool->bufferSize = 64 * (1 << 10); + bufPool->totalBuffers = maxNbBuffers; + bufPool->nbBuffers = 0; + bufPool->cMem = cMem; + return bufPool; + } + + /* only works at initialization, not during compression */ + private static nuint ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool_s* bufPool) + { + nuint poolSize = (nuint)sizeof(ZSTDMT_bufferPool_s); + nuint arraySize = bufPool->totalBuffers * (uint)sizeof(buffer_s); + uint u; + nuint totalBufferSize = 0; + SynchronizationWrapper.Enter(&bufPool->poolMutex); + for (u = 0; u < bufPool->totalBuffers; u++) + totalBufferSize += bufPool->buffers[u].capacity; + SynchronizationWrapper.Exit(&bufPool->poolMutex); + return poolSize + arraySize + totalBufferSize; + } + + /* ZSTDMT_setBufferSize() : + * all future buffers provided by this buffer pool will have _at least_ this size + * note : it's better for all buffers to have same size, + * as they become freely interchangeable, reducing malloc/free usages and memory fragmentation */ + private static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool_s* bufPool, nuint bSize) + { + SynchronizationWrapper.Enter(&bufPool->poolMutex); + bufPool->bufferSize = bSize; + SynchronizationWrapper.Exit(&bufPool->poolMutex); + } + + private static ZSTDMT_bufferPool_s* ZSTDMT_expandBufferPool( + ZSTDMT_bufferPool_s* srcBufPool, + uint maxNbBuffers + ) + { + if (srcBufPool == null) + return null; + if (srcBufPool->totalBuffers >= maxNbBuffers) + return srcBufPool; + { + ZSTD_customMem cMem = srcBufPool->cMem; + /* forward parameters */ + nuint bSize = srcBufPool->bufferSize; + ZSTDMT_bufferPool_s* newBufPool; + ZSTDMT_freeBufferPool(srcBufPool); + newBufPool = ZSTDMT_createBufferPool(maxNbBuffers, cMem); + if (newBufPool == null) + return newBufPool; + ZSTDMT_setBufferSize(newBufPool, bSize); + return newBufPool; + } + } + + /** ZSTDMT_getBuffer() : + * assumption : bufPool must be valid + * @return : a buffer, with start pointer and size + * note: allocation may fail, in this case, start==NULL and size==0 */ + private static buffer_s ZSTDMT_getBuffer(ZSTDMT_bufferPool_s* bufPool) + { + nuint bSize = bufPool->bufferSize; + SynchronizationWrapper.Enter(&bufPool->poolMutex); + if (bufPool->nbBuffers != 0) + { + buffer_s buf = bufPool->buffers[--bufPool->nbBuffers]; + nuint availBufferSize = buf.capacity; + bufPool->buffers[bufPool->nbBuffers] = g_nullBuffer; + if (availBufferSize >= bSize && availBufferSize >> 3 <= bSize) + { + SynchronizationWrapper.Exit(&bufPool->poolMutex); + return buf; + } + + ZSTD_customFree(buf.start, bufPool->cMem); + } + + SynchronizationWrapper.Exit(&bufPool->poolMutex); + { + buffer_s buffer; + void* start = ZSTD_customMalloc(bSize, bufPool->cMem); + buffer.start = start; + buffer.capacity = start == null ? 0 : bSize; + return buffer; + } + } + + /* store buffer for later re-use, up to pool capacity */ + private static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool_s* bufPool, buffer_s buf) + { + if (buf.start == null) + return; + SynchronizationWrapper.Enter(&bufPool->poolMutex); + if (bufPool->nbBuffers < bufPool->totalBuffers) + { + bufPool->buffers[bufPool->nbBuffers++] = buf; + SynchronizationWrapper.Exit(&bufPool->poolMutex); + return; + } + + SynchronizationWrapper.Exit(&bufPool->poolMutex); + ZSTD_customFree(buf.start, bufPool->cMem); + } + + private static nuint ZSTDMT_sizeof_seqPool(ZSTDMT_bufferPool_s* seqPool) + { + return ZSTDMT_sizeof_bufferPool(seqPool); + } + + private static RawSeqStore_t bufferToSeq(buffer_s buffer) + { + RawSeqStore_t seq = kNullRawSeqStore; + seq.seq = (rawSeq*)buffer.start; + seq.capacity = buffer.capacity / (nuint)sizeof(rawSeq); + return seq; + } + + private static buffer_s seqToBuffer(RawSeqStore_t seq) + { + buffer_s buffer; + buffer.start = seq.seq; + buffer.capacity = seq.capacity * (nuint)sizeof(rawSeq); + return buffer; + } + + private static RawSeqStore_t ZSTDMT_getSeq(ZSTDMT_bufferPool_s* seqPool) + { + if (seqPool->bufferSize == 0) + { + return kNullRawSeqStore; + } + + return bufferToSeq(ZSTDMT_getBuffer(seqPool)); + } + + private static void ZSTDMT_releaseSeq(ZSTDMT_bufferPool_s* seqPool, RawSeqStore_t seq) + { + ZSTDMT_releaseBuffer(seqPool, seqToBuffer(seq)); + } + + private static void ZSTDMT_setNbSeq(ZSTDMT_bufferPool_s* seqPool, nuint nbSeq) + { + ZSTDMT_setBufferSize(seqPool, nbSeq * (nuint)sizeof(rawSeq)); + } + + private static ZSTDMT_bufferPool_s* ZSTDMT_createSeqPool(uint nbWorkers, ZSTD_customMem cMem) + { + ZSTDMT_bufferPool_s* seqPool = ZSTDMT_createBufferPool(nbWorkers, cMem); + if (seqPool == null) + return null; + ZSTDMT_setNbSeq(seqPool, 0); + return seqPool; + } + + private static void ZSTDMT_freeSeqPool(ZSTDMT_bufferPool_s* seqPool) + { + ZSTDMT_freeBufferPool(seqPool); + } + + private static ZSTDMT_bufferPool_s* ZSTDMT_expandSeqPool( + ZSTDMT_bufferPool_s* pool, + uint nbWorkers + ) + { + return ZSTDMT_expandBufferPool(pool, nbWorkers); + } + + /* note : all CCtx borrowed from the pool must be reverted back to the pool _before_ freeing the pool */ + private static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool) + { + if (pool == null) + return; + SynchronizationWrapper.Free(&pool->poolMutex); + if (pool->cctxs != null) + { + int cid; + for (cid = 0; cid < pool->totalCCtx; cid++) + ZSTD_freeCCtx(pool->cctxs[cid]); + ZSTD_customFree(pool->cctxs, pool->cMem); + } + + ZSTD_customFree(pool, pool->cMem); + } + + /* ZSTDMT_createCCtxPool() : + * implies nbWorkers >= 1 , checked by caller ZSTDMT_createCCtx() */ + private static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(int nbWorkers, ZSTD_customMem cMem) + { + ZSTDMT_CCtxPool* cctxPool = (ZSTDMT_CCtxPool*)ZSTD_customCalloc( + (nuint)sizeof(ZSTDMT_CCtxPool), + cMem + ); + assert(nbWorkers > 0); + if (cctxPool == null) + return null; + SynchronizationWrapper.Init(&cctxPool->poolMutex); + cctxPool->totalCCtx = nbWorkers; + cctxPool->cctxs = (ZSTD_CCtx_s**)ZSTD_customCalloc( + (nuint)(nbWorkers * sizeof(ZSTD_CCtx_s*)), + cMem + ); + if (cctxPool->cctxs == null) + { + ZSTDMT_freeCCtxPool(cctxPool); + return null; + } + + cctxPool->cMem = cMem; + cctxPool->cctxs[0] = ZSTD_createCCtx_advanced(cMem); + if (cctxPool->cctxs[0] == null) + { + ZSTDMT_freeCCtxPool(cctxPool); + return null; + } + + cctxPool->availCCtx = 1; + return cctxPool; + } + + private static ZSTDMT_CCtxPool* ZSTDMT_expandCCtxPool(ZSTDMT_CCtxPool* srcPool, int nbWorkers) + { + if (srcPool == null) + return null; + if (nbWorkers <= srcPool->totalCCtx) + return srcPool; + { + ZSTD_customMem cMem = srcPool->cMem; + ZSTDMT_freeCCtxPool(srcPool); + return ZSTDMT_createCCtxPool(nbWorkers, cMem); + } + } + + /* only works during initialization phase, not during compression */ + private static nuint ZSTDMT_sizeof_CCtxPool(ZSTDMT_CCtxPool* cctxPool) + { + SynchronizationWrapper.Enter(&cctxPool->poolMutex); + { + uint nbWorkers = (uint)cctxPool->totalCCtx; + nuint poolSize = (nuint)sizeof(ZSTDMT_CCtxPool); + nuint arraySize = (nuint)(cctxPool->totalCCtx * sizeof(ZSTD_CCtx_s*)); + nuint totalCCtxSize = 0; + uint u; + for (u = 0; u < nbWorkers; u++) + { + totalCCtxSize += ZSTD_sizeof_CCtx(cctxPool->cctxs[u]); + } + + SynchronizationWrapper.Exit(&cctxPool->poolMutex); + assert(nbWorkers > 0); + return poolSize + arraySize + totalCCtxSize; + } + } + + private static ZSTD_CCtx_s* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* cctxPool) + { + SynchronizationWrapper.Enter(&cctxPool->poolMutex); + if (cctxPool->availCCtx != 0) + { + cctxPool->availCCtx--; + { + ZSTD_CCtx_s* cctx = cctxPool->cctxs[cctxPool->availCCtx]; + SynchronizationWrapper.Exit(&cctxPool->poolMutex); + return cctx; + } + } + + SynchronizationWrapper.Exit(&cctxPool->poolMutex); + return ZSTD_createCCtx_advanced(cctxPool->cMem); + } + + private static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx_s* cctx) + { + if (cctx == null) + return; + SynchronizationWrapper.Enter(&pool->poolMutex); + if (pool->availCCtx < pool->totalCCtx) + pool->cctxs[pool->availCCtx++] = cctx; + else + { + ZSTD_freeCCtx(cctx); + } + + SynchronizationWrapper.Exit(&pool->poolMutex); + } + + private static int ZSTDMT_serialState_reset( + SerialState* serialState, + ZSTDMT_bufferPool_s* seqPool, + ZSTD_CCtx_params_s @params, + nuint jobSize, + void* dict, + nuint dictSize, + ZSTD_dictContentType_e dictContentType + ) + { + if (@params.ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + ZSTD_ldm_adjustParameters(&@params.ldmParams, &@params.cParams); + assert(@params.ldmParams.hashLog >= @params.ldmParams.bucketSizeLog); + assert(@params.ldmParams.hashRateLog < 32); + } + else + { + @params.ldmParams = new ldmParams_t(); + } + + serialState->nextJobID = 0; + if (@params.fParams.checksumFlag != 0) + ZSTD_XXH64_reset(&serialState->xxhState, 0); + if (@params.ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + ZSTD_customMem cMem = @params.customMem; + uint hashLog = @params.ldmParams.hashLog; + nuint hashSize = ((nuint)1 << (int)hashLog) * (nuint)sizeof(ldmEntry_t); + uint bucketLog = @params.ldmParams.hashLog - @params.ldmParams.bucketSizeLog; + uint prevBucketLog = + serialState->@params.ldmParams.hashLog + - serialState->@params.ldmParams.bucketSizeLog; + nuint numBuckets = (nuint)1 << (int)bucketLog; + ZSTDMT_setNbSeq(seqPool, ZSTD_ldm_getMaxNbSeq(@params.ldmParams, jobSize)); + ZSTD_window_init(&serialState->ldmState.window); + if ( + serialState->ldmState.hashTable == null + || serialState->@params.ldmParams.hashLog < hashLog + ) + { + ZSTD_customFree(serialState->ldmState.hashTable, cMem); + serialState->ldmState.hashTable = (ldmEntry_t*)ZSTD_customMalloc(hashSize, cMem); + } + + if (serialState->ldmState.bucketOffsets == null || prevBucketLog < bucketLog) + { + ZSTD_customFree(serialState->ldmState.bucketOffsets, cMem); + serialState->ldmState.bucketOffsets = (byte*)ZSTD_customMalloc(numBuckets, cMem); + } + + if ( + serialState->ldmState.hashTable == null + || serialState->ldmState.bucketOffsets == null + ) + return 1; + memset(serialState->ldmState.hashTable, 0, (uint)hashSize); + memset(serialState->ldmState.bucketOffsets, 0, (uint)numBuckets); + serialState->ldmState.loadedDictEnd = 0; + if (dictSize > 0) + { + if (dictContentType == ZSTD_dictContentType_e.ZSTD_dct_rawContent) + { + byte* dictEnd = (byte*)dict + dictSize; + ZSTD_window_update(&serialState->ldmState.window, dict, dictSize, 0); + ZSTD_ldm_fillHashTable( + &serialState->ldmState, + (byte*)dict, + dictEnd, + &@params.ldmParams + ); + serialState->ldmState.loadedDictEnd = + @params.forceWindow != 0 + ? 0 + : (uint)(dictEnd - serialState->ldmState.window.@base); + } + } + + serialState->ldmWindow = serialState->ldmState.window; + } + + serialState->@params = @params; + serialState->@params.jobSize = (uint)jobSize; + return 0; + } + + private static int ZSTDMT_serialState_init(SerialState* serialState) + { + int initError = 0; + *serialState = new SerialState(); + SynchronizationWrapper.Init(&serialState->mutex); + initError |= 0; + initError |= 0; + SynchronizationWrapper.Init(&serialState->ldmWindowMutex); + initError |= 0; + initError |= 0; + return initError; + } + + private static void ZSTDMT_serialState_free(SerialState* serialState) + { + ZSTD_customMem cMem = serialState->@params.customMem; + SynchronizationWrapper.Free(&serialState->mutex); + SynchronizationWrapper.Free(&serialState->ldmWindowMutex); + ZSTD_customFree(serialState->ldmState.hashTable, cMem); + ZSTD_customFree(serialState->ldmState.bucketOffsets, cMem); + } + + private static void ZSTDMT_serialState_genSequences( + SerialState* serialState, + RawSeqStore_t* seqStore, + Range src, + uint jobID + ) + { + SynchronizationWrapper.Enter(&serialState->mutex); + while (serialState->nextJobID < jobID) + { + SynchronizationWrapper.Wait(&serialState->mutex); + } + + if (serialState->nextJobID == jobID) + { + if (serialState->@params.ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + nuint error; + assert( + seqStore->seq != null + && seqStore->pos == 0 + && seqStore->size == 0 + && seqStore->capacity > 0 + ); + assert(src.size <= serialState->@params.jobSize); + ZSTD_window_update(&serialState->ldmState.window, src.start, src.size, 0); + error = ZSTD_ldm_generateSequences( + &serialState->ldmState, + seqStore, + &serialState->@params.ldmParams, + src.start, + src.size + ); + assert(!ERR_isError(error)); + SynchronizationWrapper.Enter(&serialState->ldmWindowMutex); + serialState->ldmWindow = serialState->ldmState.window; + SynchronizationWrapper.Pulse(&serialState->ldmWindowMutex); + SynchronizationWrapper.Exit(&serialState->ldmWindowMutex); + } + + if (serialState->@params.fParams.checksumFlag != 0 && src.size > 0) + ZSTD_XXH64_update(&serialState->xxhState, src.start, src.size); + } + + serialState->nextJobID++; + SynchronizationWrapper.PulseAll(&serialState->mutex); + SynchronizationWrapper.Exit(&serialState->mutex); + } + + private static void ZSTDMT_serialState_applySequences( + SerialState* serialState, + ZSTD_CCtx_s* jobCCtx, + RawSeqStore_t* seqStore + ) + { + if (seqStore->size > 0) + { + assert(serialState->@params.ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable); + assert(jobCCtx != null); + ZSTD_referenceExternalSequences(jobCCtx, seqStore->seq, seqStore->size); + } + } + + private static void ZSTDMT_serialState_ensureFinished( + SerialState* serialState, + uint jobID, + nuint cSize + ) + { + SynchronizationWrapper.Enter(&serialState->mutex); + if (serialState->nextJobID <= jobID) + { + assert(ERR_isError(cSize)); + serialState->nextJobID = jobID + 1; + SynchronizationWrapper.PulseAll(&serialState->mutex); + SynchronizationWrapper.Enter(&serialState->ldmWindowMutex); + ZSTD_window_clear(&serialState->ldmWindow); + SynchronizationWrapper.Pulse(&serialState->ldmWindowMutex); + SynchronizationWrapper.Exit(&serialState->ldmWindowMutex); + } + + SynchronizationWrapper.Exit(&serialState->mutex); + } + + private static readonly Range kNullRange = new Range(start: null, size: 0); + + /* ZSTDMT_compressionJob() is a POOL_function type */ + private static void ZSTDMT_compressionJob(void* jobDescription) + { + ZSTDMT_jobDescription* job = (ZSTDMT_jobDescription*)jobDescription; + /* do not modify job->params ! copy it, modify the copy */ + ZSTD_CCtx_params_s jobParams = job->@params; + ZSTD_CCtx_s* cctx = ZSTDMT_getCCtx(job->cctxPool); + RawSeqStore_t rawSeqStore = ZSTDMT_getSeq(job->seqPool); + buffer_s dstBuff = job->dstBuff; + nuint lastCBlockSize = 0; + if (cctx == null) + { + SynchronizationWrapper.Enter(&job->job_mutex); + job->cSize = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + SynchronizationWrapper.Exit(&job->job_mutex); + goto _endJob; + } + + if (dstBuff.start == null) + { + dstBuff = ZSTDMT_getBuffer(job->bufPool); + if (dstBuff.start == null) + { + SynchronizationWrapper.Enter(&job->job_mutex); + job->cSize = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + SynchronizationWrapper.Exit(&job->job_mutex); + goto _endJob; + } + + job->dstBuff = dstBuff; + } + + if ( + jobParams.ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable + && rawSeqStore.seq == null + ) + { + SynchronizationWrapper.Enter(&job->job_mutex); + job->cSize = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + SynchronizationWrapper.Exit(&job->job_mutex); + goto _endJob; + } + + if (job->jobID != 0) + jobParams.fParams.checksumFlag = 0; + jobParams.ldmParams.enableLdm = ZSTD_paramSwitch_e.ZSTD_ps_disable; + jobParams.nbWorkers = 0; + ZSTDMT_serialState_genSequences(job->serial, &rawSeqStore, job->src, job->jobID); + if (job->cdict != null) + { + nuint initError = ZSTD_compressBegin_advanced_internal( + cctx, + null, + 0, + ZSTD_dictContentType_e.ZSTD_dct_auto, + ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast, + job->cdict, + &jobParams, + job->fullFrameSize + ); + assert(job->firstJob != 0); + if (ERR_isError(initError)) + { + SynchronizationWrapper.Enter(&job->job_mutex); + job->cSize = initError; + SynchronizationWrapper.Exit(&job->job_mutex); + goto _endJob; + } + } + else + { + ulong pledgedSrcSize = job->firstJob != 0 ? job->fullFrameSize : job->src.size; + { + nuint forceWindowError = ZSTD_CCtxParams_setParameter( + &jobParams, + ZSTD_cParameter.ZSTD_c_experimentalParam3, + job->firstJob == 0 ? 1 : 0 + ); + if (ERR_isError(forceWindowError)) + { + SynchronizationWrapper.Enter(&job->job_mutex); + job->cSize = forceWindowError; + SynchronizationWrapper.Exit(&job->job_mutex); + goto _endJob; + } + } + + if (job->firstJob == 0) + { + nuint err = ZSTD_CCtxParams_setParameter( + &jobParams, + ZSTD_cParameter.ZSTD_c_experimentalParam15, + 0 + ); + if (ERR_isError(err)) + { + SynchronizationWrapper.Enter(&job->job_mutex); + job->cSize = err; + SynchronizationWrapper.Exit(&job->job_mutex); + goto _endJob; + } + } + + { + nuint initError = ZSTD_compressBegin_advanced_internal( + cctx, + job->prefix.start, + job->prefix.size, + ZSTD_dictContentType_e.ZSTD_dct_rawContent, + ZSTD_dictTableLoadMethod_e.ZSTD_dtlm_fast, + null, + &jobParams, + pledgedSrcSize + ); + if (ERR_isError(initError)) + { + SynchronizationWrapper.Enter(&job->job_mutex); + job->cSize = initError; + SynchronizationWrapper.Exit(&job->job_mutex); + goto _endJob; + } + } + } + + ZSTDMT_serialState_applySequences(job->serial, cctx, &rawSeqStore); + if (job->firstJob == 0) + { + nuint hSize = ZSTD_compressContinue_public( + cctx, + dstBuff.start, + dstBuff.capacity, + job->src.start, + 0 + ); + if (ERR_isError(hSize)) + { + SynchronizationWrapper.Enter(&job->job_mutex); + job->cSize = hSize; + SynchronizationWrapper.Exit(&job->job_mutex); + goto _endJob; + } + + ZSTD_invalidateRepCodes(cctx); + } + + { + const nuint chunkSize = 4 * (1 << 17); + int nbChunks = (int)((job->src.size + (chunkSize - 1)) / chunkSize); + byte* ip = (byte*)job->src.start; + byte* ostart = (byte*)dstBuff.start; + byte* op = ostart; + byte* oend = op + dstBuff.capacity; + int chunkNb; +#if DEBUG + if (sizeof(nuint) > sizeof(int)) + assert(job->src.size < unchecked(2147483647 * chunkSize)); +#endif + assert(job->cSize == 0); + for (chunkNb = 1; chunkNb < nbChunks; chunkNb++) + { + nuint cSize = ZSTD_compressContinue_public( + cctx, + op, + (nuint)(oend - op), + ip, + chunkSize + ); + if (ERR_isError(cSize)) + { + SynchronizationWrapper.Enter(&job->job_mutex); + job->cSize = cSize; + SynchronizationWrapper.Exit(&job->job_mutex); + goto _endJob; + } + + ip += chunkSize; + op += cSize; + assert(op < oend); + SynchronizationWrapper.Enter(&job->job_mutex); + job->cSize += cSize; + job->consumed = chunkSize * (nuint)chunkNb; + SynchronizationWrapper.Pulse(&job->job_mutex); + SynchronizationWrapper.Exit(&job->job_mutex); + } + + assert(chunkSize > 0); + assert((chunkSize & chunkSize - 1) == 0); + if (((uint)(nbChunks > 0 ? 1 : 0) | job->lastJob) != 0) + { + nuint lastBlockSize1 = job->src.size & chunkSize - 1; + nuint lastBlockSize = + lastBlockSize1 == 0 && job->src.size >= chunkSize ? chunkSize : lastBlockSize1; + nuint cSize = + job->lastJob != 0 + ? ZSTD_compressEnd_public(cctx, op, (nuint)(oend - op), ip, lastBlockSize) + : ZSTD_compressContinue_public( + cctx, + op, + (nuint)(oend - op), + ip, + lastBlockSize + ); + if (ERR_isError(cSize)) + { + SynchronizationWrapper.Enter(&job->job_mutex); + job->cSize = cSize; + SynchronizationWrapper.Exit(&job->job_mutex); + goto _endJob; + } + + lastCBlockSize = cSize; + } + } + +#if DEBUG + if (job->firstJob == 0) + { + assert(ZSTD_window_hasExtDict(cctx->blockState.matchState.window) == 0); + } +#endif + + ZSTD_CCtx_trace(cctx, 0); + _endJob: + ZSTDMT_serialState_ensureFinished(job->serial, job->jobID, job->cSize); + ZSTDMT_releaseSeq(job->seqPool, rawSeqStore); + ZSTDMT_releaseCCtx(job->cctxPool, cctx); + SynchronizationWrapper.Enter(&job->job_mutex); + if (ERR_isError(job->cSize)) + assert(lastCBlockSize == 0); + job->cSize += lastCBlockSize; + job->consumed = job->src.size; + SynchronizationWrapper.Pulse(&job->job_mutex); + SynchronizationWrapper.Exit(&job->job_mutex); + } + + private static readonly RoundBuff_t kNullRoundBuff = new RoundBuff_t( + buffer: null, + capacity: 0, + pos: 0 + ); + + private static void ZSTDMT_freeJobsTable( + ZSTDMT_jobDescription* jobTable, + uint nbJobs, + ZSTD_customMem cMem + ) + { + uint jobNb; + if (jobTable == null) + return; + for (jobNb = 0; jobNb < nbJobs; jobNb++) + { + SynchronizationWrapper.Free(&jobTable[jobNb].job_mutex); + } + + ZSTD_customFree(jobTable, cMem); + } + + /* ZSTDMT_allocJobsTable() + * allocate and init a job table. + * update *nbJobsPtr to next power of 2 value, as size of table */ + private static ZSTDMT_jobDescription* ZSTDMT_createJobsTable( + uint* nbJobsPtr, + ZSTD_customMem cMem + ) + { + uint nbJobsLog2 = ZSTD_highbit32(*nbJobsPtr) + 1; + uint nbJobs = (uint)(1 << (int)nbJobsLog2); + uint jobNb; + ZSTDMT_jobDescription* jobTable = (ZSTDMT_jobDescription*)ZSTD_customCalloc( + nbJobs * (uint)sizeof(ZSTDMT_jobDescription), + cMem + ); + int initError = 0; + if (jobTable == null) + return null; + *nbJobsPtr = nbJobs; + for (jobNb = 0; jobNb < nbJobs; jobNb++) + { + SynchronizationWrapper.Init(&jobTable[jobNb].job_mutex); + initError |= 0; + initError |= 0; + } + + if (initError != 0) + { + ZSTDMT_freeJobsTable(jobTable, nbJobs, cMem); + return null; + } + + return jobTable; + } + + private static nuint ZSTDMT_expandJobsTable(ZSTDMT_CCtx_s* mtctx, uint nbWorkers) + { + uint nbJobs = nbWorkers + 2; + if (nbJobs > mtctx->jobIDMask + 1) + { + ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask + 1, mtctx->cMem); + mtctx->jobIDMask = 0; + mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, mtctx->cMem); + if (mtctx->jobs == null) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + assert(nbJobs != 0 && (nbJobs & nbJobs - 1) == 0); + mtctx->jobIDMask = nbJobs - 1; + } + + return 0; + } + + /* ZSTDMT_CCtxParam_setNbWorkers(): + * Internal use only */ + private static nuint ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params_s* @params, uint nbWorkers) + { + return ZSTD_CCtxParams_setParameter( + @params, + ZSTD_cParameter.ZSTD_c_nbWorkers, + (int)nbWorkers + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ZSTDMT_CCtx_s* ZSTDMT_createCCtx_advanced_internal( + uint nbWorkers, + ZSTD_customMem cMem, + void* pool + ) + { + ZSTDMT_CCtx_s* mtctx; + uint nbJobs = nbWorkers + 2; + int initError; + if (nbWorkers < 1) + return null; + nbWorkers = + nbWorkers < (uint)(sizeof(void*) == 4 ? 64 : 256) + ? nbWorkers + : (uint)(sizeof(void*) == 4 ? 64 : 256); + if (((cMem.customAlloc != null ? 1 : 0) ^ (cMem.customFree != null ? 1 : 0)) != 0) + return null; + mtctx = (ZSTDMT_CCtx_s*)ZSTD_customCalloc((nuint)sizeof(ZSTDMT_CCtx_s), cMem); + if (mtctx == null) + return null; + ZSTDMT_CCtxParam_setNbWorkers(&mtctx->@params, nbWorkers); + mtctx->cMem = cMem; + mtctx->allJobsCompleted = 1; + if (pool != null) + { + mtctx->factory = pool; + mtctx->providedFactory = 1; + } + else + { + mtctx->factory = POOL_create_advanced(nbWorkers, 0, cMem); + mtctx->providedFactory = 0; + } + + mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, cMem); + assert(nbJobs > 0); + assert((nbJobs & nbJobs - 1) == 0); + mtctx->jobIDMask = nbJobs - 1; + mtctx->bufPool = ZSTDMT_createBufferPool(2 * nbWorkers + 3, cMem); + mtctx->cctxPool = ZSTDMT_createCCtxPool((int)nbWorkers, cMem); + mtctx->seqPool = ZSTDMT_createSeqPool(nbWorkers, cMem); + initError = ZSTDMT_serialState_init(&mtctx->serial); + mtctx->roundBuff = kNullRoundBuff; + if ( + ( + ( + mtctx->factory == null + || mtctx->jobs == null + || mtctx->bufPool == null + || mtctx->cctxPool == null + || mtctx->seqPool == null + ? 1 + : 0 + ) | initError + ) != 0 + ) + { + ZSTDMT_freeCCtx(mtctx); + return null; + } + + return mtctx; + } + + /* Requires ZSTD_MULTITHREAD to be defined during compilation, otherwise it will return NULL. */ + private static ZSTDMT_CCtx_s* ZSTDMT_createCCtx_advanced( + uint nbWorkers, + ZSTD_customMem cMem, + void* pool + ) + { + return ZSTDMT_createCCtx_advanced_internal(nbWorkers, cMem, pool); + } + + /* ZSTDMT_releaseAllJobResources() : + * note : ensure all workers are killed first ! */ + private static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx_s* mtctx) + { + uint jobID; + for (jobID = 0; jobID <= mtctx->jobIDMask; jobID++) + { + /* Copy the mutex/cond out */ + void* mutex = mtctx->jobs[jobID].job_mutex; + void* cond = mtctx->jobs[jobID].job_cond; + ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff); + mtctx->jobs[jobID] = new ZSTDMT_jobDescription { job_mutex = mutex, job_cond = cond }; + } + + mtctx->inBuff.buffer = g_nullBuffer; + mtctx->inBuff.filled = 0; + mtctx->allJobsCompleted = 1; + } + + private static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx_s* mtctx) + { + while (mtctx->doneJobID < mtctx->nextJobID) + { + uint jobID = mtctx->doneJobID & mtctx->jobIDMask; + SynchronizationWrapper.Enter(&mtctx->jobs[jobID].job_mutex); + while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].src.size) + { + SynchronizationWrapper.Wait(&mtctx->jobs[jobID].job_mutex); + } + + SynchronizationWrapper.Exit(&mtctx->jobs[jobID].job_mutex); + mtctx->doneJobID++; + } + } + + private static nuint ZSTDMT_freeCCtx(ZSTDMT_CCtx_s* mtctx) + { + if (mtctx == null) + return 0; + if (mtctx->providedFactory == 0) + POOL_free(mtctx->factory); + ZSTDMT_releaseAllJobResources(mtctx); + ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask + 1, mtctx->cMem); + ZSTDMT_freeBufferPool(mtctx->bufPool); + ZSTDMT_freeCCtxPool(mtctx->cctxPool); + ZSTDMT_freeSeqPool(mtctx->seqPool); + ZSTDMT_serialState_free(&mtctx->serial); + ZSTD_freeCDict(mtctx->cdictLocal); + if (mtctx->roundBuff.buffer != null) + ZSTD_customFree(mtctx->roundBuff.buffer, mtctx->cMem); + ZSTD_customFree(mtctx, mtctx->cMem); + return 0; + } + + private static nuint ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx_s* mtctx) + { + if (mtctx == null) + return 0; + return (nuint)sizeof(ZSTDMT_CCtx_s) + + POOL_sizeof(mtctx->factory) + + ZSTDMT_sizeof_bufferPool(mtctx->bufPool) + + (mtctx->jobIDMask + 1) * (uint)sizeof(ZSTDMT_jobDescription) + + ZSTDMT_sizeof_CCtxPool(mtctx->cctxPool) + + ZSTDMT_sizeof_seqPool(mtctx->seqPool) + + ZSTD_sizeof_CDict(mtctx->cdictLocal) + + mtctx->roundBuff.capacity; + } + + /* ZSTDMT_resize() : + * @return : error code if fails, 0 on success */ + private static nuint ZSTDMT_resize(ZSTDMT_CCtx_s* mtctx, uint nbWorkers) + { + if (POOL_resize(mtctx->factory, nbWorkers) != 0) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + { + nuint err_code = ZSTDMT_expandJobsTable(mtctx, nbWorkers); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + mtctx->bufPool = ZSTDMT_expandBufferPool(mtctx->bufPool, 2 * nbWorkers + 3); + if (mtctx->bufPool == null) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + mtctx->cctxPool = ZSTDMT_expandCCtxPool(mtctx->cctxPool, (int)nbWorkers); + if (mtctx->cctxPool == null) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + mtctx->seqPool = ZSTDMT_expandSeqPool(mtctx->seqPool, nbWorkers); + if (mtctx->seqPool == null) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + ZSTDMT_CCtxParam_setNbWorkers(&mtctx->@params, nbWorkers); + return 0; + } + + /*! ZSTDMT_updateCParams_whileCompressing() : + * Updates a selected set of compression parameters, remaining compatible with currently active frame. + * New parameters will be applied to next compression job. */ + private static void ZSTDMT_updateCParams_whileCompressing( + ZSTDMT_CCtx_s* mtctx, + ZSTD_CCtx_params_s* cctxParams + ) + { + /* Do not modify windowLog while compressing */ + uint saved_wlog = mtctx->@params.cParams.windowLog; + int compressionLevel = cctxParams->compressionLevel; + mtctx->@params.compressionLevel = compressionLevel; + { + ZSTD_compressionParameters cParams = ZSTD_getCParamsFromCCtxParams( + cctxParams, + unchecked(0UL - 1), + 0, + ZSTD_CParamMode_e.ZSTD_cpm_noAttachDict + ); + cParams.windowLog = saved_wlog; + mtctx->@params.cParams = cParams; + } + } + + /* ZSTDMT_getFrameProgression(): + * tells how much data has been consumed (input) and produced (output) for current frame. + * able to count progression inside worker threads. + * Note : mutex will be acquired during statistics collection inside workers. */ + private static ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx_s* mtctx) + { + ZSTD_frameProgression fps; + fps.ingested = mtctx->consumed + mtctx->inBuff.filled; + fps.consumed = mtctx->consumed; + fps.produced = fps.flushed = mtctx->produced; + fps.currentJobID = mtctx->nextJobID; + fps.nbActiveWorkers = 0; + { + uint jobNb; + uint lastJobNb = mtctx->nextJobID + (uint)mtctx->jobReady; + assert(mtctx->jobReady <= 1); + for (jobNb = mtctx->doneJobID; jobNb < lastJobNb; jobNb++) + { + uint wJobID = jobNb & mtctx->jobIDMask; + ZSTDMT_jobDescription* jobPtr = &mtctx->jobs[wJobID]; + SynchronizationWrapper.Enter(&jobPtr->job_mutex); + { + nuint cResult = jobPtr->cSize; + nuint produced = ERR_isError(cResult) ? 0 : cResult; + nuint flushed = ERR_isError(cResult) ? 0 : jobPtr->dstFlushed; + assert(flushed <= produced); + fps.ingested += jobPtr->src.size; + fps.consumed += jobPtr->consumed; + fps.produced += produced; + fps.flushed += flushed; + fps.nbActiveWorkers += jobPtr->consumed < jobPtr->src.size ? 1U : 0U; + } + + SynchronizationWrapper.Exit(&mtctx->jobs[wJobID].job_mutex); + } + } + + return fps; + } + + /*! ZSTDMT_toFlushNow() + * Tell how many bytes are ready to be flushed immediately. + * Probe the oldest active job (not yet entirely flushed) and check its output buffer. + * If return 0, it means there is no active job, + * or, it means oldest job is still active, but everything produced has been flushed so far, + * therefore flushing is limited by speed of oldest job. */ + private static nuint ZSTDMT_toFlushNow(ZSTDMT_CCtx_s* mtctx) + { + nuint toFlush; + uint jobID = mtctx->doneJobID; + assert(jobID <= mtctx->nextJobID); + if (jobID == mtctx->nextJobID) + return 0; + { + uint wJobID = jobID & mtctx->jobIDMask; + ZSTDMT_jobDescription* jobPtr = &mtctx->jobs[wJobID]; + SynchronizationWrapper.Enter(&jobPtr->job_mutex); + { + nuint cResult = jobPtr->cSize; + nuint produced = ERR_isError(cResult) ? 0 : cResult; + nuint flushed = ERR_isError(cResult) ? 0 : jobPtr->dstFlushed; + assert(flushed <= produced); + assert(jobPtr->consumed <= jobPtr->src.size); + toFlush = produced - flushed; +#if DEBUG + if (toFlush == 0) + { + assert(jobPtr->consumed < jobPtr->src.size); + } +#endif + } + + SynchronizationWrapper.Exit(&mtctx->jobs[wJobID].job_mutex); + } + + return toFlush; + } + + /* ------------------------------------------ */ + /* ===== Multi-threaded compression ===== */ + /* ------------------------------------------ */ + private static uint ZSTDMT_computeTargetJobLog(ZSTD_CCtx_params_s* @params) + { + uint jobLog; + if (@params->ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + jobLog = + 21 > ZSTD_cycleLog(@params->cParams.chainLog, @params->cParams.strategy) + 3 + ? 21 + : ZSTD_cycleLog(@params->cParams.chainLog, @params->cParams.strategy) + 3; + } + else + { + jobLog = 20 > @params->cParams.windowLog + 2 ? 20 : @params->cParams.windowLog + 2; + } + + return jobLog < (uint)(MEM_32bits ? 29 : 30) ? jobLog : (uint)(MEM_32bits ? 29 : 30); + } + + private static int ZSTDMT_overlapLog_default(ZSTD_strategy strat) + { + switch (strat) + { + case ZSTD_strategy.ZSTD_btultra2: + return 9; + case ZSTD_strategy.ZSTD_btultra: + case ZSTD_strategy.ZSTD_btopt: + return 8; + case ZSTD_strategy.ZSTD_btlazy2: + case ZSTD_strategy.ZSTD_lazy2: + return 7; + case ZSTD_strategy.ZSTD_lazy: + case ZSTD_strategy.ZSTD_greedy: + case ZSTD_strategy.ZSTD_dfast: + case ZSTD_strategy.ZSTD_fast: + default: + break; + } + + return 6; + } + + private static int ZSTDMT_overlapLog(int ovlog, ZSTD_strategy strat) + { + assert(0 <= ovlog && ovlog <= 9); + if (ovlog == 0) + return ZSTDMT_overlapLog_default(strat); + return ovlog; + } + + private static nuint ZSTDMT_computeOverlapSize(ZSTD_CCtx_params_s* @params) + { + int overlapRLog = 9 - ZSTDMT_overlapLog(@params->overlapLog, @params->cParams.strategy); + int ovLog = (int)(overlapRLog >= 8 ? 0 : @params->cParams.windowLog - (uint)overlapRLog); + assert(0 <= overlapRLog && overlapRLog <= 8); + if (@params->ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + ovLog = (int)( + ( + @params->cParams.windowLog < ZSTDMT_computeTargetJobLog(@params) - 2 + ? @params->cParams.windowLog + : ZSTDMT_computeTargetJobLog(@params) - 2 + ) - (uint)overlapRLog + ); + } + + assert(0 <= ovLog && ovLog <= (sizeof(nuint) == 4 ? 30 : 31)); + return ovLog == 0 ? 0 : (nuint)1 << ovLog; + } + + /* ====================================== */ + /* ======= Streaming API ======= */ + /* ====================================== */ + private static nuint ZSTDMT_initCStream_internal( + ZSTDMT_CCtx_s* mtctx, + void* dict, + nuint dictSize, + ZSTD_dictContentType_e dictContentType, + ZSTD_CDict_s* cdict, + ZSTD_CCtx_params_s @params, + ulong pledgedSrcSize + ) + { + assert(!ERR_isError(ZSTD_checkCParams(@params.cParams))); + assert(!(dict != null && cdict != null)); + if (@params.nbWorkers != mtctx->@params.nbWorkers) + { + /* init */ + nuint err_code = ZSTDMT_resize(mtctx, (uint)@params.nbWorkers); + if (ERR_isError(err_code)) + { + return err_code; + } + } + + if (@params.jobSize != 0 && @params.jobSize < 512 * (1 << 10)) + @params.jobSize = 512 * (1 << 10); + if (@params.jobSize > (nuint)(MEM_32bits ? 512 * (1 << 20) : 1024 * (1 << 20))) + @params.jobSize = (nuint)(MEM_32bits ? 512 * (1 << 20) : 1024 * (1 << 20)); + if (mtctx->allJobsCompleted == 0) + { + ZSTDMT_waitForAllJobsCompleted(mtctx); + ZSTDMT_releaseAllJobResources(mtctx); + mtctx->allJobsCompleted = 1; + } + + mtctx->@params = @params; + mtctx->frameContentSize = pledgedSrcSize; + ZSTD_freeCDict(mtctx->cdictLocal); + if (dict != null) + { + mtctx->cdictLocal = ZSTD_createCDict_advanced( + dict, + dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byCopy, + dictContentType, + @params.cParams, + mtctx->cMem + ); + mtctx->cdict = mtctx->cdictLocal; + if (mtctx->cdictLocal == null) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + else + { + mtctx->cdictLocal = null; + mtctx->cdict = cdict; + } + + mtctx->targetPrefixSize = ZSTDMT_computeOverlapSize(&@params); + mtctx->targetSectionSize = @params.jobSize; + if (mtctx->targetSectionSize == 0) + { + mtctx->targetSectionSize = (nuint)(1UL << (int)ZSTDMT_computeTargetJobLog(&@params)); + } + + assert( + mtctx->targetSectionSize <= (nuint)(MEM_32bits ? 512 * (1 << 20) : 1024 * (1 << 20)) + ); + if (@params.rsyncable != 0) + { + /* Aim for the targetsectionSize as the average job size. */ + uint jobSizeKB = (uint)(mtctx->targetSectionSize >> 10); + assert(jobSizeKB >= 1); + uint rsyncBits = ZSTD_highbit32(jobSizeKB) + 10; + assert(rsyncBits >= 17 + 2); + mtctx->rsync.hash = 0; + mtctx->rsync.hitMask = (1UL << (int)rsyncBits) - 1; + mtctx->rsync.primePower = ZSTD_rollingHash_primePower(32); + } + + if (mtctx->targetSectionSize < mtctx->targetPrefixSize) + mtctx->targetSectionSize = mtctx->targetPrefixSize; + ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(mtctx->targetSectionSize)); + { + /* If ldm is enabled we need windowSize space. */ + nuint windowSize = + mtctx->@params.ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable + ? 1U << (int)mtctx->@params.cParams.windowLog + : 0; + /* Two buffers of slack, plus extra space for the overlap + * This is the minimum slack that LDM works with. One extra because + * flush might waste up to targetSectionSize-1 bytes. Another extra + * for the overlap (if > 0), then one to fill which doesn't overlap + * with the LDM window. + */ + nuint nbSlackBuffers = (nuint)(2 + (mtctx->targetPrefixSize > 0 ? 1 : 0)); + nuint slackSize = mtctx->targetSectionSize * nbSlackBuffers; + /* Compute the total size, and always have enough slack */ + nuint nbWorkers = (nuint)(mtctx->@params.nbWorkers > 1 ? mtctx->@params.nbWorkers : 1); + nuint sectionsSize = mtctx->targetSectionSize * nbWorkers; + nuint capacity = (windowSize > sectionsSize ? windowSize : sectionsSize) + slackSize; + if (mtctx->roundBuff.capacity < capacity) + { + if (mtctx->roundBuff.buffer != null) + ZSTD_customFree(mtctx->roundBuff.buffer, mtctx->cMem); + mtctx->roundBuff.buffer = (byte*)ZSTD_customMalloc(capacity, mtctx->cMem); + if (mtctx->roundBuff.buffer == null) + { + mtctx->roundBuff.capacity = 0; + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + + mtctx->roundBuff.capacity = capacity; + } + } + + mtctx->roundBuff.pos = 0; + mtctx->inBuff.buffer = g_nullBuffer; + mtctx->inBuff.filled = 0; + mtctx->inBuff.prefix = kNullRange; + mtctx->doneJobID = 0; + mtctx->nextJobID = 0; + mtctx->frameEnded = 0; + mtctx->allJobsCompleted = 0; + mtctx->consumed = 0; + mtctx->produced = 0; + ZSTD_freeCDict(mtctx->cdictLocal); + mtctx->cdictLocal = null; + mtctx->cdict = null; + if (dict != null) + { + if (dictContentType == ZSTD_dictContentType_e.ZSTD_dct_rawContent) + { + mtctx->inBuff.prefix.start = (byte*)dict; + mtctx->inBuff.prefix.size = dictSize; + } + else + { + mtctx->cdictLocal = ZSTD_createCDict_advanced( + dict, + dictSize, + ZSTD_dictLoadMethod_e.ZSTD_dlm_byRef, + dictContentType, + @params.cParams, + mtctx->cMem + ); + mtctx->cdict = mtctx->cdictLocal; + if (mtctx->cdictLocal == null) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + } + } + else + { + mtctx->cdict = cdict; + } + + if ( + ZSTDMT_serialState_reset( + &mtctx->serial, + mtctx->seqPool, + @params, + mtctx->targetSectionSize, + dict, + dictSize, + dictContentType + ) != 0 + ) + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + return 0; + } + + /* ZSTDMT_writeLastEmptyBlock() + * Write a single empty block with an end-of-frame to finish a frame. + * Job must be created from streaming variant. + * This function is always successful if expected conditions are fulfilled. + */ + private static void ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job) + { + assert(job->lastJob == 1); + assert(job->src.size == 0); + assert(job->firstJob == 0); + assert(job->dstBuff.start == null); + job->dstBuff = ZSTDMT_getBuffer(job->bufPool); + if (job->dstBuff.start == null) + { + job->cSize = unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_memory_allocation)); + return; + } + + assert(job->dstBuff.capacity >= ZSTD_blockHeaderSize); + job->src = kNullRange; + job->cSize = ZSTD_writeLastEmptyBlock(job->dstBuff.start, job->dstBuff.capacity); + assert(!ERR_isError(job->cSize)); + assert(job->consumed == 0); + } + + private static nuint ZSTDMT_createCompressionJob( + ZSTDMT_CCtx_s* mtctx, + nuint srcSize, + ZSTD_EndDirective endOp + ) + { + uint jobID = mtctx->nextJobID & mtctx->jobIDMask; + int endFrame = endOp == ZSTD_EndDirective.ZSTD_e_end ? 1 : 0; + if (mtctx->nextJobID > mtctx->doneJobID + mtctx->jobIDMask) + { + assert((mtctx->nextJobID & mtctx->jobIDMask) == (mtctx->doneJobID & mtctx->jobIDMask)); + return 0; + } + + if (mtctx->jobReady == 0) + { + byte* src = (byte*)mtctx->inBuff.buffer.start; + mtctx->jobs[jobID].src.start = src; + mtctx->jobs[jobID].src.size = srcSize; + assert(mtctx->inBuff.filled >= srcSize); + mtctx->jobs[jobID].prefix = mtctx->inBuff.prefix; + mtctx->jobs[jobID].consumed = 0; + mtctx->jobs[jobID].cSize = 0; + mtctx->jobs[jobID].@params = mtctx->@params; + mtctx->jobs[jobID].cdict = mtctx->nextJobID == 0 ? mtctx->cdict : null; + mtctx->jobs[jobID].fullFrameSize = mtctx->frameContentSize; + mtctx->jobs[jobID].dstBuff = g_nullBuffer; + mtctx->jobs[jobID].cctxPool = mtctx->cctxPool; + mtctx->jobs[jobID].bufPool = mtctx->bufPool; + mtctx->jobs[jobID].seqPool = mtctx->seqPool; + mtctx->jobs[jobID].serial = &mtctx->serial; + mtctx->jobs[jobID].jobID = mtctx->nextJobID; + mtctx->jobs[jobID].firstJob = mtctx->nextJobID == 0 ? 1U : 0U; + mtctx->jobs[jobID].lastJob = (uint)endFrame; + mtctx->jobs[jobID].frameChecksumNeeded = + mtctx->@params.fParams.checksumFlag != 0 && endFrame != 0 && mtctx->nextJobID > 0 + ? 1U + : 0U; + mtctx->jobs[jobID].dstFlushed = 0; + mtctx->roundBuff.pos += srcSize; + mtctx->inBuff.buffer = g_nullBuffer; + mtctx->inBuff.filled = 0; + if (endFrame == 0) + { + nuint newPrefixSize = + srcSize < mtctx->targetPrefixSize ? srcSize : mtctx->targetPrefixSize; + mtctx->inBuff.prefix.start = src + srcSize - newPrefixSize; + mtctx->inBuff.prefix.size = newPrefixSize; + } + else + { + mtctx->inBuff.prefix = kNullRange; + mtctx->frameEnded = (uint)endFrame; + if (mtctx->nextJobID == 0) + { + mtctx->@params.fParams.checksumFlag = 0; + } + } + + if (srcSize == 0 && mtctx->nextJobID > 0) + { + assert(endOp == ZSTD_EndDirective.ZSTD_e_end); + ZSTDMT_writeLastEmptyBlock(mtctx->jobs + jobID); + mtctx->nextJobID++; + return 0; + } + } + + if ( + POOL_tryAdd( + mtctx->factory, + (delegate* managed)(&ZSTDMT_compressionJob), + &mtctx->jobs[jobID] + ) != 0 + ) + { + mtctx->nextJobID++; + mtctx->jobReady = 0; + } + else + { + mtctx->jobReady = 1; + } + + return 0; + } + + /*! ZSTDMT_flushProduced() : + * flush whatever data has been produced but not yet flushed in current job. + * move to next job if current one is fully flushed. + * `output` : `pos` will be updated with amount of data flushed . + * `blockToFlush` : if >0, the function will block and wait if there is no data available to flush . + * @return : amount of data remaining within internal buffer, 0 if no more, 1 if unknown but > 0, or an error code */ + private static nuint ZSTDMT_flushProduced( + ZSTDMT_CCtx_s* mtctx, + ZSTD_outBuffer_s* output, + uint blockToFlush, + ZSTD_EndDirective end + ) + { + uint wJobID = mtctx->doneJobID & mtctx->jobIDMask; + assert(output->size >= output->pos); + SynchronizationWrapper.Enter(&mtctx->jobs[wJobID].job_mutex); + if (blockToFlush != 0 && mtctx->doneJobID < mtctx->nextJobID) + { + assert(mtctx->jobs[wJobID].dstFlushed <= mtctx->jobs[wJobID].cSize); + while (mtctx->jobs[wJobID].dstFlushed == mtctx->jobs[wJobID].cSize) + { + if (mtctx->jobs[wJobID].consumed == mtctx->jobs[wJobID].src.size) + { + break; + } + + SynchronizationWrapper.Wait(&mtctx->jobs[wJobID].job_mutex); + } + } + + { + /* shared */ + nuint cSize = mtctx->jobs[wJobID].cSize; + /* shared */ + nuint srcConsumed = mtctx->jobs[wJobID].consumed; + /* read-only, could be done after mutex lock, but no-declaration-after-statement */ + nuint srcSize = mtctx->jobs[wJobID].src.size; + SynchronizationWrapper.Exit(&mtctx->jobs[wJobID].job_mutex); + if (ERR_isError(cSize)) + { + ZSTDMT_waitForAllJobsCompleted(mtctx); + ZSTDMT_releaseAllJobResources(mtctx); + return cSize; + } + + assert(srcConsumed <= srcSize); + if (srcConsumed == srcSize && mtctx->jobs[wJobID].frameChecksumNeeded != 0) + { + uint checksum = (uint)ZSTD_XXH64_digest(&mtctx->serial.xxhState); + MEM_writeLE32( + (sbyte*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].cSize, + checksum + ); + cSize += 4; + mtctx->jobs[wJobID].cSize += 4; + mtctx->jobs[wJobID].frameChecksumNeeded = 0; + } + + if (cSize > 0) + { + nuint toFlush = + cSize - mtctx->jobs[wJobID].dstFlushed < output->size - output->pos + ? cSize - mtctx->jobs[wJobID].dstFlushed + : output->size - output->pos; + assert(mtctx->doneJobID < mtctx->nextJobID); + assert(cSize >= mtctx->jobs[wJobID].dstFlushed); + assert(mtctx->jobs[wJobID].dstBuff.start != null); + if (toFlush > 0) + { + memcpy( + (sbyte*)output->dst + output->pos, + (sbyte*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed, + (uint)toFlush + ); + } + + output->pos += toFlush; + mtctx->jobs[wJobID].dstFlushed += toFlush; + if (srcConsumed == srcSize && mtctx->jobs[wJobID].dstFlushed == cSize) + { + ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[wJobID].dstBuff); + mtctx->jobs[wJobID].dstBuff = g_nullBuffer; + mtctx->jobs[wJobID].cSize = 0; + mtctx->consumed += srcSize; + mtctx->produced += cSize; + mtctx->doneJobID++; + } + } + + if (cSize > mtctx->jobs[wJobID].dstFlushed) + return cSize - mtctx->jobs[wJobID].dstFlushed; + if (srcSize > srcConsumed) + return 1; + } + + if (mtctx->doneJobID < mtctx->nextJobID) + return 1; + if (mtctx->jobReady != 0) + return 1; + if (mtctx->inBuff.filled > 0) + return 1; + mtctx->allJobsCompleted = mtctx->frameEnded; + if (end == ZSTD_EndDirective.ZSTD_e_end) + return mtctx->frameEnded == 0 ? 1U : 0U; + return 0; + } + + /** + * Returns the range of data used by the earliest job that is not yet complete. + * If the data of the first job is broken up into two segments, we cover both + * sections. + */ + private static Range ZSTDMT_getInputDataInUse(ZSTDMT_CCtx_s* mtctx) + { + uint firstJobID = mtctx->doneJobID; + uint lastJobID = mtctx->nextJobID; + uint jobID; + /* no need to check during first round */ + nuint roundBuffCapacity = mtctx->roundBuff.capacity; + nuint nbJobs1stRoundMin = roundBuffCapacity / mtctx->targetSectionSize; + if (lastJobID < nbJobs1stRoundMin) + return kNullRange; + for (jobID = firstJobID; jobID < lastJobID; ++jobID) + { + uint wJobID = jobID & mtctx->jobIDMask; + nuint consumed; + SynchronizationWrapper.Enter(&mtctx->jobs[wJobID].job_mutex); + consumed = mtctx->jobs[wJobID].consumed; + SynchronizationWrapper.Exit(&mtctx->jobs[wJobID].job_mutex); + if (consumed < mtctx->jobs[wJobID].src.size) + { + Range range = mtctx->jobs[wJobID].prefix; + if (range.size == 0) + { + range = mtctx->jobs[wJobID].src; + } + + assert(range.start <= mtctx->jobs[wJobID].src.start); + return range; + } + } + + return kNullRange; + } + + /** + * Returns non-zero iff buffer and range overlap. + */ + private static int ZSTDMT_isOverlapped(buffer_s buffer, Range range) + { + byte* bufferStart = (byte*)buffer.start; + byte* rangeStart = (byte*)range.start; + if (rangeStart == null || bufferStart == null) + return 0; + { + byte* bufferEnd = bufferStart + buffer.capacity; + byte* rangeEnd = rangeStart + range.size; + if (bufferStart == bufferEnd || rangeStart == rangeEnd) + return 0; + return bufferStart < rangeEnd && rangeStart < bufferEnd ? 1 : 0; + } + } + + private static int ZSTDMT_doesOverlapWindow(buffer_s buffer, ZSTD_window_t window) + { + Range extDict; + Range prefix; + extDict.start = window.dictBase + window.lowLimit; + extDict.size = window.dictLimit - window.lowLimit; + prefix.start = window.@base + window.dictLimit; + prefix.size = (nuint)(window.nextSrc - (window.@base + window.dictLimit)); + return ZSTDMT_isOverlapped(buffer, extDict) != 0 || ZSTDMT_isOverlapped(buffer, prefix) != 0 + ? 1 + : 0; + } + + private static void ZSTDMT_waitForLdmComplete(ZSTDMT_CCtx_s* mtctx, buffer_s buffer) + { + if (mtctx->@params.ldmParams.enableLdm == ZSTD_paramSwitch_e.ZSTD_ps_enable) + { + void** mutex = &mtctx->serial.ldmWindowMutex; + SynchronizationWrapper.Enter(mutex); + while (ZSTDMT_doesOverlapWindow(buffer, mtctx->serial.ldmWindow) != 0) + { + SynchronizationWrapper.Wait(mutex); + } + + SynchronizationWrapper.Exit(mutex); + } + } + + /** + * Attempts to set the inBuff to the next section to fill. + * If any part of the new section is still in use we give up. + * Returns non-zero if the buffer is filled. + */ + private static int ZSTDMT_tryGetInputRange(ZSTDMT_CCtx_s* mtctx) + { + Range inUse = ZSTDMT_getInputDataInUse(mtctx); + nuint spaceLeft = mtctx->roundBuff.capacity - mtctx->roundBuff.pos; + nuint spaceNeeded = mtctx->targetSectionSize; + buffer_s buffer; + assert(mtctx->inBuff.buffer.start == null); + assert(mtctx->roundBuff.capacity >= spaceNeeded); + if (spaceLeft < spaceNeeded) + { + /* ZSTD_invalidateRepCodes() doesn't work for extDict variants. + * Simply copy the prefix to the beginning in that case. + */ + byte* start = mtctx->roundBuff.buffer; + nuint prefixSize = mtctx->inBuff.prefix.size; + buffer.start = start; + buffer.capacity = prefixSize; + if (ZSTDMT_isOverlapped(buffer, inUse) != 0) + { + return 0; + } + + ZSTDMT_waitForLdmComplete(mtctx, buffer); + memmove(start, mtctx->inBuff.prefix.start, prefixSize); + mtctx->inBuff.prefix.start = start; + mtctx->roundBuff.pos = prefixSize; + } + + buffer.start = mtctx->roundBuff.buffer + mtctx->roundBuff.pos; + buffer.capacity = spaceNeeded; + if (ZSTDMT_isOverlapped(buffer, inUse) != 0) + { + return 0; + } + + assert(ZSTDMT_isOverlapped(buffer, mtctx->inBuff.prefix) == 0); + ZSTDMT_waitForLdmComplete(mtctx, buffer); + mtctx->inBuff.buffer = buffer; + mtctx->inBuff.filled = 0; + assert(mtctx->roundBuff.pos + buffer.capacity <= mtctx->roundBuff.capacity); + return 1; + } + + /** + * Searches through the input for a synchronization point. If one is found, we + * will instruct the caller to flush, and return the number of bytes to load. + * Otherwise, we will load as many bytes as possible and instruct the caller + * to continue as normal. + */ + private static SyncPoint findSynchronizationPoint(ZSTDMT_CCtx_s* mtctx, ZSTD_inBuffer_s input) + { + byte* istart = (byte*)input.src + input.pos; + ulong primePower = mtctx->rsync.primePower; + ulong hitMask = mtctx->rsync.hitMask; + SyncPoint syncPoint; + ulong hash; + byte* prev; + nuint pos; + syncPoint.toLoad = + input.size - input.pos < mtctx->targetSectionSize - mtctx->inBuff.filled + ? input.size - input.pos + : mtctx->targetSectionSize - mtctx->inBuff.filled; + syncPoint.flush = 0; + if (mtctx->@params.rsyncable == 0) + return syncPoint; + if (mtctx->inBuff.filled + input.size - input.pos < 1 << 17) + return syncPoint; + if (mtctx->inBuff.filled + syncPoint.toLoad < 32) + return syncPoint; + if (mtctx->inBuff.filled < 1 << 17) + { + pos = (1 << 17) - mtctx->inBuff.filled; + if (pos >= 32) + { + prev = istart + pos - 32; + hash = ZSTD_rollingHash_compute(prev, 32); + } + else + { + assert(mtctx->inBuff.filled >= 32); + prev = (byte*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled - 32; + hash = ZSTD_rollingHash_compute(prev + pos, 32 - pos); + hash = ZSTD_rollingHash_append(hash, istart, pos); + } + } + else + { + assert(mtctx->inBuff.filled >= 1 << 17); + assert(1 << 17 >= 32); + pos = 0; + prev = (byte*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled - 32; + hash = ZSTD_rollingHash_compute(prev, 32); + if ((hash & hitMask) == hitMask) + { + syncPoint.toLoad = 0; + syncPoint.flush = 1; + return syncPoint; + } + } + + assert(pos < 32 || ZSTD_rollingHash_compute(istart + pos - 32, 32) == hash); + for (; pos < syncPoint.toLoad; ++pos) + { + byte toRemove = pos < 32 ? prev[pos] : istart[pos - 32]; + hash = ZSTD_rollingHash_rotate(hash, toRemove, istart[pos], primePower); + assert(mtctx->inBuff.filled + pos >= 1 << 17); + if ((hash & hitMask) == hitMask) + { + syncPoint.toLoad = pos + 1; + syncPoint.flush = 1; + ++pos; + break; + } + } + + assert(pos < 32 || ZSTD_rollingHash_compute(istart + pos - 32, 32) == hash); + return syncPoint; + } + + /* === Streaming functions === */ + private static nuint ZSTDMT_nextInputSizeHint(ZSTDMT_CCtx_s* mtctx) + { + nuint hintInSize = mtctx->targetSectionSize - mtctx->inBuff.filled; + if (hintInSize == 0) + hintInSize = mtctx->targetSectionSize; + return hintInSize; + } + + /** ZSTDMT_compressStream_generic() : + * internal use only - exposed to be invoked from zstd_compress.c + * assumption : output and input are valid (pos <= size) + * @return : minimum amount of data remaining to flush, 0 if none */ + private static nuint ZSTDMT_compressStream_generic( + ZSTDMT_CCtx_s* mtctx, + ZSTD_outBuffer_s* output, + ZSTD_inBuffer_s* input, + ZSTD_EndDirective endOp + ) + { + uint forwardInputProgress = 0; + assert(output->pos <= output->size); + assert(input->pos <= input->size); + if (mtctx->frameEnded != 0 && endOp == ZSTD_EndDirective.ZSTD_e_continue) + { + return unchecked((nuint)(-(int)ZSTD_ErrorCode.ZSTD_error_stage_wrong)); + } + + if (mtctx->jobReady == 0 && input->size > input->pos) + { + if (mtctx->inBuff.buffer.start == null) + { + assert(mtctx->inBuff.filled == 0); + if (ZSTDMT_tryGetInputRange(mtctx) == 0) + { + assert(mtctx->doneJobID != mtctx->nextJobID); + } + } + + if (mtctx->inBuff.buffer.start != null) + { + SyncPoint syncPoint = findSynchronizationPoint(mtctx, *input); + if (syncPoint.flush != 0 && endOp == ZSTD_EndDirective.ZSTD_e_continue) + { + endOp = ZSTD_EndDirective.ZSTD_e_flush; + } + + assert(mtctx->inBuff.buffer.capacity >= mtctx->targetSectionSize); + memcpy( + (sbyte*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, + (sbyte*)input->src + input->pos, + (uint)syncPoint.toLoad + ); + input->pos += syncPoint.toLoad; + mtctx->inBuff.filled += syncPoint.toLoad; + forwardInputProgress = syncPoint.toLoad > 0 ? 1U : 0U; + } + } + + if (input->pos < input->size && endOp == ZSTD_EndDirective.ZSTD_e_end) + { + assert( + mtctx->inBuff.filled == 0 + || mtctx->inBuff.filled == mtctx->targetSectionSize + || mtctx->@params.rsyncable != 0 + ); + endOp = ZSTD_EndDirective.ZSTD_e_flush; + } + + if ( + mtctx->jobReady != 0 + || mtctx->inBuff.filled >= mtctx->targetSectionSize + || endOp != ZSTD_EndDirective.ZSTD_e_continue && mtctx->inBuff.filled > 0 + || endOp == ZSTD_EndDirective.ZSTD_e_end && mtctx->frameEnded == 0 + ) + { + nuint jobSize = mtctx->inBuff.filled; + assert(mtctx->inBuff.filled <= mtctx->targetSectionSize); + { + nuint err_code = ZSTDMT_createCompressionJob(mtctx, jobSize, endOp); + if (ERR_isError(err_code)) + { + return err_code; + } + } + } + + { + /* block if there was no forward input progress */ + nuint remainingToFlush = ZSTDMT_flushProduced( + mtctx, + output, + forwardInputProgress == 0 ? 1U : 0U, + endOp + ); + if (input->pos < input->size) + return remainingToFlush > 1 ? remainingToFlush : 1; + return remainingToFlush; + } + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/_wksps_e__Union.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/_wksps_e__Union.cs new file mode 100644 index 00000000..af51bc19 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/_wksps_e__Union.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +[StructLayout(LayoutKind.Explicit)] +public unsafe struct _wksps_e__Union +{ + [FieldOffset(0)] + public HUF_buildCTable_wksp_tables buildCTable_wksp; + + [FieldOffset(0)] + public HUF_WriteCTableWksp writeCTable_wksp; + + [FieldOffset(0)] + public fixed uint hist_wksp[1024]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/algo_time_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/algo_time_t.cs new file mode 100644 index 00000000..869a6880 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/algo_time_t.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct algo_time_t +{ + public uint tableTime; + public uint decode256Time; + + public algo_time_t(uint tableTime, uint decode256Time) + { + this.tableTime = tableTime; + this.decode256Time = decode256Time; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/base_directive_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/base_directive_e.cs new file mode 100644 index 00000000..b461bbfd --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/base_directive_e.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum base_directive_e +{ + base_0possible = 0, + base_1guaranteed = 1, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/blockProperties_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/blockProperties_t.cs new file mode 100644 index 00000000..e1f68e38 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/blockProperties_t.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct blockProperties_t +{ + public blockType_e blockType; + public uint lastBlock; + public uint origSize; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/blockType_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/blockType_e.cs new file mode 100644 index 00000000..1c0df0a2 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/blockType_e.cs @@ -0,0 +1,9 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum blockType_e +{ + bt_raw, + bt_rle, + bt_compressed, + bt_reserved, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/buffer_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/buffer_s.cs new file mode 100644 index 00000000..6ddf1b22 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/buffer_s.cs @@ -0,0 +1,15 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* ===== Buffer Pool ===== */ +/* a single Buffer Pool can be invoked from multiple threads in parallel */ +public unsafe struct buffer_s +{ + public void* start; + public nuint capacity; + + public buffer_s(void* start, nuint capacity) + { + this.start = start; + this.capacity = capacity; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/dictItem.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/dictItem.cs new file mode 100644 index 00000000..929fea69 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/dictItem.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct dictItem +{ + public uint pos; + public uint length; + public uint savings; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/inBuff_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/inBuff_t.cs new file mode 100644 index 00000000..1ea5fe48 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/inBuff_t.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* ------------------------------------------ */ +/* ===== Multi-threaded compression ===== */ +/* ------------------------------------------ */ +public struct InBuff_t +{ + /* read-only non-owned prefix buffer */ + public Range prefix; + public buffer_s buffer; + public nuint filled; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmEntry_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmEntry_t.cs new file mode 100644 index 00000000..81304cea --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmEntry_t.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ldmEntry_t +{ + public uint offset; + public uint checksum; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmMatchCandidate_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmMatchCandidate_t.cs new file mode 100644 index 00000000..e6f6d465 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmMatchCandidate_t.cs @@ -0,0 +1,9 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ldmMatchCandidate_t +{ + public byte* split; + public uint hash; + public uint checksum; + public ldmEntry_t* bucket; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmParams_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmParams_t.cs new file mode 100644 index 00000000..71c108dd --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmParams_t.cs @@ -0,0 +1,22 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ldmParams_t +{ + /* ZSTD_ps_enable to enable LDM. ZSTD_ps_auto by default */ + public ZSTD_paramSwitch_e enableLdm; + + /* Log size of hashTable */ + public uint hashLog; + + /* Log bucket size for collision resolution, at most 8 */ + public uint bucketSizeLog; + + /* Minimum match length */ + public uint minMatchLength; + + /* Log number of entries to skip */ + public uint hashRateLog; + + /* Window log for the LDM */ + public uint windowLog; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmRollingHashState_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmRollingHashState_t.cs new file mode 100644 index 00000000..73d11fd4 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmRollingHashState_t.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct ldmRollingHashState_t +{ + public ulong rolling; + public ulong stopMask; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmState_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmState_t.cs new file mode 100644 index 00000000..5596880d --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/ldmState_t.cs @@ -0,0 +1,170 @@ +using System.Runtime.CompilerServices; + +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct ldmState_t +{ + /* State for the window round buffer management */ + public ZSTD_window_t window; + public ldmEntry_t* hashTable; + public uint loadedDictEnd; + + /* Next position in bucket to insert entry */ + public byte* bucketOffsets; + public _splitIndices_e__FixedBuffer splitIndices; + public _matchCandidates_e__FixedBuffer matchCandidates; + +#if NET8_0_OR_GREATER + [InlineArray(64)] + public unsafe struct _splitIndices_e__FixedBuffer + { + public nuint e0; + } + +#else + public unsafe struct _splitIndices_e__FixedBuffer + { + public nuint e0; + public nuint e1; + public nuint e2; + public nuint e3; + public nuint e4; + public nuint e5; + public nuint e6; + public nuint e7; + public nuint e8; + public nuint e9; + public nuint e10; + public nuint e11; + public nuint e12; + public nuint e13; + public nuint e14; + public nuint e15; + public nuint e16; + public nuint e17; + public nuint e18; + public nuint e19; + public nuint e20; + public nuint e21; + public nuint e22; + public nuint e23; + public nuint e24; + public nuint e25; + public nuint e26; + public nuint e27; + public nuint e28; + public nuint e29; + public nuint e30; + public nuint e31; + public nuint e32; + public nuint e33; + public nuint e34; + public nuint e35; + public nuint e36; + public nuint e37; + public nuint e38; + public nuint e39; + public nuint e40; + public nuint e41; + public nuint e42; + public nuint e43; + public nuint e44; + public nuint e45; + public nuint e46; + public nuint e47; + public nuint e48; + public nuint e49; + public nuint e50; + public nuint e51; + public nuint e52; + public nuint e53; + public nuint e54; + public nuint e55; + public nuint e56; + public nuint e57; + public nuint e58; + public nuint e59; + public nuint e60; + public nuint e61; + public nuint e62; + public nuint e63; + } +#endif + +#if NET8_0_OR_GREATER + [InlineArray(64)] + public unsafe struct _matchCandidates_e__FixedBuffer + { + public ldmMatchCandidate_t e0; + } + +#else + public unsafe struct _matchCandidates_e__FixedBuffer + { + public ldmMatchCandidate_t e0; + public ldmMatchCandidate_t e1; + public ldmMatchCandidate_t e2; + public ldmMatchCandidate_t e3; + public ldmMatchCandidate_t e4; + public ldmMatchCandidate_t e5; + public ldmMatchCandidate_t e6; + public ldmMatchCandidate_t e7; + public ldmMatchCandidate_t e8; + public ldmMatchCandidate_t e9; + public ldmMatchCandidate_t e10; + public ldmMatchCandidate_t e11; + public ldmMatchCandidate_t e12; + public ldmMatchCandidate_t e13; + public ldmMatchCandidate_t e14; + public ldmMatchCandidate_t e15; + public ldmMatchCandidate_t e16; + public ldmMatchCandidate_t e17; + public ldmMatchCandidate_t e18; + public ldmMatchCandidate_t e19; + public ldmMatchCandidate_t e20; + public ldmMatchCandidate_t e21; + public ldmMatchCandidate_t e22; + public ldmMatchCandidate_t e23; + public ldmMatchCandidate_t e24; + public ldmMatchCandidate_t e25; + public ldmMatchCandidate_t e26; + public ldmMatchCandidate_t e27; + public ldmMatchCandidate_t e28; + public ldmMatchCandidate_t e29; + public ldmMatchCandidate_t e30; + public ldmMatchCandidate_t e31; + public ldmMatchCandidate_t e32; + public ldmMatchCandidate_t e33; + public ldmMatchCandidate_t e34; + public ldmMatchCandidate_t e35; + public ldmMatchCandidate_t e36; + public ldmMatchCandidate_t e37; + public ldmMatchCandidate_t e38; + public ldmMatchCandidate_t e39; + public ldmMatchCandidate_t e40; + public ldmMatchCandidate_t e41; + public ldmMatchCandidate_t e42; + public ldmMatchCandidate_t e43; + public ldmMatchCandidate_t e44; + public ldmMatchCandidate_t e45; + public ldmMatchCandidate_t e46; + public ldmMatchCandidate_t e47; + public ldmMatchCandidate_t e48; + public ldmMatchCandidate_t e49; + public ldmMatchCandidate_t e50; + public ldmMatchCandidate_t e51; + public ldmMatchCandidate_t e52; + public ldmMatchCandidate_t e53; + public ldmMatchCandidate_t e54; + public ldmMatchCandidate_t e55; + public ldmMatchCandidate_t e56; + public ldmMatchCandidate_t e57; + public ldmMatchCandidate_t e58; + public ldmMatchCandidate_t e59; + public ldmMatchCandidate_t e60; + public ldmMatchCandidate_t e61; + public ldmMatchCandidate_t e62; + public ldmMatchCandidate_t e63; + } +#endif +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/nodeElt_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/nodeElt_s.cs new file mode 100644 index 00000000..de28886c --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/nodeElt_s.cs @@ -0,0 +1,12 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* ************************************************************** + * Required declarations + ****************************************************************/ +public struct nodeElt_s +{ + public uint count; + public ushort parent; + public byte @byte; + public byte nbBits; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/offsetCount_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/offsetCount_t.cs new file mode 100644 index 00000000..9a5c8098 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/offsetCount_t.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct offsetCount_t +{ + public uint offset; + public uint count; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/optState_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/optState_t.cs new file mode 100644 index 00000000..d15e8684 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/optState_t.cs @@ -0,0 +1,53 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct optState_t +{ + /* table of literals statistics, of size 256 */ + public uint* litFreq; + + /* table of litLength statistics, of size (MaxLL+1) */ + public uint* litLengthFreq; + + /* table of matchLength statistics, of size (MaxML+1) */ + public uint* matchLengthFreq; + + /* table of offCode statistics, of size (MaxOff+1) */ + public uint* offCodeFreq; + + /* list of found matches, of size ZSTD_OPT_SIZE */ + public ZSTD_match_t* matchTable; + + /* All positions tracked by optimal parser, of size ZSTD_OPT_SIZE */ + public ZSTD_optimal_t* priceTable; + + /* nb of literals */ + public uint litSum; + + /* nb of litLength codes */ + public uint litLengthSum; + + /* nb of matchLength codes */ + public uint matchLengthSum; + + /* nb of offset codes */ + public uint offCodeSum; + + /* to compare to log2(litfreq) */ + public uint litSumBasePrice; + + /* to compare to log2(llfreq) */ + public uint litLengthSumBasePrice; + + /* to compare to log2(mlfreq) */ + public uint matchLengthSumBasePrice; + + /* to compare to log2(offreq) */ + public uint offCodeSumBasePrice; + + /* prices can be determined dynamically, or follow a pre-defined cost structure */ + public ZSTD_OptPrice_e priceType; + + /* pre-calculated dictionary statistics */ + public ZSTD_entropyCTables_t* symbolCosts; + public ZSTD_paramSwitch_e literalCompressionMode; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/rankPos.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/rankPos.cs new file mode 100644 index 00000000..c93cb1f6 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/rankPos.cs @@ -0,0 +1,7 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct rankPos +{ + public ushort @base; + public ushort curr; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/rankValCol_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/rankValCol_t.cs new file mode 100644 index 00000000..94eea418 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/rankValCol_t.cs @@ -0,0 +1,6 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct rankValCol_t +{ + public fixed uint Body[13]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/rawSeq.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/rawSeq.cs new file mode 100644 index 00000000..e2c315d7 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/rawSeq.cs @@ -0,0 +1,13 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct rawSeq +{ + /* Offset of sequence */ + public uint offset; + + /* Length of literals prior to match */ + public uint litLength; + + /* Raw length of match */ + public uint matchLength; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/repcodes_s.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/repcodes_s.cs new file mode 100644 index 00000000..f9b97bda --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/repcodes_s.cs @@ -0,0 +1,6 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct repcodes_s +{ + public fixed uint rep[3]; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/searchMethod_e.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/searchMethod_e.cs new file mode 100644 index 00000000..10395b04 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/searchMethod_e.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public enum searchMethod_e +{ + search_hashChain = 0, + search_binaryTree = 1, + search_rowHash = 2, +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/seqState_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/seqState_t.cs new file mode 100644 index 00000000..a6aec9c3 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/seqState_t.cs @@ -0,0 +1,17 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public unsafe struct seqState_t +{ + public BIT_DStream_t DStream; + public ZSTD_fseState stateLL; + public ZSTD_fseState stateOffb; + public ZSTD_fseState stateML; + public _prevOffset_e__FixedBuffer prevOffset; + + public unsafe struct _prevOffset_e__FixedBuffer + { + public nuint e0; + public nuint e1; + public nuint e2; + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/seqStoreSplits.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/seqStoreSplits.cs new file mode 100644 index 00000000..83679d5f --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/seqStoreSplits.cs @@ -0,0 +1,11 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* Struct to keep track of where we are in our recursive calls. */ +public unsafe struct seqStoreSplits +{ + /* Array of split indices */ + public uint* splitLocations; + + /* The current index within splitLocations being worked on */ + public nuint idx; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/seq_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/seq_t.cs new file mode 100644 index 00000000..88395a25 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/seq_t.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct seq_t +{ + public nuint litLength; + public nuint matchLength; + public nuint offset; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/sortedSymbol_t.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/sortedSymbol_t.cs new file mode 100644 index 00000000..acfe3dd9 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/sortedSymbol_t.cs @@ -0,0 +1,6 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +public struct sortedSymbol_t +{ + public byte symbol; +} diff --git a/src/SharpCompress/Compressors/ZStandard/Unsafe/streaming_operation.cs b/src/SharpCompress/Compressors/ZStandard/Unsafe/streaming_operation.cs new file mode 100644 index 00000000..77f189ec --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/Unsafe/streaming_operation.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.ZStandard.Unsafe; + +/* Streaming state is used to inform allocation of the literal buffer */ +public enum streaming_operation +{ + not_streaming = 0, + is_streaming = 1, +} diff --git a/src/SharpCompress/Compressors/ZStandard/UnsafeHelper.cs b/src/SharpCompress/Compressors/ZStandard/UnsafeHelper.cs new file mode 100644 index 00000000..b145099b --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/UnsafeHelper.cs @@ -0,0 +1,107 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SharpCompress.Compressors.ZStandard; + +public static unsafe class UnsafeHelper +{ + public static void* PoisonMemory(void* destination, ulong size) + { + memset(destination, 0xCC, (uint)size); + return destination; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* malloc(ulong size) + { +#if NET6_0_OR_GREATER + var ptr = NativeMemory.Alloc((nuint)size); +#else + var ptr = (void*)Marshal.AllocHGlobal((nint)size); +#endif +#if DEBUG + return PoisonMemory(ptr, size); +#else + return ptr; +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void* calloc(ulong num, ulong size) + { +#if NET6_0_OR_GREATER + return NativeMemory.AllocZeroed((nuint)num, (nuint)size); +#else + var total = num * size; + assert(total <= uint.MaxValue); + var destination = (void*)Marshal.AllocHGlobal((nint)total); + memset(destination, 0, (uint)total); + return destination; +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void memcpy(void* destination, void* source, uint size) => + System.Runtime.CompilerServices.Unsafe.CopyBlockUnaligned(destination, source, size); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void memset(void* memPtr, byte val, uint size) => + System.Runtime.CompilerServices.Unsafe.InitBlockUnaligned(memPtr, val, size); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void free(void* ptr) + { +#if NET6_0_OR_GREATER + NativeMemory.Free(ptr); +#else + Marshal.FreeHGlobal((IntPtr)ptr); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T* GetArrayPointer(T[] array) + where T : unmanaged + { + var size = (uint)(sizeof(T) * array.Length); +#if NET9_0_OR_GREATER + // This function is used to allocate memory for static data blocks. + // We have to use AllocateTypeAssociatedMemory and link the memory's + // lifetime to this assembly, in order to prevent memory leaks when + // loading the assembly in an unloadable AssemblyLoadContext. + // While introduced in .NET 5, we call this only in .NET 9+, because + // it's not implemented in the Mono runtime until then. + var destination = (T*) + RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(UnsafeHelper), (int)size); +#else + var destination = (T*)malloc(size); +#endif + fixed (void* source = &array[0]) + System.Runtime.CompilerServices.Unsafe.CopyBlockUnaligned(destination, source, size); + + return destination; + } + + [Conditional("DEBUG")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void assert(bool condition, string? message = null) + { + if (!condition) + throw new ArgumentException(message ?? "assert failed"); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void memmove(void* destination, void* source, ulong size) => + Buffer.MemoryCopy(source, destination, size, size); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int memcmp(void* buf1, void* buf2, ulong size) + { + assert(size <= int.MaxValue); + var intSize = (int)size; + return new ReadOnlySpan(buf1, intSize).SequenceCompareTo( + new ReadOnlySpan(buf2, intSize) + ); + } +} diff --git a/src/SharpCompress/Compressors/ZStandard/ZStandardStream.cs b/src/SharpCompress/Compressors/ZStandard/ZStandardStream.cs index 61f7fbb4..d465c835 100644 --- a/src/SharpCompress/Compressors/ZStandard/ZStandardStream.cs +++ b/src/SharpCompress/Compressors/ZStandard/ZStandardStream.cs @@ -8,7 +8,7 @@ using SharpCompress.IO; namespace SharpCompress.Compressors.ZStandard; -internal class ZStandardStream : ZstdSharp.DecompressionStream, IStreamStack +internal class ZStandardStream : DecompressionStream, IStreamStack { #if DEBUG_STREAMS long IStreamStack.InstanceId { get; set; } diff --git a/src/SharpCompress/Compressors/ZStandard/ZstandardConstants.cs b/src/SharpCompress/Compressors/ZStandard/ZstandardConstants.cs index 8025c658..ac4756d4 100644 --- a/src/SharpCompress/Compressors/ZStandard/ZstandardConstants.cs +++ b/src/SharpCompress/Compressors/ZStandard/ZstandardConstants.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace SharpCompress.Compressors.ZStandard; internal class ZstandardConstants diff --git a/src/SharpCompress/Compressors/ZStandard/ZstdException.cs b/src/SharpCompress/Compressors/ZStandard/ZstdException.cs new file mode 100644 index 00000000..55d79579 --- /dev/null +++ b/src/SharpCompress/Compressors/ZStandard/ZstdException.cs @@ -0,0 +1,12 @@ +using SharpCompress.Common; +using SharpCompress.Compressors.ZStandard.Unsafe; + +namespace SharpCompress.Compressors.ZStandard; + +public class ZstdException : SharpCompressException +{ + public ZstdException(ZSTD_ErrorCode code, string message) + : base(message) => Code = code; + + public ZSTD_ErrorCode Code { get; } +} diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index fe39bc38..0d051d0c 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -38,7 +38,6 @@ $(DefineConstants);DEBUG_STREAMS - diff --git a/src/SharpCompress/Writers/Tar/TarWriter.cs b/src/SharpCompress/Writers/Tar/TarWriter.cs index 7d17d55a..afad63be 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.cs @@ -15,11 +15,13 @@ namespace SharpCompress.Writers.Tar; public class TarWriter : AbstractWriter { private readonly bool finalizeArchiveOnClose; + private TarHeaderWriteFormat headerFormat; public TarWriter(Stream destination, TarWriterOptions options) : base(ArchiveType.Tar, options) { finalizeArchiveOnClose = options.FinalizeArchiveOnClose; + headerFormat = options.HeaderFormat; if (!destination.CanWrite) { @@ -121,7 +123,7 @@ public class TarWriter : AbstractWriter var realSize = size ?? source.Length; - var header = new TarHeader(WriterOptions.ArchiveEncoding); + var header = new TarHeader(WriterOptions.ArchiveEncoding, headerFormat); header.LastModifiedTime = modificationTime ?? TarHeader.EPOCH; header.Name = NormalizeFilename(filename); diff --git a/src/SharpCompress/Writers/Tar/TarWriterOptions.cs b/src/SharpCompress/Writers/Tar/TarWriterOptions.cs index 8f2e866d..a8c3a29e 100755 --- a/src/SharpCompress/Writers/Tar/TarWriterOptions.cs +++ b/src/SharpCompress/Writers/Tar/TarWriterOptions.cs @@ -1,4 +1,5 @@ using SharpCompress.Common; +using SharpCompress.Common.Tar.Headers; namespace SharpCompress.Writers.Tar; @@ -9,8 +10,18 @@ public class TarWriterOptions : WriterOptions /// public bool FinalizeArchiveOnClose { get; } - public TarWriterOptions(CompressionType compressionType, bool finalizeArchiveOnClose) - : base(compressionType) => FinalizeArchiveOnClose = finalizeArchiveOnClose; + public TarHeaderWriteFormat HeaderFormat { get; } + + public TarWriterOptions( + CompressionType compressionType, + bool finalizeArchiveOnClose, + TarHeaderWriteFormat headerFormat = TarHeaderWriteFormat.GNU_TAR_LONG_LINK + ) + : base(compressionType) + { + FinalizeArchiveOnClose = finalizeArchiveOnClose; + HeaderFormat = headerFormat; + } internal TarWriterOptions(WriterOptions options) : this(options.CompressionType, true) => ArchiveEncoding = options.ArchiveEncoding; diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index 1d5b5fc8..8c4b96b6 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -13,6 +13,7 @@ using SharpCompress.Compressors.BZip2; using SharpCompress.Compressors.Deflate; using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.PPMd; +using SharpCompress.Compressors.ZStandard; using SharpCompress.IO; namespace SharpCompress.Writers.Zip; @@ -466,7 +467,7 @@ public class ZipWriter : AbstractWriter } case ZipCompressionMethod.ZStandard: { - return new ZstdSharp.CompressionStream(counting, compressionLevel); + return new CompressionStream(counting, compressionLevel); } default: { diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index 82a5df3d..581ac15c 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -58,17 +58,6 @@ "System.ValueTuple": "4.6.1" } }, - "ZstdSharp.Port": { - "type": "Direct", - "requested": "[0.8.6, )", - "resolved": "0.8.6", - "contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", "resolved": "8.0.0", @@ -125,12 +114,6 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, - "ZstdSharp.Port": { - "type": "Direct", - "requested": "[0.8.6, )", - "resolved": "0.8.6", - "contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==" - }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", "resolved": "8.0.0", @@ -159,12 +142,6 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, - "ZstdSharp.Port": { - "type": "Direct", - "requested": "[0.8.6, )", - "resolved": "0.8.6", - "contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==" - }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", "resolved": "8.0.0", diff --git a/tests/SharpCompress.Test/packages.lock.json b/tests/SharpCompress.Test/packages.lock.json index 8e2236f2..7f87d400 100644 --- a/tests/SharpCompress.Test/packages.lock.json +++ b/tests/SharpCompress.Test/packages.lock.json @@ -155,8 +155,7 @@ "Microsoft.Bcl.AsyncInterfaces": "[10.0.0, )", "System.Buffers": "[4.6.1, )", "System.Memory": "[4.6.3, )", - "System.Text.Encoding.CodePages": "[10.0.0, )", - "ZstdSharp.Port": "[0.8.6, )" + "System.Text.Encoding.CodePages": "[10.0.0, )" } }, "Microsoft.Bcl.AsyncInterfaces": { @@ -195,17 +194,6 @@ "System.Runtime.CompilerServices.Unsafe": "6.1.2", "System.ValueTuple": "4.6.1" } - }, - "ZstdSharp.Port": { - "type": "CentralTransitive", - "requested": "[0.8.6, )", - "resolved": "0.8.6", - "contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } } }, "net10.0": { @@ -270,10 +258,7 @@ "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", "resolved": "18.0.1", - "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==", - "dependencies": { - "System.Reflection.Metadata": "8.0.0" - } + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==" }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", @@ -289,19 +274,6 @@ "resolved": "13.0.3", "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==" - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", - "dependencies": { - "System.Collections.Immutable": "8.0.0" - } - }, "xunit.abstractions": { "type": "Transitive", "resolved": "2.0.3", @@ -343,16 +315,7 @@ } }, "sharpcompress": { - "type": "Project", - "dependencies": { - "ZstdSharp.Port": "[0.8.6, )" - } - }, - "ZstdSharp.Port": { - "type": "CentralTransitive", - "requested": "[0.8.6, )", - "resolved": "0.8.6", - "contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==" + "type": "Project" } } }