diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 6872d3364..2ff1fa209 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -983,7 +983,8 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams, return CCtxParams->enableMatchFinderFallback; case ZSTD_c_maxBlockSize: - BOUNDCHECK(ZSTD_c_maxBlockSize, value); + if (value!=0) /* 0 ==> default */ + BOUNDCHECK(ZSTD_c_maxBlockSize, value); CCtxParams->maxBlockSize = value; return CCtxParams->maxBlockSize; diff --git a/lib/zstd.h b/lib/zstd.h index 0c0fc1afc..9d1d507ff 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -2071,8 +2071,13 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo #define ZSTD_c_enableMatchFinderFallback ZSTD_c_experimentalParam17 /* ZSTD_c_maxBlockSize + * Allowed values are between 1KB and ZSTD_BLOCKSIZE_MAX (128KB). + * The default is ZSTD_BLOCKSIZE_MAX, and setting to 0 will set to the default. * - * Default is ZSTD_BLOCKSIZE_MAX. + * This parameter can be used to set an upper bound on the blocksize + * that overrides the default ZSTD_BLOCKSIZE_MAX. It cannot be used to set upper + * bounds greater than ZSTD_BLOCKSIZE_MAX or bounds lower than 1KB (will make + * compressBound() innacurate). Only currently meant to be used for testing. * */ #define ZSTD_c_maxBlockSize ZSTD_c_experimentalParam18 diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index bd829fbb1..3b8512310 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -1930,41 +1930,97 @@ static int basicUnitTests(U32 seed, double compressibility, int bigTests) DISPLAYLEVEL(3, "test%3i : Testing maxBlockSize PR#3418: ", testNb++); { ZSTD_CCtx* cctx = ZSTD_createCCtx(); - size_t const srcSize = 2 << 10; - void* const src = CNBuffer; - size_t const dstSize = ZSTD_compressBound(srcSize); - void* const dst1 = compressedBuffer; - void* const dst2 = (BYTE*)compressedBuffer + dstSize; - size_t size1, size2; - void* const checkBuf = malloc(srcSize); - - memset(src, 'x', srcSize); /* Quick test to make sure maxBlockSize bounds are enforced */ assert(ZSTD_isError(ZSTD_CCtx_setParameter(cctx, ZSTD_c_maxBlockSize, ZSTD_BLOCKSIZE_MAX_MIN - 1))); assert(ZSTD_isError(ZSTD_CCtx_setParameter(cctx, ZSTD_c_maxBlockSize, ZSTD_BLOCKSIZE_MAX + 1))); - /* maxBlockSize = 1KB */ - CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_maxBlockSize, 1 << 10)); - size1 = ZSTD_compress2(cctx, dst1, dstSize, src, srcSize); + /* Test maxBlockSize < windowSize and windowSize < maxBlockSize*/ + { + size_t srcSize = 2 << 10; + void* const src = CNBuffer; + size_t dstSize = ZSTD_compressBound(srcSize); + void* const dst1 = compressedBuffer; + void* const dst2 = (BYTE*)compressedBuffer + dstSize; + size_t size1, size2; + void* const checkBuf = malloc(srcSize); + memset(src, 'x', srcSize); - if (ZSTD_isError(size1)) goto _output_error; - CHECK_Z(ZSTD_decompress(checkBuf, srcSize, dst1, size1)); - CHECK(memcmp(src, checkBuf, srcSize) != 0, "Corruption!"); + /* maxBlockSize = 1KB */ + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_maxBlockSize, 1u << 10)); + size1 = ZSTD_compress2(cctx, dst1, dstSize, src, srcSize); - /* maxBlockSize = 3KB */ - CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_maxBlockSize, 3 << 10)); - size2 = ZSTD_compress2(cctx, dst2, dstSize, src, srcSize); + if (ZSTD_isError(size1)) goto _output_error; + CHECK_Z(ZSTD_decompress(checkBuf, srcSize, dst1, size1)); + CHECK(memcmp(src, checkBuf, srcSize) != 0, "Corruption!"); - if (ZSTD_isError(size2)) goto _output_error; - CHECK_Z(ZSTD_decompress(checkBuf, srcSize, dst2, size2)); - CHECK(memcmp(src, checkBuf, srcSize) != 0, "Corruption!"); + /* maxBlockSize = 3KB */ + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_maxBlockSize, 3u << 10)); + size2 = ZSTD_compress2(cctx, dst2, dstSize, src, srcSize); - /* Compressed output should not be equal */ - assert(memcmp(dst1, dst2, dstSize) != 0); - assert(size1 - size2 == 4); /* We add another RLE block with header + character */ + if (ZSTD_isError(size2)) goto _output_error; + CHECK_Z(ZSTD_decompress(checkBuf, srcSize, dst2, size2)); + CHECK(memcmp(src, checkBuf, srcSize) != 0, "Corruption!"); + + assert(size1 - size2 == 4); /* We add another RLE block with header + character */ + assert(memcmp(dst1, dst2, size2) != 0); /* Compressed output should not be equal */ + + /* maxBlockSize = 1KB, windowLog = 10 */ + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_maxBlockSize, 1u << 10)); + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_windowLog, 10)); + size1 = ZSTD_compress2(cctx, dst1, dstSize, src, srcSize); + + if (ZSTD_isError(size1)) goto _output_error; + CHECK_Z(ZSTD_decompress(checkBuf, srcSize, dst1, size1)); + CHECK(memcmp(src, checkBuf, srcSize) != 0, "Corruption!"); + + /* maxBlockSize = 3KB, windowLog = 10 */ + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_maxBlockSize, 3u << 10)); + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_windowLog, 10)); + size2 = ZSTD_compress2(cctx, dst2, dstSize, src, srcSize); + + if (ZSTD_isError(size2)) goto _output_error; + CHECK_Z(ZSTD_decompress(checkBuf, srcSize, dst2, size2)); + CHECK(memcmp(src, checkBuf, srcSize) != 0, "Corruption!"); + + assert(size1 == size2); + assert(memcmp(dst1, dst2, size1) == 0); /* Compressed output should be equal */ + + free(checkBuf); + } + + ZSTD_CCtx_reset(cctx, ZSTD_reset_session_and_parameters); + + /* Test maxBlockSize = 0 is valid */ + { size_t srcSize = 256 << 10; + void* const src = CNBuffer; + size_t dstSize = ZSTD_compressBound(srcSize); + void* const dst1 = compressedBuffer; + void* const dst2 = (BYTE*)compressedBuffer + dstSize; + size_t size1, size2; + void* const checkBuf = malloc(srcSize); + + /* maxBlockSize = 0 */ + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_maxBlockSize, 0)); + size1 = ZSTD_compress2(cctx, dst1, dstSize, src, srcSize); + + if (ZSTD_isError(size1)) goto _output_error; + CHECK_Z(ZSTD_decompress(checkBuf, srcSize, dst1, size1)); + CHECK(memcmp(src, checkBuf, srcSize) != 0, "Corruption!"); + + /* maxBlockSize = ZSTD_BLOCKSIZE_MAX */ + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_maxBlockSize, ZSTD_BLOCKSIZE_MAX)); + size2 = ZSTD_compress2(cctx, dst2, dstSize, src, srcSize); + + if (ZSTD_isError(size2)) goto _output_error; + CHECK_Z(ZSTD_decompress(checkBuf, srcSize, dst2, size2)); + CHECK(memcmp(src, checkBuf, srcSize) != 0, "Corruption!"); + + assert(size1 == size2); + assert(memcmp(dst1, dst2, size1) == 0); /* Compressed output should be equal */ + free(checkBuf); + } ZSTD_freeCCtx(cctx); - free(checkBuf); } DISPLAYLEVEL(3, "OK \n");