refactor(compress): move CCtx estimate tier policy to Rust

Move the fixed source-size tier traversal used by
ZSTD_estimateCCtxSize_internal() into Rust.  C retains the authoritative
parameter lookup and per-tier workspace estimator behind callbacks, while Rust
owns the tier ordering and maximum-selection policy.  The projection includes
ABI layout assertions and tests for callback order, level propagation, and
error-valued maxima.

Test Plan:
- `ulimit -v 41943040; cargo +nightly fmt --manifest-path rust/Cargo.toml --all -- --check`
- `ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings`
- `ulimit -v 41943040; make -j1`
- `ulimit -v 41943040; make -j1 -C tests test`
- `ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings`
- `ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml --all-targets`

The integrated checks ran at the combined working-tree tip under a serial
40 GiB virtual-memory limit. Standalone root Rust unit linking remains
unavailable because the crate imports C-owned bridge symbols without a Cargo
build/link setup.
This commit is contained in:
2026-07-20 09:49:37 +02:00
parent dd87b328a4
commit 4409ef1e16
2 changed files with 161 additions and 10 deletions
+117
View File
@@ -8588,6 +8588,57 @@ fn max_estimate_cctx_size(
estimate0.max(estimate1).max(estimate2).max(estimate3)
}
type EstimateCCtxSizeGetCParamsFn =
unsafe extern "C" fn(*mut c_void, c_int, u64) -> ZSTD_compressionParameters;
type EstimateCCtxSizeEstimateFn =
unsafe extern "C" fn(*mut c_void, ZSTD_compressionParameters) -> usize;
/// Rust owns the fixed source-size tier policy for CCtx estimates. C keeps
/// the configuration-sensitive parameter lookup and workspace estimator behind
/// callbacks, so this seam does not expose any private context layout.
#[repr(C)]
pub struct ZSTD_rust_estimateCCtxSizeState {
callback_context: *mut c_void,
compression_level: c_int,
get_c_params: EstimateCCtxSizeGetCParamsFn,
estimate: EstimateCCtxSizeEstimateFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_estimateCCtxSizeState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_estimateCCtxSizeState, compression_level) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_estimateCCtxSizeState, get_c_params) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_estimateCCtxSizeState, estimate) == 3 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_estimateCCtxSizeState>() == 4 * size_of::<usize>());
};
/// Evaluate the four source-size tiers used by `ZSTD_estimateCCtxSize()` and
/// return the largest raw result, including size_t-encoded error values.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_estimateCCtxSizeInternal(
state: *const ZSTD_rust_estimateCCtxSizeState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let source_size_tiers = [16 * 1024, 128 * 1024, 256 * 1024, ZSTD_CONTENTSIZE_UNKNOWN];
let mut estimates = [0usize; 4];
for (estimate, &source_size_hint) in estimates.iter_mut().zip(source_size_tiers.iter()) {
let c_params = unsafe {
(state.get_c_params)(
state.callback_context,
state.compression_level,
source_size_hint,
)
};
*estimate = unsafe { (state.estimate)(state.callback_context, c_params) };
}
max_estimate_cctx_size(estimates[0], estimates[1], estimates[2], estimates[3])
}
/// Return the largest raw estimate, including any C size_t error values.
#[no_mangle]
pub extern "C" fn ZSTD_rust_maxEstimateCCtxSize(
@@ -10177,6 +10228,41 @@ mod tests {
const ZSTD_BTOPT: c_int = 7;
const ZSTD_BTULTRA2: c_int = 9;
struct EstimateCCtxSizeTestContext {
compression_levels: Vec<c_int>,
source_size_hints: Vec<u64>,
parameter_tiers: Vec<u32>,
}
unsafe extern "C" fn estimate_cctx_size_test_get_c_params(
context: *mut c_void,
compression_level: c_int,
source_size_hint: u64,
) -> ZSTD_compressionParameters {
let context = unsafe { &mut *(context.cast::<EstimateCCtxSizeTestContext>()) };
context.compression_levels.push(compression_level);
context.source_size_hints.push(source_size_hint);
ZSTD_compressionParameters {
windowLog: context.source_size_hints.len() as u32,
..ZSTD_compressionParameters::default()
}
}
unsafe extern "C" fn estimate_cctx_size_test_estimate(
context: *mut c_void,
c_params: ZSTD_compressionParameters,
) -> usize {
let context = unsafe { &mut *(context.cast::<EstimateCCtxSizeTestContext>()) };
context.parameter_tiers.push(c_params.windowLog);
match c_params.windowLog {
1 => 17,
2 => 91,
3 => 23,
4 => 67,
_ => ERROR(ZstdErrorCode::Generic),
}
}
struct GenerateSequencesTestContext {
events: Vec<&'static str>,
target_c_block_size: c_int,
@@ -19906,6 +19992,37 @@ mod tests {
);
}
#[test]
fn estimate_cctx_size_internal_preserves_tier_order_and_max_policy() {
let mut context = EstimateCCtxSizeTestContext {
compression_levels: Vec::new(),
source_size_hints: Vec::new(),
parameter_tiers: Vec::new(),
};
let state = ZSTD_rust_estimateCCtxSizeState {
callback_context: (&mut context as *mut EstimateCCtxSizeTestContext).cast(),
compression_level: 7,
get_c_params: estimate_cctx_size_test_get_c_params,
estimate: estimate_cctx_size_test_estimate,
};
assert_eq!(unsafe { ZSTD_rust_estimateCCtxSizeInternal(&state) }, 91);
assert_eq!(context.compression_levels, vec![7; 4]);
assert_eq!(
context.source_size_hints,
vec![16 * 1024, 128 * 1024, 256 * 1024, ZSTD_CONTENTSIZE_UNKNOWN]
);
assert_eq!(context.parameter_tiers, vec![1, 2, 3, 4]);
}
#[test]
fn estimate_cctx_size_internal_rejects_a_null_state() {
assert_eq!(
unsafe { ZSTD_rust_estimateCCtxSizeInternal(ptr::null()) },
ERROR(ZstdErrorCode::Generic)
);
}
#[test]
fn public_one_shot_abi_is_c_compatible() {
let entry: unsafe extern "C" fn(*mut c_void, usize, *const c_void, usize, c_int) -> usize =