diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index d0e951630..cd0cd89d2 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -2723,12 +2723,13 @@ static size_t ZSTDMT_createCompressionJob(void* opaque, size_t srcSize, unsigned { ZSTDMT_CCtx* const mtctx = (ZSTDMT_CCtx*)opaque; ZSTD_EndDirective const endOp = (ZSTD_EndDirective)end; + ZSTDMT_RustCreateJobProjection projection; /* Rust fills the reusable input range in its local projection before * calling this C-owned job-construction callback. Publish that count * before the callback prepares a job and checks the C context. */ assert(srcSize <= mtctx->inBuff.buffer.capacity); mtctx->inBuff.filled = srcSize; - ZSTDMT_RustCreateJobProjection const projection = { + projection = (ZSTDMT_RustCreateJobProjection){ mtctx->doneJobID, mtctx->nextJobID, mtctx->jobIDMask, @@ -3099,7 +3100,11 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, (U32)endOp, (U32)(input->size - input->pos)); assert(output->pos <= output->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; output->pos = result.outputPos; DEBUGLOG(5, "end of ZSTDMT_compressStream_generic: remainingToFlush = %u", (U32)result.result);