feat(compress): move end-of-frame orchestration into Rust

Move ZSTD_compressEnd_public's continuation, epilogue placement, pledged-size validation, trace ordering, and output accounting into the Rust compression projection. Keep CCtx-dependent continuation, checksum/epilogue, and trace behavior behind opaque C callbacks with compile-time ABI layout checks.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml compress_end_ -- --test-threads=1
- ulimit -v 41943040; make -B -C lib -j1 lib
- ulimit -v 41943040; make -B -C programs -j1 zstd
- ulimit -v 41943040; make -B -C tests -j1 test-cli-tests
This commit is contained in:
2026-07-19 10:52:09 +02:00
parent 3b2e8155cd
commit bd7fbc81fc
2 changed files with 342 additions and 21 deletions
+76 -21
View File
@@ -232,6 +232,50 @@ typedef char ZSTD_rust_compress_continue_state_layout[
== (sizeof(void*) == 8 ? 96 : 52))
? 1 : -1];
/* Rust owns the end-of-frame orchestration. The callbacks retain the
* private CCtx-dependent continue, epilogue, and trace operations in C. */
typedef size_t (*ZSTD_rust_compressEndContinue_f)(void* context,
void* dst,
size_t dstCapacity,
const void* src,
size_t srcSize,
U32 frame,
U32 lastFrameChunk);
typedef size_t (*ZSTD_rust_compressEndEpilogue_f)(void* context,
void* dst,
size_t dstCapacity);
typedef void (*ZSTD_rust_compressEndTrace_f)(void* context,
size_t extraCSize);
typedef struct {
void* callbackContext;
ZSTD_rust_compressEndContinue_f compressContinue;
ZSTD_rust_compressEndEpilogue_f writeEpilogue;
ZSTD_rust_compressEndTrace_f trace;
unsigned long long* consumedSrcSize;
U64 pledgedSrcSizePlusOne;
int contentSizeFlag;
} ZSTD_rust_compressEndState;
size_t ZSTD_rust_compressEnd(const ZSTD_rust_compressEndState* state,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize);
typedef char ZSTD_rust_compress_end_state_layout[
(offsetof(ZSTD_rust_compressEndState, callbackContext) == 0
&& offsetof(ZSTD_rust_compressEndState, compressContinue)
== sizeof(void*)
&& offsetof(ZSTD_rust_compressEndState, writeEpilogue)
== 2 * sizeof(void*)
&& offsetof(ZSTD_rust_compressEndState, trace)
== 3 * sizeof(void*)
&& offsetof(ZSTD_rust_compressEndState, consumedSrcSize)
== 4 * sizeof(void*)
&& offsetof(ZSTD_rust_compressEndState, pledgedSrcSizePlusOne)
== 5 * sizeof(void*)
&& offsetof(ZSTD_rust_compressEndState, contentSizeFlag)
== 5 * sizeof(void*) + sizeof(U64)
&& sizeof(ZSTD_rust_compressEndState)
== (sizeof(void*) == 8 ? 56 : 32))
? 1 : -1];
/* Rust owns the single-threaded buffered/stable stream state machine. The
* projection contains only stream bookkeeping and callback slots; operations
* which still need the private CCtx layout remain C callbacks. */
@@ -4045,31 +4089,42 @@ void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize)
#endif
}
static size_t ZSTD_rust_compressEnd_continue(
void* context, void* dst, size_t dstCapacity,
const void* src, size_t srcSize, U32 frame, U32 lastFrameChunk)
{
ZSTD_CCtx* const cctx = (ZSTD_CCtx*)context;
return ZSTD_compressContinue_dispatch(
cctx, dst, dstCapacity, src, srcSize,
frame, lastFrameChunk, cctx->blockSizeMax,
0 /* block size already selected */);
}
static size_t ZSTD_rust_compressEnd_writeEpilogue(
void* context, void* dst, size_t dstCapacity)
{
return ZSTD_writeEpilogue((ZSTD_CCtx*)context, dst, dstCapacity);
}
static void ZSTD_rust_compressEnd_trace(void* context, size_t extraCSize)
{
ZSTD_CCtx_trace((ZSTD_CCtx*)context, extraCSize);
}
size_t ZSTD_compressEnd_public(ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize)
{
size_t endResult;
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));
if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */
ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1);
DEBUGLOG(4, "end of frame : controlling src size");
RETURN_ERROR_IF(
cctx->pledgedSrcSizePlusOne != cctx->consumedSrcSize+1,
srcSize_wrong,
"error : pledgedSrcSize = %u, while realSrcSize = %u",
(unsigned)cctx->pledgedSrcSizePlusOne-1,
(unsigned)cctx->consumedSrcSize);
}
ZSTD_CCtx_trace(cctx, endResult);
return cSize + endResult;
ZSTD_rust_compressEndState state;
state.callbackContext = cctx;
state.compressContinue = ZSTD_rust_compressEnd_continue;
state.writeEpilogue = ZSTD_rust_compressEnd_writeEpilogue;
state.trace = ZSTD_rust_compressEnd_trace;
state.consumedSrcSize = &cctx->consumedSrcSize;
state.pledgedSrcSizePlusOne = cctx->pledgedSrcSizePlusOne;
state.contentSizeFlag = cctx->appliedParams.fParams.contentSizeFlag;
return ZSTD_rust_compressEnd(
&state, dst, dstCapacity, src, srcSize);
}
/* NOTE: Must just wrap ZSTD_compressEnd_public() */