refactor(compress): move simple-path eligibility to Rust

Project the private requested-parameter and context state into a fixed scalar ABI record, leaving only C's parameter-selection leaf and private layout access in the shim. Rust now owns the complete-input eligibility predicate, including advanced-state exclusions, LDM sentinel handling, and the final fast/double-fast strategy gate, while preserving the requested-level and INT_MIN results.

Add focused policy tests for fast and double-fast acceptance, representative advanced-state rejection, the accepted LDM sentinel, non-fast strategies, and null state.

Test Plan:

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo +nightly fmt --manifest-path rust/Cargo.toml --all -- --check

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 make -j1

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml --all-targets

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 make -j1 -C tests test
This commit is contained in:
2026-07-21 12:36:26 +02:00
parent 0735a5fd7f
commit 5c6302a3c3
2 changed files with 304 additions and 68 deletions
+118 -68
View File
@@ -1043,6 +1043,64 @@ typedef char ZSTD_rust_compress_stream2_mt_coordinator_state_layout[
== 6 * sizeof(void*))
? 1 : -1];
int ZSTD_rust_simpleCompress2Level(const void* cctx, size_t srcSize);
typedef struct {
int format;
int contentSizeFlag;
int checksumFlag;
int cParamsWindowLog;
int cParamsChainLog;
int cParamsHashLog;
int cParamsSearchLog;
int cParamsMinMatch;
int cParamsTargetLength;
int requestedStrategy;
int forceWindow;
int targetCBlockSizeSet;
int srcSizeHintSet;
int attachDictPref;
int literalCompressionMode;
int nbWorkersSet;
int jobSizeSet;
int overlapLogSet;
int rsyncable;
int ldmEnable;
int ldmHashLogSet;
int ldmBucketSizeLog;
int ldmMinMatchLengthSet;
int ldmHashRateLog;
int ldmWindowLogSet;
int dedicatedDictSearch;
int inBufferMode;
int outBufferMode;
int blockDelimiters;
int validateSequences;
int postBlockSplitter;
int preBlockSplitterLevelSet;
int maxBlockSizeSet;
int maxBlockSizeSetByApi;
int useRowMatchFinder;
int deterministicRefPrefix;
int prefetchCDictTables;
int matchFinderFallback;
int extSeqProdFuncSet;
int searchForExternalRepcodes;
int poolSet;
int collectSequences;
int cdictSet;
int prefixDictSet;
int localDictSet;
int compressionLevel;
int selectedStrategy;
} ZSTD_rust_simpleCompress2LevelState;
int ZSTD_rust_simpleCompress2LevelPolicy(
const ZSTD_rust_simpleCompress2LevelState* state);
typedef char ZSTD_rust_simple_compress2_level_state_layout[
(offsetof(ZSTD_rust_simpleCompress2LevelState, format) == 0
&& offsetof(ZSTD_rust_simpleCompress2LevelState, selectedStrategy)
== 46 * sizeof(int)
&& sizeof(ZSTD_rust_simpleCompress2LevelState)
== 47 * sizeof(int))
? 1 : -1];
typedef int (*ZSTD_rust_simpleCompress2Level_f)(const void* cctx, size_t srcSize);
typedef struct {
int streamStage;
@@ -7236,78 +7294,70 @@ void ZSTD_rust_markSimpleCompression2Complete(void* cctx)
((ZSTD_CCtx*)cctx)->rustSimpleCompress2Completed = 1;
}
/* Return the requested level when the context has only the parameters that
* the Rust frame path currently implements. All other contexts continue
* through ZSTD_compress2_c(), preserving the full C stateful implementation
* while this boundary is migrated incrementally. */
/* Project private context state before Rust evaluates the simple-path policy.
* The parameter-selection leaf remains C-owned because it depends on the
* private compression-parameter tables and implementation details. */
int ZSTD_rust_simpleCompress2Level(const void* opaqueCctx, size_t srcSize)
{
ZSTD_CCtx const* const cctx = (ZSTD_CCtx const*)opaqueCctx;
ZSTD_CCtx_params const* const params = cctx ? &cctx->requestedParams : NULL;
ZSTD_compressionParameters const cParams = params
? ZSTD_getCParams_internal(params->compressionLevel,
srcSize, 0,
ZSTD_cpm_noAttachDict)
: (ZSTD_compressionParameters){ 0 };
if (params == NULL
|| params->format != ZSTD_f_zstd1
|| params->fParams.contentSizeFlag == 0
|| params->fParams.checksumFlag != 0
|| params->cParams.windowLog != 0
|| params->cParams.chainLog != 0
|| params->cParams.hashLog != 0
|| params->cParams.searchLog != 0
|| params->cParams.minMatch != 0
|| params->cParams.targetLength != 0
|| params->cParams.strategy != 0
|| params->forceWindow != 0
|| params->targetCBlockSize != 0
|| params->srcSizeHint != 0
|| params->attachDictPref != ZSTD_dictDefaultAttach
|| params->literalCompressionMode != (ZSTD_ParamSwitch_e)ZSTD_lcm_auto
|| params->nbWorkers != 0
|| params->jobSize != 0
|| params->overlapLog != 0
|| params->rsyncable != 0
|| params->ldmParams.enableLdm != ZSTD_ps_auto
|| params->ldmParams.hashLog != 0
|| (params->ldmParams.bucketSizeLog != 0
&& params->ldmParams.bucketSizeLog != 9999)
|| params->ldmParams.minMatchLength != 0
|| (params->ldmParams.hashRateLog != 0
&& params->ldmParams.hashRateLog != 9999)
|| params->ldmParams.windowLog != 0
|| params->enableDedicatedDictSearch != 0
|| params->inBufferMode != ZSTD_bm_buffered
|| params->outBufferMode != ZSTD_bm_buffered
|| params->blockDelimiters != ZSTD_sf_noBlockDelimiters
|| params->validateSequences != 0
|| params->postBlockSplitter != ZSTD_ps_auto
|| params->preBlockSplitter_level != 0
|| params->maxBlockSize != 0
|| cctx->rustSimpleCompress2MaxBlockSizeSet != 0
|| params->useRowMatchFinder != ZSTD_ps_auto
|| params->deterministicRefPrefix != 0
|| params->prefetchCDictTables != ZSTD_ps_auto
|| params->enableMatchFinderFallback != 0
|| params->extSeqProdFunc != NULL
|| params->searchForExternalRepcodes != ZSTD_ps_auto
|| cctx->pool != NULL
|| cctx->seqCollector.collectSequences != 0
|| cctx->cdict != NULL
|| cctx->prefixDict.dict != NULL
|| cctx->localDict.dict != NULL
|| cctx->localDict.dictBuffer != NULL
|| cctx->localDict.cdict != NULL) {
return (-2147483647 - 1);
if (cctx == NULL) return (-2147483647 - 1);
{
ZSTD_CCtx_params const* const params = &cctx->requestedParams;
ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(
params->compressionLevel, srcSize, 0, ZSTD_cpm_noAttachDict);
ZSTD_rust_simpleCompress2LevelState const state = {
(int)params->format,
(int)params->fParams.contentSizeFlag,
(int)params->fParams.checksumFlag,
(int)params->cParams.windowLog,
(int)params->cParams.chainLog,
(int)params->cParams.hashLog,
(int)params->cParams.searchLog,
(int)params->cParams.minMatch,
(int)params->cParams.targetLength,
(int)params->cParams.strategy,
params->forceWindow != 0,
(int)(params->targetCBlockSize != 0),
(int)(params->srcSizeHint != 0),
(int)params->attachDictPref,
(int)params->literalCompressionMode,
(int)(params->nbWorkers != 0),
(int)(params->jobSize != 0),
(int)(params->overlapLog != 0),
params->rsyncable != 0,
(int)params->ldmParams.enableLdm,
(int)(params->ldmParams.hashLog != 0),
(int)params->ldmParams.bucketSizeLog,
(int)(params->ldmParams.minMatchLength != 0),
(int)params->ldmParams.hashRateLog,
(int)(params->ldmParams.windowLog != 0),
params->enableDedicatedDictSearch != 0,
(int)params->inBufferMode,
(int)params->outBufferMode,
(int)params->blockDelimiters,
params->validateSequences != 0,
(int)params->postBlockSplitter,
(int)(params->preBlockSplitter_level != 0),
(int)(params->maxBlockSize != 0),
(int)(cctx->rustSimpleCompress2MaxBlockSizeSet != 0),
(int)params->useRowMatchFinder,
params->deterministicRefPrefix != 0,
(int)params->prefetchCDictTables,
params->enableMatchFinderFallback != 0,
(int)(params->extSeqProdFunc != NULL),
(int)params->searchForExternalRepcodes,
(int)(cctx->pool != NULL),
cctx->seqCollector.collectSequences != 0,
(int)(cctx->cdict != NULL),
(int)(cctx->prefixDict.dict != NULL),
(int)(cctx->localDict.dict != NULL
|| cctx->localDict.dictBuffer != NULL
|| cctx->localDict.cdict != NULL),
params->compressionLevel,
(int)cParams.strategy
};
return ZSTD_rust_simpleCompress2LevelPolicy(&state);
}
/* The Rust frame leaf currently implements only the fast and double-fast
* match finders. Keep lazy and optimal strategies on the stateful path,
* which owns their strategy-specific match-table lifecycle. */
if (cParams.strategy != ZSTD_fast && cParams.strategy != ZSTD_dfast) {
return (-2147483647 - 1);
}
return params->compressionLevel;
}
/* Return the simple level only while a new stream frame can be initialized.