feat(compress): move CDict query leaves into Rust

Move CDict compression-parameter and dictionary-ID snapshot reads behind a
Rust-owned projection while retaining the private C field access and the
existing non-null assertion for ZSTD_getCParamsFromCDict. Preserve the public
NULL dictionary-ID result and ABI-compatible compression-parameter return.

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
- make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s
- focused cdict_query_projection unit tests
This commit is contained in:
2026-07-19 14:26:34 +02:00
parent 993fd0acbe
commit 724c7c60fa
2 changed files with 104 additions and 3 deletions
+21 -3
View File
@@ -1277,6 +1277,19 @@ typedef char ZSTD_rust_compress_begin_using_cdict_state_layout[
&& sizeof(ZSTD_rust_compressBeginUsingCDictState)
== 12 * sizeof(void*))
? 1 : -1];
typedef struct {
const ZSTD_compressionParameters* cParams;
const unsigned* dictID;
} ZSTD_rust_cdictQueryState;
ZSTD_compressionParameters ZSTD_rust_getCParamsFromCDict(
const ZSTD_rust_cdictQueryState* state);
unsigned ZSTD_rust_getDictIDFromCDict(
const ZSTD_rust_cdictQueryState* state);
typedef char ZSTD_rust_cdict_query_state_layout[
(offsetof(ZSTD_rust_cdictQueryState, cParams) == 0
&& offsetof(ZSTD_rust_cdictQueryState, dictID) == sizeof(void*)
&& sizeof(ZSTD_rust_cdictQueryState) == 2 * sizeof(void*))
? 1 : -1];
size_t ZSTD_rust_transferSequencesWBlockDelim(
SeqStore_t* seqStore, ZSTD_SequencePosition* seqPos,
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
@@ -4939,8 +4952,11 @@ const ZSTD_CDict* ZSTD_initStaticCDict(
ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict)
{
ZSTD_rust_cdictQueryState state;
assert(cdict != NULL);
return cdict->matchState.cParams;
state.cParams = &cdict->matchState.cParams;
state.dictID = NULL;
return ZSTD_rust_getCParamsFromCDict(&state);
}
/*! ZSTD_getDictID_fromCDict() :
@@ -4949,8 +4965,10 @@ ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict)
* Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */
unsigned ZSTD_getDictID_fromCDict(const ZSTD_CDict* cdict)
{
if (cdict==NULL) return 0;
return cdict->dictID;
ZSTD_rust_cdictQueryState state;
state.cParams = NULL;
state.dictID = cdict == NULL ? NULL : &cdict->dictID;
return ZSTD_rust_getDictIDFromCDict(&state);
}
static void ZSTD_rust_compressBeginUsingCDict_initParams(
+83
View File
@@ -232,6 +232,49 @@ pub unsafe extern "C" fn ZSTD_rust_compressBeginUsingCDict(
}
}
/// Scalar projections for the public CDict query helpers.
#[repr(C)]
pub struct ZSTD_rust_cdictQueryState {
c_params: *const ZSTD_compressionParameters,
dict_id: *const c_uint,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_cdictQueryState, c_params) == 0);
assert!(offset_of!(ZSTD_rust_cdictQueryState, dict_id) == size_of::<usize>());
assert!(size_of::<ZSTD_rust_cdictQueryState>() == 2 * size_of::<usize>());
};
/// Return a copied compression-parameter snapshot from a CDict projection.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_getCParamsFromCDict(
state: *const ZSTD_rust_cdictQueryState,
) -> ZSTD_compressionParameters {
if state.is_null() {
return ZSTD_compressionParameters::default();
}
let state = unsafe { &*state };
if state.c_params.is_null() {
return ZSTD_compressionParameters::default();
}
unsafe { *state.c_params }
}
/// Return the dictionary ID from a CDict projection, preserving NULL -> 0.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_getDictIDFromCDict(
state: *const ZSTD_rust_cdictQueryState,
) -> c_uint {
if state.is_null() {
return 0;
}
let state = unsafe { &*state };
if state.dict_id.is_null() {
return 0;
}
unsafe { *state.dict_id }
}
/// Rust-owned policy for `ZSTD_CCtx_loadDictionary_advanced()`.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_CCtx_loadDictionaryAdvanced(
@@ -891,6 +934,46 @@ mod tests {
assert_eq!(probe.events, [3]);
}
#[test]
fn cdict_query_projection_returns_private_snapshots() {
let cparams = ZSTD_compressionParameters {
windowLog: 17,
chainLog: 12,
hashLog: 13,
searchLog: 1,
minMatch: 4,
targetLength: 16,
strategy: 3,
};
let dict_id = 0x1234_5678;
let state = ZSTD_rust_cdictQueryState {
c_params: &cparams,
dict_id: &dict_id,
};
assert_eq!(unsafe { ZSTD_rust_getCParamsFromCDict(&state) }, cparams);
assert_eq!(unsafe { ZSTD_rust_getDictIDFromCDict(&state) }, dict_id);
}
#[test]
fn cdict_query_projection_handles_missing_snapshots() {
let state = ZSTD_rust_cdictQueryState {
c_params: ptr::null(),
dict_id: ptr::null(),
};
assert_eq!(
unsafe { ZSTD_rust_getCParamsFromCDict(&state) },
ZSTD_compressionParameters::default()
);
assert_eq!(unsafe { ZSTD_rust_getDictIDFromCDict(&state) }, 0);
assert_eq!(
unsafe { ZSTD_rust_getCParamsFromCDict(ptr::null()) },
ZSTD_compressionParameters::default()
);
assert_eq!(unsafe { ZSTD_rust_getDictIDFromCDict(ptr::null()) }, 0);
}
#[derive(Default)]
struct CdictBeginProbe {
events: Vec<&'static str>,