]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/zstd/tests/zstreamtest.c
MFV r317781:
[FreeBSD/FreeBSD.git] / contrib / zstd / tests / zstreamtest.c
1 /**
2  * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree. An additional grant
7  * of patent rights can be found in the PATENTS file in the same directory.
8  */
9
10
11 /*-************************************
12 *  Compiler specific
13 **************************************/
14 #ifdef _MSC_VER    /* Visual Studio */
15 #  define _CRT_SECURE_NO_WARNINGS     /* fgets */
16 #  pragma warning(disable : 4127)     /* disable: C4127: conditional expression is constant */
17 #  pragma warning(disable : 4146)     /* disable: C4146: minus unsigned expression */
18 #endif
19
20
21 /*-************************************
22 *  Includes
23 **************************************/
24 #include <stdlib.h>       /* free */
25 #include <stdio.h>        /* fgets, sscanf */
26 #include <time.h>         /* clock_t, clock() */
27 #include <string.h>       /* strcmp */
28 #include "mem.h"
29 #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_maxCLevel, ZSTD_customMem, ZSTD_getDictID_fromFrame */
30 #include "zstd.h"         /* ZSTD_compressBound */
31 #include "zstd_errors.h"  /* ZSTD_error_srcSize_wrong */
32 #include "zstdmt_compress.h"
33 #include "zdict.h"        /* ZDICT_trainFromBuffer */
34 #include "datagen.h"      /* RDG_genBuffer */
35 #define XXH_STATIC_LINKING_ONLY   /* XXH64_state_t */
36 #include "xxhash.h"       /* XXH64_* */
37
38
39 /*-************************************
40 *  Constants
41 **************************************/
42 #define KB *(1U<<10)
43 #define MB *(1U<<20)
44 #define GB *(1U<<30)
45
46 static const U32 nbTestsDefault = 10000;
47 #define COMPRESSIBLE_NOISE_LENGTH (10 MB)
48 #define FUZ_COMPRESSIBILITY_DEFAULT 50
49 static const U32 prime32 = 2654435761U;
50
51
52 /*-************************************
53 *  Display Macros
54 **************************************/
55 #define DISPLAY(...)          fprintf(stderr, __VA_ARGS__)
56 #define DISPLAYLEVEL(l, ...)  if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); }
57 static U32 g_displayLevel = 2;
58
59 #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
60             if ((FUZ_GetClockSpan(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \
61             { g_displayClock = clock(); DISPLAY(__VA_ARGS__); \
62             if (g_displayLevel>=4) fflush(stdout); } }
63 static const clock_t g_refreshRate = CLOCKS_PER_SEC / 6;
64 static clock_t g_displayClock = 0;
65
66 static clock_t g_clockTime = 0;
67
68
69 /*-*******************************************************
70 *  Fuzzer functions
71 *********************************************************/
72 #define MAX(a,b) ((a)>(b)?(a):(b))
73
74 static clock_t FUZ_GetClockSpan(clock_t clockStart)
75 {
76     return clock() - clockStart;  /* works even when overflow. Max span ~ 30 mn */
77 }
78
79 /*! FUZ_rand() :
80     @return : a 27 bits random value, from a 32-bits `seed`.
81     `seed` is also modified */
82 #define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r)))
83 unsigned int FUZ_rand(unsigned int* seedPtr)
84 {
85     static const U32 prime2 = 2246822519U;
86     U32 rand32 = *seedPtr;
87     rand32 *= prime32;
88     rand32 += prime2;
89     rand32  = FUZ_rotl32(rand32, 13);
90     *seedPtr = rand32;
91     return rand32 >> 5;
92 }
93
94 static void* allocFunction(void* opaque, size_t size)
95 {
96     void* address = malloc(size);
97     (void)opaque;
98     return address;
99 }
100
101 static void freeFunction(void* opaque, void* address)
102 {
103     (void)opaque;
104     free(address);
105 }
106
107
108 /*======================================================
109 *   Basic Unit tests
110 ======================================================*/
111
112 typedef struct {
113     void* start;
114     size_t size;
115     size_t filled;
116 } buffer_t;
117
118 static const buffer_t g_nullBuffer = { NULL, 0 , 0 };
119
120 static buffer_t FUZ_createDictionary(const void* src, size_t srcSize, size_t blockSize, size_t requestedDictSize)
121 {
122     buffer_t dict = { NULL, 0, 0 };
123     size_t const nbBlocks = (srcSize + (blockSize-1)) / blockSize;
124     size_t* const blockSizes = (size_t*) malloc(nbBlocks * sizeof(size_t));
125     if (!blockSizes) return dict;
126     dict.start = malloc(requestedDictSize);
127     if (!dict.start) { free(blockSizes); return dict; }
128     {   size_t nb;
129         for (nb=0; nb<nbBlocks-1; nb++) blockSizes[nb] = blockSize;
130         blockSizes[nbBlocks-1] = srcSize - (blockSize * (nbBlocks-1));
131     }
132     {   size_t const dictSize = ZDICT_trainFromBuffer(dict.start, requestedDictSize, src, blockSizes, (unsigned)nbBlocks);
133         free(blockSizes);
134         if (ZDICT_isError(dictSize)) { free(dict.start); return (buffer_t){ NULL, 0, 0 }; }
135         dict.size = requestedDictSize;
136         dict.filled = dictSize;
137         return dict;   /* how to return dictSize ? */
138     }
139 }
140
141 static void FUZ_freeDictionary(buffer_t dict)
142 {
143     free(dict.start);
144 }
145
146
147 static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem customMem)
148 {
149     size_t const CNBufferSize = COMPRESSIBLE_NOISE_LENGTH;
150     void* CNBuffer = malloc(CNBufferSize);
151     size_t const skippableFrameSize = 11;
152     size_t const compressedBufferSize = (8 + skippableFrameSize) + ZSTD_compressBound(COMPRESSIBLE_NOISE_LENGTH);
153     void* compressedBuffer = malloc(compressedBufferSize);
154     size_t const decodedBufferSize = CNBufferSize;
155     void* decodedBuffer = malloc(decodedBufferSize);
156     size_t cSize;
157     int testResult = 0;
158     U32 testNb=0;
159     ZSTD_CStream* zc = ZSTD_createCStream_advanced(customMem);
160     ZSTD_DStream* zd = ZSTD_createDStream_advanced(customMem);
161     ZSTD_inBuffer  inBuff, inBuff2;
162     ZSTD_outBuffer outBuff;
163     buffer_t dictionary = g_nullBuffer;
164     unsigned dictID = 0;
165
166     /* Create compressible test buffer */
167     if (!CNBuffer || !compressedBuffer || !decodedBuffer || !zc || !zd) {
168         DISPLAY("Not enough memory, aborting \n");
169         goto _output_error;
170     }
171     RDG_genBuffer(CNBuffer, CNBufferSize, compressibility, 0., seed);
172
173     /* Create dictionary */
174     MEM_STATIC_ASSERT(COMPRESSIBLE_NOISE_LENGTH >= 4 MB);
175     dictionary = FUZ_createDictionary(CNBuffer, 4 MB, 4 KB, 40 KB);
176     if (!dictionary.start) {
177         DISPLAY("Error creating dictionary, aborting \n");
178         goto _output_error;
179     }
180     dictID = ZDICT_getDictID(dictionary.start, dictionary.filled);
181
182     /* generate skippable frame */
183     MEM_writeLE32(compressedBuffer, ZSTD_MAGIC_SKIPPABLE_START);
184     MEM_writeLE32(((char*)compressedBuffer)+4, (U32)skippableFrameSize);
185     cSize = skippableFrameSize + 8;
186
187     /* Basic compression test */
188     DISPLAYLEVEL(3, "test%3i : compress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH);
189     ZSTD_initCStream_usingDict(zc, CNBuffer, 128 KB, 1);
190     outBuff.dst = (char*)(compressedBuffer)+cSize;
191     outBuff.size = compressedBufferSize;
192     outBuff.pos = 0;
193     inBuff.src = CNBuffer;
194     inBuff.size = CNBufferSize;
195     inBuff.pos = 0;
196     { size_t const r = ZSTD_compressStream(zc, &outBuff, &inBuff);
197       if (ZSTD_isError(r)) goto _output_error; }
198     if (inBuff.pos != inBuff.size) goto _output_error;   /* entire input should be consumed */
199     { size_t const r = ZSTD_endStream(zc, &outBuff);
200       if (r != 0) goto _output_error; }  /* error, or some data not flushed */
201     cSize += outBuff.pos;
202     DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/COMPRESSIBLE_NOISE_LENGTH*100);
203
204     DISPLAYLEVEL(3, "test%3i : check CStream size : ", testNb++);
205     { size_t const s = ZSTD_sizeof_CStream(zc);
206       if (ZSTD_isError(s)) goto _output_error;
207       DISPLAYLEVEL(3, "OK (%u bytes) \n", (U32)s);
208     }
209
210     /* skippable frame test */
211     DISPLAYLEVEL(3, "test%3i : decompress skippable frame : ", testNb++);
212     ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB);
213     inBuff.src = compressedBuffer;
214     inBuff.size = cSize;
215     inBuff.pos = 0;
216     outBuff.dst = decodedBuffer;
217     outBuff.size = CNBufferSize;
218     outBuff.pos = 0;
219     { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
220       if (r != 0) goto _output_error; }
221     if (outBuff.pos != 0) goto _output_error;   /* skippable frame output len is 0 */
222     DISPLAYLEVEL(3, "OK \n");
223
224     /* Basic decompression test */
225     inBuff2 = inBuff;
226     DISPLAYLEVEL(3, "test%3i : decompress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH);
227     ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB);
228     { size_t const r = ZSTD_setDStreamParameter(zd, DStream_p_maxWindowSize, 1000000000);  /* large limit */
229       if (ZSTD_isError(r)) goto _output_error; }
230     { size_t const remaining = ZSTD_decompressStream(zd, &outBuff, &inBuff);
231       if (remaining != 0) goto _output_error; }  /* should reach end of frame == 0; otherwise, some data left, or an error */
232     if (outBuff.pos != CNBufferSize) goto _output_error;   /* should regenerate the same amount */
233     if (inBuff.pos != inBuff.size) goto _output_error;   /* should have read the entire frame */
234     DISPLAYLEVEL(3, "OK \n");
235
236     /* Re-use without init */
237     DISPLAYLEVEL(3, "test%3i : decompress again without init (re-use previous settings): ", testNb++);
238     outBuff.pos = 0;
239     { size_t const remaining = ZSTD_decompressStream(zd, &outBuff, &inBuff2);
240       if (remaining != 0) goto _output_error; }  /* should reach end of frame == 0; otherwise, some data left, or an error */
241     if (outBuff.pos != CNBufferSize) goto _output_error;   /* should regenerate the same amount */
242     if (inBuff.pos != inBuff.size) goto _output_error;   /* should have read the entire frame */
243     DISPLAYLEVEL(3, "OK \n");
244
245     /* check regenerated data is byte exact */
246     DISPLAYLEVEL(3, "test%3i : check decompressed result : ", testNb++);
247     {   size_t i;
248         for (i=0; i<CNBufferSize; i++) {
249             if (((BYTE*)decodedBuffer)[i] != ((BYTE*)CNBuffer)[i]) goto _output_error;
250     }   }
251     DISPLAYLEVEL(3, "OK \n");
252
253     DISPLAYLEVEL(3, "test%3i : check DStream size : ", testNb++);
254     { size_t const s = ZSTD_sizeof_DStream(zd);
255       if (ZSTD_isError(s)) goto _output_error;
256       DISPLAYLEVEL(3, "OK (%u bytes) \n", (U32)s);
257     }
258
259     /* Byte-by-byte decompression test */
260     DISPLAYLEVEL(3, "test%3i : decompress byte-by-byte : ", testNb++);
261     {   /* skippable frame */
262         size_t r = 1;
263         ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB);
264         inBuff.src = compressedBuffer;
265         outBuff.dst = decodedBuffer;
266         inBuff.pos = 0;
267         outBuff.pos = 0;
268         while (r) {   /* skippable frame */
269             inBuff.size = inBuff.pos + 1;
270             outBuff.size = outBuff.pos + 1;
271             r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
272             if (ZSTD_isError(r)) goto _output_error;
273         }
274         /* normal frame */
275         ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB);
276         r=1;
277         while (r) {
278             inBuff.size = inBuff.pos + 1;
279             outBuff.size = outBuff.pos + 1;
280             r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
281             if (ZSTD_isError(r)) goto _output_error;
282         }
283     }
284     if (outBuff.pos != CNBufferSize) goto _output_error;   /* should regenerate the same amount */
285     if (inBuff.pos != cSize) goto _output_error;   /* should have read the entire frame */
286     DISPLAYLEVEL(3, "OK \n");
287
288     /* check regenerated data is byte exact */
289     DISPLAYLEVEL(3, "test%3i : check decompressed result : ", testNb++);
290     {   size_t i;
291         for (i=0; i<CNBufferSize; i++) {
292             if (((BYTE*)decodedBuffer)[i] != ((BYTE*)CNBuffer)[i]) goto _output_error;;
293     }   }
294     DISPLAYLEVEL(3, "OK \n");
295
296     /* _srcSize compression test */
297     DISPLAYLEVEL(3, "test%3i : compress_srcSize %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH);
298     ZSTD_initCStream_srcSize(zc, 1, CNBufferSize);
299     outBuff.dst = (char*)(compressedBuffer);
300     outBuff.size = compressedBufferSize;
301     outBuff.pos = 0;
302     inBuff.src = CNBuffer;
303     inBuff.size = CNBufferSize;
304     inBuff.pos = 0;
305     { size_t const r = ZSTD_compressStream(zc, &outBuff, &inBuff);
306       if (ZSTD_isError(r)) goto _output_error; }
307     if (inBuff.pos != inBuff.size) goto _output_error;   /* entire input should be consumed */
308     { size_t const r = ZSTD_endStream(zc, &outBuff);
309       if (r != 0) goto _output_error; }  /* error, or some data not flushed */
310     { unsigned long long origSize = ZSTD_findDecompressedSize(outBuff.dst, outBuff.pos);
311       if ((size_t)origSize != CNBufferSize) goto _output_error; }  /* exact original size must be present */
312     DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/COMPRESSIBLE_NOISE_LENGTH*100);
313
314     /* wrong _srcSize compression test */
315     DISPLAYLEVEL(3, "test%3i : wrong srcSize : %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH-1);
316     ZSTD_initCStream_srcSize(zc, 1, CNBufferSize-1);
317     outBuff.dst = (char*)(compressedBuffer);
318     outBuff.size = compressedBufferSize;
319     outBuff.pos = 0;
320     inBuff.src = CNBuffer;
321     inBuff.size = CNBufferSize;
322     inBuff.pos = 0;
323     { size_t const r = ZSTD_compressStream(zc, &outBuff, &inBuff);
324       if (ZSTD_isError(r)) goto _output_error; }
325     if (inBuff.pos != inBuff.size) goto _output_error;   /* entire input should be consumed */
326     { size_t const r = ZSTD_endStream(zc, &outBuff);
327       if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error;    /* must fail : wrong srcSize */
328       DISPLAYLEVEL(3, "OK (error detected : %s) \n", ZSTD_getErrorName(r)); }
329
330     /* Complex context re-use scenario */
331     DISPLAYLEVEL(3, "test%3i : context re-use : ", testNb++);
332     ZSTD_freeCStream(zc);
333     zc = ZSTD_createCStream_advanced(customMem);
334     if (zc==NULL) goto _output_error;   /* memory allocation issue */
335     /* use 1 */
336     {   size_t const inSize = 513;
337         ZSTD_initCStream_advanced(zc, NULL, 0, ZSTD_getParams(19, inSize, 0), inSize);   /* needs btopt + search3 to trigger hashLog3 */
338         inBuff.src = CNBuffer;
339         inBuff.size = inSize;
340         inBuff.pos = 0;
341         outBuff.dst = (char*)(compressedBuffer)+cSize;
342         outBuff.size = ZSTD_compressBound(inSize);
343         outBuff.pos = 0;
344         { size_t const r = ZSTD_compressStream(zc, &outBuff, &inBuff);
345             if (ZSTD_isError(r)) goto _output_error; }
346         if (inBuff.pos != inBuff.size) goto _output_error;   /* entire input should be consumed */
347         { size_t const r = ZSTD_endStream(zc, &outBuff);
348             if (r != 0) goto _output_error; }  /* error, or some data not flushed */
349     }
350     /* use 2 */
351     {   size_t const inSize = 1025;   /* will not continue, because tables auto-adjust and are therefore different size */
352         ZSTD_initCStream_advanced(zc, NULL, 0, ZSTD_getParams(19, inSize, 0), inSize);   /* needs btopt + search3 to trigger hashLog3 */
353         inBuff.src = CNBuffer;
354         inBuff.size = inSize;
355         inBuff.pos = 0;
356         outBuff.dst = (char*)(compressedBuffer)+cSize;
357         outBuff.size = ZSTD_compressBound(inSize);
358         outBuff.pos = 0;
359         { size_t const r = ZSTD_compressStream(zc, &outBuff, &inBuff);
360             if (ZSTD_isError(r)) goto _output_error; }
361         if (inBuff.pos != inBuff.size) goto _output_error;   /* entire input should be consumed */
362         { size_t const r = ZSTD_endStream(zc, &outBuff);
363             if (r != 0) goto _output_error; }  /* error, or some data not flushed */
364     }
365     DISPLAYLEVEL(3, "OK \n");
366
367     /* CDict scenario */
368     DISPLAYLEVEL(3, "test%3i : digested dictionary : ", testNb++);
369     {   ZSTD_CDict* const cdict = ZSTD_createCDict(dictionary.start, dictionary.filled, 1);
370         size_t const initError = ZSTD_initCStream_usingCDict(zc, cdict);
371         if (ZSTD_isError(initError)) goto _output_error;
372         cSize = 0;
373         outBuff.dst = compressedBuffer;
374         outBuff.size = compressedBufferSize;
375         outBuff.pos = 0;
376         inBuff.src = CNBuffer;
377         inBuff.size = CNBufferSize;
378         inBuff.pos = 0;
379         { size_t const r = ZSTD_compressStream(zc, &outBuff, &inBuff);
380           if (ZSTD_isError(r)) goto _output_error; }
381         if (inBuff.pos != inBuff.size) goto _output_error;   /* entire input should be consumed */
382         { size_t const r = ZSTD_endStream(zc, &outBuff);
383           if (r != 0) goto _output_error; }  /* error, or some data not flushed */
384         cSize = outBuff.pos;
385         ZSTD_freeCDict(cdict);
386         DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBufferSize*100);
387     }
388
389     DISPLAYLEVEL(3, "test%3i : check CStream size : ", testNb++);
390     { size_t const s = ZSTD_sizeof_CStream(zc);
391       if (ZSTD_isError(s)) goto _output_error;
392       DISPLAYLEVEL(3, "OK (%u bytes) \n", (U32)s);
393     }
394
395     DISPLAYLEVEL(4, "test%3i : check Dictionary ID : ", testNb++);
396     { unsigned const dID = ZSTD_getDictID_fromFrame(compressedBuffer, cSize);
397       if (dID != dictID) goto _output_error;
398       DISPLAYLEVEL(4, "OK (%u) \n", dID);
399     }
400
401     /* DDict scenario */
402     DISPLAYLEVEL(3, "test%3i : decompress %u bytes with digested dictionary : ", testNb++, (U32)CNBufferSize);
403     {   ZSTD_DDict* const ddict = ZSTD_createDDict(dictionary.start, dictionary.filled);
404         size_t const initError = ZSTD_initDStream_usingDDict(zd, ddict);
405         if (ZSTD_isError(initError)) goto _output_error;
406         inBuff.src = compressedBuffer;
407         inBuff.size = cSize;
408         inBuff.pos = 0;
409         outBuff.dst = decodedBuffer;
410         outBuff.size = CNBufferSize;
411         outBuff.pos = 0;
412         { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
413           if (r != 0) goto _output_error; }  /* should reach end of frame == 0; otherwise, some data left, or an error */
414         if (outBuff.pos != CNBufferSize) goto _output_error;   /* should regenerate the same amount */
415         if (inBuff.pos != inBuff.size) goto _output_error;   /* should have read the entire frame */
416         ZSTD_freeDDict(ddict);
417         DISPLAYLEVEL(3, "OK \n");
418     }
419
420     /* test ZSTD_setDStreamParameter() resilience */
421     DISPLAYLEVEL(3, "test%3i : wrong parameter for ZSTD_setDStreamParameter(): ", testNb++);
422     { size_t const r = ZSTD_setDStreamParameter(zd, (ZSTD_DStreamParameter_e)999, 1);  /* large limit */
423       if (!ZSTD_isError(r)) goto _output_error; }
424     DISPLAYLEVEL(3, "OK \n");
425
426     /* Memory restriction */
427     DISPLAYLEVEL(3, "test%3i : maxWindowSize < frame requirement : ", testNb++);
428     ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB);
429     { size_t const r = ZSTD_setDStreamParameter(zd, DStream_p_maxWindowSize, 1000);  /* too small limit */
430       if (ZSTD_isError(r)) goto _output_error; }
431     inBuff.src = compressedBuffer;
432     inBuff.size = cSize;
433     inBuff.pos = 0;
434     outBuff.dst = decodedBuffer;
435     outBuff.size = CNBufferSize;
436     outBuff.pos = 0;
437     { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
438       if (!ZSTD_isError(r)) goto _output_error;  /* must fail : frame requires > 100 bytes */
439       DISPLAYLEVEL(3, "OK (%s)\n", ZSTD_getErrorName(r)); }
440
441     /* Unknown srcSize */
442     DISPLAYLEVEL(3, "test%3i : pledgedSrcSize == 0 behaves properly : ", testNb++);
443     {   ZSTD_parameters params = ZSTD_getParams(5, 0, 0);
444         params.fParams.contentSizeFlag = 1;
445         ZSTD_initCStream_advanced(zc, NULL, 0, params, 0); } /* cstream advanced should write the 0 size field */
446     inBuff.src = CNBuffer;
447     inBuff.size = 0;
448     inBuff.pos = 0;
449     outBuff.dst = compressedBuffer;
450     outBuff.size = compressedBufferSize;
451     outBuff.pos = 0;
452     if (ZSTD_isError(ZSTD_compressStream(zc, &outBuff, &inBuff))) goto _output_error;
453     if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error;
454     cSize = outBuff.pos;
455     if (ZSTD_findDecompressedSize(compressedBuffer, cSize) != 0) goto _output_error;
456
457     ZSTD_resetCStream(zc, 0); /* resetCStream should treat 0 as unknown */
458     inBuff.src = CNBuffer;
459     inBuff.size = 0;
460     inBuff.pos = 0;
461     outBuff.dst = compressedBuffer;
462     outBuff.size = compressedBufferSize;
463     outBuff.pos = 0;
464     if (ZSTD_isError(ZSTD_compressStream(zc, &outBuff, &inBuff))) goto _output_error;
465     if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error;
466     cSize = outBuff.pos;
467     if (ZSTD_findDecompressedSize(compressedBuffer, cSize) != ZSTD_CONTENTSIZE_UNKNOWN) goto _output_error;
468     DISPLAYLEVEL(3, "OK \n");
469
470     /* Overlen overwriting window data bug */
471     DISPLAYLEVEL(3, "test%3i : wildcopy doesn't overwrite potential match data : ", testNb++);
472     {   /* This test has a window size of 1024 bytes and consists of 3 blocks:
473             1. 'a' repeated 517 times
474             2. 'b' repeated 516 times
475             3. a compressed block with no literals and 3 sequence commands:
476                 litlength = 0, offset = 24, match length = 24
477                 litlength = 0, offset = 24, match length = 3 (this one creates an overlength write of length 2*WILDCOPY_OVERLENGTH - 3)
478                 litlength = 0, offset = 1021, match length = 3 (this one will try to read from overwritten data if the buffer is too small) */
479
480         const char* testCase =
481             "\x28\xB5\x2F\xFD\x04\x00\x4C\x00\x00\x10\x61\x61\x01\x00\x00\x2A"
482             "\x80\x05\x44\x00\x00\x08\x62\x01\x00\x00\x2A\x20\x04\x5D\x00\x00"
483             "\x00\x03\x40\x00\x00\x64\x60\x27\xB0\xE0\x0C\x67\x62\xCE\xE0";
484         ZSTD_DStream* zds = ZSTD_createDStream();
485
486         ZSTD_initDStream(zds);
487         inBuff.src = testCase;
488         inBuff.size = 47;
489         inBuff.pos = 0;
490         outBuff.dst = decodedBuffer;
491         outBuff.size = CNBufferSize;
492         outBuff.pos = 0;
493
494         while (inBuff.pos < inBuff.size) {
495             size_t const r = ZSTD_decompressStream(zds, &outBuff, &inBuff);
496             /* Bug will cause checksum to fail */
497             if (ZSTD_isError(r)) goto _output_error;
498         }
499
500         ZSTD_freeDStream(zds);
501     }
502     DISPLAYLEVEL(3, "OK \n");
503
504 _end:
505     FUZ_freeDictionary(dictionary);
506     ZSTD_freeCStream(zc);
507     ZSTD_freeDStream(zd);
508     free(CNBuffer);
509     free(compressedBuffer);
510     free(decodedBuffer);
511     return testResult;
512
513 _output_error:
514     testResult = 1;
515     DISPLAY("Error detected in Unit tests ! \n");
516     goto _end;
517 }
518
519
520 /* ======   Fuzzer tests   ====== */
521
522 static size_t findDiff(const void* buf1, const void* buf2, size_t max)
523 {
524     const BYTE* b1 = (const BYTE*)buf1;
525     const BYTE* b2 = (const BYTE*)buf2;
526     size_t u;
527     for (u=0; u<max; u++) {
528         if (b1[u] != b2[u]) break;
529     }
530     DISPLAY("Error at position %u / %u \n", (U32)u, (U32)max);
531     DISPLAY(" %02X %02X %02X  :%02X:  %02X %02X %02X %02X %02X \n",
532             b1[u-3], b1[u-2], b1[u-1], b1[u-0], b1[u+1], b1[u+2], b1[u+3], b1[u+4], b1[u+5]);
533     DISPLAY(" %02X %02X %02X  :%02X:  %02X %02X %02X %02X %02X \n",
534             b2[u-3], b2[u-2], b2[u-1], b2[u-0], b2[u+1], b2[u+2], b2[u+3], b2[u+4], b2[u+5]);
535     return u;
536 }
537
538 static size_t FUZ_rLogLength(U32* seed, U32 logLength)
539 {
540     size_t const lengthMask = ((size_t)1 << logLength) - 1;
541     return (lengthMask+1) + (FUZ_rand(seed) & lengthMask);
542 }
543
544 static size_t FUZ_randomLength(U32* seed, U32 maxLog)
545 {
546     U32 const logLength = FUZ_rand(seed) % maxLog;
547     return FUZ_rLogLength(seed, logLength);
548 }
549
550 #define MIN(a,b)   ( (a) < (b) ? (a) : (b) )
551
552 #define CHECK(cond, ...) if (cond) { DISPLAY("Error => "); DISPLAY(__VA_ARGS__); \
553                          DISPLAY(" (seed %u, test nb %u)  \n", seed, testNb); goto _output_error; }
554
555 static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compressibility)
556 {
557     static const U32 maxSrcLog = 24;
558     static const U32 maxSampleLog = 19;
559     size_t const srcBufferSize = (size_t)1<<maxSrcLog;
560     BYTE* cNoiseBuffer[5];
561     size_t const copyBufferSize= srcBufferSize + (1<<maxSampleLog);
562     BYTE*  const copyBuffer = (BYTE*)malloc (copyBufferSize);
563     size_t const cBufferSize   = ZSTD_compressBound(srcBufferSize);
564     BYTE*  const cBuffer = (BYTE*)malloc (cBufferSize);
565     size_t const dstBufferSize = srcBufferSize;
566     BYTE*  const dstBuffer = (BYTE*)malloc (dstBufferSize);
567     U32 result = 0;
568     U32 testNb = 0;
569     U32 coreSeed = seed;
570     ZSTD_CStream* zc = ZSTD_createCStream();   /* will be reset sometimes */
571     ZSTD_DStream* zd = ZSTD_createDStream();   /* will be reset sometimes */
572     ZSTD_DStream* const zd_noise = ZSTD_createDStream();
573     clock_t const startClock = clock();
574     const BYTE* dict=NULL;   /* can keep same dict on 2 consecutive tests */
575     size_t dictSize = 0;
576     U32 oldTestLog = 0;
577
578     /* allocations */
579     cNoiseBuffer[0] = (BYTE*)malloc (srcBufferSize);
580     cNoiseBuffer[1] = (BYTE*)malloc (srcBufferSize);
581     cNoiseBuffer[2] = (BYTE*)malloc (srcBufferSize);
582     cNoiseBuffer[3] = (BYTE*)malloc (srcBufferSize);
583     cNoiseBuffer[4] = (BYTE*)malloc (srcBufferSize);
584     CHECK (!cNoiseBuffer[0] || !cNoiseBuffer[1] || !cNoiseBuffer[2] || !cNoiseBuffer[3] || !cNoiseBuffer[4] ||
585            !copyBuffer || !dstBuffer || !cBuffer || !zc || !zd || !zd_noise ,
586            "Not enough memory, fuzzer tests cancelled");
587
588     /* Create initial samples */
589     RDG_genBuffer(cNoiseBuffer[0], srcBufferSize, 0.00, 0., coreSeed);    /* pure noise */
590     RDG_genBuffer(cNoiseBuffer[1], srcBufferSize, 0.05, 0., coreSeed);    /* barely compressible */
591     RDG_genBuffer(cNoiseBuffer[2], srcBufferSize, compressibility, 0., coreSeed);
592     RDG_genBuffer(cNoiseBuffer[3], srcBufferSize, 0.95, 0., coreSeed);    /* highly compressible */
593     RDG_genBuffer(cNoiseBuffer[4], srcBufferSize, 1.00, 0., coreSeed);    /* sparse content */
594     memset(copyBuffer, 0x65, copyBufferSize);                             /* make copyBuffer considered initialized */
595     ZSTD_initDStream_usingDict(zd, NULL, 0);  /* ensure at least one init */
596
597     /* catch up testNb */
598     for (testNb=1; testNb < startTest; testNb++)
599         FUZ_rand(&coreSeed);
600
601     /* test loop */
602     for ( ; (testNb <= nbTests) || (FUZ_GetClockSpan(startClock) < g_clockTime) ; testNb++ ) {
603         U32 lseed;
604         const BYTE* srcBuffer;
605         size_t totalTestSize, totalGenSize, cSize;
606         XXH64_state_t xxhState;
607         U64 crcOrig;
608         U32 resetAllowed = 1;
609         size_t maxTestSize;
610
611         /* init */
612         if (nbTests >= testNb) { DISPLAYUPDATE(2, "\r%6u/%6u    ", testNb, nbTests); }
613         else { DISPLAYUPDATE(2, "\r%6u          ", testNb); }
614         FUZ_rand(&coreSeed);
615         lseed = coreSeed ^ prime32;
616
617         /* states full reset (deliberately not synchronized) */
618         /* some issues can only happen when reusing states */
619         if ((FUZ_rand(&lseed) & 0xFF) == 131) { ZSTD_freeCStream(zc); zc = ZSTD_createCStream(); resetAllowed=0; }
620         if ((FUZ_rand(&lseed) & 0xFF) == 132) { ZSTD_freeDStream(zd); zd = ZSTD_createDStream(); ZSTD_initDStream_usingDict(zd, NULL, 0);  /* ensure at least one init */ }
621
622         /* srcBuffer selection [0-4] */
623         {   U32 buffNb = FUZ_rand(&lseed) & 0x7F;
624             if (buffNb & 7) buffNb=2;   /* most common : compressible (P) */
625             else {
626                 buffNb >>= 3;
627                 if (buffNb & 7) {
628                     const U32 tnb[2] = { 1, 3 };   /* barely/highly compressible */
629                     buffNb = tnb[buffNb >> 3];
630                 } else {
631                     const U32 tnb[2] = { 0, 4 };   /* not compressible / sparse */
632                     buffNb = tnb[buffNb >> 3];
633             }   }
634             srcBuffer = cNoiseBuffer[buffNb];
635         }
636
637         /* compression init */
638         if ((FUZ_rand(&lseed)&1) /* at beginning, to keep same nb of rand */
639             && oldTestLog /* at least one test happened */ && resetAllowed) {
640             maxTestSize = FUZ_randomLength(&lseed, oldTestLog+2);
641             if (maxTestSize >= srcBufferSize) maxTestSize = srcBufferSize-1;
642             {   U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? 0 : maxTestSize;
643                 size_t const resetError = ZSTD_resetCStream(zc, pledgedSrcSize);
644                 CHECK(ZSTD_isError(resetError), "ZSTD_resetCStream error : %s", ZSTD_getErrorName(resetError));
645             }
646         } else {
647             U32 const testLog = FUZ_rand(&lseed) % maxSrcLog;
648             U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog;
649             U32 const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (MAX(testLog, dictLog)/3))) + 1;
650             maxTestSize = FUZ_rLogLength(&lseed, testLog);
651             oldTestLog = testLog;
652             /* random dictionary selection */
653             dictSize  = ((FUZ_rand(&lseed)&63)==1) ? FUZ_rLogLength(&lseed, dictLog) : 0;
654             {   size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize);
655                 dict = srcBuffer + dictStart;
656             }
657             {   U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? 0 : maxTestSize;
658                 ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize);
659                 params.fParams.checksumFlag = FUZ_rand(&lseed) & 1;
660                 params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1;
661                 {   size_t const initError = ZSTD_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize);
662                     CHECK (ZSTD_isError(initError),"ZSTD_initCStream_advanced error : %s", ZSTD_getErrorName(initError));
663         }   }   }
664
665         /* multi-segments compression test */
666         XXH64_reset(&xxhState, 0);
667         {   ZSTD_outBuffer outBuff = { cBuffer, cBufferSize, 0 } ;
668             U32 n;
669             for (n=0, cSize=0, totalTestSize=0 ; totalTestSize < maxTestSize ; n++) {
670                 /* compress random chunks into randomly sized dst buffers */
671                 {   size_t const randomSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
672                     size_t const srcSize = MIN (maxTestSize-totalTestSize, randomSrcSize);
673                     size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize);
674                     size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
675                     size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize);
676                     ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 };
677                     outBuff.size = outBuff.pos + dstBuffSize;
678
679                     { size_t const compressionError = ZSTD_compressStream(zc, &outBuff, &inBuff);
680                       CHECK (ZSTD_isError(compressionError), "compression error : %s", ZSTD_getErrorName(compressionError)); }
681
682                     XXH64_update(&xxhState, srcBuffer+srcStart, inBuff.pos);
683                     memcpy(copyBuffer+totalTestSize, srcBuffer+srcStart, inBuff.pos);
684                     totalTestSize += inBuff.pos;
685                 }
686
687                 /* random flush operation, to mess around */
688                 if ((FUZ_rand(&lseed) & 15) == 0) {
689                     size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
690                     size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize);
691                     outBuff.size = outBuff.pos + adjustedDstSize;
692                     {   size_t const flushError = ZSTD_flushStream(zc, &outBuff);
693                         CHECK (ZSTD_isError(flushError), "flush error : %s", ZSTD_getErrorName(flushError));
694             }   }   }
695
696             /* final frame epilogue */
697             {   size_t remainingToFlush = (size_t)(-1);
698                 while (remainingToFlush) {
699                     size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
700                     size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize);
701                     U32 const enoughDstSize = (adjustedDstSize >= remainingToFlush);
702                     outBuff.size = outBuff.pos + adjustedDstSize;
703                     remainingToFlush = ZSTD_endStream(zc, &outBuff);
704                     CHECK (ZSTD_isError(remainingToFlush), "flush error : %s", ZSTD_getErrorName(remainingToFlush));
705                     CHECK (enoughDstSize && remainingToFlush, "ZSTD_endStream() not fully flushed (%u remaining), but enough space available", (U32)remainingToFlush);
706             }   }
707             crcOrig = XXH64_digest(&xxhState);
708             cSize = outBuff.pos;
709         }
710
711         /* multi - fragments decompression test */
712         if (!dictSize /* don't reset if dictionary : could be different */ && (FUZ_rand(&lseed) & 1)) {
713             CHECK (ZSTD_isError(ZSTD_resetDStream(zd)), "ZSTD_resetDStream failed");
714         } else {
715             ZSTD_initDStream_usingDict(zd, dict, dictSize);
716         }
717         {   size_t decompressionResult = 1;
718             ZSTD_inBuffer  inBuff = { cBuffer, cSize, 0 };
719             ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
720             for (totalGenSize = 0 ; decompressionResult ; ) {
721                 size_t const readCSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
722                 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
723                 size_t const dstBuffSize = MIN(dstBufferSize - totalGenSize, randomDstSize);
724                 inBuff.size = inBuff.pos + readCSrcSize;
725                 outBuff.size = inBuff.pos + dstBuffSize;
726                 decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff);
727                 CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult));
728             }
729             CHECK (decompressionResult != 0, "frame not fully decoded");
730             CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size")
731             CHECK (inBuff.pos != cSize, "compressed data should be fully read")
732             {   U64 const crcDest = XXH64(dstBuffer, totalTestSize, 0);
733                 if (crcDest!=crcOrig) findDiff(copyBuffer, dstBuffer, totalTestSize);
734                 CHECK (crcDest!=crcOrig, "decompressed data corrupted");
735         }   }
736
737         /*=====   noisy/erroneous src decompression test   =====*/
738
739         /* add some noise */
740         {   U32 const nbNoiseChunks = (FUZ_rand(&lseed) & 7) + 2;
741             U32 nn; for (nn=0; nn<nbNoiseChunks; nn++) {
742                 size_t const randomNoiseSize = FUZ_randomLength(&lseed, maxSampleLog);
743                 size_t const noiseSize  = MIN((cSize/3) , randomNoiseSize);
744                 size_t const noiseStart = FUZ_rand(&lseed) % (srcBufferSize - noiseSize);
745                 size_t const cStart = FUZ_rand(&lseed) % (cSize - noiseSize);
746                 memcpy(cBuffer+cStart, srcBuffer+noiseStart, noiseSize);
747         }   }
748
749         /* try decompression on noisy data */
750         ZSTD_initDStream(zd_noise);   /* note : no dictionary */
751         {   ZSTD_inBuffer  inBuff = { cBuffer, cSize, 0 };
752             ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
753             while (outBuff.pos < dstBufferSize) {
754                 size_t const randomCSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
755                 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
756                 size_t const adjustedDstSize = MIN(dstBufferSize - outBuff.pos, randomDstSize);
757                 size_t const adjustedCSrcSize = MIN(cSize - inBuff.pos, randomCSrcSize);
758                 outBuff.size = outBuff.pos + adjustedDstSize;
759                 inBuff.size  = inBuff.pos + adjustedCSrcSize;
760                 {   size_t const decompressError = ZSTD_decompressStream(zd, &outBuff, &inBuff);
761                     if (ZSTD_isError(decompressError)) break;   /* error correctly detected */
762                     /* No forward progress possible */
763                     if (outBuff.pos < outBuff.size && inBuff.pos == cSize) break;
764     }   }   }   }
765     DISPLAY("\r%u fuzzer tests completed   \n", testNb);
766
767 _cleanup:
768     ZSTD_freeCStream(zc);
769     ZSTD_freeDStream(zd);
770     ZSTD_freeDStream(zd_noise);
771     free(cNoiseBuffer[0]);
772     free(cNoiseBuffer[1]);
773     free(cNoiseBuffer[2]);
774     free(cNoiseBuffer[3]);
775     free(cNoiseBuffer[4]);
776     free(copyBuffer);
777     free(cBuffer);
778     free(dstBuffer);
779     return result;
780
781 _output_error:
782     result = 1;
783     goto _cleanup;
784 }
785
786
787 /* Multi-threading version of fuzzer Tests */
788 static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double compressibility)
789 {
790     static const U32 maxSrcLog = 24;
791     static const U32 maxSampleLog = 19;
792     size_t const srcBufferSize = (size_t)1<<maxSrcLog;
793     BYTE* cNoiseBuffer[5];
794     size_t const copyBufferSize= srcBufferSize + (1<<maxSampleLog);
795     BYTE*  const copyBuffer = (BYTE*)malloc (copyBufferSize);
796     size_t const cBufferSize   = ZSTD_compressBound(srcBufferSize);
797     BYTE*  const cBuffer = (BYTE*)malloc (cBufferSize);
798     size_t const dstBufferSize = srcBufferSize;
799     BYTE*  const dstBuffer = (BYTE*)malloc (dstBufferSize);
800     U32 result = 0;
801     U32 testNb = 0;
802     U32 coreSeed = seed;
803     ZSTDMT_CCtx* zc = ZSTDMT_createCCtx(2);   /* will be reset sometimes */
804     ZSTD_DStream* zd = ZSTD_createDStream();   /* will be reset sometimes */
805     ZSTD_DStream* const zd_noise = ZSTD_createDStream();
806     clock_t const startClock = clock();
807     const BYTE* dict=NULL;   /* can keep same dict on 2 consecutive tests */
808     size_t dictSize = 0;
809     U32 oldTestLog = 0;
810
811     /* allocations */
812     cNoiseBuffer[0] = (BYTE*)malloc (srcBufferSize);
813     cNoiseBuffer[1] = (BYTE*)malloc (srcBufferSize);
814     cNoiseBuffer[2] = (BYTE*)malloc (srcBufferSize);
815     cNoiseBuffer[3] = (BYTE*)malloc (srcBufferSize);
816     cNoiseBuffer[4] = (BYTE*)malloc (srcBufferSize);
817     CHECK (!cNoiseBuffer[0] || !cNoiseBuffer[1] || !cNoiseBuffer[2] || !cNoiseBuffer[3] || !cNoiseBuffer[4] ||
818            !copyBuffer || !dstBuffer || !cBuffer || !zc || !zd || !zd_noise ,
819            "Not enough memory, fuzzer tests cancelled");
820
821     /* Create initial samples */
822     RDG_genBuffer(cNoiseBuffer[0], srcBufferSize, 0.00, 0., coreSeed);    /* pure noise */
823     RDG_genBuffer(cNoiseBuffer[1], srcBufferSize, 0.05, 0., coreSeed);    /* barely compressible */
824     RDG_genBuffer(cNoiseBuffer[2], srcBufferSize, compressibility, 0., coreSeed);
825     RDG_genBuffer(cNoiseBuffer[3], srcBufferSize, 0.95, 0., coreSeed);    /* highly compressible */
826     RDG_genBuffer(cNoiseBuffer[4], srcBufferSize, 1.00, 0., coreSeed);    /* sparse content */
827     memset(copyBuffer, 0x65, copyBufferSize);                             /* make copyBuffer considered initialized */
828     ZSTD_initDStream_usingDict(zd, NULL, 0);  /* ensure at least one init */
829
830     /* catch up testNb */
831     for (testNb=1; testNb < startTest; testNb++)
832         FUZ_rand(&coreSeed);
833
834     /* test loop */
835     for ( ; (testNb <= nbTests) || (FUZ_GetClockSpan(startClock) < g_clockTime) ; testNb++ ) {
836         U32 lseed;
837         const BYTE* srcBuffer;
838         size_t totalTestSize, totalGenSize, cSize;
839         XXH64_state_t xxhState;
840         U64 crcOrig;
841         U32 resetAllowed = 1;
842         size_t maxTestSize;
843
844         /* init */
845         if (nbTests >= testNb) { DISPLAYUPDATE(2, "\r%6u/%6u    ", testNb, nbTests); }
846         else { DISPLAYUPDATE(2, "\r%6u          ", testNb); }
847         FUZ_rand(&coreSeed);
848         lseed = coreSeed ^ prime32;
849
850         /* states full reset (deliberately not synchronized) */
851         /* some issues can only happen when reusing states */
852         if ((FUZ_rand(&lseed) & 0xFF) == 131) {
853             U32 const nbThreads = (FUZ_rand(&lseed) % 6) + 1;
854             ZSTDMT_freeCCtx(zc);
855             zc = ZSTDMT_createCCtx(nbThreads);
856             resetAllowed=0;
857         }
858         if ((FUZ_rand(&lseed) & 0xFF) == 132) {
859             ZSTD_freeDStream(zd);
860             zd = ZSTD_createDStream();
861             ZSTD_initDStream_usingDict(zd, NULL, 0);  /* ensure at least one init */
862         }
863
864         /* srcBuffer selection [0-4] */
865         {   U32 buffNb = FUZ_rand(&lseed) & 0x7F;
866             if (buffNb & 7) buffNb=2;   /* most common : compressible (P) */
867             else {
868                 buffNb >>= 3;
869                 if (buffNb & 7) {
870                     const U32 tnb[2] = { 1, 3 };   /* barely/highly compressible */
871                     buffNb = tnb[buffNb >> 3];
872                 } else {
873                     const U32 tnb[2] = { 0, 4 };   /* not compressible / sparse */
874                     buffNb = tnb[buffNb >> 3];
875             }   }
876             srcBuffer = cNoiseBuffer[buffNb];
877         }
878
879         /* compression init */
880         if ((FUZ_rand(&lseed)&1) /* at beginning, to keep same nb of rand */
881             && oldTestLog /* at least one test happened */ && resetAllowed) {
882             maxTestSize = FUZ_randomLength(&lseed, oldTestLog+2);
883             if (maxTestSize >= srcBufferSize) maxTestSize = srcBufferSize-1;
884             {   int const compressionLevel = (FUZ_rand(&lseed) % 5) + 1;
885                 size_t const resetError = ZSTDMT_initCStream(zc, compressionLevel);
886                 CHECK(ZSTD_isError(resetError), "ZSTDMT_initCStream error : %s", ZSTD_getErrorName(resetError));
887             }
888         } else {
889             U32 const testLog = FUZ_rand(&lseed) % maxSrcLog;
890             U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog;
891             U32 const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (MAX(testLog, dictLog)/3))) + 1;
892             maxTestSize = FUZ_rLogLength(&lseed, testLog);
893             oldTestLog = testLog;
894             /* random dictionary selection */
895             dictSize  = ((FUZ_rand(&lseed)&63)==1) ? FUZ_rLogLength(&lseed, dictLog) : 0;
896             {   size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize);
897                 dict = srcBuffer + dictStart;
898             }
899             {   U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? 0 : maxTestSize;
900                 ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize);
901                 DISPLAYLEVEL(5, "Init with windowLog = %u \n", params.cParams.windowLog);
902                 params.fParams.checksumFlag = FUZ_rand(&lseed) & 1;
903                 params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1;
904                 { size_t const initError = ZSTDMT_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize);
905                   CHECK (ZSTD_isError(initError),"ZSTDMT_initCStream_advanced error : %s", ZSTD_getErrorName(initError)); }
906                 ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_overlapSectionLog, FUZ_rand(&lseed) % 12);
907                 ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_sectionSize, FUZ_rand(&lseed) % (2*maxTestSize+1));
908         }   }
909
910         /* multi-segments compression test */
911         XXH64_reset(&xxhState, 0);
912         {   ZSTD_outBuffer outBuff = { cBuffer, cBufferSize, 0 } ;
913             U32 n;
914             for (n=0, cSize=0, totalTestSize=0 ; totalTestSize < maxTestSize ; n++) {
915                 /* compress random chunks into randomly sized dst buffers */
916                 {   size_t const randomSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
917                     size_t const srcSize = MIN (maxTestSize-totalTestSize, randomSrcSize);
918                     size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize);
919                     size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
920                     size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize);
921                     ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 };
922                     outBuff.size = outBuff.pos + dstBuffSize;
923
924                     DISPLAYLEVEL(5, "Sending %u bytes to compress \n", (U32)srcSize);
925                     { size_t const compressionError = ZSTDMT_compressStream(zc, &outBuff, &inBuff);
926                       CHECK (ZSTD_isError(compressionError), "compression error : %s", ZSTD_getErrorName(compressionError)); }
927                     DISPLAYLEVEL(5, "%u bytes read by ZSTDMT_compressStream \n", (U32)inBuff.pos);
928
929                     XXH64_update(&xxhState, srcBuffer+srcStart, inBuff.pos);
930                     memcpy(copyBuffer+totalTestSize, srcBuffer+srcStart, inBuff.pos);
931                     totalTestSize += inBuff.pos;
932                 }
933
934                 /* random flush operation, to mess around */
935                 if ((FUZ_rand(&lseed) & 15) == 0) {
936                     size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
937                     size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize);
938                     outBuff.size = outBuff.pos + adjustedDstSize;
939                     DISPLAYLEVEL(5, "Flushing into dst buffer of size %u \n", (U32)adjustedDstSize);
940                     {   size_t const flushError = ZSTDMT_flushStream(zc, &outBuff);
941                         CHECK (ZSTD_isError(flushError), "flush error : %s", ZSTD_getErrorName(flushError));
942             }   }   }
943
944             /* final frame epilogue */
945             {   size_t remainingToFlush = (size_t)(-1);
946                 while (remainingToFlush) {
947                     size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
948                     size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize);
949                     outBuff.size = outBuff.pos + adjustedDstSize;
950                     DISPLAYLEVEL(5, "Ending into dst buffer of size %u \n", (U32)adjustedDstSize);
951                     remainingToFlush = ZSTDMT_endStream(zc, &outBuff);
952                     CHECK (ZSTD_isError(remainingToFlush), "flush error : %s", ZSTD_getErrorName(remainingToFlush));
953                     DISPLAYLEVEL(5, "endStream : remainingToFlush : %u \n", (U32)remainingToFlush);
954             }   }
955             DISPLAYLEVEL(5, "Frame completed \n");
956             crcOrig = XXH64_digest(&xxhState);
957             cSize = outBuff.pos;
958         }
959
960         /* multi - fragments decompression test */
961         if (!dictSize /* don't reset if dictionary : could be different */ && (FUZ_rand(&lseed) & 1)) {
962             CHECK (ZSTD_isError(ZSTD_resetDStream(zd)), "ZSTD_resetDStream failed");
963         } else {
964             ZSTD_initDStream_usingDict(zd, dict, dictSize);
965         }
966         {   size_t decompressionResult = 1;
967             ZSTD_inBuffer  inBuff = { cBuffer, cSize, 0 };
968             ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
969             for (totalGenSize = 0 ; decompressionResult ; ) {
970                 size_t const readCSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
971                 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
972                 size_t const dstBuffSize = MIN(dstBufferSize - totalGenSize, randomDstSize);
973                 inBuff.size = inBuff.pos + readCSrcSize;
974                 outBuff.size = inBuff.pos + dstBuffSize;
975                 decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff);
976                 CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult));
977             }
978             CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size (%u != %u)", (U32)outBuff.pos, (U32)totalTestSize);
979             CHECK (inBuff.pos != cSize, "compressed data should be fully read (%u != %u)", (U32)inBuff.pos, (U32)cSize);
980             {   U64 const crcDest = XXH64(dstBuffer, totalTestSize, 0);
981                 if (crcDest!=crcOrig) findDiff(copyBuffer, dstBuffer, totalTestSize);
982                 CHECK (crcDest!=crcOrig, "decompressed data corrupted");
983         }   }
984
985         /*=====   noisy/erroneous src decompression test   =====*/
986
987         /* add some noise */
988         {   U32 const nbNoiseChunks = (FUZ_rand(&lseed) & 7) + 2;
989             U32 nn; for (nn=0; nn<nbNoiseChunks; nn++) {
990                 size_t const randomNoiseSize = FUZ_randomLength(&lseed, maxSampleLog);
991                 size_t const noiseSize  = MIN((cSize/3) , randomNoiseSize);
992                 size_t const noiseStart = FUZ_rand(&lseed) % (srcBufferSize - noiseSize);
993                 size_t const cStart = FUZ_rand(&lseed) % (cSize - noiseSize);
994                 memcpy(cBuffer+cStart, srcBuffer+noiseStart, noiseSize);
995         }   }
996
997         /* try decompression on noisy data */
998         ZSTD_initDStream(zd_noise);   /* note : no dictionary */
999         {   ZSTD_inBuffer  inBuff = { cBuffer, cSize, 0 };
1000             ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
1001             while (outBuff.pos < dstBufferSize) {
1002                 size_t const randomCSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
1003                 size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
1004                 size_t const adjustedDstSize = MIN(dstBufferSize - outBuff.pos, randomDstSize);
1005                 size_t const adjustedCSrcSize = MIN(cSize - inBuff.pos, randomCSrcSize);
1006                 outBuff.size = outBuff.pos + adjustedDstSize;
1007                 inBuff.size  = inBuff.pos + adjustedCSrcSize;
1008                 {   size_t const decompressError = ZSTD_decompressStream(zd, &outBuff, &inBuff);
1009                     if (ZSTD_isError(decompressError)) break;   /* error correctly detected */
1010                     /* No forward progress possible */
1011                     if (outBuff.pos < outBuff.size && inBuff.pos == cSize) break;
1012     }   }   }   }
1013     DISPLAY("\r%u fuzzer tests completed   \n", testNb);
1014
1015 _cleanup:
1016     ZSTDMT_freeCCtx(zc);
1017     ZSTD_freeDStream(zd);
1018     ZSTD_freeDStream(zd_noise);
1019     free(cNoiseBuffer[0]);
1020     free(cNoiseBuffer[1]);
1021     free(cNoiseBuffer[2]);
1022     free(cNoiseBuffer[3]);
1023     free(cNoiseBuffer[4]);
1024     free(copyBuffer);
1025     free(cBuffer);
1026     free(dstBuffer);
1027     return result;
1028
1029 _output_error:
1030     result = 1;
1031     goto _cleanup;
1032 }
1033
1034
1035 /*-*******************************************************
1036 *  Command line
1037 *********************************************************/
1038 int FUZ_usage(const char* programName)
1039 {
1040     DISPLAY( "Usage :\n");
1041     DISPLAY( "      %s [args]\n", programName);
1042     DISPLAY( "\n");
1043     DISPLAY( "Arguments :\n");
1044     DISPLAY( " -i#    : Nb of tests (default:%u) \n", nbTestsDefault);
1045     DISPLAY( " -s#    : Select seed (default:prompt user)\n");
1046     DISPLAY( " -t#    : Select starting test number (default:0)\n");
1047     DISPLAY( " -P#    : Select compressibility in %% (default:%i%%)\n", FUZ_COMPRESSIBILITY_DEFAULT);
1048     DISPLAY( " -v     : verbose\n");
1049     DISPLAY( " -p     : pause at the end\n");
1050     DISPLAY( " -h     : display help and exit\n");
1051     return 0;
1052 }
1053
1054
1055 int main(int argc, const char** argv)
1056 {
1057     U32 seed=0;
1058     int seedset=0;
1059     int argNb;
1060     int nbTests = nbTestsDefault;
1061     int testNb = 0;
1062     int proba = FUZ_COMPRESSIBILITY_DEFAULT;
1063     int result=0;
1064     int mainPause = 0;
1065     int mtOnly = 0;
1066     const char* const programName = argv[0];
1067     ZSTD_customMem const customMem = { allocFunction, freeFunction, NULL };
1068     ZSTD_customMem const customNULL = { NULL, NULL, NULL };
1069
1070     /* Check command line */
1071     for(argNb=1; argNb<argc; argNb++) {
1072         const char* argument = argv[argNb];
1073         if(!argument) continue;   /* Protection if argument empty */
1074
1075         /* Parsing commands. Aggregated commands are allowed */
1076         if (argument[0]=='-') {
1077
1078             if (!strcmp(argument, "--mt")) { mtOnly=1; continue; }
1079
1080             argument++;
1081             while (*argument!=0) {
1082                 switch(*argument)
1083                 {
1084                 case 'h':
1085                     return FUZ_usage(programName);
1086
1087                 case 'v':
1088                     argument++;
1089                     g_displayLevel++;
1090                     break;
1091
1092                 case 'q':
1093                     argument++;
1094                     g_displayLevel--;
1095                     break;
1096
1097                 case 'p': /* pause at the end */
1098                     argument++;
1099                     mainPause = 1;
1100                     break;
1101
1102                 case 'i':   /* limit tests by nb of iterations (default) */
1103                     argument++;
1104                     nbTests=0; g_clockTime=0;
1105                     while ((*argument>='0') && (*argument<='9')) {
1106                         nbTests *= 10;
1107                         nbTests += *argument - '0';
1108                         argument++;
1109                     }
1110                     break;
1111
1112                 case 'T':   /* limit tests by time */
1113                     argument++;
1114                     nbTests=0; g_clockTime=0;
1115                     while ((*argument>='0') && (*argument<='9')) {
1116                         g_clockTime *= 10;
1117                         g_clockTime += *argument - '0';
1118                         argument++;
1119                     }
1120                     if (*argument=='m') g_clockTime *=60, argument++;
1121                     if (*argument=='n') argument++;
1122                     g_clockTime *= CLOCKS_PER_SEC;
1123                     break;
1124
1125                 case 's':   /* manually select seed */
1126                     argument++;
1127                     seed=0;
1128                     seedset=1;
1129                     while ((*argument>='0') && (*argument<='9')) {
1130                         seed *= 10;
1131                         seed += *argument - '0';
1132                         argument++;
1133                     }
1134                     break;
1135
1136                 case 't':   /* select starting test number */
1137                     argument++;
1138                     testNb=0;
1139                     while ((*argument>='0') && (*argument<='9')) {
1140                         testNb *= 10;
1141                         testNb += *argument - '0';
1142                         argument++;
1143                     }
1144                     break;
1145
1146                 case 'P':   /* compressibility % */
1147                     argument++;
1148                     proba=0;
1149                     while ((*argument>='0') && (*argument<='9')) {
1150                         proba *= 10;
1151                         proba += *argument - '0';
1152                         argument++;
1153                     }
1154                     if (proba<0) proba=0;
1155                     if (proba>100) proba=100;
1156                     break;
1157
1158                 default:
1159                     return FUZ_usage(programName);
1160                 }
1161     }   }   }   /* for(argNb=1; argNb<argc; argNb++) */
1162
1163     /* Get Seed */
1164     DISPLAY("Starting zstream tester (%i-bits, %s)\n", (int)(sizeof(size_t)*8), ZSTD_VERSION_STRING);
1165
1166     if (!seedset) {
1167         time_t const t = time(NULL);
1168         U32 const h = XXH32(&t, sizeof(t), 1);
1169         seed = h % 10000;
1170     }
1171
1172     DISPLAY("Seed = %u\n", seed);
1173     if (proba!=FUZ_COMPRESSIBILITY_DEFAULT) DISPLAY("Compressibility : %i%%\n", proba);
1174
1175     if (nbTests<=0) nbTests=1;
1176
1177     if (testNb==0) {
1178         result = basicUnitTests(0, ((double)proba) / 100, customNULL);  /* constant seed for predictability */
1179         if (!result) {
1180             DISPLAYLEVEL(3, "Unit tests using customMem :\n")
1181             result = basicUnitTests(0, ((double)proba) / 100, customMem);  /* use custom memory allocation functions */
1182     }   }
1183
1184     if (!result && !mtOnly) result = fuzzerTests(seed, nbTests, testNb, ((double)proba) / 100);
1185     if (!result) result = fuzzerTests_MT(seed, nbTests, testNb, ((double)proba) / 100);
1186
1187     if (mainPause) {
1188         int unused;
1189         DISPLAY("Press Enter \n");
1190         unused = getchar();
1191         (void)unused;
1192     }
1193     return result;
1194 }