diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index cb710475c2a..05e688ca2a8 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -755,6 +755,11 @@ static void ZSTDMT_compressionJob(void* jobDescription) if (ZSTD_isError(hSize)) JOB_ERROR(hSize); DEBUGLOG(5, "ZSTDMT_compressionJob: flush and overwrite %u bytes of frame header (not first job)", (U32)hSize); ZSTD_invalidateRepCodes(cctx); + /* ZSTD_compressBegin() sets isFirstBlock=1 for every job, but only the + * frame's first block must skip RLE to stay compatible with decoders + * <= v1.4.3. Without this, the first block of each job is emitted as a + * regular compressed block instead of an RLE block. */ + cctx->isFirstBlock = 0; } /* compress the entire job by smaller chunks, for better granularity */ diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 32360b9a58d..824c5539d39 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -4325,6 +4325,36 @@ static int basicUnitTests(U32 const seed, double compressibility) DISPLAYLEVEL(3, "OK \n"); } + /* The RLE guard must only skip the frame's first block, not the first + * block of every multithreaded job (issue #4457). */ + { size_t const srcSize = 2 MB; + size_t const jobSize = 512 KB; + size_t singleSize; + size_t mtSize; + ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + DISPLAYLEVEL(3, "test%3i : RLE detection in multithreaded jobs : ", testNb++); + if (cctx == NULL) goto _output_error; + memset(CNBuffer, 0, srcSize); + CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, 1) ); + CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers, 0) ); + singleSize = ZSTD_compress2(cctx, compressedBuffer, compressedBufferSize, CNBuffer, srcSize); + if (ZSTD_isError(singleSize)) goto _output_error; + CHECK_Z( ZSTD_CCtx_reset(cctx, ZSTD_reset_session_and_parameters) ); + CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, 1) ); + CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers, 2) ); + CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_c_jobSize, (int)jobSize) ); + mtSize = ZSTD_compress2(cctx, compressedBuffer, compressedBufferSize, CNBuffer, srcSize); + if (ZSTD_isError(mtSize)) goto _output_error; + if (mtSize != singleSize) { + DISPLAYLEVEL(4, "error: MT cSize %u != single-thread cSize %u ! \n", (unsigned)mtSize, (unsigned)singleSize); + goto _output_error; + } + { CHECK_NEWV(regenSize, ZSTD_decompress(decodedBuffer, srcSize, compressedBuffer, mtSize)); + if (regenSize != srcSize) goto _output_error; } + ZSTD_freeCCtx(cctx); + DISPLAYLEVEL(3, "OK \n"); + } + DISPLAYLEVEL(3, "test%3i : ZSTD_generateSequences decode from sequences test : ", testNb++); { size_t srcSize = 150 KB;