fix(mt): preserve range state across job callback

The first MT state-publication fix made the C count current before job
construction, but the Rust result still unconditionally copied its local
pre-job fill count back into `mtctx` after the callback returned.  Preparing a
job clears `mtctx->inBuff.buffer` and `mtctx->inBuff.filled`, so the next stream
pass then observed a consumed range as if it were still active and returned a
generic error in the multi-file CLI test.

Keep the C-cleared state whenever job preparation has detached the active
range.  Rust's filled count is published only while the C range remains active,
which covers the blocked/table-full path where no job reset occurred.  The
callback still receives the current count before preparation, and the C90
projection declaration is kept before executable statements.

Test Plan:
- `cc -fsyntax-only -Werror=incompatible-pointer-types -Ilib -Ilib/common -Ilib/compress -Ilib/decompress -Ilib/dict -Ilib/legacy lib/compress/zstdmt_compress.c` -- passed
- `git diff --check` and `git diff --cached --check` -- passed
- The capped upstream suite reproduced a generic error in the multi-file CLI
  path before this correction; the full rerun is pending
This commit is contained in:
2026-07-20 15:17:11 +02:00
parent ab9e2243ed
commit a57e0f83e1
+7 -2
View File
@@ -2723,12 +2723,13 @@ static size_t ZSTDMT_createCompressionJob(void* opaque, size_t srcSize, unsigned
{ {
ZSTDMT_CCtx* const mtctx = (ZSTDMT_CCtx*)opaque; ZSTDMT_CCtx* const mtctx = (ZSTDMT_CCtx*)opaque;
ZSTD_EndDirective const endOp = (ZSTD_EndDirective)end; ZSTD_EndDirective const endOp = (ZSTD_EndDirective)end;
ZSTDMT_RustCreateJobProjection projection;
/* Rust fills the reusable input range in its local projection before /* Rust fills the reusable input range in its local projection before
* calling this C-owned job-construction callback. Publish that count * calling this C-owned job-construction callback. Publish that count
* before the callback prepares a job and checks the C context. */ * before the callback prepares a job and checks the C context. */
assert(srcSize <= mtctx->inBuff.buffer.capacity); assert(srcSize <= mtctx->inBuff.buffer.capacity);
mtctx->inBuff.filled = srcSize; mtctx->inBuff.filled = srcSize;
ZSTDMT_RustCreateJobProjection const projection = { projection = (ZSTDMT_RustCreateJobProjection){
mtctx->doneJobID, mtctx->doneJobID,
mtctx->nextJobID, mtctx->nextJobID,
mtctx->jobIDMask, mtctx->jobIDMask,
@@ -3099,7 +3100,11 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
(U32)endOp, (U32)(input->size - input->pos)); (U32)endOp, (U32)(input->size - input->pos));
assert(output->pos <= output->size); assert(output->pos <= output->size);
assert(input->pos <= input->size); assert(input->pos <= input->size);
mtctx->inBuff.filled = result.inBuffFilled; /* A prepared job clears the C range while Rust still has the pre-job
* count in its local projection. Only publish the result when the range
* remains active, which is the no-job-created path. */
if (mtctx->inBuff.buffer.start != NULL)
mtctx->inBuff.filled = result.inBuffFilled;
input->pos = result.inputPos; input->pos = result.inputPos;
output->pos = result.outputPos; output->pos = result.outputPos;
DEBUGLOG(5, "end of ZSTDMT_compressStream_generic: remainingToFlush = %u", (U32)result.result); DEBUGLOG(5, "end of ZSTDMT_compressStream_generic: remainingToFlush = %u", (U32)result.result);