feat(compress): move stream initialization policy into Rust

Move the transparent ZSTD_CCtx_init_compressStream2 initialization policy into
Rust. Rust now owns the ordered local-dictionary, prefix, parameter-resolution,
pledged-size, worker-selection, and ordinary-buffering decisions through a
scalar projection and explicit callbacks.

Keep the private CCtx and parameter layouts, allocator and trace state, MT
context lifecycle, codec operations, reset behavior, and mutation details in C.
The C shim therefore remains the ABI and private-state boundary while the
high-level stream setup flow is testable in Rust without duplicating those
layouts.

Test Plan:
- cargo test --manifest-path rust/Cargo.toml --all-targets -- --test-threads=1
- cargo test --manifest-path rust/cli/Cargo.toml --all-targets -- --test-threads=1
- run the legacy Rust feature matrix and all six library/CLI clippy gates with
  -D warnings
- run lib and program native rebuilds plus test-cli-tests,
  test-rust-lib-smoke, and test-zstd with make -j1
- run fuzzer, zstream, and decode-corpus stress gates serially with
  ulimit -v 41943040

Commit is intentionally unsigned because GPG pinentry hangs in this
non-interactive environment.
This commit is contained in:
2026-07-19 09:43:03 +02:00
parent 492c6292c9
commit a9589d2d7d
2 changed files with 1134 additions and 87 deletions
+705
View File
@@ -696,6 +696,291 @@ const _: () = {
);
};
const ZSTD_RUST_INIT_RESOLVE_BLOCK_SPLITTER: c_int = 0;
const ZSTD_RUST_INIT_RESOLVE_LDM: c_int = 1;
const ZSTD_RUST_INIT_RESOLVE_ROW_MATCH_FINDER: c_int = 2;
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 CompressStreamInitRefreshCDictFn =
unsafe extern "C" fn(*mut c_void, *mut ZSTD_rust_compressStreamInitDictionaryState);
type CompressStreamInitClearPrefixFn = unsafe extern "C" fn(*mut c_void);
type CompressStreamInitAssertDictionariesFn = unsafe extern "C" fn(*mut c_void, *const c_void);
type CompressStreamInitSetLevelFn = unsafe extern "C" fn(*mut c_void, c_int);
type CompressStreamInitDebugFn = unsafe extern "C" fn(*mut c_void);
type CompressStreamInitGetPledgedFn = unsafe extern "C" fn(*mut c_void) -> u64;
type CompressStreamInitSetPledgedFn = unsafe extern "C" fn(*mut c_void, usize);
type CompressStreamInitGetCParamModeFn =
unsafe extern "C" fn(*mut c_void, *const c_void, u64) -> c_int;
type CompressStreamInitBuildCParamsFn = unsafe extern "C" fn(*mut c_void, u64, usize, c_int);
type CompressStreamInitResolveParamsFn = unsafe extern "C" fn(*mut c_void, c_int);
type CompressStreamInitGetNbWorkersFn = unsafe extern "C" fn(*mut c_void) -> c_uint;
type CompressStreamInitSetNbWorkersFn = unsafe extern "C" fn(*mut c_void, c_uint);
type CompressStreamInitHasExtSeqProdFn = unsafe extern "C" fn(*mut c_void) -> c_int;
type CompressStreamInitTraceFn = unsafe extern "C" fn(*mut c_void);
type CompressStreamInitGetMTContextFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void;
type CompressStreamInitCreateMTContextFn = unsafe extern "C" fn(*mut c_void, c_uint) -> usize;
type CompressStreamInitMTFn = unsafe extern "C" fn(
*mut c_void,
*mut c_void,
*const ZSTD_rust_compressStreamInitDictionaryState,
*mut c_void,
u64,
) -> usize;
type CompressStreamInitCommitMTFn = unsafe extern "C" fn(
*mut c_void,
*const ZSTD_rust_compressStreamInitDictionaryState,
*mut c_void,
);
type CompressStreamInitCheckCParamsFn = unsafe extern "C" fn(*mut c_void);
type CompressStreamInitBeginFn = unsafe extern "C" fn(
*mut c_void,
*const ZSTD_rust_compressStreamInitDictionaryState,
*mut c_void,
u64,
) -> usize;
type CompressStreamInitAssertOrdinaryFn = unsafe extern "C" fn(*mut c_void);
type CompressStreamInitGetBufferModeFn = unsafe extern "C" fn(*mut c_void) -> c_int;
type CompressStreamInitGetBlockSizeFn = unsafe extern "C" fn(*mut c_void) -> usize;
type CompressStreamInitCommitOrdinaryFn = unsafe extern "C" fn(*mut c_void, usize);
/// Scalar dictionary snapshot used by transparent stream initialization.
///
/// The prefix is populated by C before local-dictionary initialization, which
/// preserves the original single-use snapshot order. C refreshes only the
/// CDict fields after local-dictionary initialization because that operation
/// may create the local CDict.
#[repr(C)]
pub struct ZSTD_rust_compressStreamInitDictionaryState {
prefix_dict: *const c_void,
prefix_dict_size: usize,
prefix_dict_content_type: c_int,
cdict: *const c_void,
cdict_is_local: c_int,
cdict_compression_level: c_int,
cdict_dict_content_size: usize,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressStreamInitDictionaryState, prefix_dict) == 0);
assert!(
offset_of!(
ZSTD_rust_compressStreamInitDictionaryState,
prefix_dict_size
) == size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_compressStreamInitDictionaryState,
prefix_dict_content_type
) == 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitDictionaryState, cdict) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitDictionaryState, cdict_is_local)
== 4 * size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_compressStreamInitDictionaryState,
cdict_compression_level
) == 4 * size_of::<usize>() + size_of::<c_int>()
);
assert!(
offset_of!(
ZSTD_rust_compressStreamInitDictionaryState,
cdict_dict_content_size
) == 4 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_compressStreamInitDictionaryState>()
== if size_of::<usize>() == 8 { 48 } else { 28 }
);
};
/// Projection for `ZSTD_CCtx_init_compressStream2()`.
///
/// Rust owns the ordering and branch policy. The C callback slots retain
/// private parameter/context layouts, local dictionary storage, allocators,
/// trace setup, MT construction/init, and codec/reset operations.
#[repr(C)]
pub struct ZSTD_rust_compressStreamInitState {
callback_context: *mut c_void,
params: *mut c_void,
dictionaries: *mut ZSTD_rust_compressStreamInitDictionaryState,
end_op: c_int,
in_size: usize,
multithreaded: c_int,
mt_job_size_min: usize,
init_local_dict: CompressStreamInitLocalDictFn,
refresh_cdict: CompressStreamInitRefreshCDictFn,
clear_prefix: CompressStreamInitClearPrefixFn,
assert_dictionaries: CompressStreamInitAssertDictionariesFn,
set_compression_level: CompressStreamInitSetLevelFn,
debug_init: CompressStreamInitDebugFn,
get_pledged_src_size_plus_one: CompressStreamInitGetPledgedFn,
set_pledged_src_size: CompressStreamInitSetPledgedFn,
get_cparam_mode: CompressStreamInitGetCParamModeFn,
build_cparams: CompressStreamInitBuildCParamsFn,
resolve_params: CompressStreamInitResolveParamsFn,
get_nb_workers: CompressStreamInitGetNbWorkersFn,
set_nb_workers: CompressStreamInitSetNbWorkersFn,
has_ext_seq_prod: CompressStreamInitHasExtSeqProdFn,
trace_begin: CompressStreamInitTraceFn,
get_mt_context: CompressStreamInitGetMTContextFn,
create_mt_context: CompressStreamInitCreateMTContextFn,
init_mt: CompressStreamInitMTFn,
commit_mt: CompressStreamInitCommitMTFn,
check_cparams: CompressStreamInitCheckCParamsFn,
compress_begin: CompressStreamInitBeginFn,
assert_ordinary: CompressStreamInitAssertOrdinaryFn,
get_buffer_mode: CompressStreamInitGetBufferModeFn,
get_block_size: CompressStreamInitGetBlockSizeFn,
commit_ordinary: CompressStreamInitCommitOrdinaryFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressStreamInitState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressStreamInitState, params) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressStreamInitState, dictionaries) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressStreamInitState, end_op) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressStreamInitState, in_size) == 4 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressStreamInitState, multithreaded)
== 4 * size_of::<usize>() + size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressStreamInitState, mt_job_size_min)
== 5 * size_of::<usize>() + size_of::<usize>()
);
assert!(
size_of::<ZSTD_rust_compressStreamInitState>()
== if size_of::<usize>() == 8 { 256 } else { 128 }
);
};
#[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) };
if ERR_isError(result) {
return result;
}
unsafe { (state.refresh_cdict)(state.callback_context, state.dictionaries) };
let dictionaries = unsafe { &*state.dictionaries };
unsafe { (state.clear_prefix)(state.callback_context) };
unsafe { (state.assert_dictionaries)(state.callback_context, dictionaries.prefix_dict) };
if !dictionaries.cdict.is_null() && dictionaries.cdict_is_local == 0 {
unsafe {
(state.set_compression_level)(state.params, dictionaries.cdict_compression_level)
};
}
unsafe { (state.debug_init)(state.callback_context) };
if state.end_op == ZSTD_E_END {
unsafe { (state.set_pledged_src_size)(state.callback_context, state.in_size) };
}
let pledged_src_size_plus_one =
unsafe { (state.get_pledged_src_size_plus_one)(state.callback_context) };
let pledged_src_size = pledged_src_size_plus_one.wrapping_sub(1);
let dict_size = if !dictionaries.prefix_dict.is_null() {
dictionaries.prefix_dict_size
} else if !dictionaries.cdict.is_null() {
dictionaries.cdict_dict_content_size
} else {
0
};
let mode =
unsafe { (state.get_cparam_mode)(state.params, dictionaries.cdict, pledged_src_size) };
unsafe {
(state.build_cparams)(state.params, pledged_src_size, dict_size, mode);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_BLOCK_SPLITTER);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_LDM);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_ROW_MATCH_FINDER);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_VALIDATE_SEQUENCES);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_MAX_BLOCK_SIZE);
(state.resolve_params)(state.params, ZSTD_RUST_INIT_RESOLVE_EXTERNAL_REPCODE_SEARCH);
}
if state.multithreaded != 0 {
let has_ext_seq_prod = unsafe { (state.has_ext_seq_prod)(state.params) };
let mut nb_workers = unsafe { (state.get_nb_workers)(state.params) };
if has_ext_seq_prod != 0 && nb_workers >= 1 {
return ERROR(ZstdErrorCode::ParameterCombinationUnsupported);
}
if pledged_src_size <= state.mt_job_size_min as u64 {
nb_workers = 0;
unsafe { (state.set_nb_workers)(state.params, 0) };
}
if nb_workers > 0 {
unsafe { (state.trace_begin)(state.callback_context) };
if unsafe { (state.get_mt_context)(state.callback_context) }.is_null() {
let result =
unsafe { (state.create_mt_context)(state.callback_context, nb_workers) };
if ERR_isError(result) {
return result;
}
}
let result = unsafe {
(state.init_mt)(
state.callback_context,
(state.get_mt_context)(state.callback_context),
dictionaries,
state.params,
pledged_src_size,
)
};
if ERR_isError(result) {
return result;
}
unsafe { (state.commit_mt)(state.callback_context, dictionaries, state.params) };
return 0;
}
}
unsafe { (state.check_cparams)(state.params) };
let result = unsafe {
(state.compress_begin)(
state.callback_context,
dictionaries,
state.params,
pledged_src_size,
)
};
if ERR_isError(result) {
return result;
}
unsafe { (state.assert_ordinary)(state.callback_context) };
let buffer_mode = unsafe { (state.get_buffer_mode)(state.callback_context) };
let block_size = unsafe { (state.get_block_size)(state.callback_context) };
let in_buff_target = if buffer_mode == ZSTD_BM_BUFFERED {
block_size.wrapping_add(usize::from(block_size as u64 == pledged_src_size))
} else {
0
};
unsafe { (state.commit_ordinary)(state.callback_context, in_buff_target) };
0
}
/// Rust-owned high-level policy for transparent stream initialization.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressStreamInit(
state: *const ZSTD_rust_compressStreamInitState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
unsafe { compress_stream_init_body_with(&*state) }
}
#[inline]
unsafe fn stream_limit_copy(
dst: *mut c_void,
@@ -4270,6 +4555,426 @@ mod tests {
const ZSTD_BTOPT: c_int = 7;
const ZSTD_BTULTRA2: c_int = 9;
#[derive(Default)]
struct CompressStreamInitTestContext {
events: Vec<&'static str>,
local_result: usize,
create_result: usize,
mt_result: usize,
begin_result: usize,
pledged: u64,
cdict: *const c_void,
cdict_is_local: c_int,
cdict_compression_level: c_int,
cdict_dict_content_size: usize,
expected_prefix: *const c_void,
nb_workers: c_uint,
has_ext_seq_prod: c_int,
mt_context: *mut c_void,
buffer_mode: c_int,
block_size: usize,
ordinary_target: usize,
compression_level: c_int,
}
unsafe fn compress_stream_init_test_context(
context: *mut c_void,
) -> &'static mut CompressStreamInitTestContext {
unsafe { &mut *context.cast::<CompressStreamInitTestContext>() }
}
unsafe extern "C" fn compress_stream_init_test_local_dict(context: *mut c_void) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("local-dict");
context.local_result
}
unsafe extern "C" fn compress_stream_init_test_refresh_cdict(
context: *mut c_void,
dictionaries: *mut ZSTD_rust_compressStreamInitDictionaryState,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("refresh-cdict");
let dictionaries = unsafe { &mut *dictionaries };
dictionaries.cdict = context.cdict;
dictionaries.cdict_is_local = context.cdict_is_local;
dictionaries.cdict_compression_level = context.cdict_compression_level;
dictionaries.cdict_dict_content_size = context.cdict_dict_content_size;
}
unsafe extern "C" fn compress_stream_init_test_clear_prefix(context: *mut c_void) {
unsafe { compress_stream_init_test_context(context) }
.events
.push("clear-prefix");
}
unsafe extern "C" fn compress_stream_init_test_assert_dictionaries(
context: *mut c_void,
prefix: *const c_void,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("assert-dictionaries");
assert_eq!(prefix, context.expected_prefix);
}
unsafe extern "C" fn compress_stream_init_test_set_level(params: *mut c_void, level: c_int) {
let context = unsafe { &mut *params.cast::<CompressStreamInitTestContext>() };
context.events.push("set-level");
context.compression_level = level;
}
unsafe extern "C" fn compress_stream_init_test_debug(context: *mut c_void) {
unsafe { compress_stream_init_test_context(context) }
.events
.push("debug");
}
unsafe extern "C" fn compress_stream_init_test_get_pledged(context: *mut c_void) -> u64 {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("get-pledged");
context.pledged
}
unsafe extern "C" fn compress_stream_init_test_set_pledged(
context: *mut c_void,
in_size: usize,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("set-pledged");
context.pledged = (in_size as u64).wrapping_add(1);
}
unsafe extern "C" fn compress_stream_init_test_get_cparam_mode(
params: *mut c_void,
_cdict: *const c_void,
_pledged: u64,
) -> c_int {
unsafe { compress_stream_init_test_context(params) }
.events
.push("get-cparam-mode");
0
}
unsafe extern "C" fn compress_stream_init_test_build_cparams(
params: *mut c_void,
_pledged: u64,
_dict_size: usize,
_mode: c_int,
) {
unsafe { compress_stream_init_test_context(params) }
.events
.push("build-cparams");
}
unsafe extern "C" fn compress_stream_init_test_resolve_params(
params: *mut c_void,
operation: c_int,
) {
let context = unsafe { compress_stream_init_test_context(params) };
context.events.push(match operation {
ZSTD_RUST_INIT_RESOLVE_BLOCK_SPLITTER => "resolve:block-splitter",
ZSTD_RUST_INIT_RESOLVE_LDM => "resolve:ldm",
ZSTD_RUST_INIT_RESOLVE_ROW_MATCH_FINDER => "resolve:row-match-finder",
ZSTD_RUST_INIT_RESOLVE_VALIDATE_SEQUENCES => "resolve:validate-sequences",
ZSTD_RUST_INIT_RESOLVE_MAX_BLOCK_SIZE => "resolve:max-block-size",
ZSTD_RUST_INIT_RESOLVE_EXTERNAL_REPCODE_SEARCH => "resolve:external-repcodes",
_ => "resolve:unknown",
});
}
unsafe extern "C" fn compress_stream_init_test_get_nb_workers(params: *mut c_void) -> c_uint {
let context = unsafe { compress_stream_init_test_context(params) };
context.events.push("get-workers");
context.nb_workers
}
unsafe extern "C" fn compress_stream_init_test_set_nb_workers(
params: *mut c_void,
nb_workers: c_uint,
) {
let context = unsafe { compress_stream_init_test_context(params) };
context.events.push("set-workers");
context.nb_workers = nb_workers;
}
unsafe extern "C" fn compress_stream_init_test_has_ext_seq_prod(params: *mut c_void) -> c_int {
let context = unsafe { compress_stream_init_test_context(params) };
context.events.push("has-ext-seq-prod");
context.has_ext_seq_prod
}
unsafe extern "C" fn compress_stream_init_test_trace(context: *mut c_void) {
unsafe { compress_stream_init_test_context(context) }
.events
.push("trace");
}
unsafe extern "C" fn compress_stream_init_test_get_mt_context(
context: *mut c_void,
) -> *mut c_void {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("get-mt-context");
context.mt_context
}
unsafe extern "C" fn compress_stream_init_test_create_mt_context(
context: *mut c_void,
_nb_workers: c_uint,
) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("create-mt-context");
if !ERR_isError(context.create_result) {
context.mt_context = ptr::dangling_mut::<c_void>();
}
context.create_result
}
unsafe extern "C" fn compress_stream_init_test_mt(
context: *mut c_void,
_mt_context: *mut c_void,
_dictionaries: *const ZSTD_rust_compressStreamInitDictionaryState,
_params: *mut c_void,
_pledged: u64,
) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("init-mt");
context.mt_result
}
unsafe extern "C" fn compress_stream_init_test_commit_mt(
context: *mut c_void,
_dictionaries: *const ZSTD_rust_compressStreamInitDictionaryState,
_params: *mut c_void,
) {
unsafe { compress_stream_init_test_context(context) }
.events
.push("commit-mt");
}
unsafe extern "C" fn compress_stream_init_test_check_cparams(params: *mut c_void) {
unsafe { compress_stream_init_test_context(params) }
.events
.push("check-cparams");
}
unsafe extern "C" fn compress_stream_init_test_begin(
context: *mut c_void,
_dictionaries: *const ZSTD_rust_compressStreamInitDictionaryState,
_params: *mut c_void,
_pledged: u64,
) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("compress-begin");
context.begin_result
}
unsafe extern "C" fn compress_stream_init_test_assert_ordinary(context: *mut c_void) {
unsafe { compress_stream_init_test_context(context) }
.events
.push("assert-ordinary");
}
unsafe extern "C" fn compress_stream_init_test_get_buffer_mode(context: *mut c_void) -> c_int {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("get-buffer-mode");
context.buffer_mode
}
unsafe extern "C" fn compress_stream_init_test_get_block_size(context: *mut c_void) -> usize {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("get-block-size");
context.block_size
}
unsafe extern "C" fn compress_stream_init_test_commit_ordinary(
context: *mut c_void,
in_buff_target: usize,
) {
let context = unsafe { compress_stream_init_test_context(context) };
context.events.push("commit-ordinary");
context.ordinary_target = in_buff_target;
}
fn compress_stream_init_test_state(
context: &mut CompressStreamInitTestContext,
dictionaries: &mut ZSTD_rust_compressStreamInitDictionaryState,
end_op: c_int,
in_size: usize,
multithreaded: c_int,
) -> ZSTD_rust_compressStreamInitState {
ZSTD_rust_compressStreamInitState {
callback_context: (context as *mut CompressStreamInitTestContext).cast(),
params: (context as *mut CompressStreamInitTestContext).cast(),
dictionaries,
end_op,
in_size,
multithreaded,
mt_job_size_min: 10,
init_local_dict: compress_stream_init_test_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,
set_compression_level: compress_stream_init_test_set_level,
debug_init: compress_stream_init_test_debug,
get_pledged_src_size_plus_one: compress_stream_init_test_get_pledged,
set_pledged_src_size: compress_stream_init_test_set_pledged,
get_cparam_mode: compress_stream_init_test_get_cparam_mode,
build_cparams: compress_stream_init_test_build_cparams,
resolve_params: compress_stream_init_test_resolve_params,
get_nb_workers: compress_stream_init_test_get_nb_workers,
set_nb_workers: compress_stream_init_test_set_nb_workers,
has_ext_seq_prod: compress_stream_init_test_has_ext_seq_prod,
trace_begin: compress_stream_init_test_trace,
get_mt_context: compress_stream_init_test_get_mt_context,
create_mt_context: compress_stream_init_test_create_mt_context,
init_mt: compress_stream_init_test_mt,
commit_mt: compress_stream_init_test_commit_mt,
check_cparams: compress_stream_init_test_check_cparams,
compress_begin: compress_stream_init_test_begin,
assert_ordinary: compress_stream_init_test_assert_ordinary,
get_buffer_mode: compress_stream_init_test_get_buffer_mode,
get_block_size: compress_stream_init_test_get_block_size,
commit_ordinary: compress_stream_init_test_commit_ordinary,
}
}
#[test]
fn compress_stream_init_preserves_ordinary_callback_order_and_policy() {
let mut context = CompressStreamInitTestContext {
pledged: 0,
cdict: ptr::dangling::<c_void>(),
cdict_compression_level: 17,
cdict_dict_content_size: 3,
nb_workers: 0,
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_END, 4, 0);
let result = unsafe { ZSTD_rust_compressStreamInit(&state) };
assert_eq!(result, 0);
assert_eq!(context.compression_level, 17);
assert_eq!(context.pledged, 5);
assert_eq!(context.ordinary_target, 5);
assert_eq!(
context.events,
[
"local-dict",
"refresh-cdict",
"clear-prefix",
"assert-dictionaries",
"set-level",
"debug",
"set-pledged",
"get-pledged",
"get-cparam-mode",
"build-cparams",
"resolve:block-splitter",
"resolve:ldm",
"resolve:row-match-finder",
"resolve:validate-sequences",
"resolve:max-block-size",
"resolve:external-repcodes",
"check-cparams",
"compress-begin",
"assert-ordinary",
"get-buffer-mode",
"get-block-size",
"commit-ordinary",
]
);
}
#[test]
fn compress_stream_init_stops_before_later_callbacks_on_error() {
let mut context = CompressStreamInitTestContext {
local_result: ERROR(ZstdErrorCode::MemoryAllocation),
..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, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["local-dict"]);
}
#[test]
fn compress_stream_init_preserves_mt_branch_and_init_error_order() {
let mut context = CompressStreamInitTestContext {
cdict: ptr::dangling::<c_void>(),
cdict_dict_content_size: 3,
nb_workers: 2,
mt_result: ERROR(ZstdErrorCode::MemoryAllocation),
..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_END, 99, 1);
let result = unsafe { ZSTD_rust_compressStreamInit(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(
context.events,
[
"local-dict",
"refresh-cdict",
"clear-prefix",
"assert-dictionaries",
"set-level",
"debug",
"set-pledged",
"get-pledged",
"get-cparam-mode",
"build-cparams",
"resolve:block-splitter",
"resolve:ldm",
"resolve:row-match-finder",
"resolve:validate-sequences",
"resolve:max-block-size",
"resolve:external-repcodes",
"has-ext-seq-prod",
"get-workers",
"trace",
"get-mt-context",
"create-mt-context",
"get-mt-context",
"init-mt",
]
);
}
fn system_round_trip(compressed: &[u8]) -> Option<Vec<u8>> {
let mut child = Command::new("zstd")
.args(["-q", "-d", "-c"])