From b4e37ad09ff5d00eca3286f950c3b4891b634779 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sun, 19 Jul 2026 12:59:56 +0200 Subject: [PATCH] feat(compress): move frame parameter updates into Rust Move ZSTD_CCtx_setFParams's ordered frame-flag updates into the Rust projection. C retains the context-bound ZSTD_CCtx_setParameter callback while Rust preserves content-size, checksum, and dictionary-ID boolean conversions, update order, and first-error propagation. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml set_fparams -- --test-threads=1 - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings - ulimit -v 41943040; make -B -C programs -j1 zstd - git diff --cached --check --- lib/compress/zstd_compress.c | 27 ++++++- rust/src/zstd_compress.rs | 149 ++++++++++++++++++++++++++++++++++- 2 files changed, 168 insertions(+), 8 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index fcd6730b4..a16fb2443 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -213,6 +213,19 @@ typedef char ZSTD_rust_set_cparams_state_layout[ && offsetof(ZSTD_rust_setCParamsState, setParameter) == 2 * sizeof(void*) && sizeof(ZSTD_rust_setCParamsState) == 3 * sizeof(void*)) ? 1 : -1]; +typedef size_t (*ZSTD_rust_setFParamsSetParameter_f)( + void* context, int param, int value); +typedef struct { + void* callbackContext; + ZSTD_rust_setFParamsSetParameter_f setParameter; +} ZSTD_rust_setFParamsState; +size_t ZSTD_rust_setFParams(const ZSTD_rust_setFParamsState* state, + ZSTD_frameParameters fParams); +typedef char ZSTD_rust_set_fparams_state_layout[ + (offsetof(ZSTD_rust_setFParamsState, callbackContext) == 0 + && offsetof(ZSTD_rust_setFParamsState, setParameter) == sizeof(void*) + && sizeof(ZSTD_rust_setFParamsState) == 2 * sizeof(void*)) + ? 1 : -1]; ZSTD_frameProgression ZSTD_rust_frameProgression(U64 consumedSrcSize, size_t buffered, U64 producedCSize); @@ -1903,6 +1916,12 @@ static size_t ZSTD_rust_setCParams_setParameter( return ZSTD_CCtx_setParameter((ZSTD_CCtx*)context, (ZSTD_cParameter)param, value); } +static size_t ZSTD_rust_setFParams_setParameter( + void* context, int param, int value) +{ + return ZSTD_CCtx_setParameter((ZSTD_CCtx*)context, (ZSTD_cParameter)param, value); +} + size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams) { ZSTD_rust_setCParamsState state; @@ -1916,12 +1935,12 @@ size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams) size_t ZSTD_CCtx_setFParams(ZSTD_CCtx* cctx, ZSTD_frameParameters fparams) { + ZSTD_rust_setFParamsState state; ZSTD_STATIC_ASSERT(sizeof(fparams) == 3 * 4 /* all params are listed below */); DEBUGLOG(4, "ZSTD_CCtx_setFParams"); - FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_contentSizeFlag, fparams.contentSizeFlag != 0), ""); - FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, fparams.checksumFlag != 0), ""); - FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_dictIDFlag, fparams.noDictIDFlag == 0), ""); - return 0; + state.callbackContext = cctx; + state.setParameter = ZSTD_rust_setFParams_setParameter; + return ZSTD_rust_setFParams(&state, fparams); } size_t ZSTD_CCtx_setParams(ZSTD_CCtx* cctx, ZSTD_parameters params) diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index 5e04a6c68..f4f74f42f 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -23,9 +23,9 @@ use crate::zstd_compress_frame::{ }; use crate::zstd_compress_literals::min_gain; use crate::zstd_compress_params::{ - ZSTD_compressionParameters, ZSTD_parameters, 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, + ZSTD_compressionParameters, ZSTD_frameParameters, ZSTD_parameters, + 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_sequences::SeqDef; use crate::zstd_compress_stats::{ @@ -120,6 +120,9 @@ const ZSTD_C_SEARCH_LOG: c_int = 104; const ZSTD_C_MIN_MATCH: c_int = 105; const ZSTD_C_TARGET_LENGTH: c_int = 106; const ZSTD_C_STRATEGY: c_int = 107; +const ZSTD_C_CONTENT_SIZE_FLAG: c_int = 200; +const ZSTD_C_CHECKSUM_FLAG: c_int = 201; +const ZSTD_C_DICT_ID_FLAG: c_int = 202; const ZSTD_CSTREAM_STAGE_LOAD: c_int = 1; const ZSTD_CSTREAM_STAGE_FLUSH: c_int = 2; const ZSTD_BSS_COMPRESS: c_int = 0; @@ -1267,6 +1270,47 @@ pub unsafe extern "C" fn ZSTD_rust_setCParams( 0 } +type SetFParamsSetParameterFn = unsafe extern "C" fn(*mut c_void, c_int, c_int) -> usize; + +/// Explicit projection for `ZSTD_CCtx_setFParams`. +#[repr(C)] +pub struct ZSTD_rust_setFParamsState { + callback_context: *mut c_void, + set_parameter: SetFParamsSetParameterFn, +} + +const _: () = { + assert!(offset_of!(ZSTD_rust_setFParamsState, callback_context) == 0); + assert!(offset_of!(ZSTD_rust_setFParamsState, set_parameter) == size_of::()); + assert!(size_of::() == 2 * size_of::()); +}; + +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_setFParams( + state: *const ZSTD_rust_setFParamsState, + fparams: ZSTD_frameParameters, +) -> usize { + if state.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + let state = unsafe { &*state }; + let parameters = [ + ( + ZSTD_C_CONTENT_SIZE_FLAG, + (fparams.contentSizeFlag != 0) as c_int, + ), + (ZSTD_C_CHECKSUM_FLAG, (fparams.checksumFlag != 0) as c_int), + (ZSTD_C_DICT_ID_FLAG, (fparams.noDictIDFlag == 0) as c_int), + ]; + for (param, value) in parameters { + let result = unsafe { (state.set_parameter)(state.callback_context, param, value) }; + if ERR_isError(result) { + return result; + } + } + 0 +} + type CompressStreamBlockFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize) -> usize; type CompressStreamResetFn = unsafe extern "C" fn(*mut c_void) -> usize; @@ -5575,7 +5619,6 @@ pub unsafe extern "C" fn ZSTD_compressStream2( mod tests { use super::*; use crate::errors::ERR_getErrorCode; - use crate::zstd_compress_params::ZSTD_frameParameters; use std::io::Write; use std::process::{Command, Stdio}; @@ -9802,6 +9845,104 @@ mod tests { assert_eq!(context.parameter_calls.len(), 4); } + #[derive(Default)] + struct SetFParamsTestContext { + parameter_calls: Vec<(c_int, c_int)>, + set_result: usize, + fail_at: usize, + } + + unsafe fn set_fparams_test_context(context: *mut c_void) -> &'static mut SetFParamsTestContext { + unsafe { &mut *context.cast::() } + } + + unsafe extern "C" fn set_fparams_test_set_parameter( + context: *mut c_void, + param: c_int, + value: c_int, + ) -> usize { + let context = unsafe { set_fparams_test_context(context) }; + context.parameter_calls.push((param, value)); + if context.fail_at != 0 && context.parameter_calls.len() == context.fail_at { + context.set_result + } else { + 0 + } + } + + fn set_fparams_test_state(context: &mut SetFParamsTestContext) -> ZSTD_rust_setFParamsState { + ZSTD_rust_setFParamsState { + callback_context: (context as *mut SetFParamsTestContext).cast(), + set_parameter: set_fparams_test_set_parameter, + } + } + + #[test] + fn set_fparams_preserves_parameter_order_and_boolean_conversions() { + let fparams = ZSTD_frameParameters { + contentSizeFlag: 0, + checksumFlag: 2, + noDictIDFlag: 7, + }; + let mut context = SetFParamsTestContext::default(); + let state = set_fparams_test_state(&mut context); + + let result = unsafe { ZSTD_rust_setFParams(&state, fparams) }; + + assert_eq!(result, 0); + assert_eq!( + context.parameter_calls, + [ + (ZSTD_C_CONTENT_SIZE_FLAG, 0), + (ZSTD_C_CHECKSUM_FLAG, 1), + (ZSTD_C_DICT_ID_FLAG, 0), + ] + ); + } + + #[test] + fn set_fparams_stops_after_the_first_parameter_error() { + let fparams = ZSTD_frameParameters { + contentSizeFlag: 1, + checksumFlag: 1, + noDictIDFlag: 0, + }; + let mut context = SetFParamsTestContext { + set_result: ERROR(ZstdErrorCode::StageWrong), + fail_at: 1, + ..SetFParamsTestContext::default() + }; + let state = set_fparams_test_state(&mut context); + + let result = unsafe { ZSTD_rust_setFParams(&state, fparams) }; + + assert_eq!(result, ERROR(ZstdErrorCode::StageWrong)); + assert_eq!(context.parameter_calls, [(ZSTD_C_CONTENT_SIZE_FLAG, 1)]); + } + + #[test] + fn set_fparams_propagates_a_later_parameter_error_without_following_updates() { + let fparams = ZSTD_frameParameters { + contentSizeFlag: 1, + checksumFlag: 0, + noDictIDFlag: 0, + }; + let mut context = SetFParamsTestContext { + set_result: ERROR(ZstdErrorCode::ParameterOutOfBound), + fail_at: 2, + ..SetFParamsTestContext::default() + }; + let state = set_fparams_test_state(&mut context); + + let result = unsafe { ZSTD_rust_setFParams(&state, fparams) }; + + assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound)); + assert_eq!( + context.parameter_calls, + [(ZSTD_C_CONTENT_SIZE_FLAG, 1), (ZSTD_C_CHECKSUM_FLAG, 0),] + ); + } + #[test] fn pledged_src_size_writes_the_init_stage_value_plus_one() { let mut pledged_src_size_plus_one = 0;