From 7867f64413a1e579d9f87b2d604c6da6e815adc3 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 18:43:00 +0200 Subject: [PATCH] feat(params): move context-free getters into Rust Compression-parameter table selection and adjustment were already implemented as Rust leaves, but the internal and public ZSTD_getCParams/ZSTD_getParams helpers still assembled those results in the C translation unit. That left source-size and dictionary-mode policy duplicated at the C/Rust boundary. Add Rust implementations for the internal and public getter policies. The internal helpers preserve a zero source size as a known empty input, while the public helpers retain the API rule that zero means unknown. The C side keeps only the build-specific exclusion-mask construction and thin ABI adapters, so reduced builds continue to select the same available strategy cascade. Test Plan: - `cargo test --manifest-path rust/Cargo.toml --lib -- --test-threads=1` -- passed (411 tests). - `cargo clippy --manifest-path rust/Cargo.toml --lib -- -D warnings` -- passed. - `cargo +nightly fmt --manifest-path rust/Cargo.toml -- --check` -- passed. - `make -B -C lib -j2 lib` -- passed. - Focused tests cover public zero-to-unknown translation, internal zero semantics, default frame parameters, and dictionary attachment modes. --- lib/compress/zstd_compress.c | 36 +++-- rust/src/zstd_compress_params.rs | 218 +++++++++++++++++++++++++++++-- 2 files changed, 229 insertions(+), 25 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 91f0b608d..1c576b26f 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -148,6 +148,18 @@ ZSTD_compressionParameters ZSTD_rust_params_adjustCParams(ZSTD_compressionParameters cParams, U64 srcSize, size_t dictSize, int mode, int useRowMatchFinder); +ZSTD_compressionParameters ZSTD_rust_params_getCParamsInternal( + int compressionLevel, U64 srcSizeHint, size_t dictSize, int mode, + U32 exclusionMask); +ZSTD_parameters ZSTD_rust_params_getParamsInternal( + int compressionLevel, U64 srcSizeHint, size_t dictSize, int mode, + U32 exclusionMask); +ZSTD_compressionParameters ZSTD_rust_params_getCParams( + int compressionLevel, U64 srcSizeHint, size_t dictSize, + U32 exclusionMask); +ZSTD_parameters ZSTD_rust_params_getParams( + int compressionLevel, U64 srcSizeHint, size_t dictSize, + U32 exclusionMask); ZSTD_compressionParameters ZSTD_rust_params_applyStrategyExclusions( ZSTD_compressionParameters cParams, U32 exclusionMask); ZSTD_compressionParameters ZSTD_rust_params_getCParamsFromCCtxParams( @@ -5697,12 +5709,9 @@ static void ZSTD_dedicatedDictSearch_revertCParams( * Note: `mode` controls how we treat the `dictSize`. See docs for `ZSTD_CParamMode_e`. */ static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize, ZSTD_CParamMode_e mode) { - /* Table selection is context-free and lives in Rust; the adjustment step - * stays behind ZSTD_adjustCParams_internal() so this build's strategy - * cascade applies. */ - ZSTD_compressionParameters const cp = ZSTD_rust_params_selectCParams( - compressionLevel, srcSizeHint, dictSize, (int)mode); - return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize, mode, ZSTD_ps_auto); + return ZSTD_rust_params_getCParamsInternal( + compressionLevel, srcSizeHint, dictSize, (int)mode, + ZSTD_getCParamsExclusionMask()); } /*! ZSTD_getCParams() : @@ -5710,8 +5719,9 @@ static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, * Size values are optional, provide 0 if not known or unused */ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) { - if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN; - return ZSTD_getCParams_internal(compressionLevel, srcSizeHint, dictSize, ZSTD_cpm_unknown); + return ZSTD_rust_params_getCParams( + compressionLevel, srcSizeHint, dictSize, + ZSTD_getCParamsExclusionMask()); } /*! ZSTD_getParams() : @@ -5721,9 +5731,10 @@ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long l static ZSTD_parameters ZSTD_getParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize, ZSTD_CParamMode_e mode) { - ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, srcSizeHint, dictSize, mode); DEBUGLOG(5, "ZSTD_getParams (cLevel=%i)", compressionLevel); - return ZSTD_rust_params_makeParams(cParams); + return ZSTD_rust_params_getParamsInternal( + compressionLevel, srcSizeHint, dictSize, (int)mode, + ZSTD_getCParamsExclusionMask()); } /*! ZSTD_getParams() : @@ -5732,8 +5743,9 @@ ZSTD_getParams_internal(int compressionLevel, unsigned long long srcSizeHint, si * Fields of `ZSTD_frameParameters` are set to default values */ ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) { - if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN; - return ZSTD_getParams_internal(compressionLevel, srcSizeHint, dictSize, ZSTD_cpm_unknown); + return ZSTD_rust_params_getParams( + compressionLevel, srcSizeHint, dictSize, + ZSTD_getCParamsExclusionMask()); } void ZSTD_registerSequenceProducer( diff --git a/rust/src/zstd_compress_params.rs b/rust/src/zstd_compress_params.rs index dd0126335..e095fc466 100644 --- a/rust/src/zstd_compress_params.rs +++ b/rust/src/zstd_compress_params.rs @@ -5,13 +5,14 @@ //! Context-free compression-parameter selection and sizing leaves. //! -//! This module deliberately does **not** own the public `ZSTD_*` symbols yet. -//! `zstd_compress.c` still owns configuration-sensitive policy: private -//! `ZSTD_CCtx_params` layouts, the C-preprocessor construction of excluded -//! block-compressor bits, LDM workspace sizing, and ASAN workspace policy. -//! The Rust policy leaves receive those build values as explicit scalar inputs, -//! retaining byte-for-byte C behavior for reduced builds without exposing -//! private C state across the ABI. +//! The public `ZSTD_*` symbols remain C-owned thin adapters. Rust owns the +//! context-free getter policy behind scalar ABI functions. `zstd_compress.c` +//! still owns configuration-sensitive policy: private `ZSTD_CCtx_params` +//! layouts, the C-preprocessor construction of excluded block-compressor +//! bits, LDM workspace sizing, and ASAN workspace policy. The Rust policy +//! leaves receive those build values as explicit scalar inputs, retaining +//! byte-for-byte C behavior for reduced builds without exposing private C +//! state across the ABI. use crate::errors::{ZstdErrorCode, ERROR}; use std::mem::size_of; @@ -855,6 +856,77 @@ fn select_cparams( cparams } +fn get_cparams_internal( + compression_level: c_int, + src_size_hint: u64, + dict_size: usize, + mode: c_int, + exclusion_mask: u32, +) -> ZSTD_compressionParameters { + /* ZSTD_getCParams_internal() preserves srcSizeHint == 0 as a known empty + * input. Only the public getter translates zero to UNKNOWN. */ + let cparams = select_cparams(compression_level, src_size_hint, dict_size, mode); + adjust_cparams( + apply_strategy_exclusions(cparams, exclusion_mask), + src_size_hint, + dict_size, + mode, + ZSTD_RUST_PS_AUTO, + ) +} + +#[inline] +fn get_params_internal( + compression_level: c_int, + src_size_hint: u64, + dict_size: usize, + mode: c_int, + exclusion_mask: u32, +) -> ZSTD_parameters { + make_params(get_cparams_internal( + compression_level, + src_size_hint, + dict_size, + mode, + exclusion_mask, + )) +} + +fn get_cparams_public( + compression_level: c_int, + src_size_hint: u64, + dict_size: usize, + exclusion_mask: u32, +) -> ZSTD_compressionParameters { + let src_size_hint = if src_size_hint == 0 { + ZSTD_CONTENTSIZE_UNKNOWN + } else { + src_size_hint + }; + get_cparams_internal( + compression_level, + src_size_hint, + dict_size, + ZSTD_RUST_CPM_UNKNOWN, + exclusion_mask, + ) +} + +#[inline] +fn get_params_public( + compression_level: c_int, + src_size_hint: u64, + dict_size: usize, + exclusion_mask: u32, +) -> ZSTD_parameters { + make_params(get_cparams_public( + compression_level, + src_size_hint, + dict_size, + exclusion_mask, + )) +} + fn get_cparams_from_cctx_params( compression_level: c_int, cctx_src_size_hint: c_int, @@ -873,15 +945,12 @@ fn get_cparams_from_cctx_params( } /* ZSTD_getCParams_internal() selects and performs the first adjustment. */ - let mut cparams = adjust_cparams( - apply_strategy_exclusions( - select_cparams(compression_level, src_size_hint, dict_size, mode), - exclusion_mask, - ), + let mut cparams = get_cparams_internal( + compression_level, src_size_hint, dict_size, mode, - ZSTD_RUST_PS_AUTO, + exclusion_mask, ); if enable_ldm == ZSTD_RUST_PS_ENABLE { @@ -1043,6 +1112,57 @@ pub extern "C" fn ZSTD_rust_params_adjustCParams( adjust_cparams(cparams, srcSize, dictSize, mode, useRowMatchFinder) } +/// Reproduces `ZSTD_getCParams_internal()` without translating a zero source +/// size to `ZSTD_CONTENTSIZE_UNKNOWN`. +#[no_mangle] +pub extern "C" fn ZSTD_rust_params_getCParamsInternal( + compressionLevel: c_int, + srcSizeHint: u64, + dictSize: usize, + mode: c_int, + exclusionMask: u32, +) -> ZSTD_compressionParameters { + get_cparams_internal(compressionLevel, srcSizeHint, dictSize, mode, exclusionMask) +} + +/// Reproduces `ZSTD_getParams_internal()` without translating a zero source +/// size to `ZSTD_CONTENTSIZE_UNKNOWN`. +#[no_mangle] +pub extern "C" fn ZSTD_rust_params_getParamsInternal( + compressionLevel: c_int, + srcSizeHint: u64, + dictSize: usize, + mode: c_int, + exclusionMask: u32, +) -> ZSTD_parameters { + get_params_internal(compressionLevel, srcSizeHint, dictSize, mode, exclusionMask) +} + +/// Reproduces the public `ZSTD_getCParams()` source-size policy: zero means +/// an unknown size, while the internal helper keeps zero as a known empty +/// input. +#[no_mangle] +pub extern "C" fn ZSTD_rust_params_getCParams( + compressionLevel: c_int, + srcSizeHint: u64, + dictSize: usize, + exclusionMask: u32, +) -> ZSTD_compressionParameters { + get_cparams_public(compressionLevel, srcSizeHint, dictSize, exclusionMask) +} + +/// Reproduces the public `ZSTD_getParams()` source-size policy and default +/// frame-parameter construction. +#[no_mangle] +pub extern "C" fn ZSTD_rust_params_getParams( + compressionLevel: c_int, + srcSizeHint: u64, + dictSize: usize, + exclusionMask: u32, +) -> ZSTD_parameters { + get_params_public(compressionLevel, srcSizeHint, dictSize, exclusionMask) +} + /// Reproduces `ZSTD_getCParamsFromCCtxParams()` from scalar snapshots. /// /// The C shim supplies the private build policy as an exclusion mask and the @@ -2075,6 +2195,78 @@ mod tests { ); } + #[test] + fn public_getters_translate_zero_to_unknown_but_internal_getters_do_not() { + let public_zero = ZSTD_rust_params_getCParams(3, 0, 1, 0); + let public_unknown = ZSTD_rust_params_getCParams(3, ZSTD_CONTENTSIZE_UNKNOWN, 1, 0); + assert_eq!(public_zero, public_unknown); + + let internal_zero = ZSTD_rust_params_getCParamsInternal(3, 0, 1, ZSTD_RUST_CPM_UNKNOWN, 0); + assert_eq!(internal_zero.windowLog, 10); + assert_eq!(public_unknown.windowLog, 14); + assert_ne!(internal_zero, public_unknown); + + let public_params_zero = ZSTD_rust_params_getParams(3, 0, 1, 0); + let public_params_unknown = ZSTD_rust_params_getParams(3, ZSTD_CONTENTSIZE_UNKNOWN, 1, 0); + assert_eq!(public_params_zero, public_params_unknown); + assert_eq!(public_params_zero.cParams, public_zero); + assert_eq!( + ZSTD_rust_params_getParamsInternal(3, 0, 1, ZSTD_RUST_CPM_UNKNOWN, 0).cParams, + internal_zero + ); + } + + #[test] + fn getter_modes_preserve_dictionary_attachment_boundaries() { + let attached_with_dict = ZSTD_rust_params_getCParamsInternal( + 3, + ZSTD_CONTENTSIZE_UNKNOWN, + 1, + ZSTD_RUST_CPM_ATTACH_DICT, + 0, + ); + let attached_without_dict = ZSTD_rust_params_getCParamsInternal( + 3, + ZSTD_CONTENTSIZE_UNKNOWN, + 0, + ZSTD_RUST_CPM_ATTACH_DICT, + 0, + ); + assert_eq!(attached_with_dict, attached_without_dict); + + let no_attach = ZSTD_rust_params_getCParamsInternal( + 3, + ZSTD_CONTENTSIZE_UNKNOWN, + 1, + ZSTD_RUST_CPM_NO_ATTACH_DICT, + 0, + ); + let create_cdict = ZSTD_rust_params_getCParamsInternal( + 3, + ZSTD_CONTENTSIZE_UNKNOWN, + 1, + ZSTD_RUST_CPM_CREATE_CDICT, + 0, + ); + assert_eq!(attached_with_dict.windowLog, 21); + assert_eq!(no_attach.windowLog, 14); + assert_eq!(create_cdict.windowLog, 10); + assert!(attached_with_dict.windowLog > no_attach.windowLog); + assert!(no_attach.windowLog > create_cdict.windowLog); + + assert_eq!( + ZSTD_rust_params_getCParamsInternal( + 3, + ZSTD_CONTENTSIZE_UNKNOWN, + 1, + ZSTD_RUST_CPM_UNKNOWN, + 0, + ) + .windowLog, + no_attach.windowLog + ); + } + fn cctx_policy_params( cctx_src_size_hint: c_int, src_size_hint: u64,