feat(compress): move CDict attachment into Rust

Project the prepared CDict window extent and destination match-state fields
through an explicit ABI state so Rust owns the post-reset attachment
operation. Preserve the original empty-dictionary and already-advanced
window branches, including window-limit clearing and loaded-dictionary
referential updates, while keeping private C pointer arithmetic and layout
ownership in the C adapter.

Test Plan:
- CARGO_BUILD_JOBS=1 cargo test --lib zstd_compress_dictionary::tests::reset_cctx_by_attaching_cdict -- --nocapture
- CARGO_BUILD_JOBS=1 cargo clippy --all-targets -- -D warnings
- CARGO_BUILD_JOBS=1 cargo test
- CARGO_BUILD_JOBS=1 make -j1
- CARGO_BUILD_JOBS=1 make -j1 -C tests test-zstream ZSTREAM_TESTTIME=-T2s
- CARGO_BUILD_JOBS=1 make -j1 -C tests test-fuzzer FUZZERTEST=-T3s FUZZER_FLAGS=--no-big-tests

All commands were run serially with a 40 GiB virtual-memory cap.
This commit is contained in:
2026-07-19 23:49:43 +02:00
parent f758c41c5d
commit d8e8683353
2 changed files with 154 additions and 58 deletions
+28 -35
View File
@@ -2563,15 +2563,21 @@ typedef char ZSTD_rust_reset_cctx_by_copying_cdict_state_layout[
typedef size_t (*ZSTD_rust_resetCCtxByAttachingCDictReset_f)(
void* context, const void* cdict, const void* params,
U64 pledgedSrcSize, int zbuff);
typedef void (*ZSTD_rust_resetCCtxByAttachingCDictAttach_f)(
void* context, const void* cdict);
typedef struct {
void* callbackContext;
const void* cdict;
const void* params;
U64 pledgedSrcSize;
ZSTD_rust_resetCCtxByAttachingCDictReset_f reset;
ZSTD_rust_resetCCtxByAttachingCDictAttach_f attach;
const ZSTD_MatchState_t* sourceMatchState;
const U32* sourceCDictEnd;
const U32* sourceCDictLen;
const ZSTD_MatchState_t** destinationDictMatchState;
const BYTE** destinationWindowNextSrc;
const BYTE* const* destinationWindowBase;
U32* destinationWindowLowLimit;
U32* destinationWindowDictLimit;
U32* destinationLoadedDictEnd;
U32* destinationDictID;
const U32* sourceDictID;
size_t* destinationDictContentSize;
@@ -2593,7 +2599,7 @@ typedef char ZSTD_rust_reset_cctx_by_attaching_cdict_state_layout[
&& offsetof(ZSTD_rust_resetCCtxByAttachingCDictState, reset)
== 3 * sizeof(void*) + sizeof(U64)
&& offsetof(ZSTD_rust_resetCCtxByAttachingCDictState, zbuff)
== 3 * sizeof(void*) + sizeof(U64) + 8 * sizeof(void*)
== 3 * sizeof(void*) + sizeof(U64) + 16 * sizeof(void*)
&& sizeof(ZSTD_rust_resetCCtxByAttachingCDictState)
== ((offsetof(ZSTD_rust_resetCCtxByAttachingCDictState, zbuff)
+ sizeof(int) + sizeof(void*) - 1) / sizeof(void*))
@@ -4505,36 +4511,6 @@ static size_t ZSTD_rust_resetCCtx_byAttachingCDict_reset(
return 0;
}
static void ZSTD_rust_resetCCtx_byAttachingCDict_attach(
void* context, const void* cdictOpaque)
{
ZSTD_CCtx* const cctx = (ZSTD_CCtx*)context;
const ZSTD_CDict* const cdict = (const ZSTD_CDict*)cdictOpaque;
U32 const cdictEnd = (U32)(cdict->matchState.window.nextSrc
- cdict->matchState.window.base);
U32 const cdictLen = cdictEnd - cdict->matchState.window.dictLimit;
if (cdictLen == 0) {
DEBUGLOG(4, "skipping attaching empty dictionary");
return;
}
DEBUGLOG(4, "attaching dictionary into context");
cctx->blockState.matchState.dictMatchState = &cdict->matchState;
if (cctx->blockState.matchState.window.dictLimit < cdictEnd) {
cctx->blockState.matchState.window.nextSrc =
cctx->blockState.matchState.window.base + cdictEnd;
ZSTD_rust_windowClear(
(size_t)(cctx->blockState.matchState.window.nextSrc
- cctx->blockState.matchState.window.base),
&cctx->blockState.matchState.window.lowLimit,
&cctx->blockState.matchState.window.dictLimit);
}
/* loadedDictEnd is expressed within the active context referential. */
cctx->blockState.matchState.loadedDictEnd =
cctx->blockState.matchState.window.dictLimit;
}
static size_t
ZSTD_resetCCtx_byAttachingCDict(ZSTD_CCtx* cctx,
const ZSTD_CDict* cdict,
@@ -4542,13 +4518,30 @@ ZSTD_resetCCtx_byAttachingCDict(ZSTD_CCtx* cctx,
U64 pledgedSrcSize,
ZSTD_buffered_policy_e zbuff)
{
U32 const cdictEnd = (U32)(cdict->matchState.window.nextSrc
- cdict->matchState.window.base);
U32 const cdictLen = cdictEnd - cdict->matchState.window.dictLimit;
ZSTD_rust_resetCCtxByAttachingCDictState state;
state.callbackContext = cctx;
state.cdict = cdict;
state.params = &params;
state.pledgedSrcSize = pledgedSrcSize;
state.reset = ZSTD_rust_resetCCtx_byAttachingCDict_reset;
state.attach = ZSTD_rust_resetCCtx_byAttachingCDict_attach;
state.sourceMatchState = &cdict->matchState;
state.sourceCDictEnd = &cdictEnd;
state.sourceCDictLen = &cdictLen;
state.destinationDictMatchState =
&cctx->blockState.matchState.dictMatchState;
state.destinationWindowNextSrc =
&cctx->blockState.matchState.window.nextSrc;
state.destinationWindowBase =
&cctx->blockState.matchState.window.base;
state.destinationWindowLowLimit =
&cctx->blockState.matchState.window.lowLimit;
state.destinationWindowDictLimit =
&cctx->blockState.matchState.window.dictLimit;
state.destinationLoadedDictEnd =
&cctx->blockState.matchState.loadedDictEnd;
state.destinationDictID = &cctx->dictID;
state.sourceDictID = &cdict->dictID;
state.destinationDictContentSize = &cctx->dictContentSize;