From b2e7be480a40d8ff58c96685d63e90d55a21bc71 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sun, 19 Jul 2026 13:40:12 +0200 Subject: [PATCH] feat(compress): move stored parameter policy into Rust Move ZSTD_CCtx_setParametersUsingCCtxParams stage, dictionary, and copy policy into a Rust-owned ABI bridge while retaining C-owned parameter object storage. Preserve stage-before-dictionary validation, error codes, and no-mutation behavior on rejected calls. Test Plan: - cargo test --manifest-path rust/Cargo.toml --lib - cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings - make -B -C programs -j1 zstd - make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s - focused set_parameters_using_cctx_params unit tests --- lib/compress/zstd_compress.c | 32 +++++-- rust/src/zstd_compress.rs | 161 +++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+), 8 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 31748ca66..d61ea93f4 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -247,6 +247,25 @@ typedef char ZSTD_rust_set_params_state_layout[ && offsetof(ZSTD_rust_setParamsState, setCParams) == 3 * sizeof(void*) && sizeof(ZSTD_rust_setParamsState) == 4 * sizeof(void*)) ? 1 : -1]; +typedef struct { + ZSTD_CCtx_params* requestedParams; + const ZSTD_CCtx_params* sourceParams; + int streamStage; + const void* cdict; +} ZSTD_rust_setParametersUsingCCtxParamsState; +size_t ZSTD_rust_setParametersUsingCCtxParams( + const ZSTD_rust_setParametersUsingCCtxParamsState* state); +typedef char ZSTD_rust_set_parameters_using_cctx_params_state_layout[ + (offsetof(ZSTD_rust_setParametersUsingCCtxParamsState, requestedParams) == 0 + && offsetof(ZSTD_rust_setParametersUsingCCtxParamsState, sourceParams) + == sizeof(void*) + && offsetof(ZSTD_rust_setParametersUsingCCtxParamsState, streamStage) + == 2 * sizeof(void*) + && offsetof(ZSTD_rust_setParametersUsingCCtxParamsState, cdict) + == 3 * sizeof(void*) + && sizeof(ZSTD_rust_setParametersUsingCCtxParamsState) + == 4 * sizeof(void*)) + ? 1 : -1]; typedef void (*ZSTD_rust_resetCCtxClearAllDicts_f)(void* context); typedef size_t (*ZSTD_rust_resetCCtxResetParams_f)(void* context); typedef struct { @@ -1942,14 +1961,11 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params) { DEBUGLOG(4, "ZSTD_CCtx_setParametersUsingCCtxParams"); - RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong, - "The context is in the wrong stage!"); - RETURN_ERROR_IF(cctx->cdict, stage_wrong, - "Can't override parameters with cdict attached (some must " - "be inherited from the cdict)."); - - cctx->requestedParams = *params; - return 0; + { ZSTD_rust_setParametersUsingCCtxParamsState const state = { + &cctx->requestedParams, params, (int)cctx->streamStage, cctx->cdict + }; + return ZSTD_rust_setParametersUsingCCtxParams(&state); + } } static size_t ZSTD_rust_setCParams_checkCParams( diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index f982dc70b..5f970537a 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -27,6 +27,7 @@ use crate::zstd_compress_params::{ ZSTD_rust_params_adjustCParams, ZSTD_rust_params_maxNbSeq, ZSTD_rust_params_selectCParams, ZSTD_RUST_CPM_NO_ATTACH_DICT, ZSTD_RUST_PS_AUTO, ZSTD_RUST_PS_DISABLE, }; +use crate::zstd_compress_params_api::ZSTD_CCtx_params; use crate::zstd_compress_sequences::SeqDef; use crate::zstd_compress_stats::{ update_rep, SeqCollector, SeqStore_t, ZSTD_Sequence, ZSTD_SequencePosition, @@ -817,6 +818,60 @@ pub unsafe extern "C" fn ZSTD_rust_compress2( unsafe { compress2_body_with(&*state, dst, dst_capacity, src, src_size) } } +/// Explicit projection for `ZSTD_CCtx_setParametersUsingCCtxParams`. +/// +/// Rust owns the stage and dictionary policy while the C-owned parameter +/// objects cross the ABI as pointers to the shared layout mirror. +#[repr(C)] +pub struct ZSTD_rust_setParametersUsingCCtxParamsState { + requested_params: *mut ZSTD_CCtx_params, + source_params: *const ZSTD_CCtx_params, + stream_stage: c_int, + cdict: *const c_void, +} + +const _: () = { + assert!( + offset_of!( + ZSTD_rust_setParametersUsingCCtxParamsState, + requested_params + ) == 0 + ); + assert!( + offset_of!(ZSTD_rust_setParametersUsingCCtxParamsState, source_params) + == size_of::() + ); + assert!( + offset_of!(ZSTD_rust_setParametersUsingCCtxParamsState, stream_stage) + == 2 * size_of::() + ); + assert!( + offset_of!(ZSTD_rust_setParametersUsingCCtxParamsState, cdict) == 3 * size_of::() + ); + assert!(size_of::() == 4 * size_of::()); +}; + +/// Apply stored parameters through the C-owned context projection. +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_setParametersUsingCCtxParams( + state: *const ZSTD_rust_setParametersUsingCCtxParamsState, +) -> usize { + if state.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + let state = unsafe { &*state }; + if state.stream_stage != ZSTD_CSTREAM_STAGE_INIT { + return ERROR(ZstdErrorCode::StageWrong); + } + if !state.cdict.is_null() { + return ERROR(ZstdErrorCode::StageWrong); + } + unsafe { + *state.requested_params = *state.source_params; + } + 0 +} + type ResetCCtxClearAllDictsFn = unsafe extern "C" fn(*mut c_void); type ResetCCtxResetParamsFn = unsafe extern "C" fn(*mut c_void) -> usize; @@ -5745,6 +5800,9 @@ pub unsafe extern "C" fn ZSTD_compressStream2( mod tests { use super::*; use crate::errors::ERR_getErrorCode; + use crate::zstd_compress_params_api::{ + ZSTD_CCtxParams_getParameter, ZSTD_CCtxParams_setParameter, + }; use std::io::Write; use std::process::{Command, Stdio}; @@ -10019,6 +10077,109 @@ mod tests { ); } + fn set_parameters_using_cctx_params_test_value(params: &ZSTD_CCtx_params) -> c_int { + let mut value = -1; + let result = unsafe { ZSTD_CCtxParams_getParameter(params, 10, &mut value) }; + assert_eq!(result, 0); + value + } + + fn set_parameters_using_cctx_params_test_state( + requested_params: &mut ZSTD_CCtx_params, + source_params: &ZSTD_CCtx_params, + stream_stage: c_int, + cdict: *const c_void, + ) -> ZSTD_rust_setParametersUsingCCtxParamsState { + ZSTD_rust_setParametersUsingCCtxParamsState { + requested_params, + source_params, + stream_stage, + cdict, + } + } + + #[test] + fn set_parameters_using_cctx_params_copies_source_after_policy_checks() { + let mut source_params = unsafe { MaybeUninit::::zeroed().assume_init() }; + let mut requested_params = + unsafe { MaybeUninit::::zeroed().assume_init() }; + assert_eq!( + unsafe { ZSTD_CCtxParams_setParameter(&mut source_params, 10, 1) }, + 1 + ); + let state = set_parameters_using_cctx_params_test_state( + &mut requested_params, + &source_params, + ZSTD_CSTREAM_STAGE_INIT, + ptr::null(), + ); + + let result = unsafe { ZSTD_rust_setParametersUsingCCtxParams(&state) }; + + assert_eq!(result, 0); + assert_eq!( + set_parameters_using_cctx_params_test_value(&requested_params), + 1 + ); + } + + #[test] + fn set_parameters_using_cctx_params_rejects_non_init_stage_without_copying() { + let mut source_params = unsafe { MaybeUninit::::zeroed().assume_init() }; + let mut requested_params = + unsafe { MaybeUninit::::zeroed().assume_init() }; + assert_eq!( + unsafe { ZSTD_CCtxParams_setParameter(&mut source_params, 10, 1) }, + 1 + ); + let state = set_parameters_using_cctx_params_test_state( + &mut requested_params, + &source_params, + ZSTD_CSTREAM_STAGE_LOAD, + ptr::null(), + ); + + let result = unsafe { ZSTD_rust_setParametersUsingCCtxParams(&state) }; + + assert_eq!(result, ERROR(ZstdErrorCode::StageWrong)); + assert_eq!( + set_parameters_using_cctx_params_test_value(&requested_params), + 0 + ); + } + + #[test] + fn set_parameters_using_cctx_params_rejects_cdict_without_copying() { + let mut source_params = unsafe { MaybeUninit::::zeroed().assume_init() }; + let mut requested_params = + unsafe { MaybeUninit::::zeroed().assume_init() }; + assert_eq!( + unsafe { ZSTD_CCtxParams_setParameter(&mut source_params, 10, 1) }, + 1 + ); + let state = set_parameters_using_cctx_params_test_state( + &mut requested_params, + &source_params, + ZSTD_CSTREAM_STAGE_INIT, + ptr::dangling(), + ); + + let result = unsafe { ZSTD_rust_setParametersUsingCCtxParams(&state) }; + + assert_eq!(result, ERROR(ZstdErrorCode::StageWrong)); + assert_eq!( + set_parameters_using_cctx_params_test_value(&requested_params), + 0 + ); + } + + #[test] + fn set_parameters_using_cctx_params_rejects_null_state() { + let result = unsafe { ZSTD_rust_setParametersUsingCCtxParams(ptr::null()) }; + + assert_eq!(result, ERROR(ZstdErrorCode::Generic)); + } + #[derive(Default)] struct SetCParamsTestContext { events: Vec<&'static str>,