refactor(decompress): move stack-context dispatch policy to Rust
Keep the heapmode guard, stack-owned ZSTD_DCtx, static initialization, and private context layout in C. Once the context is initialized, Rust now owns the one-shot stack-context policy: it clears the projected static-size marker and dispatches the existing ZSTD_decompressDCtx implementation through the opaque context pointer. Add matching C/Rust view-layout assertions and focused callback tests covering static-size clearing, argument forwarding, and null-context rejection. 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` (all upstream tests completed successfully) - Standalone Rust unit linking remains unavailable without the C-owned bridge symbols; the integrated build and upstream C/Rust oracle supplied the runtime/link coverage.
This commit is contained in:
@@ -106,6 +106,12 @@ typedef struct {
|
||||
size_t dctx_size;
|
||||
} ZSTD_rustDctxView;
|
||||
|
||||
typedef char ZSTD_rust_dctx_view_layout[
|
||||
(offsetof(ZSTD_rustDctxView, dctx) == 0
|
||||
&& offsetof(ZSTD_rustDctxView, static_size) == 29 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rustDctxView, dctx_size) == 67 * sizeof(void*)
|
||||
&& sizeof(ZSTD_rustDctxView) == 68 * sizeof(void*)) ? 1 : -1];
|
||||
|
||||
/* Rust exposes this as u64. Use the trace API's exact unsigned-long-long
|
||||
* representation instead of U64, whose typedef may be a different C type on
|
||||
* some targets even when it has the same width. */
|
||||
@@ -133,6 +139,9 @@ int ZSTD_rust_no_forward_progress_max(void);
|
||||
int ZSTD_rust_heapmode(void);
|
||||
size_t ZSTD_rust_decompress_stack(void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize);
|
||||
size_t ZSTD_rust_decompress_stack_context(ZSTD_DCtx* dctx,
|
||||
void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize);
|
||||
ZSTD_DDict* ZSTD_rust_create_ddict(const void* dict, size_t dictSize,
|
||||
ZSTD_dictLoadMethod_e dictLoadMethod,
|
||||
ZSTD_dictContentType_e dictContentType,
|
||||
@@ -277,11 +286,8 @@ size_t ZSTD_rust_decompress_stack(void* dst, size_t dstCapacity,
|
||||
ZSTD_DCtx dctx;
|
||||
ZSTD_DCtx* const initialized = ZSTD_initStaticDCtx(&dctx, sizeof(dctx));
|
||||
if (initialized == NULL) return ERROR(memory_allocation);
|
||||
/* This is a stack DCtx, not a user-provided static workspace. Keep the
|
||||
* original heapmode=0 semantics so legacy decoding is permitted and no
|
||||
* static-context allocation restrictions leak into the one-shot API. */
|
||||
initialized->staticSize = 0;
|
||||
return ZSTD_decompressDCtx(initialized, dst, dstCapacity, src, srcSize);
|
||||
return ZSTD_rust_decompress_stack_context(initialized, dst, dstCapacity,
|
||||
src, srcSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+133
-1
@@ -26,7 +26,7 @@ use crate::zstd_ddict::{
|
||||
};
|
||||
use std::cmp::{max, min};
|
||||
use std::ffi::c_void;
|
||||
use std::mem::{size_of, MaybeUninit};
|
||||
use std::mem::{align_of, offset_of, size_of, MaybeUninit};
|
||||
use std::os::raw::{c_int, c_uint};
|
||||
use std::ptr;
|
||||
|
||||
@@ -299,6 +299,14 @@ struct ZSTD_rustDctxView {
|
||||
dctx_size: usize,
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(offset_of!(ZSTD_rustDctxView, dctx) == 0);
|
||||
assert!(offset_of!(ZSTD_rustDctxView, static_size) == 29 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rustDctxView, dctx_size) == 67 * size_of::<usize>());
|
||||
assert!(size_of::<ZSTD_rustDctxView>() == 68 * size_of::<usize>());
|
||||
assert!(align_of::<ZSTD_rustDctxView>() == align_of::<usize>());
|
||||
};
|
||||
|
||||
type ZSTD_rustTraceBeginFn = unsafe extern "C" fn(*const c_void) -> u64;
|
||||
type ZSTD_rustTraceEndFn = unsafe extern "C" fn(u64, *const c_void);
|
||||
|
||||
@@ -375,6 +383,9 @@ unsafe extern "C" {
|
||||
fn ZSTD_checkContinuity(dctx: *mut ZSTD_DCtx, dst: *const c_void, dst_size: usize);
|
||||
}
|
||||
|
||||
type ZstdDecompressDCtxFn =
|
||||
unsafe extern "C" fn(*mut ZSTD_DCtx, *mut c_void, usize, *const c_void, usize) -> usize;
|
||||
|
||||
#[inline]
|
||||
unsafe fn field<T: Copy>(slot: *mut c_void) -> T {
|
||||
unsafe { slot.cast::<T>().read() }
|
||||
@@ -2867,6 +2878,88 @@ mod dctx_trace_tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod stack_context_tests {
|
||||
use super::*;
|
||||
|
||||
#[repr(C)]
|
||||
struct StackDispatchProbe {
|
||||
static_size: usize,
|
||||
calls: usize,
|
||||
dctx: usize,
|
||||
dst: usize,
|
||||
dst_capacity: usize,
|
||||
src: usize,
|
||||
src_size: usize,
|
||||
result: usize,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn dispatch_probe(
|
||||
dctx: *mut ZSTD_DCtx,
|
||||
dst: *mut c_void,
|
||||
dst_capacity: usize,
|
||||
src: *const c_void,
|
||||
src_size: usize,
|
||||
) -> usize {
|
||||
let probe = unsafe { &mut *dctx.cast::<StackDispatchProbe>() };
|
||||
probe.calls += 1;
|
||||
probe.dctx = dctx as usize;
|
||||
probe.dst = dst as usize;
|
||||
probe.dst_capacity = dst_capacity;
|
||||
probe.src = src as usize;
|
||||
probe.src_size = src_size;
|
||||
probe.result
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stack_context_callback_clears_static_size_before_dispatch() {
|
||||
let mut probe = StackDispatchProbe {
|
||||
static_size: usize::MAX,
|
||||
calls: 0,
|
||||
dctx: 0,
|
||||
dst: 0,
|
||||
dst_capacity: 0,
|
||||
src: 0,
|
||||
src_size: 0,
|
||||
result: 0x1234,
|
||||
};
|
||||
let mut view = unsafe { MaybeUninit::<ZSTD_rustDctxView>::zeroed().assume_init() };
|
||||
view.dctx = (&mut probe as *mut StackDispatchProbe).cast();
|
||||
view.static_size = std::ptr::addr_of_mut!(probe.static_size).cast();
|
||||
|
||||
let mut dst = [0u8; 8];
|
||||
let src = [1u8, 2, 3];
|
||||
let result = unsafe {
|
||||
decompress_stack_context(
|
||||
&view,
|
||||
dst.as_mut_ptr().cast(),
|
||||
dst.len(),
|
||||
src.as_ptr().cast(),
|
||||
src.len(),
|
||||
dispatch_probe,
|
||||
)
|
||||
};
|
||||
|
||||
assert_eq!(result, probe.result);
|
||||
assert_eq!(probe.calls, 1);
|
||||
assert_eq!(probe.static_size, 0);
|
||||
assert_eq!(probe.dctx, view.dctx as usize);
|
||||
assert_eq!(probe.dst, dst.as_mut_ptr() as usize);
|
||||
assert_eq!(probe.dst_capacity, dst.len());
|
||||
assert_eq!(probe.src, src.as_ptr() as usize);
|
||||
assert_eq!(probe.src_size, src.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stack_context_callback_rejects_null_context() {
|
||||
let result = unsafe {
|
||||
ZSTD_rust_decompress_stack_context(ptr::null_mut(), ptr::null_mut(), 0, ptr::null(), 0)
|
||||
};
|
||||
|
||||
assert_eq!(result, ERROR(ZstdErrorCode::Generic));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn select_frame_ddict(view: &ZSTD_rustDctxView) {
|
||||
let set: *mut DDictHashSet = unsafe { field(view.ddict_set) };
|
||||
if set.is_null() || unsafe { dctx_ddict(view) }.is_null() {
|
||||
@@ -3483,6 +3576,45 @@ pub unsafe extern "C" fn ZSTD_decompressDCtx(
|
||||
unsafe { ZSTD_decompress_usingDDict(dctx, dst, dst_capacity, src, src_size, ddict) }
|
||||
}
|
||||
|
||||
unsafe fn decompress_stack_context(
|
||||
view: &ZSTD_rustDctxView,
|
||||
dst: *mut c_void,
|
||||
dst_capacity: usize,
|
||||
src: *const c_void,
|
||||
src_size: usize,
|
||||
dispatch: ZstdDecompressDCtxFn,
|
||||
) -> usize {
|
||||
if view.dctx.is_null() || view.static_size.is_null() {
|
||||
return ERROR(ZstdErrorCode::Generic);
|
||||
}
|
||||
/* A stack DCtx is not a user-provided static workspace. Clear the
|
||||
* projected marker before dispatch so heapmode=0 retains the historical
|
||||
* one-shot behavior, including legacy decoding and allocations. */
|
||||
unsafe { set_field(view.static_size, 0usize) };
|
||||
unsafe { dispatch(view.dctx.cast(), dst, dst_capacity, src, src_size) }
|
||||
}
|
||||
|
||||
/// Rust policy/dispatch for the C-owned stack context used by heapmode=0.
|
||||
///
|
||||
/// The context remains opaque here; the C projection supplies only the field
|
||||
/// addresses Rust needs and keeps `ZSTD_DCtx_s` layout private to C.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ZSTD_rust_decompress_stack_context(
|
||||
dctx: *mut ZSTD_DCtx,
|
||||
dst: *mut c_void,
|
||||
dst_capacity: usize,
|
||||
src: *const c_void,
|
||||
src_size: usize,
|
||||
) -> usize {
|
||||
if dctx.is_null() {
|
||||
return ERROR(ZstdErrorCode::Generic);
|
||||
}
|
||||
let view = unsafe { dctx_view(dctx) };
|
||||
unsafe {
|
||||
decompress_stack_context(&view, dst, dst_capacity, src, src_size, ZSTD_decompressDCtx)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ZSTD_decompress(
|
||||
dst: *mut c_void,
|
||||
|
||||
Reference in New Issue
Block a user