Export ZSTD_useTargetCBlockSize and ZSTD_blockSplitterEnabled directly from Rust under their existing caller symbols. Remove the redundant C forwarding wrappers while preserving diagnostics, ABI signatures, and parameter layout. Test Plan: - worker capped format, C syntax, and diff checks - parent capped root clippy and native build - parent capped upstream make -j1 -C tests test - parent capped CLI clippy and tests
2083 lines
72 KiB
Rust
2083 lines
72 KiB
Rust
#![allow(non_camel_case_types)]
|
|
#![allow(non_snake_case)]
|
|
#![allow(clippy::missing_safety_doc)]
|
|
#![allow(clippy::too_many_arguments)]
|
|
|
|
//! Rust implementation of the narrow `ZSTD_CCtx_params` object API.
|
|
//!
|
|
//! Advanced initialization, bounds calculation, and context policy are split
|
|
//! narrowly between this module and `zstd_compress.c`. This module owns the
|
|
//! parameter object's storage lifecycle, initialization, parameter set/get,
|
|
//! sequence-producer registration, and advanced initialization's small
|
|
//! default-resolution policy.
|
|
|
|
use crate::errors::{ZstdErrorCode, ERROR};
|
|
use crate::zstd_compress_params::{
|
|
ZSTD_bounds, ZSTD_compressionParameters, ZSTD_frameParameters, ZSTD_parameters,
|
|
ZSTD_rust_params_checkCParams, ZSTD_rust_params_dedicatedDictSearchIsSupported,
|
|
ZSTD_rust_params_dedicatedDictSearch_getCParams, ZSTD_rust_params_getBounds,
|
|
ZSTD_rust_params_getCParamsFromCCtxParams, ZSTD_rust_params_getCParamsInternal,
|
|
ZSTD_rust_params_overrideCParams, ZSTD_rust_params_resolveBlockSplitterMode,
|
|
ZSTD_rust_params_resolveEnableLdm, ZSTD_rust_params_resolveExternalRepcodeSearch,
|
|
ZSTD_rust_params_resolveExternalSequenceValidation, ZSTD_rust_params_resolveMaxBlockSize,
|
|
ZSTD_rust_params_resolveRowMatchFinderMode, ZSTD_CONTENTSIZE_UNKNOWN,
|
|
ZSTD_RUST_CPM_CREATE_CDICT, ZSTD_RUST_PS_AUTO, ZSTD_RUST_PS_DISABLE, ZSTD_RUST_PS_ENABLE,
|
|
};
|
|
use std::mem::size_of;
|
|
use std::os::raw::{c_int, c_void};
|
|
use std::ptr;
|
|
|
|
type ZstdAllocFunction = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void;
|
|
type ZstdFreeFunction = unsafe extern "C" fn(*mut c_void, *mut c_void);
|
|
|
|
/// ABI-compatible representation of `ZSTD_customMem`.
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy)]
|
|
pub struct ZSTD_customMem {
|
|
customAlloc: Option<ZstdAllocFunction>,
|
|
customFree: Option<ZstdFreeFunction>,
|
|
opaque: *mut c_void,
|
|
}
|
|
|
|
/// ABI-compatible private LDM parameter object.
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Default)]
|
|
struct ldmParams_t {
|
|
enableLdm: c_int,
|
|
hashLog: u32,
|
|
bucketSizeLog: u32,
|
|
minMatchLength: u32,
|
|
hashRateLog: u32,
|
|
windowLog: u32,
|
|
}
|
|
|
|
/// ABI-compatible callback parameter type from `zstd.h`.
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Default)]
|
|
pub struct ZSTD_Sequence {
|
|
offset: u32,
|
|
litLength: u32,
|
|
matchLength: u32,
|
|
rep: u32,
|
|
}
|
|
|
|
pub type ZSTD_sequenceProducer_F = unsafe extern "C" fn(
|
|
sequenceProducerState: *mut c_void,
|
|
outSeqs: *mut ZSTD_Sequence,
|
|
outSeqsCapacity: usize,
|
|
src: *const c_void,
|
|
srcSize: usize,
|
|
dict: *const c_void,
|
|
dictSize: usize,
|
|
compressionLevel: c_int,
|
|
windowSize: usize,
|
|
) -> usize;
|
|
|
|
/// Layout mirror for `struct ZSTD_CCtx_params_s`.
|
|
///
|
|
/// The type is public only because exported Rust ABI functions cannot expose
|
|
/// a private type. Its fields and all helper types remain private to this
|
|
/// module; C remains the owner of the actual parameter object contract.
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy)]
|
|
pub struct ZSTD_CCtx_params {
|
|
format: c_int,
|
|
cParams: ZSTD_compressionParameters,
|
|
fParams: ZSTD_frameParameters,
|
|
|
|
compressionLevel: c_int,
|
|
forceWindow: c_int,
|
|
targetCBlockSize: usize,
|
|
srcSizeHint: c_int,
|
|
|
|
attachDictPref: c_int,
|
|
literalCompressionMode: c_int,
|
|
|
|
nbWorkers: c_int,
|
|
jobSize: usize,
|
|
overlapLog: c_int,
|
|
rsyncable: c_int,
|
|
|
|
ldmParams: ldmParams_t,
|
|
|
|
enableDedicatedDictSearch: c_int,
|
|
|
|
inBufferMode: c_int,
|
|
outBufferMode: c_int,
|
|
|
|
blockDelimiters: c_int,
|
|
validateSequences: c_int,
|
|
|
|
postBlockSplitter: c_int,
|
|
preBlockSplitter_level: c_int,
|
|
|
|
maxBlockSize: usize,
|
|
|
|
useRowMatchFinder: c_int,
|
|
deterministicRefPrefix: c_int,
|
|
|
|
customMem: ZSTD_customMem,
|
|
|
|
prefetchCDictTables: c_int,
|
|
enableMatchFinderFallback: c_int,
|
|
|
|
extSeqProdState: *mut c_void,
|
|
extSeqProdFunc: Option<ZSTD_sequenceProducer_F>,
|
|
|
|
searchForExternalRepcodes: c_int,
|
|
}
|
|
|
|
const DEFAULT_CLEVEL: c_int = 3;
|
|
const NO_CLEVEL: c_int = 0;
|
|
const PS_AUTO: c_int = 0;
|
|
const PS_DISABLE: c_int = 2;
|
|
|
|
/* Private strategy values used by the existing C policy helpers. */
|
|
const STRATEGY_GREEDY: c_int = 3;
|
|
const STRATEGY_LAZY2: c_int = 5;
|
|
const STRATEGY_BTOPT: c_int = 7;
|
|
|
|
const BLOCKSIZE_MAX: usize = 1 << 17;
|
|
const BLOCKSIZE_MAX_MIN: c_int = 1 << 10;
|
|
const TARGET_C_BLOCK_SIZE_MIN: c_int = 1340;
|
|
|
|
const C_COMPRESSION_LEVEL: c_int = 100;
|
|
const C_WINDOW_LOG: c_int = 101;
|
|
const C_HASH_LOG: c_int = 102;
|
|
const C_CHAIN_LOG: c_int = 103;
|
|
const C_SEARCH_LOG: c_int = 104;
|
|
const C_MIN_MATCH: c_int = 105;
|
|
const C_TARGET_LENGTH: c_int = 106;
|
|
const C_STRATEGY: c_int = 107;
|
|
const C_TARGET_C_BLOCK_SIZE: c_int = 130;
|
|
const C_ENABLE_LDM: c_int = 160;
|
|
const C_LDM_HASH_LOG: c_int = 161;
|
|
const C_LDM_MIN_MATCH: c_int = 162;
|
|
const C_LDM_BUCKET_SIZE_LOG: c_int = 163;
|
|
const C_LDM_HASH_RATE_LOG: c_int = 164;
|
|
const C_CONTENT_SIZE_FLAG: c_int = 200;
|
|
const C_CHECKSUM_FLAG: c_int = 201;
|
|
const C_DICT_ID_FLAG: c_int = 202;
|
|
const C_NB_WORKERS: c_int = 400;
|
|
const C_JOB_SIZE: c_int = 401;
|
|
const C_OVERLAP_LOG: c_int = 402;
|
|
|
|
const C_RSYNCABLE: c_int = 500;
|
|
const C_FORMAT: c_int = 10;
|
|
const C_FORCE_MAX_WINDOW: c_int = 1000;
|
|
const C_FORCE_ATTACH_DICT: c_int = 1001;
|
|
const C_LITERAL_COMPRESSION_MODE: c_int = 1002;
|
|
const C_SRC_SIZE_HINT: c_int = 1004;
|
|
const C_ENABLE_DEDICATED_DICT_SEARCH: c_int = 1005;
|
|
const C_STABLE_IN_BUFFER: c_int = 1006;
|
|
const C_STABLE_OUT_BUFFER: c_int = 1007;
|
|
const C_BLOCK_DELIMITERS: c_int = 1008;
|
|
const C_VALIDATE_SEQUENCES: c_int = 1009;
|
|
const C_SPLIT_AFTER_SEQUENCES: c_int = 1010;
|
|
const C_USE_ROW_MATCH_FINDER: c_int = 1011;
|
|
const C_DETERMINISTIC_REF_PREFIX: c_int = 1012;
|
|
const C_PREFETCH_CDICT_TABLES: c_int = 1013;
|
|
const C_ENABLE_SEQ_PRODUCER_FALLBACK: c_int = 1014;
|
|
const C_MAX_BLOCK_SIZE: c_int = 1015;
|
|
const C_REPCODE_RESOLUTION: c_int = 1016;
|
|
const C_BLOCK_SPLITTER_LEVEL: c_int = 1017;
|
|
|
|
#[inline]
|
|
fn is_update_authorized(param: c_int) -> bool {
|
|
matches!(
|
|
param,
|
|
C_COMPRESSION_LEVEL
|
|
| C_HASH_LOG
|
|
| C_CHAIN_LOG
|
|
| C_SEARCH_LOG
|
|
| C_MIN_MATCH
|
|
| C_TARGET_LENGTH
|
|
| C_STRATEGY
|
|
| C_BLOCK_SPLITTER_LEVEL
|
|
)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn ZSTD_rust_isUpdateAuthorized(param: c_int) -> c_int {
|
|
is_update_authorized(param) as c_int
|
|
}
|
|
|
|
#[cfg(not(test))]
|
|
unsafe extern "C" {
|
|
fn ZSTD_rust_cctx_params_is_multithreaded() -> c_int;
|
|
fn ZSTD_rust_cctx_params_nb_workers_max() -> c_int;
|
|
fn ZSTD_rust_cctx_params_job_size_min() -> c_int;
|
|
fn ZSTD_rust_cctx_params_job_size_max() -> c_int;
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct BoundsConfig {
|
|
multithreaded: bool,
|
|
nb_workers_max: c_int,
|
|
job_size_max: c_int,
|
|
}
|
|
|
|
#[cfg(not(test))]
|
|
unsafe fn current_bounds_config() -> BoundsConfig {
|
|
BoundsConfig {
|
|
multithreaded: unsafe { ZSTD_rust_cctx_params_is_multithreaded() != 0 },
|
|
nb_workers_max: unsafe { ZSTD_rust_cctx_params_nb_workers_max() },
|
|
job_size_max: unsafe { ZSTD_rust_cctx_params_job_size_max() },
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
unsafe fn current_bounds_config() -> BoundsConfig {
|
|
BoundsConfig {
|
|
multithreaded: false,
|
|
nb_workers_max: 0,
|
|
job_size_max: 0,
|
|
}
|
|
}
|
|
|
|
fn bounds_for(param: c_int, config: BoundsConfig) -> ZSTD_bounds {
|
|
let (lower, upper) = match param {
|
|
C_COMPRESSION_LEVEL | C_WINDOW_LOG | C_HASH_LOG | C_CHAIN_LOG | C_SEARCH_LOG
|
|
| C_MIN_MATCH | C_TARGET_LENGTH | C_STRATEGY => return ZSTD_rust_params_getBounds(param),
|
|
C_FORMAT => (0, 1),
|
|
C_FORCE_ATTACH_DICT => (0, 3),
|
|
C_LITERAL_COMPRESSION_MODE => (0, 2),
|
|
C_CONTENT_SIZE_FLAG | C_CHECKSUM_FLAG | C_DICT_ID_FLAG => (0, 1),
|
|
C_NB_WORKERS => (
|
|
0,
|
|
if config.multithreaded {
|
|
config.nb_workers_max
|
|
} else {
|
|
0
|
|
},
|
|
),
|
|
C_JOB_SIZE => (
|
|
0,
|
|
if config.multithreaded {
|
|
config.job_size_max
|
|
} else {
|
|
0
|
|
},
|
|
),
|
|
C_OVERLAP_LOG => {
|
|
if config.multithreaded {
|
|
(0, 9)
|
|
} else {
|
|
(0, 0)
|
|
}
|
|
}
|
|
C_ENABLE_DEDICATED_DICT_SEARCH => (0, 1),
|
|
C_ENABLE_LDM
|
|
| C_SPLIT_AFTER_SEQUENCES
|
|
| C_USE_ROW_MATCH_FINDER
|
|
| C_PREFETCH_CDICT_TABLES
|
|
| C_REPCODE_RESOLUTION => (PS_AUTO, PS_DISABLE),
|
|
C_LDM_HASH_LOG => {
|
|
let bounds = ZSTD_rust_params_getBounds(C_HASH_LOG);
|
|
(bounds.lowerBound, bounds.upperBound)
|
|
}
|
|
C_LDM_MIN_MATCH => (4, 4096),
|
|
C_LDM_BUCKET_SIZE_LOG => (1, 8),
|
|
C_LDM_HASH_RATE_LOG => {
|
|
let window_log = ZSTD_rust_params_getBounds(C_WINDOW_LOG);
|
|
let hash_log = ZSTD_rust_params_getBounds(C_HASH_LOG);
|
|
(0, window_log.upperBound - hash_log.lowerBound)
|
|
}
|
|
C_RSYNCABLE
|
|
| C_FORCE_MAX_WINDOW
|
|
| C_VALIDATE_SEQUENCES
|
|
| C_DETERMINISTIC_REF_PREFIX
|
|
| C_ENABLE_SEQ_PRODUCER_FALLBACK => (0, 1),
|
|
C_TARGET_C_BLOCK_SIZE => (TARGET_C_BLOCK_SIZE_MIN, BLOCKSIZE_MAX as c_int),
|
|
C_SRC_SIZE_HINT => (0, c_int::MAX),
|
|
C_STABLE_IN_BUFFER | C_STABLE_OUT_BUFFER | C_BLOCK_DELIMITERS => (0, 1),
|
|
C_BLOCK_SPLITTER_LEVEL => (0, 6),
|
|
C_MAX_BLOCK_SIZE => (BLOCKSIZE_MAX_MIN, BLOCKSIZE_MAX as c_int),
|
|
_ => {
|
|
return ZSTD_bounds {
|
|
error: ERROR(ZstdErrorCode::ParameterUnsupported),
|
|
lowerBound: 0,
|
|
upperBound: 0,
|
|
};
|
|
}
|
|
};
|
|
ZSTD_bounds {
|
|
error: 0,
|
|
lowerBound: lower,
|
|
upperBound: upper,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn current_bounds(param: c_int) -> ZSTD_bounds {
|
|
bounds_for(param, unsafe { current_bounds_config() })
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn within_bounds(param: c_int, value: c_int) -> bool {
|
|
within_bounds_for(param, value, unsafe { current_bounds_config() })
|
|
}
|
|
|
|
#[inline]
|
|
fn within_bounds_for(param: c_int, value: c_int, config: BoundsConfig) -> bool {
|
|
let bounds = bounds_for(param, config);
|
|
bounds.error == 0 && value >= bounds.lowerBound && value <= bounds.upperBound
|
|
}
|
|
|
|
#[inline]
|
|
fn clamp_bounds_for(param: c_int, value: &mut c_int, config: BoundsConfig) -> usize {
|
|
let bounds = bounds_for(param, config);
|
|
if bounds.error != 0 {
|
|
return bounds.error;
|
|
}
|
|
*value = (*value).clamp(bounds.lowerBound, bounds.upperBound);
|
|
0
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn clamp_bounds(param: c_int, value: &mut c_int) -> usize {
|
|
clamp_bounds_for(param, value, unsafe { current_bounds_config() })
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_cctx_params_get_bounds(param: c_int) -> ZSTD_bounds {
|
|
unsafe { current_bounds(param) }
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_cctx_params_clamp_bounds(
|
|
param: c_int,
|
|
value: *mut c_int,
|
|
) -> usize {
|
|
unsafe { clamp_bounds(param, &mut *value) }
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_cctx_params_within_bounds(param: c_int, value: c_int) -> c_int {
|
|
unsafe { within_bounds(param, value) as c_int }
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn use_target_c_block_size(cctx_params: *const ZSTD_CCtx_params) -> c_int {
|
|
unsafe { ((*cctx_params).targetCBlockSize != 0) as c_int }
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_useTargetCBlockSize(cctx_params: *const ZSTD_CCtx_params) -> c_int {
|
|
unsafe { use_target_c_block_size(cctx_params) }
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn block_splitter_enabled(cctx_params: *mut ZSTD_CCtx_params) -> c_int {
|
|
let post_block_splitter = unsafe { (*cctx_params).postBlockSplitter };
|
|
assert_ne!(post_block_splitter, PS_AUTO);
|
|
(post_block_splitter == ZSTD_RUST_PS_ENABLE) as c_int
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_blockSplitterEnabled(cctx_params: *mut ZSTD_CCtx_params) -> c_int {
|
|
unsafe { block_splitter_enabled(cctx_params) }
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn multithreading_enabled() -> bool {
|
|
#[cfg(not(test))]
|
|
{
|
|
unsafe { ZSTD_rust_cctx_params_is_multithreaded() != 0 }
|
|
}
|
|
#[cfg(test)]
|
|
{
|
|
false
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn job_size_min() -> c_int {
|
|
#[cfg(not(test))]
|
|
{
|
|
unsafe { ZSTD_rust_cctx_params_job_size_min() }
|
|
}
|
|
#[cfg(test)]
|
|
{
|
|
512 << 10
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn require_bounds(param: c_int, value: c_int) -> Result<(), usize> {
|
|
if unsafe { within_bounds(param, value) } {
|
|
Ok(())
|
|
} else {
|
|
Err(ERROR(ZstdErrorCode::ParameterOutOfBound))
|
|
}
|
|
}
|
|
|
|
unsafe fn init_impl(params: *mut ZSTD_CCtx_params, compression_level: c_int) -> usize {
|
|
if params.is_null() {
|
|
return ERROR(ZstdErrorCode::Generic);
|
|
}
|
|
unsafe {
|
|
ptr::write_bytes(params.cast::<u8>(), 0, size_of::<ZSTD_CCtx_params>());
|
|
(*params).compressionLevel = compression_level;
|
|
(*params).fParams.contentSizeFlag = 1;
|
|
}
|
|
0
|
|
}
|
|
|
|
unsafe fn init_internal_impl(
|
|
cctx_params: *mut ZSTD_CCtx_params,
|
|
zstd_params: *const ZSTD_parameters,
|
|
compression_level: c_int,
|
|
) {
|
|
unsafe {
|
|
ptr::write_bytes(cctx_params.cast::<u8>(), 0, size_of::<ZSTD_CCtx_params>());
|
|
(*cctx_params).cParams = (*zstd_params).cParams;
|
|
(*cctx_params).fParams = (*zstd_params).fParams;
|
|
/* Keep the level for tracing even when params came from a zstd-params object. */
|
|
(*cctx_params).compressionLevel = compression_level;
|
|
(*cctx_params).useRowMatchFinder = resolve_row_match_finder((*zstd_params).cParams);
|
|
(*cctx_params).postBlockSplitter = resolve_block_splitter((*zstd_params).cParams);
|
|
(*cctx_params).ldmParams.enableLdm = resolve_ldm((*zstd_params).cParams);
|
|
(*cctx_params).validateSequences =
|
|
ZSTD_rust_params_resolveExternalSequenceValidation(ZSTD_RUST_PS_AUTO);
|
|
(*cctx_params).maxBlockSize = ZSTD_rust_params_resolveMaxBlockSize(0);
|
|
(*cctx_params).searchForExternalRepcodes =
|
|
ZSTD_rust_params_resolveExternalRepcodeSearch(ZSTD_RUST_PS_AUTO, compression_level);
|
|
}
|
|
}
|
|
|
|
/// Initializes the internal parameter object from validated zstd parameters.
|
|
///
|
|
/// The C adapter retains validation assertions and diagnostic logging; this
|
|
/// function owns only the narrow reset, copy, and default-resolution policy.
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_CCtxParams_init_internal(
|
|
cctx_params: *mut ZSTD_CCtx_params,
|
|
zstd_params: *const ZSTD_parameters,
|
|
compression_level: c_int,
|
|
) {
|
|
unsafe { init_internal_impl(cctx_params, zstd_params, compression_level) }
|
|
}
|
|
|
|
unsafe fn make_cctx_params_from_cparams_impl(
|
|
cctx_params: *mut ZSTD_CCtx_params,
|
|
cparams: *const ZSTD_compressionParameters,
|
|
) {
|
|
unsafe {
|
|
let _ = init_impl(cctx_params, DEFAULT_CLEVEL);
|
|
(*cctx_params).cParams = *cparams;
|
|
(*cctx_params).ldmParams.enableLdm =
|
|
ZSTD_rust_params_resolveEnableLdm(ZSTD_RUST_PS_AUTO, *cparams);
|
|
(*cctx_params).postBlockSplitter =
|
|
ZSTD_rust_params_resolveBlockSplitterMode(ZSTD_RUST_PS_AUTO, *cparams);
|
|
(*cctx_params).useRowMatchFinder =
|
|
ZSTD_rust_params_resolveRowMatchFinderMode(ZSTD_RUST_PS_AUTO, *cparams);
|
|
(*cctx_params).validateSequences =
|
|
ZSTD_rust_params_resolveExternalSequenceValidation((*cctx_params).validateSequences);
|
|
(*cctx_params).maxBlockSize =
|
|
ZSTD_rust_params_resolveMaxBlockSize((*cctx_params).maxBlockSize);
|
|
(*cctx_params).searchForExternalRepcodes = ZSTD_rust_params_resolveExternalRepcodeSearch(
|
|
(*cctx_params).searchForExternalRepcodes,
|
|
(*cctx_params).compressionLevel,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Builds the context parameters used by the C size-estimation adapters.
|
|
///
|
|
/// The output is written through a pointer so the private `repr(C)` object is
|
|
/// never returned by value across the language boundary. C retains LDM
|
|
/// parameter adjustment and validation assertions around this policy leaf.
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_makeCCtxParamsFromCParams(
|
|
cctx_params: *mut ZSTD_CCtx_params,
|
|
cparams: *const ZSTD_compressionParameters,
|
|
) {
|
|
unsafe { make_cctx_params_from_cparams_impl(cctx_params, cparams) }
|
|
}
|
|
|
|
#[inline]
|
|
fn resolve_row_match_finder(cparams: ZSTD_compressionParameters) -> c_int {
|
|
if (STRATEGY_GREEDY..=STRATEGY_LAZY2).contains(&cparams.strategy) && cparams.windowLog > 14 {
|
|
ZSTD_RUST_PS_ENABLE
|
|
} else {
|
|
ZSTD_RUST_PS_DISABLE
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn resolve_block_splitter(cparams: ZSTD_compressionParameters) -> c_int {
|
|
if cparams.strategy >= STRATEGY_BTOPT && cparams.windowLog >= 17 {
|
|
ZSTD_RUST_PS_ENABLE
|
|
} else {
|
|
ZSTD_RUST_PS_DISABLE
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn resolve_ldm(cparams: ZSTD_compressionParameters) -> c_int {
|
|
if cparams.strategy >= STRATEGY_BTOPT && cparams.windowLog >= 27 {
|
|
ZSTD_RUST_PS_ENABLE
|
|
} else {
|
|
ZSTD_RUST_PS_DISABLE
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn resolve_external_repcode_search() -> c_int {
|
|
/* ZSTD_NO_CLEVEL is below the threshold used by the C policy helper. */
|
|
2
|
|
}
|
|
|
|
unsafe fn init_advanced_impl(params: *mut ZSTD_CCtx_params, zstd_params: ZSTD_parameters) -> usize {
|
|
if params.is_null() {
|
|
return ERROR(ZstdErrorCode::Generic);
|
|
}
|
|
|
|
let error = ZSTD_rust_params_checkCParams(zstd_params.cParams);
|
|
if error != 0 {
|
|
return error;
|
|
}
|
|
|
|
unsafe {
|
|
ptr::write_bytes(params.cast::<u8>(), 0, size_of::<ZSTD_CCtx_params>());
|
|
(*params).cParams = zstd_params.cParams;
|
|
(*params).fParams = zstd_params.fParams;
|
|
(*params).compressionLevel = NO_CLEVEL;
|
|
(*params).useRowMatchFinder = resolve_row_match_finder(zstd_params.cParams);
|
|
(*params).postBlockSplitter = resolve_block_splitter(zstd_params.cParams);
|
|
(*params).ldmParams.enableLdm = resolve_ldm(zstd_params.cParams);
|
|
(*params).validateSequences = 0;
|
|
(*params).maxBlockSize = ZSTD_rust_params_resolveMaxBlockSize((*params).maxBlockSize);
|
|
(*params).searchForExternalRepcodes = resolve_external_repcode_search();
|
|
}
|
|
0
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_CCtxParams_init_advanced(
|
|
params: *mut ZSTD_CCtx_params,
|
|
zstd_params: ZSTD_parameters,
|
|
) -> usize {
|
|
unsafe { init_advanced_impl(params, zstd_params) }
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_zstd_params_impl(
|
|
cctx_params: *mut ZSTD_CCtx_params,
|
|
zstd_params: *const ZSTD_parameters,
|
|
) {
|
|
unsafe {
|
|
(*cctx_params).cParams = (*zstd_params).cParams;
|
|
(*cctx_params).fParams = (*zstd_params).fParams;
|
|
(*cctx_params).compressionLevel = NO_CLEVEL;
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_CCtxParams_setZstdParams(
|
|
cctx_params: *mut ZSTD_CCtx_params,
|
|
zstd_params: *const ZSTD_parameters,
|
|
) {
|
|
unsafe { set_zstd_params_impl(cctx_params, zstd_params) }
|
|
}
|
|
|
|
/// Prepares the parameter subset used by `ZSTD_createCDict_advanced2()`.
|
|
///
|
|
/// The C dictionary allocator remains responsible for allocation and
|
|
/// initialization. This leaf owns the context-free parameter selection,
|
|
/// dedicated-dictionary-search fallback, and final row-match-finder
|
|
/// resolution while keeping the `ZSTD_CCtx_params` fields private here.
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_params_prepareAdvancedCDict(
|
|
params: *mut ZSTD_CCtx_params,
|
|
dictSize: usize,
|
|
ldmDefaultWindowLog: u32,
|
|
exclusionMask: u32,
|
|
) -> usize {
|
|
if params.is_null() {
|
|
return ERROR(ZstdErrorCode::Generic);
|
|
}
|
|
|
|
let params = unsafe { &mut *params };
|
|
let mut cparams = if params.enableDedicatedDictSearch != 0 {
|
|
let mut cparams = ZSTD_rust_params_getCParamsInternal(
|
|
params.compressionLevel,
|
|
0,
|
|
dictSize,
|
|
ZSTD_RUST_CPM_CREATE_CDICT,
|
|
exclusionMask,
|
|
);
|
|
cparams = ZSTD_rust_params_dedicatedDictSearch_getCParams(cparams);
|
|
unsafe { ZSTD_rust_params_overrideCParams(&mut cparams, ¶ms.cParams) };
|
|
cparams
|
|
} else {
|
|
ZSTD_rust_params_getCParamsFromCCtxParams(
|
|
params.compressionLevel,
|
|
params.srcSizeHint,
|
|
ZSTD_CONTENTSIZE_UNKNOWN,
|
|
dictSize,
|
|
ZSTD_RUST_CPM_CREATE_CDICT,
|
|
params.ldmParams.enableLdm,
|
|
ldmDefaultWindowLog,
|
|
params.cParams,
|
|
params.useRowMatchFinder,
|
|
exclusionMask,
|
|
)
|
|
};
|
|
|
|
if ZSTD_rust_params_dedicatedDictSearchIsSupported(cparams) == 0 {
|
|
params.enableDedicatedDictSearch = 0;
|
|
cparams = ZSTD_rust_params_getCParamsFromCCtxParams(
|
|
params.compressionLevel,
|
|
params.srcSizeHint,
|
|
ZSTD_CONTENTSIZE_UNKNOWN,
|
|
dictSize,
|
|
ZSTD_RUST_CPM_CREATE_CDICT,
|
|
params.ldmParams.enableLdm,
|
|
ldmDefaultWindowLog,
|
|
params.cParams,
|
|
params.useRowMatchFinder,
|
|
exclusionMask,
|
|
);
|
|
}
|
|
|
|
params.cParams = cparams;
|
|
params.useRowMatchFinder =
|
|
ZSTD_rust_params_resolveRowMatchFinderMode(params.useRowMatchFinder, cparams);
|
|
0
|
|
}
|
|
|
|
unsafe fn custom_calloc(size: usize, custom_mem: ZSTD_customMem) -> *mut c_void {
|
|
if let Some(custom_alloc) = custom_mem.customAlloc {
|
|
let allocation = unsafe { custom_alloc(custom_mem.opaque, size) };
|
|
if !allocation.is_null() {
|
|
unsafe { ptr::write_bytes(allocation.cast::<u8>(), 0, size) };
|
|
}
|
|
allocation
|
|
} else {
|
|
unsafe { libc::calloc(1, size) }
|
|
}
|
|
}
|
|
|
|
unsafe fn custom_free(allocation: *mut c_void, custom_mem: ZSTD_customMem) {
|
|
if allocation.is_null() {
|
|
return;
|
|
}
|
|
if let Some(custom_free) = custom_mem.customFree {
|
|
unsafe { custom_free(custom_mem.opaque, allocation) };
|
|
} else {
|
|
unsafe { libc::free(allocation) };
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_createCCtxParams(
|
|
custom_mem: ZSTD_customMem,
|
|
) -> *mut ZSTD_CCtx_params {
|
|
if custom_mem.customAlloc.is_some() != custom_mem.customFree.is_some() {
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let params = unsafe { custom_calloc(size_of::<ZSTD_CCtx_params>(), custom_mem) }
|
|
.cast::<ZSTD_CCtx_params>();
|
|
if params.is_null() {
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
if unsafe { ZSTD_CCtxParams_init(params, DEFAULT_CLEVEL) } != 0 {
|
|
unsafe { custom_free(params.cast(), custom_mem) };
|
|
return ptr::null_mut();
|
|
}
|
|
unsafe { (*params).customMem = custom_mem };
|
|
params
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_rust_freeCCtxParams(params: *mut ZSTD_CCtx_params) -> usize {
|
|
if params.is_null() {
|
|
return 0;
|
|
}
|
|
let custom_mem = unsafe { (*params).customMem };
|
|
unsafe { custom_free(params.cast(), custom_mem) };
|
|
0
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_CCtxParams_reset(params: *mut ZSTD_CCtx_params) -> usize {
|
|
unsafe { init_impl(params, DEFAULT_CLEVEL) }
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_CCtxParams_init(
|
|
params: *mut ZSTD_CCtx_params,
|
|
compression_level: c_int,
|
|
) -> usize {
|
|
unsafe { init_impl(params, compression_level) }
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_parameter(params: *mut ZSTD_CCtx_params, param: c_int, mut value: c_int) -> usize {
|
|
let params = unsafe { &mut *params };
|
|
match param {
|
|
C_FORMAT => {
|
|
if let Err(error) = unsafe { require_bounds(C_FORMAT, value) } {
|
|
return error;
|
|
}
|
|
params.format = value;
|
|
value as usize
|
|
}
|
|
C_COMPRESSION_LEVEL => {
|
|
let error = unsafe { clamp_bounds(C_COMPRESSION_LEVEL, &mut value) };
|
|
if error != 0 {
|
|
return error;
|
|
}
|
|
params.compressionLevel = if value == 0 { DEFAULT_CLEVEL } else { value };
|
|
if params.compressionLevel >= 0 {
|
|
params.compressionLevel as usize
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
C_WINDOW_LOG => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_WINDOW_LOG, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.cParams.windowLog = value as u32;
|
|
params.cParams.windowLog as usize
|
|
}
|
|
C_HASH_LOG => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_HASH_LOG, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.cParams.hashLog = value as u32;
|
|
params.cParams.hashLog as usize
|
|
}
|
|
C_CHAIN_LOG => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_CHAIN_LOG, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.cParams.chainLog = value as u32;
|
|
params.cParams.chainLog as usize
|
|
}
|
|
C_SEARCH_LOG => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_SEARCH_LOG, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.cParams.searchLog = value as u32;
|
|
value as usize
|
|
}
|
|
C_MIN_MATCH => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_MIN_MATCH, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.cParams.minMatch = value as u32;
|
|
params.cParams.minMatch as usize
|
|
}
|
|
C_TARGET_LENGTH => {
|
|
if let Err(error) = unsafe { require_bounds(C_TARGET_LENGTH, value) } {
|
|
return error;
|
|
}
|
|
params.cParams.targetLength = value as u32;
|
|
params.cParams.targetLength as usize
|
|
}
|
|
C_STRATEGY => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_STRATEGY, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.cParams.strategy = value;
|
|
value as usize
|
|
}
|
|
C_CONTENT_SIZE_FLAG => {
|
|
params.fParams.contentSizeFlag = (value != 0) as c_int;
|
|
params.fParams.contentSizeFlag as usize
|
|
}
|
|
C_CHECKSUM_FLAG => {
|
|
params.fParams.checksumFlag = (value != 0) as c_int;
|
|
params.fParams.checksumFlag as usize
|
|
}
|
|
C_DICT_ID_FLAG => {
|
|
params.fParams.noDictIDFlag = (value == 0) as c_int;
|
|
(params.fParams.noDictIDFlag == 0) as usize
|
|
}
|
|
C_FORCE_MAX_WINDOW => {
|
|
params.forceWindow = (value != 0) as c_int;
|
|
params.forceWindow as usize
|
|
}
|
|
C_FORCE_ATTACH_DICT => {
|
|
if let Err(error) = unsafe { require_bounds(C_FORCE_ATTACH_DICT, value) } {
|
|
return error;
|
|
}
|
|
params.attachDictPref = value;
|
|
value as usize
|
|
}
|
|
C_LITERAL_COMPRESSION_MODE => {
|
|
if let Err(error) = unsafe { require_bounds(C_LITERAL_COMPRESSION_MODE, value) } {
|
|
return error;
|
|
}
|
|
params.literalCompressionMode = value;
|
|
value as usize
|
|
}
|
|
C_NB_WORKERS => {
|
|
if !unsafe { multithreading_enabled() } {
|
|
if value != 0 {
|
|
return ERROR(ZstdErrorCode::ParameterUnsupported);
|
|
}
|
|
return 0;
|
|
}
|
|
let error = unsafe { clamp_bounds(C_NB_WORKERS, &mut value) };
|
|
if error != 0 {
|
|
return error;
|
|
}
|
|
params.nbWorkers = value;
|
|
params.nbWorkers as usize
|
|
}
|
|
C_JOB_SIZE => {
|
|
if !unsafe { multithreading_enabled() } {
|
|
if value != 0 {
|
|
return ERROR(ZstdErrorCode::ParameterUnsupported);
|
|
}
|
|
return 0;
|
|
}
|
|
let minimum = unsafe { job_size_min() };
|
|
if value != 0 && value < minimum {
|
|
value = minimum;
|
|
}
|
|
let error = unsafe { clamp_bounds(C_JOB_SIZE, &mut value) };
|
|
if error != 0 {
|
|
return error;
|
|
}
|
|
params.jobSize = value as usize;
|
|
params.jobSize
|
|
}
|
|
C_OVERLAP_LOG => {
|
|
if !unsafe { multithreading_enabled() } {
|
|
if value != 0 {
|
|
return ERROR(ZstdErrorCode::ParameterUnsupported);
|
|
}
|
|
return 0;
|
|
}
|
|
let error = unsafe { clamp_bounds(C_OVERLAP_LOG, &mut value) };
|
|
if error != 0 {
|
|
return error;
|
|
}
|
|
params.overlapLog = value;
|
|
params.overlapLog as usize
|
|
}
|
|
C_RSYNCABLE => {
|
|
if !unsafe { multithreading_enabled() } {
|
|
if value != 0 {
|
|
return ERROR(ZstdErrorCode::ParameterUnsupported);
|
|
}
|
|
return 0;
|
|
}
|
|
/* The C implementation intentionally clamps this with overlapLog. */
|
|
let error = unsafe { clamp_bounds(C_OVERLAP_LOG, &mut value) };
|
|
if error != 0 {
|
|
return error;
|
|
}
|
|
params.rsyncable = value;
|
|
params.rsyncable as usize
|
|
}
|
|
C_ENABLE_DEDICATED_DICT_SEARCH => {
|
|
params.enableDedicatedDictSearch = (value != 0) as c_int;
|
|
params.enableDedicatedDictSearch as usize
|
|
}
|
|
C_ENABLE_LDM => {
|
|
if let Err(error) = unsafe { require_bounds(C_ENABLE_LDM, value) } {
|
|
return error;
|
|
}
|
|
params.ldmParams.enableLdm = value;
|
|
value as usize
|
|
}
|
|
C_LDM_HASH_LOG => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_LDM_HASH_LOG, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.ldmParams.hashLog = value as u32;
|
|
params.ldmParams.hashLog as usize
|
|
}
|
|
C_LDM_MIN_MATCH => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_LDM_MIN_MATCH, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.ldmParams.minMatchLength = value as u32;
|
|
params.ldmParams.minMatchLength as usize
|
|
}
|
|
C_LDM_BUCKET_SIZE_LOG => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_LDM_BUCKET_SIZE_LOG, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.ldmParams.bucketSizeLog = value as u32;
|
|
params.ldmParams.bucketSizeLog as usize
|
|
}
|
|
C_LDM_HASH_RATE_LOG => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_LDM_HASH_RATE_LOG, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.ldmParams.hashRateLog = value as u32;
|
|
params.ldmParams.hashRateLog as usize
|
|
}
|
|
C_TARGET_C_BLOCK_SIZE => {
|
|
if value != 0 {
|
|
value = value.max(TARGET_C_BLOCK_SIZE_MIN);
|
|
if let Err(error) = unsafe { require_bounds(C_TARGET_C_BLOCK_SIZE, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.targetCBlockSize = value as u32 as usize;
|
|
params.targetCBlockSize
|
|
}
|
|
C_SRC_SIZE_HINT => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_SRC_SIZE_HINT, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.srcSizeHint = value;
|
|
params.srcSizeHint as usize
|
|
}
|
|
C_STABLE_IN_BUFFER => {
|
|
if let Err(error) = unsafe { require_bounds(C_STABLE_IN_BUFFER, value) } {
|
|
return error;
|
|
}
|
|
params.inBufferMode = value;
|
|
value as usize
|
|
}
|
|
C_STABLE_OUT_BUFFER => {
|
|
if let Err(error) = unsafe { require_bounds(C_STABLE_OUT_BUFFER, value) } {
|
|
return error;
|
|
}
|
|
params.outBufferMode = value;
|
|
value as usize
|
|
}
|
|
C_BLOCK_DELIMITERS => {
|
|
if let Err(error) = unsafe { require_bounds(C_BLOCK_DELIMITERS, value) } {
|
|
return error;
|
|
}
|
|
params.blockDelimiters = value;
|
|
value as usize
|
|
}
|
|
C_VALIDATE_SEQUENCES => {
|
|
if let Err(error) = unsafe { require_bounds(C_VALIDATE_SEQUENCES, value) } {
|
|
return error;
|
|
}
|
|
params.validateSequences = value;
|
|
value as usize
|
|
}
|
|
C_SPLIT_AFTER_SEQUENCES => {
|
|
if let Err(error) = unsafe { require_bounds(C_SPLIT_AFTER_SEQUENCES, value) } {
|
|
return error;
|
|
}
|
|
params.postBlockSplitter = value;
|
|
value as usize
|
|
}
|
|
C_BLOCK_SPLITTER_LEVEL => {
|
|
if let Err(error) = unsafe { require_bounds(C_BLOCK_SPLITTER_LEVEL, value) } {
|
|
return error;
|
|
}
|
|
params.preBlockSplitter_level = value;
|
|
value as usize
|
|
}
|
|
C_USE_ROW_MATCH_FINDER => {
|
|
if let Err(error) = unsafe { require_bounds(C_USE_ROW_MATCH_FINDER, value) } {
|
|
return error;
|
|
}
|
|
params.useRowMatchFinder = value;
|
|
value as usize
|
|
}
|
|
C_DETERMINISTIC_REF_PREFIX => {
|
|
if let Err(error) = unsafe { require_bounds(C_DETERMINISTIC_REF_PREFIX, value) } {
|
|
return error;
|
|
}
|
|
params.deterministicRefPrefix = (value != 0) as c_int;
|
|
params.deterministicRefPrefix as usize
|
|
}
|
|
C_PREFETCH_CDICT_TABLES => {
|
|
if let Err(error) = unsafe { require_bounds(C_PREFETCH_CDICT_TABLES, value) } {
|
|
return error;
|
|
}
|
|
params.prefetchCDictTables = value;
|
|
value as usize
|
|
}
|
|
C_ENABLE_SEQ_PRODUCER_FALLBACK => {
|
|
if let Err(error) = unsafe { require_bounds(C_ENABLE_SEQ_PRODUCER_FALLBACK, value) } {
|
|
return error;
|
|
}
|
|
params.enableMatchFinderFallback = value;
|
|
value as usize
|
|
}
|
|
C_MAX_BLOCK_SIZE => {
|
|
if value != 0 {
|
|
if let Err(error) = unsafe { require_bounds(C_MAX_BLOCK_SIZE, value) } {
|
|
return error;
|
|
}
|
|
}
|
|
params.maxBlockSize = value as usize;
|
|
params.maxBlockSize
|
|
}
|
|
C_REPCODE_RESOLUTION => {
|
|
if let Err(error) = unsafe { require_bounds(C_REPCODE_RESOLUTION, value) } {
|
|
return error;
|
|
}
|
|
params.searchForExternalRepcodes = value;
|
|
value as usize
|
|
}
|
|
_ => ERROR(ZstdErrorCode::ParameterUnsupported),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_CCtxParams_setParameter(
|
|
params: *mut ZSTD_CCtx_params,
|
|
param: c_int,
|
|
value: c_int,
|
|
) -> usize {
|
|
unsafe { set_parameter(params, param, value) }
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_CCtxParams_getParameter(
|
|
params: *const ZSTD_CCtx_params,
|
|
param: c_int,
|
|
value: *mut c_int,
|
|
) -> usize {
|
|
let params = unsafe { &*params };
|
|
let result = match param {
|
|
C_FORMAT => params.format,
|
|
C_COMPRESSION_LEVEL => params.compressionLevel,
|
|
C_WINDOW_LOG => params.cParams.windowLog as c_int,
|
|
C_HASH_LOG => params.cParams.hashLog as c_int,
|
|
C_CHAIN_LOG => params.cParams.chainLog as c_int,
|
|
C_SEARCH_LOG => params.cParams.searchLog as c_int,
|
|
C_MIN_MATCH => params.cParams.minMatch as c_int,
|
|
C_TARGET_LENGTH => params.cParams.targetLength as c_int,
|
|
C_STRATEGY => params.cParams.strategy,
|
|
C_CONTENT_SIZE_FLAG => params.fParams.contentSizeFlag,
|
|
C_CHECKSUM_FLAG => params.fParams.checksumFlag,
|
|
C_DICT_ID_FLAG => (params.fParams.noDictIDFlag == 0) as c_int,
|
|
C_FORCE_MAX_WINDOW => params.forceWindow,
|
|
C_FORCE_ATTACH_DICT => params.attachDictPref,
|
|
C_LITERAL_COMPRESSION_MODE => params.literalCompressionMode,
|
|
C_NB_WORKERS => {
|
|
if !unsafe { multithreading_enabled() } {
|
|
debug_assert_eq!(params.nbWorkers, 0);
|
|
}
|
|
params.nbWorkers
|
|
}
|
|
C_JOB_SIZE => {
|
|
if !unsafe { multithreading_enabled() } {
|
|
return ERROR(ZstdErrorCode::ParameterUnsupported);
|
|
}
|
|
debug_assert!(params.jobSize <= c_int::MAX as usize);
|
|
params.jobSize as c_int
|
|
}
|
|
C_OVERLAP_LOG => {
|
|
if !unsafe { multithreading_enabled() } {
|
|
return ERROR(ZstdErrorCode::ParameterUnsupported);
|
|
}
|
|
params.overlapLog
|
|
}
|
|
C_RSYNCABLE => {
|
|
if !unsafe { multithreading_enabled() } {
|
|
return ERROR(ZstdErrorCode::ParameterUnsupported);
|
|
}
|
|
params.rsyncable
|
|
}
|
|
C_ENABLE_DEDICATED_DICT_SEARCH => params.enableDedicatedDictSearch,
|
|
C_ENABLE_LDM => params.ldmParams.enableLdm,
|
|
C_LDM_HASH_LOG => params.ldmParams.hashLog as c_int,
|
|
C_LDM_MIN_MATCH => params.ldmParams.minMatchLength as c_int,
|
|
C_LDM_BUCKET_SIZE_LOG => params.ldmParams.bucketSizeLog as c_int,
|
|
C_LDM_HASH_RATE_LOG => params.ldmParams.hashRateLog as c_int,
|
|
C_TARGET_C_BLOCK_SIZE => params.targetCBlockSize as c_int,
|
|
C_SRC_SIZE_HINT => params.srcSizeHint,
|
|
C_STABLE_IN_BUFFER => params.inBufferMode,
|
|
C_STABLE_OUT_BUFFER => params.outBufferMode,
|
|
C_BLOCK_DELIMITERS => params.blockDelimiters,
|
|
C_VALIDATE_SEQUENCES => params.validateSequences,
|
|
C_SPLIT_AFTER_SEQUENCES => params.postBlockSplitter,
|
|
C_BLOCK_SPLITTER_LEVEL => params.preBlockSplitter_level,
|
|
C_USE_ROW_MATCH_FINDER => params.useRowMatchFinder,
|
|
C_DETERMINISTIC_REF_PREFIX => params.deterministicRefPrefix,
|
|
C_PREFETCH_CDICT_TABLES => params.prefetchCDictTables,
|
|
C_ENABLE_SEQ_PRODUCER_FALLBACK => params.enableMatchFinderFallback,
|
|
C_MAX_BLOCK_SIZE => params.maxBlockSize as c_int,
|
|
C_REPCODE_RESOLUTION => params.searchForExternalRepcodes,
|
|
_ => return ERROR(ZstdErrorCode::ParameterUnsupported),
|
|
};
|
|
unsafe { *value = result };
|
|
0
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn ZSTD_CCtxParams_registerSequenceProducer(
|
|
params: *mut ZSTD_CCtx_params,
|
|
ext_seq_prod_state: *mut c_void,
|
|
ext_seq_prod_func: Option<ZSTD_sequenceProducer_F>,
|
|
) {
|
|
debug_assert!(!params.is_null());
|
|
let params = unsafe { &mut *params };
|
|
if ext_seq_prod_func.is_some() {
|
|
params.extSeqProdFunc = ext_seq_prod_func;
|
|
params.extSeqProdState = ext_seq_prod_state;
|
|
} else {
|
|
params.extSeqProdFunc = None;
|
|
params.extSeqProdState = ptr::null_mut();
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::errors::{ERR_getErrorCode, ERR_isError};
|
|
use std::mem::{align_of, offset_of, size_of, MaybeUninit};
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
|
#[test]
|
|
fn update_authorization_matches_the_c_parameter_policy() {
|
|
let authorized = [
|
|
C_COMPRESSION_LEVEL,
|
|
C_HASH_LOG,
|
|
C_CHAIN_LOG,
|
|
C_SEARCH_LOG,
|
|
C_MIN_MATCH,
|
|
C_TARGET_LENGTH,
|
|
C_STRATEGY,
|
|
C_BLOCK_SPLITTER_LEVEL,
|
|
];
|
|
let unauthorized = [
|
|
C_FORMAT,
|
|
C_WINDOW_LOG,
|
|
C_CONTENT_SIZE_FLAG,
|
|
C_CHECKSUM_FLAG,
|
|
C_DICT_ID_FLAG,
|
|
C_FORCE_MAX_WINDOW,
|
|
C_NB_WORKERS,
|
|
C_JOB_SIZE,
|
|
C_OVERLAP_LOG,
|
|
C_RSYNCABLE,
|
|
C_ENABLE_DEDICATED_DICT_SEARCH,
|
|
C_ENABLE_LDM,
|
|
C_LDM_HASH_LOG,
|
|
C_LDM_MIN_MATCH,
|
|
C_LDM_BUCKET_SIZE_LOG,
|
|
C_LDM_HASH_RATE_LOG,
|
|
C_FORCE_ATTACH_DICT,
|
|
C_LITERAL_COMPRESSION_MODE,
|
|
C_TARGET_C_BLOCK_SIZE,
|
|
C_SRC_SIZE_HINT,
|
|
C_STABLE_IN_BUFFER,
|
|
C_STABLE_OUT_BUFFER,
|
|
C_BLOCK_DELIMITERS,
|
|
C_VALIDATE_SEQUENCES,
|
|
C_SPLIT_AFTER_SEQUENCES,
|
|
C_USE_ROW_MATCH_FINDER,
|
|
C_DETERMINISTIC_REF_PREFIX,
|
|
C_PREFETCH_CDICT_TABLES,
|
|
C_ENABLE_SEQ_PRODUCER_FALLBACK,
|
|
C_MAX_BLOCK_SIZE,
|
|
C_REPCODE_RESOLUTION,
|
|
-1,
|
|
1018,
|
|
];
|
|
|
|
for param in authorized {
|
|
assert!(is_update_authorized(param));
|
|
assert_eq!(ZSTD_rust_isUpdateAuthorized(param), 1);
|
|
}
|
|
for param in unauthorized {
|
|
assert!(!is_update_authorized(param));
|
|
assert_eq!(ZSTD_rust_isUpdateAuthorized(param), 0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn private_parameter_mirror_matches_the_c_layout() {
|
|
let pointer_size = size_of::<usize>();
|
|
assert_eq!(align_of::<ZSTD_CCtx_params>(), pointer_size);
|
|
assert_eq!(size_of::<ZSTD_compressionParameters>(), 28);
|
|
assert_eq!(size_of::<ZSTD_frameParameters>(), 12);
|
|
assert_eq!(size_of::<ldmParams_t>(), 24);
|
|
assert_eq!(
|
|
size_of::<ZSTD_customMem>(),
|
|
if pointer_size == 8 { 24 } else { 12 }
|
|
);
|
|
assert_eq!(offset_of!(ZSTD_CCtx_params, format), 0);
|
|
assert_eq!(offset_of!(ZSTD_CCtx_params, cParams), 4);
|
|
assert_eq!(offset_of!(ZSTD_CCtx_params, fParams), 32);
|
|
assert_eq!(offset_of!(ZSTD_CCtx_params, compressionLevel), 44);
|
|
assert_eq!(offset_of!(ZSTD_CCtx_params, forceWindow), 48);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, targetCBlockSize),
|
|
if pointer_size == 8 { 56 } else { 52 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, srcSizeHint),
|
|
if pointer_size == 8 { 64 } else { 56 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, attachDictPref),
|
|
if pointer_size == 8 { 68 } else { 60 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, literalCompressionMode),
|
|
if pointer_size == 8 { 72 } else { 64 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, nbWorkers),
|
|
if pointer_size == 8 { 76 } else { 68 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, jobSize),
|
|
if pointer_size == 8 { 80 } else { 72 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, overlapLog),
|
|
if pointer_size == 8 { 88 } else { 76 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, rsyncable),
|
|
if pointer_size == 8 { 92 } else { 80 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, ldmParams),
|
|
if pointer_size == 8 { 96 } else { 84 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, enableDedicatedDictSearch),
|
|
if pointer_size == 8 { 120 } else { 108 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, inBufferMode),
|
|
if pointer_size == 8 { 124 } else { 112 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, outBufferMode),
|
|
if pointer_size == 8 { 128 } else { 116 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, blockDelimiters),
|
|
if pointer_size == 8 { 132 } else { 120 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, validateSequences),
|
|
if pointer_size == 8 { 136 } else { 124 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, postBlockSplitter),
|
|
if pointer_size == 8 { 140 } else { 128 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, preBlockSplitter_level),
|
|
if pointer_size == 8 { 144 } else { 132 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, maxBlockSize),
|
|
if pointer_size == 8 { 152 } else { 136 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, useRowMatchFinder),
|
|
if pointer_size == 8 { 160 } else { 140 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, deterministicRefPrefix),
|
|
if pointer_size == 8 { 164 } else { 144 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, customMem),
|
|
if pointer_size == 8 { 168 } else { 148 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, prefetchCDictTables),
|
|
if pointer_size == 8 { 192 } else { 160 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, enableMatchFinderFallback),
|
|
if pointer_size == 8 { 196 } else { 164 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, extSeqProdState),
|
|
if pointer_size == 8 { 200 } else { 168 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, extSeqProdFunc),
|
|
if pointer_size == 8 { 208 } else { 172 }
|
|
);
|
|
assert_eq!(
|
|
offset_of!(ZSTD_CCtx_params, searchForExternalRepcodes),
|
|
if pointer_size == 8 { 216 } else { 176 }
|
|
);
|
|
assert_eq!(
|
|
size_of::<ZSTD_CCtx_params>(),
|
|
if pointer_size == 8 { 224 } else { 180 }
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn target_c_block_size_predicate_uses_zero_as_the_disable_value() {
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
|
|
let params = storage.as_mut_ptr();
|
|
unsafe {
|
|
(*params).targetCBlockSize = 0;
|
|
assert_eq!(ZSTD_useTargetCBlockSize(params), 0);
|
|
|
|
(*params).targetCBlockSize = 1;
|
|
assert_eq!(ZSTD_useTargetCBlockSize(params), 1);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn block_splitter_predicate_only_enables_the_enable_mode() {
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
|
|
let params = storage.as_mut_ptr();
|
|
unsafe {
|
|
(*params).postBlockSplitter = ZSTD_RUST_PS_ENABLE;
|
|
assert_eq!(ZSTD_blockSplitterEnabled(params), 1);
|
|
|
|
(*params).postBlockSplitter = PS_DISABLE;
|
|
assert_eq!(ZSTD_blockSplitterEnabled(params), 0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn block_splitter_predicate_rejects_auto_mode() {
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
|
|
let params = storage.as_mut_ptr();
|
|
unsafe {
|
|
(*params).postBlockSplitter = PS_AUTO;
|
|
let _ = block_splitter_enabled(params);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn migrated_non_core_bounds_preserve_pointer_and_thread_configuration() {
|
|
let single_threaded = BoundsConfig {
|
|
multithreaded: false,
|
|
nb_workers_max: 0,
|
|
job_size_max: 0,
|
|
};
|
|
let expected = [
|
|
(C_FORMAT, 0, 1),
|
|
(C_FORCE_ATTACH_DICT, 0, 3),
|
|
(C_LITERAL_COMPRESSION_MODE, 0, 2),
|
|
(C_CONTENT_SIZE_FLAG, 0, 1),
|
|
(C_CHECKSUM_FLAG, 0, 1),
|
|
(C_DICT_ID_FLAG, 0, 1),
|
|
(C_NB_WORKERS, 0, 0),
|
|
(C_JOB_SIZE, 0, 0),
|
|
(C_OVERLAP_LOG, 0, 0),
|
|
(C_ENABLE_DEDICATED_DICT_SEARCH, 0, 1),
|
|
(C_ENABLE_LDM, PS_AUTO, PS_DISABLE),
|
|
(C_LDM_HASH_LOG, 6, 30),
|
|
(C_LDM_MIN_MATCH, 4, 4096),
|
|
(C_LDM_BUCKET_SIZE_LOG, 1, 8),
|
|
(
|
|
C_LDM_HASH_RATE_LOG,
|
|
0,
|
|
if size_of::<usize>() == 4 { 24 } else { 25 },
|
|
),
|
|
(C_RSYNCABLE, 0, 1),
|
|
(C_FORCE_MAX_WINDOW, 0, 1),
|
|
(C_VALIDATE_SEQUENCES, 0, 1),
|
|
(C_SPLIT_AFTER_SEQUENCES, PS_AUTO, PS_DISABLE),
|
|
(C_USE_ROW_MATCH_FINDER, PS_AUTO, PS_DISABLE),
|
|
(C_DETERMINISTIC_REF_PREFIX, 0, 1),
|
|
(C_PREFETCH_CDICT_TABLES, PS_AUTO, PS_DISABLE),
|
|
(C_ENABLE_SEQ_PRODUCER_FALLBACK, 0, 1),
|
|
(
|
|
C_TARGET_C_BLOCK_SIZE,
|
|
TARGET_C_BLOCK_SIZE_MIN,
|
|
BLOCKSIZE_MAX as c_int,
|
|
),
|
|
(C_SRC_SIZE_HINT, 0, c_int::MAX),
|
|
(C_STABLE_IN_BUFFER, 0, 1),
|
|
(C_STABLE_OUT_BUFFER, 0, 1),
|
|
(C_BLOCK_DELIMITERS, 0, 1),
|
|
(C_BLOCK_SPLITTER_LEVEL, 0, 6),
|
|
(C_MAX_BLOCK_SIZE, BLOCKSIZE_MAX_MIN, BLOCKSIZE_MAX as c_int),
|
|
(C_REPCODE_RESOLUTION, PS_AUTO, PS_DISABLE),
|
|
];
|
|
|
|
for (param, lower, upper) in expected {
|
|
let bounds = bounds_for(param, single_threaded);
|
|
assert_eq!(bounds.error, 0, "param {param} returned an error");
|
|
assert_eq!((bounds.lowerBound, bounds.upperBound), (lower, upper));
|
|
assert!(within_bounds_for(param, lower, single_threaded));
|
|
assert!(within_bounds_for(param, upper, single_threaded));
|
|
if lower > c_int::MIN {
|
|
assert!(!within_bounds_for(param, lower - 1, single_threaded));
|
|
}
|
|
if upper < c_int::MAX {
|
|
assert!(!within_bounds_for(param, upper + 1, single_threaded));
|
|
}
|
|
}
|
|
|
|
let unsupported = bounds_for(12345, single_threaded);
|
|
assert_eq!(
|
|
unsupported.error,
|
|
ERROR(ZstdErrorCode::ParameterUnsupported)
|
|
);
|
|
|
|
let multithreaded = BoundsConfig {
|
|
multithreaded: true,
|
|
nb_workers_max: 17,
|
|
job_size_max: 987_654_321,
|
|
};
|
|
assert_eq!(
|
|
(
|
|
bounds_for(C_NB_WORKERS, multithreaded).lowerBound,
|
|
bounds_for(C_NB_WORKERS, multithreaded).upperBound
|
|
),
|
|
(0, 17)
|
|
);
|
|
assert_eq!(
|
|
(
|
|
bounds_for(C_JOB_SIZE, multithreaded).lowerBound,
|
|
bounds_for(C_JOB_SIZE, multithreaded).upperBound
|
|
),
|
|
(0, 987_654_321)
|
|
);
|
|
assert_eq!(
|
|
(
|
|
bounds_for(C_OVERLAP_LOG, multithreaded).lowerBound,
|
|
bounds_for(C_OVERLAP_LOG, multithreaded).upperBound
|
|
),
|
|
(0, 9)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn migrated_clamp_bounds_clamps_and_reports_unsupported_parameters() {
|
|
let multithreaded = BoundsConfig {
|
|
multithreaded: true,
|
|
nb_workers_max: 17,
|
|
job_size_max: 987_654_321,
|
|
};
|
|
|
|
let mut value = -1;
|
|
assert_eq!(clamp_bounds_for(C_FORMAT, &mut value, multithreaded), 0);
|
|
assert_eq!(value, 0);
|
|
|
|
value = c_int::MAX;
|
|
assert_eq!(
|
|
clamp_bounds_for(C_TARGET_C_BLOCK_SIZE, &mut value, multithreaded),
|
|
0
|
|
);
|
|
assert_eq!(value, BLOCKSIZE_MAX as c_int);
|
|
|
|
value = c_int::MAX;
|
|
assert_eq!(clamp_bounds_for(C_NB_WORKERS, &mut value, multithreaded), 0);
|
|
assert_eq!(value, 17);
|
|
|
|
value = -1;
|
|
assert_eq!(clamp_bounds_for(C_JOB_SIZE, &mut value, multithreaded), 0);
|
|
assert_eq!(value, 0);
|
|
|
|
value = -1;
|
|
assert_eq!(
|
|
clamp_bounds_for(12345, &mut value, multithreaded),
|
|
ERROR(ZstdErrorCode::ParameterUnsupported)
|
|
);
|
|
assert_eq!(value, -1);
|
|
}
|
|
|
|
#[test]
|
|
fn set_zstd_params_updates_only_zstd_parameter_fields() {
|
|
let zstd_params = ZSTD_parameters {
|
|
cParams: ZSTD_compressionParameters {
|
|
windowLog: 20,
|
|
chainLog: 19,
|
|
hashLog: 18,
|
|
searchLog: 5,
|
|
minMatch: 4,
|
|
targetLength: 32,
|
|
strategy: STRATEGY_GREEDY,
|
|
},
|
|
fParams: ZSTD_frameParameters {
|
|
contentSizeFlag: 1,
|
|
checksumFlag: 1,
|
|
noDictIDFlag: 1,
|
|
},
|
|
};
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
|
|
let params = storage.as_mut_ptr();
|
|
|
|
unsafe {
|
|
(*params).format = 1;
|
|
(*params).compressionLevel = 99;
|
|
(*params).forceWindow = 1;
|
|
(*params).targetCBlockSize = 4096;
|
|
(*params).ldmParams.enableLdm = 1;
|
|
(*params).customMem.opaque = ptr::dangling_mut::<c_void>();
|
|
(*params).extSeqProdState = ptr::dangling_mut::<c_void>();
|
|
(*params).searchForExternalRepcodes = 1;
|
|
let before = *params;
|
|
|
|
ZSTD_rust_CCtxParams_setZstdParams(params, &zstd_params);
|
|
|
|
assert_eq!((*params).cParams, zstd_params.cParams);
|
|
assert_eq!((*params).fParams, zstd_params.fParams);
|
|
assert_eq!((*params).compressionLevel, NO_CLEVEL);
|
|
assert_eq!((*params).format, before.format);
|
|
assert_eq!((*params).forceWindow, before.forceWindow);
|
|
assert_eq!((*params).targetCBlockSize, before.targetCBlockSize);
|
|
assert_eq!((*params).ldmParams.enableLdm, before.ldmParams.enableLdm);
|
|
assert_eq!((*params).customMem.opaque, before.customMem.opaque);
|
|
assert_eq!((*params).extSeqProdState, before.extSeqProdState);
|
|
assert_eq!(
|
|
(*params).searchForExternalRepcodes,
|
|
before.searchForExternalRepcodes
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn init_internal_resets_and_resolves_policy_without_changing_inputs() {
|
|
let zstd_params = ZSTD_parameters {
|
|
cParams: ZSTD_compressionParameters {
|
|
windowLog: 27,
|
|
chainLog: 27,
|
|
hashLog: 25,
|
|
searchLog: 9,
|
|
minMatch: 4,
|
|
targetLength: 32,
|
|
strategy: STRATEGY_BTOPT,
|
|
},
|
|
fParams: ZSTD_frameParameters {
|
|
contentSizeFlag: 1,
|
|
checksumFlag: 1,
|
|
noDictIDFlag: 0,
|
|
},
|
|
};
|
|
|
|
for (compression_level, expected_repcode_search) in
|
|
[(9, ZSTD_RUST_PS_DISABLE), (10, ZSTD_RUST_PS_ENABLE)]
|
|
{
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
|
|
let params = storage.as_mut_ptr();
|
|
unsafe {
|
|
(*params).format = 1;
|
|
(*params).customMem.opaque = ptr::dangling_mut::<c_void>();
|
|
|
|
ZSTD_rust_CCtxParams_init_internal(params, &zstd_params, compression_level);
|
|
|
|
assert_eq!((*params).cParams, zstd_params.cParams);
|
|
assert_eq!((*params).fParams, zstd_params.fParams);
|
|
assert_eq!((*params).compressionLevel, compression_level);
|
|
assert_eq!((*params).useRowMatchFinder, ZSTD_RUST_PS_DISABLE);
|
|
assert_eq!((*params).postBlockSplitter, ZSTD_RUST_PS_ENABLE);
|
|
assert_eq!((*params).ldmParams.enableLdm, ZSTD_RUST_PS_ENABLE);
|
|
assert_eq!((*params).validateSequences, 0);
|
|
assert_eq!((*params).maxBlockSize, BLOCKSIZE_MAX);
|
|
assert_eq!((*params).searchForExternalRepcodes, expected_repcode_search);
|
|
assert_eq!((*params).format, 0);
|
|
assert!((*params).customMem.opaque.is_null());
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn make_cctx_params_from_cparams_resets_and_resolves_defaults() {
|
|
let cparams = ZSTD_compressionParameters {
|
|
windowLog: 27,
|
|
chainLog: 27,
|
|
hashLog: 25,
|
|
searchLog: 9,
|
|
minMatch: 4,
|
|
targetLength: 32,
|
|
strategy: STRATEGY_BTOPT,
|
|
};
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
|
|
let params = storage.as_mut_ptr();
|
|
|
|
unsafe {
|
|
(*params).format = 1;
|
|
(*params).customMem.opaque = ptr::dangling_mut::<c_void>();
|
|
(*params).ldmParams.hashLog = 99;
|
|
|
|
ZSTD_rust_makeCCtxParamsFromCParams(params, &cparams);
|
|
|
|
assert_eq!((*params).cParams, cparams);
|
|
assert_eq!((*params).compressionLevel, DEFAULT_CLEVEL);
|
|
assert_eq!((*params).fParams.contentSizeFlag, 1);
|
|
assert_eq!((*params).fParams.checksumFlag, 0);
|
|
assert_eq!((*params).fParams.noDictIDFlag, 0);
|
|
assert_eq!((*params).ldmParams.enableLdm, ZSTD_RUST_PS_ENABLE);
|
|
assert_eq!((*params).ldmParams.hashLog, 0);
|
|
assert_eq!((*params).postBlockSplitter, ZSTD_RUST_PS_ENABLE);
|
|
assert_eq!((*params).useRowMatchFinder, ZSTD_RUST_PS_DISABLE);
|
|
assert_eq!((*params).validateSequences, 0);
|
|
assert_eq!((*params).maxBlockSize, BLOCKSIZE_MAX);
|
|
assert_eq!((*params).searchForExternalRepcodes, ZSTD_RUST_PS_DISABLE);
|
|
assert_eq!((*params).format, 0);
|
|
assert!((*params).customMem.opaque.is_null());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn advanced_init_resolves_policy_and_resets_internal_fields() {
|
|
let cases = [
|
|
(
|
|
ZSTD_parameters {
|
|
cParams: ZSTD_compressionParameters {
|
|
windowLog: 15,
|
|
chainLog: 15,
|
|
hashLog: 15,
|
|
searchLog: 5,
|
|
minMatch: 4,
|
|
targetLength: 16,
|
|
strategy: STRATEGY_GREEDY,
|
|
},
|
|
fParams: ZSTD_frameParameters {
|
|
contentSizeFlag: 0,
|
|
checksumFlag: 1,
|
|
noDictIDFlag: 1,
|
|
},
|
|
},
|
|
ZSTD_RUST_PS_ENABLE,
|
|
ZSTD_RUST_PS_DISABLE,
|
|
ZSTD_RUST_PS_DISABLE,
|
|
),
|
|
(
|
|
ZSTD_parameters {
|
|
cParams: ZSTD_compressionParameters {
|
|
windowLog: 27,
|
|
chainLog: 27,
|
|
hashLog: 25,
|
|
searchLog: 9,
|
|
minMatch: 3,
|
|
targetLength: 999,
|
|
strategy: STRATEGY_BTOPT + 2,
|
|
},
|
|
fParams: ZSTD_frameParameters {
|
|
contentSizeFlag: 1,
|
|
checksumFlag: 0,
|
|
noDictIDFlag: 0,
|
|
},
|
|
},
|
|
ZSTD_RUST_PS_DISABLE,
|
|
ZSTD_RUST_PS_ENABLE,
|
|
ZSTD_RUST_PS_ENABLE,
|
|
),
|
|
];
|
|
|
|
for (zstd_params, expected_row, expected_splitter, expected_ldm) in cases {
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
|
|
let params = storage.as_mut_ptr();
|
|
unsafe {
|
|
(*params).customMem.opaque = ptr::dangling_mut::<c_void>();
|
|
assert_eq!(ZSTD_rust_CCtxParams_init_advanced(params, zstd_params), 0);
|
|
assert_eq!((*params).cParams, zstd_params.cParams);
|
|
assert_eq!((*params).fParams, zstd_params.fParams);
|
|
assert_eq!((*params).compressionLevel, NO_CLEVEL);
|
|
assert_eq!((*params).useRowMatchFinder, expected_row);
|
|
assert_eq!((*params).postBlockSplitter, expected_splitter);
|
|
assert_eq!((*params).ldmParams.enableLdm, expected_ldm);
|
|
assert_eq!((*params).validateSequences, 0);
|
|
assert_eq!((*params).maxBlockSize, BLOCKSIZE_MAX);
|
|
assert_eq!((*params).searchForExternalRepcodes, PS_DISABLE);
|
|
assert!((*params).customMem.customAlloc.is_none());
|
|
assert!((*params).customMem.customFree.is_none());
|
|
assert!((*params).customMem.opaque.is_null());
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn advanced_init_rejects_null_and_invalid_cparams_before_mutating() {
|
|
let valid_params = ZSTD_parameters {
|
|
cParams: ZSTD_compressionParameters {
|
|
windowLog: 20,
|
|
chainLog: 20,
|
|
hashLog: 20,
|
|
searchLog: 5,
|
|
minMatch: 4,
|
|
targetLength: 16,
|
|
strategy: STRATEGY_GREEDY,
|
|
},
|
|
fParams: ZSTD_frameParameters::default(),
|
|
};
|
|
assert_eq!(
|
|
unsafe { ZSTD_rust_CCtxParams_init_advanced(ptr::null_mut(), valid_params) },
|
|
ERROR(ZstdErrorCode::Generic)
|
|
);
|
|
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
|
|
let params = storage.as_mut_ptr();
|
|
unsafe {
|
|
(*params).format = 1;
|
|
(*params).customMem.opaque = ptr::dangling_mut::<c_void>();
|
|
}
|
|
|
|
let mut invalid_params = valid_params;
|
|
invalid_params.cParams.minMatch = 2;
|
|
assert_eq!(
|
|
unsafe { ZSTD_rust_CCtxParams_init_advanced(params, invalid_params) },
|
|
ERROR(ZstdErrorCode::ParameterOutOfBound)
|
|
);
|
|
unsafe {
|
|
assert_eq!((*params).format, 1);
|
|
assert_eq!((*params).customMem.opaque, ptr::dangling_mut::<c_void>());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn prepare_advanced_cdict_rejects_null_params() {
|
|
assert_eq!(
|
|
unsafe { ZSTD_rust_params_prepareAdvancedCDict(ptr::null_mut(), 32 * 1024, 27, 0) },
|
|
ERROR(ZstdErrorCode::Generic)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn prepare_advanced_cdict_publishes_normal_policy() {
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
|
|
let params = storage.as_mut_ptr();
|
|
let dict_size = 32 * 1024;
|
|
let ldm_default_window_log = 27;
|
|
let exclusion_mask = 0;
|
|
|
|
unsafe {
|
|
assert_eq!(ZSTD_CCtxParams_init(params, DEFAULT_CLEVEL), 0);
|
|
(*params).srcSizeHint = 16 * 1024;
|
|
(*params).ldmParams.enableLdm = ZSTD_RUST_PS_ENABLE;
|
|
(*params).cParams = ZSTD_compressionParameters {
|
|
windowLog: 27,
|
|
chainLog: 20,
|
|
hashLog: 25,
|
|
searchLog: 5,
|
|
minMatch: 4,
|
|
targetLength: 16,
|
|
strategy: STRATEGY_GREEDY,
|
|
};
|
|
(*params).useRowMatchFinder = PS_AUTO;
|
|
|
|
let expected = ZSTD_rust_params_getCParamsFromCCtxParams(
|
|
(*params).compressionLevel,
|
|
(*params).srcSizeHint,
|
|
ZSTD_CONTENTSIZE_UNKNOWN,
|
|
dict_size,
|
|
ZSTD_RUST_CPM_CREATE_CDICT,
|
|
(*params).ldmParams.enableLdm,
|
|
ldm_default_window_log,
|
|
(*params).cParams,
|
|
(*params).useRowMatchFinder,
|
|
exclusion_mask,
|
|
);
|
|
let expected_row =
|
|
ZSTD_rust_params_resolveRowMatchFinderMode((*params).useRowMatchFinder, expected);
|
|
|
|
assert_eq!(
|
|
ZSTD_rust_params_prepareAdvancedCDict(
|
|
params,
|
|
dict_size,
|
|
ldm_default_window_log,
|
|
exclusion_mask,
|
|
),
|
|
0
|
|
);
|
|
assert_eq!((*params).enableDedicatedDictSearch, 0);
|
|
assert_eq!((*params).cParams, expected);
|
|
assert_eq!((*params).useRowMatchFinder, expected_row);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn prepare_advanced_cdict_applies_dedicated_search_overrides() {
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
|
|
let params = storage.as_mut_ptr();
|
|
let dict_size = 32 * 1024;
|
|
let ldm_default_window_log = 27;
|
|
let exclusion_mask = 0;
|
|
|
|
unsafe {
|
|
assert_eq!(ZSTD_CCtxParams_init(params, DEFAULT_CLEVEL), 0);
|
|
(*params).enableDedicatedDictSearch = 1;
|
|
(*params).cParams = ZSTD_compressionParameters {
|
|
windowLog: 20,
|
|
chainLog: 20,
|
|
hashLog: 25,
|
|
searchLog: 5,
|
|
minMatch: 4,
|
|
targetLength: 32,
|
|
strategy: STRATEGY_GREEDY,
|
|
};
|
|
(*params).useRowMatchFinder = PS_DISABLE;
|
|
|
|
let mut expected = ZSTD_rust_params_getCParamsInternal(
|
|
(*params).compressionLevel,
|
|
0,
|
|
dict_size,
|
|
ZSTD_RUST_CPM_CREATE_CDICT,
|
|
exclusion_mask,
|
|
);
|
|
expected = ZSTD_rust_params_dedicatedDictSearch_getCParams(expected);
|
|
ZSTD_rust_params_overrideCParams(&mut expected, &(*params).cParams);
|
|
assert_eq!(ZSTD_rust_params_dedicatedDictSearchIsSupported(expected), 1);
|
|
|
|
assert_eq!(
|
|
ZSTD_rust_params_prepareAdvancedCDict(
|
|
params,
|
|
dict_size,
|
|
ldm_default_window_log,
|
|
exclusion_mask,
|
|
),
|
|
0
|
|
);
|
|
assert_eq!((*params).enableDedicatedDictSearch, 1);
|
|
assert_eq!((*params).cParams, expected);
|
|
assert_eq!((*params).useRowMatchFinder, PS_DISABLE);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn prepare_advanced_cdict_falls_back_from_unsupported_dedicated_search() {
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::zeroed();
|
|
let params = storage.as_mut_ptr();
|
|
let dict_size = 32 * 1024;
|
|
let ldm_default_window_log = 27;
|
|
let exclusion_mask = 0;
|
|
|
|
unsafe {
|
|
assert_eq!(ZSTD_CCtxParams_init(params, DEFAULT_CLEVEL), 0);
|
|
(*params).srcSizeHint = 16 * 1024;
|
|
(*params).enableDedicatedDictSearch = 1;
|
|
(*params).cParams = ZSTD_compressionParameters {
|
|
windowLog: 20,
|
|
chainLog: 20,
|
|
hashLog: 25,
|
|
searchLog: 5,
|
|
minMatch: 4,
|
|
targetLength: 32,
|
|
strategy: STRATEGY_BTOPT,
|
|
};
|
|
(*params).useRowMatchFinder = PS_AUTO;
|
|
|
|
let expected = ZSTD_rust_params_getCParamsFromCCtxParams(
|
|
(*params).compressionLevel,
|
|
(*params).srcSizeHint,
|
|
ZSTD_CONTENTSIZE_UNKNOWN,
|
|
dict_size,
|
|
ZSTD_RUST_CPM_CREATE_CDICT,
|
|
(*params).ldmParams.enableLdm,
|
|
ldm_default_window_log,
|
|
(*params).cParams,
|
|
(*params).useRowMatchFinder,
|
|
exclusion_mask,
|
|
);
|
|
let expected_row =
|
|
ZSTD_rust_params_resolveRowMatchFinderMode((*params).useRowMatchFinder, expected);
|
|
|
|
assert_eq!(
|
|
ZSTD_rust_params_prepareAdvancedCDict(
|
|
params,
|
|
dict_size,
|
|
ldm_default_window_log,
|
|
exclusion_mask,
|
|
),
|
|
0
|
|
);
|
|
assert_eq!((*params).enableDedicatedDictSearch, 0);
|
|
assert_eq!((*params).cParams, expected);
|
|
assert_eq!((*params).useRowMatchFinder, expected_row);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn allocation_lifecycle_preserves_default_and_custom_memory_contracts() {
|
|
unsafe extern "C" fn counting_alloc(opaque: *mut c_void, size: usize) -> *mut c_void {
|
|
unsafe {
|
|
(*opaque.cast::<AtomicUsize>()).fetch_add(1, Ordering::Relaxed);
|
|
libc::malloc(size)
|
|
}
|
|
}
|
|
|
|
unsafe extern "C" fn counting_free(opaque: *mut c_void, allocation: *mut c_void) {
|
|
unsafe {
|
|
(*opaque.cast::<AtomicUsize>()).fetch_add(1, Ordering::Relaxed);
|
|
libc::free(allocation);
|
|
}
|
|
}
|
|
|
|
unsafe {
|
|
let params = ZSTD_rust_createCCtxParams(ZSTD_customMem {
|
|
customAlloc: None,
|
|
customFree: None,
|
|
opaque: ptr::null_mut(),
|
|
});
|
|
assert!(!params.is_null());
|
|
assert_eq!((*params).compressionLevel, DEFAULT_CLEVEL);
|
|
assert_eq!((*params).fParams.contentSizeFlag, 1);
|
|
assert_eq!(ZSTD_rust_freeCCtxParams(params), 0);
|
|
assert_eq!(ZSTD_rust_freeCCtxParams(ptr::null_mut()), 0);
|
|
|
|
let calls = AtomicUsize::new(0);
|
|
let custom_mem = ZSTD_customMem {
|
|
customAlloc: Some(counting_alloc),
|
|
customFree: Some(counting_free),
|
|
opaque: (&calls as *const AtomicUsize).cast_mut().cast(),
|
|
};
|
|
let params = ZSTD_rust_createCCtxParams(custom_mem);
|
|
assert!(!params.is_null());
|
|
assert_eq!((*params).customMem.opaque, custom_mem.opaque);
|
|
assert_eq!(calls.load(Ordering::Relaxed), 1);
|
|
assert_eq!(ZSTD_rust_freeCCtxParams(params), 0);
|
|
assert_eq!(calls.load(Ordering::Relaxed), 2);
|
|
|
|
assert!(ZSTD_rust_createCCtxParams(ZSTD_customMem {
|
|
customAlloc: Some(counting_alloc),
|
|
customFree: None,
|
|
opaque: ptr::null_mut(),
|
|
})
|
|
.is_null());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn init_reset_and_set_get_preserve_public_semantics() {
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::uninit();
|
|
let params = storage.as_mut_ptr();
|
|
unsafe {
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_init(ptr::null_mut(), DEFAULT_CLEVEL),
|
|
ERROR(ZstdErrorCode::Generic)
|
|
);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_reset(ptr::null_mut()),
|
|
ERROR(ZstdErrorCode::Generic)
|
|
);
|
|
assert_eq!(ZSTD_CCtxParams_init(params, -7), 0);
|
|
assert_eq!(ZSTD_CCtxParams_setParameter(params, C_FORMAT, 1), 1);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_setParameter(params, C_COMPRESSION_LEVEL, 0),
|
|
3
|
|
);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_setParameter(params, C_COMPRESSION_LEVEL, -7),
|
|
0
|
|
);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_setParameter(params, C_TARGET_C_BLOCK_SIZE, 1),
|
|
1340
|
|
);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_setParameter(params, C_CONTENT_SIZE_FLAG, -1),
|
|
1
|
|
);
|
|
assert_eq!(ZSTD_CCtxParams_setParameter(params, C_DICT_ID_FLAG, 0), 0);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_setParameter(params, C_WINDOW_LOG, 9),
|
|
ERROR(ZstdErrorCode::ParameterOutOfBound)
|
|
);
|
|
|
|
let mut value = -1;
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_getParameter(params, C_COMPRESSION_LEVEL, &mut value),
|
|
0
|
|
);
|
|
assert_eq!(value, -7);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_getParameter(params, C_FORMAT, &mut value),
|
|
0
|
|
);
|
|
assert_eq!(value, 1);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_getParameter(params, C_DICT_ID_FLAG, &mut value),
|
|
0
|
|
);
|
|
assert_eq!(value, 0);
|
|
|
|
assert_eq!(ZSTD_CCtxParams_reset(params), 0);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_getParameter(params, C_COMPRESSION_LEVEL, &mut value),
|
|
0
|
|
);
|
|
assert_eq!(value, DEFAULT_CLEVEL);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_getParameter(params, C_CHECKSUM_FLAG, &mut value),
|
|
0
|
|
);
|
|
assert_eq!(value, 0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn unsupported_and_non_multithreaded_parameters_keep_error_codes() {
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::uninit();
|
|
let params = storage.as_mut_ptr();
|
|
unsafe {
|
|
assert_eq!(ZSTD_CCtxParams_init(params, DEFAULT_CLEVEL), 0);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_setParameter(params, C_NB_WORKERS, 1),
|
|
ERROR(ZstdErrorCode::ParameterUnsupported)
|
|
);
|
|
let mut value = -1;
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_getParameter(params, C_JOB_SIZE, &mut value),
|
|
ERROR(ZstdErrorCode::ParameterUnsupported)
|
|
);
|
|
assert_eq!(
|
|
ZSTD_CCtxParams_setParameter(params, 12345, 0),
|
|
ERROR(ZstdErrorCode::ParameterUnsupported)
|
|
);
|
|
assert!(ERR_isError(ERROR(ZstdErrorCode::ParameterUnsupported)));
|
|
assert_eq!(
|
|
ERR_getErrorCode(ERROR(ZstdErrorCode::ParameterUnsupported)),
|
|
ZstdErrorCode::ParameterUnsupported as i32
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn sequence_producer_registration_sets_and_clears_both_fields() {
|
|
unsafe extern "C" fn producer(
|
|
_state: *mut c_void,
|
|
_out: *mut ZSTD_Sequence,
|
|
_capacity: usize,
|
|
_src: *const c_void,
|
|
_src_size: usize,
|
|
_dict: *const c_void,
|
|
_dict_size: usize,
|
|
_level: c_int,
|
|
_window: usize,
|
|
) -> usize {
|
|
0
|
|
}
|
|
|
|
let mut storage = MaybeUninit::<ZSTD_CCtx_params>::uninit();
|
|
let params = storage.as_mut_ptr();
|
|
let state = std::ptr::dangling_mut::<c_void>();
|
|
unsafe {
|
|
assert_eq!(ZSTD_CCtxParams_init(params, DEFAULT_CLEVEL), 0);
|
|
ZSTD_CCtxParams_registerSequenceProducer(params, state, Some(producer));
|
|
assert_eq!((*params).extSeqProdState, state);
|
|
assert!((*params).extSeqProdFunc.is_some());
|
|
ZSTD_CCtxParams_registerSequenceProducer(params, state, None);
|
|
assert!((*params).extSeqProdState.is_null());
|
|
assert!((*params).extSeqProdFunc.is_none());
|
|
}
|
|
}
|
|
}
|