feat(compress): move CCtx reset policy into Rust
Move ZSTD_CCtx_reset directive handling into a Rust-owned state machine while keeping dictionary cleanup and parameter reset behind C callbacks. Preserve the original session ordering, stage error boundary, completion/max-block flag updates, and reset error propagation across the ABI bridge. 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 - focused reset_cctx unit tests
This commit is contained in:
@@ -247,6 +247,34 @@ typedef char ZSTD_rust_set_params_state_layout[
|
|||||||
&& offsetof(ZSTD_rust_setParamsState, setCParams) == 3 * sizeof(void*)
|
&& offsetof(ZSTD_rust_setParamsState, setCParams) == 3 * sizeof(void*)
|
||||||
&& sizeof(ZSTD_rust_setParamsState) == 4 * sizeof(void*))
|
&& sizeof(ZSTD_rust_setParamsState) == 4 * sizeof(void*))
|
||||||
? 1 : -1];
|
? 1 : -1];
|
||||||
|
typedef void (*ZSTD_rust_resetCCtxClearAllDicts_f)(void* context);
|
||||||
|
typedef size_t (*ZSTD_rust_resetCCtxResetParams_f)(void* context);
|
||||||
|
typedef struct {
|
||||||
|
void* callbackContext;
|
||||||
|
unsigned* rustSimpleCompress2Completed;
|
||||||
|
ZSTD_cStreamStage* streamStage;
|
||||||
|
unsigned long long* pledgedSrcSizePlusOne;
|
||||||
|
unsigned* rustSimpleCompress2MaxBlockSizeSet;
|
||||||
|
ZSTD_rust_resetCCtxClearAllDicts_f clearAllDicts;
|
||||||
|
ZSTD_rust_resetCCtxResetParams_f resetParams;
|
||||||
|
} ZSTD_rust_resetCCtxState;
|
||||||
|
size_t ZSTD_rust_resetCCtx(const ZSTD_rust_resetCCtxState* state, int reset);
|
||||||
|
typedef char ZSTD_rust_reset_cctx_state_layout[
|
||||||
|
(offsetof(ZSTD_rust_resetCCtxState, callbackContext) == 0
|
||||||
|
&& offsetof(ZSTD_rust_resetCCtxState, rustSimpleCompress2Completed)
|
||||||
|
== sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_resetCCtxState, streamStage)
|
||||||
|
== 2 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_resetCCtxState, pledgedSrcSizePlusOne)
|
||||||
|
== 3 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_resetCCtxState, rustSimpleCompress2MaxBlockSizeSet)
|
||||||
|
== 4 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_resetCCtxState, clearAllDicts)
|
||||||
|
== 5 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_resetCCtxState, resetParams)
|
||||||
|
== 6 * sizeof(void*)
|
||||||
|
&& sizeof(ZSTD_rust_resetCCtxState) == 7 * sizeof(void*))
|
||||||
|
? 1 : -1];
|
||||||
ZSTD_frameProgression ZSTD_rust_frameProgression(U64 consumedSrcSize,
|
ZSTD_frameProgression ZSTD_rust_frameProgression(U64 consumedSrcSize,
|
||||||
size_t buffered,
|
size_t buffered,
|
||||||
U64 producedCSize);
|
U64 producedCSize);
|
||||||
@@ -2099,25 +2127,31 @@ size_t ZSTD_CCtx_refPrefix_advanced(
|
|||||||
ZSTD_clearAllDicts_callback, ZSTD_assignPrefixDict_callback);
|
ZSTD_clearAllDicts_callback, ZSTD_assignPrefixDict_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ZSTD_rust_resetCCtx_clearAllDicts(void* context)
|
||||||
|
{
|
||||||
|
ZSTD_clearAllDicts((ZSTD_CCtx*)context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t ZSTD_rust_resetCCtx_resetParams(void* context)
|
||||||
|
{
|
||||||
|
ZSTD_CCtx* const cctx = (ZSTD_CCtx*)context;
|
||||||
|
return ZSTD_CCtxParams_reset(&cctx->requestedParams);
|
||||||
|
}
|
||||||
|
|
||||||
/*! ZSTD_CCtx_reset() :
|
/*! ZSTD_CCtx_reset() :
|
||||||
* Also dumps dictionary */
|
* Also dumps dictionary */
|
||||||
size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset)
|
size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset)
|
||||||
{
|
{
|
||||||
cctx->rustSimpleCompress2Completed = 0;
|
ZSTD_rust_resetCCtxState state;
|
||||||
if ( (reset == ZSTD_reset_session_only)
|
state.callbackContext = cctx;
|
||||||
|| (reset == ZSTD_reset_session_and_parameters) ) {
|
state.rustSimpleCompress2Completed = &cctx->rustSimpleCompress2Completed;
|
||||||
cctx->streamStage = zcss_init;
|
state.streamStage = &cctx->streamStage;
|
||||||
cctx->pledgedSrcSizePlusOne = 0;
|
state.pledgedSrcSizePlusOne = &cctx->pledgedSrcSizePlusOne;
|
||||||
}
|
state.rustSimpleCompress2MaxBlockSizeSet =
|
||||||
if ( (reset == ZSTD_reset_parameters)
|
&cctx->rustSimpleCompress2MaxBlockSizeSet;
|
||||||
|| (reset == ZSTD_reset_session_and_parameters) ) {
|
state.clearAllDicts = ZSTD_rust_resetCCtx_clearAllDicts;
|
||||||
RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,
|
state.resetParams = ZSTD_rust_resetCCtx_resetParams;
|
||||||
"Reset parameters is only possible during init stage.");
|
return ZSTD_rust_resetCCtx(&state, (int)reset);
|
||||||
ZSTD_clearAllDicts(cctx);
|
|
||||||
cctx->rustSimpleCompress2MaxBlockSizeSet = 0;
|
|
||||||
return ZSTD_CCtxParams_reset(&cctx->requestedParams);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,9 @@ const ZSTD_C_STRATEGY: c_int = 107;
|
|||||||
const ZSTD_C_CONTENT_SIZE_FLAG: c_int = 200;
|
const ZSTD_C_CONTENT_SIZE_FLAG: c_int = 200;
|
||||||
const ZSTD_C_CHECKSUM_FLAG: c_int = 201;
|
const ZSTD_C_CHECKSUM_FLAG: c_int = 201;
|
||||||
const ZSTD_C_DICT_ID_FLAG: c_int = 202;
|
const ZSTD_C_DICT_ID_FLAG: c_int = 202;
|
||||||
|
const ZSTD_RESET_SESSION_ONLY: c_int = 1;
|
||||||
|
const ZSTD_RESET_PARAMETERS: c_int = 2;
|
||||||
|
const ZSTD_RESET_SESSION_AND_PARAMETERS: c_int = 3;
|
||||||
const ZSTD_CSTREAM_STAGE_LOAD: c_int = 1;
|
const ZSTD_CSTREAM_STAGE_LOAD: c_int = 1;
|
||||||
const ZSTD_CSTREAM_STAGE_FLUSH: c_int = 2;
|
const ZSTD_CSTREAM_STAGE_FLUSH: c_int = 2;
|
||||||
const ZSTD_BSS_COMPRESS: c_int = 0;
|
const ZSTD_BSS_COMPRESS: c_int = 0;
|
||||||
@@ -814,6 +817,80 @@ pub unsafe extern "C" fn ZSTD_rust_compress2(
|
|||||||
unsafe { compress2_body_with(&*state, dst, dst_capacity, src, src_size) }
|
unsafe { compress2_body_with(&*state, dst, dst_capacity, src, src_size) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResetCCtxClearAllDictsFn = unsafe extern "C" fn(*mut c_void);
|
||||||
|
type ResetCCtxResetParamsFn = unsafe extern "C" fn(*mut c_void) -> usize;
|
||||||
|
|
||||||
|
/// Explicit projection for the public `ZSTD_CCtx_reset` wrapper.
|
||||||
|
///
|
||||||
|
/// Rust owns the reset-directive policy and ordering. The callback context
|
||||||
|
/// remains in C, where the dictionary and parameter reset operations retain
|
||||||
|
/// access to the private `ZSTD_CCtx` layout.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ZSTD_rust_resetCCtxState {
|
||||||
|
callback_context: *mut c_void,
|
||||||
|
rust_simple_compress2_completed: *mut c_uint,
|
||||||
|
stream_stage: *mut c_int,
|
||||||
|
pledged_src_size_plus_one: *mut u64,
|
||||||
|
rust_simple_compress2_max_block_size_set: *mut c_uint,
|
||||||
|
clear_all_dicts: ResetCCtxClearAllDictsFn,
|
||||||
|
reset_params: ResetCCtxResetParamsFn,
|
||||||
|
}
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
assert!(size_of::<ResetCCtxClearAllDictsFn>() == size_of::<usize>());
|
||||||
|
assert!(size_of::<ResetCCtxResetParamsFn>() == size_of::<usize>());
|
||||||
|
assert!(offset_of!(ZSTD_rust_resetCCtxState, callback_context) == 0);
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_resetCCtxState, rust_simple_compress2_completed) == size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(offset_of!(ZSTD_rust_resetCCtxState, stream_stage) == 2 * size_of::<usize>());
|
||||||
|
assert!(
|
||||||
|
offset_of!(ZSTD_rust_resetCCtxState, pledged_src_size_plus_one) == 3 * size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
offset_of!(
|
||||||
|
ZSTD_rust_resetCCtxState,
|
||||||
|
rust_simple_compress2_max_block_size_set
|
||||||
|
) == 4 * size_of::<usize>()
|
||||||
|
);
|
||||||
|
assert!(offset_of!(ZSTD_rust_resetCCtxState, clear_all_dicts) == 5 * size_of::<usize>());
|
||||||
|
assert!(offset_of!(ZSTD_rust_resetCCtxState, reset_params) == 6 * size_of::<usize>());
|
||||||
|
assert!(size_of::<ZSTD_rust_resetCCtxState>() == 7 * size_of::<usize>());
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Drive the public `ZSTD_CCtx_reset` policy through C-owned callbacks.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn ZSTD_rust_resetCCtx(
|
||||||
|
state: *const ZSTD_rust_resetCCtxState,
|
||||||
|
reset: c_int,
|
||||||
|
) -> usize {
|
||||||
|
if state.is_null() {
|
||||||
|
return ERROR(ZstdErrorCode::Generic);
|
||||||
|
}
|
||||||
|
let state = unsafe { &*state };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*state.rust_simple_compress2_completed = 0;
|
||||||
|
}
|
||||||
|
if reset == ZSTD_RESET_SESSION_ONLY || reset == ZSTD_RESET_SESSION_AND_PARAMETERS {
|
||||||
|
unsafe {
|
||||||
|
*state.stream_stage = ZSTD_CSTREAM_STAGE_INIT;
|
||||||
|
*state.pledged_src_size_plus_one = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if reset == ZSTD_RESET_PARAMETERS || reset == ZSTD_RESET_SESSION_AND_PARAMETERS {
|
||||||
|
if unsafe { *state.stream_stage } != ZSTD_CSTREAM_STAGE_INIT {
|
||||||
|
return ERROR(ZstdErrorCode::StageWrong);
|
||||||
|
}
|
||||||
|
unsafe { (state.clear_all_dicts)(state.callback_context) };
|
||||||
|
unsafe {
|
||||||
|
*state.rust_simple_compress2_max_block_size_set = 0;
|
||||||
|
}
|
||||||
|
return unsafe { (state.reset_params)(state.callback_context) };
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
type ResetCStreamResetFn = unsafe extern "C" fn(*mut c_void) -> usize;
|
type ResetCStreamResetFn = unsafe extern "C" fn(*mut c_void) -> usize;
|
||||||
type ResetCStreamSetPledgedSrcSizeFn = unsafe extern "C" fn(*mut c_void, u64) -> usize;
|
type ResetCStreamSetPledgedSrcSizeFn = unsafe extern "C" fn(*mut c_void, u64) -> usize;
|
||||||
|
|
||||||
@@ -9116,6 +9193,171 @@ mod tests {
|
|||||||
assert_eq!(dict_limit, 0x89ab_cdef);
|
assert_eq!(dict_limit, 0x89ab_cdef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ResetCCtxTestContext {
|
||||||
|
events: Vec<&'static str>,
|
||||||
|
rust_simple_compress2_completed: c_uint,
|
||||||
|
stream_stage: c_int,
|
||||||
|
pledged_src_size_plus_one: u64,
|
||||||
|
rust_simple_compress2_max_block_size_set: c_uint,
|
||||||
|
reset_params_result: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn reset_cctx_test_context(context: *mut c_void) -> &'static mut ResetCCtxTestContext {
|
||||||
|
unsafe { &mut *context.cast::<ResetCCtxTestContext>() }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn reset_cctx_test_clear_all_dicts(context: *mut c_void) {
|
||||||
|
unsafe { reset_cctx_test_context(context) }
|
||||||
|
.events
|
||||||
|
.push("clear-dicts");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn reset_cctx_test_reset_params(context: *mut c_void) -> usize {
|
||||||
|
let context = unsafe { reset_cctx_test_context(context) };
|
||||||
|
assert_eq!(context.rust_simple_compress2_max_block_size_set, 0);
|
||||||
|
context.events.push("reset-params");
|
||||||
|
context.reset_params_result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_cctx_test_state(context: &mut ResetCCtxTestContext) -> ZSTD_rust_resetCCtxState {
|
||||||
|
ZSTD_rust_resetCCtxState {
|
||||||
|
callback_context: (context as *mut ResetCCtxTestContext).cast(),
|
||||||
|
rust_simple_compress2_completed: &mut context.rust_simple_compress2_completed,
|
||||||
|
stream_stage: &mut context.stream_stage,
|
||||||
|
pledged_src_size_plus_one: &mut context.pledged_src_size_plus_one,
|
||||||
|
rust_simple_compress2_max_block_size_set: &mut context
|
||||||
|
.rust_simple_compress2_max_block_size_set,
|
||||||
|
clear_all_dicts: reset_cctx_test_clear_all_dicts,
|
||||||
|
reset_params: reset_cctx_test_reset_params,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_cctx_session_only_clears_session_state() {
|
||||||
|
let mut context = ResetCCtxTestContext {
|
||||||
|
rust_simple_compress2_completed: 1,
|
||||||
|
stream_stage: ZSTD_CSTREAM_STAGE_LOAD,
|
||||||
|
pledged_src_size_plus_one: 123,
|
||||||
|
rust_simple_compress2_max_block_size_set: 1,
|
||||||
|
..ResetCCtxTestContext::default()
|
||||||
|
};
|
||||||
|
let state = reset_cctx_test_state(&mut context);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_resetCCtx(&state, ZSTD_RESET_SESSION_ONLY) };
|
||||||
|
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
assert_eq!(context.rust_simple_compress2_completed, 0);
|
||||||
|
assert_eq!(context.stream_stage, ZSTD_CSTREAM_STAGE_INIT);
|
||||||
|
assert_eq!(context.pledged_src_size_plus_one, 0);
|
||||||
|
assert_eq!(context.rust_simple_compress2_max_block_size_set, 1);
|
||||||
|
assert!(context.events.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_cctx_parameters_runs_callbacks_in_order_and_clears_max_block_flag() {
|
||||||
|
let mut context = ResetCCtxTestContext {
|
||||||
|
rust_simple_compress2_completed: 1,
|
||||||
|
stream_stage: ZSTD_CSTREAM_STAGE_INIT,
|
||||||
|
pledged_src_size_plus_one: 123,
|
||||||
|
rust_simple_compress2_max_block_size_set: 1,
|
||||||
|
..ResetCCtxTestContext::default()
|
||||||
|
};
|
||||||
|
let state = reset_cctx_test_state(&mut context);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_resetCCtx(&state, ZSTD_RESET_PARAMETERS) };
|
||||||
|
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
assert_eq!(context.rust_simple_compress2_completed, 0);
|
||||||
|
assert_eq!(context.stream_stage, ZSTD_CSTREAM_STAGE_INIT);
|
||||||
|
assert_eq!(context.pledged_src_size_plus_one, 123);
|
||||||
|
assert_eq!(context.rust_simple_compress2_max_block_size_set, 0);
|
||||||
|
assert_eq!(context.events, ["clear-dicts", "reset-params"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_cctx_session_and_parameters_resets_session_before_callbacks() {
|
||||||
|
let mut context = ResetCCtxTestContext {
|
||||||
|
rust_simple_compress2_completed: 1,
|
||||||
|
stream_stage: ZSTD_CSTREAM_STAGE_LOAD,
|
||||||
|
pledged_src_size_plus_one: 123,
|
||||||
|
rust_simple_compress2_max_block_size_set: 1,
|
||||||
|
..ResetCCtxTestContext::default()
|
||||||
|
};
|
||||||
|
let state = reset_cctx_test_state(&mut context);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_resetCCtx(&state, ZSTD_RESET_SESSION_AND_PARAMETERS) };
|
||||||
|
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
assert_eq!(context.rust_simple_compress2_completed, 0);
|
||||||
|
assert_eq!(context.stream_stage, ZSTD_CSTREAM_STAGE_INIT);
|
||||||
|
assert_eq!(context.pledged_src_size_plus_one, 0);
|
||||||
|
assert_eq!(context.rust_simple_compress2_max_block_size_set, 0);
|
||||||
|
assert_eq!(context.events, ["clear-dicts", "reset-params"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_cctx_parameters_rejects_non_init_stage_before_mutating_parameters() {
|
||||||
|
let mut context = ResetCCtxTestContext {
|
||||||
|
rust_simple_compress2_completed: 1,
|
||||||
|
stream_stage: ZSTD_CSTREAM_STAGE_LOAD,
|
||||||
|
pledged_src_size_plus_one: 123,
|
||||||
|
rust_simple_compress2_max_block_size_set: 1,
|
||||||
|
..ResetCCtxTestContext::default()
|
||||||
|
};
|
||||||
|
let state = reset_cctx_test_state(&mut context);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_resetCCtx(&state, ZSTD_RESET_PARAMETERS) };
|
||||||
|
|
||||||
|
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
|
||||||
|
assert_eq!(context.rust_simple_compress2_completed, 0);
|
||||||
|
assert_eq!(context.stream_stage, ZSTD_CSTREAM_STAGE_LOAD);
|
||||||
|
assert_eq!(context.pledged_src_size_plus_one, 123);
|
||||||
|
assert_eq!(context.rust_simple_compress2_max_block_size_set, 1);
|
||||||
|
assert!(context.events.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_cctx_propagates_parameter_reset_error_after_clearing_state() {
|
||||||
|
let mut context = ResetCCtxTestContext {
|
||||||
|
rust_simple_compress2_completed: 1,
|
||||||
|
stream_stage: ZSTD_CSTREAM_STAGE_INIT,
|
||||||
|
pledged_src_size_plus_one: 123,
|
||||||
|
rust_simple_compress2_max_block_size_set: 1,
|
||||||
|
reset_params_result: ERROR(ZstdErrorCode::MemoryAllocation),
|
||||||
|
..ResetCCtxTestContext::default()
|
||||||
|
};
|
||||||
|
let state = reset_cctx_test_state(&mut context);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_resetCCtx(&state, ZSTD_RESET_PARAMETERS) };
|
||||||
|
|
||||||
|
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
|
||||||
|
assert_eq!(context.rust_simple_compress2_completed, 0);
|
||||||
|
assert_eq!(context.rust_simple_compress2_max_block_size_set, 0);
|
||||||
|
assert_eq!(context.events, ["clear-dicts", "reset-params"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_cctx_ignores_unknown_directives_after_clearing_completion() {
|
||||||
|
let mut context = ResetCCtxTestContext {
|
||||||
|
rust_simple_compress2_completed: 1,
|
||||||
|
stream_stage: ZSTD_CSTREAM_STAGE_LOAD,
|
||||||
|
pledged_src_size_plus_one: 123,
|
||||||
|
rust_simple_compress2_max_block_size_set: 1,
|
||||||
|
..ResetCCtxTestContext::default()
|
||||||
|
};
|
||||||
|
let state = reset_cctx_test_state(&mut context);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_resetCCtx(&state, 99) };
|
||||||
|
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
assert_eq!(context.rust_simple_compress2_completed, 0);
|
||||||
|
assert_eq!(context.stream_stage, ZSTD_CSTREAM_STAGE_LOAD);
|
||||||
|
assert_eq!(context.pledged_src_size_plus_one, 123);
|
||||||
|
assert_eq!(context.rust_simple_compress2_max_block_size_set, 1);
|
||||||
|
assert!(context.events.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ResetCStreamTestContext {
|
struct ResetCStreamTestContext {
|
||||||
events: Vec<&'static str>,
|
events: Vec<&'static str>,
|
||||||
|
|||||||
Reference in New Issue
Block a user