new api : setting compression parameters is refused if a dictionary is already loaded
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index f848c08..3620e13 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -253,11 +253,15 @@
case ZSTD_p_compressionLevel :
if ((int)value > ZSTD_maxCLevel()) value = ZSTD_maxCLevel(); /* cap max compression level */
if (value == 0) return 0; /* special value : 0 means "don't change anything" */
+ if (cctx->cdict) return ERROR(stage_wrong);
cctx->compressionLevel = value;
return 0;
case ZSTD_p_windowLog :
+ DEBUGLOG(5, "setting ZSTD_p_windowLog = %u (cdict:%u)",
+ value, (cctx->cdict!=NULL));
if (value == 0) return 0; /* special value : 0 means "don't change anything" */
+ if (cctx->cdict) return ERROR(stage_wrong);
CLAMPCHECK(value, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX);
ZSTD_cLevelToCParams(cctx);
cctx->requestedParams.cParams.windowLog = value;
@@ -265,6 +269,7 @@
case ZSTD_p_hashLog :
if (value == 0) return 0; /* special value : 0 means "don't change anything" */
+ if (cctx->cdict) return ERROR(stage_wrong);
CLAMPCHECK(value, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX);
ZSTD_cLevelToCParams(cctx);
cctx->requestedParams.cParams.hashLog = value;
@@ -272,6 +277,7 @@
case ZSTD_p_chainLog :
if (value == 0) return 0; /* special value : 0 means "don't change anything" */
+ if (cctx->cdict) return ERROR(stage_wrong);
CLAMPCHECK(value, ZSTD_CHAINLOG_MIN, ZSTD_CHAINLOG_MAX);
ZSTD_cLevelToCParams(cctx);
cctx->requestedParams.cParams.chainLog = value;
@@ -279,6 +285,7 @@
case ZSTD_p_searchLog :
if (value == 0) return 0; /* special value : 0 means "don't change anything" */
+ if (cctx->cdict) return ERROR(stage_wrong);
CLAMPCHECK(value, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX);
ZSTD_cLevelToCParams(cctx);
cctx->requestedParams.cParams.searchLog = value;
@@ -286,6 +293,7 @@
case ZSTD_p_minMatch :
if (value == 0) return 0; /* special value : 0 means "don't change anything" */
+ if (cctx->cdict) return ERROR(stage_wrong);
CLAMPCHECK(value, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX);
ZSTD_cLevelToCParams(cctx);
cctx->requestedParams.cParams.searchLength = value;
@@ -293,6 +301,7 @@
case ZSTD_p_targetLength :
if (value == 0) return 0; /* special value : 0 means "don't change anything" */
+ if (cctx->cdict) return ERROR(stage_wrong);
CLAMPCHECK(value, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX);
ZSTD_cLevelToCParams(cctx);
cctx->requestedParams.cParams.targetLength = value;
@@ -300,6 +309,7 @@
case ZSTD_p_compressionStrategy :
if (value == 0) return 0; /* special value : 0 means "don't change anything" */
+ if (cctx->cdict) return ERROR(stage_wrong);
CLAMPCHECK(value, (unsigned)ZSTD_fast, (unsigned)ZSTD_btultra);
ZSTD_cLevelToCParams(cctx);
cctx->requestedParams.cParams.strategy = (ZSTD_strategy)value;
@@ -384,6 +394,7 @@
{
if (cctx->streamStage != zcss_init) return ERROR(stage_wrong);
if (cctx->staticSize) return ERROR(memory_allocation); /* no malloc for static CCtx */
+ DEBUGLOG(5, "load dictionary of size %u", (U32)dictSize);
ZSTD_freeCDict(cctx->cdictLocal); /* in case one already exists */
if (dict==NULL || dictSize==0) { /* no dictionary mode */
cctx->cdictLocal = NULL;
diff --git a/lib/zstd.h b/lib/zstd.h
index 928abe2..b204b9a 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -528,8 +528,9 @@
* If it needs more memory than available, it will simply error out.
* Note 2 : there is no corresponding "free" function.
* Since workspace was allocated externally, it must be freed externally too.
- * Limitation : currently not compatible with internal CDict creation, such as
- * ZSTD_CCtx_loadDictionary() or ZSTD_initCStream_usingDict().
+ * Limitation 1 : currently not compatible with internal CDict creation, such as
+ * ZSTD_CCtx_loadDictionary() or ZSTD_initCStream_usingDict().
+ * Limitation 2 : currently not compatible with multi-threading
*/
ZSTDLIB_API ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
@@ -746,7 +747,7 @@
* It's also a CPU-heavy operation, with non-negligible impact on latency.
* Note 3 : Dictionary will be used for all future compression jobs.
* To return to "no-dictionary" situation, load a NULL dictionary */
-ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); /* Not ready yet ! */
+ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
/*! ZSTD_CCtx_refPrefix() :
* Reference a prefix (content-only dictionary) to bootstrap next compression job.
diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
index 09aaae5..57993fc 100644
--- a/tests/zstreamtest.c
+++ b/tests/zstreamtest.c
@@ -1161,7 +1161,7 @@
}
-/* Tests for ZSTD_compressGeneric() API */
+/* Tests for ZSTD_compress_generic() API */
static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double compressibility, int bigTests)
{
static const U32 maxSrcLog = 24;
@@ -1181,7 +1181,7 @@
ZSTD_DStream* zd = ZSTD_createDStream(); /* will be reset sometimes */
ZSTD_DStream* const zd_noise = ZSTD_createDStream();
clock_t const startClock = clock();
- const BYTE* dict=NULL; /* can keep same dict on 2 consecutive tests */
+ const BYTE* dict = NULL; /* can keep same dict on 2 consecutive tests */
size_t dictSize = 0;
U32 oldTestLog = 0;
int const cLevelLimiter = bigTests ? 3 : 2;
@@ -1278,11 +1278,13 @@
dictSize = ((FUZ_rand(&lseed)&63)==1) ? FUZ_rLogLength(&lseed, dictLog) : 0;
{ size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize);
dict = srcBuffer + dictStart;
+ if (!dictSize) dict=NULL;
}
{ U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? ZSTD_CONTENTSIZE_UNKNOWN : maxTestSize;
ZSTD_compressionParameters cParams = ZSTD_getCParams(cLevel, pledgedSrcSize, dictSize);
/* mess with compression parameters */
+ CHECK_Z( ZSTD_CCtx_loadDictionary(zc, NULL, 0) ); /* always cancel previous dict, to make user it's possible to pass compression parameters */
cParams.windowLog += (FUZ_rand(&lseed) & 3) - 1;
cParams.hashLog += (FUZ_rand(&lseed) & 3) - 1;
cParams.chainLog += (FUZ_rand(&lseed) & 3) - 1;
@@ -1298,10 +1300,15 @@
if (FUZ_rand(&lseed) & 1) CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_p_minMatch, cParams.searchLength) );
if (FUZ_rand(&lseed) & 1) CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_p_targetLength, cParams.targetLength) );
- if (FUZ_rand(&lseed) & 1) { dict=NULL; dictSize=0; }
- CHECK_Z( ZSTD_CCtx_loadDictionary(zc, dict, dictSize) ); /* unconditionally set, to be sync with decoder (could be improved) */
+ /* unconditionally set, to be sync with decoder */
+ CHECK_Z( ZSTD_CCtx_loadDictionary(zc, dict, dictSize) );
- /* to do : check that cParams are blocked after loading non-NULL dictionary */
+ if (dict && dictSize) {
+ /* test that compression parameters are correctly rejected after setting a dictionary */
+ DISPLAYLEVEL(5, "setting windowLog while dict!=NULL \n");
+ size_t const setError = ZSTD_CCtx_setParameter(zc, ZSTD_p_windowLog, cParams.windowLog-1) ;
+ CHECK(!ZSTD_isError(setError), "ZSTD_CCtx_setParameter should have failed");
+ }
/* mess with frame parameters */
if (FUZ_rand(&lseed) & 1) CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_p_checksumFlag, FUZ_rand(&lseed) & 1) );
@@ -1352,7 +1359,9 @@
outBuff.size = outBuff.pos + adjustedDstSize;
DISPLAYLEVEL(5, "End-flush into dst buffer of size %u \n", (U32)adjustedDstSize);
remainingToFlush = ZSTD_compress_generic(zc, &outBuff, &inBuff, ZSTD_e_end);
- CHECK(ZSTD_isError(remainingToFlush), "ZSTD_compress_generic w/ ZSTD_e_end error : %s", ZSTD_getErrorName(remainingToFlush));
+ CHECK(ZSTD_isError(remainingToFlush),
+ "ZSTD_compress_generic w/ ZSTD_e_end error : %s",
+ ZSTD_getErrorName(remainingToFlush) );
} }
crcOrig = XXH64_digest(&xxhState);
cSize = outBuff.pos;
@@ -1471,7 +1480,7 @@
e_api selected_api = simple_api;
const char* const programName = argv[0];
ZSTD_customMem const customMem = { allocFunction, freeFunction, NULL };
- ZSTD_customMem const customNULL = { NULL, NULL, NULL };
+ ZSTD_customMem const customNULL = ZSTD_defaultCMem;
/* Check command line */
for(argNb=1; argNb<argc; argNb++) {