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.
This commit is contained in:
2026-07-20 02:56:36 +02:00
parent 6c0bcced58
commit 73e0a5e629
2 changed files with 249 additions and 36 deletions
+48 -34
View File
@@ -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;
}
+201 -2
View File
@@ -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<BlockCompressorFn>,
row_block_compressors: *const Option<BlockCompressorFn>,
selected_compressor: *mut Option<BlockCompressorFn>,
}
const _: () = {
assert!(size_of::<BlockCompressorFn>() == size_of::<usize>());
assert!(size_of::<Option<BlockCompressorFn>>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_selectBlockCompressorState, block_compressors) == 0);
assert!(
offset_of!(ZSTD_rust_selectBlockCompressorState, row_block_compressors)
== size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_selectBlockCompressorState, selected_compressor)
== 2 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_selectBlockCompressorState>() == 3 * size_of::<usize>());
};
/// 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<BlockCompressorFn>],
row_block_compressors: &[Option<BlockCompressorFn>],
selected_compressor: &mut Option<BlockCompressorFn>,
) -> 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>,