refactor(compress): move end directive validation to Rust

Project the scalar ZSTD_compressStream2 end directive into Rust and keep C responsible for the private streaming context, existing parameter error, and diagnostic text. The Rust range policy preserves the original unsigned enum validation for continue, flush, and end.

Test Plan: ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings; cargo +nightly fmt --manifest-path rust/Cargo.toml --all -- --check; make -j1; ./tests/rustLibSmoke; make -j1 -C tests test (all shell tests, large streaming tests, native tester, fuzzer phases, and zstream tester passed).
This commit is contained in:
2026-07-20 18:25:52 +02:00
parent 03d0d4edac
commit d6cda74b84
2 changed files with 70 additions and 1 deletions
+13 -1
View File
@@ -769,6 +769,15 @@ size_t ZSTD_compressStream2_c(ZSTD_CCtx* cctx,
ZSTD_outBuffer* output,
ZSTD_inBuffer* input,
ZSTD_EndDirective endOp);
typedef struct {
int endOp;
} ZSTD_rust_compressStream2PolicyState;
int ZSTD_rust_compressStream2Policy(
const ZSTD_rust_compressStream2PolicyState* state);
typedef char ZSTD_rust_compress_stream2_policy_state_layout[
(offsetof(ZSTD_rust_compressStream2PolicyState, endOp) == 0
&& sizeof(ZSTD_rust_compressStream2PolicyState) == sizeof(int))
? 1 : -1];
int ZSTD_rust_simpleCompress2Level(const void* cctx, size_t srcSize);
typedef int (*ZSTD_rust_simpleCompress2Level_f)(const void* cctx, size_t srcSize);
typedef struct {
@@ -8266,7 +8275,10 @@ size_t ZSTD_compressStream2_c( ZSTD_CCtx* cctx,
/* check conditions */
RETURN_ERROR_IF(output->pos > output->size, dstSize_tooSmall, "invalid output buffer");
RETURN_ERROR_IF(input->pos > input->size, srcSize_wrong, "invalid input buffer");
RETURN_ERROR_IF((U32)endOp > (U32)ZSTD_e_end, parameter_outOfBound, "invalid endDirective");
{ ZSTD_rust_compressStream2PolicyState const state = {(int)endOp};
RETURN_ERROR_IF(!ZSTD_rust_compressStream2Policy(&state),
parameter_outOfBound, "invalid endDirective");
}
assert(cctx != NULL);
/* transparent initialization stage */
+57
View File
@@ -163,6 +163,44 @@ const ZSTD_CHUNKSIZE_MAX: usize = u32::MAX as usize - ZSTD_CURRENT_MAX;
const ZSTD_E_END: c_int = 2;
const ZSTD_E_CONTINUE: c_int = 0;
const ZSTD_E_FLUSH: c_int = 1;
/// Scalar projection for the public `ZSTD_compressStream2` directive policy.
///
/// The C fallback retains the private context and streaming implementation;
/// Rust owns only the public range decision; C retains error encoding and
/// diagnostics.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTD_rust_compressStream2PolicyState {
pub end_op: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressStream2PolicyState, end_op) == 0);
assert!(size_of::<ZSTD_rust_compressStream2PolicyState>() == size_of::<c_int>());
};
#[inline]
fn compress_stream2_policy(end_op: c_int) -> c_int {
if (ZSTD_E_CONTINUE..=ZSTD_E_END).contains(&end_op) {
1
} else {
0
}
}
/// Decide whether the public stream end directive is in range before entering
/// C's private fallback state machine.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressStream2Policy(
state: *const ZSTD_rust_compressStream2PolicyState,
) -> c_int {
let Some(state) = (unsafe { state.as_ref() }) else {
return 0;
};
compress_stream2_policy(state.end_op)
}
const ZSTD_C_WINDOW_LOG: c_int = 101;
const ZSTD_C_HASH_LOG: c_int = 102;
const ZSTD_C_CHAIN_LOG: c_int = 103;
@@ -14033,6 +14071,25 @@ mod tests {
assert_eq!(context.calls, 0);
}
#[test]
fn compress_stream2_policy_accepts_all_public_directives() {
for end_op in [ZSTD_E_CONTINUE, ZSTD_E_FLUSH, ZSTD_E_END] {
let state = ZSTD_rust_compressStream2PolicyState { end_op };
let result = unsafe { ZSTD_rust_compressStream2Policy(&state) };
assert_eq!(result, 1);
}
}
#[test]
fn compress_stream2_policy_rejects_out_of_range_directives() {
for end_op in [c_int::MIN, -1, ZSTD_E_END + 1, c_int::MAX] {
let state = ZSTD_rust_compressStream2PolicyState { end_op };
let result = unsafe { ZSTD_rust_compressStream2Policy(&state) };
assert_eq!(result, 0);
}
assert_eq!(unsafe { ZSTD_rust_compressStream2Policy(ptr::null()) }, 0);
}
#[test]
fn reduce_table_applies_threshold_and_wrapping_subtraction() {
let mut table = [0, 1, 2, 3, 4, 5, 6, u32::MAX, 0, 0, 0, 0, 0, 0, 0, 0];