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
This commit is contained in:
2026-07-19 13:40:12 +02:00
parent 1cf5a92e02
commit b2e7be480a
2 changed files with 185 additions and 8 deletions
+24 -8
View File
@@ -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(
+161
View File
@@ -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::<usize>()
);
assert!(
offset_of!(ZSTD_rust_setParametersUsingCCtxParamsState, stream_stage)
== 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_setParametersUsingCCtxParamsState, cdict) == 3 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_setParametersUsingCCtxParamsState>() == 4 * size_of::<usize>());
};
/// 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::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::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::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::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::<ZSTD_CCtx_params>::zeroed().assume_init() };
let mut requested_params =
unsafe { MaybeUninit::<ZSTD_CCtx_params>::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>,