From 73e0a5e629f13e222854264385ca34c07370d3da Mon Sep 17 00:00:00 2001 From: ddidderr Date: Mon, 20 Jul 2026 02:56:36 +0200 Subject: [PATCH] feat(compress): move block compressor dispatch policy to Rust `ZSTD_selectBlockCompressor` already delegated scalar strategy/index calculation to the Rust parameter module, but C still owned table selection and dictionary-mode offsets. Keep the configuration-dependent C function- pointer tables and NULL entries for excluded compressors as private leaves, and pass those tables through a narrow ABI state so Rust owns row versus ordinary selection, dictionary-mode indexing, and selected callback publication. The legacy C selector signature remains intact for the LDM caller. Test Plan: - `rustfmt --edition 2021 --check rust/src/zstd_compress.rs` -- passed. - `git diff --check` and `git diff --cached --check` -- passed. - Heavy cargo/make/test/fuzzer verification not run per the enforced 40 GiB serial verification limit. --- lib/compress/zstd_compress.c | 82 ++++++++------ rust/src/zstd_compress.rs | 203 ++++++++++++++++++++++++++++++++++- 2 files changed, 249 insertions(+), 36 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 543d87793..959dcee95 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1793,7 +1793,6 @@ size_t ZSTD_rust_params_getBlockSize(size_t maxBlockSize, U32 windowLog); int ZSTD_rust_params_resolveExternalSequenceValidation(int mode); int ZSTD_rust_params_rowMatchFinderSupported(int strategy); int ZSTD_rust_params_rowMatchFinderUsed(int strategy, int mode); -int ZSTD_rust_params_selectBlockCompressor(int strategy, int mode); int ZSTD_rust_params_resolveRowMatchFinderMode( int mode, ZSTD_compressionParameters cParams); int ZSTD_rust_params_resolveBlockSplitterMode( @@ -1811,6 +1810,23 @@ int ZSTD_rust_params_getCParamMode(int cdict_present, int cdict_strategy, int params_attachDictPref, int params_forceWindow); +typedef struct { + const ZSTD_BlockCompressor_f* blockCompressors; + const ZSTD_BlockCompressor_f* rowBlockCompressors; + ZSTD_BlockCompressor_f* selectedCompressor; +} ZSTD_rust_selectBlockCompressorState; +size_t ZSTD_rust_selectBlockCompressor( + const ZSTD_rust_selectBlockCompressorState* state, + int strategy, int useRowMatchFinder, int dictMode); +typedef char ZSTD_rust_select_block_compressor_state_layout[ + (offsetof(ZSTD_rust_selectBlockCompressorState, blockCompressors) == 0 + && offsetof(ZSTD_rust_selectBlockCompressorState, rowBlockCompressors) + == sizeof(void*) + && offsetof(ZSTD_rust_selectBlockCompressorState, selectedCompressor) + == 2 * sizeof(void*) + && sizeof(ZSTD_rust_selectBlockCompressorState) == 3 * sizeof(void*)) + ? 1 : -1]; + /* CCtx parameter state is mirrored by rust/src/zstd_compress_params_api.rs. * Rust owns parameter bounds and clamping; C exposes only the * build-configuration values required by that narrow ABI. */ @@ -5123,43 +5139,41 @@ ZSTD_BlockCompressor_f ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_Para NULL, NULL } }; - ZSTD_BlockCompressor_f selectedCompressor; - int selectedCompressorIndex; + static const ZSTD_BlockCompressor_f rowBasedBlockCompressors[4][3] = { + { + ZSTD_COMPRESSBLOCK_GREEDY_ROW, + ZSTD_COMPRESSBLOCK_LAZY_ROW, + ZSTD_COMPRESSBLOCK_LAZY2_ROW + }, + { + ZSTD_COMPRESSBLOCK_GREEDY_EXTDICT_ROW, + ZSTD_COMPRESSBLOCK_LAZY_EXTDICT_ROW, + ZSTD_COMPRESSBLOCK_LAZY2_EXTDICT_ROW + }, + { + ZSTD_COMPRESSBLOCK_GREEDY_DICTMATCHSTATE_ROW, + ZSTD_COMPRESSBLOCK_LAZY_DICTMATCHSTATE_ROW, + ZSTD_COMPRESSBLOCK_LAZY2_DICTMATCHSTATE_ROW + }, + { + ZSTD_COMPRESSBLOCK_GREEDY_DEDICATEDDICTSEARCH_ROW, + ZSTD_COMPRESSBLOCK_LAZY_DEDICATEDDICTSEARCH_ROW, + ZSTD_COMPRESSBLOCK_LAZY2_DEDICATEDDICTSEARCH_ROW + } + }; + ZSTD_rust_selectBlockCompressorState state; + ZSTD_BlockCompressor_f selectedCompressor = NULL; + size_t selectionResult; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, (int)strat)); DEBUGLOG(5, "Selected block compressor: dictMode=%d strat=%d rowMatchfinder=%d", (int)dictMode, (int)strat, (int)useRowMatchFinder); - selectedCompressorIndex = ZSTD_rust_params_selectBlockCompressor( - (int)strat, (int)useRowMatchFinder); - if (selectedCompressorIndex < 3) { - static const ZSTD_BlockCompressor_f rowBasedBlockCompressors[4][3] = { - { - ZSTD_COMPRESSBLOCK_GREEDY_ROW, - ZSTD_COMPRESSBLOCK_LAZY_ROW, - ZSTD_COMPRESSBLOCK_LAZY2_ROW - }, - { - ZSTD_COMPRESSBLOCK_GREEDY_EXTDICT_ROW, - ZSTD_COMPRESSBLOCK_LAZY_EXTDICT_ROW, - ZSTD_COMPRESSBLOCK_LAZY2_EXTDICT_ROW - }, - { - ZSTD_COMPRESSBLOCK_GREEDY_DICTMATCHSTATE_ROW, - ZSTD_COMPRESSBLOCK_LAZY_DICTMATCHSTATE_ROW, - ZSTD_COMPRESSBLOCK_LAZY2_DICTMATCHSTATE_ROW - }, - { - ZSTD_COMPRESSBLOCK_GREEDY_DEDICATEDDICTSEARCH_ROW, - ZSTD_COMPRESSBLOCK_LAZY_DEDICATEDDICTSEARCH_ROW, - ZSTD_COMPRESSBLOCK_LAZY2_DEDICATEDDICTSEARCH_ROW - } - }; - DEBUGLOG(5, "Selecting a row-based matchfinder"); - assert(useRowMatchFinder != ZSTD_ps_auto); - selectedCompressor = rowBasedBlockCompressors[(int)dictMode][selectedCompressorIndex]; - } else { - selectedCompressor = blockCompressor[(int)dictMode][selectedCompressorIndex - 3]; - } + state.blockCompressors = &blockCompressor[0][0]; + state.rowBlockCompressors = &rowBasedBlockCompressors[0][0]; + state.selectedCompressor = &selectedCompressor; + selectionResult = ZSTD_rust_selectBlockCompressor( + &state, (int)strat, (int)useRowMatchFinder, (int)dictMode); + assert(!ERR_isError(selectionResult)); assert(selectedCompressor != NULL); return selectedCompressor; } diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index 26574caad..7635908eb 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -34,8 +34,9 @@ use crate::zstd_compress_params::{ ZSTD_rust_params_checkCParams, ZSTD_rust_params_defaultCLevel, ZSTD_rust_params_estimateMatchStateSize, ZSTD_rust_params_getParamsInternal, ZSTD_rust_params_maxNbSeq, ZSTD_rust_params_resolveMaxBlockSize, - ZSTD_rust_params_rowMatchFinderUsed, ZSTD_rust_params_selectCParams, - ZSTD_RUST_CPM_NO_ATTACH_DICT, ZSTD_RUST_PS_AUTO, ZSTD_RUST_PS_DISABLE, ZSTD_RUST_PS_ENABLE, + ZSTD_rust_params_rowMatchFinderUsed, ZSTD_rust_params_selectBlockCompressor, + ZSTD_rust_params_selectCParams, ZSTD_RUST_CPM_NO_ATTACH_DICT, ZSTD_RUST_PS_AUTO, + ZSTD_RUST_PS_DISABLE, ZSTD_RUST_PS_ENABLE, }; use crate::zstd_compress_params_api::{ ZSTD_CCtxParams_setParameter, ZSTD_CCtx_params, ZSTD_customMem, ZSTD_rust_isUpdateAuthorized, @@ -191,6 +192,89 @@ type BuildSeqStoreTryExternalProducerFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize, *mut c_int, *mut c_int) -> usize; type BuildSeqStoreClearLdmFn = unsafe extern "C" fn(*mut c_void); +type BlockCompressorFn = + unsafe extern "C" fn(*mut c_void, *mut SeqStore_t, *mut u32, *const c_void, usize) -> usize; + +const ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH: usize = 10; +const ZSTD_RUST_ROW_BLOCK_COMPRESSOR_TABLE_WIDTH: usize = 3; +const ZSTD_RUST_DICT_MODE_COUNT: c_int = 4; +const ZSTD_RUST_BLOCK_COMPRESSOR_MAX_STRATEGY: c_int = + ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH as c_int - 1; + +/// C supplies the configuration-dependent compressor tables and their private +/// match-state leaves. Rust owns the strategy/row-mode index and dictionary +/// mode dispatch into those tables. +#[repr(C)] +pub struct ZSTD_rust_selectBlockCompressorState { + block_compressors: *const Option, + row_block_compressors: *const Option, + selected_compressor: *mut Option, +} + +const _: () = { + assert!(size_of::() == size_of::()); + assert!(size_of::>() == size_of::()); + assert!(offset_of!(ZSTD_rust_selectBlockCompressorState, block_compressors) == 0); + assert!( + offset_of!(ZSTD_rust_selectBlockCompressorState, row_block_compressors) + == size_of::() + ); + assert!( + offset_of!(ZSTD_rust_selectBlockCompressorState, selected_compressor) + == 2 * size_of::() + ); + assert!(size_of::() == 3 * size_of::()); +}; + +/// Select one C-owned match-state compressor while keeping the table policy +/// and dispatch-index arithmetic in Rust. +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_selectBlockCompressor( + state: *const ZSTD_rust_selectBlockCompressorState, + strategy: c_int, + use_row_match_finder: c_int, + dict_mode: c_int, +) -> usize { + if state.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + let state = unsafe { &*state }; + if state.block_compressors.is_null() + || state.row_block_compressors.is_null() + || state.selected_compressor.is_null() + || !(ZSTD_FAST..=ZSTD_RUST_BLOCK_COMPRESSOR_MAX_STRATEGY).contains(&strategy) + || !(0..ZSTD_RUST_DICT_MODE_COUNT).contains(&dict_mode) + { + return ERROR(ZstdErrorCode::Generic); + } + + unsafe { *state.selected_compressor = None }; + let selected_index = ZSTD_rust_params_selectBlockCompressor(strategy, use_row_match_finder); + let (table, table_width, table_index) = if selected_index < 3 { + ( + state.row_block_compressors, + ZSTD_RUST_ROW_BLOCK_COMPRESSOR_TABLE_WIDTH, + selected_index, + ) + } else { + ( + state.block_compressors, + ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH, + selected_index - 3, + ) + }; + if table_index < 0 { + return ERROR(ZstdErrorCode::Generic); + } + + let callback = unsafe { *table.add(dict_mode as usize * table_width + table_index as usize) }; + let Some(callback) = callback else { + return ERROR(ZstdErrorCode::Generic); + }; + unsafe { *state.selected_compressor = Some(callback) }; + 0 +} + /// Explicit projection for the sequence-store builder. /// /// Rust owns the threshold/reset/repcode/literal-store orchestration. The @@ -9430,6 +9514,121 @@ mod tests { const ZSTD_BTOPT: c_int = 7; const ZSTD_BTULTRA2: c_int = 9; + unsafe extern "C" fn select_block_compressor_test_default( + _match_state: *mut c_void, + _seq_store: *mut SeqStore_t, + _rep: *mut u32, + _src: *const c_void, + _src_size: usize, + ) -> usize { + 7 + } + + unsafe extern "C" fn select_block_compressor_test_ordinary( + _match_state: *mut c_void, + _seq_store: *mut SeqStore_t, + _rep: *mut u32, + _src: *const c_void, + _src_size: usize, + ) -> usize { + 41 + } + + unsafe extern "C" fn select_block_compressor_test_row( + _match_state: *mut c_void, + _seq_store: *mut SeqStore_t, + _rep: *mut u32, + _src: *const c_void, + _src_size: usize, + ) -> usize { + 43 + } + + fn select_block_compressor_test_state( + block_compressors: &[Option], + row_block_compressors: &[Option], + selected_compressor: &mut Option, + ) -> ZSTD_rust_selectBlockCompressorState { + ZSTD_rust_selectBlockCompressorState { + block_compressors: block_compressors.as_ptr(), + row_block_compressors: row_block_compressors.as_ptr(), + selected_compressor, + } + } + + #[test] + fn select_block_compressor_dispatches_ordinary_and_row_tables() { + let default = Some(select_block_compressor_test_default as BlockCompressorFn); + let mut block_compressors = [default; 4 * ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH]; + let mut row_block_compressors = [default; 4 * ZSTD_RUST_ROW_BLOCK_COMPRESSOR_TABLE_WIDTH]; + block_compressors[ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH + 2] = + Some(select_block_compressor_test_ordinary as BlockCompressorFn); + row_block_compressors[3 * ZSTD_RUST_ROW_BLOCK_COMPRESSOR_TABLE_WIDTH] = + Some(select_block_compressor_test_row as BlockCompressorFn); + let mut selected_compressor = None; + let state = select_block_compressor_test_state( + &block_compressors, + &row_block_compressors, + &mut selected_compressor, + ); + + assert_eq!( + unsafe { ZSTD_rust_selectBlockCompressor(&state, ZSTD_DFAST, ZSTD_RUST_PS_DISABLE, 1) }, + 0 + ); + let ordinary = selected_compressor.expect("ordinary compressor should be selected"); + assert_eq!( + unsafe { + ordinary( + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null(), + 0, + ) + }, + 41 + ); + + assert_eq!( + unsafe { ZSTD_rust_selectBlockCompressor(&state, ZSTD_GREEDY, ZSTD_RUST_PS_ENABLE, 3) }, + 0 + ); + let row = selected_compressor.expect("row compressor should be selected"); + assert_eq!( + unsafe { + row( + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null(), + 0, + ) + }, + 43 + ); + } + + #[test] + fn select_block_compressor_rejects_an_excluded_dispatch_leaf() { + let default = Some(select_block_compressor_test_default as BlockCompressorFn); + let mut block_compressors = [default; 4 * ZSTD_RUST_BLOCK_COMPRESSOR_TABLE_WIDTH]; + let row_block_compressors = [default; 4 * ZSTD_RUST_ROW_BLOCK_COMPRESSOR_TABLE_WIDTH]; + block_compressors[1] = None; + let mut selected_compressor = None; + let state = select_block_compressor_test_state( + &block_compressors, + &row_block_compressors, + &mut selected_compressor, + ); + + let result = + unsafe { ZSTD_rust_selectBlockCompressor(&state, ZSTD_FAST, ZSTD_RUST_PS_DISABLE, 0) }; + + assert!(ERR_isError(result)); + assert!(selected_compressor.is_none()); + } + #[derive(Default)] struct BuildSeqStoreSelectProbe { events: Vec<&'static str>,