diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 2a10e66ea..7fb61dee1 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -65,6 +65,83 @@ size_t ZSTD_rust_params_maxNbSeq(size_t blockSize, U32 minMatch, int useSequenceProducer); size_t ZSTD_rust_params_resolveMaxBlockSize(size_t maxBlockSize); +/* CCtx parameter state is mirrored by rust/src/zstd_compress_params_api.rs. + * Keep bounds and build-configuration policy in C, and expose only the + * private helpers required by that Rust ABI implementation. */ +size_t ZSTD_rust_cctx_params_clamp_bounds(int param, int* value); +int ZSTD_rust_cctx_params_within_bounds(int param, int value); +int ZSTD_rust_cctx_params_is_multithreaded(void); +int ZSTD_rust_cctx_params_job_size_min(void); + +#define ZSTD_RUST_CCTX_PARAMS_ASSERT(name, condition) \ + typedef char name[(condition) ? 1 : -1] +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_ldm_size, sizeof(ldmParams_t) == 24); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_custom_mem_size, + sizeof(ZSTD_customMem) == (sizeof(void*) == 8 ? 24 : 12)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_size, + sizeof(ZSTD_CCtx_params) == (sizeof(void*) == 8 ? 224 : 180)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_format_offset, + offsetof(ZSTD_CCtx_params, format) == 0); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_cparams_offset, + offsetof(ZSTD_CCtx_params, cParams) == 4); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_fparams_offset, + offsetof(ZSTD_CCtx_params, fParams) == 32); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_level_offset, + offsetof(ZSTD_CCtx_params, compressionLevel) == 44); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_force_window_offset, + offsetof(ZSTD_CCtx_params, forceWindow) == 48); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_target_block_offset, + offsetof(ZSTD_CCtx_params, targetCBlockSize) == (sizeof(void*) == 8 ? 56 : 52)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_src_hint_offset, + offsetof(ZSTD_CCtx_params, srcSizeHint) == (sizeof(void*) == 8 ? 64 : 56)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_attach_offset, + offsetof(ZSTD_CCtx_params, attachDictPref) == (sizeof(void*) == 8 ? 68 : 60)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_literal_mode_offset, + offsetof(ZSTD_CCtx_params, literalCompressionMode) == (sizeof(void*) == 8 ? 72 : 64)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_workers_offset, + offsetof(ZSTD_CCtx_params, nbWorkers) == (sizeof(void*) == 8 ? 76 : 68)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_job_size_offset, + offsetof(ZSTD_CCtx_params, jobSize) == (sizeof(void*) == 8 ? 80 : 72)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_overlap_offset, + offsetof(ZSTD_CCtx_params, overlapLog) == (sizeof(void*) == 8 ? 88 : 76)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_rsyncable_offset, + offsetof(ZSTD_CCtx_params, rsyncable) == (sizeof(void*) == 8 ? 92 : 80)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_ldm_offset, + offsetof(ZSTD_CCtx_params, ldmParams) == (sizeof(void*) == 8 ? 96 : 84)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_dedicated_dict_offset, + offsetof(ZSTD_CCtx_params, enableDedicatedDictSearch) == (sizeof(void*) == 8 ? 120 : 108)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_in_buffer_offset, + offsetof(ZSTD_CCtx_params, inBufferMode) == (sizeof(void*) == 8 ? 124 : 112)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_out_buffer_offset, + offsetof(ZSTD_CCtx_params, outBufferMode) == (sizeof(void*) == 8 ? 128 : 116)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_delimiters_offset, + offsetof(ZSTD_CCtx_params, blockDelimiters) == (sizeof(void*) == 8 ? 132 : 120)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_validate_offset, + offsetof(ZSTD_CCtx_params, validateSequences) == (sizeof(void*) == 8 ? 136 : 124)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_post_splitter_offset, + offsetof(ZSTD_CCtx_params, postBlockSplitter) == (sizeof(void*) == 8 ? 140 : 128)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_pre_splitter_offset, + offsetof(ZSTD_CCtx_params, preBlockSplitter_level) == (sizeof(void*) == 8 ? 144 : 132)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_max_block_offset, + offsetof(ZSTD_CCtx_params, maxBlockSize) == (sizeof(void*) == 8 ? 152 : 136)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_row_matcher_offset, + offsetof(ZSTD_CCtx_params, useRowMatchFinder) == (sizeof(void*) == 8 ? 160 : 140)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_deterministic_prefix_offset, + offsetof(ZSTD_CCtx_params, deterministicRefPrefix) == (sizeof(void*) == 8 ? 164 : 144)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_custom_mem_offset, + offsetof(ZSTD_CCtx_params, customMem) == (sizeof(void*) == 8 ? 168 : 148)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_prefetch_offset, + offsetof(ZSTD_CCtx_params, prefetchCDictTables) == (sizeof(void*) == 8 ? 192 : 160)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_fallback_offset, + offsetof(ZSTD_CCtx_params, enableMatchFinderFallback) == (sizeof(void*) == 8 ? 196 : 164)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_state_offset, + offsetof(ZSTD_CCtx_params, extSeqProdState) == (sizeof(void*) == 8 ? 200 : 168)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_function_offset, + offsetof(ZSTD_CCtx_params, extSeqProdFunc) == (sizeof(void*) == 8 ? 208 : 172)); +ZSTD_RUST_CCTX_PARAMS_ASSERT(ZSTD_rust_cctx_params_repcode_offset, + offsetof(ZSTD_CCtx_params, searchForExternalRepcodes) == (sizeof(void*) == 8 ? 216 : 176)); +#undef ZSTD_RUST_CCTX_PARAMS_ASSERT + typedef struct { U32 hashLog3Max; size_t matchTSize; @@ -461,19 +538,6 @@ size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params) return 0; } -size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params) -{ - return ZSTD_CCtxParams_init(params, ZSTD_CLEVEL_DEFAULT); -} - -size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) { - RETURN_ERROR_IF(!cctxParams, GENERIC, "NULL pointer!"); - ZSTD_memset(cctxParams, 0, sizeof(*cctxParams)); - cctxParams->compressionLevel = compressionLevel; - cctxParams->fParams.contentSizeFlag = 1; - return 0; -} - #define ZSTD_NO_CLEVEL 0 /** @@ -731,6 +795,34 @@ static size_t ZSTD_cParam_clampBounds(ZSTD_cParameter cParam, int* value) return 0; } +size_t ZSTD_rust_cctx_params_clamp_bounds(int param, int* value) +{ + return ZSTD_cParam_clampBounds((ZSTD_cParameter)param, value); +} + +int ZSTD_rust_cctx_params_within_bounds(int param, int value) +{ + return ZSTD_cParam_withinBounds((ZSTD_cParameter)param, value); +} + +int ZSTD_rust_cctx_params_is_multithreaded(void) +{ +#ifdef ZSTD_MULTITHREAD + return 1; +#else + return 0; +#endif +} + +int ZSTD_rust_cctx_params_job_size_min(void) +{ +#ifdef ZSTD_MULTITHREAD + return ZSTDMT_JOBSIZE_MIN; +#else + return 0; +#endif +} + #define BOUNDCHECK(cParam, val) \ do { \ RETURN_ERROR_IF(!ZSTD_cParam_withinBounds(cParam,val), \ @@ -850,407 +942,11 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value) return ZSTD_CCtxParams_setParameter(&cctx->requestedParams, param, value); } -size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams, - ZSTD_cParameter param, int value) -{ - DEBUGLOG(4, "ZSTD_CCtxParams_setParameter (%i, %i)", (int)param, value); - switch(param) - { - case ZSTD_c_format : - BOUNDCHECK(ZSTD_c_format, value); - CCtxParams->format = (ZSTD_format_e)value; - return (size_t)CCtxParams->format; - - case ZSTD_c_compressionLevel : { - FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value), ""); - if (value == 0) - CCtxParams->compressionLevel = ZSTD_CLEVEL_DEFAULT; /* 0 == default */ - else - CCtxParams->compressionLevel = value; - if (CCtxParams->compressionLevel >= 0) return (size_t)CCtxParams->compressionLevel; - return 0; /* return type (size_t) cannot represent negative values */ - } - - case ZSTD_c_windowLog : - if (value!=0) /* 0 => use default */ - BOUNDCHECK(ZSTD_c_windowLog, value); - CCtxParams->cParams.windowLog = (U32)value; - return CCtxParams->cParams.windowLog; - - case ZSTD_c_hashLog : - if (value!=0) /* 0 => use default */ - BOUNDCHECK(ZSTD_c_hashLog, value); - CCtxParams->cParams.hashLog = (U32)value; - return CCtxParams->cParams.hashLog; - - case ZSTD_c_chainLog : - if (value!=0) /* 0 => use default */ - BOUNDCHECK(ZSTD_c_chainLog, value); - CCtxParams->cParams.chainLog = (U32)value; - return CCtxParams->cParams.chainLog; - - case ZSTD_c_searchLog : - if (value!=0) /* 0 => use default */ - BOUNDCHECK(ZSTD_c_searchLog, value); - CCtxParams->cParams.searchLog = (U32)value; - return (size_t)value; - - case ZSTD_c_minMatch : - if (value!=0) /* 0 => use default */ - BOUNDCHECK(ZSTD_c_minMatch, value); - CCtxParams->cParams.minMatch = (U32)value; - return CCtxParams->cParams.minMatch; - - case ZSTD_c_targetLength : - BOUNDCHECK(ZSTD_c_targetLength, value); - CCtxParams->cParams.targetLength = (U32)value; - return CCtxParams->cParams.targetLength; - - case ZSTD_c_strategy : - if (value!=0) /* 0 => use default */ - BOUNDCHECK(ZSTD_c_strategy, value); - CCtxParams->cParams.strategy = (ZSTD_strategy)value; - return (size_t)CCtxParams->cParams.strategy; - - case ZSTD_c_contentSizeFlag : - /* Content size written in frame header _when known_ (default:1) */ - DEBUGLOG(4, "set content size flag = %u", (value!=0)); - CCtxParams->fParams.contentSizeFlag = value != 0; - return (size_t)CCtxParams->fParams.contentSizeFlag; - - case ZSTD_c_checksumFlag : - /* A 32-bits content checksum will be calculated and written at end of frame (default:0) */ - CCtxParams->fParams.checksumFlag = value != 0; - return (size_t)CCtxParams->fParams.checksumFlag; - - case ZSTD_c_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */ - DEBUGLOG(4, "set dictIDFlag = %u", (value!=0)); - CCtxParams->fParams.noDictIDFlag = !value; - return !CCtxParams->fParams.noDictIDFlag; - - case ZSTD_c_forceMaxWindow : - CCtxParams->forceWindow = (value != 0); - return (size_t)CCtxParams->forceWindow; - - case ZSTD_c_forceAttachDict : { - const ZSTD_dictAttachPref_e pref = (ZSTD_dictAttachPref_e)value; - BOUNDCHECK(ZSTD_c_forceAttachDict, (int)pref); - CCtxParams->attachDictPref = pref; - return CCtxParams->attachDictPref; - } - - case ZSTD_c_literalCompressionMode : { - const ZSTD_ParamSwitch_e lcm = (ZSTD_ParamSwitch_e)value; - BOUNDCHECK(ZSTD_c_literalCompressionMode, (int)lcm); - CCtxParams->literalCompressionMode = lcm; - return CCtxParams->literalCompressionMode; - } - - case ZSTD_c_nbWorkers : -#ifndef ZSTD_MULTITHREAD - RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); - return 0; -#else - FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value), ""); - CCtxParams->nbWorkers = value; - return (size_t)(CCtxParams->nbWorkers); -#endif - - case ZSTD_c_jobSize : -#ifndef ZSTD_MULTITHREAD - RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); - return 0; -#else - /* Adjust to the minimum non-default value. */ - if (value != 0 && value < ZSTDMT_JOBSIZE_MIN) - value = ZSTDMT_JOBSIZE_MIN; - FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value), ""); - assert(value >= 0); - CCtxParams->jobSize = (size_t)value; - return CCtxParams->jobSize; -#endif - - case ZSTD_c_overlapLog : -#ifndef ZSTD_MULTITHREAD - RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); - return 0; -#else - FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(ZSTD_c_overlapLog, &value), ""); - CCtxParams->overlapLog = value; - return (size_t)CCtxParams->overlapLog; -#endif - - case ZSTD_c_rsyncable : -#ifndef ZSTD_MULTITHREAD - RETURN_ERROR_IF(value!=0, parameter_unsupported, "not compiled with multithreading"); - return 0; -#else - FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(ZSTD_c_overlapLog, &value), ""); - CCtxParams->rsyncable = value; - return (size_t)CCtxParams->rsyncable; -#endif - - case ZSTD_c_enableDedicatedDictSearch : - CCtxParams->enableDedicatedDictSearch = (value!=0); - return (size_t)CCtxParams->enableDedicatedDictSearch; - - case ZSTD_c_enableLongDistanceMatching : - BOUNDCHECK(ZSTD_c_enableLongDistanceMatching, value); - CCtxParams->ldmParams.enableLdm = (ZSTD_ParamSwitch_e)value; - return CCtxParams->ldmParams.enableLdm; - - case ZSTD_c_ldmHashLog : - if (value!=0) /* 0 ==> auto */ - BOUNDCHECK(ZSTD_c_ldmHashLog, value); - CCtxParams->ldmParams.hashLog = (U32)value; - return CCtxParams->ldmParams.hashLog; - - case ZSTD_c_ldmMinMatch : - if (value!=0) /* 0 ==> default */ - BOUNDCHECK(ZSTD_c_ldmMinMatch, value); - CCtxParams->ldmParams.minMatchLength = (U32)value; - return CCtxParams->ldmParams.minMatchLength; - - case ZSTD_c_ldmBucketSizeLog : - if (value!=0) /* 0 ==> default */ - BOUNDCHECK(ZSTD_c_ldmBucketSizeLog, value); - CCtxParams->ldmParams.bucketSizeLog = (U32)value; - return CCtxParams->ldmParams.bucketSizeLog; - - case ZSTD_c_ldmHashRateLog : - if (value!=0) /* 0 ==> default */ - BOUNDCHECK(ZSTD_c_ldmHashRateLog, value); - CCtxParams->ldmParams.hashRateLog = (U32)value; - return CCtxParams->ldmParams.hashRateLog; - - case ZSTD_c_targetCBlockSize : - if (value!=0) { /* 0 ==> default */ - value = MAX(value, ZSTD_TARGETCBLOCKSIZE_MIN); - BOUNDCHECK(ZSTD_c_targetCBlockSize, value); - } - CCtxParams->targetCBlockSize = (U32)value; - return CCtxParams->targetCBlockSize; - - case ZSTD_c_srcSizeHint : - if (value!=0) /* 0 ==> default */ - BOUNDCHECK(ZSTD_c_srcSizeHint, value); - CCtxParams->srcSizeHint = value; - return (size_t)CCtxParams->srcSizeHint; - - case ZSTD_c_stableInBuffer: - BOUNDCHECK(ZSTD_c_stableInBuffer, value); - CCtxParams->inBufferMode = (ZSTD_bufferMode_e)value; - return CCtxParams->inBufferMode; - - case ZSTD_c_stableOutBuffer: - BOUNDCHECK(ZSTD_c_stableOutBuffer, value); - CCtxParams->outBufferMode = (ZSTD_bufferMode_e)value; - return CCtxParams->outBufferMode; - - case ZSTD_c_blockDelimiters: - BOUNDCHECK(ZSTD_c_blockDelimiters, value); - CCtxParams->blockDelimiters = (ZSTD_SequenceFormat_e)value; - return CCtxParams->blockDelimiters; - - case ZSTD_c_validateSequences: - BOUNDCHECK(ZSTD_c_validateSequences, value); - CCtxParams->validateSequences = value; - return (size_t)CCtxParams->validateSequences; - - case ZSTD_c_splitAfterSequences: - BOUNDCHECK(ZSTD_c_splitAfterSequences, value); - CCtxParams->postBlockSplitter = (ZSTD_ParamSwitch_e)value; - return CCtxParams->postBlockSplitter; - - case ZSTD_c_blockSplitterLevel: - BOUNDCHECK(ZSTD_c_blockSplitterLevel, value); - CCtxParams->preBlockSplitter_level = value; - return (size_t)CCtxParams->preBlockSplitter_level; - - case ZSTD_c_useRowMatchFinder: - BOUNDCHECK(ZSTD_c_useRowMatchFinder, value); - CCtxParams->useRowMatchFinder = (ZSTD_ParamSwitch_e)value; - return CCtxParams->useRowMatchFinder; - - case ZSTD_c_deterministicRefPrefix: - BOUNDCHECK(ZSTD_c_deterministicRefPrefix, value); - CCtxParams->deterministicRefPrefix = !!value; - return (size_t)CCtxParams->deterministicRefPrefix; - - case ZSTD_c_prefetchCDictTables: - BOUNDCHECK(ZSTD_c_prefetchCDictTables, value); - CCtxParams->prefetchCDictTables = (ZSTD_ParamSwitch_e)value; - return CCtxParams->prefetchCDictTables; - - case ZSTD_c_enableSeqProducerFallback: - BOUNDCHECK(ZSTD_c_enableSeqProducerFallback, value); - CCtxParams->enableMatchFinderFallback = value; - return (size_t)CCtxParams->enableMatchFinderFallback; - - case ZSTD_c_maxBlockSize: - if (value!=0) /* 0 ==> default */ - BOUNDCHECK(ZSTD_c_maxBlockSize, value); - assert(value>=0); - CCtxParams->maxBlockSize = (size_t)value; - return CCtxParams->maxBlockSize; - - case ZSTD_c_repcodeResolution: - BOUNDCHECK(ZSTD_c_repcodeResolution, value); - CCtxParams->searchForExternalRepcodes = (ZSTD_ParamSwitch_e)value; - return CCtxParams->searchForExternalRepcodes; - - default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); - } -} - size_t ZSTD_CCtx_getParameter(ZSTD_CCtx const* cctx, ZSTD_cParameter param, int* value) { return ZSTD_CCtxParams_getParameter(&cctx->requestedParams, param, value); } -size_t ZSTD_CCtxParams_getParameter( - ZSTD_CCtx_params const* CCtxParams, ZSTD_cParameter param, int* value) -{ - switch(param) - { - case ZSTD_c_format : - *value = (int)CCtxParams->format; - break; - case ZSTD_c_compressionLevel : - *value = CCtxParams->compressionLevel; - break; - case ZSTD_c_windowLog : - *value = (int)CCtxParams->cParams.windowLog; - break; - case ZSTD_c_hashLog : - *value = (int)CCtxParams->cParams.hashLog; - break; - case ZSTD_c_chainLog : - *value = (int)CCtxParams->cParams.chainLog; - break; - case ZSTD_c_searchLog : - *value = (int)CCtxParams->cParams.searchLog; - break; - case ZSTD_c_minMatch : - *value = (int)CCtxParams->cParams.minMatch; - break; - case ZSTD_c_targetLength : - *value = (int)CCtxParams->cParams.targetLength; - break; - case ZSTD_c_strategy : - *value = (int)CCtxParams->cParams.strategy; - break; - case ZSTD_c_contentSizeFlag : - *value = CCtxParams->fParams.contentSizeFlag; - break; - case ZSTD_c_checksumFlag : - *value = CCtxParams->fParams.checksumFlag; - break; - case ZSTD_c_dictIDFlag : - *value = !CCtxParams->fParams.noDictIDFlag; - break; - case ZSTD_c_forceMaxWindow : - *value = CCtxParams->forceWindow; - break; - case ZSTD_c_forceAttachDict : - *value = (int)CCtxParams->attachDictPref; - break; - case ZSTD_c_literalCompressionMode : - *value = (int)CCtxParams->literalCompressionMode; - break; - case ZSTD_c_nbWorkers : -#ifndef ZSTD_MULTITHREAD - assert(CCtxParams->nbWorkers == 0); -#endif - *value = CCtxParams->nbWorkers; - break; - case ZSTD_c_jobSize : -#ifndef ZSTD_MULTITHREAD - RETURN_ERROR(parameter_unsupported, "not compiled with multithreading"); -#else - assert(CCtxParams->jobSize <= INT_MAX); - *value = (int)CCtxParams->jobSize; - break; -#endif - case ZSTD_c_overlapLog : -#ifndef ZSTD_MULTITHREAD - RETURN_ERROR(parameter_unsupported, "not compiled with multithreading"); -#else - *value = CCtxParams->overlapLog; - break; -#endif - case ZSTD_c_rsyncable : -#ifndef ZSTD_MULTITHREAD - RETURN_ERROR(parameter_unsupported, "not compiled with multithreading"); -#else - *value = CCtxParams->rsyncable; - break; -#endif - case ZSTD_c_enableDedicatedDictSearch : - *value = CCtxParams->enableDedicatedDictSearch; - break; - case ZSTD_c_enableLongDistanceMatching : - *value = (int)CCtxParams->ldmParams.enableLdm; - break; - case ZSTD_c_ldmHashLog : - *value = (int)CCtxParams->ldmParams.hashLog; - break; - case ZSTD_c_ldmMinMatch : - *value = (int)CCtxParams->ldmParams.minMatchLength; - break; - case ZSTD_c_ldmBucketSizeLog : - *value = (int)CCtxParams->ldmParams.bucketSizeLog; - break; - case ZSTD_c_ldmHashRateLog : - *value = (int)CCtxParams->ldmParams.hashRateLog; - break; - case ZSTD_c_targetCBlockSize : - *value = (int)CCtxParams->targetCBlockSize; - break; - case ZSTD_c_srcSizeHint : - *value = (int)CCtxParams->srcSizeHint; - break; - case ZSTD_c_stableInBuffer : - *value = (int)CCtxParams->inBufferMode; - break; - case ZSTD_c_stableOutBuffer : - *value = (int)CCtxParams->outBufferMode; - break; - case ZSTD_c_blockDelimiters : - *value = (int)CCtxParams->blockDelimiters; - break; - case ZSTD_c_validateSequences : - *value = (int)CCtxParams->validateSequences; - break; - case ZSTD_c_splitAfterSequences : - *value = (int)CCtxParams->postBlockSplitter; - break; - case ZSTD_c_blockSplitterLevel : - *value = CCtxParams->preBlockSplitter_level; - break; - case ZSTD_c_useRowMatchFinder : - *value = (int)CCtxParams->useRowMatchFinder; - break; - case ZSTD_c_deterministicRefPrefix: - *value = (int)CCtxParams->deterministicRefPrefix; - break; - case ZSTD_c_prefetchCDictTables: - *value = (int)CCtxParams->prefetchCDictTables; - break; - case ZSTD_c_enableSeqProducerFallback: - *value = CCtxParams->enableMatchFinderFallback; - break; - case ZSTD_c_maxBlockSize: - *value = (int)CCtxParams->maxBlockSize; - break; - case ZSTD_c_repcodeResolution: - *value = (int)CCtxParams->searchForExternalRepcodes; - break; - default: RETURN_ERROR(parameter_unsupported, "unknown parameter"); - } - return 0; -} - /** ZSTD_CCtx_setParametersUsingCCtxParams() : * just applies `params` into `cctx` * no action is performed, parameters are merely stored. @@ -7053,18 +6749,3 @@ void ZSTD_registerSequenceProducer( &zc->requestedParams, extSeqProdState, extSeqProdFunc ); } - -void ZSTD_CCtxParams_registerSequenceProducer( - ZSTD_CCtx_params* params, - void* extSeqProdState, - ZSTD_sequenceProducer_F extSeqProdFunc) -{ - assert(params != NULL); - if (extSeqProdFunc != NULL) { - params->extSeqProdFunc = extSeqProdFunc; - params->extSeqProdState = extSeqProdState; - } else { - params->extSeqProdFunc = NULL; - params->extSeqProdState = NULL; - } -} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 572424892..5acbad404 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -42,6 +42,8 @@ pub mod zstd_compress_literals; #[cfg(feature = "compression")] pub mod zstd_compress_params; #[cfg(feature = "compression")] +pub mod zstd_compress_params_api; +#[cfg(feature = "compression")] pub mod zstd_compress_sequences; #[cfg(feature = "compression")] pub mod zstd_compress_stats; diff --git a/rust/src/zstd_compress_params_api.rs b/rust/src/zstd_compress_params_api.rs new file mode 100644 index 000000000..d03f790cc --- /dev/null +++ b/rust/src/zstd_compress_params_api.rs @@ -0,0 +1,1000 @@ +#![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. +//! +//! Allocation, advanced initialization, bounds calculation, and context +//! policy remain in `zstd_compress.c`. This module owns only the object +//! reset/initialization, parameter set/get, and sequence-producer registration +//! entry points. + +use crate::errors::{ZstdErrorCode, ERROR}; +#[cfg(test)] +use crate::zstd_compress_params::ZSTD_bounds; +use crate::zstd_compress_params::{ZSTD_compressionParameters, ZSTD_frameParameters}; +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)] +struct ZSTD_customMem { + customAlloc: Option, + customFree: Option, + 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, + + searchForExternalRepcodes: c_int, +} + +const DEFAULT_CLEVEL: c_int = 3; +#[cfg(test)] +const PS_AUTO: c_int = 0; +#[cfg(test)] +const PS_DISABLE: c_int = 2; + +#[cfg(test)] +const BLOCKSIZE_MAX: usize = 1 << 17; +#[cfg(test)] +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; + +#[cfg(not(test))] +unsafe extern "C" { + fn ZSTD_rust_cctx_params_clamp_bounds(param: c_int, value: *mut c_int) -> usize; + fn ZSTD_rust_cctx_params_within_bounds(param: c_int, value: c_int) -> c_int; + fn ZSTD_rust_cctx_params_is_multithreaded() -> c_int; + fn ZSTD_rust_cctx_params_job_size_min() -> c_int; +} + +#[cfg(test)] +fn fallback_bounds(param: c_int) -> 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 crate::zstd_compress_params::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 | C_JOB_SIZE | C_OVERLAP_LOG => (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 => (6, 30), + C_LDM_MIN_MATCH => (4, 4096), + C_LDM_BUCKET_SIZE_LOG => (1, 8), + C_LDM_HASH_RATE_LOG => (0, if size_of::() == 4 { 24 } else { 25 }), + 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 within_bounds(param: c_int, value: c_int) -> bool { + #[cfg(not(test))] + { + unsafe { ZSTD_rust_cctx_params_within_bounds(param, value) != 0 } + } + #[cfg(test)] + { + let bounds = fallback_bounds(param); + bounds.error == 0 && value >= bounds.lowerBound && value <= bounds.upperBound + } +} + +#[inline] +unsafe fn clamp_bounds(param: c_int, value: &mut c_int) -> usize { + #[cfg(not(test))] + { + unsafe { ZSTD_rust_cctx_params_clamp_bounds(param, value) } + } + #[cfg(test)] + { + let bounds = fallback_bounds(param); + if bounds.error != 0 { + return bounds.error; + } + *value = (*value).clamp(bounds.lowerBound, bounds.upperBound); + 0 + } +} + +#[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::(), 0, size_of::()); + (*params).compressionLevel = compression_level; + (*params).fParams.contentSizeFlag = 1; + } + 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, +) { + 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}; + + #[test] + fn private_parameter_mirror_matches_the_c_layout() { + let pointer_size = size_of::(); + assert_eq!(align_of::(), pointer_size); + assert_eq!(size_of::(), 28); + assert_eq!(size_of::(), 12); + assert_eq!(size_of::(), 24); + assert_eq!( + size_of::(), + 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::(), + if pointer_size == 8 { 224 } else { 180 } + ); + } + + #[test] + fn init_reset_and_set_get_preserve_public_semantics() { + let mut storage = MaybeUninit::::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::::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::::uninit(); + let params = storage.as_mut_ptr(); + let state = std::ptr::dangling_mut::(); + 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()); + } + } +}