diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index e4d93c62b..53748758c 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -248,6 +248,8 @@ ZSTD_RUST_LDM_OFFSET_CHECK( #undef ZSTD_RUST_LDM_OFFSET_CHECK typedef char ZSTD_rust_ldm_rep_count[(ZSTD_REP_NUM == 3) ? 1 : -1]; +typedef char ZSTD_rust_ldm_strategy_values_check[ + (ZSTD_fast == 1 && ZSTD_dfast == 2 && ZSTD_btultra2 == 9) ? 1 : -1]; void ZSTD_rust_ldm_adjustParameters( void* params, U32 windowLog, int strategy, @@ -270,6 +272,14 @@ size_t ZSTD_rust_ldm_blockCompress( const void* src, size_t srcSize, U32 minMatch, int strategy); U32 ZSTD_rust_ldm_limitTableUpdate(U32 curr, U32 nextToUpdate); +enum { + ZSTD_RUST_LDM_FAST_TABLES_NONE = 0, + ZSTD_RUST_LDM_FAST_TABLES_FAST = 1, + ZSTD_RUST_LDM_FAST_TABLES_DFAST = 2 +}; + +int ZSTD_rust_ldm_fastTableKind(int strategy); + const U64* ZSTD_ldm_rust_gearTable(void); void ZSTD_ldm_rust_prepareBlock(void* blockContext, const void* anchor); size_t ZSTD_ldm_rust_compressLiterals( @@ -289,24 +299,18 @@ typedef struct { static void ZSTD_rust_ldm_fillFastTables(ZSTD_MatchState_t* ms, const BYTE* end) { - switch (ms->cParams.strategy) { - case ZSTD_fast: + switch (ZSTD_rust_ldm_fastTableKind((int)ms->cParams.strategy)) { + case ZSTD_RUST_LDM_FAST_TABLES_FAST: ZSTD_fillHashTable(ms, end, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx); break; - case ZSTD_dfast: + case ZSTD_RUST_LDM_FAST_TABLES_DFAST: #ifndef ZSTD_EXCLUDE_DFAST_BLOCK_COMPRESSOR ZSTD_fillDoubleHashTable(ms, end, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx); #else assert(0); #endif break; - case ZSTD_greedy: - case ZSTD_lazy: - case ZSTD_lazy2: - case ZSTD_btlazy2: - case ZSTD_btopt: - case ZSTD_btultra: - case ZSTD_btultra2: + case ZSTD_RUST_LDM_FAST_TABLES_NONE: break; default: assert(0); diff --git a/rust/src/zstd_ldm.rs b/rust/src/zstd_ldm.rs index e9a2df1f5..ea8fc9f41 100644 --- a/rust/src/zstd_ldm.rs +++ b/rust/src/zstd_ldm.rs @@ -23,8 +23,16 @@ const LDM_MIN_MATCH_LENGTH: u32 = 64; const HASH_READ_SIZE: usize = 8; const ZSTD_REP_NUM: usize = 3; const ZSTD_WINDOW_START_INDEX: u32 = 2; +const ZSTD_FAST_STRATEGY: c_int = 1; +const ZSTD_DFAST_STRATEGY: c_int = 2; +const ZSTD_STRATEGY_MAX: c_int = 9; const ZSTD_BTOPT: c_int = 7; +const LDM_FAST_TABLES_NONE: c_int = 0; +const LDM_FAST_TABLES_FAST: c_int = 1; +const LDM_FAST_TABLES_DFAST: c_int = 2; +const LDM_FAST_TABLES_INVALID: c_int = -1; + #[repr(C)] #[derive(Clone, Copy)] struct LdmEntry { @@ -196,6 +204,20 @@ pub extern "C" fn ZSTD_rust_ldm_limitTableUpdate(curr: u32, next_to_update: u32) } } +/// Classify the C-owned table update needed before an LDM literal segment. +/// +/// The table fill itself stays in C because it owns the private match state; +/// Rust owns only this deterministic strategy policy. +#[no_mangle] +pub extern "C" fn ZSTD_rust_ldm_fastTableKind(strategy: c_int) -> c_int { + match strategy { + ZSTD_FAST_STRATEGY => LDM_FAST_TABLES_FAST, + ZSTD_DFAST_STRATEGY => LDM_FAST_TABLES_DFAST, + 3..=ZSTD_STRATEGY_MAX => LDM_FAST_TABLES_NONE, + _ => LDM_FAST_TABLES_INVALID, + } +} + #[inline] unsafe fn ldm_bucket(hash_table: *mut LdmEntry, hash: u32, bucket_size_log: u32) -> *mut LdmEntry { unsafe { hash_table.add((hash as usize) << bucket_size_log) } @@ -1075,6 +1097,31 @@ mod tests { assert!(use_optimal_parser(9)); } + #[test] + fn fast_table_kind_preserves_c_strategy_dispatch() { + assert_eq!( + ZSTD_rust_ldm_fastTableKind(ZSTD_FAST_STRATEGY), + LDM_FAST_TABLES_FAST + ); + assert_eq!( + ZSTD_rust_ldm_fastTableKind(ZSTD_DFAST_STRATEGY), + LDM_FAST_TABLES_DFAST + ); + for strategy in 3..=ZSTD_STRATEGY_MAX { + assert_eq!(ZSTD_rust_ldm_fastTableKind(strategy), LDM_FAST_TABLES_NONE); + } + } + + #[test] + fn fast_table_kind_rejects_invalid_strategy_values() { + assert_eq!(ZSTD_rust_ldm_fastTableKind(0), LDM_FAST_TABLES_INVALID); + assert_eq!( + ZSTD_rust_ldm_fastTableKind(ZSTD_STRATEGY_MAX + 1), + LDM_FAST_TABLES_INVALID + ); + assert_eq!(ZSTD_rust_ldm_fastTableKind(-1), LDM_FAST_TABLES_INVALID); + } + #[test] fn raw_sequence_skipping_merges_short_tail_matches() { let mut sequences = [