2 * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
5 * This source code is licensed under both the BSD-style license (found in the
6 * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7 * in the COPYING file in the root directory of this source tree).
8 * You may select, at your option, one of the above-listed licenses.
12 /*-************************************
14 **************************************/
15 #ifdef _MSC_VER /* Visual Studio */
16 # define _CRT_SECURE_NO_WARNINGS /* fgets */
17 # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
18 # pragma warning(disable : 4146) /* disable: C4146: minus unsigned expression */
22 /*-************************************
24 **************************************/
25 #include <stdlib.h> /* free */
26 #include <stdio.h> /* fgets, sscanf */
27 #include <string.h> /* strcmp */
28 #include <assert.h> /* assert */
29 #include "timefn.h" /* UTIL_time_t, UTIL_getTime */
31 #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel, ZSTD_customMem, ZSTD_getDictID_fromFrame */
32 #include "zstd.h" /* ZSTD_compressBound */
33 #include "zstd_errors.h" /* ZSTD_error_srcSize_wrong */
34 #include "zstdmt_compress.h"
35 #include "zdict.h" /* ZDICT_trainFromBuffer */
36 #include "datagen.h" /* RDG_genBuffer */
37 #define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */
38 #include "xxhash.h" /* XXH64_* */
41 #include "timefn.h" /* UTIL_time_t, UTIL_clockSpanMicro, UTIL_getTime */
44 /*-************************************
46 **************************************/
51 static const int nbTestsDefault = 10000;
52 static const U32 g_cLevelMax_smallTests = 10;
53 #define COMPRESSIBLE_NOISE_LENGTH (10 MB)
54 #define FUZ_COMPRESSIBILITY_DEFAULT 50
55 static const U32 prime32 = 2654435761U;
58 /*-************************************
60 **************************************/
61 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
62 #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { \
63 DISPLAY(__VA_ARGS__); \
64 if (g_displayLevel>=4) fflush(stderr); }
65 static U32 g_displayLevel = 2;
67 static const U64 g_refreshRate = SEC_TO_MICRO / 6;
68 static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER;
70 #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
71 if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \
72 { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \
73 if (g_displayLevel>=4) fflush(stderr); } }
75 static U64 g_clockTime = 0;
78 /*-*******************************************************
80 *********************************************************/
83 #define MIN(a,b) ((a)<(b)?(a):(b))
84 #define MAX(a,b) ((a)>(b)?(a):(b))
86 @return : a 27 bits random value, from a 32-bits `seed`.
87 `seed` is also modified */
88 #define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r)))
89 static U32 FUZ_rand(U32* seedPtr)
91 static const U32 prime2 = 2246822519U;
92 U32 rand32 = *seedPtr;
95 rand32 = FUZ_rotl32(rand32, 13);
100 #define CHECK(cond, ...) { \
102 DISPLAY("Error => "); \
103 DISPLAY(__VA_ARGS__); \
104 DISPLAY(" (seed %u, test nb %u, line %u) \n", \
105 (unsigned)seed, testNb, __LINE__); \
106 goto _output_error; \
109 #define CHECK_Z(f) { \
110 size_t const err = f; \
111 CHECK(ZSTD_isError(err), "%s : %s ", \
112 #f, ZSTD_getErrorName(err)); \
115 #define CHECK_RET(ret, cond, ...) { \
117 DISPLAY("Error %llu => ", (unsigned long long)ret); \
118 DISPLAY(__VA_ARGS__); \
119 DISPLAY(" (line %u)\n", __LINE__); \
123 #define CHECK_RET_Z(f) { \
124 size_t const err = f; \
125 CHECK_RET(err, ZSTD_isError(err), "%s : %s ", \
126 #f, ZSTD_getErrorName(err)); \
130 /*======================================================
132 *======================================================*/
140 static const buffer_t kBuffNull = { NULL, 0 , 0 };
142 static void FUZ_freeDictionary(buffer_t dict)
147 static buffer_t FUZ_createDictionary(const void* src, size_t srcSize, size_t blockSize, size_t requestedDictSize)
149 buffer_t dict = kBuffNull;
150 size_t const nbBlocks = (srcSize + (blockSize-1)) / blockSize;
151 size_t* const blockSizes = (size_t*)malloc(nbBlocks * sizeof(size_t));
152 if (!blockSizes) return kBuffNull;
153 dict.start = malloc(requestedDictSize);
154 if (!dict.start) { free(blockSizes); return kBuffNull; }
156 for (nb=0; nb<nbBlocks-1; nb++) blockSizes[nb] = blockSize;
157 blockSizes[nbBlocks-1] = srcSize - (blockSize * (nbBlocks-1));
159 { size_t const dictSize = ZDICT_trainFromBuffer(dict.start, requestedDictSize, src, blockSizes, (unsigned)nbBlocks);
161 if (ZDICT_isError(dictSize)) { FUZ_freeDictionary(dict); return kBuffNull; }
162 dict.size = requestedDictSize;
163 dict.filled = dictSize;
168 /* Round trips data and updates xxh with the decompressed data produced */
169 static size_t SEQ_roundTrip(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx,
170 XXH64_state_t* xxh, void* data, size_t size,
171 ZSTD_EndDirective endOp)
173 static BYTE compressed[1024];
174 static BYTE uncompressed[1024];
176 ZSTD_inBuffer cin = {data, size, 0};
180 ZSTD_outBuffer cout = { compressed, sizeof(compressed), 0 };
181 ZSTD_inBuffer din = { compressed, 0, 0 };
182 ZSTD_outBuffer dout = { uncompressed, 0, 0 };
184 cret = ZSTD_compressStream2(cctx, &cout, &cin, endOp);
185 if (ZSTD_isError(cret))
189 while (din.pos < din.size || (endOp == ZSTD_e_end && cret == 0)) {
193 dout.size = sizeof(uncompressed);
194 dret = ZSTD_decompressStream(dctx, &dout, &din);
195 if (ZSTD_isError(dret))
197 XXH64_update(xxh, dout.dst, dout.pos);
201 } while (cin.pos < cin.size || (endOp != ZSTD_e_continue && cret != 0));
205 /* Generates some data and round trips it */
206 static size_t SEQ_generateRoundTrip(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx,
207 XXH64_state_t* xxh, SEQ_stream* seq,
208 SEQ_gen_type type, unsigned value)
210 static BYTE data[1024];
214 SEQ_outBuffer sout = {data, sizeof(data), 0};
216 gen = SEQ_gen(seq, type, value, &sout);
218 ret = SEQ_roundTrip(cctx, dctx, xxh, sout.dst, sout.pos, ZSTD_e_continue);
219 if (ZSTD_isError(ret))
226 static size_t getCCtxParams(ZSTD_CCtx* zc, ZSTD_parameters* savedParams)
229 CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_windowLog, (int*)&savedParams->cParams.windowLog));
230 CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_hashLog, (int*)&savedParams->cParams.hashLog));
231 CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_chainLog, (int*)&savedParams->cParams.chainLog));
232 CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_searchLog, (int*)&savedParams->cParams.searchLog));
233 CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_minMatch, (int*)&savedParams->cParams.minMatch));
234 CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_targetLength, (int*)&savedParams->cParams.targetLength));
235 CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_strategy, &value));
236 savedParams->cParams.strategy = value;
238 CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_checksumFlag, &savedParams->fParams.checksumFlag));
239 CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_contentSizeFlag, &savedParams->fParams.contentSizeFlag));
240 CHECK_RET_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_dictIDFlag, &value));
241 savedParams->fParams.noDictIDFlag = !value;
245 static U32 badParameters(ZSTD_CCtx* zc, ZSTD_parameters const savedParams)
247 ZSTD_parameters params;
248 if (ZSTD_isError(getCCtxParams(zc, ¶ms))) return 10;
249 CHECK_RET(1, params.cParams.windowLog != savedParams.cParams.windowLog, "windowLog");
250 CHECK_RET(2, params.cParams.hashLog != savedParams.cParams.hashLog, "hashLog");
251 CHECK_RET(3, params.cParams.chainLog != savedParams.cParams.chainLog, "chainLog");
252 CHECK_RET(4, params.cParams.searchLog != savedParams.cParams.searchLog, "searchLog");
253 CHECK_RET(5, params.cParams.minMatch != savedParams.cParams.minMatch, "minMatch");
254 CHECK_RET(6, params.cParams.targetLength != savedParams.cParams.targetLength, "targetLength");
256 CHECK_RET(7, params.fParams.checksumFlag != savedParams.fParams.checksumFlag, "checksumFlag");
257 CHECK_RET(8, params.fParams.contentSizeFlag != savedParams.fParams.contentSizeFlag, "contentSizeFlag");
258 CHECK_RET(9, params.fParams.noDictIDFlag != savedParams.fParams.noDictIDFlag, "noDictIDFlag");
262 static int basicUnitTests(U32 seed, double compressibility)
264 size_t const CNBufferSize = COMPRESSIBLE_NOISE_LENGTH;
265 void* CNBuffer = malloc(CNBufferSize);
266 size_t const skippableFrameSize = 200 KB;
267 size_t const compressedBufferSize = (8 + skippableFrameSize) + ZSTD_compressBound(COMPRESSIBLE_NOISE_LENGTH);
268 void* compressedBuffer = malloc(compressedBufferSize);
269 size_t const decodedBufferSize = CNBufferSize;
270 void* decodedBuffer = malloc(decodedBufferSize);
274 U32 coreSeed = 0; /* this name to conform with CHECK_Z macro display */
275 ZSTD_CStream* zc = ZSTD_createCStream();
276 ZSTD_DStream* zd = ZSTD_createDStream();
277 ZSTDMT_CCtx* mtctx = ZSTDMT_createCCtx(2);
279 ZSTD_inBuffer inBuff, inBuff2;
280 ZSTD_outBuffer outBuff;
281 buffer_t dictionary = kBuffNull;
282 size_t const dictSize = 128 KB;
285 /* Create compressible test buffer */
286 if (!CNBuffer || !compressedBuffer || !decodedBuffer || !zc || !zd) {
287 DISPLAY("Not enough memory, aborting \n");
290 RDG_genBuffer(CNBuffer, CNBufferSize, compressibility, 0., seed);
292 /* Create dictionary */
293 DISPLAYLEVEL(3, "creating dictionary for unit tests \n");
294 dictionary = FUZ_createDictionary(CNBuffer, CNBufferSize / 3, 16 KB, 48 KB);
295 if (!dictionary.start) {
296 DISPLAY("Error creating dictionary, aborting \n");
299 dictID = ZDICT_getDictID(dictionary.start, dictionary.filled);
301 /* Basic compression test */
302 DISPLAYLEVEL(3, "test%3i : compress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH);
303 CHECK_Z( ZSTD_initCStream(zc, 1 /* cLevel */) );
304 outBuff.dst = (char*)(compressedBuffer);
305 outBuff.size = compressedBufferSize;
307 inBuff.src = CNBuffer;
308 inBuff.size = CNBufferSize;
310 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
311 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
312 { size_t const r = ZSTD_endStream(zc, &outBuff);
313 if (r != 0) goto _output_error; } /* error, or some data not flushed */
314 DISPLAYLEVEL(3, "OK (%u bytes)\n", (unsigned)outBuff.pos);
316 /* generate skippable frame */
317 MEM_writeLE32(compressedBuffer, ZSTD_MAGIC_SKIPPABLE_START);
318 MEM_writeLE32(((char*)compressedBuffer)+4, (U32)skippableFrameSize);
319 cSize = skippableFrameSize + 8;
321 /* Basic compression test using dict */
322 DISPLAYLEVEL(3, "test%3i : skipframe + compress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH);
323 CHECK_Z( ZSTD_initCStream_usingDict(zc, CNBuffer, dictSize, 1 /* cLevel */) );
324 outBuff.dst = (char*)(compressedBuffer)+cSize;
325 assert(compressedBufferSize > cSize);
326 outBuff.size = compressedBufferSize - cSize;
328 inBuff.src = CNBuffer;
329 inBuff.size = CNBufferSize;
331 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
332 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
333 { size_t const r = ZSTD_endStream(zc, &outBuff);
334 if (r != 0) goto _output_error; } /* error, or some data not flushed */
335 cSize += outBuff.pos;
336 DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n",
337 (unsigned)cSize, (double)cSize/COMPRESSIBLE_NOISE_LENGTH*100);
339 /* context size functions */
340 DISPLAYLEVEL(3, "test%3i : estimate CStream size : ", testNb++);
341 { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBufferSize, dictSize);
342 size_t const cstreamSize = ZSTD_estimateCStreamSize_usingCParams(cParams);
343 size_t const cdictSize = ZSTD_estimateCDictSize_advanced(dictSize, cParams, ZSTD_dlm_byCopy); /* uses ZSTD_initCStream_usingDict() */
344 if (ZSTD_isError(cstreamSize)) goto _output_error;
345 if (ZSTD_isError(cdictSize)) goto _output_error;
346 DISPLAYLEVEL(3, "OK (%u bytes) \n", (unsigned)(cstreamSize + cdictSize));
349 /* context size functions */
350 DISPLAYLEVEL(3, "test%3i : estimate CStream size using CCtxParams : ", testNb++);
351 { ZSTD_CCtx_params* const params = ZSTD_createCCtxParams();
352 size_t cstreamSize, cctxSize;
353 CHECK_Z( ZSTD_CCtxParams_setParameter(params, ZSTD_c_compressionLevel, 19) );
354 cstreamSize = ZSTD_estimateCStreamSize_usingCCtxParams(params);
355 CHECK_Z(cstreamSize);
356 cctxSize = ZSTD_estimateCCtxSize_usingCCtxParams(params);
358 if (cstreamSize <= cctxSize + 2 * ZSTD_BLOCKSIZE_MAX) goto _output_error;
359 ZSTD_freeCCtxParams(params);
360 DISPLAYLEVEL(3, "OK \n");
363 DISPLAYLEVEL(3, "test%3i : check actual CStream size : ", testNb++);
364 { size_t const s = ZSTD_sizeof_CStream(zc);
365 if (ZSTD_isError(s)) goto _output_error;
366 DISPLAYLEVEL(3, "OK (%u bytes) \n", (unsigned)s);
369 /* Attempt bad compression parameters */
370 DISPLAYLEVEL(3, "test%3i : use bad compression parameters : ", testNb++);
372 ZSTD_parameters params = ZSTD_getParams(1, 0, 0);
373 params.cParams.minMatch = 2;
374 r = ZSTD_initCStream_advanced(zc, NULL, 0, params, 0);
375 if (!ZSTD_isError(r)) goto _output_error;
376 DISPLAYLEVEL(3, "init error : %s \n", ZSTD_getErrorName(r));
379 /* skippable frame test */
380 DISPLAYLEVEL(3, "test%3i : decompress skippable frame : ", testNb++);
381 CHECK_Z( ZSTD_initDStream_usingDict(zd, CNBuffer, dictSize) );
382 inBuff.src = compressedBuffer;
385 outBuff.dst = decodedBuffer;
386 outBuff.size = CNBufferSize;
388 { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
389 DISPLAYLEVEL(5, " ( ZSTD_decompressStream => %u ) ", (unsigned)r);
390 if (r != 0) goto _output_error;
392 if (outBuff.pos != 0) goto _output_error; /* skippable frame output len is 0 */
393 DISPLAYLEVEL(3, "OK \n");
395 /* Basic decompression test */
397 DISPLAYLEVEL(3, "test%3i : decompress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH);
398 ZSTD_initDStream_usingDict(zd, CNBuffer, dictSize);
399 CHECK_Z( ZSTD_DCtx_setParameter(zd, ZSTD_d_windowLogMax, ZSTD_WINDOWLOG_LIMIT_DEFAULT+1) ); /* large limit */
400 { size_t const remaining = ZSTD_decompressStream(zd, &outBuff, &inBuff);
401 if (remaining != 0) goto _output_error; } /* should reach end of frame == 0; otherwise, some data left, or an error */
402 if (outBuff.pos != CNBufferSize) goto _output_error; /* should regenerate the same amount */
403 if (inBuff.pos != inBuff.size) goto _output_error; /* should have read the entire frame */
404 DISPLAYLEVEL(3, "OK \n");
406 /* Re-use without init */
407 DISPLAYLEVEL(3, "test%3i : decompress again without init (re-use previous settings): ", testNb++);
409 { size_t const remaining = ZSTD_decompressStream(zd, &outBuff, &inBuff2);
410 if (remaining != 0) goto _output_error; } /* should reach end of frame == 0; otherwise, some data left, or an error */
411 if (outBuff.pos != CNBufferSize) goto _output_error; /* should regenerate the same amount */
412 if (inBuff.pos != inBuff.size) goto _output_error; /* should have read the entire frame */
413 DISPLAYLEVEL(3, "OK \n");
415 /* check regenerated data is byte exact */
416 DISPLAYLEVEL(3, "test%3i : check decompressed result : ", testNb++);
418 for (i=0; i<CNBufferSize; i++) {
419 if (((BYTE*)decodedBuffer)[i] != ((BYTE*)CNBuffer)[i]) goto _output_error;
421 DISPLAYLEVEL(3, "OK \n");
423 /* context size functions */
424 DISPLAYLEVEL(3, "test%3i : estimate DStream size : ", testNb++);
425 { ZSTD_frameHeader fhi;
426 const void* cStart = (char*)compressedBuffer + (skippableFrameSize + 8);
427 size_t const gfhError = ZSTD_getFrameHeader(&fhi, cStart, cSize);
428 if (gfhError!=0) goto _output_error;
429 DISPLAYLEVEL(5, " (windowSize : %u) ", (unsigned)fhi.windowSize);
430 { size_t const s = ZSTD_estimateDStreamSize(fhi.windowSize)
431 /* uses ZSTD_initDStream_usingDict() */
432 + ZSTD_estimateDDictSize(dictSize, ZSTD_dlm_byCopy);
433 if (ZSTD_isError(s)) goto _output_error;
434 DISPLAYLEVEL(3, "OK (%u bytes) \n", (unsigned)s);
437 DISPLAYLEVEL(3, "test%3i : check actual DStream size : ", testNb++);
438 { size_t const s = ZSTD_sizeof_DStream(zd);
439 if (ZSTD_isError(s)) goto _output_error;
440 DISPLAYLEVEL(3, "OK (%u bytes) \n", (unsigned)s);
443 /* Decompression by small increment */
444 DISPLAYLEVEL(3, "test%3i : decompress byte-by-byte : ", testNb++);
445 { /* skippable frame */
447 ZSTD_initDStream_usingDict(zd, CNBuffer, dictSize);
448 inBuff.src = compressedBuffer;
449 outBuff.dst = decodedBuffer;
452 while (r) { /* skippable frame */
453 size_t const inSize = (FUZ_rand(&coreSeed) & 15) + 1;
454 size_t const outSize = (FUZ_rand(&coreSeed) & 15) + 1;
455 inBuff.size = inBuff.pos + inSize;
456 outBuff.size = outBuff.pos + outSize;
457 r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
458 if (ZSTD_isError(r)) DISPLAYLEVEL(4, "ZSTD_decompressStream on skippable frame error : %s \n", ZSTD_getErrorName(r));
459 if (ZSTD_isError(r)) goto _output_error;
462 ZSTD_initDStream_usingDict(zd, CNBuffer, dictSize);
465 size_t const inSize = FUZ_rand(&coreSeed) & 15;
466 size_t const outSize = (FUZ_rand(&coreSeed) & 15) + (!inSize); /* avoid having both sizes at 0 => would trigger a no_forward_progress error */
467 inBuff.size = inBuff.pos + inSize;
468 outBuff.size = outBuff.pos + outSize;
469 r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
470 if (ZSTD_isError(r)) DISPLAYLEVEL(4, "ZSTD_decompressStream error : %s \n", ZSTD_getErrorName(r));
471 if (ZSTD_isError(r)) goto _output_error;
474 if (outBuff.pos != CNBufferSize) DISPLAYLEVEL(4, "outBuff.pos != CNBufferSize : should have regenerated same amount ! \n");
475 if (outBuff.pos != CNBufferSize) goto _output_error; /* should regenerate the same amount */
476 if (inBuff.pos != cSize) DISPLAYLEVEL(4, "inBuff.pos != cSize : should have real all input ! \n");
477 if (inBuff.pos != cSize) goto _output_error; /* should have read the entire frame */
478 DISPLAYLEVEL(3, "OK \n");
480 /* check regenerated data is byte exact */
481 DISPLAYLEVEL(3, "test%3i : check decompressed result : ", testNb++);
483 for (i=0; i<CNBufferSize; i++) {
484 if (((BYTE*)decodedBuffer)[i] != ((BYTE*)CNBuffer)[i]) goto _output_error;;
486 DISPLAYLEVEL(3, "OK \n");
488 /* Decompression forward progress */
489 DISPLAYLEVEL(3, "test%3i : generate error when ZSTD_decompressStream() doesn't progress : ", testNb++);
490 { /* skippable frame */
493 int const maxDec = 100;
494 inBuff.src = compressedBuffer;
498 outBuff.dst = decodedBuffer;
500 outBuff.size = CNBufferSize-1; /* 1 byte missing */
502 for (decNb=0; decNb<maxDec; decNb++) {
503 if (r==0) ZSTD_initDStream_usingDict(zd, CNBuffer, dictSize);
504 r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
505 if (ZSTD_isError(r)) break;
507 if (!ZSTD_isError(r)) DISPLAYLEVEL(4, "ZSTD_decompressStream should have triggered a no_forward_progress error \n");
508 if (!ZSTD_isError(r)) goto _output_error; /* should have triggered no_forward_progress error */
510 DISPLAYLEVEL(3, "OK \n");
512 /* _srcSize compression test */
513 DISPLAYLEVEL(3, "test%3i : compress_srcSize %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH);
514 CHECK_Z( ZSTD_initCStream_srcSize(zc, 1, CNBufferSize) );
515 outBuff.dst = (char*)(compressedBuffer);
516 outBuff.size = compressedBufferSize;
518 inBuff.src = CNBuffer;
519 inBuff.size = CNBufferSize;
521 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
522 CHECK(inBuff.pos != inBuff.size, "Entire input should be consumed");
523 { size_t const r = ZSTD_endStream(zc, &outBuff);
524 CHECK(r != 0, "Error or some data not flushed (ret=%zu)", r);
526 { unsigned long long origSize = ZSTD_findDecompressedSize(outBuff.dst, outBuff.pos);
527 CHECK(origSize == ZSTD_CONTENTSIZE_UNKNOWN, "Unknown!");
528 CHECK((size_t)origSize != CNBufferSize, "Exact original size must be present (got %llu)", origSize);
530 DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (unsigned)cSize, (double)cSize/COMPRESSIBLE_NOISE_LENGTH*100);
532 /* wrong _srcSize compression test */
533 DISPLAYLEVEL(3, "test%3i : too large srcSize : %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH-1);
534 ZSTD_initCStream_srcSize(zc, 1, CNBufferSize+1);
535 outBuff.dst = (char*)(compressedBuffer);
536 outBuff.size = compressedBufferSize;
538 inBuff.src = CNBuffer;
539 inBuff.size = CNBufferSize;
541 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
542 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
543 { size_t const r = ZSTD_endStream(zc, &outBuff);
544 if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; /* must fail : wrong srcSize */
545 DISPLAYLEVEL(3, "OK (error detected : %s) \n", ZSTD_getErrorName(r)); }
547 /* wrong _srcSize compression test */
548 DISPLAYLEVEL(3, "test%3i : too small srcSize : %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH-1);
549 ZSTD_initCStream_srcSize(zc, 1, CNBufferSize-1);
550 outBuff.dst = (char*)(compressedBuffer);
551 outBuff.size = compressedBufferSize;
553 inBuff.src = CNBuffer;
554 inBuff.size = CNBufferSize;
556 { size_t const r = ZSTD_compressStream(zc, &outBuff, &inBuff);
557 if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; /* must fail : wrong srcSize */
558 DISPLAYLEVEL(3, "OK (error detected : %s) \n", ZSTD_getErrorName(r));
561 DISPLAYLEVEL(3, "test%3i : wrong srcSize !contentSizeFlag : %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH-1);
562 { ZSTD_parameters params = ZSTD_getParams(1, CNBufferSize, 0);
563 params.fParams.contentSizeFlag = 0;
564 CHECK_Z(ZSTD_initCStream_advanced(zc, NULL, 0, params, CNBufferSize - MIN(CNBufferSize, 200 KB)));
565 outBuff.dst = (char*)compressedBuffer;
566 outBuff.size = compressedBufferSize;
568 inBuff.src = CNBuffer;
569 inBuff.size = CNBufferSize;
571 { size_t const r = ZSTD_compressStream(zc, &outBuff, &inBuff);
572 if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; /* must fail : wrong srcSize */
573 DISPLAYLEVEL(3, "OK (error detected : %s) \n", ZSTD_getErrorName(r));
576 /* Complex context re-use scenario */
577 DISPLAYLEVEL(3, "test%3i : context re-use : ", testNb++);
578 ZSTD_freeCStream(zc);
579 zc = ZSTD_createCStream();
580 if (zc==NULL) goto _output_error; /* memory allocation issue */
582 { size_t const inSize = 513;
583 DISPLAYLEVEL(5, "use1 ");
584 ZSTD_initCStream_advanced(zc, NULL, 0, ZSTD_getParams(19, inSize, 0), inSize); /* needs btopt + search3 to trigger hashLog3 */
585 inBuff.src = CNBuffer;
586 inBuff.size = inSize;
588 outBuff.dst = (char*)(compressedBuffer)+cSize;
589 outBuff.size = ZSTD_compressBound(inSize);
591 DISPLAYLEVEL(5, "compress1 ");
592 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
593 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
594 DISPLAYLEVEL(5, "end1 ");
595 { size_t const r = ZSTD_endStream(zc, &outBuff);
596 if (r != 0) goto _output_error; } /* error, or some data not flushed */
599 { size_t const inSize = 1025; /* will not continue, because tables auto-adjust and are therefore different size */
600 DISPLAYLEVEL(5, "use2 ");
601 ZSTD_initCStream_advanced(zc, NULL, 0, ZSTD_getParams(19, inSize, 0), inSize); /* needs btopt + search3 to trigger hashLog3 */
602 inBuff.src = CNBuffer;
603 inBuff.size = inSize;
605 outBuff.dst = (char*)(compressedBuffer)+cSize;
606 outBuff.size = ZSTD_compressBound(inSize);
608 DISPLAYLEVEL(5, "compress2 ");
609 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
610 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
611 DISPLAYLEVEL(5, "end2 ");
612 { size_t const r = ZSTD_endStream(zc, &outBuff);
613 if (r != 0) goto _output_error; } /* error, or some data not flushed */
615 DISPLAYLEVEL(3, "OK \n");
618 DISPLAYLEVEL(3, "test%3i : digested dictionary : ", testNb++);
619 { ZSTD_CDict* const cdict = ZSTD_createCDict(dictionary.start, dictionary.filled, 1 /*byRef*/ );
620 size_t const initError = ZSTD_initCStream_usingCDict(zc, cdict);
621 DISPLAYLEVEL(5, "ZSTD_initCStream_usingCDict result : %u ", (unsigned)initError);
622 if (ZSTD_isError(initError)) goto _output_error;
623 outBuff.dst = compressedBuffer;
624 outBuff.size = compressedBufferSize;
626 inBuff.src = CNBuffer;
627 inBuff.size = CNBufferSize;
629 DISPLAYLEVEL(5, "- starting ZSTD_compressStream ");
630 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
631 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
632 { size_t const r = ZSTD_endStream(zc, &outBuff);
633 DISPLAYLEVEL(5, "- ZSTD_endStream result : %u ", (unsigned)r);
634 if (r != 0) goto _output_error; /* error, or some data not flushed */
637 ZSTD_freeCDict(cdict);
638 DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (unsigned)cSize, (double)cSize/CNBufferSize*100);
641 DISPLAYLEVEL(3, "test%3i : check CStream size : ", testNb++);
642 { size_t const s = ZSTD_sizeof_CStream(zc);
643 if (ZSTD_isError(s)) goto _output_error;
644 DISPLAYLEVEL(3, "OK (%u bytes) \n", (unsigned)s);
647 DISPLAYLEVEL(4, "test%3i : check Dictionary ID : ", testNb++);
648 { unsigned const dID = ZSTD_getDictID_fromFrame(compressedBuffer, cSize);
649 if (dID != dictID) goto _output_error;
650 DISPLAYLEVEL(4, "OK (%u) \n", dID);
654 DISPLAYLEVEL(3, "test%3i : decompress %u bytes with digested dictionary : ", testNb++, (unsigned)CNBufferSize);
655 { ZSTD_DDict* const ddict = ZSTD_createDDict(dictionary.start, dictionary.filled);
656 size_t const initError = ZSTD_initDStream_usingDDict(zd, ddict);
657 if (ZSTD_isError(initError)) goto _output_error;
658 outBuff.dst = decodedBuffer;
659 outBuff.size = CNBufferSize;
661 inBuff.src = compressedBuffer;
664 { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
665 if (r != 0) goto _output_error; } /* should reach end of frame == 0; otherwise, some data left, or an error */
666 if (outBuff.pos != CNBufferSize) goto _output_error; /* should regenerate the same amount */
667 if (inBuff.pos != inBuff.size) goto _output_error; /* should have read the entire frame */
668 ZSTD_freeDDict(ddict);
669 DISPLAYLEVEL(3, "OK \n");
672 /* Memory restriction */
673 DISPLAYLEVEL(3, "test%3i : maxWindowSize < frame requirement : ", testNb++);
674 ZSTD_initDStream_usingDict(zd, CNBuffer, dictSize);
675 CHECK_Z( ZSTD_DCtx_setParameter(zd, ZSTD_d_windowLogMax, 10) ); /* too small limit */
676 outBuff.dst = decodedBuffer;
677 outBuff.size = CNBufferSize;
679 inBuff.src = compressedBuffer;
682 { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
683 if (!ZSTD_isError(r)) goto _output_error; /* must fail : frame requires > 100 bytes */
684 DISPLAYLEVEL(3, "OK (%s)\n", ZSTD_getErrorName(r)); }
685 ZSTD_DCtx_reset(zd, ZSTD_reset_session_and_parameters); /* leave zd in good shape for next tests */
687 DISPLAYLEVEL(3, "test%3i : dictionary source size and level : ", testNb++);
688 { ZSTD_DCtx* const dctx = ZSTD_createDCtx();
689 int const maxLevel = 16; /* first level with zstd_opt */
691 assert(maxLevel < ZSTD_maxCLevel());
692 CHECK_Z( ZSTD_DCtx_loadDictionary_byReference(dctx, dictionary.start, dictionary.filled) );
693 for (level = 1; level <= maxLevel; ++level) {
694 ZSTD_CDict* const cdict = ZSTD_createCDict(dictionary.start, dictionary.filled, level);
695 size_t const maxSize = MIN(1 MB, CNBufferSize);
697 for (size = 512; size <= maxSize; size <<= 1) {
698 U64 const crcOrig = XXH64(CNBuffer, size, 0);
699 ZSTD_CCtx* const cctx = ZSTD_createCCtx();
700 ZSTD_parameters savedParams;
701 getCCtxParams(cctx, &savedParams);
702 outBuff.dst = compressedBuffer;
703 outBuff.size = compressedBufferSize;
705 inBuff.src = CNBuffer;
708 CHECK_Z(ZSTD_CCtx_refCDict(cctx, cdict));
709 CHECK_Z(ZSTD_compressStream2(cctx, &outBuff, &inBuff, ZSTD_e_end));
710 CHECK(badParameters(cctx, savedParams), "Bad CCtx params");
711 if (inBuff.pos != inBuff.size) goto _output_error;
712 { ZSTD_outBuffer decOut = {decodedBuffer, size, 0};
713 ZSTD_inBuffer decIn = {outBuff.dst, outBuff.pos, 0};
714 CHECK_Z( ZSTD_decompressStream(dctx, &decOut, &decIn) );
715 if (decIn.pos != decIn.size) goto _output_error;
716 if (decOut.pos != size) goto _output_error;
717 { U64 const crcDec = XXH64(decOut.dst, decOut.pos, 0);
718 if (crcDec != crcOrig) goto _output_error;
722 ZSTD_freeCDict(cdict);
726 DISPLAYLEVEL(3, "OK\n");
728 ZSTD_CCtx_reset(zc, ZSTD_reset_session_and_parameters);
729 CHECK_Z( ZSTD_CCtx_loadDictionary(zc, dictionary.start, dictionary.filled) );
730 cSize = ZSTD_compress2(zc, compressedBuffer, compressedBufferSize, CNBuffer, MIN(CNBufferSize, 100 KB));
732 DISPLAYLEVEL(3, "test%3i : ZSTD_decompressStream() with dictionary : ", testNb++);
734 ZSTD_DCtx* dctx = ZSTD_createDCtx();
735 /* We should fail to decompress without a dictionary. */
736 ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters);
737 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
738 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
739 size_t const ret = ZSTD_decompressStream(dctx, &out, &in);
740 if (!ZSTD_isError(ret)) goto _output_error;
742 /* We should succeed to decompress with the dictionary. */
743 ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters);
744 CHECK_Z( ZSTD_DCtx_loadDictionary(dctx, dictionary.start, dictionary.filled) );
745 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
746 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
747 if (ZSTD_decompressStream(dctx, &out, &in) != 0) goto _output_error;
748 if (in.pos != in.size) goto _output_error;
750 /* The dictionary should presist across calls. */
751 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
752 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
753 if (ZSTD_decompressStream(dctx, &out, &in) != 0) goto _output_error;
754 if (in.pos != in.size) goto _output_error;
756 /* The dictionary should not be cleared by ZSTD_reset_session_only. */
757 ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only);
758 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
759 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
760 if (ZSTD_decompressStream(dctx, &out, &in) != 0) goto _output_error;
761 if (in.pos != in.size) goto _output_error;
763 /* When we reset the context the dictionary is cleared. */
764 ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters);
765 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
766 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
767 size_t const ret = ZSTD_decompressStream(dctx, &out, &in);
768 if (!ZSTD_isError(ret)) goto _output_error;
772 DISPLAYLEVEL(3, "OK \n");
774 DISPLAYLEVEL(3, "test%3i : ZSTD_resetDStream() with dictionary : ", testNb++);
776 ZSTD_DCtx* dctx = ZSTD_createDCtx();
777 /* We should succeed to decompress with the dictionary. */
778 ZSTD_resetDStream(dctx);
779 CHECK_Z( ZSTD_DCtx_loadDictionary(dctx, dictionary.start, dictionary.filled) );
780 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
781 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
782 if (ZSTD_decompressStream(dctx, &out, &in) != 0) goto _output_error;
783 if (in.pos != in.size) goto _output_error;
785 /* The dictionary should not be cleared by ZSTD_resetDStream(). */
786 ZSTD_resetDStream(dctx);
787 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
788 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
789 if (ZSTD_decompressStream(dctx, &out, &in) != 0) goto _output_error;
790 if (in.pos != in.size) goto _output_error;
792 /* The dictionary should be cleared by ZSTD_initDStream(). */
793 CHECK_Z( ZSTD_initDStream(dctx) );
794 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
795 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
796 size_t const ret = ZSTD_decompressStream(dctx, &out, &in);
797 if (!ZSTD_isError(ret)) goto _output_error;
801 DISPLAYLEVEL(3, "OK \n");
803 DISPLAYLEVEL(3, "test%3i : ZSTD_decompressStream() with ddict : ", testNb++);
805 ZSTD_DCtx* dctx = ZSTD_createDCtx();
806 ZSTD_DDict* ddict = ZSTD_createDDict(dictionary.start, dictionary.filled);
807 /* We should succeed to decompress with the ddict. */
808 ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters);
809 CHECK_Z( ZSTD_DCtx_refDDict(dctx, ddict) );
810 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
811 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
812 if (ZSTD_decompressStream(dctx, &out, &in) != 0) goto _output_error;
813 if (in.pos != in.size) goto _output_error;
815 /* The ddict should presist across calls. */
816 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
817 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
818 if (ZSTD_decompressStream(dctx, &out, &in) != 0) goto _output_error;
819 if (in.pos != in.size) goto _output_error;
821 /* When we reset the context the ddict is cleared. */
822 ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters);
823 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
824 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
825 size_t const ret = ZSTD_decompressStream(dctx, &out, &in);
826 if (!ZSTD_isError(ret)) goto _output_error;
829 ZSTD_freeDDict(ddict);
831 DISPLAYLEVEL(3, "OK \n");
833 DISPLAYLEVEL(3, "test%3i : ZSTD_decompressDCtx() with prefix : ", testNb++);
835 ZSTD_DCtx* dctx = ZSTD_createDCtx();
836 /* We should succeed to decompress with the prefix. */
837 ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters);
838 CHECK_Z( ZSTD_DCtx_refPrefix_advanced(dctx, dictionary.start, dictionary.filled, ZSTD_dct_auto) );
839 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
840 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
841 if (ZSTD_decompressStream(dctx, &out, &in) != 0) goto _output_error;
842 if (in.pos != in.size) goto _output_error;
844 /* The prefix should be cleared after the first compression. */
845 { ZSTD_outBuffer out = {decodedBuffer, decodedBufferSize, 0};
846 ZSTD_inBuffer in = {compressedBuffer, cSize, 0};
847 size_t const ret = ZSTD_decompressStream(dctx, &out, &in);
848 if (!ZSTD_isError(ret)) goto _output_error;
852 DISPLAYLEVEL(3, "OK \n");
854 DISPLAYLEVEL(3, "test%3i : ZSTD_initDStream*() with dictionary : ", testNb++);
856 ZSTD_DCtx* dctx = ZSTD_createDCtx();
857 ZSTD_DDict* ddict = ZSTD_createDDict(dictionary.start, dictionary.filled);
859 /* We should succeed to decompress with the dictionary. */
860 CHECK_Z( ZSTD_initDStream_usingDict(dctx, dictionary.start, dictionary.filled) );
861 CHECK_Z( ZSTD_decompressDCtx(dctx, decodedBuffer, decodedBufferSize, compressedBuffer, cSize) );
862 /* The dictionary should presist across calls. */
863 CHECK_Z( ZSTD_decompressDCtx(dctx, decodedBuffer, decodedBufferSize, compressedBuffer, cSize) );
864 /* We should succeed to decompress with the ddict. */
865 CHECK_Z( ZSTD_initDStream_usingDDict(dctx, ddict) );
866 CHECK_Z( ZSTD_decompressDCtx(dctx, decodedBuffer, decodedBufferSize, compressedBuffer, cSize) );
867 /* The ddict should presist across calls. */
868 CHECK_Z( ZSTD_decompressDCtx(dctx, decodedBuffer, decodedBufferSize, compressedBuffer, cSize) );
869 /* When we reset the context the ddict is cleared. */
870 CHECK_Z( ZSTD_initDStream(dctx) );
871 ret = ZSTD_decompressDCtx(dctx, decodedBuffer, decodedBufferSize, compressedBuffer, cSize);
872 if (!ZSTD_isError(ret)) goto _output_error;
874 ZSTD_freeDDict(ddict);
876 DISPLAYLEVEL(3, "OK \n");
878 DISPLAYLEVEL(3, "test%3i : ZSTD_initCStream_usingCDict_advanced with masked dictID : ", testNb++);
879 { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBufferSize, dictionary.filled);
880 ZSTD_frameParameters const fParams = { 1 /* contentSize */, 1 /* checksum */, 1 /* noDictID */};
881 ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dct_auto, cParams, ZSTD_defaultCMem);
882 size_t const initError = ZSTD_initCStream_usingCDict_advanced(zc, cdict, fParams, CNBufferSize);
883 if (ZSTD_isError(initError)) goto _output_error;
884 outBuff.dst = compressedBuffer;
885 outBuff.size = compressedBufferSize;
887 inBuff.src = CNBuffer;
888 inBuff.size = CNBufferSize;
890 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
891 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
892 { size_t const r = ZSTD_endStream(zc, &outBuff);
893 if (r != 0) goto _output_error; } /* error, or some data not flushed */
895 ZSTD_freeCDict(cdict);
896 DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (unsigned)cSize, (double)cSize/CNBufferSize*100);
899 DISPLAYLEVEL(3, "test%3i : try retrieving dictID from frame : ", testNb++);
900 { U32 const did = ZSTD_getDictID_fromFrame(compressedBuffer, cSize);
901 if (did != 0) goto _output_error;
903 DISPLAYLEVEL(3, "OK (not detected) \n");
905 DISPLAYLEVEL(3, "test%3i : decompress without dictionary : ", testNb++);
906 { size_t const r = ZSTD_decompress(decodedBuffer, CNBufferSize, compressedBuffer, cSize);
907 if (!ZSTD_isError(r)) goto _output_error; /* must fail : dictionary not used */
908 DISPLAYLEVEL(3, "OK (%s)\n", ZSTD_getErrorName(r));
911 DISPLAYLEVEL(3, "test%3i : compress with ZSTD_CCtx_refPrefix : ", testNb++);
912 CHECK_Z( ZSTD_CCtx_refPrefix(zc, dictionary.start, dictionary.filled) );
913 outBuff.dst = compressedBuffer;
914 outBuff.size = compressedBufferSize;
916 inBuff.src = CNBuffer;
917 inBuff.size = CNBufferSize;
919 CHECK_Z( ZSTD_compressStream2(zc, &outBuff, &inBuff, ZSTD_e_end) );
920 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
922 DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (unsigned)cSize, (double)cSize/CNBufferSize*100);
924 DISPLAYLEVEL(3, "test%3i : decompress with ZSTD_DCtx_refPrefix : ", testNb++);
925 CHECK_Z( ZSTD_DCtx_refPrefix(zd, dictionary.start, dictionary.filled) );
926 outBuff.dst = decodedBuffer;
927 outBuff.size = CNBufferSize;
929 inBuff.src = compressedBuffer;
932 CHECK_Z( ZSTD_decompressStream(zd, &outBuff, &inBuff) );
933 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
934 if (outBuff.pos != CNBufferSize) goto _output_error; /* must regenerate whole input */
935 DISPLAYLEVEL(3, "OK \n");
937 DISPLAYLEVEL(3, "test%3i : decompress without dictionary (should fail): ", testNb++);
938 { size_t const r = ZSTD_decompress(decodedBuffer, CNBufferSize, compressedBuffer, cSize);
939 if (!ZSTD_isError(r)) goto _output_error; /* must fail : dictionary not used */
940 DISPLAYLEVEL(3, "OK (%s)\n", ZSTD_getErrorName(r));
943 DISPLAYLEVEL(3, "test%3i : compress again with ZSTD_compressStream2 : ", testNb++);
944 outBuff.dst = compressedBuffer;
945 outBuff.size = compressedBufferSize;
947 inBuff.src = CNBuffer;
948 inBuff.size = CNBufferSize;
950 CHECK_Z( ZSTD_compressStream2(zc, &outBuff, &inBuff, ZSTD_e_end) );
951 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
953 DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (unsigned)cSize, (double)cSize/CNBufferSize*100);
955 DISPLAYLEVEL(3, "test%3i : decompress without dictionary (should work): ", testNb++);
956 CHECK_Z( ZSTD_decompress(decodedBuffer, CNBufferSize, compressedBuffer, cSize) );
957 DISPLAYLEVEL(3, "OK \n");
960 DISPLAYLEVEL(3, "test%3i : ZSTD_initCStream_advanced with pledgedSrcSize=0 and dict : ", testNb++);
961 { ZSTD_parameters params = ZSTD_getParams(5, 0, 0);
962 params.fParams.contentSizeFlag = 1;
963 CHECK_Z( ZSTD_initCStream_advanced(zc, dictionary.start, dictionary.filled, params, 0 /* pledgedSrcSize==0 means "empty" when params.fParams.contentSizeFlag is set */) );
964 } /* cstream advanced shall write content size = 0 */
965 outBuff.dst = compressedBuffer;
966 outBuff.size = compressedBufferSize;
968 inBuff.src = CNBuffer;
971 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
972 if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error;
974 if (ZSTD_findDecompressedSize(compressedBuffer, cSize) != 0) goto _output_error;
975 DISPLAYLEVEL(3, "OK \n");
977 DISPLAYLEVEL(3, "test%3i : pledgedSrcSize == 0 behaves properly : ", testNb++);
978 { ZSTD_parameters params = ZSTD_getParams(5, 0, 0);
979 params.fParams.contentSizeFlag = 1;
980 CHECK_Z( ZSTD_initCStream_advanced(zc, NULL, 0, params, 0) );
981 } /* cstream advanced shall write content size = 0 */
982 inBuff.src = CNBuffer;
985 outBuff.dst = compressedBuffer;
986 outBuff.size = compressedBufferSize;
988 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
989 if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error;
991 if (ZSTD_findDecompressedSize(compressedBuffer, cSize) != 0) goto _output_error;
993 ZSTD_resetCStream(zc, 0); /* resetCStream should treat 0 as unknown */
994 outBuff.dst = compressedBuffer;
995 outBuff.size = compressedBufferSize;
997 inBuff.src = CNBuffer;
1000 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
1001 if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error;
1002 cSize = outBuff.pos;
1003 if (ZSTD_findDecompressedSize(compressedBuffer, cSize) != ZSTD_CONTENTSIZE_UNKNOWN) goto _output_error;
1004 DISPLAYLEVEL(3, "OK \n");
1006 /* Basic multithreading compression test */
1007 DISPLAYLEVEL(3, "test%3i : compress %u bytes with multiple threads : ", testNb++, COMPRESSIBLE_NOISE_LENGTH);
1008 { ZSTD_parameters const params = ZSTD_getParams(1, 0, 0);
1010 CHECK_Z( ZSTDMT_getMTCtxParameter(mtctx, ZSTDMT_p_jobSize, &jobSize));
1011 CHECK(jobSize != 0, "job size non-zero");
1012 CHECK_Z( ZSTDMT_initCStream_advanced(mtctx, CNBuffer, dictSize, params, CNBufferSize) );
1013 CHECK_Z( ZSTDMT_getMTCtxParameter(mtctx, ZSTDMT_p_jobSize, &jobSize));
1014 CHECK(jobSize != 0, "job size non-zero");
1016 outBuff.dst = compressedBuffer;
1017 outBuff.size = compressedBufferSize;
1019 inBuff.src = CNBuffer;
1020 inBuff.size = CNBufferSize;
1022 { size_t const compressResult = ZSTDMT_compressStream_generic(mtctx, &outBuff, &inBuff, ZSTD_e_end);
1023 if (compressResult != 0) goto _output_error; /* compression must be completed in a single round */
1025 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
1026 { size_t const compressedSize = ZSTD_findFrameCompressedSize(compressedBuffer, outBuff.pos);
1027 if (compressedSize != outBuff.pos) goto _output_error; /* must be a full valid frame */
1029 DISPLAYLEVEL(3, "OK \n");
1031 /* Complex multithreading + dictionary test */
1032 { U32 const nbWorkers = 2;
1033 size_t const jobSize = 4 * 1 MB;
1034 size_t const srcSize = jobSize * nbWorkers; /* we want each job to have predictable size */
1035 size_t const segLength = 2 KB;
1036 size_t const offset = 600 KB; /* must be larger than window defined in cdict */
1037 size_t const start = jobSize + (offset-1);
1038 const BYTE* const srcToCopy = (const BYTE*)CNBuffer + start;
1039 BYTE* const dst = (BYTE*)CNBuffer + start - offset;
1040 DISPLAYLEVEL(3, "test%3i : compress %u bytes with multiple threads + dictionary : ", testNb++, (unsigned)srcSize);
1041 CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_c_compressionLevel, 3) );
1042 CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_c_nbWorkers, nbWorkers) );
1043 CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_c_jobSize, jobSize) );
1044 assert(start > offset);
1045 assert(start + segLength < COMPRESSIBLE_NOISE_LENGTH);
1046 memcpy(dst, srcToCopy, segLength); /* create a long repetition at long distance for job 2 */
1047 outBuff.dst = compressedBuffer;
1048 outBuff.size = compressedBufferSize;
1050 inBuff.src = CNBuffer;
1051 inBuff.size = srcSize; assert(srcSize < COMPRESSIBLE_NOISE_LENGTH);
1054 { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, 4 KB, dictionary.filled); /* intentionally lies on estimatedSrcSize, to push cdict into targeting a small window size */
1055 ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dct_fullDict, cParams, ZSTD_defaultCMem);
1056 DISPLAYLEVEL(5, "cParams.windowLog = %u : ", cParams.windowLog);
1057 CHECK_Z( ZSTD_CCtx_refCDict(zc, cdict) );
1058 CHECK_Z( ZSTD_compressStream2(zc, &outBuff, &inBuff, ZSTD_e_end) );
1059 CHECK_Z( ZSTD_CCtx_refCDict(zc, NULL) ); /* do not keep a reference to cdict, as its lifetime ends */
1060 ZSTD_freeCDict(cdict);
1062 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
1063 cSize = outBuff.pos;
1064 DISPLAYLEVEL(3, "OK \n");
1066 DISPLAYLEVEL(3, "test%3i : decompress large frame created from multiple threads + dictionary : ", testNb++);
1067 { ZSTD_DStream* const dstream = ZSTD_createDCtx();
1068 ZSTD_frameHeader zfh;
1069 ZSTD_getFrameHeader(&zfh, compressedBuffer, cSize);
1070 DISPLAYLEVEL(5, "frame windowsize = %u : ", (unsigned)zfh.windowSize);
1071 outBuff.dst = decodedBuffer;
1072 outBuff.size = CNBufferSize;
1074 inBuff.src = compressedBuffer;
1076 CHECK_Z( ZSTD_initDStream_usingDict(dstream, dictionary.start, dictionary.filled) );
1077 inBuff.size = 1; /* avoid shortcut to single-pass mode */
1078 CHECK_Z( ZSTD_decompressStream(dstream, &outBuff, &inBuff) );
1079 inBuff.size = cSize;
1080 CHECK_Z( ZSTD_decompressStream(dstream, &outBuff, &inBuff) );
1081 if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
1082 ZSTD_freeDStream(dstream);
1084 DISPLAYLEVEL(3, "OK \n");
1086 DISPLAYLEVEL(3, "test%3i : check dictionary FSE tables can represent every code : ", testNb++);
1087 { unsigned const kMaxWindowLog = 24;
1089 ZSTD_compressionParameters cParams = ZSTD_getCParams(3, 1U << kMaxWindowLog, 1024);
1092 SEQ_stream seq = SEQ_initStream(0x87654321);
1096 XXH64_reset(&xxh, 0);
1097 cParams.windowLog = kMaxWindowLog;
1098 cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dct_fullDict, cParams, ZSTD_defaultCMem);
1099 ddict = ZSTD_createDDict(dictionary.start, dictionary.filled);
1101 if (!cdict || !ddict) goto _output_error;
1103 ZSTD_CCtx_reset(zc, ZSTD_reset_session_only);
1104 ZSTD_resetDStream(zd);
1105 CHECK_Z(ZSTD_CCtx_refCDict(zc, cdict));
1106 CHECK_Z(ZSTD_initDStream_usingDDict(zd, ddict));
1107 CHECK_Z(ZSTD_DCtx_setParameter(zd, ZSTD_d_windowLogMax, kMaxWindowLog));
1108 /* Test all values < 300 */
1109 for (value = 0; value < 300; ++value) {
1110 for (type = (SEQ_gen_type)0; type < SEQ_gen_max; ++type) {
1111 CHECK_Z(SEQ_generateRoundTrip(zc, zd, &xxh, &seq, type, value));
1114 /* Test values 2^8 to 2^17 */
1115 for (value = (1 << 8); value < (1 << 17); value <<= 1) {
1116 for (type = (SEQ_gen_type)0; type < SEQ_gen_max; ++type) {
1117 CHECK_Z(SEQ_generateRoundTrip(zc, zd, &xxh, &seq, type, value));
1118 CHECK_Z(SEQ_generateRoundTrip(zc, zd, &xxh, &seq, type, value + (value >> 2)));
1121 /* Test offset values up to the max window log */
1122 for (value = 8; value <= kMaxWindowLog; ++value) {
1123 CHECK_Z(SEQ_generateRoundTrip(zc, zd, &xxh, &seq, SEQ_gen_of, (1U << value) - 1));
1126 CHECK_Z(SEQ_roundTrip(zc, zd, &xxh, NULL, 0, ZSTD_e_end));
1127 CHECK(SEQ_digest(&seq) != XXH64_digest(&xxh), "SEQ XXH64 does not match");
1129 ZSTD_freeCDict(cdict);
1130 ZSTD_freeDDict(ddict);
1132 DISPLAYLEVEL(3, "OK \n");
1134 DISPLAYLEVEL(3, "test%3i : ZSTD_initCStream_srcSize sets requestedParams : ", testNb++);
1136 CHECK_Z(ZSTD_initCStream_srcSize(zc, 11, ZSTD_CONTENTSIZE_UNKNOWN));
1137 CHECK_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_compressionLevel, &level));
1138 CHECK(level != 11, "Compression level does not match");
1139 ZSTD_resetCStream(zc, ZSTD_CONTENTSIZE_UNKNOWN);
1140 CHECK_Z(ZSTD_CCtx_getParameter(zc, ZSTD_c_compressionLevel, &level));
1141 CHECK(level != 11, "Compression level does not match");
1143 DISPLAYLEVEL(3, "OK \n");
1145 DISPLAYLEVEL(3, "test%3i : ZSTD_initCStream_advanced sets requestedParams : ", testNb++);
1146 { ZSTD_parameters const params = ZSTD_getParams(9, 0, 0);
1147 CHECK_Z(ZSTD_initCStream_advanced(zc, NULL, 0, params, ZSTD_CONTENTSIZE_UNKNOWN));
1148 CHECK(badParameters(zc, params), "Compression parameters do not match");
1149 ZSTD_resetCStream(zc, ZSTD_CONTENTSIZE_UNKNOWN);
1150 CHECK(badParameters(zc, params), "Compression parameters do not match");
1152 DISPLAYLEVEL(3, "OK \n");
1154 /* Overlen overwriting window data bug */
1155 DISPLAYLEVEL(3, "test%3i : wildcopy doesn't overwrite potential match data : ", testNb++);
1156 { /* This test has a window size of 1024 bytes and consists of 3 blocks:
1157 1. 'a' repeated 517 times
1158 2. 'b' repeated 516 times
1159 3. a compressed block with no literals and 3 sequence commands:
1160 litlength = 0, offset = 24, match length = 24
1161 litlength = 0, offset = 24, match length = 3 (this one creates an overlength write of length 2*WILDCOPY_OVERLENGTH - 3)
1162 litlength = 0, offset = 1021, match length = 3 (this one will try to read from overwritten data if the buffer is too small) */
1164 const char* testCase =
1165 "\x28\xB5\x2F\xFD\x04\x00\x4C\x00\x00\x10\x61\x61\x01\x00\x00\x2A"
1166 "\x80\x05\x44\x00\x00\x08\x62\x01\x00\x00\x2A\x20\x04\x5D\x00\x00"
1167 "\x00\x03\x40\x00\x00\x64\x60\x27\xB0\xE0\x0C\x67\x62\xCE\xE0";
1168 ZSTD_DStream* const zds = ZSTD_createDStream();
1169 if (zds==NULL) goto _output_error;
1171 CHECK_Z( ZSTD_initDStream(zds) );
1172 inBuff.src = testCase;
1175 outBuff.dst = decodedBuffer;
1176 outBuff.size = CNBufferSize;
1179 while (inBuff.pos < inBuff.size) {
1180 CHECK_Z( ZSTD_decompressStream(zds, &outBuff, &inBuff) );
1183 ZSTD_freeDStream(zds);
1185 DISPLAYLEVEL(3, "OK \n");
1187 /* Small Sequence Section bug */
1188 DISPLAYLEVEL(3, "test%3i : decompress blocks with small sequences section : ", testNb++);
1189 { /* This test consists of 3 blocks. Each block has one sequence.
1190 The sequence has literal length of 10, match length of 10 and offset of 10.
1191 The sequence value and compression mode for the blocks are following:
1192 The order of values are ll, ml, of.
1193 - First block : (10, 7, 13) (rle, rle, rle)
1194 - size of sequences section: 6 bytes (1 byte for nbSeq, 1 byte for encoding mode, 3 bytes for rle, 1 byte bitstream)
1195 - Second block : (10, 7, 1) (repeat, repeat, rle)
1196 - size of sequences section: 4 bytes (1 byte for nbSeq, 1 byte for encoding mode, 1 bytes for rle, 1 byte bitstream)
1197 - Third block : (10, 7, 1) (repeat, repeat, repeat)
1198 - size of sequences section: 3 bytes (1 byte for nbSeq, 1 byte for encoding mode, 1 byte bitstream) */
1200 unsigned char compressed[] = {
1201 0x28, 0xb5, 0x2f, 0xfd, 0x24, 0x3c, 0x35, 0x01, 0x00, 0xf0, 0x85, 0x08,
1202 0xc2, 0xc4, 0x70, 0xcf, 0xd7, 0xc0, 0x96, 0x7e, 0x4c, 0x6b, 0xa9, 0x8b,
1203 0xbc, 0xc5, 0xb6, 0xd9, 0x7f, 0x4c, 0xf1, 0x05, 0xa6, 0x54, 0xef, 0xac,
1204 0x69, 0x94, 0x89, 0x1c, 0x03, 0x44, 0x0a, 0x07, 0x00, 0xb4, 0x04, 0x80,
1207 unsigned int compressedSize = 51;
1208 unsigned char decompressed[] = {
1209 0x85, 0x08, 0xc2, 0xc4, 0x70, 0xcf, 0xd7, 0xc0, 0x96, 0x7e, 0x85, 0x08,
1210 0xc2, 0xc4, 0x70, 0xcf, 0xd7, 0xc0, 0x96, 0x7e, 0x4c, 0x6b, 0xa9, 0x8b,
1211 0xbc, 0xc5, 0xb6, 0xd9, 0x7f, 0x4c, 0x4c, 0x6b, 0xa9, 0x8b, 0xbc, 0xc5,
1212 0xb6, 0xd9, 0x7f, 0x4c, 0xf1, 0x05, 0xa6, 0x54, 0xef, 0xac, 0x69, 0x94,
1213 0x89, 0x1c, 0xf1, 0x05, 0xa6, 0x54, 0xef, 0xac, 0x69, 0x94, 0x89, 0x1c
1215 unsigned int decompressedSize = 60;
1217 ZSTD_DStream* const zds = ZSTD_createDStream();
1218 if (zds==NULL) goto _output_error;
1220 CHECK_Z( ZSTD_initDStream(zds) );
1221 inBuff.src = compressed;
1222 inBuff.size = compressedSize;
1224 outBuff.dst = decodedBuffer;
1225 outBuff.size = CNBufferSize;
1228 CHECK(ZSTD_decompressStream(zds, &outBuff, &inBuff) != 0,
1229 "Decompress did not reach the end of frame");
1230 CHECK(inBuff.pos != inBuff.size, "Decompress did not fully consume input");
1231 CHECK(outBuff.pos != decompressedSize, "Decompressed size does not match");
1232 CHECK(memcmp(outBuff.dst, decompressed, decompressedSize) != 0,
1233 "Decompressed data does not match");
1235 ZSTD_freeDStream(zds);
1237 DISPLAYLEVEL(3, "OK \n");
1239 DISPLAYLEVEL(3, "test%3i : dictionary + uncompressible block + reusing tables checks offset table validity: ", testNb++);
1240 { ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(
1241 dictionary.start, dictionary.filled,
1242 ZSTD_dlm_byRef, ZSTD_dct_fullDict,
1243 ZSTD_getCParams(3, 0, dictionary.filled),
1245 const size_t inbufsize = 2 * 128 * 1024; /* 2 blocks */
1246 const size_t outbufsize = ZSTD_compressBound(inbufsize);
1247 size_t inbufpos = 0;
1248 size_t cursegmentlen;
1249 BYTE *inbuf = (BYTE *)malloc(inbufsize);
1250 BYTE *outbuf = (BYTE *)malloc(outbufsize);
1251 BYTE *checkbuf = (BYTE *)malloc(inbufsize);
1254 CHECK(cdict == NULL, "failed to alloc cdict");
1255 CHECK(inbuf == NULL, "failed to alloc input buffer");
1257 /* first block is uncompressible */
1258 cursegmentlen = 128 * 1024;
1259 RDG_genBuffer(inbuf + inbufpos, cursegmentlen, 0., 0., seed);
1260 inbufpos += cursegmentlen;
1262 /* second block is compressible */
1263 cursegmentlen = 128 * 1024 - 256;
1264 RDG_genBuffer(inbuf + inbufpos, cursegmentlen, 0.05, 0., seed);
1265 inbufpos += cursegmentlen;
1267 /* and includes a very long backref */
1268 cursegmentlen = 128;
1269 memcpy(inbuf + inbufpos, dictionary.start + 256, cursegmentlen);
1270 inbufpos += cursegmentlen;
1272 /* and includes a very long backref */
1273 cursegmentlen = 128;
1274 memcpy(inbuf + inbufpos, dictionary.start + 128, cursegmentlen);
1275 inbufpos += cursegmentlen;
1277 ret = ZSTD_compress_usingCDict(zc, outbuf, outbufsize, inbuf, inbufpos, cdict);
1280 ret = ZSTD_decompress_usingDict(zd, checkbuf, inbufsize, outbuf, ret, dictionary.start, dictionary.filled);
1283 CHECK(memcmp(inbuf, checkbuf, inbufpos), "start and finish buffers don't match");
1285 ZSTD_freeCDict(cdict);
1290 DISPLAYLEVEL(3, "OK \n");
1292 DISPLAYLEVEL(3, "test%3i : dictionary + small blocks + reusing tables checks offset table validity: ", testNb++);
1293 { ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(
1294 dictionary.start, dictionary.filled,
1295 ZSTD_dlm_byRef, ZSTD_dct_fullDict,
1296 ZSTD_getCParams(3, 0, dictionary.filled),
1298 ZSTD_outBuffer out = {compressedBuffer, compressedBufferSize, 0};
1299 int remainingInput = 256 * 1024;
1302 CHECK_Z(ZSTD_CCtx_reset(zc, ZSTD_reset_session_and_parameters));
1303 CHECK_Z(ZSTD_CCtx_refCDict(zc, cdict));
1304 CHECK_Z(ZSTD_CCtx_setParameter(zc, ZSTD_c_checksumFlag, 1));
1305 /* Write a bunch of 6 byte blocks */
1306 while (remainingInput > 0) {
1307 char testBuffer[6] = "\xAA\xAA\xAA\xAA\xAA\xAA";
1308 const size_t kSmallBlockSize = sizeof(testBuffer);
1309 ZSTD_inBuffer in = {testBuffer, kSmallBlockSize, 0};
1311 CHECK_Z(ZSTD_compressStream2(zc, &out, &in, ZSTD_e_flush));
1312 CHECK(in.pos != in.size, "input not fully consumed");
1313 remainingInput -= kSmallBlockSize;
1315 /* Write several very long offset matches into the dictionary */
1316 for (offset = 1024; offset >= 0; offset -= 128) {
1317 ZSTD_inBuffer in = {dictionary.start + offset, 128, 0};
1318 ZSTD_EndDirective flush = offset > 0 ? ZSTD_e_continue : ZSTD_e_end;
1319 CHECK_Z(ZSTD_compressStream2(zc, &out, &in, flush));
1320 CHECK(in.pos != in.size, "input not fully consumed");
1322 /* Ensure decompression works */
1323 CHECK_Z(ZSTD_decompress_usingDict(zd, decodedBuffer, CNBufferSize, out.dst, out.pos, dictionary.start, dictionary.filled));
1325 ZSTD_freeCDict(cdict);
1327 DISPLAYLEVEL(3, "OK \n");
1330 FUZ_freeDictionary(dictionary);
1331 ZSTD_freeCStream(zc);
1332 ZSTD_freeDStream(zd);
1333 ZSTDMT_freeCCtx(mtctx);
1335 free(compressedBuffer);
1336 free(decodedBuffer);
1341 DISPLAY("Error detected in Unit tests ! \n");
1346 /* ====== Fuzzer tests ====== */
1348 static size_t findDiff(const void* buf1, const void* buf2, size_t max)
1350 const BYTE* b1 = (const BYTE*)buf1;
1351 const BYTE* b2 = (const BYTE*)buf2;
1353 for (u=0; u<max; u++) {
1354 if (b1[u] != b2[u]) break;
1357 DISPLAY("=> No difference detected within %u bytes \n", (unsigned)max);
1360 DISPLAY("Error at position %u / %u \n", (unsigned)u, (unsigned)max);
1362 DISPLAY(" %02X %02X %02X ",
1363 b1[u-3], b1[u-2], b1[u-1]);
1364 DISPLAY(" :%02X: %02X %02X %02X %02X %02X \n",
1365 b1[u], b1[u+1], b1[u+2], b1[u+3], b1[u+4], b1[u+5]);
1367 DISPLAY(" %02X %02X %02X ",
1368 b2[u-3], b2[u-2], b2[u-1]);
1369 DISPLAY(" :%02X: %02X %02X %02X %02X %02X \n",
1370 b2[u], b2[u+1], b2[u+2], b2[u+3], b2[u+4], b2[u+5]);
1374 static size_t FUZ_rLogLength(U32* seed, U32 logLength)
1376 size_t const lengthMask = ((size_t)1 << logLength) - 1;
1377 return (lengthMask+1) + (FUZ_rand(seed) & lengthMask);
1380 static size_t FUZ_randomLength(U32* seed, U32 maxLog)
1382 U32 const logLength = FUZ_rand(seed) % maxLog;
1383 return FUZ_rLogLength(seed, logLength);
1386 /* Return value in range minVal <= v <= maxVal */
1387 static U32 FUZ_randomClampedLength(U32* seed, U32 minVal, U32 maxVal)
1389 U32 const mod = maxVal < minVal ? 1 : (maxVal + 1) - minVal;
1390 return (U32)((FUZ_rand(seed) % mod) + minVal);
1393 static int fuzzerTests(U32 seed, unsigned nbTests, unsigned startTest, double compressibility, int bigTests)
1395 U32 const maxSrcLog = bigTests ? 24 : 22;
1396 static const U32 maxSampleLog = 19;
1397 size_t const srcBufferSize = (size_t)1<<maxSrcLog;
1398 BYTE* cNoiseBuffer[5];
1399 size_t const copyBufferSize = srcBufferSize + (1<<maxSampleLog);
1400 BYTE* const copyBuffer = (BYTE*)malloc (copyBufferSize);
1401 size_t const cBufferSize = ZSTD_compressBound(srcBufferSize);
1402 BYTE* const cBuffer = (BYTE*)malloc (cBufferSize);
1403 size_t const dstBufferSize = srcBufferSize;
1404 BYTE* const dstBuffer = (BYTE*)malloc (dstBufferSize);
1406 unsigned testNb = 0;
1407 U32 coreSeed = seed;
1408 ZSTD_CStream* zc = ZSTD_createCStream(); /* will be re-created sometimes */
1409 ZSTD_DStream* zd = ZSTD_createDStream(); /* will be re-created sometimes */
1410 ZSTD_DStream* const zd_noise = ZSTD_createDStream();
1411 UTIL_time_t const startClock = UTIL_getTime();
1412 const BYTE* dict = NULL; /* can keep same dict on 2 consecutive tests */
1413 size_t dictSize = 0;
1415 U32 const cLevelMax = bigTests ? (U32)ZSTD_maxCLevel() : g_cLevelMax_smallTests;
1418 cNoiseBuffer[0] = (BYTE*)malloc (srcBufferSize);
1419 cNoiseBuffer[1] = (BYTE*)malloc (srcBufferSize);
1420 cNoiseBuffer[2] = (BYTE*)malloc (srcBufferSize);
1421 cNoiseBuffer[3] = (BYTE*)malloc (srcBufferSize);
1422 cNoiseBuffer[4] = (BYTE*)malloc (srcBufferSize);
1423 CHECK (!cNoiseBuffer[0] || !cNoiseBuffer[1] || !cNoiseBuffer[2] || !cNoiseBuffer[3] || !cNoiseBuffer[4] ||
1424 !copyBuffer || !dstBuffer || !cBuffer || !zc || !zd || !zd_noise ,
1425 "Not enough memory, fuzzer tests cancelled");
1427 /* Create initial samples */
1428 RDG_genBuffer(cNoiseBuffer[0], srcBufferSize, 0.00, 0., coreSeed); /* pure noise */
1429 RDG_genBuffer(cNoiseBuffer[1], srcBufferSize, 0.05, 0., coreSeed); /* barely compressible */
1430 RDG_genBuffer(cNoiseBuffer[2], srcBufferSize, compressibility, 0., coreSeed);
1431 RDG_genBuffer(cNoiseBuffer[3], srcBufferSize, 0.95, 0., coreSeed); /* highly compressible */
1432 RDG_genBuffer(cNoiseBuffer[4], srcBufferSize, 1.00, 0., coreSeed); /* sparse content */
1433 memset(copyBuffer, 0x65, copyBufferSize); /* make copyBuffer considered initialized */
1434 ZSTD_initDStream_usingDict(zd, NULL, 0); /* ensure at least one init */
1436 /* catch up testNb */
1437 for (testNb=1; testNb < startTest; testNb++)
1438 FUZ_rand(&coreSeed);
1441 for ( ; (testNb <= nbTests) || (UTIL_clockSpanMicro(startClock) < g_clockTime) ; testNb++ ) {
1443 const BYTE* srcBuffer;
1444 size_t totalTestSize, totalGenSize, cSize;
1445 XXH64_state_t xxhState;
1447 U32 resetAllowed = 1;
1451 FUZ_rand(&coreSeed);
1452 lseed = coreSeed ^ prime32;
1453 if (nbTests >= testNb) {
1454 DISPLAYUPDATE(2, "\r%6u/%6u ", testNb, nbTests);
1456 DISPLAYUPDATE(2, "\r%6u ", testNb);
1459 /* states full reset (deliberately not synchronized) */
1460 /* some issues can only happen when reusing states */
1461 if ((FUZ_rand(&lseed) & 0xFF) == 131) {
1462 ZSTD_freeCStream(zc);
1463 zc = ZSTD_createCStream();
1464 CHECK(zc==NULL, "ZSTD_createCStream : allocation error");
1467 if ((FUZ_rand(&lseed) & 0xFF) == 132) {
1468 ZSTD_freeDStream(zd);
1469 zd = ZSTD_createDStream();
1470 CHECK(zd==NULL, "ZSTD_createDStream : allocation error");
1471 CHECK_Z( ZSTD_initDStream_usingDict(zd, NULL, 0) ); /* ensure at least one init */
1474 /* srcBuffer selection [0-4] */
1475 { U32 buffNb = FUZ_rand(&lseed) & 0x7F;
1476 if (buffNb & 7) buffNb=2; /* most common : compressible (P) */
1480 const U32 tnb[2] = { 1, 3 }; /* barely/highly compressible */
1481 buffNb = tnb[buffNb >> 3];
1483 const U32 tnb[2] = { 0, 4 }; /* not compressible / sparse */
1484 buffNb = tnb[buffNb >> 3];
1486 srcBuffer = cNoiseBuffer[buffNb];
1489 /* compression init */
1490 if ((FUZ_rand(&lseed)&1) /* at beginning, to keep same nb of rand */
1491 && oldTestLog /* at least one test happened */ && resetAllowed) {
1492 maxTestSize = FUZ_randomLength(&lseed, oldTestLog+2);
1493 maxTestSize = MIN(maxTestSize, srcBufferSize-16);
1494 { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? 0 : maxTestSize;
1495 CHECK_Z( ZSTD_resetCStream(zc, pledgedSrcSize) );
1498 U32 const testLog = FUZ_rand(&lseed) % maxSrcLog;
1499 U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog;
1500 U32 const cLevelCandidate = ( FUZ_rand(&lseed) %
1502 (MAX(testLog, dictLog) / 3)))
1504 U32 const cLevel = MIN(cLevelCandidate, cLevelMax);
1505 maxTestSize = FUZ_rLogLength(&lseed, testLog);
1506 oldTestLog = testLog;
1507 /* random dictionary selection */
1508 dictSize = ((FUZ_rand(&lseed)&7)==1) ? FUZ_rLogLength(&lseed, dictLog) : 0;
1509 { size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize);
1510 dict = srcBuffer + dictStart;
1512 { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? ZSTD_CONTENTSIZE_UNKNOWN : maxTestSize;
1513 ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize);
1514 params.fParams.checksumFlag = FUZ_rand(&lseed) & 1;
1515 params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1;
1516 params.fParams.contentSizeFlag = FUZ_rand(&lseed) & 1;
1517 CHECK_Z ( ZSTD_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize) );
1520 /* multi-segments compression test */
1521 XXH64_reset(&xxhState, 0);
1522 { ZSTD_outBuffer outBuff = { cBuffer, cBufferSize, 0 } ;
1524 for (n=0, cSize=0, totalTestSize=0 ; totalTestSize < maxTestSize ; n++) {
1525 /* compress random chunks into randomly sized dst buffers */
1526 { size_t const randomSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
1527 size_t const srcSize = MIN(maxTestSize-totalTestSize, randomSrcSize);
1528 size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize);
1529 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
1530 size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize);
1531 ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 };
1532 outBuff.size = outBuff.pos + dstBuffSize;
1534 CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
1536 XXH64_update(&xxhState, srcBuffer+srcStart, inBuff.pos);
1537 memcpy(copyBuffer+totalTestSize, srcBuffer+srcStart, inBuff.pos);
1538 totalTestSize += inBuff.pos;
1541 /* random flush operation, to mess around */
1542 if ((FUZ_rand(&lseed) & 15) == 0) {
1543 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
1544 size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize);
1545 outBuff.size = outBuff.pos + adjustedDstSize;
1546 CHECK_Z( ZSTD_flushStream(zc, &outBuff) );
1549 /* final frame epilogue */
1550 { size_t remainingToFlush = (size_t)(-1);
1551 while (remainingToFlush) {
1552 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
1553 size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize);
1554 outBuff.size = outBuff.pos + adjustedDstSize;
1555 remainingToFlush = ZSTD_endStream(zc, &outBuff);
1556 CHECK (ZSTD_isError(remainingToFlush), "end error : %s", ZSTD_getErrorName(remainingToFlush));
1558 crcOrig = XXH64_digest(&xxhState);
1559 cSize = outBuff.pos;
1562 /* multi - fragments decompression test */
1563 if (!dictSize /* don't reset if dictionary : could be different */ && (FUZ_rand(&lseed) & 1)) {
1564 CHECK_Z ( ZSTD_resetDStream(zd) );
1566 CHECK_Z ( ZSTD_initDStream_usingDict(zd, dict, dictSize) );
1568 { size_t decompressionResult = 1;
1569 ZSTD_inBuffer inBuff = { cBuffer, cSize, 0 };
1570 ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
1571 for (totalGenSize = 0 ; decompressionResult ; ) {
1572 size_t const readCSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
1573 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
1574 size_t const dstBuffSize = MIN(dstBufferSize - totalGenSize, randomDstSize);
1575 inBuff.size = inBuff.pos + readCSrcSize;
1576 outBuff.size = outBuff.pos + dstBuffSize;
1577 decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff);
1578 if (ZSTD_getErrorCode(decompressionResult) == ZSTD_error_checksum_wrong) {
1579 DISPLAY("checksum error : \n");
1580 findDiff(copyBuffer, dstBuffer, totalTestSize);
1582 CHECK( ZSTD_isError(decompressionResult), "decompression error : %s",
1583 ZSTD_getErrorName(decompressionResult) );
1585 CHECK (decompressionResult != 0, "frame not fully decoded");
1586 CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size (%u != %u)",
1587 (unsigned)outBuff.pos, (unsigned)totalTestSize);
1588 CHECK (inBuff.pos != cSize, "compressed data should be fully read")
1589 { U64 const crcDest = XXH64(dstBuffer, totalTestSize, 0);
1590 if (crcDest!=crcOrig) findDiff(copyBuffer, dstBuffer, totalTestSize);
1591 CHECK (crcDest!=crcOrig, "decompressed data corrupted");
1594 /*===== noisy/erroneous src decompression test =====*/
1596 /* add some noise */
1597 { U32 const nbNoiseChunks = (FUZ_rand(&lseed) & 7) + 2;
1598 U32 nn; for (nn=0; nn<nbNoiseChunks; nn++) {
1599 size_t const randomNoiseSize = FUZ_randomLength(&lseed, maxSampleLog);
1600 size_t const noiseSize = MIN((cSize/3) , randomNoiseSize);
1601 size_t const noiseStart = FUZ_rand(&lseed) % (srcBufferSize - noiseSize);
1602 size_t const cStart = FUZ_rand(&lseed) % (cSize - noiseSize);
1603 memcpy(cBuffer+cStart, srcBuffer+noiseStart, noiseSize);
1606 /* try decompression on noisy data */
1607 CHECK_Z( ZSTD_initDStream(zd_noise) ); /* note : no dictionary */
1608 { ZSTD_inBuffer inBuff = { cBuffer, cSize, 0 };
1609 ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
1610 while (outBuff.pos < dstBufferSize) {
1611 size_t const randomCSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
1612 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
1613 size_t const adjustedDstSize = MIN(dstBufferSize - outBuff.pos, randomDstSize);
1614 size_t const adjustedCSrcSize = MIN(cSize - inBuff.pos, randomCSrcSize);
1615 outBuff.size = outBuff.pos + adjustedDstSize;
1616 inBuff.size = inBuff.pos + adjustedCSrcSize;
1617 { size_t const decompressError = ZSTD_decompressStream(zd, &outBuff, &inBuff);
1618 if (ZSTD_isError(decompressError)) break; /* error correctly detected */
1619 /* No forward progress possible */
1620 if (outBuff.pos < outBuff.size && inBuff.pos == cSize) break;
1622 DISPLAY("\r%u fuzzer tests completed \n", testNb);
1625 ZSTD_freeCStream(zc);
1626 ZSTD_freeDStream(zd);
1627 ZSTD_freeDStream(zd_noise);
1628 free(cNoiseBuffer[0]);
1629 free(cNoiseBuffer[1]);
1630 free(cNoiseBuffer[2]);
1631 free(cNoiseBuffer[3]);
1632 free(cNoiseBuffer[4]);
1644 /* fuzzing ZSTDMT_* interface */
1645 static int fuzzerTests_MT(U32 seed, int nbTests, int startTest,
1646 double compressibility, int bigTests)
1648 const U32 maxSrcLog = bigTests ? 24 : 22;
1649 static const U32 maxSampleLog = 19;
1650 size_t const srcBufferSize = (size_t)1<<maxSrcLog;
1651 BYTE* cNoiseBuffer[5];
1652 size_t const copyBufferSize= srcBufferSize + (1<<maxSampleLog);
1653 BYTE* const copyBuffer = (BYTE*)malloc (copyBufferSize);
1654 size_t const cBufferSize = ZSTD_compressBound(srcBufferSize);
1655 BYTE* const cBuffer = (BYTE*)malloc (cBufferSize);
1656 size_t const dstBufferSize = srcBufferSize;
1657 BYTE* const dstBuffer = (BYTE*)malloc (dstBufferSize);
1660 U32 coreSeed = seed;
1662 ZSTDMT_CCtx* zc = ZSTDMT_createCCtx(nbThreads); /* will be reset sometimes */
1663 ZSTD_DStream* zd = ZSTD_createDStream(); /* will be reset sometimes */
1664 ZSTD_DStream* const zd_noise = ZSTD_createDStream();
1665 UTIL_time_t const startClock = UTIL_getTime();
1666 const BYTE* dict=NULL; /* can keep same dict on 2 consecutive tests */
1667 size_t dictSize = 0;
1668 int const cLevelMax = bigTests ? (U32)ZSTD_maxCLevel()-1 : g_cLevelMax_smallTests;
1669 U32 const nbThreadsMax = bigTests ? 4 : 2;
1672 cNoiseBuffer[0] = (BYTE*)malloc (srcBufferSize);
1673 cNoiseBuffer[1] = (BYTE*)malloc (srcBufferSize);
1674 cNoiseBuffer[2] = (BYTE*)malloc (srcBufferSize);
1675 cNoiseBuffer[3] = (BYTE*)malloc (srcBufferSize);
1676 cNoiseBuffer[4] = (BYTE*)malloc (srcBufferSize);
1677 CHECK (!cNoiseBuffer[0] || !cNoiseBuffer[1] || !cNoiseBuffer[2] || !cNoiseBuffer[3] || !cNoiseBuffer[4] ||
1678 !copyBuffer || !dstBuffer || !cBuffer || !zc || !zd || !zd_noise ,
1679 "Not enough memory, fuzzer tests cancelled");
1681 /* Create initial samples */
1682 RDG_genBuffer(cNoiseBuffer[0], srcBufferSize, 0.00, 0., coreSeed); /* pure noise */
1683 RDG_genBuffer(cNoiseBuffer[1], srcBufferSize, 0.05, 0., coreSeed); /* barely compressible */
1684 RDG_genBuffer(cNoiseBuffer[2], srcBufferSize, compressibility, 0., coreSeed);
1685 RDG_genBuffer(cNoiseBuffer[3], srcBufferSize, 0.95, 0., coreSeed); /* highly compressible */
1686 RDG_genBuffer(cNoiseBuffer[4], srcBufferSize, 1.00, 0., coreSeed); /* sparse content */
1687 memset(copyBuffer, 0x65, copyBufferSize); /* make copyBuffer considered initialized */
1688 ZSTD_initDStream_usingDict(zd, NULL, 0); /* ensure at least one init */
1689 DISPLAYLEVEL(6, "Creating initial context with %i threads \n", nbThreads);
1691 /* catch up testNb */
1692 for (testNb=1; testNb < startTest; testNb++)
1693 FUZ_rand(&coreSeed);
1696 for ( ; (testNb <= nbTests) || (UTIL_clockSpanMicro(startClock) < g_clockTime) ; testNb++ ) {
1698 const BYTE* srcBuffer;
1699 size_t totalTestSize, totalGenSize, cSize;
1700 XXH64_state_t xxhState;
1704 FUZ_rand(&coreSeed);
1705 if (nbTests >= testNb) {
1706 DISPLAYUPDATE(2, "\r%6u/%6u ", testNb, nbTests);
1708 DISPLAYUPDATE(2, "\r%6u ", testNb);
1710 lseed = coreSeed ^ prime32;
1712 /* states full reset (deliberately not synchronized) */
1713 /* some issues can only happen when reusing states */
1714 if ((FUZ_rand(&lseed) & 0xFF) == 131) {
1715 nbThreads = (FUZ_rand(&lseed) % nbThreadsMax) + 1;
1716 DISPLAYLEVEL(5, "Creating new context with %u threads \n", nbThreads);
1717 ZSTDMT_freeCCtx(zc);
1718 zc = ZSTDMT_createCCtx(nbThreads);
1719 CHECK(zc==NULL, "ZSTDMT_createCCtx allocation error")
1721 if ((FUZ_rand(&lseed) & 0xFF) == 132) {
1722 ZSTD_freeDStream(zd);
1723 zd = ZSTD_createDStream();
1724 CHECK(zd==NULL, "ZSTDMT_createCCtx allocation error")
1725 ZSTD_initDStream_usingDict(zd, NULL, 0); /* ensure at least one init */
1728 /* srcBuffer selection [0-4] */
1729 { U32 buffNb = FUZ_rand(&lseed) & 0x7F;
1730 if (buffNb & 7) buffNb=2; /* most common : compressible (P) */
1734 const U32 tnb[2] = { 1, 3 }; /* barely/highly compressible */
1735 buffNb = tnb[buffNb >> 3];
1737 const U32 tnb[2] = { 0, 4 }; /* not compressible / sparse */
1738 buffNb = tnb[buffNb >> 3];
1740 srcBuffer = cNoiseBuffer[buffNb];
1743 /* compression init */
1744 { U32 const testLog = FUZ_rand(&lseed) % maxSrcLog;
1745 U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog;
1746 int const cLevelCandidate = ( FUZ_rand(&lseed)
1747 % (ZSTD_maxCLevel() - (MAX(testLog, dictLog) / 2)) )
1749 int const cLevelThreadAdjusted = cLevelCandidate - (nbThreads * 2) + 2; /* reduce cLevel when multiple threads to reduce memory consumption */
1750 int const cLevelMin = MAX(cLevelThreadAdjusted, 1); /* no negative cLevel yet */
1751 int const cLevel = MIN(cLevelMin, cLevelMax);
1752 maxTestSize = FUZ_rLogLength(&lseed, testLog);
1754 if (FUZ_rand(&lseed)&1) { /* simple init */
1755 int const compressionLevel = (FUZ_rand(&lseed) % 5) + 1;
1756 DISPLAYLEVEL(5, "Init with compression level = %i \n", compressionLevel);
1757 CHECK_Z( ZSTDMT_initCStream(zc, compressionLevel) );
1758 } else { /* advanced init */
1759 /* random dictionary selection */
1760 dictSize = ((FUZ_rand(&lseed)&63)==1) ? FUZ_rLogLength(&lseed, dictLog) : 0;
1761 { size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize);
1762 dict = srcBuffer + dictStart;
1764 { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? ZSTD_CONTENTSIZE_UNKNOWN : maxTestSize;
1765 ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize);
1766 DISPLAYLEVEL(5, "Init with windowLog = %u, pledgedSrcSize = %u, dictSize = %u \n",
1767 params.cParams.windowLog, (unsigned)pledgedSrcSize, (unsigned)dictSize);
1768 params.fParams.checksumFlag = FUZ_rand(&lseed) & 1;
1769 params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1;
1770 params.fParams.contentSizeFlag = FUZ_rand(&lseed) & 1;
1771 DISPLAYLEVEL(5, "checksumFlag : %u \n", params.fParams.checksumFlag);
1772 CHECK_Z( ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_overlapLog, FUZ_rand(&lseed) % 12) );
1773 CHECK_Z( ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_jobSize, FUZ_rand(&lseed) % (2*maxTestSize+1)) ); /* custom job size */
1774 CHECK_Z( ZSTDMT_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize) );
1777 /* multi-segments compression test */
1778 XXH64_reset(&xxhState, 0);
1779 { ZSTD_outBuffer outBuff = { cBuffer, cBufferSize, 0 } ;
1781 for (n=0, cSize=0, totalTestSize=0 ; totalTestSize < maxTestSize ; n++) {
1782 /* compress random chunks into randomly sized dst buffers */
1783 { size_t const randomSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
1784 size_t const srcSize = MIN (maxTestSize-totalTestSize, randomSrcSize);
1785 size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize);
1786 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
1787 size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize);
1788 ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 };
1789 outBuff.size = outBuff.pos + dstBuffSize;
1791 DISPLAYLEVEL(6, "Sending %u bytes to compress \n", (unsigned)srcSize);
1792 CHECK_Z( ZSTDMT_compressStream(zc, &outBuff, &inBuff) );
1793 DISPLAYLEVEL(6, "%u bytes read by ZSTDMT_compressStream \n", (unsigned)inBuff.pos);
1795 XXH64_update(&xxhState, srcBuffer+srcStart, inBuff.pos);
1796 memcpy(copyBuffer+totalTestSize, srcBuffer+srcStart, inBuff.pos);
1797 totalTestSize += inBuff.pos;
1800 /* random flush operation, to mess around */
1801 if ((FUZ_rand(&lseed) & 15) == 0) {
1802 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
1803 size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize);
1804 size_t const previousPos = outBuff.pos;
1805 outBuff.size = outBuff.pos + adjustedDstSize;
1806 DISPLAYLEVEL(5, "Flushing into dst buffer of size %u \n", (unsigned)adjustedDstSize);
1807 CHECK_Z( ZSTDMT_flushStream(zc, &outBuff) );
1808 assert(outBuff.pos >= previousPos);
1809 DISPLAYLEVEL(6, "%u bytes flushed by ZSTDMT_flushStream \n", (unsigned)(outBuff.pos-previousPos));
1812 /* final frame epilogue */
1813 { size_t remainingToFlush = (size_t)(-1);
1814 while (remainingToFlush) {
1815 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
1816 size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize);
1817 size_t const previousPos = outBuff.pos;
1818 outBuff.size = outBuff.pos + adjustedDstSize;
1819 DISPLAYLEVEL(5, "Ending into dst buffer of size %u \n", (unsigned)adjustedDstSize);
1820 remainingToFlush = ZSTDMT_endStream(zc, &outBuff);
1821 CHECK (ZSTD_isError(remainingToFlush), "ZSTDMT_endStream error : %s", ZSTD_getErrorName(remainingToFlush));
1822 assert(outBuff.pos >= previousPos);
1823 DISPLAYLEVEL(6, "%u bytes flushed by ZSTDMT_endStream \n", (unsigned)(outBuff.pos-previousPos));
1824 DISPLAYLEVEL(5, "endStream : remainingToFlush : %u \n", (unsigned)remainingToFlush);
1826 crcOrig = XXH64_digest(&xxhState);
1827 cSize = outBuff.pos;
1828 DISPLAYLEVEL(5, "Frame completed : %u bytes compressed into %u bytes \n",
1829 (unsigned)totalTestSize, (unsigned)cSize);
1832 /* multi - fragments decompression test */
1833 assert(totalTestSize < dstBufferSize);
1834 memset(dstBuffer, 170, totalTestSize); /* init dest area */
1835 if (!dictSize /* don't reset if dictionary : could be different */ && (FUZ_rand(&lseed) & 1)) {
1836 CHECK_Z( ZSTD_resetDStream(zd) );
1838 CHECK_Z( ZSTD_initDStream_usingDict(zd, dict, dictSize) );
1840 { size_t decompressionResult = 1;
1841 ZSTD_inBuffer inBuff = { cBuffer, cSize, 0 };
1842 ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
1843 for (totalGenSize = 0 ; decompressionResult ; ) {
1844 size_t const readCSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
1845 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
1846 size_t const dstBuffSize = MIN(dstBufferSize - totalGenSize, randomDstSize);
1847 inBuff.size = inBuff.pos + readCSrcSize;
1848 outBuff.size = outBuff.pos + dstBuffSize;
1849 DISPLAYLEVEL(6, "ZSTD_decompressStream input %u bytes into outBuff %u bytes \n",
1850 (unsigned)readCSrcSize, (unsigned)dstBuffSize);
1851 decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff);
1852 if (ZSTD_isError(decompressionResult)) {
1853 DISPLAY("ZSTD_decompressStream error : %s \n", ZSTD_getErrorName(decompressionResult));
1854 findDiff(copyBuffer, dstBuffer, totalTestSize);
1856 CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult));
1857 DISPLAYLEVEL(6, "total ingested (inBuff.pos) = %u and produced (outBuff.pos) = %u \n",
1858 (unsigned)inBuff.pos, (unsigned)outBuff.pos);
1860 CHECK (outBuff.pos != totalTestSize,
1861 "decompressed data : wrong size (%u != %u)",
1862 (unsigned)outBuff.pos, (unsigned)totalTestSize );
1863 CHECK (inBuff.pos != cSize,
1864 "compressed data should be fully read (%u != %u)",
1865 (unsigned)inBuff.pos, (unsigned)cSize );
1866 { U64 const crcDest = XXH64(dstBuffer, totalTestSize, 0);
1867 if (crcDest!=crcOrig) findDiff(copyBuffer, dstBuffer, totalTestSize);
1868 CHECK (crcDest!=crcOrig, "decompressed data corrupted");
1871 /*===== noisy/erroneous src decompression test =====*/
1873 /* add some noise */
1874 { U32 const nbNoiseChunks = (FUZ_rand(&lseed) & 7) + 2;
1875 U32 nn; for (nn=0; nn<nbNoiseChunks; nn++) {
1876 size_t const randomNoiseSize = FUZ_randomLength(&lseed, maxSampleLog);
1877 size_t const noiseSize = MIN((cSize/3) , randomNoiseSize);
1878 size_t const noiseStart = FUZ_rand(&lseed) % (srcBufferSize - noiseSize);
1879 size_t const cStart = FUZ_rand(&lseed) % (cSize - noiseSize);
1880 memcpy(cBuffer+cStart, srcBuffer+noiseStart, noiseSize);
1883 /* try decompression on noisy data */
1884 CHECK_Z( ZSTD_initDStream(zd_noise) ); /* note : no dictionary */
1885 { ZSTD_inBuffer inBuff = { cBuffer, cSize, 0 };
1886 ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
1887 while (outBuff.pos < dstBufferSize) {
1888 size_t const randomCSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
1889 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
1890 size_t const adjustedDstSize = MIN(dstBufferSize - outBuff.pos, randomDstSize);
1891 size_t const adjustedCSrcSize = MIN(cSize - inBuff.pos, randomCSrcSize);
1892 outBuff.size = outBuff.pos + adjustedDstSize;
1893 inBuff.size = inBuff.pos + adjustedCSrcSize;
1894 { size_t const decompressError = ZSTD_decompressStream(zd, &outBuff, &inBuff);
1895 if (ZSTD_isError(decompressError)) break; /* error correctly detected */
1896 /* No forward progress possible */
1897 if (outBuff.pos < outBuff.size && inBuff.pos == cSize) break;
1899 DISPLAY("\r%u fuzzer tests completed \n", testNb);
1902 ZSTDMT_freeCCtx(zc);
1903 ZSTD_freeDStream(zd);
1904 ZSTD_freeDStream(zd_noise);
1905 free(cNoiseBuffer[0]);
1906 free(cNoiseBuffer[1]);
1907 free(cNoiseBuffer[2]);
1908 free(cNoiseBuffer[3]);
1909 free(cNoiseBuffer[4]);
1920 /** If useOpaqueAPI, sets param in cctxParams.
1921 * Otherwise, sets the param in zc. */
1922 static size_t setCCtxParameter(ZSTD_CCtx* zc, ZSTD_CCtx_params* cctxParams,
1923 ZSTD_cParameter param, unsigned value,
1927 return ZSTD_CCtxParams_setParameter(cctxParams, param, value);
1929 return ZSTD_CCtx_setParameter(zc, param, value);
1933 /* Tests for ZSTD_compress_generic() API */
1934 static int fuzzerTests_newAPI(U32 seed, int nbTests, int startTest,
1935 double compressibility, int bigTests)
1937 U32 const maxSrcLog = bigTests ? 24 : 22;
1938 static const U32 maxSampleLog = 19;
1939 size_t const srcBufferSize = (size_t)1<<maxSrcLog;
1940 BYTE* cNoiseBuffer[5];
1941 size_t const copyBufferSize= srcBufferSize + (1<<maxSampleLog);
1942 BYTE* const copyBuffer = (BYTE*)malloc (copyBufferSize);
1943 size_t const cBufferSize = ZSTD_compressBound(srcBufferSize);
1944 BYTE* const cBuffer = (BYTE*)malloc (cBufferSize);
1945 size_t const dstBufferSize = srcBufferSize;
1946 BYTE* const dstBuffer = (BYTE*)malloc (dstBufferSize);
1949 U32 coreSeed = seed;
1950 ZSTD_CCtx* zc = ZSTD_createCCtx(); /* will be reset sometimes */
1951 ZSTD_DStream* zd = ZSTD_createDStream(); /* will be reset sometimes */
1952 ZSTD_DStream* const zd_noise = ZSTD_createDStream();
1953 UTIL_time_t const startClock = UTIL_getTime();
1954 const BYTE* dict = NULL; /* can keep same dict on 2 consecutive tests */
1955 size_t dictSize = 0;
1957 U32 windowLogMalus = 0; /* can survive between 2 loops */
1958 U32 const cLevelMax = bigTests ? (U32)ZSTD_maxCLevel()-1 : g_cLevelMax_smallTests;
1959 U32 const nbThreadsMax = bigTests ? 4 : 2;
1960 ZSTD_CCtx_params* cctxParams = ZSTD_createCCtxParams();
1963 cNoiseBuffer[0] = (BYTE*)malloc (srcBufferSize);
1964 cNoiseBuffer[1] = (BYTE*)malloc (srcBufferSize);
1965 cNoiseBuffer[2] = (BYTE*)malloc (srcBufferSize);
1966 cNoiseBuffer[3] = (BYTE*)malloc (srcBufferSize);
1967 cNoiseBuffer[4] = (BYTE*)malloc (srcBufferSize);
1968 CHECK (!cNoiseBuffer[0] || !cNoiseBuffer[1] || !cNoiseBuffer[2] || !cNoiseBuffer[3] || !cNoiseBuffer[4] ||
1969 !copyBuffer || !dstBuffer || !cBuffer || !zc || !zd || !zd_noise ,
1970 "Not enough memory, fuzzer tests cancelled");
1972 /* Create initial samples */
1973 RDG_genBuffer(cNoiseBuffer[0], srcBufferSize, 0.00, 0., coreSeed); /* pure noise */
1974 RDG_genBuffer(cNoiseBuffer[1], srcBufferSize, 0.05, 0., coreSeed); /* barely compressible */
1975 RDG_genBuffer(cNoiseBuffer[2], srcBufferSize, compressibility, 0., coreSeed);
1976 RDG_genBuffer(cNoiseBuffer[3], srcBufferSize, 0.95, 0., coreSeed); /* highly compressible */
1977 RDG_genBuffer(cNoiseBuffer[4], srcBufferSize, 1.00, 0., coreSeed); /* sparse content */
1978 memset(copyBuffer, 0x65, copyBufferSize); /* make copyBuffer considered initialized */
1979 CHECK_Z( ZSTD_initDStream_usingDict(zd, NULL, 0) ); /* ensure at least one init */
1981 /* catch up testNb */
1982 for (testNb=1; testNb < startTest; testNb++)
1983 FUZ_rand(&coreSeed);
1986 for ( ; (testNb <= nbTests) || (UTIL_clockSpanMicro(startClock) < g_clockTime) ; testNb++ ) {
1989 const BYTE* srcBuffer;
1990 size_t totalTestSize, totalGenSize, cSize;
1991 XXH64_state_t xxhState;
1993 U32 resetAllowed = 1;
1995 ZSTD_parameters savedParams;
1998 if (nbTests >= testNb) { DISPLAYUPDATE(2, "\r%6u/%6u ", testNb, nbTests); }
1999 else { DISPLAYUPDATE(2, "\r%6u ", testNb); }
2000 FUZ_rand(&coreSeed);
2001 lseed = coreSeed ^ prime32;
2002 DISPLAYLEVEL(5, " *** Test %u *** \n", testNb);
2003 opaqueAPI = FUZ_rand(&lseed) & 1;
2005 /* states full reset (deliberately not synchronized) */
2006 /* some issues can only happen when reusing states */
2007 if ((FUZ_rand(&lseed) & 0xFF) == 131) {
2008 DISPLAYLEVEL(5, "Creating new context \n");
2010 zc = ZSTD_createCCtx();
2011 CHECK(zc == NULL, "ZSTD_createCCtx allocation error");
2014 if ((FUZ_rand(&lseed) & 0xFF) == 132) {
2015 ZSTD_freeDStream(zd);
2016 zd = ZSTD_createDStream();
2017 CHECK(zd == NULL, "ZSTD_createDStream allocation error");
2018 ZSTD_initDStream_usingDict(zd, NULL, 0); /* ensure at least one init */
2021 /* srcBuffer selection [0-4] */
2022 { U32 buffNb = FUZ_rand(&lseed) & 0x7F;
2023 if (buffNb & 7) buffNb=2; /* most common : compressible (P) */
2027 const U32 tnb[2] = { 1, 3 }; /* barely/highly compressible */
2028 buffNb = tnb[buffNb >> 3];
2030 const U32 tnb[2] = { 0, 4 }; /* not compressible / sparse */
2031 buffNb = tnb[buffNb >> 3];
2033 srcBuffer = cNoiseBuffer[buffNb];
2036 /* compression init */
2037 CHECK_Z( ZSTD_CCtx_loadDictionary(zc, NULL, 0) ); /* cancel previous dict /*/
2038 if ((FUZ_rand(&lseed)&1) /* at beginning, to keep same nb of rand */
2039 && oldTestLog /* at least one test happened */
2041 /* just set a compression level */
2042 maxTestSize = FUZ_randomLength(&lseed, oldTestLog+2);
2043 if (maxTestSize >= srcBufferSize) maxTestSize = srcBufferSize-1;
2044 { int const compressionLevel = (FUZ_rand(&lseed) % 5) + 1;
2045 DISPLAYLEVEL(5, "t%u : compression level : %i \n", testNb, compressionLevel);
2046 CHECK_Z (setCCtxParameter(zc, cctxParams, ZSTD_c_compressionLevel, compressionLevel, opaqueAPI) );
2049 U32 const testLog = FUZ_rand(&lseed) % maxSrcLog;
2050 U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog;
2051 U32 const cLevelCandidate = (FUZ_rand(&lseed) %
2053 (MAX(testLog, dictLog) / 2))) +
2055 int const cLevel = MIN(cLevelCandidate, cLevelMax);
2056 DISPLAYLEVEL(5, "t%i: base cLevel : %u \n", testNb, cLevel);
2057 maxTestSize = FUZ_rLogLength(&lseed, testLog);
2058 DISPLAYLEVEL(5, "t%i: maxTestSize : %u \n", testNb, (unsigned)maxTestSize);
2059 oldTestLog = testLog;
2060 /* random dictionary selection */
2061 dictSize = ((FUZ_rand(&lseed)&63)==1) ? FUZ_rLogLength(&lseed, dictLog) : 0;
2062 { size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize);
2063 dict = srcBuffer + dictStart;
2064 if (!dictSize) dict=NULL;
2066 { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? ZSTD_CONTENTSIZE_UNKNOWN : maxTestSize;
2067 ZSTD_compressionParameters cParams = ZSTD_getCParams(cLevel, pledgedSrcSize, dictSize);
2068 const U32 windowLogMax = bigTests ? 24 : 20;
2069 const U32 searchLogMax = bigTests ? 15 : 13;
2071 DISPLAYLEVEL(5, "t%u: with dictionary of size : %zu \n", testNb, dictSize);
2073 /* mess with compression parameters */
2074 cParams.windowLog += (FUZ_rand(&lseed) & 3) - 1;
2075 cParams.windowLog = MIN(windowLogMax, cParams.windowLog);
2076 cParams.hashLog += (FUZ_rand(&lseed) & 3) - 1;
2077 cParams.chainLog += (FUZ_rand(&lseed) & 3) - 1;
2078 cParams.searchLog += (FUZ_rand(&lseed) & 3) - 1;
2079 cParams.searchLog = MIN(searchLogMax, cParams.searchLog);
2080 cParams.minMatch += (FUZ_rand(&lseed) & 3) - 1;
2081 cParams.targetLength = (U32)((cParams.targetLength + 1 ) * (0.5 + ((double)(FUZ_rand(&lseed) & 127) / 128)));
2082 cParams = ZSTD_adjustCParams(cParams, pledgedSrcSize, dictSize);
2084 if (FUZ_rand(&lseed) & 1) {
2085 DISPLAYLEVEL(5, "t%u: windowLog : %u \n", testNb, cParams.windowLog);
2086 CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_windowLog, cParams.windowLog, opaqueAPI) );
2087 assert(cParams.windowLog >= ZSTD_WINDOWLOG_MIN); /* guaranteed by ZSTD_adjustCParams() */
2088 windowLogMalus = (cParams.windowLog - ZSTD_WINDOWLOG_MIN) / 5;
2090 if (FUZ_rand(&lseed) & 1) {
2091 DISPLAYLEVEL(5, "t%u: hashLog : %u \n", testNb, cParams.hashLog);
2092 CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_hashLog, cParams.hashLog, opaqueAPI) );
2094 if (FUZ_rand(&lseed) & 1) {
2095 DISPLAYLEVEL(5, "t%u: chainLog : %u \n", testNb, cParams.chainLog);
2096 CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_chainLog, cParams.chainLog, opaqueAPI) );
2098 if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_searchLog, cParams.searchLog, opaqueAPI) );
2099 if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_minMatch, cParams.minMatch, opaqueAPI) );
2100 if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_targetLength, cParams.targetLength, opaqueAPI) );
2102 /* mess with long distance matching parameters */
2104 if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_enableLongDistanceMatching, FUZ_rand(&lseed) & 63, opaqueAPI) );
2105 if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_ldmHashLog, FUZ_randomClampedLength(&lseed, ZSTD_HASHLOG_MIN, 23), opaqueAPI) );
2106 if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_ldmMinMatch, FUZ_randomClampedLength(&lseed, ZSTD_LDM_MINMATCH_MIN, ZSTD_LDM_MINMATCH_MAX), opaqueAPI) );
2107 if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_ldmBucketSizeLog, FUZ_randomClampedLength(&lseed, ZSTD_LDM_BUCKETSIZELOG_MIN, ZSTD_LDM_BUCKETSIZELOG_MAX), opaqueAPI) );
2108 if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_ldmHashRateLog, FUZ_randomClampedLength(&lseed, ZSTD_LDM_HASHRATELOG_MIN, ZSTD_LDM_HASHRATELOG_MAX), opaqueAPI) );
2111 /* mess with frame parameters */
2112 if (FUZ_rand(&lseed) & 1) {
2113 int const checksumFlag = FUZ_rand(&lseed) & 1;
2114 DISPLAYLEVEL(5, "t%u: frame checksum : %u \n", testNb, checksumFlag);
2115 CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_checksumFlag, checksumFlag, opaqueAPI) );
2117 if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_dictIDFlag, FUZ_rand(&lseed) & 1, opaqueAPI) );
2118 if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_contentSizeFlag, FUZ_rand(&lseed) & 1, opaqueAPI) );
2119 if (FUZ_rand(&lseed) & 1) {
2120 DISPLAYLEVEL(5, "t%u: pledgedSrcSize : %u \n", testNb, (unsigned)pledgedSrcSize);
2121 CHECK_Z( ZSTD_CCtx_setPledgedSrcSize(zc, pledgedSrcSize) );
2124 /* multi-threading parameters. Only adjust occasionally for small tests. */
2125 if (bigTests || (FUZ_rand(&lseed) & 0xF) == 0xF) {
2126 U32 const nbThreadsCandidate = (FUZ_rand(&lseed) & 4) + 1;
2127 U32 const nbThreadsAdjusted = (windowLogMalus < nbThreadsCandidate) ? nbThreadsCandidate - windowLogMalus : 1;
2128 int const nbThreads = MIN(nbThreadsAdjusted, nbThreadsMax);
2129 DISPLAYLEVEL(5, "t%i: nbThreads : %u \n", testNb, nbThreads);
2130 CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_nbWorkers, nbThreads, opaqueAPI) );
2131 if (nbThreads > 1) {
2132 U32 const jobLog = FUZ_rand(&lseed) % (testLog+1);
2133 CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_overlapLog, FUZ_rand(&lseed) % 10, opaqueAPI) );
2134 CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_jobSize, (U32)FUZ_rLogLength(&lseed, jobLog), opaqueAPI) );
2137 /* Enable rsyncable mode 1 in 4 times. */
2138 setCCtxParameter(zc, cctxParams, ZSTD_c_rsyncable, (FUZ_rand(&lseed) % 4 == 0), opaqueAPI);
2140 if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_c_forceMaxWindow, FUZ_rand(&lseed) & 1, opaqueAPI) );
2142 /* Apply parameters */
2144 DISPLAYLEVEL(5, "t%u: applying CCtxParams \n", testNb);
2145 CHECK_Z (ZSTD_CCtx_setParametersUsingCCtxParams(zc, cctxParams) );
2148 if (FUZ_rand(&lseed) & 1) {
2149 if (FUZ_rand(&lseed) & 1) {
2150 CHECK_Z( ZSTD_CCtx_loadDictionary(zc, dict, dictSize) );
2152 CHECK_Z( ZSTD_CCtx_loadDictionary_byReference(zc, dict, dictSize) );
2155 CHECK_Z( ZSTD_CCtx_refPrefix(zc, dict, dictSize) );
2159 CHECK_Z(getCCtxParams(zc, &savedParams));
2161 /* multi-segments compression test */
2162 XXH64_reset(&xxhState, 0);
2163 { ZSTD_outBuffer outBuff = { cBuffer, cBufferSize, 0 } ;
2164 for (cSize=0, totalTestSize=0 ; (totalTestSize < maxTestSize) ; ) {
2165 /* compress random chunks into randomly sized dst buffers */
2166 size_t const randomSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
2167 size_t const srcSize = MIN(maxTestSize-totalTestSize, randomSrcSize);
2168 size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize);
2169 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog+1);
2170 size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize);
2171 ZSTD_EndDirective const flush = (FUZ_rand(&lseed) & 15) ? ZSTD_e_continue : ZSTD_e_flush;
2172 ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 };
2173 outBuff.size = outBuff.pos + dstBuffSize;
2175 CHECK_Z( ZSTD_compressStream2(zc, &outBuff, &inBuff, flush) );
2176 DISPLAYLEVEL(6, "t%u: compress consumed %u bytes (total : %u) ; flush: %u (total : %u) \n",
2177 testNb, (unsigned)inBuff.pos, (unsigned)(totalTestSize + inBuff.pos), (unsigned)flush, (unsigned)outBuff.pos);
2179 XXH64_update(&xxhState, srcBuffer+srcStart, inBuff.pos);
2180 memcpy(copyBuffer+totalTestSize, srcBuffer+srcStart, inBuff.pos);
2181 totalTestSize += inBuff.pos;
2184 /* final frame epilogue */
2185 { size_t remainingToFlush = 1;
2186 while (remainingToFlush) {
2187 ZSTD_inBuffer inBuff = { NULL, 0, 0 };
2188 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog+1);
2189 size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize);
2190 outBuff.size = outBuff.pos + adjustedDstSize;
2191 DISPLAYLEVEL(6, "t%u: End-flush into dst buffer of size %u \n", testNb, (unsigned)adjustedDstSize);
2192 remainingToFlush = ZSTD_compressStream2(zc, &outBuff, &inBuff, ZSTD_e_end);
2193 DISPLAYLEVEL(6, "t%u: Total flushed so far : %u bytes \n", testNb, (unsigned)outBuff.pos);
2194 CHECK( ZSTD_isError(remainingToFlush),
2195 "ZSTD_compressStream2 w/ ZSTD_e_end error : %s",
2196 ZSTD_getErrorName(remainingToFlush) );
2198 crcOrig = XXH64_digest(&xxhState);
2199 cSize = outBuff.pos;
2200 DISPLAYLEVEL(5, "Frame completed : %zu bytes \n", cSize);
2203 CHECK(badParameters(zc, savedParams), "CCtx params are wrong");
2205 /* multi - fragments decompression test */
2206 if (!dictSize /* don't reset if dictionary : could be different */ && (FUZ_rand(&lseed) & 1)) {
2207 DISPLAYLEVEL(5, "resetting DCtx (dict:%p) \n", dict);
2208 CHECK_Z( ZSTD_resetDStream(zd) );
2211 DISPLAYLEVEL(5, "using dictionary of size %zu \n", dictSize);
2212 CHECK_Z( ZSTD_initDStream_usingDict(zd, dict, dictSize) );
2214 { size_t decompressionResult = 1;
2215 ZSTD_inBuffer inBuff = { cBuffer, cSize, 0 };
2216 ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
2217 for (totalGenSize = 0 ; decompressionResult ; ) {
2218 size_t const readCSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
2219 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
2220 size_t const dstBuffSize = MIN(dstBufferSize - totalGenSize, randomDstSize);
2221 inBuff.size = inBuff.pos + readCSrcSize;
2222 outBuff.size = outBuff.pos + dstBuffSize;
2223 DISPLAYLEVEL(6, "decompression presented %u new bytes (pos:%u/%u)\n",
2224 (unsigned)readCSrcSize, (unsigned)inBuff.pos, (unsigned)cSize);
2225 decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff);
2226 DISPLAYLEVEL(6, "so far: consumed = %u, produced = %u \n",
2227 (unsigned)inBuff.pos, (unsigned)outBuff.pos);
2228 if (ZSTD_isError(decompressionResult)) {
2229 DISPLAY("ZSTD_decompressStream error : %s \n", ZSTD_getErrorName(decompressionResult));
2230 findDiff(copyBuffer, dstBuffer, totalTestSize);
2232 CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult));
2233 CHECK (inBuff.pos > cSize, "ZSTD_decompressStream consumes too much input : %u > %u ", (unsigned)inBuff.pos, (unsigned)cSize);
2235 CHECK (inBuff.pos != cSize, "compressed data should be fully read (%u != %u)", (unsigned)inBuff.pos, (unsigned)cSize);
2236 CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size (%u != %u)", (unsigned)outBuff.pos, (unsigned)totalTestSize);
2237 { U64 const crcDest = XXH64(dstBuffer, totalTestSize, 0);
2238 if (crcDest!=crcOrig) findDiff(copyBuffer, dstBuffer, totalTestSize);
2239 CHECK (crcDest!=crcOrig, "decompressed data corrupted");
2242 /*===== noisy/erroneous src decompression test =====*/
2244 /* add some noise */
2245 { U32 const nbNoiseChunks = (FUZ_rand(&lseed) & 7) + 2;
2246 U32 nn; for (nn=0; nn<nbNoiseChunks; nn++) {
2247 size_t const randomNoiseSize = FUZ_randomLength(&lseed, maxSampleLog);
2248 size_t const noiseSize = MIN((cSize/3) , randomNoiseSize);
2249 size_t const noiseStart = FUZ_rand(&lseed) % (srcBufferSize - noiseSize);
2250 size_t const cStart = FUZ_rand(&lseed) % (cSize - noiseSize);
2251 memcpy(cBuffer+cStart, srcBuffer+noiseStart, noiseSize);
2254 /* try decompression on noisy data */
2255 CHECK_Z( ZSTD_initDStream(zd_noise) ); /* note : no dictionary */
2256 { ZSTD_inBuffer inBuff = { cBuffer, cSize, 0 };
2257 ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
2258 while (outBuff.pos < dstBufferSize) {
2259 size_t const randomCSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
2260 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
2261 size_t const adjustedDstSize = MIN(dstBufferSize - outBuff.pos, randomDstSize);
2262 size_t const adjustedCSrcSize = MIN(cSize - inBuff.pos, randomCSrcSize);
2263 outBuff.size = outBuff.pos + adjustedDstSize;
2264 inBuff.size = inBuff.pos + adjustedCSrcSize;
2265 { size_t const decompressError = ZSTD_decompressStream(zd, &outBuff, &inBuff);
2266 if (ZSTD_isError(decompressError)) break; /* error correctly detected */
2267 /* Good so far, but no more progress possible */
2268 if (outBuff.pos < outBuff.size && inBuff.pos == cSize) break;
2270 DISPLAY("\r%u fuzzer tests completed \n", testNb-1);
2274 ZSTD_freeDStream(zd);
2275 ZSTD_freeDStream(zd_noise);
2276 ZSTD_freeCCtxParams(cctxParams);
2277 free(cNoiseBuffer[0]);
2278 free(cNoiseBuffer[1]);
2279 free(cNoiseBuffer[2]);
2280 free(cNoiseBuffer[3]);
2281 free(cNoiseBuffer[4]);
2292 /*-*******************************************************
2294 *********************************************************/
2295 static int FUZ_usage(const char* programName)
2297 DISPLAY( "Usage :\n");
2298 DISPLAY( " %s [args]\n", programName);
2300 DISPLAY( "Arguments :\n");
2301 DISPLAY( " -i# : Nb of tests (default:%u) \n", nbTestsDefault);
2302 DISPLAY( " -s# : Select seed (default:prompt user)\n");
2303 DISPLAY( " -t# : Select starting test number (default:0)\n");
2304 DISPLAY( " -P# : Select compressibility in %% (default:%i%%)\n", FUZ_COMPRESSIBILITY_DEFAULT);
2305 DISPLAY( " -v : verbose\n");
2306 DISPLAY( " -p : pause at the end\n");
2307 DISPLAY( " -h : display help and exit\n");
2311 typedef enum { simple_api, mt_api, advanced_api } e_api;
2313 int main(int argc, const char** argv)
2317 int nbTests = nbTestsDefault;
2319 int proba = FUZ_COMPRESSIBILITY_DEFAULT;
2322 int bigTests = (sizeof(size_t) == 8);
2323 e_api selected_api = simple_api;
2324 const char* const programName = argv[0];
2327 /* Check command line */
2328 for(argNb=1; argNb<argc; argNb++) {
2329 const char* argument = argv[argNb];
2330 assert(argument != NULL);
2332 /* Parsing commands. Aggregated commands are allowed */
2333 if (argument[0]=='-') {
2335 if (!strcmp(argument, "--mt")) { selected_api=mt_api; testNb += !testNb; continue; }
2336 if (!strcmp(argument, "--newapi")) { selected_api=advanced_api; testNb += !testNb; continue; }
2337 if (!strcmp(argument, "--no-big-tests")) { bigTests=0; continue; }
2340 while (*argument!=0) {
2344 return FUZ_usage(programName);
2356 case 'p': /* pause at the end */
2361 case 'i': /* limit tests by nb of iterations (default) */
2363 nbTests=0; g_clockTime=0;
2364 while ((*argument>='0') && (*argument<='9')) {
2366 nbTests += *argument - '0';
2371 case 'T': /* limit tests by time */
2373 nbTests=0; g_clockTime=0;
2374 while ((*argument>='0') && (*argument<='9')) {
2376 g_clockTime += *argument - '0';
2379 if (*argument=='m') { /* -T1m == -T60 */
2380 g_clockTime *=60, argument++;
2381 if (*argument=='n') argument++; /* -T1mn == -T60 */
2382 } else if (*argument=='s') argument++; /* -T10s == -T10 */
2383 g_clockTime *= SEC_TO_MICRO;
2386 case 's': /* manually select seed */
2390 while ((*argument>='0') && (*argument<='9')) {
2392 seed += *argument - '0';
2397 case 't': /* select starting test number */
2400 while ((*argument>='0') && (*argument<='9')) {
2402 testNb += *argument - '0';
2407 case 'P': /* compressibility % */
2410 while ((*argument>='0') && (*argument<='9')) {
2412 proba += *argument - '0';
2415 if (proba<0) proba=0;
2416 if (proba>100) proba=100;
2420 return FUZ_usage(programName);
2422 } } } /* for(argNb=1; argNb<argc; argNb++) */
2425 DISPLAY("Starting zstream tester (%i-bits, %s)\n", (int)(sizeof(size_t)*8), ZSTD_VERSION_STRING);
2428 time_t const t = time(NULL);
2429 U32 const h = XXH32(&t, sizeof(t), 1);
2433 DISPLAY("Seed = %u\n", (unsigned)seed);
2434 if (proba!=FUZ_COMPRESSIBILITY_DEFAULT) DISPLAY("Compressibility : %i%%\n", proba);
2436 if (nbTests<=0) nbTests=1;
2439 result = basicUnitTests(0, ((double)proba) / 100); /* constant seed for predictability */
2443 switch(selected_api)
2446 result = fuzzerTests(seed, nbTests, testNb, ((double)proba) / 100, bigTests);
2449 result = fuzzerTests_MT(seed, nbTests, testNb, ((double)proba) / 100, bigTests);
2452 result = fuzzerTests_newAPI(seed, nbTests, testNb, ((double)proba) / 100, bigTests);
2455 assert(0); /* impossible */
2461 DISPLAY("Press Enter \n");