feat(compress): move continue orchestration into Rust

The public and deprecated block APIs previously entered a C-owned
ZSTD_compressContinue_internal routine, which mixed frame-header sequencing,
window maintenance, block dispatch, and frame-size accounting with the private
CCtx match state.  That made the high-level compression boundary both harder
to test and easy to diverge from the already-ported frame-chunk body.

Add an explicit C/Rust projection containing only scalar state and callback
slots.  Rust now owns stage validation and transitions, header sequencing,
frame-versus-block dispatch, and consumed/produced progression, while C
callbacks retain access to the private match state and context-sensitive block
operations.  The same path now covers compressContinue, deprecated block
compression, and compressEnd without exposing the unstable CCtx layout.

Test Plan:
- `cargo test --manifest-path rust/Cargo.toml --all-targets -- --test-threads=1` -- 473 passed.
- `cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression,decompression,dict-builder,legacy-v01,legacy-v02,legacy-v03,legacy-v04,legacy-v05,legacy-v06,legacy-v07 --all-targets -- --test-threads=1` -- 528 passed.
- Native `test-fuzzer`, `test-zstream`, `test-decodecorpus`, `test-cli-tests`, and `test-zstd` -- passed.
- `cargo clippy` default, benches, and tests for library and CLI -- passed.
This commit is contained in:
2026-07-18 22:40:35 +02:00
parent 5aeef8096e
commit 217cc76e74
2 changed files with 644 additions and 72 deletions
+144 -72
View File
@@ -147,6 +147,59 @@ typedef char ZSTD_rust_frame_chunk_state_layout[
&& sizeof(ZSTD_rust_frameChunkState)
== 12 * sizeof(void*) + sizeof(S64) + 6 * sizeof(int))
? 1 : -1];
/* The high-level continue/block entry points are Rust-owned. This projection
* carries only mutable scalar state and C callbacks; the private CCtx and
* match-state layout never crosses the ABI. */
typedef size_t (*ZSTD_rust_compressContinueHeader_f)(void* context,
void* dst,
size_t dstCapacity);
typedef void (*ZSTD_rust_compressContinueWindow_f)(void* context,
const void* src,
size_t srcSize);
typedef size_t (*ZSTD_rust_compressContinueBlock_f)(void* context,
void* dst,
size_t dstCapacity,
const void* src,
size_t srcSize,
U32 lastFrameChunk);
typedef struct {
void* callbackContext;
ZSTD_rust_compressContinueHeader_f writeFrameHeader;
ZSTD_rust_compressContinueWindow_f updateWindow;
ZSTD_rust_compressContinueWindow_f correctOverflow;
ZSTD_rust_compressContinueBlock_f compressFrameChunk;
ZSTD_rust_compressContinueBlock_f compressBlock;
ZSTD_compressionStage_e* stage;
unsigned long long* consumedSrcSize;
unsigned long long* producedCSize;
U64 pledgedSrcSizePlusOne;
size_t blockSizeMax;
int checkBlockSize;
} ZSTD_rust_compressContinueState;
size_t ZSTD_rust_compressContinue(
const ZSTD_rust_compressContinueState* state,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
U32 frame, U32 lastFrameChunk);
typedef char ZSTD_rust_compress_continue_state_layout[
(offsetof(ZSTD_rust_compressContinueState, callbackContext) == 0
&& offsetof(ZSTD_rust_compressContinueState, writeFrameHeader) == sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, updateWindow) == 2 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, correctOverflow) == 3 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, compressFrameChunk) == 4 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, compressBlock) == 5 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, stage) == 6 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, consumedSrcSize) == 7 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, producedCSize) == 8 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, pledgedSrcSizePlusOne) == 9 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, blockSizeMax)
== 9 * sizeof(void*) + sizeof(U64)
&& offsetof(ZSTD_rust_compressContinueState, checkBlockSize)
== 9 * sizeof(void*) + sizeof(U64) + sizeof(size_t)
&& sizeof(ZSTD_rust_compressContinueState)
== (sizeof(void*) == 8 ? 96 : 52))
? 1 : -1];
/* The target-sized block body only needs this narrow projection of ZSTD_CCtx.
* Matchfinder/window state, sequence-store construction, and outer repeat-mode
* cleanup remain in C. */
@@ -3069,6 +3122,84 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity,
pledgedSrcSize, dictID);
}
static size_t ZSTD_rust_compressContinue_writeFrameHeader(
void* context, void* dst, size_t dstCapacity)
{
ZSTD_CCtx* const cctx = (ZSTD_CCtx*)context;
return ZSTD_writeFrameHeader(
dst, dstCapacity, &cctx->appliedParams,
cctx->pledgedSrcSizePlusOne - 1, cctx->dictID);
}
static void ZSTD_rust_compressContinue_updateWindow(
void* context, const void* src, size_t srcSize)
{
ZSTD_CCtx* const cctx = (ZSTD_CCtx*)context;
ZSTD_MatchState_t* const ms = &cctx->blockState.matchState;
if (!ZSTD_window_update(&ms->window, src, srcSize, ms->forceNonContiguous)) {
ms->forceNonContiguous = 0;
ms->nextToUpdate = ms->window.dictLimit;
}
if (cctx->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable) {
ZSTD_window_update(&cctx->ldmState.window, src, srcSize,
/* forceNonContiguous */ 0);
}
}
static void ZSTD_rust_compressContinue_correctOverflow(
void* context, const void* src, size_t srcSize)
{
ZSTD_CCtx* const cctx = (ZSTD_CCtx*)context;
ZSTD_MatchState_t* const ms = &cctx->blockState.matchState;
ZSTD_overflowCorrectIfNeeded(
ms, &cctx->workspace, &cctx->appliedParams,
src, (const BYTE*)src + srcSize);
}
static size_t ZSTD_rust_compressContinue_frameChunk(
void* context, void* dst, size_t dstCapacity,
const void* src, size_t srcSize, U32 lastFrameChunk)
{
return ZSTD_compress_frameChunk(
(ZSTD_CCtx*)context, dst, dstCapacity, src, srcSize,
lastFrameChunk);
}
static size_t ZSTD_rust_compressContinue_block(
void* context, void* dst, size_t dstCapacity,
const void* src, size_t srcSize, U32 lastFrameChunk)
{
(void)lastFrameChunk;
return ZSTD_compressBlock_internal(
(ZSTD_CCtx*)context, dst, dstCapacity, src, srcSize,
0 /* frame */);
}
static size_t ZSTD_compressContinue_dispatch(
ZSTD_CCtx* cctx, void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
U32 frame, U32 lastFrameChunk,
size_t blockSizeMax, int checkBlockSize)
{
ZSTD_rust_compressContinueState state;
state.callbackContext = cctx;
state.writeFrameHeader = ZSTD_rust_compressContinue_writeFrameHeader;
state.updateWindow = ZSTD_rust_compressContinue_updateWindow;
state.correctOverflow = ZSTD_rust_compressContinue_correctOverflow;
state.compressFrameChunk = ZSTD_rust_compressContinue_frameChunk;
state.compressBlock = ZSTD_rust_compressContinue_block;
state.stage = &cctx->stage;
state.consumedSrcSize = &cctx->consumedSrcSize;
state.producedCSize = &cctx->producedCSize;
state.pledgedSrcSizePlusOne = cctx->pledgedSrcSizePlusOne;
state.blockSizeMax = blockSizeMax;
state.checkBlockSize = checkBlockSize;
return ZSTD_rust_compressContinue(
&state, dst, dstCapacity, src, srcSize, frame, lastFrameChunk);
}
void ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq)
{
assert(cctx->stage == ZSTDcs_init);
@@ -3081,75 +3212,15 @@ void ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq)
}
static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
U32 frame, U32 lastFrameChunk)
{
ZSTD_MatchState_t* const ms = &cctx->blockState.matchState;
size_t fhSize = 0;
DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u, srcSize: %u",
cctx->stage, (unsigned)srcSize);
RETURN_ERROR_IF(cctx->stage==ZSTDcs_created, stage_wrong,
"missing init (ZSTD_compressBegin)");
if (frame && (cctx->stage==ZSTDcs_init)) {
fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, &cctx->appliedParams,
cctx->pledgedSrcSizePlusOne-1, cctx->dictID);
FORWARD_IF_ERROR(fhSize, "ZSTD_writeFrameHeader failed");
assert(fhSize <= dstCapacity);
dstCapacity -= fhSize;
dst = (char*)dst + fhSize;
cctx->stage = ZSTDcs_ongoing;
}
if (!srcSize) return fhSize; /* do not generate an empty block if no input */
if (!ZSTD_window_update(&ms->window, src, srcSize, ms->forceNonContiguous)) {
ms->forceNonContiguous = 0;
ms->nextToUpdate = ms->window.dictLimit;
}
if (cctx->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable) {
ZSTD_window_update(&cctx->ldmState.window, src, srcSize, /* forceNonContiguous */ 0);
}
if (!frame) {
/* overflow check and correction for block mode */
ZSTD_overflowCorrectIfNeeded(
ms, &cctx->workspace, &cctx->appliedParams,
src, (BYTE const*)src + srcSize);
}
DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (unsigned)cctx->blockSizeMax);
{ size_t const cSize = frame ?
ZSTD_compress_frameChunk (cctx, dst, dstCapacity, src, srcSize, lastFrameChunk) :
ZSTD_compressBlock_internal (cctx, dst, dstCapacity, src, srcSize, 0 /* frame */);
int srcSizeWrong;
FORWARD_IF_ERROR(cSize, "%s", frame ? "ZSTD_compress_frameChunk failed" : "ZSTD_compressBlock_internal failed");
srcSizeWrong = ZSTD_rust_updateFrameProgression(
&cctx->consumedSrcSize, &cctx->producedCSize,
cctx->pledgedSrcSizePlusOne, srcSize, cSize, fhSize);
assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0));
if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */
ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1);
RETURN_ERROR_IF(
srcSizeWrong,
srcSize_wrong,
"error : pledgedSrcSize = %u, while realSrcSize >= %u",
(unsigned)cctx->pledgedSrcSizePlusOne-1,
(unsigned)cctx->consumedSrcSize);
}
return cSize + fhSize;
}
}
size_t ZSTD_compressContinue_public(ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize)
{
DEBUGLOG(5, "ZSTD_compressContinue (srcSize=%u)", (unsigned)srcSize);
return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1 /* frame mode */, 0 /* last chunk */);
return ZSTD_compressContinue_dispatch(
cctx, dst, dstCapacity, src, srcSize,
1 /* frame mode */, 0 /* last chunk */,
cctx->blockSizeMax, 0 /* block size already selected */);
}
/* NOTE: Must just wrap ZSTD_compressContinue_public() */
@@ -3177,10 +3248,10 @@ size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx)
size_t ZSTD_compressBlock_deprecated(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize)
{
DEBUGLOG(5, "ZSTD_compressBlock: srcSize = %u", (unsigned)srcSize);
{ size_t const blockSizeMax = ZSTD_getBlockSize_deprecated(cctx);
RETURN_ERROR_IF(srcSize > blockSizeMax, srcSize_wrong, "input is larger than a block"); }
return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0 /* frame mode */, 0 /* last chunk */);
return ZSTD_compressContinue_dispatch(
cctx, dst, dstCapacity, src, srcSize,
0 /* block mode */, 0 /* last chunk */,
ZSTD_getBlockSize_deprecated(cctx), 1 /* validate block size */);
}
/* NOTE: Must just wrap ZSTD_compressBlock_deprecated() */
@@ -3581,10 +3652,11 @@ size_t ZSTD_compressEnd_public(ZSTD_CCtx* cctx,
const void* src, size_t srcSize)
{
size_t endResult;
size_t const cSize = ZSTD_compressContinue_internal(cctx,
dst, dstCapacity, src, srcSize,
1 /* frame mode */, 1 /* last chunk */);
FORWARD_IF_ERROR(cSize, "ZSTD_compressContinue_internal failed");
size_t const cSize = ZSTD_compressContinue_dispatch(
cctx, dst, dstCapacity, src, srcSize,
1 /* frame mode */, 1 /* last chunk */,
cctx->blockSizeMax, 0 /* block size already selected */);
FORWARD_IF_ERROR(cSize, "ZSTD_compressContinue failed");
endResult = ZSTD_writeEpilogue(cctx, (char*)dst + cSize, dstCapacity-cSize);
FORWARD_IF_ERROR(endResult, "ZSTD_writeEpilogue failed");
assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0));