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:
+201
-2
@@ -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>,
|
||||
|
||||
Reference in New Issue
Block a user