refactor(decompress): move heap mode policy to Rust

`ZSTD_HEAPMODE` was still returned directly from C, and the stack entry
projection also copied the raw macro. That left the one-shot stack-versus-heap
classification in the C configuration shim even though Rust owns the public
decompression branch and stack action.

Keep the build-time override in C, but pass it through a one-field repr(C)
projection to Rust. Rust normalizes values below one to the stack mode and
values at or above one to the heap mode, preserving the existing behavior
while making the branch policy explicit. The C stack object, private DCtx
layout, platform initialization, and trace boundary remain C-owned; moving
those would either duplicate build configuration or cross the requested ABI
boundary. The stack projection now uses the same normalized result as the
one-shot branch.

ABI layout assertions cover the scalar projection, and focused Rust tests cover
negative, zero, positive, maximum, and missing-configuration inputs.

Test Plan:
- `git diff --cached --check` -- passed
- `rustfmt --edition 2021 --check rust/src/zstd_decompress.rs` -- passed
- Cargo, Make, native tests, and heavy commands intentionally not run per the
  task restriction; integration verification remains pending.
This commit is contained in:
2026-07-21 09:12:42 +02:00
parent b0b7441e94
commit cb770812fd
2 changed files with 61 additions and 2 deletions
+48
View File
@@ -321,6 +321,17 @@ const _: () = {
assert!(size_of::<ZSTD_rustNoForwardProgressProjection>() == size_of::<c_int>());
};
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTD_rustHeapModeProjection {
pub configured_mode: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rustHeapModeProjection, configured_mode) == 0);
assert!(size_of::<ZSTD_rustHeapModeProjection>() == size_of::<c_int>());
};
const _: () = {
assert!(offset_of!(ZSTD_rustDctxView, dctx) == 0);
assert!(offset_of!(ZSTD_rustDctxView, static_size) == 29 * size_of::<usize>());
@@ -682,6 +693,21 @@ pub unsafe extern "C" fn ZSTD_rust_no_forward_progress_policy(
projection.configured_max
}
/// Normalize the C build's heap-mode setting to the one-shot branch policy.
///
/// C supplies the compile-time value because it may be overridden by the
/// build. Rust owns the decision that values below one use the stack entry
/// path and values at or above one use an allocated DCtx.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_heapmode_policy(
projection: *const ZSTD_rustHeapModeProjection,
) -> c_int {
let Some(projection) = (unsafe { projection.as_ref() }) else {
return 0;
};
c_int::from(projection.configured_mode >= 1)
}
#[cfg(test)]
mod no_forward_progress_policy_tests {
use super::*;
@@ -706,6 +732,28 @@ mod no_forward_progress_policy_tests {
}
}
#[cfg(test)]
mod heapmode_policy_tests {
use super::*;
#[test]
fn normalizes_stack_and_heap_modes() {
for configured_mode in [c_int::MIN, -1, 0] {
let projection = ZSTD_rustHeapModeProjection { configured_mode };
assert_eq!(unsafe { ZSTD_rust_heapmode_policy(&projection) }, 0);
}
for configured_mode in [1, c_int::MAX] {
let projection = ZSTD_rustHeapModeProjection { configured_mode };
assert_eq!(unsafe { ZSTD_rust_heapmode_policy(&projection) }, 1);
}
}
#[test]
fn rejects_missing_heapmode_configuration() {
assert_eq!(unsafe { ZSTD_rust_heapmode_policy(ptr::null()) }, 0);
}
}
/// Normalize the C build's legacy-support threshold before dispatch.
///
/// C supplies the active compile-time value because test builds intentionally