feat(compress): move local dictionary init policy into Rust

The transparent stream initializer previously called a C helper that both
inspected local-dictionary state and chose the no-dictionary, already-created,
and create/publish branches.  Project that private state through a checked C/Rust
ABI so Rust owns the branch and callback order while C retains the requested
parameters, custom allocator, and CDict implementation details.  Keep creation
and publication split so allocation failure cannot publish a partial dictionary.

Test Plan:
- cargo fmt --manifest-path rust/Cargo.toml -- --check
- CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml compress_stream_init_
- CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings
- make -j1
- make -j1 -C tests test-zstream ZSTREAM_TESTTIME=-T2s
- make -j1 -C tests test-fuzzer FUZZERTEST=-T3s FUZZER_FLAGS=--no-big-tests
- CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml
This commit is contained in:
2026-07-19 21:16:32 +02:00
parent 2d2214eb1d
commit 9d8c03d87d
2 changed files with 269 additions and 52 deletions
+191 -10
View File
@@ -2661,7 +2661,12 @@ const ZSTD_RUST_INIT_RESOLVE_VALIDATE_SEQUENCES: c_int = 3;
const ZSTD_RUST_INIT_RESOLVE_MAX_BLOCK_SIZE: c_int = 4;
const ZSTD_RUST_INIT_RESOLVE_EXTERNAL_REPCODE_SEARCH: c_int = 5;
type CompressStreamInitLocalDictFn = unsafe extern "C" fn(*mut c_void) -> usize;
type CompressStreamInitGetLocalDictFn =
unsafe extern "C" fn(*mut c_void, *mut ZSTD_rust_compressStreamInitLocalDictState);
type CompressStreamInitCreateLocalDictFn =
unsafe extern "C" fn(*mut c_void, *mut ZSTD_rust_compressStreamInitLocalDictState) -> usize;
type CompressStreamInitPublishLocalDictFn =
unsafe extern "C" fn(*mut c_void, *const ZSTD_rust_compressStreamInitLocalDictState);
type CompressStreamInitRefreshCDictFn =
unsafe extern "C" fn(*mut c_void, *mut ZSTD_rust_compressStreamInitDictionaryState);
type CompressStreamInitClearPrefixFn = unsafe extern "C" fn(*mut c_void);
@@ -2704,6 +2709,54 @@ type CompressStreamInitGetBufferModeFn = unsafe extern "C" fn(*mut c_void) -> c_
type CompressStreamInitGetBlockSizeFn = unsafe extern "C" fn(*mut c_void) -> usize;
type CompressStreamInitCommitOrdinaryFn = unsafe extern "C" fn(*mut c_void, usize);
/// Private local-dictionary projection used by transparent stream initialization.
///
/// Rust owns the no-dictionary, already-initialized, create, and publish order;
/// C retains the private dictionary storage and allocator-sensitive callbacks.
#[repr(C)]
pub struct ZSTD_rust_compressStreamInitLocalDictState {
dict: *const c_void,
dict_buffer: *mut c_void,
dict_size: usize,
dict_content_type: c_int,
local_cdict: *mut c_void,
cdict: *const c_void,
prefix_dict: *const c_void,
created_cdict: *mut c_void,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressStreamInitLocalDictState, dict) == 0);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, dict_buffer) == size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, dict_size) == 2 * size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_compressStreamInitLocalDictState,
dict_content_type
) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, local_cdict)
== 4 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, cdict) == 5 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, prefix_dict)
== 6 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitLocalDictState, created_cdict)
== 7 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_compressStreamInitLocalDictState>() == size_of::<[usize; 8]>());
};
/// Scalar dictionary snapshot used by transparent stream initialization.
///
/// The prefix is populated by C before local-dictionary initialization, which
@@ -2774,7 +2827,9 @@ pub struct ZSTD_rust_compressStreamInitState {
in_size: usize,
multithreaded: c_int,
mt_job_size_min: usize,
init_local_dict: CompressStreamInitLocalDictFn,
get_local_dict: CompressStreamInitGetLocalDictFn,
create_local_dict: CompressStreamInitCreateLocalDictFn,
publish_local_dict: CompressStreamInitPublishLocalDictFn,
refresh_cdict: CompressStreamInitRefreshCDictFn,
clear_prefix: CompressStreamInitClearPrefixFn,
assert_dictionaries: CompressStreamInitAssertDictionariesFn,
@@ -2817,17 +2872,57 @@ const _: () = {
);
assert!(
size_of::<ZSTD_rust_compressStreamInitState>()
== if size_of::<usize>() == 8 { 256 } else { 128 }
== if size_of::<usize>() == 8 { 272 } else { 144 }
);
};
#[inline]
unsafe fn compress_stream_init_local_dict_with(state: &ZSTD_rust_compressStreamInitState) -> usize {
let mut local_dict = ZSTD_rust_compressStreamInitLocalDictState {
dict: ptr::null(),
dict_buffer: ptr::null_mut(),
dict_size: 0,
dict_content_type: 0,
local_cdict: ptr::null_mut(),
cdict: ptr::null(),
prefix_dict: ptr::null(),
created_cdict: ptr::null_mut(),
};
unsafe {
(state.get_local_dict)(state.callback_context, &mut local_dict);
}
if local_dict.dict.is_null() {
debug_assert!(local_dict.dict_buffer.is_null());
debug_assert!(local_dict.local_cdict.is_null());
debug_assert_eq!(local_dict.dict_size, 0);
return 0;
}
if !local_dict.local_cdict.is_null() {
debug_assert_eq!(local_dict.cdict, local_dict.local_cdict.cast_const(),);
return 0;
}
debug_assert!(local_dict.dict_size > 0);
debug_assert!(local_dict.cdict.is_null());
debug_assert!(local_dict.prefix_dict.is_null());
let result = unsafe { (state.create_local_dict)(state.callback_context, &mut local_dict) };
if ERR_isError(result) {
return result;
}
debug_assert!(!local_dict.created_cdict.is_null());
unsafe {
(state.publish_local_dict)(state.callback_context, &local_dict);
}
0
}
#[inline]
unsafe fn compress_stream_init_body_with(state: &ZSTD_rust_compressStreamInitState) -> usize {
if state.params.is_null() || state.dictionaries.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let result = unsafe { (state.init_local_dict)(state.callback_context) };
let result = unsafe { compress_stream_init_local_dict_with(state) };
if ERR_isError(result) {
return result;
}
@@ -9572,11 +9667,19 @@ mod tests {
#[derive(Default)]
struct CompressStreamInitTestContext {
events: Vec<&'static str>,
local_result: usize,
local_create_result: usize,
create_result: usize,
mt_result: usize,
begin_result: usize,
pledged: u64,
local_dict: *const c_void,
local_dict_buffer: *mut c_void,
local_dict_size: usize,
local_dict_content_type: c_int,
local_cdict: *mut c_void,
context_cdict: *const c_void,
prefix_dict: *const c_void,
created_local_cdict: *mut c_void,
cdict: *const c_void,
cdict_is_local: c_int,
cdict_compression_level: c_int,
@@ -9597,10 +9700,43 @@ mod tests {
unsafe { &mut *context.cast::<CompressStreamInitTestContext>() }
}
unsafe extern "C" fn compress_stream_init_test_local_dict(context: *mut c_void) -> usize {
unsafe extern "C" fn compress_stream_init_test_get_local_dict(
context: *mut c_void,
local_dict: *mut ZSTD_rust_compressStreamInitLocalDictState,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("local-dict");
context.local_result
let local_dict = unsafe { &mut *local_dict };
local_dict.dict = context.local_dict;
local_dict.dict_buffer = context.local_dict_buffer;
local_dict.dict_size = context.local_dict_size;
local_dict.dict_content_type = context.local_dict_content_type;
local_dict.local_cdict = context.local_cdict;
local_dict.cdict = context.context_cdict;
local_dict.prefix_dict = context.prefix_dict;
local_dict.created_cdict = ptr::null_mut();
}
unsafe extern "C" fn compress_stream_init_test_create_local_dict(
context: *mut c_void,
local_dict: *mut ZSTD_rust_compressStreamInitLocalDictState,
) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("create-local-dict");
unsafe { &mut *local_dict }.created_cdict = context.created_local_cdict;
context.local_create_result
}
unsafe extern "C" fn compress_stream_init_test_publish_local_dict(
context: *mut c_void,
local_dict: *const ZSTD_rust_compressStreamInitLocalDictState,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("publish-local-dict");
let local_dict = unsafe { &*local_dict };
context.local_cdict = local_dict.created_cdict;
context.context_cdict = local_dict.created_cdict.cast_const();
context.cdict = local_dict.created_cdict.cast_const();
}
unsafe extern "C" fn compress_stream_init_test_refresh_cdict(
@@ -9824,7 +9960,9 @@ mod tests {
in_size,
multithreaded,
mt_job_size_min: 10,
init_local_dict: compress_stream_init_test_local_dict,
get_local_dict: compress_stream_init_test_get_local_dict,
create_local_dict: compress_stream_init_test_create_local_dict,
publish_local_dict: compress_stream_init_test_publish_local_dict,
refresh_cdict: compress_stream_init_test_refresh_cdict,
clear_prefix: compress_stream_init_test_clear_prefix,
assert_dictionaries: compress_stream_init_test_assert_dictionaries,
@@ -9911,10 +10049,53 @@ mod tests {
);
}
#[test]
fn compress_stream_init_orchestrates_local_dictionary_creation_and_publish() {
let mut context = CompressStreamInitTestContext {
local_dict: ptr::dangling::<c_void>(),
local_dict_size: 3,
created_local_cdict: ptr::dangling_mut::<c_void>(),
cdict_is_local: 1,
cdict_dict_content_size: 3,
buffer_mode: ZSTD_BM_BUFFERED,
block_size: 4,
..CompressStreamInitTestContext::default()
};
let mut dictionaries = ZSTD_rust_compressStreamInitDictionaryState {
prefix_dict: ptr::null(),
prefix_dict_size: 0,
prefix_dict_content_type: 0,
cdict: ptr::null(),
cdict_is_local: 0,
cdict_compression_level: 0,
cdict_dict_content_size: 0,
};
let state =
compress_stream_init_test_state(&mut context, &mut dictionaries, ZSTD_E_CONTINUE, 0, 0);
let result = unsafe { ZSTD_rust_compressStreamInit(&state) };
assert_eq!(result, 0);
assert_eq!(context.local_cdict, context.created_local_cdict);
assert_eq!(
context.context_cdict,
context.created_local_cdict.cast_const()
);
assert_eq!(
&context.events[..3],
["local-dict", "create-local-dict", "publish-local-dict"]
);
assert_eq!(context.events[3], "refresh-cdict");
assert_eq!(dictionaries.cdict, context.created_local_cdict.cast_const());
assert_eq!(dictionaries.cdict_is_local, 1);
}
#[test]
fn compress_stream_init_stops_before_later_callbacks_on_error() {
let mut context = CompressStreamInitTestContext {
local_result: ERROR(ZstdErrorCode::MemoryAllocation),
local_dict: ptr::dangling::<c_void>(),
local_dict_size: 3,
local_create_result: ERROR(ZstdErrorCode::MemoryAllocation),
..CompressStreamInitTestContext::default()
};
let mut dictionaries = ZSTD_rust_compressStreamInitDictionaryState {
@@ -9932,7 +10113,7 @@ mod tests {
let result = unsafe { ZSTD_rust_compressStreamInit(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["local-dict"]);
assert_eq!(context.events, ["local-dict", "create-local-dict"]);
}
#[test]