Files
zstd-rs/rust/src/zstd_compress_dictionary.rs
T
ddidderr f758c41c5d feat(compress): zero CDict hash table3 in Rust
Move the final CDict-copy hashTable3 clearing operation into the Rust reset
orchestration. The C bridge now projects the post-reset destination table
and hash log while retaining only the private reset, workspace dirty/clean,
and metadata operations on the C side.

Preserve C's zero-log contract: hashLog3 == 0 means no table bytes are
cleared and a null hashTable3 is valid. Nonzero logs are bounds-checked before
Rust zeroes the table, keeping the operation between the dirty and clean
transitions.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings
- ulimit -v 41943040; make -j1
- ulimit -v 41943040; make -j1 -C tests test-zstream ZSTREAM_TESTTIME=-T2s
- ulimit -v 41943040; make -j1 -C tests test-fuzzer FUZZERTEST=-T3s FUZZER_FLAGS=--no-big-tests
2026-07-19 23:40:39 +02:00

5152 lines
181 KiB
Rust

#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(clippy::too_many_arguments)]
//! Dictionary entropy-header loading.
//!
//! This is the Rust leaf for `ZSTD_dictNCountRepeat()` and
//! `ZSTD_loadCEntropy()` in `lib/compress/zstd_compress.c`. C retains the
//! original symbol through a thin compatibility shim and keeps dictionary
//! content loading and compression-context ownership on its side.
use crate::bits::ZSTD_highbit32;
use crate::common::{LL_FSE_LOG, MAX_LL, MAX_ML, MAX_OFF, ML_FSE_LOG, OFF_FSE_LOG};
use crate::entropy_common::FSE_readNCount;
use crate::errors::{ERR_isError, ZstdErrorCode, ERROR};
use crate::fse_compress::FSE_buildCTable_wksp;
use crate::huf_compress::HUF_readCTable;
use crate::zstd_compress::ZSTD_rust_copyCDictTableIntoCCtx;
use crate::zstd_compress_params::{
ZSTD_compressionParameters, ZSTD_frameParameters, ZSTD_parameters,
ZSTD_rust_params_allocateChainTable, ZSTD_rust_params_defaultCLevel,
ZSTD_rust_params_getCParams, ZSTD_rust_params_getParamsInternal,
ZSTD_rust_params_rowMatchFinderUsed, ZSTD_rust_params_shouldAttachDict,
ZSTD_CONTENTSIZE_UNKNOWN, ZSTD_RUST_CPM_CREATE_CDICT, ZSTD_RUST_CPM_NO_ATTACH_DICT,
};
use crate::zstd_compress_params_api::ZSTD_CCtx_params;
use crate::zstd_compress_stats::{
ZSTD_compressedBlockState_t, ZSTD_rust_resetCompressedBlockState,
};
use std::ffi::c_void;
use std::mem::{offset_of, size_of};
use std::os::raw::{c_int, c_short, c_uint};
use std::ptr;
use std::slice;
const HUF_REPEAT_CHECK: c_int = 1;
const HUF_REPEAT_VALID: c_int = 2;
const FSE_REPEAT_CHECK: c_int = 1;
const FSE_REPEAT_VALID: c_int = 2;
const HUF_WORKSPACE_SIZE: usize = (8 << 10) + 512;
const DICTIONARY_ID_AND_MAGIC_SIZE: usize = 8;
const REPCODE_SECTION_SIZE: usize = 12;
const ZSTD_MAGIC_DICTIONARY: u32 = 0xEC30_A437;
const ZSTD_DCT_AUTO: c_int = 0;
const ZSTD_DCT_RAW_CONTENT: c_int = 1;
const ZSTD_DCT_FULL_DICT: c_int = 2;
const ZSTD_STATIC_WORKSPACE_ALIGNMENT: usize = 8;
/// C keeps match-state/content insertion private because it depends on the
/// configuration-sensitive `ZSTD_MatchState_t`, `ldmState_t`, workspace, and
/// parameter layouts. Rust owns the dictionary dispatch and calls this narrow
/// operation only after selecting the raw or full-dictionary path.
pub type LoadDictionaryContentFn = unsafe extern "C" fn(
match_state: *mut c_void,
ldm_state: *mut c_void,
workspace_state: *mut c_void,
params: *const c_void,
src: *const c_void,
src_size: usize,
dtlm: c_int,
tfp: c_int,
) -> usize;
/// These callbacks are deliberately opaque: C retains the private CCtx,
/// local/prefix dictionary layouts, allocation, and CDict lifetime rules.
/// Rust owns the stage checks and the order in which clear/assign operations
/// are selected.
pub type CctxDictionaryClearFn = unsafe extern "C" fn(context: *mut c_void);
pub type CctxAssignLocalDictFn = unsafe extern "C" fn(
context: *mut c_void,
dict: *const c_void,
dict_size: usize,
dict_load_method: c_int,
dict_content_type: c_int,
) -> usize;
pub type CctxAssignCDictFn = unsafe extern "C" fn(context: *mut c_void, cdict: *const c_void);
pub type CctxAssignPrefixDictFn = unsafe extern "C" fn(
context: *mut c_void,
prefix: *const c_void,
prefix_size: usize,
dict_content_type: c_int,
);
#[inline]
fn dictionary_corrupted() -> usize {
ERROR(ZstdErrorCode::DictionaryCorrupted)
}
const ZSTD_CCTX_INIT_STAGE: c_int = 0;
const ZSTD_DLM_BY_REF: c_int = 1;
#[cfg(test)]
const ZSTD_DLM_BY_COPY: c_int = 0;
#[inline]
fn stage_wrong() -> usize {
ERROR(ZstdErrorCode::StageWrong)
}
type CompressBeginUsingCDictInitParamsFn =
unsafe extern "C" fn(*mut c_void, *const ZSTD_parameters, c_int);
type CompressBeginUsingCDictAdjustWindowFn = unsafe extern "C" fn(*mut c_void, c_uint);
type CompressBeginUsingCDictBeginFn =
unsafe extern "C" fn(*mut c_void, *const c_void, *const c_void, u64) -> usize;
/// Explicit projection for `ZSTD_compressBegin_usingCDict_internal`.
///
/// Rust owns CDict/source-size parameter selection, initialization ordering,
/// and the source-window floor. C retains the private parameter/context
/// layouts and the final begin operation behind callbacks.
#[repr(C)]
pub struct ZSTD_rust_compressBeginUsingCDictState {
cctx: *mut c_void,
cdict: *const c_void,
cctx_params: *mut ZSTD_CCtx_params,
cdict_cparams: *const ZSTD_compressionParameters,
cdict_content_size: *const usize,
cdict_compression_level: *const c_int,
f_params: *const ZSTD_frameParameters,
pledged_src_size: *const u64,
exclusion_mask: *const c_uint,
init_params: CompressBeginUsingCDictInitParamsFn,
adjust_window: CompressBeginUsingCDictAdjustWindowFn,
begin: CompressBeginUsingCDictBeginFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressBeginUsingCDictState, cctx) == 0);
assert!(offset_of!(ZSTD_rust_compressBeginUsingCDictState, cdict) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressBeginUsingCDictState, cctx_params) == 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressBeginUsingCDictState, cdict_cparams) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressBeginUsingCDictState, cdict_content_size)
== 4 * size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_compressBeginUsingCDictState,
cdict_compression_level
) == 5 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_compressBeginUsingCDictState, f_params) == 6 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressBeginUsingCDictState, pledged_src_size)
== 7 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressBeginUsingCDictState, exclusion_mask)
== size_of::<[usize; 8]>()
);
assert!(
offset_of!(ZSTD_rust_compressBeginUsingCDictState, init_params) == 9 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressBeginUsingCDictState, adjust_window)
== 10 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_compressBeginUsingCDictState, begin) == 11 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_compressBeginUsingCDictState>() == size_of::<[usize; 12]>());
};
const CDICT_PARAMS_SRC_SIZE_CUTOFF: u64 = 128 * 1024;
const CDICT_PARAMS_DICT_SIZE_MULTIPLIER: u64 = 6;
const CDICT_WINDOW_SOURCE_SIZE_CAP: u64 = 1 << 19;
/// Apply the CDict begin policy while keeping private C state behind callbacks.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressBeginUsingCDict(
state: *const ZSTD_rust_compressBeginUsingCDictState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.cdict.is_null() {
return ERROR(ZstdErrorCode::DictionaryWrong);
}
if state.cctx_params.is_null()
|| state.cdict_cparams.is_null()
|| state.cdict_content_size.is_null()
|| state.cdict_compression_level.is_null()
|| state.f_params.is_null()
|| state.pledged_src_size.is_null()
|| state.exclusion_mask.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let cdict_cparams = unsafe { *state.cdict_cparams };
let cdict_content_size = unsafe { *state.cdict_content_size };
let cdict_compression_level = unsafe { *state.cdict_compression_level };
let pledged_src_size = unsafe { *state.pledged_src_size };
let cdict_size_threshold =
(cdict_content_size as u64).wrapping_mul(CDICT_PARAMS_DICT_SIZE_MULTIPLIER);
let cparams = if pledged_src_size < CDICT_PARAMS_SRC_SIZE_CUTOFF
|| pledged_src_size < cdict_size_threshold
|| pledged_src_size == ZSTD_CONTENTSIZE_UNKNOWN
|| cdict_compression_level == 0
{
cdict_cparams
} else {
ZSTD_rust_params_getCParams(
cdict_compression_level,
pledged_src_size,
cdict_content_size,
unsafe { *state.exclusion_mask },
)
};
let params = ZSTD_parameters {
cParams: cparams,
fParams: unsafe { *state.f_params },
};
unsafe {
(state.init_params)(state.cctx_params.cast(), &params, cdict_compression_level);
}
if pledged_src_size != ZSTD_CONTENTSIZE_UNKNOWN {
let limited_src_size = pledged_src_size.min(CDICT_WINDOW_SOURCE_SIZE_CAP) as u32;
let limited_src_log = if limited_src_size > 1 {
ZSTD_highbit32(limited_src_size - 1) + 1
} else {
1
};
unsafe {
(state.adjust_window)(state.cctx_params.cast(), limited_src_log);
}
}
unsafe {
(state.begin)(
state.cctx,
state.cdict,
state.cctx_params.cast_const().cast(),
pledged_src_size,
)
}
}
type CompressUsingCDictBeginFn =
unsafe extern "C" fn(*mut c_void, *const c_void, *const ZSTD_frameParameters, u64) -> usize;
type CompressUsingCDictEndFn =
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize) -> usize;
/// Explicit projection for the public `ZSTD_compress_usingCDict` wrapper.
///
/// Rust owns the hardcoded frame policy and begin-then-end ordering. C
/// retains the private CDict begin path and end-of-frame implementation behind
/// callbacks.
#[repr(C)]
pub struct ZSTD_rust_compressUsingCDictState {
callback_context: *mut c_void,
cdict: *const c_void,
begin: CompressUsingCDictBeginFn,
end: CompressUsingCDictEndFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressUsingCDictState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressUsingCDictState, cdict) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressUsingCDictState, begin) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressUsingCDictState, end) == 3 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_compressUsingCDictState>() == size_of::<[usize; 4]>());
};
/// Start a CDict frame and finish it through the existing C-owned leaves.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressUsingCDict(
state: *const ZSTD_rust_compressUsingCDictState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.callback_context.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if state.cdict.is_null() {
return ERROR(ZstdErrorCode::DictionaryWrong);
}
let f_params = ZSTD_frameParameters {
contentSizeFlag: 1,
checksumFlag: 0,
noDictIDFlag: 0,
};
let begin_result = unsafe {
(state.begin)(
state.callback_context,
state.cdict,
&f_params,
src_size as u64,
)
};
if ERR_isError(begin_result) {
return begin_result;
}
unsafe { (state.end)(state.callback_context, dst, dst_capacity, src, src_size) }
}
/// Explicit projection for the deprecated public
/// `ZSTD_compress_usingCDict_advanced` wrapper.
///
/// Rust owns CDict validation and begin-then-end ordering while C retains the
/// private begin and end operations behind callbacks.
#[repr(C)]
pub struct ZSTD_rust_compressUsingCDictAdvancedState {
callback_context: *mut c_void,
cdict: *const c_void,
f_params: *const ZSTD_frameParameters,
begin: CompressUsingCDictBeginFn,
end: CompressUsingCDictEndFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressUsingCDictAdvancedState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressUsingCDictAdvancedState, cdict) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressUsingCDictAdvancedState, f_params) == 2 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_compressUsingCDictAdvancedState, begin) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressUsingCDictAdvancedState, end) == 4 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_compressUsingCDictAdvancedState>() == size_of::<[usize; 5]>());
};
/// Compress a complete source using an explicit CDict frame policy.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressUsingCDictAdvanced(
state: *const ZSTD_rust_compressUsingCDictAdvancedState,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.callback_context.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if state.cdict.is_null() {
return ERROR(ZstdErrorCode::DictionaryWrong);
}
if state.f_params.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let begin_result = unsafe {
(state.begin)(
state.callback_context,
state.cdict,
state.f_params,
src_size as u64,
)
};
if ERR_isError(begin_result) {
return begin_result;
}
unsafe { (state.end)(state.callback_context, dst, dst_capacity, src, src_size) }
}
type CompressBeginUsingCDictPublicBeginFn =
unsafe extern "C" fn(*mut c_void, *const c_void, *const ZSTD_frameParameters, u64) -> usize;
/// Explicit projection for the public `ZSTD_compressBegin_usingCDict` wrapper.
///
/// Rust owns the fixed frame policy and unknown-source pledge. C retains the
/// private CDict begin path behind a callback.
#[repr(C)]
pub struct ZSTD_rust_compressBeginUsingCDictPublicState {
callback_context: *mut c_void,
cdict: *const c_void,
begin: CompressBeginUsingCDictPublicBeginFn,
}
const _: () = {
assert!(
offset_of!(
ZSTD_rust_compressBeginUsingCDictPublicState,
callback_context
) == 0
);
assert!(offset_of!(ZSTD_rust_compressBeginUsingCDictPublicState, cdict) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressBeginUsingCDictPublicState, begin) == 2 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_compressBeginUsingCDictPublicState>() == size_of::<[usize; 3]>());
};
/// Start a CDict frame with the legacy fixed frame policy.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressBeginUsingCDictPublic(
state: *const ZSTD_rust_compressBeginUsingCDictPublicState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.cdict.is_null() {
return ERROR(ZstdErrorCode::DictionaryWrong);
}
if state.callback_context.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let f_params = ZSTD_frameParameters {
contentSizeFlag: 0,
checksumFlag: 0,
noDictIDFlag: 0,
};
unsafe {
(state.begin)(
state.callback_context,
state.cdict,
&f_params,
ZSTD_CONTENTSIZE_UNKNOWN,
)
}
}
type CreateCDictFn = unsafe extern "C" fn(
*mut c_void,
*const c_void,
usize,
c_int,
c_int,
*const ZSTD_compressionParameters,
) -> *mut c_void;
type CreateCDictSetCompressionLevelFn = unsafe extern "C" fn(*mut c_void, *mut c_void, c_int);
/// Explicit projection for the public `ZSTD_createCDict` constructors.
///
/// Rust owns create-CDict parameter selection and default-level normalization.
/// C retains advanced allocation, private workspace setup, and CDict scalar
/// publication behind callbacks.
#[repr(C)]
pub struct ZSTD_rust_createCDictState {
callback_context: *mut c_void,
exclusion_mask: *const c_uint,
create: CreateCDictFn,
set_compression_level: CreateCDictSetCompressionLevelFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_createCDictState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_createCDictState, exclusion_mask) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_createCDictState, create) == 2 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_createCDictState, set_compression_level) == 3 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_createCDictState>() == size_of::<[usize; 4]>());
};
/// Select parameters and construct a regular or by-reference CDict.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_createCDict(
state: *const ZSTD_rust_createCDictState,
dict: *const c_void,
dict_size: usize,
compression_level: c_int,
dict_load_method: c_int,
) -> *mut c_void {
if state.is_null() {
return ptr::null_mut();
}
let state = unsafe { &*state };
if state.exclusion_mask.is_null() {
return ptr::null_mut();
}
let cparams = ZSTD_rust_params_getParamsInternal(
compression_level,
ZSTD_CONTENTSIZE_UNKNOWN,
dict_size,
ZSTD_RUST_CPM_CREATE_CDICT,
unsafe { *state.exclusion_mask },
)
.cParams;
let cdict = unsafe {
(state.create)(
state.callback_context,
dict,
dict_size,
dict_load_method,
ZSTD_DCT_AUTO,
&cparams,
)
};
if cdict.is_null() {
return ptr::null_mut();
}
let normalized_level = if compression_level == 0 {
ZSTD_rust_params_defaultCLevel()
} else {
compression_level
};
unsafe { (state.set_compression_level)(state.callback_context, cdict, normalized_level) };
cdict
}
type CreateCDictAdvancedValidateCustomMemFn = unsafe extern "C" fn(*mut c_void) -> c_int;
type CreateCDictAdvancedWorkspaceSizeFn = unsafe extern "C" fn(
*mut c_void,
usize,
c_int,
*const ZSTD_compressionParameters,
c_int,
c_int,
) -> usize;
type CreateCDictAdvancedAllocateFn = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void;
type CreateCDictAdvancedCreateFn = unsafe extern "C" fn(
*mut c_void,
*mut c_void,
usize,
usize,
c_int,
*const ZSTD_compressionParameters,
c_int,
c_int,
) -> *mut c_void;
type CreateCDictAdvancedInitFn = unsafe extern "C" fn(
*mut c_void,
*mut c_void,
*const c_void,
usize,
c_int,
c_int,
*const ZSTD_CCtx_params,
) -> usize;
type CreateCDictAdvancedFreeFn = unsafe extern "C" fn(*mut c_void, *mut c_void);
type CreateCDictAdvancedFreeWorkspaceFn = unsafe extern "C" fn(*mut c_void, *mut c_void);
/// Explicit projection for the public `ZSTD_createCDict_advanced2` wrapper.
///
/// Rust owns context-free parameter preparation, custom-memory validation,
/// allocation, and callback ordering. C retains private workspace
/// construction, CDict initialization, and teardown behind narrow callbacks.
/// The three field pointers keep the private `ZSTD_CCtx_params` layout opaque
/// here while allowing C to publish the fields selected by the Rust parameter
/// leaf.
#[repr(C)]
pub struct ZSTD_rust_createCDictAdvancedState {
callback_context: *mut c_void,
cctx_params: *mut ZSTD_CCtx_params,
cparams: *const ZSTD_compressionParameters,
enable_dedicated_dict_search: *const c_int,
use_row_match_finder: *const c_int,
exclusion_mask: u32,
ldm_default_window_log: u32,
validate_custom_mem: CreateCDictAdvancedValidateCustomMemFn,
workspace_size: CreateCDictAdvancedWorkspaceSizeFn,
allocate: CreateCDictAdvancedAllocateFn,
create: CreateCDictAdvancedCreateFn,
init: CreateCDictAdvancedInitFn,
free_workspace: CreateCDictAdvancedFreeWorkspaceFn,
free: CreateCDictAdvancedFreeFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_createCDictAdvancedState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_createCDictAdvancedState, cctx_params) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_createCDictAdvancedState, cparams) == 2 * size_of::<usize>());
assert!(
offset_of!(
ZSTD_rust_createCDictAdvancedState,
enable_dedicated_dict_search
) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, use_row_match_finder)
== 4 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, exclusion_mask) == 5 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, ldm_default_window_log)
== 5 * size_of::<usize>() + size_of::<u32>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, validate_custom_mem)
== size_of::<[usize; 5]>() + size_of::<[u32; 2]>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, workspace_size)
== size_of::<[usize; 6]>() + size_of::<[u32; 2]>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, allocate)
== size_of::<[usize; 7]>() + size_of::<[u32; 2]>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, create)
== size_of::<[usize; 8]>() + size_of::<[u32; 2]>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, init)
== size_of::<[usize; 9]>() + size_of::<[u32; 2]>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, free_workspace)
== size_of::<[usize; 10]>() + size_of::<[u32; 2]>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, free)
== size_of::<[usize; 11]>() + size_of::<[u32; 2]>()
);
assert!(
size_of::<ZSTD_rust_createCDictAdvancedState>()
== size_of::<[usize; 12]>() + size_of::<[u32; 2]>()
);
};
/// Prepare advanced-CDict parameters and run the C-owned construction path.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_createCDictAdvanced(
state: *const ZSTD_rust_createCDictAdvancedState,
dict: *const c_void,
dict_size: usize,
dict_load_method: c_int,
dict_content_type: c_int,
) -> *mut c_void {
if state.is_null() {
return ptr::null_mut();
}
let state = unsafe { &*state };
if state.callback_context.is_null()
|| state.cctx_params.is_null()
|| state.cparams.is_null()
|| state.enable_dedicated_dict_search.is_null()
|| state.use_row_match_finder.is_null()
{
return ptr::null_mut();
}
let prepare_result = unsafe {
crate::zstd_compress_params_api::ZSTD_rust_params_prepareAdvancedCDict(
state.cctx_params,
dict_size,
state.ldm_default_window_log,
state.exclusion_mask,
)
};
if ERR_isError(prepare_result) {
return ptr::null_mut();
}
if unsafe { (state.validate_custom_mem)(state.callback_context) == 0 } {
return ptr::null_mut();
}
let workspace_size = unsafe {
(state.workspace_size)(
state.callback_context,
dict_size,
dict_load_method,
state.cparams,
*state.use_row_match_finder,
*state.enable_dedicated_dict_search,
)
};
let workspace = unsafe { (state.allocate)(state.callback_context, workspace_size) };
if workspace.is_null() {
return ptr::null_mut();
}
let cdict = unsafe {
(state.create)(
state.callback_context,
workspace,
workspace_size,
dict_size,
dict_load_method,
state.cparams,
*state.use_row_match_finder,
*state.enable_dedicated_dict_search,
)
};
if cdict.is_null() {
unsafe { (state.free_workspace)(state.callback_context, workspace) };
return ptr::null_mut();
}
let init_result = unsafe {
(state.init)(
state.callback_context,
cdict,
dict,
dict_size,
dict_load_method,
dict_content_type,
state.cctx_params.cast_const(),
)
};
if ERR_isError(init_result) {
unsafe { (state.free)(state.callback_context, cdict) };
return ptr::null_mut();
}
cdict
}
type FreeCDictWorkspaceFn = unsafe extern "C" fn(*mut c_void);
type FreeCDictObjectFn = unsafe extern "C" fn(*mut c_void);
/// Explicit projection for the public `ZSTD_freeCDict` wrapper.
///
/// Rust owns null handling, workspace/object ordering, and the embedded-object
/// decision. C retains private workspace and allocator teardown callbacks.
#[repr(C)]
pub struct ZSTD_rust_freeCDictState {
callback_context: *mut c_void,
cdict_in_workspace: c_int,
free_workspace: FreeCDictWorkspaceFn,
free_object: FreeCDictObjectFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_freeCDictState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_freeCDictState, cdict_in_workspace) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_freeCDictState, free_workspace) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_freeCDictState, free_object) == 3 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_freeCDictState>() == size_of::<[usize; 4]>());
};
/// Free private CDict storage and, when applicable, its outer object.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_freeCDict(state: *const ZSTD_rust_freeCDictState) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.callback_context.is_null() {
return 0;
}
unsafe {
(state.free_workspace)(state.callback_context);
if state.cdict_in_workspace == 0 {
(state.free_object)(state.callback_context);
}
}
0
}
type InitStaticCDictFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void;
/// Explicit projection for the public `ZSTD_initStaticCDict` wrapper.
///
/// Rust owns workspace pointer/alignment validation, minimum-size validation,
/// and callback ordering. C retains the private static-workspace layout and
/// dictionary initialization behind one callback.
#[repr(C)]
pub struct ZSTD_rust_initStaticCDictState {
callback_context: *mut c_void,
workspace: *mut c_void,
workspace_size: usize,
needed_size: usize,
init: InitStaticCDictFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_initStaticCDictState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_initStaticCDictState, workspace) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initStaticCDictState, workspace_size) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initStaticCDictState, needed_size) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initStaticCDictState, init) == 4 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_initStaticCDictState>() == size_of::<[usize; 5]>());
};
/// Validate static CDict workspace ownership before entering the C leaf.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_initStaticCDict(
state: *const ZSTD_rust_initStaticCDictState,
) -> *mut c_void {
if state.is_null() {
return ptr::null_mut();
}
let state = unsafe { &*state };
if state.callback_context.is_null()
|| state.workspace.is_null()
|| (state.workspace as usize) & (ZSTD_STATIC_WORKSPACE_ALIGNMENT - 1) != 0
|| state.workspace_size < state.needed_size
{
return ptr::null_mut();
}
unsafe { (state.init)(state.callback_context) }
}
type CompressBeginUsingDictBeginFn =
unsafe extern "C" fn(*mut c_void, *const c_void, usize, *const c_void, u64) -> usize;
/// Explicit projection for the public `ZSTD_compressBegin_usingDict` family.
///
/// Rust owns unknown-source parameter selection and default-level
/// normalization. C retains private `ZSTD_CCtx_params` initialization and the
/// final `ZSTD_compressBegin_internal` call behind callbacks.
#[repr(C)]
pub struct ZSTD_rust_compressBeginUsingDictState {
cctx: *mut c_void,
cctx_params: *mut ZSTD_CCtx_params,
exclusion_mask: *const c_uint,
init_params: CompressBeginUsingCDictInitParamsFn,
begin: CompressBeginUsingDictBeginFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressBeginUsingDictState, cctx) == 0);
assert!(offset_of!(ZSTD_rust_compressBeginUsingDictState, cctx_params) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressBeginUsingDictState, exclusion_mask) == 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressBeginUsingDictState, init_params) == 3 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_compressBeginUsingDictState, begin) == 4 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_compressBeginUsingDictState>() == size_of::<[usize; 5]>());
};
/// Select parameters and start a dictionary-backed frame.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressBeginUsingDict(
state: *const ZSTD_rust_compressBeginUsingDictState,
dict: *const c_void,
dict_size: usize,
compression_level: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.cctx.is_null() || state.cctx_params.is_null() || state.exclusion_mask.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let params = ZSTD_rust_params_getParamsInternal(
compression_level,
ZSTD_CONTENTSIZE_UNKNOWN,
dict_size,
ZSTD_RUST_CPM_NO_ATTACH_DICT,
unsafe { *state.exclusion_mask },
);
let init_level = if compression_level == 0 {
ZSTD_rust_params_defaultCLevel()
} else {
compression_level
};
unsafe {
(state.init_params)(state.cctx_params.cast(), &params, init_level);
(state.begin)(
state.cctx,
dict,
dict_size,
state.cctx_params.cast_const().cast(),
ZSTD_CONTENTSIZE_UNKNOWN,
)
}
}
type InitCDictReserveContentFn = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void;
type InitCDictReserveEntropyFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void;
type InitCDictResetMatchStateFn = unsafe extern "C" fn(*mut c_void, *const c_void, c_int) -> usize;
type InitCDictInsertDictionaryFn =
unsafe extern "C" fn(*mut c_void, *const c_void, *const c_void, usize, c_int) -> usize;
/// Projection for CDict content/state initialization.
///
/// Rust owns the content branch/copy, initialization order, and scalar field
/// policy. C callbacks retain private workspace allocation, match-state reset,
/// and dictionary insertion because those operations use private CDict
/// layouts.
#[repr(C)]
pub struct ZSTD_rust_initCDictState {
callback_context: *mut c_void,
params: *const c_void,
c_params: *const ZSTD_compressionParameters,
match_state_c_params: *mut ZSTD_compressionParameters,
dedicated_dict_search: *mut c_int,
enable_dedicated_dict_search: *const c_int,
use_row_match_finder: *const c_int,
dict_content: *mut *const c_void,
dict_content_size: *mut usize,
dict_content_type: *mut c_int,
entropy_workspace: *mut *mut c_void,
dict_id: *mut c_uint,
compression_level: *mut c_int,
content_size_flag: *mut c_int,
reserve_content: Option<InitCDictReserveContentFn>,
reserve_entropy: Option<InitCDictReserveEntropyFn>,
block_state: *mut c_void,
reset_match_state: Option<InitCDictResetMatchStateFn>,
insert_dictionary: Option<InitCDictInsertDictionaryFn>,
}
const _: () = {
assert!(size_of::<InitCDictReserveContentFn>() == size_of::<usize>());
assert!(size_of::<InitCDictReserveEntropyFn>() == size_of::<usize>());
assert!(size_of::<InitCDictResetMatchStateFn>() == size_of::<usize>());
assert!(size_of::<InitCDictInsertDictionaryFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_initCDictState, params) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, c_params) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, match_state_c_params) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, dedicated_dict_search) == 4 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_initCDictState, enable_dedicated_dict_search)
== 5 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_initCDictState, use_row_match_finder) == 6 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, dict_content) == 7 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, dict_content_size) == size_of::<[usize; 8]>());
assert!(offset_of!(ZSTD_rust_initCDictState, dict_content_type) == 9 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, entropy_workspace) == 10 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, dict_id) == 11 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, compression_level) == 12 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, content_size_flag) == 13 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, reserve_content) == 14 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, reserve_entropy) == 15 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, block_state) == 16 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, reset_match_state) == 17 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_initCDictState, insert_dictionary) == 18 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_initCDictState>() == size_of::<[usize; 19]>());
};
const CDICT_DEFAULT_CLEVEL: c_int = 3;
/// Initialize a private CDict through narrow C callbacks.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_initCDict(
state: *const ZSTD_rust_initCDictState,
dict: *const c_void,
dict_size: usize,
dict_load_method: c_int,
dict_content_type: c_int,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let Some(reserve_content) = state.reserve_content else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(reserve_entropy) = state.reserve_entropy else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(reset_match_state) = state.reset_match_state else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(insert_dictionary) = state.insert_dictionary else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callback_context.is_null()
|| state.params.is_null()
|| state.c_params.is_null()
|| state.match_state_c_params.is_null()
|| state.dedicated_dict_search.is_null()
|| state.enable_dedicated_dict_search.is_null()
|| state.use_row_match_finder.is_null()
|| state.dict_content.is_null()
|| state.dict_content_size.is_null()
|| state.dict_content_type.is_null()
|| state.entropy_workspace.is_null()
|| state.dict_id.is_null()
|| state.compression_level.is_null()
|| state.content_size_flag.is_null()
|| state.block_state.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
if dict_load_method == ZSTD_DLM_BY_REF || dict.is_null() || dict_size == 0 {
unsafe { *state.dict_content = dict };
} else {
let internal_buffer = unsafe { reserve_content(state.callback_context, dict_size) };
if internal_buffer.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
unsafe {
ptr::copy_nonoverlapping(dict.cast::<u8>(), internal_buffer.cast::<u8>(), dict_size);
*state.dict_content = internal_buffer.cast_const();
}
}
unsafe {
*state.match_state_c_params = *state.c_params;
*state.dedicated_dict_search = *state.enable_dedicated_dict_search;
*state.dict_content_size = dict_size;
*state.dict_content_type = dict_content_type;
}
let entropy_workspace = unsafe { reserve_entropy(state.callback_context) };
if entropy_workspace.is_null() {
return ERROR(ZstdErrorCode::MemoryAllocation);
}
unsafe {
*state.entropy_workspace = entropy_workspace;
}
unsafe { ZSTD_rust_resetCompressedBlockState(state.block_state.cast()) };
let reset_match_result = unsafe {
reset_match_state(
state.callback_context,
state.c_params.cast(),
*state.use_row_match_finder,
)
};
if ERR_isError(reset_match_result) {
return reset_match_result;
}
unsafe {
*state.compression_level = CDICT_DEFAULT_CLEVEL;
*state.content_size_flag = 1;
}
let dict_content = unsafe { *state.dict_content };
let dict_id = unsafe {
insert_dictionary(
state.callback_context,
state.params,
dict_content,
dict_size,
dict_content_type,
)
};
if ERR_isError(dict_id) {
return dict_id;
}
if dict_id > c_uint::MAX as usize {
return ERROR(ZstdErrorCode::Generic);
}
unsafe { *state.dict_id = dict_id as c_uint };
0
}
type CompressBeginResetInternalFn =
unsafe extern "C" fn(*mut c_void, *const c_void, u64, usize, c_int) -> usize;
type CompressBeginResetUsingCDictFn =
unsafe extern "C" fn(*mut c_void, *const c_void, *const c_void, u64, c_int) -> usize;
type CompressBeginInsertDictionaryFn =
unsafe extern "C" fn(*mut c_void, *const c_void, *const c_void, usize, c_int, c_int) -> usize;
/// Projection for the dictionary-selection portion of
/// `ZSTD_compressBegin_internal`.
///
/// Rust owns the CDict attach decision and dictionary-result publication. C
/// retains the context reset, CDict attach, and private insertion callbacks.
#[repr(C)]
pub struct ZSTD_rust_compressBeginState {
callback_context: *mut c_void,
params: *const c_void,
cdict: *const c_void,
cdict_content_size: *const usize,
cdict_compression_level: *const c_int,
attach_dict_pref: *const c_int,
dict: *const c_void,
dict_size: *const usize,
dict_content_type: *const c_int,
dtlm: *const c_int,
pledged_src_size: *const u64,
zbuff: *const c_int,
force_load: *const c_int,
dict_id: *mut c_uint,
dict_content_size: *mut usize,
reset_internal: Option<CompressBeginResetInternalFn>,
reset_using_cdict: Option<CompressBeginResetUsingCDictFn>,
insert_dictionary: Option<CompressBeginInsertDictionaryFn>,
}
const _: () = {
assert!(size_of::<CompressBeginResetInternalFn>() == size_of::<usize>());
assert!(size_of::<CompressBeginResetUsingCDictFn>() == size_of::<usize>());
assert!(size_of::<CompressBeginInsertDictionaryFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressBeginState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressBeginState, params) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressBeginState, cdict) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressBeginState, cdict_content_size) == 3 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressBeginState, cdict_compression_level) == 4 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_compressBeginState, attach_dict_pref) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressBeginState, dict) == 6 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressBeginState, dict_size) == 7 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressBeginState, dict_content_type) == size_of::<[usize; 8]>());
assert!(offset_of!(ZSTD_rust_compressBeginState, dtlm) == 9 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressBeginState, pledged_src_size) == 10 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressBeginState, zbuff) == 11 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressBeginState, force_load) == size_of::<[usize; 12]>());
assert!(offset_of!(ZSTD_rust_compressBeginState, dict_id) == 13 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressBeginState, dict_content_size) == size_of::<[usize; 14]>()
);
assert!(offset_of!(ZSTD_rust_compressBeginState, reset_internal) == 15 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressBeginState, reset_using_cdict) == 16 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressBeginState, insert_dictionary) == 17 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_compressBeginState>() == size_of::<[usize; 18]>());
};
/// Select and begin a dictionary-backed compression context.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressBegin(
state: *const ZSTD_rust_compressBeginState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let Some(reset_internal) = state.reset_internal else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(reset_using_cdict) = state.reset_using_cdict else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(insert_dictionary) = state.insert_dictionary else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callback_context.is_null()
|| state.params.is_null()
|| state.cdict_content_size.is_null() && !state.cdict.is_null()
|| state.cdict_compression_level.is_null() && !state.cdict.is_null()
|| state.attach_dict_pref.is_null()
|| state.dict_size.is_null()
|| state.dict_content_type.is_null()
|| state.dtlm.is_null()
|| state.pledged_src_size.is_null()
|| state.zbuff.is_null()
|| state.force_load.is_null()
|| state.dict_id.is_null()
|| state.dict_content_size.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let cdict_present = !state.cdict.is_null();
let pledged_src_size = unsafe { *state.pledged_src_size };
let dict_content_size = if cdict_present {
unsafe { *state.cdict_content_size }
} else {
unsafe { *state.dict_size }
};
let cdict_compression_level = if cdict_present {
unsafe { *state.cdict_compression_level }
} else {
0
};
let should_attach = cdict_present
&& dict_content_size > 0
&& (pledged_src_size < CDICT_PARAMS_SRC_SIZE_CUTOFF
|| pledged_src_size
< (dict_content_size as u64).wrapping_mul(CDICT_PARAMS_DICT_SIZE_MULTIPLIER)
|| pledged_src_size == ZSTD_CONTENTSIZE_UNKNOWN
|| cdict_compression_level == 0)
&& unsafe { *state.attach_dict_pref } != unsafe { *state.force_load };
if should_attach {
let result = unsafe {
reset_using_cdict(
state.callback_context,
state.cdict,
state.params,
pledged_src_size,
*state.zbuff,
)
};
return result;
}
let reset_result = unsafe {
reset_internal(
state.callback_context,
state.params,
pledged_src_size,
dict_content_size,
*state.zbuff,
)
};
if ERR_isError(reset_result) {
return reset_result;
}
let dict_id = unsafe {
insert_dictionary(
state.callback_context,
state.cdict,
state.dict,
*state.dict_size,
*state.dict_content_type,
*state.dtlm,
)
};
if ERR_isError(dict_id) {
return dict_id;
}
if dict_id > c_uint::MAX as usize {
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
*state.dict_id = dict_id as c_uint;
*state.dict_content_size = dict_content_size;
}
0
}
type ResetCCtxUsingCDictAttachFn =
unsafe extern "C" fn(*mut c_void, *const c_void, *const c_void, u64, c_int) -> usize;
type ResetCCtxUsingCDictCopyFn =
unsafe extern "C" fn(*mut c_void, *const c_void, *const c_void, u64, c_int) -> usize;
/// Projection for the attach-versus-copy decision in
/// `ZSTD_resetCCtx_usingCDict`.
///
/// Rust owns the dictionary attachment policy. C retains both reset
/// implementations because they operate on private CCtx, CDict, workspace,
/// and match-state layouts.
#[repr(C)]
pub struct ZSTD_rust_resetCCtxUsingCDictState {
callback_context: *mut c_void,
cdict: *const c_void,
params: *const c_void,
cdict_strategy: *const c_int,
dedicated_dict_search: *const c_int,
attach_dict_pref: *const c_int,
force_window: *const c_int,
pledged_src_size: *const u64,
zbuff: *const c_int,
attach: Option<ResetCCtxUsingCDictAttachFn>,
copy: Option<ResetCCtxUsingCDictCopyFn>,
}
const _: () = {
assert!(size_of::<ResetCCtxUsingCDictAttachFn>() == size_of::<usize>());
assert!(size_of::<ResetCCtxUsingCDictCopyFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxUsingCDictState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_resetCCtxUsingCDictState, cdict) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxUsingCDictState, params) == 2 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_resetCCtxUsingCDictState, cdict_strategy) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxUsingCDictState, dedicated_dict_search)
== 4 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxUsingCDictState, attach_dict_pref) == 5 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_resetCCtxUsingCDictState, force_window) == 6 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_resetCCtxUsingCDictState, pledged_src_size) == 7 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_resetCCtxUsingCDictState, zbuff) == size_of::<[usize; 8]>());
assert!(offset_of!(ZSTD_rust_resetCCtxUsingCDictState, attach) == 9 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxUsingCDictState, copy) == 10 * size_of::<usize>());
assert!(size_of::<ZSTD_rust_resetCCtxUsingCDictState>() == size_of::<[usize; 11]>());
};
/// Select the private C reset implementation for an already-built CDict.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_resetCCtxUsingCDict(
state: *const ZSTD_rust_resetCCtxUsingCDictState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let Some(attach) = state.attach else {
return ERROR(ZstdErrorCode::Generic);
};
let Some(copy) = state.copy else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callback_context.is_null()
|| state.cdict.is_null()
|| state.params.is_null()
|| state.cdict_strategy.is_null()
|| state.dedicated_dict_search.is_null()
|| state.attach_dict_pref.is_null()
|| state.force_window.is_null()
|| state.pledged_src_size.is_null()
|| state.zbuff.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let should_attach = ZSTD_rust_params_shouldAttachDict(
unsafe { *state.cdict_strategy },
unsafe { *state.dedicated_dict_search },
unsafe { *state.pledged_src_size },
unsafe { *state.attach_dict_pref },
unsafe { *state.force_window },
) != 0;
let callback = if should_attach { attach } else { copy };
unsafe {
callback(
state.callback_context,
state.cdict,
state.params,
*state.pledged_src_size,
*state.zbuff,
)
}
}
type ResetCCtxByCopyingCDictResetFn =
unsafe extern "C" fn(*mut c_void, *const c_void, *const c_void, u64, c_int) -> usize;
type ResetCCtxByCopyingCDictMarkTablesFn = unsafe extern "C" fn(*mut c_void);
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct ZSTD_rust_copyWindowState {
next_src: *const c_void,
base: *const c_void,
dict_base: *const c_void,
dict_limit: c_uint,
low_limit: c_uint,
nb_overflow_corrections: c_uint,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_copyWindowState, next_src) == 0);
assert!(offset_of!(ZSTD_rust_copyWindowState, base) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_copyWindowState, dict_base) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_copyWindowState, dict_limit) == 3 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_copyWindowState, low_limit)
== 3 * size_of::<usize>() + size_of::<c_uint>()
);
assert!(
offset_of!(ZSTD_rust_copyWindowState, nb_overflow_corrections)
== 3 * size_of::<usize>() + 2 * size_of::<c_uint>()
);
assert!(
size_of::<ZSTD_rust_copyWindowState>()
== (3 * size_of::<usize>() + 3 * size_of::<c_uint>()).div_ceil(size_of::<usize>())
* size_of::<usize>()
);
};
/// Projection for copying a prepared CDict into a working CCtx.
///
/// Rust owns the reset/copy ordering, table-size policy, and table copies, and
/// stops on reset allocation failure. C retains the private CCtx/CDict layout
/// behind explicit table-field projections and callbacks.
#[repr(C)]
pub struct ZSTD_rust_resetCCtxByCopyingCDictState {
callback_context: *mut c_void,
cdict: *const c_void,
params: *const c_void,
pledged_src_size: u64,
reset: Option<ResetCCtxByCopyingCDictResetFn>,
mark_tables_dirty: Option<ResetCCtxByCopyingCDictMarkTablesFn>,
mark_tables_clean: Option<ResetCCtxByCopyingCDictMarkTablesFn>,
destination_hash_table: *mut *mut c_uint,
source_hash_table: *const c_uint,
source_hash_log: *const c_uint,
destination_chain_table: *mut *mut c_uint,
source_chain_table: *const c_uint,
source_chain_log: *const c_uint,
source_strategy: *const c_int,
source_use_row_match_finder: *const c_int,
source_indices_tagged: *const c_int,
destination_strategy: *const c_int,
destination_use_row_match_finder: *const c_int,
destination_tag_table: *mut *mut u8,
source_tag_table: *const u8,
destination_hash_salt: *mut u64,
source_hash_salt: *const u64,
destination_hash_table3: *mut *mut c_uint,
destination_hash_log3: *const c_uint,
destination_window: *mut c_void,
source_window: *const c_void,
destination_next_to_update: *mut c_uint,
source_next_to_update: *const c_uint,
destination_loaded_dict_end: *mut c_uint,
source_loaded_dict_end: *const c_uint,
destination_dict_id: *mut c_uint,
source_dict_id: *const c_uint,
destination_dict_content_size: *mut usize,
source_dict_content_size: *const usize,
destination_block_state: *mut *mut ZSTD_compressedBlockState_t,
source_block_state: *const ZSTD_compressedBlockState_t,
zbuff: c_int,
}
const _: () = {
assert!(size_of::<ResetCCtxByCopyingCDictResetFn>() == size_of::<usize>());
assert!(size_of::<ResetCCtxByCopyingCDictMarkTablesFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxByCopyingCDictState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_resetCCtxByCopyingCDictState, cdict) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxByCopyingCDictState, params) == 2 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_resetCCtxByCopyingCDictState, pledged_src_size)
== 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxByCopyingCDictState, reset)
== 3 * size_of::<usize>() + size_of::<u64>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxByCopyingCDictState, zbuff)
== 3 * size_of::<usize>() + size_of::<u64>() + size_of::<[usize; 32]>()
);
assert!(
size_of::<ZSTD_rust_resetCCtxByCopyingCDictState>()
== (offset_of!(ZSTD_rust_resetCCtxByCopyingCDictState, zbuff) + size_of::<c_int>())
.div_ceil(size_of::<usize>())
* size_of::<usize>()
);
};
/// Run the private CDict-copy operation through C-owned layout callbacks and
/// explicit table-field projections, then copy the compressed-block state
/// directly in Rust.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_resetCCtxByCopyingCDict(
state: *const ZSTD_rust_resetCCtxByCopyingCDictState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let (Some(reset), Some(mark_tables_dirty), Some(mark_tables_clean)) = (
state.reset,
state.mark_tables_dirty,
state.mark_tables_clean,
) else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callback_context.is_null()
|| state.cdict.is_null()
|| state.params.is_null()
|| state.destination_hash_table.is_null()
|| state.source_hash_log.is_null()
|| state.destination_chain_table.is_null()
|| state.source_chain_log.is_null()
|| state.source_strategy.is_null()
|| state.source_use_row_match_finder.is_null()
|| state.source_indices_tagged.is_null()
|| state.destination_strategy.is_null()
|| state.destination_use_row_match_finder.is_null()
|| state.destination_tag_table.is_null()
|| state.destination_hash_salt.is_null()
|| state.source_hash_salt.is_null()
|| state.destination_hash_table3.is_null()
|| state.destination_hash_log3.is_null()
|| state.destination_window.is_null()
|| state.source_window.is_null()
|| state.destination_next_to_update.is_null()
|| state.source_next_to_update.is_null()
|| state.destination_loaded_dict_end.is_null()
|| state.source_loaded_dict_end.is_null()
|| state.destination_dict_id.is_null()
|| state.source_dict_id.is_null()
|| state.destination_dict_content_size.is_null()
|| state.source_dict_content_size.is_null()
|| state.destination_block_state.is_null()
|| state.source_block_state.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let source_hash_log = unsafe { *state.source_hash_log };
let source_chain_log = unsafe { *state.source_chain_log };
let source_strategy = unsafe { *state.source_strategy };
let source_use_row_match_finder = unsafe { *state.source_use_row_match_finder };
let source_indices_tagged = unsafe { *state.source_indices_tagged };
let Some(hash_table_size) = 1usize.checked_shl(source_hash_log) else {
return ERROR(ZstdErrorCode::Generic);
};
let source_chain_table_size = if ZSTD_rust_params_allocateChainTable(
source_strategy,
source_use_row_match_finder,
0,
) != 0
{
let Some(size) = 1usize.checked_shl(source_chain_log) else {
return ERROR(ZstdErrorCode::Generic);
};
size
} else {
0
};
let row_match_finder_used =
ZSTD_rust_params_rowMatchFinderUsed(source_strategy, source_use_row_match_finder) != 0;
unsafe {
let reset_error = reset(
state.callback_context,
state.cdict,
state.params,
state.pledged_src_size,
state.zbuff,
);
if ERR_isError(reset_error) {
return reset_error;
}
let destination_strategy = *state.destination_strategy;
let destination_use_row_match_finder = *state.destination_use_row_match_finder;
let destination_hash_log3 = *state.destination_hash_log3;
if destination_hash_log3 > 31 {
return ERROR(ZstdErrorCode::Generic);
}
let hash_table3_size = if destination_hash_log3 == 0 {
0
} else {
1usize << destination_hash_log3
};
let destination_chain_table_size = if ZSTD_rust_params_allocateChainTable(
destination_strategy,
destination_use_row_match_finder,
0,
) != 0
{
source_chain_table_size
} else {
0
};
let destination_hash_table = *state.destination_hash_table;
let destination_chain_table = *state.destination_chain_table;
let destination_tag_table = *state.destination_tag_table;
let destination_hash_table3 = *state.destination_hash_table3;
if (destination_hash_table.is_null() || state.source_hash_table.is_null())
|| (destination_chain_table_size != 0
&& (destination_chain_table.is_null() || state.source_chain_table.is_null()))
|| (row_match_finder_used
&& (destination_tag_table.is_null() || state.source_tag_table.is_null()))
|| (hash_table3_size != 0 && destination_hash_table3.is_null())
{
return ERROR(ZstdErrorCode::Generic);
}
mark_tables_dirty(state.callback_context);
ZSTD_rust_copyCDictTableIntoCCtx(
destination_hash_table,
state.source_hash_table,
hash_table_size,
source_indices_tagged,
);
if destination_chain_table_size != 0 {
ZSTD_rust_copyCDictTableIntoCCtx(
destination_chain_table,
state.source_chain_table,
destination_chain_table_size,
source_indices_tagged,
);
}
if row_match_finder_used {
ptr::copy_nonoverlapping(
state.source_tag_table,
destination_tag_table,
hash_table_size,
);
*state.destination_hash_salt = *state.source_hash_salt;
}
if hash_table3_size != 0 {
ptr::write_bytes(destination_hash_table3, 0, hash_table3_size);
}
mark_tables_clean(state.callback_context);
ptr::copy(
state.source_window.cast::<ZSTD_rust_copyWindowState>(),
state.destination_window.cast::<ZSTD_rust_copyWindowState>(),
1,
);
*state.destination_next_to_update = *state.source_next_to_update;
*state.destination_loaded_dict_end = *state.source_loaded_dict_end;
*state.destination_dict_id = *state.source_dict_id;
*state.destination_dict_content_size = *state.source_dict_content_size;
let destination_block_state = *state.destination_block_state;
if destination_block_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
ptr::copy_nonoverlapping(state.source_block_state, destination_block_state, 1);
}
0
}
type ResetCCtxByAttachingCDictResetFn =
unsafe extern "C" fn(*mut c_void, *const c_void, *const c_void, u64, c_int) -> usize;
type ResetCCtxByAttachingCDictAttachFn = unsafe extern "C" fn(*mut c_void, *const c_void);
/// Projection for attaching a prepared CDict to a working CCtx.
///
/// Rust owns reset, attach, and metadata-copy ordering. C retains parameter
/// adjustment, private window linkage, and the CCtx/CDict field layout.
#[repr(C)]
pub struct ZSTD_rust_resetCCtxByAttachingCDictState {
callback_context: *mut c_void,
cdict: *const c_void,
params: *const c_void,
pledged_src_size: u64,
reset: Option<ResetCCtxByAttachingCDictResetFn>,
attach: Option<ResetCCtxByAttachingCDictAttachFn>,
destination_dict_id: *mut c_uint,
source_dict_id: *const c_uint,
destination_dict_content_size: *mut usize,
source_dict_content_size: *const usize,
destination_block_state: *mut *mut ZSTD_compressedBlockState_t,
source_block_state: *const ZSTD_compressedBlockState_t,
zbuff: c_int,
}
const _: () = {
assert!(size_of::<ResetCCtxByAttachingCDictResetFn>() == size_of::<usize>());
assert!(size_of::<ResetCCtxByAttachingCDictAttachFn>() == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxByAttachingCDictState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_resetCCtxByAttachingCDictState, cdict) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_resetCCtxByAttachingCDictState, params) == 2 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_resetCCtxByAttachingCDictState, pledged_src_size)
== 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxByAttachingCDictState, reset)
== 3 * size_of::<usize>() + size_of::<u64>()
);
assert!(
offset_of!(ZSTD_rust_resetCCtxByAttachingCDictState, zbuff)
== 3 * size_of::<usize>() + size_of::<u64>() + size_of::<[usize; 8]>()
);
assert!(
size_of::<ZSTD_rust_resetCCtxByAttachingCDictState>()
== (offset_of!(ZSTD_rust_resetCCtxByAttachingCDictState, zbuff) + size_of::<c_int>())
.div_ceil(size_of::<usize>())
* size_of::<usize>()
);
};
/// Run the private CDict-attachment operation through C-owned layout callbacks
/// and copy the compressed-block state directly in Rust.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_resetCCtxByAttachingCDict(
state: *const ZSTD_rust_resetCCtxByAttachingCDictState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let (Some(reset), Some(attach)) = (state.reset, state.attach) else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callback_context.is_null()
|| state.cdict.is_null()
|| state.params.is_null()
|| state.destination_dict_id.is_null()
|| state.source_dict_id.is_null()
|| state.destination_dict_content_size.is_null()
|| state.source_dict_content_size.is_null()
|| state.destination_block_state.is_null()
|| state.source_block_state.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
unsafe {
let reset_error = reset(
state.callback_context,
state.cdict,
state.params,
state.pledged_src_size,
state.zbuff,
);
if ERR_isError(reset_error) {
return reset_error;
}
attach(state.callback_context, state.cdict);
*state.destination_dict_id = *state.source_dict_id;
*state.destination_dict_content_size = *state.source_dict_content_size;
let destination_block_state = *state.destination_block_state;
if destination_block_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
ptr::copy_nonoverlapping(state.source_block_state, destination_block_state, 1);
}
0
}
/// 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(
context: *mut c_void,
stream_stage: c_int,
dict: *const c_void,
dict_size: usize,
dict_load_method: c_int,
dict_content_type: c_int,
clear_dictionaries: CctxDictionaryClearFn,
assign_local_dict: CctxAssignLocalDictFn,
) -> usize {
if stream_stage != ZSTD_CCTX_INIT_STAGE {
return stage_wrong();
}
unsafe { clear_dictionaries(context) };
if dict.is_null() || dict_size == 0 {
return 0;
}
unsafe {
assign_local_dict(
context,
dict,
dict_size,
dict_load_method,
dict_content_type,
)
}
}
/// Rust-owned policy for `ZSTD_CCtx_refCDict()`.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_CCtx_refCDict(
context: *mut c_void,
stream_stage: c_int,
cdict: *const c_void,
clear_dictionaries: CctxDictionaryClearFn,
assign_cdict: CctxAssignCDictFn,
) -> usize {
if stream_stage != ZSTD_CCTX_INIT_STAGE {
return stage_wrong();
}
unsafe { clear_dictionaries(context) };
unsafe { assign_cdict(context, cdict) };
0
}
/// Rust-owned policy for `ZSTD_CCtx_refPrefix_advanced()`.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_CCtx_refPrefixAdvanced(
context: *mut c_void,
stream_stage: c_int,
prefix: *const c_void,
prefix_size: usize,
dict_content_type: c_int,
clear_dictionaries: CctxDictionaryClearFn,
assign_prefix_dict: CctxAssignPrefixDictFn,
) -> usize {
if stream_stage != ZSTD_CCTX_INIT_STAGE {
return stage_wrong();
}
unsafe { clear_dictionaries(context) };
if prefix.is_null() || prefix_size == 0 {
return 0;
}
unsafe { assign_prefix_dict(context, prefix, prefix_size, dict_content_type) };
0
}
/// Matches C's `ZSTD_dictNCountRepeat()`.
#[inline]
fn dict_n_count_repeat(
normalized_counter: &[c_short],
dict_max_symbol_value: usize,
max_symbol_value: usize,
) -> c_int {
if dict_max_symbol_value < max_symbol_value {
return FSE_REPEAT_CHECK;
}
if normalized_counter.len() <= max_symbol_value
|| normalized_counter[..=max_symbol_value].contains(&0)
{
return FSE_REPEAT_CHECK;
}
FSE_REPEAT_VALID
}
/// Reads and builds one dictionary FSE table, advancing `offset` on success.
///
/// C supplies `max_symbol_value` from `FSE_readNCount()` to the table builder
/// for the match-length and literal-length tables, but deliberately builds the
/// offset table with `MaxOff` so the unused tail is initialized. The caller
/// passes that distinction through `table_max_symbol_value`.
unsafe fn read_and_build_fse_table(
dictionary: &[u8],
offset: &mut usize,
normalized_counter: &mut [c_short],
initial_max_symbol_value: c_uint,
max_table_log: c_uint,
table: *mut c_uint,
table_max_symbol_value: c_uint,
workspace: *mut c_void,
) -> Result<c_uint, ()> {
let input = &dictionary[*offset..];
let mut max_symbol_value = initial_max_symbol_value;
let mut table_log = 0u32;
let header_size = unsafe {
FSE_readNCount(
normalized_counter.as_mut_ptr(),
&mut max_symbol_value,
&mut table_log,
input.as_ptr().cast(),
input.len(),
)
};
if ERR_isError(header_size)
|| header_size > input.len()
|| max_symbol_value as usize >= normalized_counter.len()
|| table_log > max_table_log
{
return Err(());
}
let build_result = unsafe {
FSE_buildCTable_wksp(
table,
normalized_counter.as_ptr(),
table_max_symbol_value,
table_log,
workspace,
HUF_WORKSPACE_SIZE,
)
};
if ERR_isError(build_result) {
return Err(());
}
*offset = offset.checked_add(header_size).ok_or(())?;
Ok(max_symbol_value)
}
/// Rust implementation of C `ZSTD_loadCEntropy()`.
///
/// The input is assumed to have the dictionary magic and ID in its first eight
/// bytes, matching the C function's documented precondition. Malformed or
/// truncated entropy headers are converted to `ZSTD_error_dictionaryCorrupted`
/// just as the C `RETURN_ERROR_IF(..., dictionary_corrupted, ...)` paths do.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_loadCEntropy(
bs: *mut ZSTD_compressedBlockState_t,
workspace: *mut c_void,
dict: *const c_void,
dict_size: usize,
) -> usize {
if bs.is_null()
|| dict.is_null()
|| workspace.is_null()
|| dict_size < DICTIONARY_ID_AND_MAGIC_SIZE
{
return dictionary_corrupted();
}
let dictionary = unsafe { slice::from_raw_parts(dict.cast::<u8>(), dict_size) };
let mut offset = DICTIONARY_ID_AND_MAGIC_SIZE;
unsafe {
(*bs).entropy.huf.repeatMode = HUF_REPEAT_CHECK;
}
let mut huf_max_symbol_value = 255u32;
let mut huf_has_zero_weights = 1u32;
let huf_header_size = unsafe {
HUF_readCTable(
(*bs).entropy.huf.CTable.as_mut_ptr(),
&mut huf_max_symbol_value,
dictionary[offset..].as_ptr().cast(),
dictionary.len() - offset,
&mut huf_has_zero_weights,
)
};
if ERR_isError(huf_header_size) || huf_header_size > dictionary.len().saturating_sub(offset) {
return dictionary_corrupted();
}
if huf_has_zero_weights == 0 && huf_max_symbol_value == 255 {
unsafe {
(*bs).entropy.huf.repeatMode = HUF_REPEAT_VALID;
}
}
offset = match offset.checked_add(huf_header_size) {
Some(value) => value,
None => return dictionary_corrupted(),
};
let mut offcode_ncount = [0i16; MAX_OFF + 1];
let offcode_max_symbol_value = MAX_OFF as c_uint;
let offcode_header_max = match unsafe {
read_and_build_fse_table(
dictionary,
&mut offset,
&mut offcode_ncount,
offcode_max_symbol_value,
OFF_FSE_LOG as c_uint,
(*bs).entropy.fse.offcodeCTable.as_mut_ptr(),
MAX_OFF as c_uint,
workspace,
)
} {
Ok(value) => value,
Err(()) => return dictionary_corrupted(),
};
let mut matchlength_ncount = [0i16; MAX_ML + 1];
let matchlength_max_symbol_value = MAX_ML as c_uint;
let matchlength_header_max = match unsafe {
read_and_build_fse_table(
dictionary,
&mut offset,
&mut matchlength_ncount,
matchlength_max_symbol_value,
ML_FSE_LOG as c_uint,
(*bs).entropy.fse.matchlengthCTable.as_mut_ptr(),
matchlength_max_symbol_value,
workspace,
)
} {
Ok(value) => value,
Err(()) => return dictionary_corrupted(),
};
unsafe {
(*bs).entropy.fse.matchlength_repeatMode =
dict_n_count_repeat(&matchlength_ncount, matchlength_header_max as usize, MAX_ML);
}
let mut litlength_ncount = [0i16; MAX_LL + 1];
let litlength_max_symbol_value = MAX_LL as c_uint;
let litlength_header_max = match unsafe {
read_and_build_fse_table(
dictionary,
&mut offset,
&mut litlength_ncount,
litlength_max_symbol_value,
LL_FSE_LOG as c_uint,
(*bs).entropy.fse.litlengthCTable.as_mut_ptr(),
litlength_max_symbol_value,
workspace,
)
} {
Ok(value) => value,
Err(()) => return dictionary_corrupted(),
};
unsafe {
(*bs).entropy.fse.litlength_repeatMode =
dict_n_count_repeat(&litlength_ncount, litlength_header_max as usize, MAX_LL);
}
if dictionary.len().saturating_sub(offset) < REPCODE_SECTION_SIZE {
return dictionary_corrupted();
}
unsafe {
(*bs).rep[0] = u32::from_le_bytes(dictionary[offset..offset + 4].try_into().unwrap());
(*bs).rep[1] = u32::from_le_bytes(dictionary[offset + 4..offset + 8].try_into().unwrap());
(*bs).rep[2] = u32::from_le_bytes(dictionary[offset + 8..offset + 12].try_into().unwrap());
}
offset += REPCODE_SECTION_SIZE;
let dict_content_size = dictionary.len() - offset;
let offcode_max = if dict_content_size <= (u32::MAX as usize) - (128 << 10) {
let max_offset = (dict_content_size + (128 << 10)) as u32;
ZSTD_highbit32(max_offset) as usize
} else {
MAX_OFF
};
let required_offcode_max = offcode_max.min(MAX_OFF);
let offcode_repeat_mode = dict_n_count_repeat(
&offcode_ncount,
offcode_header_max as usize,
required_offcode_max,
);
unsafe {
(*bs).entropy.fse.offcode_repeatMode = offcode_repeat_mode;
}
unsafe {
for &rep in &(*bs).rep {
if rep == 0 || rep as usize > dict_content_size {
return dictionary_corrupted();
}
}
}
offset
}
/// Rust-owned dispatch for C's `ZSTD_compress_insertDictionary()`.
///
/// The callback is the only operation that still crosses back into C. It
/// receives opaque pointers because the content loader needs private C
/// layouts, while Rust owns short-dictionary handling, mode selection, block
/// state reset, dictionary magic/ID handling, and entropy setup.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressInsertDictionary(
bs: *mut ZSTD_compressedBlockState_t,
match_state: *mut c_void,
ldm_state: *mut c_void,
workspace_state: *mut c_void,
params: *const c_void,
dict: *const c_void,
dict_size: usize,
dict_content_type: c_int,
dtlm: c_int,
tfp: c_int,
workspace: *mut c_void,
no_dict_id_flag: c_int,
load_dictionary_content: LoadDictionaryContentFn,
) -> usize {
if dict.is_null() || dict_size < DICTIONARY_ID_AND_MAGIC_SIZE {
if dict_content_type == ZSTD_DCT_FULL_DICT {
return ERROR(ZstdErrorCode::DictionaryWrong);
}
return 0;
}
unsafe { ZSTD_rust_resetCompressedBlockState(bs) };
if dict_content_type == ZSTD_DCT_RAW_CONTENT {
return unsafe {
load_dictionary_content(
match_state,
ldm_state,
workspace_state,
params,
dict,
dict_size,
dtlm,
tfp,
)
};
}
let dict_magic = unsafe { u32::from_le(ptr::read_unaligned(dict.cast::<u32>())) };
if dict_magic != ZSTD_MAGIC_DICTIONARY {
if dict_content_type == ZSTD_DCT_AUTO {
return unsafe {
load_dictionary_content(
match_state,
ldm_state,
workspace_state,
params,
dict,
dict_size,
dtlm,
tfp,
)
};
}
if dict_content_type == ZSTD_DCT_FULL_DICT {
return ERROR(ZstdErrorCode::DictionaryWrong);
}
debug_assert!(false, "invalid dictionary content type");
}
let dict_id = if no_dict_id_flag != 0 {
0
} else {
unsafe {
u32::from_le(ptr::read_unaligned(dict.cast::<u8>().add(4).cast::<u32>())) as usize
}
};
let entropy_size = unsafe { ZSTD_rust_loadCEntropy(bs, workspace, dict, dict_size) };
if ERR_isError(entropy_size) {
return entropy_size;
}
if entropy_size > dict_size {
return dictionary_corrupted();
}
let content_result = unsafe {
load_dictionary_content(
match_state,
ptr::null_mut(),
workspace_state,
params,
dict.cast::<u8>().add(entropy_size).cast(),
dict_size - entropy_size,
dtlm,
tfp,
)
};
if ERR_isError(content_result) {
return content_result;
}
dict_id
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::ERR_getErrorCode;
use crate::fse_compress::{FSE_normalizeCount, FSE_writeNCount};
use crate::huf_compress::{HUF_buildCTable_wksp, HUF_writeCTable_wksp};
use crate::mem::MEM_writeLE32;
use std::mem::{size_of, MaybeUninit};
const DICT_CONTENT_SIZE: usize = 16;
#[derive(Clone, Copy)]
enum ZeroWeight {
None,
Huffman,
MatchLength,
LiteralLength,
Offset,
}
struct DictionaryLoadProbe {
calls: usize,
ldm_state: *mut c_void,
dict: *const c_void,
dict_size: usize,
dtlm: c_int,
tfp: c_int,
}
impl Default for DictionaryLoadProbe {
fn default() -> Self {
Self {
calls: 0,
ldm_state: ptr::null_mut(),
dict: ptr::null(),
dict_size: 0,
dtlm: 0,
tfp: 0,
}
}
}
struct CctxPolicyProbe {
events: Vec<u8>,
local_dict: *const c_void,
local_dict_size: usize,
local_dict_load_method: c_int,
local_dict_content_type: c_int,
local_result: usize,
cdict: *const c_void,
prefix: *const c_void,
prefix_size: usize,
prefix_content_type: c_int,
}
impl Default for CctxPolicyProbe {
fn default() -> Self {
Self {
events: Vec::new(),
local_dict: ptr::null(),
local_dict_size: 0,
local_dict_load_method: 0,
local_dict_content_type: 0,
local_result: 0,
cdict: ptr::null(),
prefix: ptr::null(),
prefix_size: 0,
prefix_content_type: 0,
}
}
}
struct InitCDictProbe {
events: Vec<&'static str>,
reserved_content_size: usize,
content_storage: [u8; 16],
reserve_content_available: bool,
reset_c_params: ZSTD_compressionParameters,
reset_use_row_match_finder: c_int,
inserted_params: *const c_void,
inserted_dict: *const c_void,
inserted_size: usize,
inserted_content_type: c_int,
entropy_workspace: *mut c_void,
reset_match_result: usize,
insert_result: usize,
}
impl Default for InitCDictProbe {
fn default() -> Self {
Self {
events: Vec::new(),
reserved_content_size: 0,
content_storage: [0; 16],
reserve_content_available: true,
reset_c_params: ZSTD_compressionParameters::default(),
reset_use_row_match_finder: 0,
inserted_params: ptr::null(),
inserted_dict: ptr::null(),
inserted_size: 0,
inserted_content_type: 0,
entropy_workspace: ptr::null_mut(),
reset_match_result: 0,
insert_result: 0,
}
}
}
unsafe fn init_cdict_probe(context: *mut c_void) -> &'static mut InitCDictProbe {
unsafe { &mut *context.cast::<InitCDictProbe>() }
}
unsafe extern "C" fn init_cdict_reserve_content(
context: *mut c_void,
dict_size: usize,
) -> *mut c_void {
let probe = unsafe { init_cdict_probe(context) };
probe.events.push("reserve-content");
probe.reserved_content_size = dict_size;
if !probe.reserve_content_available || dict_size > probe.content_storage.len() {
ptr::null_mut()
} else {
probe.content_storage.as_mut_ptr().cast()
}
}
unsafe extern "C" fn init_cdict_reserve_entropy(context: *mut c_void) -> *mut c_void {
let probe = unsafe { init_cdict_probe(context) };
probe.events.push("reserve");
probe.entropy_workspace
}
unsafe extern "C" fn init_cdict_reset_match_state(
context: *mut c_void,
c_params: *const c_void,
use_row_match_finder: c_int,
) -> usize {
let probe = unsafe { init_cdict_probe(context) };
probe.events.push("reset-match");
probe.reset_c_params = unsafe { *c_params.cast::<ZSTD_compressionParameters>() };
probe.reset_use_row_match_finder = use_row_match_finder;
probe.reset_match_result
}
unsafe extern "C" fn init_cdict_insert_dictionary(
context: *mut c_void,
params: *const c_void,
dict: *const c_void,
dict_size: usize,
dict_content_type: c_int,
) -> usize {
let probe = unsafe { init_cdict_probe(context) };
probe.events.push("insert");
probe.inserted_params = params;
probe.inserted_dict = dict;
probe.inserted_size = dict_size;
probe.inserted_content_type = dict_content_type;
probe.insert_result
}
#[test]
fn cdict_init_orders_callbacks_and_publishes_state() {
let dictionary = [1u8, 2, 3, 4];
let mut probe = InitCDictProbe {
entropy_workspace: 0x1000usize as *mut c_void,
insert_result: 0x1234,
..Default::default()
};
let c_params = ZSTD_compressionParameters {
windowLog: 21,
chainLog: 18,
hashLog: 19,
searchLog: 4,
minMatch: 5,
targetLength: 16,
strategy: 3,
};
let mut match_state_c_params = ZSTD_compressionParameters::default();
let enable_dedicated_dict_search = 1;
let use_row_match_finder = 2;
let mut dedicated_dict_search = 0;
let mut block_state =
unsafe { std::mem::MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut dict_content = dictionary.as_ptr().cast::<c_void>();
let mut dict_content_size = 0;
let mut dict_content_type = 0;
let mut entropy_workspace = ptr::null_mut();
let mut dict_id = 0;
let mut compression_level = 99;
let mut content_size_flag = 0;
let params = 0x2000usize as *const c_void;
let callback_context = (&mut probe as *mut InitCDictProbe).cast();
let state = ZSTD_rust_initCDictState {
callback_context,
params,
c_params: &c_params,
match_state_c_params: &mut match_state_c_params,
dedicated_dict_search: &mut dedicated_dict_search,
enable_dedicated_dict_search: &enable_dedicated_dict_search,
use_row_match_finder: &use_row_match_finder,
dict_content: &mut dict_content,
dict_content_size: &mut dict_content_size,
dict_content_type: &mut dict_content_type,
entropy_workspace: &mut entropy_workspace,
dict_id: &mut dict_id,
compression_level: &mut compression_level,
content_size_flag: &mut content_size_flag,
reserve_content: Some(init_cdict_reserve_content),
reserve_entropy: Some(init_cdict_reserve_entropy),
block_state: (&mut block_state as *mut ZSTD_compressedBlockState_t).cast(),
reset_match_state: Some(init_cdict_reset_match_state),
insert_dictionary: Some(init_cdict_insert_dictionary),
};
let result = unsafe {
ZSTD_rust_initCDict(
&state,
dictionary.as_ptr().cast(),
dictionary.len(),
ZSTD_DLM_BY_REF,
ZSTD_DCT_RAW_CONTENT,
)
};
assert_eq!(result, 0);
assert_eq!(probe.events, ["reserve", "reset-match", "insert"]);
assert_eq!(dict_content, dictionary.as_ptr().cast());
assert_eq!(probe.reset_c_params, c_params);
assert_eq!(probe.reset_use_row_match_finder, use_row_match_finder);
assert_eq!(probe.inserted_params, params);
assert_eq!(probe.inserted_dict, dictionary.as_ptr().cast());
assert_eq!(probe.inserted_size, dictionary.len());
assert_eq!(probe.inserted_content_type, ZSTD_DCT_RAW_CONTENT);
assert_eq!(match_state_c_params, c_params);
assert_eq!(dedicated_dict_search, enable_dedicated_dict_search);
assert_eq!(dict_content_size, dictionary.len());
assert_eq!(dict_content_type, ZSTD_DCT_RAW_CONTENT);
assert_eq!(entropy_workspace, probe.entropy_workspace);
assert_eq!(dict_id, probe.insert_result as c_uint);
assert_eq!(compression_level, 3);
assert_eq!(content_size_flag, 1);
assert_eq!(block_state.rep, [1, 4, 8]);
assert_eq!(block_state.entropy.huf.repeatMode, 0);
}
#[test]
fn cdict_init_copies_by_copy_content_in_rust_after_reserving_storage() {
let dictionary = [9u8, 8, 7, 6, 5];
let mut probe = InitCDictProbe {
entropy_workspace: 0x1000usize as *mut c_void,
insert_result: 0x1234,
..Default::default()
};
let c_params = ZSTD_compressionParameters {
windowLog: 21,
chainLog: 18,
hashLog: 19,
searchLog: 4,
minMatch: 5,
targetLength: 16,
strategy: 3,
};
let mut match_state_c_params = ZSTD_compressionParameters::default();
let enable_dedicated_dict_search = 1;
let use_row_match_finder = 2;
let mut dedicated_dict_search = 0;
let mut block_state =
unsafe { std::mem::MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut dict_content = ptr::null();
let mut dict_content_size = 0;
let mut dict_content_type = 0;
let mut entropy_workspace = ptr::null_mut();
let mut dict_id = 0;
let mut compression_level = 99;
let mut content_size_flag = 0;
let params = 0x2000usize as *const c_void;
let callback_context = (&mut probe as *mut InitCDictProbe).cast();
let state = ZSTD_rust_initCDictState {
callback_context,
params,
c_params: &c_params,
match_state_c_params: &mut match_state_c_params,
dedicated_dict_search: &mut dedicated_dict_search,
enable_dedicated_dict_search: &enable_dedicated_dict_search,
use_row_match_finder: &use_row_match_finder,
dict_content: &mut dict_content,
dict_content_size: &mut dict_content_size,
dict_content_type: &mut dict_content_type,
entropy_workspace: &mut entropy_workspace,
dict_id: &mut dict_id,
compression_level: &mut compression_level,
content_size_flag: &mut content_size_flag,
reserve_content: Some(init_cdict_reserve_content),
reserve_entropy: Some(init_cdict_reserve_entropy),
block_state: (&mut block_state as *mut ZSTD_compressedBlockState_t).cast(),
reset_match_state: Some(init_cdict_reset_match_state),
insert_dictionary: Some(init_cdict_insert_dictionary),
};
let result = unsafe {
ZSTD_rust_initCDict(
&state,
dictionary.as_ptr().cast(),
dictionary.len(),
ZSTD_DLM_BY_COPY,
ZSTD_DCT_RAW_CONTENT,
)
};
assert_eq!(result, 0);
assert_eq!(
probe.events,
["reserve-content", "reserve", "reset-match", "insert"]
);
assert_eq!(probe.reserved_content_size, dictionary.len());
assert_eq!(&probe.content_storage[..dictionary.len()], &dictionary);
assert_eq!(dict_content, probe.content_storage.as_ptr().cast());
assert_eq!(probe.inserted_dict, dict_content);
assert_eq!(dict_content_size, dictionary.len());
}
struct CompressBeginProbe {
events: Vec<&'static str>,
reset_params: *const c_void,
reset_loaded_dict_size: usize,
reset_pledged_src_size: u64,
reset_zbuff: c_int,
reset_result: usize,
attach_cdict: *const c_void,
attach_params: *const c_void,
attach_pledged_src_size: u64,
attach_zbuff: c_int,
attach_result: usize,
insert_cdict: *const c_void,
insert_dict: *const c_void,
insert_size: usize,
insert_content_type: c_int,
insert_dtlm: c_int,
insert_result: usize,
}
impl Default for CompressBeginProbe {
fn default() -> Self {
Self {
events: Vec::new(),
reset_params: ptr::null(),
reset_loaded_dict_size: 0,
reset_pledged_src_size: 0,
reset_zbuff: 0,
reset_result: 0,
attach_cdict: ptr::null(),
attach_params: ptr::null(),
attach_pledged_src_size: 0,
attach_zbuff: 0,
attach_result: 0,
insert_cdict: ptr::null(),
insert_dict: ptr::null(),
insert_size: 0,
insert_content_type: 0,
insert_dtlm: 0,
insert_result: 0,
}
}
}
unsafe fn compress_begin_probe(context: *mut c_void) -> &'static mut CompressBeginProbe {
unsafe { &mut *context.cast::<CompressBeginProbe>() }
}
unsafe extern "C" fn compress_begin_reset_internal(
context: *mut c_void,
params: *const c_void,
pledged_src_size: u64,
loaded_dict_size: usize,
zbuff: c_int,
) -> usize {
let probe = unsafe { compress_begin_probe(context) };
probe.events.push("reset");
probe.reset_params = params;
probe.reset_pledged_src_size = pledged_src_size;
probe.reset_loaded_dict_size = loaded_dict_size;
probe.reset_zbuff = zbuff;
probe.reset_result
}
unsafe extern "C" fn compress_begin_reset_using_cdict(
context: *mut c_void,
cdict: *const c_void,
params: *const c_void,
pledged_src_size: u64,
zbuff: c_int,
) -> usize {
let probe = unsafe { compress_begin_probe(context) };
probe.events.push("attach");
probe.attach_cdict = cdict;
probe.attach_params = params;
probe.attach_pledged_src_size = pledged_src_size;
probe.attach_zbuff = zbuff;
probe.attach_result
}
unsafe extern "C" fn compress_begin_insert_dictionary(
context: *mut c_void,
cdict: *const c_void,
dict: *const c_void,
dict_size: usize,
dict_content_type: c_int,
dtlm: c_int,
) -> usize {
let probe = unsafe { compress_begin_probe(context) };
probe.events.push("insert");
probe.insert_cdict = cdict;
probe.insert_dict = dict;
probe.insert_size = dict_size;
probe.insert_content_type = dict_content_type;
probe.insert_dtlm = dtlm;
probe.insert_result
}
#[allow(clippy::too_many_arguments)]
fn compress_begin_test_state(
probe: &mut CompressBeginProbe,
params: *const c_void,
cdict: *const c_void,
cdict_content_size: *const usize,
cdict_compression_level: *const c_int,
attach_dict_pref: &c_int,
dict: *const c_void,
dict_size: &usize,
dict_content_type: &c_int,
dtlm: &c_int,
pledged_src_size: &u64,
zbuff: &c_int,
force_load: &c_int,
dict_id: &mut c_uint,
dict_content_size: &mut usize,
) -> ZSTD_rust_compressBeginState {
ZSTD_rust_compressBeginState {
callback_context: (probe as *mut CompressBeginProbe).cast(),
params,
cdict,
cdict_content_size,
cdict_compression_level,
attach_dict_pref,
dict,
dict_size,
dict_content_type,
dtlm,
pledged_src_size,
zbuff,
force_load,
dict_id,
dict_content_size,
reset_internal: Some(compress_begin_reset_internal),
reset_using_cdict: Some(compress_begin_reset_using_cdict),
insert_dictionary: Some(compress_begin_insert_dictionary),
}
}
#[test]
fn compress_begin_resets_then_inserts_for_loaded_dictionary() {
let dictionary = [1u8, 2, 3, 4];
let mut probe = CompressBeginProbe {
insert_result: 17,
..Default::default()
};
let params = 0x3000usize as *const c_void;
let attach_dict_pref = 0;
let dict_size = dictionary.len();
let dict_content_type = ZSTD_DCT_RAW_CONTENT;
let dtlm = 7;
let pledged_src_size = 1u64 << 20;
let zbuff = 9;
let force_load = 1;
let mut dict_id = 0;
let mut dict_content_size = 0;
let state = compress_begin_test_state(
&mut probe,
params,
ptr::null(),
ptr::null(),
ptr::null(),
&attach_dict_pref,
dictionary.as_ptr().cast(),
&dict_size,
&dict_content_type,
&dtlm,
&pledged_src_size,
&zbuff,
&force_load,
&mut dict_id,
&mut dict_content_size,
);
let result = unsafe { ZSTD_rust_compressBegin(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["reset", "insert"]);
assert_eq!(probe.reset_params, params);
assert_eq!(probe.reset_loaded_dict_size, dictionary.len());
assert_eq!(probe.reset_pledged_src_size, pledged_src_size);
assert_eq!(probe.reset_zbuff, zbuff);
assert!(probe.insert_cdict.is_null());
assert_eq!(probe.insert_dict, dictionary.as_ptr().cast());
assert_eq!(probe.insert_size, dictionary.len());
assert_eq!(probe.insert_content_type, dict_content_type);
assert_eq!(probe.insert_dtlm, dtlm);
assert_eq!(dict_id, probe.insert_result as c_uint);
assert_eq!(dict_content_size, dictionary.len());
}
#[test]
fn compress_begin_attaches_small_cdict_without_insertion() {
let mut probe = CompressBeginProbe::default();
let params = 0x3000usize as *const c_void;
let cdict = 0x4000usize as *const c_void;
let cdict_content_size = 16;
let cdict_compression_level = 0;
let attach_dict_pref = 0;
let dict_size = 0;
let dict_content_type = ZSTD_DCT_AUTO;
let dtlm = 7;
let pledged_src_size = ZSTD_CONTENTSIZE_UNKNOWN;
let zbuff = 9;
let force_load = 1;
let mut dict_id = 0;
let mut dict_content_size = 0;
let state = compress_begin_test_state(
&mut probe,
params,
cdict,
&cdict_content_size,
&cdict_compression_level,
&attach_dict_pref,
ptr::null(),
&dict_size,
&dict_content_type,
&dtlm,
&pledged_src_size,
&zbuff,
&force_load,
&mut dict_id,
&mut dict_content_size,
);
let result = unsafe { ZSTD_rust_compressBegin(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["attach"]);
assert_eq!(probe.attach_cdict, cdict);
assert_eq!(probe.attach_params, params);
assert_eq!(probe.attach_pledged_src_size, pledged_src_size);
assert_eq!(probe.attach_zbuff, zbuff);
}
struct ResetUsingCDictProbe {
events: Vec<&'static str>,
attach_cdict: *const c_void,
attach_params: *const c_void,
attach_pledged_src_size: u64,
attach_zbuff: c_int,
attach_result: usize,
copy_cdict: *const c_void,
copy_params: *const c_void,
copy_pledged_src_size: u64,
copy_zbuff: c_int,
copy_result: usize,
}
impl Default for ResetUsingCDictProbe {
fn default() -> Self {
Self {
events: Vec::new(),
attach_cdict: ptr::null(),
attach_params: ptr::null(),
attach_pledged_src_size: 0,
attach_zbuff: 0,
attach_result: 0,
copy_cdict: ptr::null(),
copy_params: ptr::null(),
copy_pledged_src_size: 0,
copy_zbuff: 0,
copy_result: 0,
}
}
}
unsafe fn reset_using_cdict_probe(context: *mut c_void) -> &'static mut ResetUsingCDictProbe {
unsafe { &mut *context.cast::<ResetUsingCDictProbe>() }
}
unsafe extern "C" fn reset_using_cdict_attach(
context: *mut c_void,
cdict: *const c_void,
params: *const c_void,
pledged_src_size: u64,
zbuff: c_int,
) -> usize {
let probe = unsafe { reset_using_cdict_probe(context) };
probe.events.push("attach");
probe.attach_cdict = cdict;
probe.attach_params = params;
probe.attach_pledged_src_size = pledged_src_size;
probe.attach_zbuff = zbuff;
probe.attach_result
}
unsafe extern "C" fn reset_using_cdict_copy(
context: *mut c_void,
cdict: *const c_void,
params: *const c_void,
pledged_src_size: u64,
zbuff: c_int,
) -> usize {
let probe = unsafe { reset_using_cdict_probe(context) };
probe.events.push("copy");
probe.copy_cdict = cdict;
probe.copy_params = params;
probe.copy_pledged_src_size = pledged_src_size;
probe.copy_zbuff = zbuff;
probe.copy_result
}
fn reset_using_cdict_test_state(
probe: &mut ResetUsingCDictProbe,
cdict: *const c_void,
params: *const c_void,
strategy: &c_int,
dedicated_dict_search: &c_int,
attach_dict_pref: &c_int,
force_window: &c_int,
pledged_src_size: &u64,
zbuff: &c_int,
) -> ZSTD_rust_resetCCtxUsingCDictState {
ZSTD_rust_resetCCtxUsingCDictState {
callback_context: (probe as *mut ResetUsingCDictProbe).cast(),
cdict,
params,
cdict_strategy: strategy,
dedicated_dict_search,
attach_dict_pref,
force_window,
pledged_src_size,
zbuff,
attach: Some(reset_using_cdict_attach),
copy: Some(reset_using_cdict_copy),
}
}
#[test]
fn reset_using_cdict_selects_attach_at_policy_cutoff() {
let mut probe = ResetUsingCDictProbe::default();
let cdict = 0x4000usize as *const c_void;
let params = 0x3000usize as *const c_void;
let strategy = 1;
let dedicated_dict_search = 0;
let attach_dict_pref = 0;
let force_window = 0;
let pledged_src_size = 8 * 1024;
let zbuff = 9;
let state = reset_using_cdict_test_state(
&mut probe,
cdict,
params,
&strategy,
&dedicated_dict_search,
&attach_dict_pref,
&force_window,
&pledged_src_size,
&zbuff,
);
let result = unsafe { ZSTD_rust_resetCCtxUsingCDict(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["attach"]);
assert_eq!(probe.attach_cdict, cdict);
assert_eq!(probe.attach_params, params);
assert_eq!(probe.attach_pledged_src_size, pledged_src_size);
assert_eq!(probe.attach_zbuff, zbuff);
assert!(probe.copy_cdict.is_null());
}
#[test]
fn reset_using_cdict_selects_copy_and_propagates_its_error() {
let mut probe = ResetUsingCDictProbe {
copy_result: ERROR(ZstdErrorCode::MemoryAllocation),
..Default::default()
};
let cdict = 0x4000usize as *const c_void;
let params = 0x3000usize as *const c_void;
let strategy = 1;
let dedicated_dict_search = 0;
let attach_dict_pref = 0;
let force_window = 0;
let pledged_src_size = 8 * 1024 + 1;
let zbuff = 9;
let state = reset_using_cdict_test_state(
&mut probe,
cdict,
params,
&strategy,
&dedicated_dict_search,
&attach_dict_pref,
&force_window,
&pledged_src_size,
&zbuff,
);
let result = unsafe { ZSTD_rust_resetCCtxUsingCDict(&state) };
assert_eq!(result, probe.copy_result);
assert_eq!(probe.events, ["copy"]);
assert_eq!(probe.copy_cdict, cdict);
assert_eq!(probe.copy_params, params);
assert_eq!(probe.copy_pledged_src_size, pledged_src_size);
assert_eq!(probe.copy_zbuff, zbuff);
assert!(probe.attach_cdict.is_null());
}
#[test]
fn reset_using_cdict_dedicated_search_overrides_force_copy() {
let mut probe = ResetUsingCDictProbe::default();
let strategy = 1;
let dedicated_dict_search = 1;
let attach_dict_pref = 2;
let force_window = 1;
let pledged_src_size = u64::MAX;
let zbuff = 9;
let state = reset_using_cdict_test_state(
&mut probe,
0x4000usize as *const c_void,
0x3000usize as *const c_void,
&strategy,
&dedicated_dict_search,
&attach_dict_pref,
&force_window,
&pledged_src_size,
&zbuff,
);
let result = unsafe { ZSTD_rust_resetCCtxUsingCDict(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["attach"]);
}
#[derive(Default)]
struct ResetCCtxByCopyingCDictProbe {
events: Vec<&'static str>,
reset_result: usize,
cdict: *const c_void,
params: *const c_void,
pledged_src_size: u64,
zbuff: c_int,
source_hash_table: [c_uint; 4],
destination_hash_table: [c_uint; 4],
destination_hash_table_slot: *mut c_uint,
source_chain_table: [c_uint; 4],
destination_chain_table: [c_uint; 4],
destination_chain_table_slot: *mut c_uint,
source_tag_table: [u8; 4],
destination_tag_table: [u8; 4],
destination_tag_table_slot: *mut u8,
source_hash_salt: u64,
destination_hash_salt: u64,
destination_hash_table3: [c_uint; 4],
destination_hash_table3_slot: *mut c_uint,
destination_hash_log3: c_uint,
source_hash_log: c_uint,
source_chain_log: c_uint,
source_strategy: c_int,
source_use_row_match_finder: c_int,
source_indices_tagged: c_int,
destination_strategy: c_int,
destination_use_row_match_finder: c_int,
}
unsafe fn reset_cctx_by_copying_cdict_probe(
context: *mut c_void,
) -> &'static mut ResetCCtxByCopyingCDictProbe {
unsafe { &mut *context.cast::<ResetCCtxByCopyingCDictProbe>() }
}
unsafe extern "C" fn reset_cctx_by_copying_cdict_reset(
context: *mut c_void,
cdict: *const c_void,
params: *const c_void,
pledged_src_size: u64,
zbuff: c_int,
) -> usize {
let probe = unsafe { reset_cctx_by_copying_cdict_probe(context) };
probe.events.push("reset");
probe.cdict = cdict;
probe.params = params;
probe.pledged_src_size = pledged_src_size;
probe.zbuff = zbuff;
probe.reset_result
}
unsafe extern "C" fn reset_cctx_by_copying_cdict_mark_dirty(context: *mut c_void) {
unsafe { reset_cctx_by_copying_cdict_probe(context) }
.events
.push("dirty");
}
unsafe extern "C" fn reset_cctx_by_copying_cdict_mark_clean(context: *mut c_void) {
unsafe { reset_cctx_by_copying_cdict_probe(context) }
.events
.push("clean");
}
fn reset_cctx_by_copying_cdict_test_state(
probe: &mut ResetCCtxByCopyingCDictProbe,
cdict: *const c_void,
params: *const c_void,
destination_window: *mut c_void,
source_window: *const c_void,
destination_next_to_update: *mut c_uint,
source_next_to_update: *const c_uint,
destination_loaded_dict_end: *mut c_uint,
source_loaded_dict_end: *const c_uint,
destination_dict_id: *mut c_uint,
source_dict_id: *const c_uint,
destination_dict_content_size: *mut usize,
source_dict_content_size: *const usize,
destination_block_state: *mut *mut ZSTD_compressedBlockState_t,
source_block_state: *const ZSTD_compressedBlockState_t,
) -> ZSTD_rust_resetCCtxByCopyingCDictState {
probe.destination_hash_table_slot = probe.destination_hash_table.as_mut_ptr();
probe.destination_chain_table_slot = probe.destination_chain_table.as_mut_ptr();
probe.destination_tag_table_slot = probe.destination_tag_table.as_mut_ptr();
probe.destination_hash_table3_slot = probe.destination_hash_table3.as_mut_ptr();
probe.destination_hash_log3 = 2;
probe.source_hash_log = 2;
probe.source_chain_log = 2;
probe.source_strategy = 2;
probe.source_use_row_match_finder = 2;
probe.source_indices_tagged = 1;
probe.destination_strategy = 2;
probe.destination_use_row_match_finder = 2;
ZSTD_rust_resetCCtxByCopyingCDictState {
callback_context: (probe as *mut ResetCCtxByCopyingCDictProbe).cast(),
cdict,
params,
pledged_src_size: 123,
reset: Some(reset_cctx_by_copying_cdict_reset),
mark_tables_dirty: Some(reset_cctx_by_copying_cdict_mark_dirty),
mark_tables_clean: Some(reset_cctx_by_copying_cdict_mark_clean),
destination_hash_table: &mut probe.destination_hash_table_slot,
source_hash_table: probe.source_hash_table.as_ptr(),
source_hash_log: &probe.source_hash_log,
destination_chain_table: &mut probe.destination_chain_table_slot,
source_chain_table: probe.source_chain_table.as_ptr(),
source_chain_log: &probe.source_chain_log,
source_strategy: &probe.source_strategy,
source_use_row_match_finder: &probe.source_use_row_match_finder,
source_indices_tagged: &probe.source_indices_tagged,
destination_strategy: &probe.destination_strategy,
destination_use_row_match_finder: &probe.destination_use_row_match_finder,
destination_tag_table: &mut probe.destination_tag_table_slot,
source_tag_table: probe.source_tag_table.as_ptr(),
destination_hash_salt: &mut probe.destination_hash_salt,
source_hash_salt: &probe.source_hash_salt,
destination_hash_table3: &mut probe.destination_hash_table3_slot,
destination_hash_log3: &probe.destination_hash_log3,
destination_window,
source_window,
destination_next_to_update,
source_next_to_update,
destination_loaded_dict_end,
source_loaded_dict_end,
destination_dict_id,
source_dict_id,
destination_dict_content_size,
source_dict_content_size,
destination_block_state,
source_block_state,
zbuff: 7,
}
}
#[test]
fn reset_cctx_by_copying_cdict_runs_callbacks_in_original_order() {
let mut probe = ResetCCtxByCopyingCDictProbe {
source_hash_table: [0x0000_0101, 0x0000_0202, 0x0000_0303, 0x0000_0404],
source_chain_table: [0x0000_1101, 0x0000_2202, 0x0000_3303, 0x0000_4404],
destination_hash_table3: [9, 9, 9, 9],
..Default::default()
};
let cdict = 0x4000usize as *const c_void;
let params = 0x3000usize as *const c_void;
let mut source_block_state =
unsafe { std::mem::MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
source_block_state.rep = [9, 10, 11];
source_block_state.entropy.huf.repeatMode = HUF_REPEAT_VALID;
source_block_state.entropy.fse.offcode_repeatMode = FSE_REPEAT_VALID;
let source_window = ZSTD_rust_copyWindowState {
next_src: 0x1000usize as *const c_void,
base: 0x2000usize as *const c_void,
dict_base: 0x3000usize as *const c_void,
dict_limit: 21,
low_limit: 13,
nb_overflow_corrections: 8,
};
let mut destination_window = ZSTD_rust_copyWindowState {
next_src: ptr::null(),
base: ptr::null(),
dict_base: ptr::null(),
dict_limit: 0,
low_limit: 0,
nb_overflow_corrections: 0,
};
let source_next_to_update = 34;
let mut destination_next_to_update = 0;
let source_loaded_dict_end = 55;
let mut destination_loaded_dict_end = 0;
let source_dict_id = 0x1234_5678;
let mut destination_dict_id = 0;
let source_dict_content_size = 9876;
let mut destination_dict_content_size = 0;
let mut destination_block_state =
unsafe { std::mem::MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut destination_block_state_slot =
&mut destination_block_state as *mut ZSTD_compressedBlockState_t;
let state = reset_cctx_by_copying_cdict_test_state(
&mut probe,
cdict,
params,
(&mut destination_window as *mut ZSTD_rust_copyWindowState).cast(),
(&source_window as *const ZSTD_rust_copyWindowState).cast(),
&mut destination_next_to_update,
&source_next_to_update,
&mut destination_loaded_dict_end,
&source_loaded_dict_end,
&mut destination_dict_id,
&source_dict_id,
&mut destination_dict_content_size,
&source_dict_content_size,
&mut destination_block_state_slot,
&source_block_state,
);
let result = unsafe { ZSTD_rust_resetCCtxByCopyingCDict(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["reset", "dirty", "clean"]);
assert_eq!(probe.destination_hash_table, [1, 2, 3, 4]);
assert_eq!(probe.destination_chain_table, [0x11, 0x22, 0x33, 0x44]);
assert_eq!(probe.destination_tag_table, [0; 4]);
assert_eq!(probe.destination_hash_salt, 0);
assert_eq!(probe.destination_hash_table3, [0; 4]);
assert_eq!(probe.cdict, cdict);
assert_eq!(probe.params, params);
assert_eq!(probe.pledged_src_size, 123);
assert_eq!(probe.zbuff, 7);
assert_eq!(destination_window, source_window);
assert_eq!(destination_next_to_update, source_next_to_update);
assert_eq!(destination_loaded_dict_end, source_loaded_dict_end);
assert_eq!(destination_dict_id, source_dict_id);
assert_eq!(destination_dict_content_size, source_dict_content_size);
assert_eq!(destination_block_state.rep, source_block_state.rep);
assert_eq!(
destination_block_state.entropy.huf.repeatMode,
source_block_state.entropy.huf.repeatMode
);
assert_eq!(
destination_block_state.entropy.fse.offcode_repeatMode,
source_block_state.entropy.fse.offcode_repeatMode
);
probe.events.clear();
probe.source_hash_table = [11, 22, 33, 44];
probe.source_tag_table = [5, 6, 7, 8];
probe.destination_hash_table = [0; 4];
probe.destination_tag_table = [0; 4];
probe.destination_hash_table3 = [9, 9, 9, 9];
probe.destination_hash_salt = 0;
probe.source_hash_salt = 0x0123_4567_89ab_cdef;
probe.source_strategy = 3;
probe.source_use_row_match_finder = 1;
probe.source_indices_tagged = 0;
probe.destination_strategy = 3;
probe.destination_use_row_match_finder = 1;
let result = unsafe { ZSTD_rust_resetCCtxByCopyingCDict(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["reset", "dirty", "clean"]);
assert_eq!(probe.destination_hash_table, probe.source_hash_table);
assert_eq!(probe.destination_tag_table, probe.source_tag_table);
assert_eq!(probe.destination_hash_salt, probe.source_hash_salt);
assert_eq!(probe.destination_hash_table3, [0; 4]);
probe.events.clear();
probe.destination_hash_table3 = [9, 9, 9, 9];
probe.destination_hash_log3 = 0;
probe.destination_hash_table3_slot = ptr::null_mut();
let result = unsafe { ZSTD_rust_resetCCtxByCopyingCDict(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["reset", "dirty", "clean"]);
assert_eq!(probe.destination_hash_table3, [9; 4]);
}
#[test]
fn reset_cctx_by_copying_cdict_stops_after_reset_error() {
let mut probe = ResetCCtxByCopyingCDictProbe {
reset_result: ERROR(ZstdErrorCode::MemoryAllocation),
..Default::default()
};
let mut destination_block_state =
unsafe { std::mem::MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut destination_block_state_slot =
&mut destination_block_state as *mut ZSTD_compressedBlockState_t;
let source_block_state =
unsafe { std::mem::MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let source_dict_id = 0x1234_5678;
let mut destination_dict_id = 0;
let source_dict_content_size = 9876;
let mut destination_dict_content_size = 0;
let state = reset_cctx_by_copying_cdict_test_state(
&mut probe,
0x4000usize as *const c_void,
0x3000usize as *const c_void,
ptr::dangling_mut(),
ptr::dangling(),
ptr::dangling_mut(),
ptr::dangling(),
ptr::dangling_mut(),
ptr::dangling(),
&mut destination_dict_id,
&source_dict_id,
&mut destination_dict_content_size,
&source_dict_content_size,
&mut destination_block_state_slot,
&source_block_state,
);
let result = unsafe { ZSTD_rust_resetCCtxByCopyingCDict(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(probe.events, ["reset"]);
}
#[derive(Default)]
struct ResetCCtxByAttachingCDictProbe {
events: Vec<&'static str>,
reset_result: usize,
cdict: *const c_void,
params: *const c_void,
pledged_src_size: u64,
zbuff: c_int,
}
unsafe fn reset_cctx_by_attaching_cdict_probe(
context: *mut c_void,
) -> &'static mut ResetCCtxByAttachingCDictProbe {
unsafe { &mut *context.cast::<ResetCCtxByAttachingCDictProbe>() }
}
unsafe extern "C" fn reset_cctx_by_attaching_cdict_reset(
context: *mut c_void,
cdict: *const c_void,
params: *const c_void,
pledged_src_size: u64,
zbuff: c_int,
) -> usize {
let probe = unsafe { reset_cctx_by_attaching_cdict_probe(context) };
probe.events.push("reset");
probe.cdict = cdict;
probe.params = params;
probe.pledged_src_size = pledged_src_size;
probe.zbuff = zbuff;
probe.reset_result
}
unsafe extern "C" fn reset_cctx_by_attaching_cdict_attach(
context: *mut c_void,
_cdict: *const c_void,
) {
unsafe { reset_cctx_by_attaching_cdict_probe(context) }
.events
.push("attach");
}
fn reset_cctx_by_attaching_cdict_test_state(
probe: &mut ResetCCtxByAttachingCDictProbe,
cdict: *const c_void,
params: *const c_void,
destination_dict_id: *mut c_uint,
source_dict_id: *const c_uint,
destination_dict_content_size: *mut usize,
source_dict_content_size: *const usize,
destination_block_state: *mut *mut ZSTD_compressedBlockState_t,
source_block_state: *const ZSTD_compressedBlockState_t,
) -> ZSTD_rust_resetCCtxByAttachingCDictState {
ZSTD_rust_resetCCtxByAttachingCDictState {
callback_context: (probe as *mut ResetCCtxByAttachingCDictProbe).cast(),
cdict,
params,
pledged_src_size: 123,
reset: Some(reset_cctx_by_attaching_cdict_reset),
attach: Some(reset_cctx_by_attaching_cdict_attach),
destination_dict_id,
source_dict_id,
destination_dict_content_size,
source_dict_content_size,
destination_block_state,
source_block_state,
zbuff: 7,
}
}
#[test]
fn reset_cctx_by_attaching_cdict_runs_callbacks_in_original_order() {
let mut probe = ResetCCtxByAttachingCDictProbe::default();
let cdict = 0x4000usize as *const c_void;
let params = 0x3000usize as *const c_void;
let mut source_block_state =
unsafe { std::mem::MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
source_block_state.rep = [12, 13, 14];
source_block_state.entropy.huf.repeatMode = HUF_REPEAT_VALID;
let source_dict_id = 0x8765_4321;
let mut destination_dict_id = 0;
let source_dict_content_size = 5432;
let mut destination_dict_content_size = 0;
let mut destination_block_state =
unsafe { std::mem::MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut destination_block_state_slot =
&mut destination_block_state as *mut ZSTD_compressedBlockState_t;
let state = reset_cctx_by_attaching_cdict_test_state(
&mut probe,
cdict,
params,
&mut destination_dict_id,
&source_dict_id,
&mut destination_dict_content_size,
&source_dict_content_size,
&mut destination_block_state_slot,
&source_block_state,
);
let result = unsafe { ZSTD_rust_resetCCtxByAttachingCDict(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["reset", "attach"]);
assert_eq!(probe.cdict, cdict);
assert_eq!(probe.params, params);
assert_eq!(probe.pledged_src_size, 123);
assert_eq!(probe.zbuff, 7);
assert_eq!(destination_dict_id, source_dict_id);
assert_eq!(destination_dict_content_size, source_dict_content_size);
assert_eq!(destination_block_state.rep, source_block_state.rep);
assert_eq!(
destination_block_state.entropy.huf.repeatMode,
source_block_state.entropy.huf.repeatMode
);
}
#[test]
fn reset_cctx_by_attaching_cdict_stops_after_reset_error() {
let mut probe = ResetCCtxByAttachingCDictProbe {
reset_result: ERROR(ZstdErrorCode::MemoryAllocation),
..Default::default()
};
let mut destination_block_state =
unsafe { std::mem::MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut destination_block_state_slot =
&mut destination_block_state as *mut ZSTD_compressedBlockState_t;
let source_block_state =
unsafe { std::mem::MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let source_dict_id = 0x8765_4321;
let mut destination_dict_id = 0;
let source_dict_content_size = 5432;
let mut destination_dict_content_size = 0;
let state = reset_cctx_by_attaching_cdict_test_state(
&mut probe,
0x4000usize as *const c_void,
0x3000usize as *const c_void,
&mut destination_dict_id,
&source_dict_id,
&mut destination_dict_content_size,
&source_dict_content_size,
&mut destination_block_state_slot,
&source_block_state,
);
let result = unsafe { ZSTD_rust_resetCCtxByAttachingCDict(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(probe.events, ["reset"]);
}
unsafe fn cctx_policy_probe(context: *mut c_void) -> &'static mut CctxPolicyProbe {
unsafe { &mut *context.cast::<CctxPolicyProbe>() }
}
unsafe extern "C" fn policy_clear(context: *mut c_void) {
unsafe { cctx_policy_probe(context) }.events.push(3);
}
unsafe extern "C" fn policy_assign_local(
context: *mut c_void,
dict: *const c_void,
dict_size: usize,
dict_load_method: c_int,
dict_content_type: c_int,
) -> usize {
let probe = unsafe { cctx_policy_probe(context) };
probe.events.push(4);
probe.local_dict = dict;
probe.local_dict_size = dict_size;
probe.local_dict_load_method = dict_load_method;
probe.local_dict_content_type = dict_content_type;
probe.local_result
}
unsafe extern "C" fn policy_assign_cdict(context: *mut c_void, cdict: *const c_void) {
let probe = unsafe { cctx_policy_probe(context) };
probe.events.push(5);
probe.cdict = cdict;
}
unsafe extern "C" fn policy_assign_prefix(
context: *mut c_void,
prefix: *const c_void,
prefix_size: usize,
dict_content_type: c_int,
) {
let probe = unsafe { cctx_policy_probe(context) };
probe.events.push(6);
probe.prefix = prefix;
probe.prefix_size = prefix_size;
probe.prefix_content_type = dict_content_type;
}
unsafe extern "C" fn record_dictionary_load(
match_state: *mut c_void,
ldm_state: *mut c_void,
_workspace_state: *mut c_void,
_params: *const c_void,
dict: *const c_void,
dict_size: usize,
dtlm: c_int,
tfp: c_int,
) -> usize {
let probe = unsafe { &mut *match_state.cast::<DictionaryLoadProbe>() };
probe.calls += 1;
probe.ldm_state = ldm_state;
probe.dict = dict;
probe.dict_size = dict_size;
probe.dtlm = dtlm;
probe.tfp = tfp;
37
}
unsafe fn dispatch_for_test(
state: &mut ZSTD_compressedBlockState_t,
probe: &mut DictionaryLoadProbe,
dict: *const c_void,
dict_size: usize,
dict_content_type: c_int,
workspace: *mut c_void,
no_dict_id_flag: c_int,
ldm_state: *mut c_void,
) -> usize {
unsafe {
ZSTD_rust_compressInsertDictionary(
state,
(probe as *mut DictionaryLoadProbe).cast(),
ldm_state,
ptr::null_mut(),
ptr::null(),
dict,
dict_size,
dict_content_type,
1,
2,
workspace,
no_dict_id_flag,
record_dictionary_load,
)
}
}
#[test]
fn cctx_dictionary_policy_checks_stage_and_orders_callbacks() {
let mut probe = CctxPolicyProbe::default();
let dictionary = [1u8, 2, 3];
let context = (&mut probe as *mut CctxPolicyProbe).cast::<c_void>();
let result = unsafe {
ZSTD_rust_CCtx_loadDictionaryAdvanced(
context,
1,
dictionary.as_ptr().cast(),
dictionary.len(),
ZSTD_DLM_BY_REF,
ZSTD_DCT_RAW_CONTENT,
policy_clear,
policy_assign_local,
)
};
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert!(probe.events.is_empty());
probe.local_result = ERROR(ZstdErrorCode::MemoryAllocation);
let result = unsafe {
ZSTD_rust_CCtx_loadDictionaryAdvanced(
context,
ZSTD_CCTX_INIT_STAGE,
dictionary.as_ptr().cast(),
dictionary.len(),
ZSTD_DLM_BY_REF,
ZSTD_DCT_RAW_CONTENT,
policy_clear,
policy_assign_local,
)
};
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(probe.events, [3, 4]);
assert_eq!(probe.local_dict, dictionary.as_ptr().cast());
assert_eq!(probe.local_dict_size, dictionary.len());
assert_eq!(probe.local_dict_load_method, ZSTD_DLM_BY_REF);
assert_eq!(probe.local_dict_content_type, ZSTD_DCT_RAW_CONTENT);
}
#[test]
fn cctx_dictionary_policy_clears_before_cdict_and_prefix_assignment() {
let mut probe = CctxPolicyProbe::default();
let cdict_marker = 17u8;
let prefix = [4u8, 5, 6];
let context = (&mut probe as *mut CctxPolicyProbe).cast::<c_void>();
let result = unsafe {
ZSTD_rust_CCtx_refCDict(
context,
ZSTD_CCTX_INIT_STAGE,
(&cdict_marker as *const u8).cast(),
policy_clear,
policy_assign_cdict,
)
};
assert_eq!(result, 0);
assert_eq!(probe.events, [3, 5]);
assert_eq!(probe.cdict, (&cdict_marker as *const u8).cast());
probe.events.clear();
let result = unsafe {
ZSTD_rust_CCtx_refPrefixAdvanced(
context,
ZSTD_CCTX_INIT_STAGE,
prefix.as_ptr().cast(),
prefix.len(),
ZSTD_DCT_FULL_DICT,
policy_clear,
policy_assign_prefix,
)
};
assert_eq!(result, 0);
assert_eq!(probe.events, [3, 6]);
assert_eq!(probe.prefix, prefix.as_ptr().cast());
assert_eq!(probe.prefix_size, prefix.len());
assert_eq!(probe.prefix_content_type, ZSTD_DCT_FULL_DICT);
probe.events.clear();
let result = unsafe {
ZSTD_rust_CCtx_refPrefixAdvanced(
context,
ZSTD_CCTX_INIT_STAGE,
ptr::null(),
0,
ZSTD_DCT_AUTO,
policy_clear,
policy_assign_prefix,
)
};
assert_eq!(result, 0);
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>,
initialized: Option<ZSTD_parameters>,
compression_level: c_int,
min_window_logs: Vec<c_uint>,
begin_result: usize,
}
unsafe fn cdict_begin_probe(context: *mut c_void) -> &'static mut CdictBeginProbe {
unsafe { &mut *context.cast::<CdictBeginProbe>() }
}
unsafe extern "C" fn cdict_begin_init(
context: *mut c_void,
params: *const ZSTD_parameters,
compression_level: c_int,
) {
let probe = unsafe { cdict_begin_probe(context) };
probe.events.push("init");
probe.initialized = Some(unsafe { *params });
probe.compression_level = compression_level;
}
unsafe extern "C" fn cdict_begin_adjust_window(context: *mut c_void, min_window_log: c_uint) {
let probe = unsafe { cdict_begin_probe(context) };
probe.events.push("adjust");
probe.min_window_logs.push(min_window_log);
}
unsafe extern "C" fn cdict_begin_begin(
context: *mut c_void,
_cdict: *const c_void,
_cctx_params: *const c_void,
_pledged_src_size: u64,
) -> usize {
let probe = unsafe { cdict_begin_probe(context) };
probe.events.push("begin");
probe.begin_result
}
fn cdict_begin_test_state(
probe: &mut CdictBeginProbe,
cdict: *const c_void,
cdict_cparams: &ZSTD_compressionParameters,
cdict_content_size: &usize,
cdict_compression_level: &c_int,
f_params: &ZSTD_frameParameters,
pledged_src_size: &u64,
exclusion_mask: &c_uint,
) -> ZSTD_rust_compressBeginUsingCDictState {
ZSTD_rust_compressBeginUsingCDictState {
cctx: (probe as *mut CdictBeginProbe).cast(),
cdict,
cctx_params: (probe as *mut CdictBeginProbe).cast(),
cdict_cparams,
cdict_content_size,
cdict_compression_level,
f_params,
pledged_src_size,
exclusion_mask,
init_params: cdict_begin_init,
adjust_window: cdict_begin_adjust_window,
begin: cdict_begin_begin,
}
}
#[test]
fn cdict_begin_rejects_a_null_dictionary_before_reading_state() {
let state = ZSTD_rust_compressBeginUsingCDictState {
cctx: ptr::null_mut(),
cdict: ptr::null(),
cctx_params: ptr::null_mut(),
cdict_cparams: ptr::null(),
cdict_content_size: ptr::null(),
cdict_compression_level: ptr::null(),
f_params: ptr::null(),
pledged_src_size: ptr::null(),
exclusion_mask: ptr::null(),
init_params: cdict_begin_init,
adjust_window: cdict_begin_adjust_window,
begin: cdict_begin_begin,
};
let result = unsafe { ZSTD_rust_compressBeginUsingCDict(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::DictionaryWrong));
}
#[test]
fn cdict_begin_reuses_cdict_params_below_the_source_cutoff() {
let mut probe = CdictBeginProbe::default();
let cdict_cparams = ZSTD_compressionParameters {
windowLog: 17,
chainLog: 12,
hashLog: 13,
searchLog: 1,
minMatch: 4,
targetLength: 16,
strategy: 3,
};
let cdict_content_size = 1usize;
let cdict_compression_level = 3;
let f_params = ZSTD_frameParameters::default();
let pledged_src_size = CDICT_PARAMS_SRC_SIZE_CUTOFF - 1;
let exclusion_mask = 0;
let state = cdict_begin_test_state(
&mut probe,
ptr::dangling(),
&cdict_cparams,
&cdict_content_size,
&cdict_compression_level,
&f_params,
&pledged_src_size,
&exclusion_mask,
);
let result = unsafe { ZSTD_rust_compressBeginUsingCDict(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["init", "adjust", "begin"]);
assert_eq!(probe.initialized.unwrap().cParams, cdict_cparams);
assert_eq!(probe.compression_level, cdict_compression_level);
assert_eq!(probe.min_window_logs, [17]);
}
#[test]
fn cdict_begin_reuses_cdict_params_for_unknown_source_without_adjustment() {
let mut probe = CdictBeginProbe::default();
let cdict_cparams = ZSTD_compressionParameters {
windowLog: 17,
chainLog: 12,
hashLog: 13,
searchLog: 1,
minMatch: 4,
targetLength: 16,
strategy: 3,
};
let cdict_content_size = 1usize;
let cdict_compression_level = 3;
let f_params = ZSTD_frameParameters::default();
let pledged_src_size = ZSTD_CONTENTSIZE_UNKNOWN;
let exclusion_mask = 0;
let state = cdict_begin_test_state(
&mut probe,
ptr::dangling(),
&cdict_cparams,
&cdict_content_size,
&cdict_compression_level,
&f_params,
&pledged_src_size,
&exclusion_mask,
);
let result = unsafe { ZSTD_rust_compressBeginUsingCDict(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["init", "begin"]);
assert_eq!(probe.initialized.unwrap().cParams, cdict_cparams);
assert!(probe.min_window_logs.is_empty());
}
#[test]
fn cdict_begin_reuses_cdict_params_for_dictionary_ratio_or_zero_level() {
let cdict_cparams = ZSTD_compressionParameters {
windowLog: 10,
chainLog: 6,
hashLog: 6,
searchLog: 1,
minMatch: 3,
targetLength: 0,
strategy: 1,
};
let cdict_content_size = 64 * 1024usize;
let f_params = ZSTD_frameParameters::default();
let exclusion_mask = 0;
let mut ratio_probe = CdictBeginProbe::default();
let ratio_compression_level = 3;
let ratio_pledged_src_size = 256 * 1024;
let ratio_state = cdict_begin_test_state(
&mut ratio_probe,
ptr::dangling(),
&cdict_cparams,
&cdict_content_size,
&ratio_compression_level,
&f_params,
&ratio_pledged_src_size,
&exclusion_mask,
);
let ratio_result = unsafe { ZSTD_rust_compressBeginUsingCDict(&ratio_state) };
assert_eq!(ratio_result, 0);
assert_eq!(ratio_probe.initialized.unwrap().cParams, cdict_cparams);
let mut zero_level_probe = CdictBeginProbe::default();
let zero_level_compression_level = 0;
let zero_level_pledged_src_size = 1 << 20;
let zero_level_state = cdict_begin_test_state(
&mut zero_level_probe,
ptr::dangling(),
&cdict_cparams,
&cdict_content_size,
&zero_level_compression_level,
&f_params,
&zero_level_pledged_src_size,
&exclusion_mask,
);
let zero_level_result = unsafe { ZSTD_rust_compressBeginUsingCDict(&zero_level_state) };
assert_eq!(zero_level_result, 0);
assert_eq!(zero_level_probe.initialized.unwrap().cParams, cdict_cparams);
}
#[test]
fn cdict_begin_selects_public_params_at_the_source_cutoff() {
let mut probe = CdictBeginProbe::default();
let cdict_cparams = ZSTD_compressionParameters {
windowLog: 10,
chainLog: 6,
hashLog: 6,
searchLog: 1,
minMatch: 3,
targetLength: 0,
strategy: 1,
};
let cdict_content_size = 1usize;
let cdict_compression_level = 3;
let f_params = ZSTD_frameParameters::default();
let pledged_src_size = CDICT_PARAMS_SRC_SIZE_CUTOFF;
let exclusion_mask = 0;
let state = cdict_begin_test_state(
&mut probe,
ptr::dangling(),
&cdict_cparams,
&cdict_content_size,
&cdict_compression_level,
&f_params,
&pledged_src_size,
&exclusion_mask,
);
let expected_cparams = ZSTD_rust_params_getCParams(
cdict_compression_level,
pledged_src_size,
cdict_content_size,
exclusion_mask,
);
let result = unsafe { ZSTD_rust_compressBeginUsingCDict(&state) };
assert_eq!(result, 0);
assert_eq!(probe.initialized.unwrap().cParams, expected_cparams);
assert_eq!(probe.events, ["init", "adjust", "begin"]);
}
#[test]
fn cdict_begin_preserves_order_and_begin_errors_for_large_sources() {
let mut probe = CdictBeginProbe {
begin_result: ERROR(ZstdErrorCode::DstSizeTooSmall),
..Default::default()
};
let cdict_cparams = ZSTD_compressionParameters {
windowLog: 10,
chainLog: 6,
hashLog: 6,
searchLog: 1,
minMatch: 3,
targetLength: 0,
strategy: 1,
};
let cdict_content_size = 1usize;
let cdict_compression_level = 3;
let f_params = ZSTD_frameParameters::default();
let pledged_src_size = 1 << 20;
let exclusion_mask = 0;
let state = cdict_begin_test_state(
&mut probe,
ptr::dangling(),
&cdict_cparams,
&cdict_content_size,
&cdict_compression_level,
&f_params,
&pledged_src_size,
&exclusion_mask,
);
let result = unsafe { ZSTD_rust_compressBeginUsingCDict(&state) };
assert_eq!(result, probe.begin_result);
assert_eq!(probe.events, ["init", "adjust", "begin"]);
assert_eq!(probe.min_window_logs, [19]);
}
#[derive(Default)]
struct CompressUsingCDictProbe {
events: Vec<&'static str>,
cdict: *const c_void,
frame_params: ZSTD_frameParameters,
pledged_src_size: u64,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
begin_result: usize,
end_result: usize,
}
unsafe extern "C" fn compress_using_cdict_test_begin(
context: *mut c_void,
cdict: *const c_void,
frame_params: *const ZSTD_frameParameters,
pledged_src_size: u64,
) -> usize {
let probe = unsafe { &mut *context.cast::<CompressUsingCDictProbe>() };
probe.events.push("begin");
probe.cdict = cdict;
probe.frame_params = unsafe { *frame_params };
probe.pledged_src_size = pledged_src_size;
probe.begin_result
}
unsafe extern "C" fn compress_using_cdict_test_end(
context: *mut c_void,
dst: *mut c_void,
dst_capacity: usize,
src: *const c_void,
src_size: usize,
) -> usize {
let probe = unsafe { &mut *context.cast::<CompressUsingCDictProbe>() };
probe.events.push("end");
probe.dst = dst;
probe.dst_capacity = dst_capacity;
probe.src = src;
probe.src_size = src_size;
probe.end_result
}
fn compress_using_cdict_test_state(
probe: &mut CompressUsingCDictProbe,
cdict: *const c_void,
) -> ZSTD_rust_compressUsingCDictState {
ZSTD_rust_compressUsingCDictState {
callback_context: (probe as *mut CompressUsingCDictProbe).cast(),
cdict,
begin: compress_using_cdict_test_begin,
end: compress_using_cdict_test_end,
}
}
#[test]
fn compress_using_cdict_preserves_frame_policy_and_begin_end_order() {
let mut probe = CompressUsingCDictProbe {
end_result: 37,
..Default::default()
};
let cdict = 0x4000usize as *const c_void;
let state = compress_using_cdict_test_state(&mut probe, cdict);
let mut dst = [0u8; 8];
let src = [1u8, 2, 3];
let result = unsafe {
ZSTD_rust_compressUsingCDict(
&state,
dst.as_mut_ptr().cast(),
dst.len(),
src.as_ptr().cast(),
src.len(),
)
};
assert_eq!(result, probe.end_result);
assert_eq!(probe.events, ["begin", "end"]);
assert_eq!(probe.cdict, cdict);
assert_eq!(probe.frame_params.contentSizeFlag, 1);
assert_eq!(probe.frame_params.checksumFlag, 0);
assert_eq!(probe.frame_params.noDictIDFlag, 0);
assert_eq!(probe.pledged_src_size, src.len() as u64);
assert_eq!(probe.dst, dst.as_mut_ptr().cast());
assert_eq!(probe.dst_capacity, dst.len());
assert_eq!(probe.src, src.as_ptr().cast());
assert_eq!(probe.src_size, src.len());
}
#[test]
fn compress_using_cdict_stops_before_end_after_begin_error() {
let mut probe = CompressUsingCDictProbe {
begin_result: ERROR(ZstdErrorCode::MemoryAllocation),
..Default::default()
};
let state = compress_using_cdict_test_state(&mut probe, ptr::dangling());
let result =
unsafe { ZSTD_rust_compressUsingCDict(&state, ptr::null_mut(), 0, ptr::null(), 0) };
assert_eq!(result, probe.begin_result);
assert_eq!(probe.events, ["begin"]);
}
fn compress_using_cdict_advanced_test_state(
probe: &mut CompressUsingCDictProbe,
cdict: *const c_void,
f_params: &ZSTD_frameParameters,
) -> ZSTD_rust_compressUsingCDictAdvancedState {
ZSTD_rust_compressUsingCDictAdvancedState {
callback_context: (probe as *mut CompressUsingCDictProbe).cast(),
cdict,
f_params,
begin: compress_using_cdict_test_begin,
end: compress_using_cdict_test_end,
}
}
#[test]
fn compress_using_cdict_advanced_preserves_frame_policy_and_begin_end_order() {
let mut probe = CompressUsingCDictProbe {
end_result: 41,
..Default::default()
};
let cdict = ptr::dangling::<c_void>();
let f_params = ZSTD_frameParameters {
contentSizeFlag: 0,
checksumFlag: 1,
noDictIDFlag: 1,
};
let state = compress_using_cdict_advanced_test_state(&mut probe, cdict, &f_params);
let mut dst = [0u8; 4];
let src = [9u8, 8];
let result = unsafe {
ZSTD_rust_compressUsingCDictAdvanced(
&state,
dst.as_mut_ptr().cast(),
dst.len(),
src.as_ptr().cast(),
src.len(),
)
};
assert_eq!(result, probe.end_result);
assert_eq!(probe.events, ["begin", "end"]);
assert_eq!(probe.cdict, cdict);
assert_eq!(probe.frame_params.contentSizeFlag, 0);
assert_eq!(probe.frame_params.checksumFlag, 1);
assert_eq!(probe.frame_params.noDictIDFlag, 1);
assert_eq!(probe.pledged_src_size, src.len() as u64);
assert_eq!(probe.dst, dst.as_mut_ptr().cast());
assert_eq!(probe.dst_capacity, dst.len());
assert_eq!(probe.src, src.as_ptr().cast());
assert_eq!(probe.src_size, src.len());
}
#[test]
fn compress_using_cdict_advanced_rejects_a_missing_frame_policy_before_callback() {
let mut probe = CompressUsingCDictProbe::default();
let state = ZSTD_rust_compressUsingCDictAdvancedState {
callback_context: (&mut probe as *mut CompressUsingCDictProbe).cast(),
cdict: ptr::dangling(),
f_params: ptr::null(),
begin: compress_using_cdict_test_begin,
end: compress_using_cdict_test_end,
};
let result = unsafe {
ZSTD_rust_compressUsingCDictAdvanced(&state, ptr::null_mut(), 0, ptr::null(), 0)
};
assert_eq!(result, ERROR(ZstdErrorCode::Generic));
assert!(probe.events.is_empty());
}
fn compress_begin_using_cdict_public_test_state(
probe: &mut CompressUsingCDictProbe,
cdict: *const c_void,
) -> ZSTD_rust_compressBeginUsingCDictPublicState {
ZSTD_rust_compressBeginUsingCDictPublicState {
callback_context: (probe as *mut CompressUsingCDictProbe).cast(),
cdict,
begin: compress_using_cdict_test_begin,
}
}
#[test]
fn compress_begin_using_cdict_public_preserves_fixed_frame_policy() {
let mut probe = CompressUsingCDictProbe {
begin_result: 43,
..Default::default()
};
let cdict = 0x5000usize as *const c_void;
let state = compress_begin_using_cdict_public_test_state(&mut probe, cdict);
let result = unsafe { ZSTD_rust_compressBeginUsingCDictPublic(&state) };
assert_eq!(result, probe.begin_result);
assert_eq!(probe.events, ["begin"]);
assert_eq!(probe.cdict, cdict);
assert_eq!(probe.frame_params.contentSizeFlag, 0);
assert_eq!(probe.frame_params.checksumFlag, 0);
assert_eq!(probe.frame_params.noDictIDFlag, 0);
assert_eq!(probe.pledged_src_size, ZSTD_CONTENTSIZE_UNKNOWN);
}
#[test]
fn compress_begin_using_cdict_public_rejects_null_dictionary_before_callback() {
let mut probe = CompressUsingCDictProbe::default();
let state = compress_begin_using_cdict_public_test_state(&mut probe, ptr::null());
let result = unsafe { ZSTD_rust_compressBeginUsingCDictPublic(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::DictionaryWrong));
assert!(probe.events.is_empty());
}
#[derive(Default)]
struct CreateCDictProbe {
events: Vec<&'static str>,
dict: *const c_void,
dict_size: usize,
dict_load_method: c_int,
dict_content_type: c_int,
cparams: ZSTD_compressionParameters,
cdict_result: *mut c_void,
published_cdict: *mut c_void,
compression_level: c_int,
}
unsafe extern "C" fn create_cdict_test_create(
context: *mut c_void,
dict: *const c_void,
dict_size: usize,
dict_load_method: c_int,
dict_content_type: c_int,
cparams: *const ZSTD_compressionParameters,
) -> *mut c_void {
let probe = unsafe { &mut *context.cast::<CreateCDictProbe>() };
probe.events.push("create");
probe.dict = dict;
probe.dict_size = dict_size;
probe.dict_load_method = dict_load_method;
probe.dict_content_type = dict_content_type;
probe.cparams = unsafe { *cparams };
probe.cdict_result
}
unsafe extern "C" fn create_cdict_test_set_compression_level(
context: *mut c_void,
cdict: *mut c_void,
compression_level: c_int,
) {
let probe = unsafe { &mut *context.cast::<CreateCDictProbe>() };
probe.events.push("set");
probe.published_cdict = cdict;
probe.compression_level = compression_level;
}
fn create_cdict_test_state(
probe: &mut CreateCDictProbe,
exclusion_mask: &c_uint,
) -> ZSTD_rust_createCDictState {
ZSTD_rust_createCDictState {
callback_context: (probe as *mut CreateCDictProbe).cast(),
exclusion_mask,
create: create_cdict_test_create,
set_compression_level: create_cdict_test_set_compression_level,
}
}
#[test]
fn create_cdict_selects_params_and_normalizes_default_level() {
let mut probe = CreateCDictProbe {
cdict_result: ptr::dangling_mut(),
..Default::default()
};
let dict = [1u8, 2, 3, 4];
let exclusion_mask = 0;
let state = create_cdict_test_state(&mut probe, &exclusion_mask);
let result = unsafe {
ZSTD_rust_createCDict(&state, dict.as_ptr().cast(), dict.len(), 0, ZSTD_DLM_BY_REF)
};
let expected_cparams = ZSTD_rust_params_getParamsInternal(
0,
ZSTD_CONTENTSIZE_UNKNOWN,
dict.len(),
ZSTD_RUST_CPM_CREATE_CDICT,
exclusion_mask,
)
.cParams;
assert_eq!(result, probe.cdict_result);
assert_eq!(probe.events, ["create", "set"]);
assert_eq!(probe.dict, dict.as_ptr().cast());
assert_eq!(probe.dict_size, dict.len());
assert_eq!(probe.dict_load_method, ZSTD_DLM_BY_REF);
assert_eq!(probe.dict_content_type, ZSTD_DCT_AUTO);
assert_eq!(probe.cparams, expected_cparams);
assert_eq!(probe.published_cdict, probe.cdict_result);
assert_eq!(probe.compression_level, ZSTD_rust_params_defaultCLevel());
}
#[test]
fn create_cdict_stops_before_publication_after_creation_failure() {
let mut probe = CreateCDictProbe::default();
let exclusion_mask = 0;
let state = create_cdict_test_state(&mut probe, &exclusion_mask);
let result = unsafe { ZSTD_rust_createCDict(&state, ptr::null(), 0, 3, ZSTD_DLM_BY_REF) };
assert!(result.is_null());
assert_eq!(probe.events, ["create"]);
assert!(probe.published_cdict.is_null());
}
#[derive(Default)]
struct CreateCDictAdvancedProbe {
events: Vec<&'static str>,
custom_mem_valid: c_int,
workspace_size_result: usize,
workspace_size_dict_size: usize,
workspace_size_dict_load_method: c_int,
workspace_size_cparams: *const ZSTD_compressionParameters,
workspace_size_use_row_match_finder: c_int,
workspace_size_enable_dedicated_dict_search: c_int,
allocated_workspace: *mut c_void,
allocated_workspace_size: usize,
create_workspace: *mut c_void,
create_workspace_size: usize,
create_dict_size: usize,
create_dict_load_method: c_int,
cparams: *const ZSTD_compressionParameters,
use_row_match_finder: c_int,
enable_dedicated_dict_search: c_int,
cdict_result: *mut c_void,
init_cdict: *mut c_void,
init_dict: *const c_void,
init_dict_size: usize,
init_dict_load_method: c_int,
init_dict_content_type: c_int,
init_cctx_params: *const ZSTD_CCtx_params,
free_workspace: *mut c_void,
free_cdict: *mut c_void,
init_result: usize,
}
unsafe extern "C" fn create_cdict_advanced_test_validate_custom_mem(
context: *mut c_void,
) -> c_int {
let probe = unsafe { &mut *context.cast::<CreateCDictAdvancedProbe>() };
probe.events.push("validate");
probe.custom_mem_valid
}
unsafe extern "C" fn create_cdict_advanced_test_workspace_size(
context: *mut c_void,
dict_size: usize,
dict_load_method: c_int,
cparams: *const ZSTD_compressionParameters,
use_row_match_finder: c_int,
enable_dedicated_dict_search: c_int,
) -> usize {
let probe = unsafe { &mut *context.cast::<CreateCDictAdvancedProbe>() };
probe.events.push("workspace_size");
probe.workspace_size_dict_size = dict_size;
probe.workspace_size_dict_load_method = dict_load_method;
probe.workspace_size_cparams = cparams;
probe.workspace_size_use_row_match_finder = use_row_match_finder;
probe.workspace_size_enable_dedicated_dict_search = enable_dedicated_dict_search;
probe.workspace_size_result
}
unsafe extern "C" fn create_cdict_advanced_test_allocate(
context: *mut c_void,
workspace_size: usize,
) -> *mut c_void {
let probe = unsafe { &mut *context.cast::<CreateCDictAdvancedProbe>() };
probe.events.push("allocate");
probe.allocated_workspace_size = workspace_size;
probe.allocated_workspace
}
unsafe extern "C" fn create_cdict_advanced_test_create(
context: *mut c_void,
workspace: *mut c_void,
workspace_size: usize,
dict_size: usize,
dict_load_method: c_int,
cparams: *const ZSTD_compressionParameters,
use_row_match_finder: c_int,
enable_dedicated_dict_search: c_int,
) -> *mut c_void {
let probe = unsafe { &mut *context.cast::<CreateCDictAdvancedProbe>() };
probe.events.push("create");
probe.create_workspace = workspace;
probe.create_workspace_size = workspace_size;
probe.create_dict_size = dict_size;
probe.create_dict_load_method = dict_load_method;
probe.cparams = cparams;
probe.use_row_match_finder = use_row_match_finder;
probe.enable_dedicated_dict_search = enable_dedicated_dict_search;
probe.cdict_result
}
unsafe extern "C" fn create_cdict_advanced_test_init(
context: *mut c_void,
cdict: *mut c_void,
dict: *const c_void,
dict_size: usize,
dict_load_method: c_int,
dict_content_type: c_int,
cctx_params: *const ZSTD_CCtx_params,
) -> usize {
let probe = unsafe { &mut *context.cast::<CreateCDictAdvancedProbe>() };
probe.events.push("init");
probe.init_cdict = cdict;
probe.init_dict = dict;
probe.init_dict_size = dict_size;
probe.init_dict_load_method = dict_load_method;
probe.init_dict_content_type = dict_content_type;
probe.init_cctx_params = cctx_params;
probe.init_result
}
unsafe extern "C" fn create_cdict_advanced_test_free_workspace(
context: *mut c_void,
workspace: *mut c_void,
) {
let probe = unsafe { &mut *context.cast::<CreateCDictAdvancedProbe>() };
probe.events.push("free_workspace");
probe.free_workspace = workspace;
}
unsafe extern "C" fn create_cdict_advanced_test_free(context: *mut c_void, cdict: *mut c_void) {
let probe = unsafe { &mut *context.cast::<CreateCDictAdvancedProbe>() };
probe.events.push("free");
probe.free_cdict = cdict;
}
fn create_cdict_advanced_test_params() -> MaybeUninit<ZSTD_CCtx_params> {
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
unsafe {
assert_eq!(
crate::zstd_compress_params_api::ZSTD_CCtxParams_init(
storage.as_mut_ptr(),
ZSTD_rust_params_defaultCLevel(),
),
0
);
}
storage
}
fn create_cdict_advanced_test_state(
probe: &mut CreateCDictAdvancedProbe,
cctx_params: *mut ZSTD_CCtx_params,
cparams: &ZSTD_compressionParameters,
enable_dedicated_dict_search: &c_int,
use_row_match_finder: &c_int,
) -> ZSTD_rust_createCDictAdvancedState {
ZSTD_rust_createCDictAdvancedState {
callback_context: (probe as *mut CreateCDictAdvancedProbe).cast(),
cctx_params,
cparams,
enable_dedicated_dict_search,
use_row_match_finder,
exclusion_mask: 0,
ldm_default_window_log: 27,
validate_custom_mem: create_cdict_advanced_test_validate_custom_mem,
workspace_size: create_cdict_advanced_test_workspace_size,
allocate: create_cdict_advanced_test_allocate,
create: create_cdict_advanced_test_create,
init: create_cdict_advanced_test_init,
free_workspace: create_cdict_advanced_test_free_workspace,
free: create_cdict_advanced_test_free,
}
}
#[test]
fn create_cdict_advanced_publishes_params_and_preserves_create_init_order() {
let mut probe = CreateCDictAdvancedProbe {
custom_mem_valid: 1,
workspace_size_result: 123,
allocated_workspace: ptr::dangling_mut(),
cdict_result: ptr::dangling_mut(),
..Default::default()
};
let mut params_storage = create_cdict_advanced_test_params();
let cctx_params = params_storage.as_mut_ptr();
let cparams = ZSTD_compressionParameters {
windowLog: 20,
chainLog: 19,
hashLog: 18,
searchLog: 5,
minMatch: 4,
targetLength: 16,
strategy: 3,
};
let enable_dedicated_dict_search = 1;
let use_row_match_finder = 2;
let state = create_cdict_advanced_test_state(
&mut probe,
cctx_params,
&cparams,
&enable_dedicated_dict_search,
&use_row_match_finder,
);
let dict = [1u8, 2, 3, 4];
let result = unsafe {
ZSTD_rust_createCDictAdvanced(
&state,
dict.as_ptr().cast(),
dict.len(),
ZSTD_DLM_BY_REF,
ZSTD_DCT_RAW_CONTENT,
)
};
assert_eq!(result, probe.cdict_result);
assert_eq!(
probe.events,
["validate", "workspace_size", "allocate", "create", "init"]
);
assert_eq!(probe.workspace_size_dict_size, dict.len());
assert_eq!(probe.workspace_size_dict_load_method, ZSTD_DLM_BY_REF);
assert_eq!(probe.workspace_size_cparams, &cparams);
assert_eq!(
probe.workspace_size_use_row_match_finder,
use_row_match_finder
);
assert_eq!(
probe.workspace_size_enable_dedicated_dict_search,
enable_dedicated_dict_search
);
assert_eq!(probe.allocated_workspace_size, 123);
assert_eq!(probe.create_workspace, probe.allocated_workspace);
assert_eq!(probe.create_workspace_size, 123);
assert_eq!(probe.create_dict_size, dict.len());
assert_eq!(probe.create_dict_load_method, ZSTD_DLM_BY_REF);
assert_eq!(probe.cparams, &cparams);
assert_eq!(probe.use_row_match_finder, use_row_match_finder);
assert_eq!(
probe.enable_dedicated_dict_search,
enable_dedicated_dict_search
);
assert_eq!(probe.init_cdict, probe.cdict_result);
assert_eq!(probe.init_dict, dict.as_ptr().cast());
assert_eq!(probe.init_dict_size, dict.len());
assert_eq!(probe.init_dict_load_method, ZSTD_DLM_BY_REF);
assert_eq!(probe.init_dict_content_type, ZSTD_DCT_RAW_CONTENT);
assert_eq!(probe.init_cctx_params, cctx_params.cast_const());
assert!(probe.free_workspace.is_null());
assert!(probe.free_cdict.is_null());
}
#[test]
fn create_cdict_advanced_rejects_invalid_custom_memory_before_allocation() {
let mut probe = CreateCDictAdvancedProbe::default();
let mut params_storage = create_cdict_advanced_test_params();
let cctx_params = params_storage.as_mut_ptr();
let cparams = ZSTD_compressionParameters::default();
let enable_dedicated_dict_search = 0;
let use_row_match_finder = 0;
let state = create_cdict_advanced_test_state(
&mut probe,
cctx_params,
&cparams,
&enable_dedicated_dict_search,
&use_row_match_finder,
);
let result = unsafe {
ZSTD_rust_createCDictAdvanced(&state, ptr::null(), 0, ZSTD_DLM_BY_REF, ZSTD_DCT_AUTO)
};
assert!(result.is_null());
assert_eq!(probe.events, ["validate"]);
assert!(probe.create_workspace.is_null());
assert!(probe.free_workspace.is_null());
assert!(probe.free_cdict.is_null());
}
#[test]
fn create_cdict_advanced_stops_after_allocation_failure() {
let mut probe = CreateCDictAdvancedProbe {
custom_mem_valid: 1,
workspace_size_result: 321,
..Default::default()
};
let mut params_storage = create_cdict_advanced_test_params();
let cctx_params = params_storage.as_mut_ptr();
let cparams = ZSTD_compressionParameters::default();
let enable_dedicated_dict_search = 0;
let use_row_match_finder = 0;
let state = create_cdict_advanced_test_state(
&mut probe,
cctx_params,
&cparams,
&enable_dedicated_dict_search,
&use_row_match_finder,
);
let result = unsafe {
ZSTD_rust_createCDictAdvanced(&state, ptr::null(), 0, ZSTD_DLM_BY_REF, ZSTD_DCT_AUTO)
};
assert!(result.is_null());
assert_eq!(probe.events, ["validate", "workspace_size", "allocate"]);
assert_eq!(probe.allocated_workspace_size, 321);
assert!(probe.create_workspace.is_null());
assert!(probe.free_workspace.is_null());
assert!(probe.free_cdict.is_null());
}
#[test]
fn create_cdict_advanced_frees_workspace_after_creation_failure() {
let workspace = ptr::dangling_mut::<c_void>();
let mut probe = CreateCDictAdvancedProbe {
custom_mem_valid: 1,
workspace_size_result: 321,
allocated_workspace: workspace,
..Default::default()
};
let mut params_storage = create_cdict_advanced_test_params();
let cctx_params = params_storage.as_mut_ptr();
let cparams = ZSTD_compressionParameters::default();
let enable_dedicated_dict_search = 0;
let use_row_match_finder = 0;
let state = create_cdict_advanced_test_state(
&mut probe,
cctx_params,
&cparams,
&enable_dedicated_dict_search,
&use_row_match_finder,
);
let result = unsafe {
ZSTD_rust_createCDictAdvanced(&state, ptr::null(), 0, ZSTD_DLM_BY_REF, ZSTD_DCT_AUTO)
};
assert!(result.is_null());
assert_eq!(
probe.events,
[
"validate",
"workspace_size",
"allocate",
"create",
"free_workspace"
]
);
assert_eq!(probe.create_workspace, workspace);
assert_eq!(probe.create_workspace_size, 321);
assert_eq!(probe.create_dict_size, 0);
assert_eq!(probe.free_workspace, workspace);
assert!(probe.free_cdict.is_null());
}
#[test]
fn create_cdict_advanced_frees_after_initialization_failure() {
let cdict = ptr::dangling_mut::<c_void>();
let workspace = ptr::dangling_mut::<c_void>();
let mut probe = CreateCDictAdvancedProbe {
custom_mem_valid: 1,
workspace_size_result: 321,
allocated_workspace: workspace,
cdict_result: cdict,
init_result: ERROR(ZstdErrorCode::MemoryAllocation),
..Default::default()
};
let mut params_storage = create_cdict_advanced_test_params();
let cctx_params = params_storage.as_mut_ptr();
let cparams = ZSTD_compressionParameters::default();
let enable_dedicated_dict_search = 0;
let use_row_match_finder = 0;
let state = create_cdict_advanced_test_state(
&mut probe,
cctx_params,
&cparams,
&enable_dedicated_dict_search,
&use_row_match_finder,
);
let result = unsafe {
ZSTD_rust_createCDictAdvanced(&state, ptr::null(), 0, ZSTD_DLM_BY_REF, ZSTD_DCT_AUTO)
};
assert!(result.is_null());
assert_eq!(
probe.events,
[
"validate",
"workspace_size",
"allocate",
"create",
"init",
"free"
]
);
assert_eq!(probe.create_workspace, workspace);
assert_eq!(probe.create_workspace_size, 321);
assert_eq!(probe.create_dict_size, 0);
assert!(probe.free_workspace.is_null());
assert_eq!(probe.free_cdict, cdict);
}
#[derive(Default)]
struct FreeCDictProbe {
events: Vec<&'static str>,
}
unsafe extern "C" fn free_cdict_test_workspace(context: *mut c_void) {
let probe = unsafe { &mut *context.cast::<FreeCDictProbe>() };
probe.events.push("workspace");
}
unsafe extern "C" fn free_cdict_test_object(context: *mut c_void) {
let probe = unsafe { &mut *context.cast::<FreeCDictProbe>() };
probe.events.push("object");
}
fn free_cdict_test_state(
probe: &mut FreeCDictProbe,
cdict_in_workspace: c_int,
) -> ZSTD_rust_freeCDictState {
ZSTD_rust_freeCDictState {
callback_context: (probe as *mut FreeCDictProbe).cast(),
cdict_in_workspace,
free_workspace: free_cdict_test_workspace,
free_object: free_cdict_test_object,
}
}
#[test]
fn free_cdict_frees_workspace_before_an_external_object() {
let mut probe = FreeCDictProbe::default();
let state = free_cdict_test_state(&mut probe, 0);
let result = unsafe { ZSTD_rust_freeCDict(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["workspace", "object"]);
}
#[test]
fn free_cdict_skips_the_external_object_when_embedded_in_workspace() {
let mut probe = FreeCDictProbe::default();
let state = free_cdict_test_state(&mut probe, 1);
let result = unsafe { ZSTD_rust_freeCDict(&state) };
assert_eq!(result, 0);
assert_eq!(probe.events, ["workspace"]);
}
#[test]
fn free_cdict_accepts_a_null_context() {
let state = ZSTD_rust_freeCDictState {
callback_context: ptr::null_mut(),
cdict_in_workspace: 0,
free_workspace: free_cdict_test_workspace,
free_object: free_cdict_test_object,
};
let result = unsafe { ZSTD_rust_freeCDict(&state) };
assert_eq!(result, 0);
}
#[derive(Default)]
struct InitStaticCDictProbe {
events: Vec<&'static str>,
result: *mut c_void,
}
unsafe extern "C" fn init_static_cdict_test_init(context: *mut c_void) -> *mut c_void {
let probe = unsafe { &mut *context.cast::<InitStaticCDictProbe>() };
probe.events.push("init");
probe.result
}
fn init_static_cdict_test_state(
probe: &mut InitStaticCDictProbe,
workspace: *mut c_void,
workspace_size: usize,
needed_size: usize,
) -> ZSTD_rust_initStaticCDictState {
ZSTD_rust_initStaticCDictState {
callback_context: (probe as *mut InitStaticCDictProbe).cast(),
workspace,
workspace_size,
needed_size,
init: init_static_cdict_test_init,
}
}
#[test]
fn init_static_cdict_rejects_an_unaligned_workspace_before_callback() {
let mut probe = InitStaticCDictProbe {
result: ptr::dangling_mut(),
..Default::default()
};
let mut workspace = [0usize; 2];
let unaligned_workspace = workspace.as_mut_ptr().cast::<u8>().wrapping_add(1).cast();
let state = init_static_cdict_test_state(&mut probe, unaligned_workspace, usize::MAX, 0);
let result = unsafe { ZSTD_rust_initStaticCDict(&state) };
assert!(result.is_null());
assert!(probe.events.is_empty());
}
#[test]
fn init_static_cdict_rejects_insufficient_workspace_before_callback() {
let mut probe = InitStaticCDictProbe {
result: ptr::dangling_mut(),
..Default::default()
};
let mut workspace = [0usize; 1];
let state = init_static_cdict_test_state(
&mut probe,
workspace.as_mut_ptr().cast(),
size_of_val(&workspace),
size_of_val(&workspace) + 1,
);
let result = unsafe { ZSTD_rust_initStaticCDict(&state) };
assert!(result.is_null());
assert!(probe.events.is_empty());
}
#[test]
fn init_static_cdict_calls_the_initializer_after_workspace_validation() {
let mut probe = InitStaticCDictProbe {
result: ptr::dangling_mut(),
..Default::default()
};
let mut workspace = [0usize; 1];
let state = init_static_cdict_test_state(
&mut probe,
workspace.as_mut_ptr().cast(),
size_of_val(&workspace),
size_of_val(&workspace),
);
let result = unsafe { ZSTD_rust_initStaticCDict(&state) };
assert_eq!(result, probe.result);
assert_eq!(probe.events, ["init"]);
}
#[test]
fn init_static_cdict_rejects_a_null_workspace() {
let mut probe = InitStaticCDictProbe {
result: ptr::dangling_mut(),
..Default::default()
};
let state = init_static_cdict_test_state(&mut probe, ptr::null_mut(), usize::MAX, 0);
let result = unsafe { ZSTD_rust_initStaticCDict(&state) };
assert!(result.is_null());
assert!(probe.events.is_empty());
}
#[derive(Default)]
struct CompressBeginUsingDictProbe {
events: Vec<&'static str>,
params: ZSTD_parameters,
compression_level: c_int,
dict: *const c_void,
dict_size: usize,
cctx_params: *const c_void,
pledged_src_size: u64,
begin_result: usize,
}
unsafe extern "C" fn compress_begin_using_dict_test_init(
context: *mut c_void,
params: *const ZSTD_parameters,
compression_level: c_int,
) {
let probe = unsafe { &mut *context.cast::<CompressBeginUsingDictProbe>() };
probe.events.push("init");
probe.params = unsafe { *params };
probe.compression_level = compression_level;
}
unsafe extern "C" fn compress_begin_using_dict_test_begin(
context: *mut c_void,
dict: *const c_void,
dict_size: usize,
cctx_params: *const c_void,
pledged_src_size: u64,
) -> usize {
let probe = unsafe { &mut *context.cast::<CompressBeginUsingDictProbe>() };
probe.events.push("begin");
probe.dict = dict;
probe.dict_size = dict_size;
probe.cctx_params = cctx_params;
probe.pledged_src_size = pledged_src_size;
probe.begin_result
}
fn compress_begin_using_dict_test_state(
probe: &mut CompressBeginUsingDictProbe,
exclusion_mask: &c_uint,
) -> ZSTD_rust_compressBeginUsingDictState {
ZSTD_rust_compressBeginUsingDictState {
cctx: (probe as *mut CompressBeginUsingDictProbe).cast(),
cctx_params: (probe as *mut CompressBeginUsingDictProbe).cast(),
exclusion_mask,
init_params: compress_begin_using_dict_test_init,
begin: compress_begin_using_dict_test_begin,
}
}
#[test]
fn compress_begin_using_dict_selects_unknown_source_params_and_default_level() {
let mut probe = CompressBeginUsingDictProbe::default();
let exclusion_mask = 0;
let state = compress_begin_using_dict_test_state(&mut probe, &exclusion_mask);
let dict = [1u8, 2, 3];
let result = unsafe {
ZSTD_rust_compressBeginUsingDict(&state, dict.as_ptr().cast(), dict.len(), 0)
};
assert_eq!(result, 0);
assert_eq!(probe.events, ["init", "begin"]);
assert_eq!(
probe.params,
ZSTD_rust_params_getParamsInternal(
0,
ZSTD_CONTENTSIZE_UNKNOWN,
dict.len(),
ZSTD_RUST_CPM_NO_ATTACH_DICT,
exclusion_mask,
)
);
assert_eq!(probe.compression_level, ZSTD_rust_params_defaultCLevel());
assert_eq!(probe.dict, dict.as_ptr().cast());
assert_eq!(probe.dict_size, dict.len());
assert_eq!(
probe.cctx_params,
(&probe as *const CompressBeginUsingDictProbe).cast()
);
assert_eq!(probe.pledged_src_size, ZSTD_CONTENTSIZE_UNKNOWN);
}
#[test]
fn compress_begin_using_dict_propagates_begin_errors_after_initialization() {
let mut probe = CompressBeginUsingDictProbe {
begin_result: ERROR(ZstdErrorCode::StageWrong),
..Default::default()
};
let exclusion_mask = 0;
let state = compress_begin_using_dict_test_state(&mut probe, &exclusion_mask);
let result = unsafe { ZSTD_rust_compressBeginUsingDict(&state, ptr::null(), 0, 3) };
assert_eq!(result, probe.begin_result);
assert_eq!(probe.events, ["init", "begin"]);
}
fn assert_dictionary_corrupted(result: usize) {
assert!(ERR_isError(result));
assert_eq!(
ERR_getErrorCode(result),
ZstdErrorCode::DictionaryCorrupted as i32
);
}
unsafe fn write_fse_header(
dictionary: &mut [u8],
offset: &mut usize,
max_symbol_value: usize,
table_log: u32,
zero_weight: bool,
) {
let mut counts = vec![1u32; max_symbol_value + 1];
if zero_weight {
counts[0] = 0;
}
let total = counts.iter().map(|&count| count as usize).sum();
let mut normalized = vec![0i16; max_symbol_value + 1];
let normalized_log = FSE_normalizeCount(
normalized.as_mut_ptr(),
table_log,
counts.as_ptr(),
total,
max_symbol_value as c_uint,
1,
);
assert!(!ERR_isError(normalized_log));
let header_size = FSE_writeNCount(
dictionary.as_mut_ptr().add(*offset).cast(),
dictionary.len() - *offset,
normalized.as_ptr(),
max_symbol_value as c_uint,
normalized_log as c_uint,
);
assert!(!ERR_isError(header_size));
*offset += header_size;
}
fn make_dictionary(zero_weight: ZeroWeight) -> Vec<u8> {
let mut dictionary = vec![0u8; 2048];
unsafe {
MEM_writeLE32(dictionary.as_mut_ptr().cast(), 0xEC30_A437);
MEM_writeLE32(dictionary.as_mut_ptr().add(4).cast(), 1234);
}
let mut offset = DICTIONARY_ID_AND_MAGIC_SIZE;
let mut huf_counts = std::array::from_fn::<_, 256, _>(|symbol| (symbol + 1) as u32);
if matches!(zero_weight, ZeroWeight::Huffman) {
huf_counts[0] = 0;
}
let mut huf_table = [0usize; 257];
let mut workspace = [0u32; HUF_WORKSPACE_SIZE / size_of::<u32>()];
let huff_log = unsafe {
HUF_buildCTable_wksp(
huf_table.as_mut_ptr(),
huf_counts.as_ptr(),
255,
11,
workspace.as_mut_ptr().cast(),
size_of::<[u32; HUF_WORKSPACE_SIZE / size_of::<u32>()]>(),
)
};
assert!(!ERR_isError(huff_log));
let huf_header_size = unsafe {
HUF_writeCTable_wksp(
dictionary.as_mut_ptr().add(offset).cast(),
dictionary.len() - offset,
huf_table.as_ptr(),
255,
huff_log as c_uint,
workspace.as_mut_ptr().cast(),
size_of::<[u32; HUF_WORKSPACE_SIZE / size_of::<u32>()]>(),
)
};
assert!(!ERR_isError(huf_header_size));
offset += huf_header_size;
unsafe {
write_fse_header(
&mut dictionary,
&mut offset,
MAX_OFF,
OFF_FSE_LOG as u32,
matches!(zero_weight, ZeroWeight::Offset),
);
write_fse_header(
&mut dictionary,
&mut offset,
MAX_ML,
ML_FSE_LOG as u32,
matches!(zero_weight, ZeroWeight::MatchLength),
);
write_fse_header(
&mut dictionary,
&mut offset,
MAX_LL,
LL_FSE_LOG as u32,
matches!(zero_weight, ZeroWeight::LiteralLength),
);
MEM_writeLE32(dictionary.as_mut_ptr().add(offset).cast(), 1);
MEM_writeLE32(dictionary.as_mut_ptr().add(offset + 4).cast(), 4);
MEM_writeLE32(dictionary.as_mut_ptr().add(offset + 8).cast(), 8);
}
offset += REPCODE_SECTION_SIZE;
dictionary[offset..offset + DICT_CONTENT_SIZE].fill(0xA5);
offset += DICT_CONTENT_SIZE;
dictionary.truncate(offset);
dictionary
}
fn load(dictionary: &[u8]) -> (usize, ZSTD_compressedBlockState_t) {
let mut state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut workspace = [0u32; HUF_WORKSPACE_SIZE / size_of::<u32>()];
let result = unsafe {
ZSTD_rust_loadCEntropy(
&mut state,
workspace.as_mut_ptr().cast(),
dictionary.as_ptr().cast(),
dictionary.len(),
)
};
(result, state)
}
#[test]
fn valid_dictionary_loads_entropy_and_marks_repeat_tables_valid() {
let dictionary = make_dictionary(ZeroWeight::None);
let (header_size, state) = load(&dictionary);
assert_eq!(header_size, dictionary.len() - DICT_CONTENT_SIZE);
assert_eq!(state.rep, [1, 4, 8]);
assert_eq!(state.entropy.huf.repeatMode, HUF_REPEAT_VALID);
assert_eq!(state.entropy.fse.offcode_repeatMode, FSE_REPEAT_VALID);
assert_eq!(state.entropy.fse.matchlength_repeatMode, FSE_REPEAT_VALID);
assert_eq!(state.entropy.fse.litlength_repeatMode, FSE_REPEAT_VALID);
}
#[test]
fn zero_weights_select_check_repeat_modes() {
for zero_weight in [
ZeroWeight::Huffman,
ZeroWeight::Offset,
ZeroWeight::MatchLength,
ZeroWeight::LiteralLength,
] {
let dictionary = make_dictionary(zero_weight);
let (header_size, state) = load(&dictionary);
assert_eq!(header_size, dictionary.len() - DICT_CONTENT_SIZE);
match zero_weight {
ZeroWeight::Huffman => {
assert_eq!(state.entropy.huf.repeatMode, HUF_REPEAT_CHECK)
}
ZeroWeight::Offset => {
assert_eq!(state.entropy.fse.offcode_repeatMode, FSE_REPEAT_CHECK)
}
ZeroWeight::MatchLength => {
assert_eq!(state.entropy.fse.matchlength_repeatMode, FSE_REPEAT_CHECK)
}
ZeroWeight::LiteralLength => {
assert_eq!(state.entropy.fse.litlength_repeatMode, FSE_REPEAT_CHECK)
}
ZeroWeight::None => unreachable!(),
}
}
}
#[test]
fn truncated_entropy_headers_are_rejected() {
let dictionary = make_dictionary(ZeroWeight::None);
let (header_size, _) = load(&dictionary);
for truncated_size in [0, 7, 8, header_size - 1] {
let truncated = &dictionary[..truncated_size];
let (result, _) = load(truncated);
assert_dictionary_corrupted(result);
}
}
#[test]
fn corrupt_repcode_header_is_rejected() {
let mut dictionary = make_dictionary(ZeroWeight::None);
let (header_size, _) = load(&dictionary);
let repcode_offset = header_size - REPCODE_SECTION_SIZE;
dictionary[repcode_offset..repcode_offset + 4].fill(0);
let (result, _) = load(&dictionary);
assert_dictionary_corrupted(result);
}
#[test]
fn short_or_missing_dictionary_preserves_state_and_full_mode_error() {
let mut state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
state.rep = [9, 10, 11];
let mut probe = DictionaryLoadProbe::default();
let result = unsafe {
dispatch_for_test(
&mut state,
&mut probe,
ptr::null(),
0,
ZSTD_DCT_AUTO,
ptr::null_mut(),
0,
ptr::null_mut(),
)
};
assert_eq!(result, 0);
assert_eq!(state.rep, [9, 10, 11]);
let short_dictionary = [0u8; DICTIONARY_ID_AND_MAGIC_SIZE - 1];
let result = unsafe {
dispatch_for_test(
&mut state,
&mut probe,
short_dictionary.as_ptr().cast(),
short_dictionary.len(),
ZSTD_DCT_FULL_DICT,
ptr::null_mut(),
0,
ptr::null_mut(),
)
};
assert!(ERR_isError(result));
assert_eq!(
ERR_getErrorCode(result),
ZstdErrorCode::DictionaryWrong as i32
);
assert_eq!(state.rep, [9, 10, 11]);
state.rep = [12, 13, 14];
let wrong_magic_dictionary = [0u8; DICTIONARY_ID_AND_MAGIC_SIZE];
let result = unsafe {
dispatch_for_test(
&mut state,
&mut probe,
wrong_magic_dictionary.as_ptr().cast(),
wrong_magic_dictionary.len(),
ZSTD_DCT_FULL_DICT,
ptr::null_mut(),
0,
ptr::null_mut(),
)
};
assert!(ERR_isError(result));
assert_eq!(
ERR_getErrorCode(result),
ZstdErrorCode::DictionaryWrong as i32
);
assert_eq!(state.rep, [1, 4, 8]);
assert_eq!(probe.calls, 0);
}
#[test]
fn raw_and_auto_dictionary_modes_use_the_content_callback() {
let mut state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut probe = DictionaryLoadProbe::default();
let raw_dictionary = [0xEC, 0x30, 0xA4, 0x37, 0, 0, 0, 0];
let result = unsafe {
dispatch_for_test(
&mut state,
&mut probe,
raw_dictionary.as_ptr().cast(),
raw_dictionary.len(),
ZSTD_DCT_RAW_CONTENT,
ptr::null_mut(),
0,
ptr::dangling_mut::<c_void>(),
)
};
assert_eq!(result, 37);
assert_eq!(probe.calls, 1);
assert_eq!(probe.dict, raw_dictionary.as_ptr().cast());
assert_eq!(probe.dict_size, raw_dictionary.len());
assert_eq!(probe.ldm_state, ptr::dangling_mut::<c_void>());
let auto_dictionary = [0u8; DICTIONARY_ID_AND_MAGIC_SIZE];
let result = unsafe {
dispatch_for_test(
&mut state,
&mut probe,
auto_dictionary.as_ptr().cast(),
auto_dictionary.len(),
ZSTD_DCT_AUTO,
ptr::null_mut(),
0,
ptr::dangling_mut::<c_void>(),
)
};
assert_eq!(result, 37);
assert_eq!(probe.calls, 2);
assert_eq!(probe.dict, auto_dictionary.as_ptr().cast());
assert_eq!(probe.dict_size, auto_dictionary.len());
}
#[test]
fn full_dictionary_loads_entropy_and_returns_or_suppresses_id() {
let dictionary = make_dictionary(ZeroWeight::None);
let mut workspace = [0u32; HUF_WORKSPACE_SIZE / size_of::<u32>()];
let mut state =
unsafe { MaybeUninit::<ZSTD_compressedBlockState_t>::zeroed().assume_init() };
let mut probe = DictionaryLoadProbe::default();
let result = unsafe {
dispatch_for_test(
&mut state,
&mut probe,
dictionary.as_ptr().cast(),
dictionary.len(),
ZSTD_DCT_FULL_DICT,
workspace.as_mut_ptr().cast(),
0,
ptr::dangling_mut::<c_void>(),
)
};
assert_eq!(result, 1234);
assert_eq!(probe.calls, 1);
assert_eq!(probe.ldm_state, ptr::null_mut());
assert_eq!(probe.dict_size, DICT_CONTENT_SIZE);
let result = unsafe {
dispatch_for_test(
&mut state,
&mut probe,
dictionary.as_ptr().cast(),
dictionary.len(),
ZSTD_DCT_FULL_DICT,
workspace.as_mut_ptr().cast(),
1,
ptr::dangling_mut::<c_void>(),
)
};
assert_eq!(result, 0);
assert_eq!(probe.calls, 2);
}
#[test]
fn dict_n_count_repeat_requires_coverage_and_nonzero_weights() {
let mut normalized = [1i16; 4];
assert_eq!(dict_n_count_repeat(&normalized, 3, 3), FSE_REPEAT_VALID);
normalized[2] = 0;
assert_eq!(dict_n_count_repeat(&normalized, 3, 3), FSE_REPEAT_CHECK);
assert_eq!(dict_n_count_repeat(&normalized, 2, 2), FSE_REPEAT_CHECK);
}
}