1 /*
2     frameTest - test tool for lz4frame
3     Copyright (C) Yann Collet 2014
4     GPL v2 License
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 
20     You can contact the author at :
21     - LZ4 source repository : http://code.google.com/p/lz4/
22     - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
23 */
24 
25 /**************************************
26   Compiler specific
27 **************************************/
28 #define _CRT_SECURE_NO_WARNINGS   // fgets
29 #ifdef _MSC_VER    /* Visual Studio */
30 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
31 #  pragma warning(disable : 4146)        /* disable: C4146: minus unsigned expression */
32 #endif
33 #define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
34 #ifdef __GNUC__
35 #  pragma GCC diagnostic ignored "-Wmissing-braces"   /* GCC bug 53119 : doesn't accept { 0 } as initializer (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119) */
36 #  pragma GCC diagnostic ignored "-Wmissing-field-initializers"   /* GCC bug 53119 : doesn't accept { 0 } as initializer (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119) */
37 #endif
38 
39 
40 /**************************************
41   Includes
42 **************************************/
43 #include <stdlib.h>     // free
44 #include <stdio.h>      // fgets, sscanf
45 #include <sys/timeb.h>  // timeb
46 #include <string.h>     // strcmp
47 #include "lz4frame_static.h"
48 #include "xxhash.h"     // XXH64
49 
50 
51 /**************************************
52    Basic Types
53 **************************************/
54 #if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)   /* C99 */
55 # include <stdint.h>
56 typedef  uint8_t BYTE;
57 typedef uint16_t U16;
58 typedef uint32_t U32;
59 typedef  int32_t S32;
60 typedef uint64_t U64;
61 #else
62 typedef unsigned char       BYTE;
63 typedef unsigned short      U16;
64 typedef unsigned int        U32;
65 typedef   signed int        S32;
66 typedef unsigned long long  U64;
67 #endif
68 
69 
70 /**************************************
71  Constants
72 **************************************/
73 #ifndef LZ4_VERSION
74 #  define LZ4_VERSION ""
75 #endif
76 
77 #define KB *(1U<<10)
78 #define MB *(1U<<20)
79 #define GB *(1U<<30)
80 
81 static const U32 nbTestsDefault = 256 KB;
82 #define COMPRESSIBLE_NOISE_LENGTH (2 MB)
83 #define FUZ_COMPRESSIBILITY_DEFAULT 50
84 static const U32 prime1 = 2654435761U;
85 static const U32 prime2 = 2246822519U;
86 
87 
88 
89 /**************************************
90   Macros
91 **************************************/
92 #define DISPLAY(...)          fprintf(stderr, __VA_ARGS__)
93 #define DISPLAYLEVEL(l, ...)  if (displayLevel>=l) { DISPLAY(__VA_ARGS__); }
94 #define DISPLAYUPDATE(l, ...) if (displayLevel>=l) { \
95             if ((FUZ_GetMilliSpan(g_time) > refreshRate) || (displayLevel>=4)) \
96             { g_time = FUZ_GetMilliStart(); DISPLAY(__VA_ARGS__); \
97             if (displayLevel>=4) fflush(stdout); } }
98 static const U32 refreshRate = 150;
99 static U32 g_time = 0;
100 
101 
102 /*****************************************
103   Local Parameters
104 *****************************************/
105 static U32 no_prompt = 0;
106 static char* programName;
107 static U32 displayLevel = 2;
108 static U32 pause = 0;
109 
110 
111 /*********************************************************
112   Fuzzer functions
113 *********************************************************/
FUZ_GetMilliStart(void)114 static U32 FUZ_GetMilliStart(void)
115 {
116     struct timeb tb;
117     U32 nCount;
118     ftime( &tb );
119     nCount = (U32) (((tb.time & 0xFFFFF) * 1000) +  tb.millitm);
120     return nCount;
121 }
122 
123 
FUZ_GetMilliSpan(U32 nTimeStart)124 static U32 FUZ_GetMilliSpan(U32 nTimeStart)
125 {
126     U32 nCurrent = FUZ_GetMilliStart();
127     U32 nSpan = nCurrent - nTimeStart;
128     if (nTimeStart > nCurrent)
129         nSpan += 0x100000 * 1000;
130     return nSpan;
131 }
132 
133 
134 #  define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r)))
FUZ_rand(unsigned int * src)135 unsigned int FUZ_rand(unsigned int* src)
136 {
137     U32 rand32 = *src;
138     rand32 *= prime1;
139     rand32 += prime2;
140     rand32  = FUZ_rotl32(rand32, 13);
141     *src = rand32;
142     return rand32 >> 5;
143 }
144 
145 
146 #define FUZ_RAND15BITS  (FUZ_rand(seed) & 0x7FFF)
147 #define FUZ_RANDLENGTH  ( (FUZ_rand(seed) & 3) ? (FUZ_rand(seed) % 15) : (FUZ_rand(seed) % 510) + 15)
FUZ_fillCompressibleNoiseBuffer(void * buffer,unsigned bufferSize,double proba,U32 * seed)148 static void FUZ_fillCompressibleNoiseBuffer(void* buffer, unsigned bufferSize, double proba, U32* seed)
149 {
150     BYTE* BBuffer = (BYTE*)buffer;
151     unsigned pos = 0;
152     U32 P32 = (U32)(32768 * proba);
153 
154     // First Byte
155     BBuffer[pos++] = (BYTE)(FUZ_rand(seed));
156 
157     while (pos < bufferSize)
158     {
159         // Select : Literal (noise) or copy (within 64K)
160         if (FUZ_RAND15BITS < P32)
161         {
162             // Copy (within 64K)
163             unsigned match, end;
164             unsigned length = FUZ_RANDLENGTH + 4;
165             unsigned offset = FUZ_RAND15BITS + 1;
166             if (offset > pos) offset = pos;
167             if (pos + length > bufferSize) length = bufferSize - pos;
168             match = pos - offset;
169             end = pos + length;
170             while (pos < end) BBuffer[pos++] = BBuffer[match++];
171         }
172         else
173         {
174             // Literal (noise)
175             unsigned end;
176             unsigned length = FUZ_RANDLENGTH;
177             if (pos + length > bufferSize) length = bufferSize - pos;
178             end = pos + length;
179             while (pos < end) BBuffer[pos++] = (BYTE)(FUZ_rand(seed) >> 5);
180         }
181     }
182 }
183 
184 
FUZ_highbit(U32 v32)185 static unsigned FUZ_highbit(U32 v32)
186 {
187     unsigned nbBits = 0;
188     if (v32==0) return 0;
189     while (v32)
190     {
191         v32 >>= 1;
192         nbBits ++;
193     }
194     return nbBits;
195 }
196 
197 
basicTests(U32 seed,double compressibility)198 int basicTests(U32 seed, double compressibility)
199 {
200     int testResult = 0;
201     void* CNBuffer;
202     void* compressedBuffer;
203     void* decodedBuffer;
204     U32 randState = seed;
205     size_t cSize, testSize;
206     LZ4F_preferences_t prefs = { 0 };
207     LZ4F_decompressionContext_t dCtx;
208     U64 crcOrig;
209 
210     // Create compressible test buffer
211     CNBuffer = malloc(COMPRESSIBLE_NOISE_LENGTH);
212     compressedBuffer = malloc(LZ4F_compressFrameBound(COMPRESSIBLE_NOISE_LENGTH, NULL));
213     decodedBuffer = malloc(COMPRESSIBLE_NOISE_LENGTH);
214     FUZ_fillCompressibleNoiseBuffer(CNBuffer, COMPRESSIBLE_NOISE_LENGTH, compressibility, &randState);
215     crcOrig = XXH64(CNBuffer, COMPRESSIBLE_NOISE_LENGTH, 1);
216 
217     // Trivial tests : one-step frame
218     testSize = COMPRESSIBLE_NOISE_LENGTH;
219     DISPLAYLEVEL(3, "Using NULL preferences : \n");
220     cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, NULL), CNBuffer, testSize, NULL);
221     if (LZ4F_isError(cSize)) goto _output_error;
222     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
223 
224     DISPLAYLEVEL(3, "Decompression test : \n");
225     {
226         size_t decodedBufferSize = COMPRESSIBLE_NOISE_LENGTH;
227         size_t compressedBufferSize = cSize;
228         BYTE* op = (BYTE*)decodedBuffer;
229         BYTE* const oend = (BYTE*)decodedBuffer + COMPRESSIBLE_NOISE_LENGTH;
230         BYTE* ip = (BYTE*)compressedBuffer;
231         BYTE* const iend = (BYTE*)compressedBuffer + cSize;
232         U64 crcDest;
233 
234         LZ4F_errorCode_t errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
235         if (LZ4F_isError(errorCode)) goto _output_error;
236 
237         DISPLAYLEVEL(3, "Single Block : \n");
238         errorCode = LZ4F_decompress(dCtx, decodedBuffer, &decodedBufferSize, compressedBuffer, &compressedBufferSize, NULL);
239         crcDest = XXH64(decodedBuffer, COMPRESSIBLE_NOISE_LENGTH, 1);
240         if (crcDest != crcOrig) goto _output_error;
241         DISPLAYLEVEL(3, "Regenerated %i bytes \n", (int)decodedBufferSize);
242 
243         DISPLAYLEVEL(3, "Byte after byte : \n");
244         while (ip < iend)
245         {
246             size_t oSize = oend-op;
247             size_t iSize = 1;
248             //DISPLAY("%7i \n", (int)(ip-(BYTE*)compressedBuffer));
249             errorCode = LZ4F_decompress(dCtx, op, &oSize, ip, &iSize, NULL);
250             if (LZ4F_isError(errorCode)) goto _output_error;
251             op += oSize;
252             ip += iSize;
253         }
254         crcDest = XXH64(decodedBuffer, COMPRESSIBLE_NOISE_LENGTH, 1);
255         if (crcDest != crcOrig) goto _output_error;
256         DISPLAYLEVEL(3, "Regenerated %i bytes \n", (int)decodedBufferSize);
257 
258         errorCode = LZ4F_freeDecompressionContext(dCtx);
259         if (LZ4F_isError(errorCode)) goto _output_error;
260     }
261 
262     DISPLAYLEVEL(3, "Using 64 KB block : \n");
263     prefs.frameInfo.blockSizeID = max64KB;
264     prefs.frameInfo.contentChecksumFlag = contentChecksumEnabled;
265     cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
266     if (LZ4F_isError(cSize)) goto _output_error;
267     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
268 
269     DISPLAYLEVEL(3, "without checksum : \n");
270     prefs.frameInfo.contentChecksumFlag = noContentChecksum;
271     cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
272     if (LZ4F_isError(cSize)) goto _output_error;
273     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
274 
275     DISPLAYLEVEL(3, "Using 256 KB block : \n");
276     prefs.frameInfo.blockSizeID = max256KB;
277     prefs.frameInfo.contentChecksumFlag = contentChecksumEnabled;
278     cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
279     if (LZ4F_isError(cSize)) goto _output_error;
280     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
281 
282     DISPLAYLEVEL(3, "Decompression test : \n");
283     {
284         size_t decodedBufferSize = COMPRESSIBLE_NOISE_LENGTH;
285         unsigned maxBits = FUZ_highbit((U32)decodedBufferSize);
286         BYTE* op = (BYTE*)decodedBuffer;
287         BYTE* const oend = (BYTE*)decodedBuffer + COMPRESSIBLE_NOISE_LENGTH;
288         BYTE* ip = (BYTE*)compressedBuffer;
289         BYTE* const iend = (BYTE*)compressedBuffer + cSize;
290         U64 crcDest;
291 
292         LZ4F_errorCode_t errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
293         if (LZ4F_isError(errorCode)) goto _output_error;
294 
295         DISPLAYLEVEL(3, "random segment sizes : \n");
296         while (ip < iend)
297         {
298             unsigned nbBits = FUZ_rand(&randState) % maxBits;
299             size_t iSize = (FUZ_rand(&randState) & ((1<<nbBits)-1)) + 1;
300             size_t oSize = oend-op;
301             if (iSize > (size_t)(iend-ip)) iSize = iend-ip;
302             //DISPLAY("%7i : + %6i\n", (int)(ip-(BYTE*)compressedBuffer), (int)iSize);
303             errorCode = LZ4F_decompress(dCtx, op, &oSize, ip, &iSize, NULL);
304             if (LZ4F_isError(errorCode)) goto _output_error;
305             op += oSize;
306             ip += iSize;
307         }
308         crcDest = XXH64(decodedBuffer, COMPRESSIBLE_NOISE_LENGTH, 1);
309         if (crcDest != crcOrig) goto _output_error;
310         DISPLAYLEVEL(3, "Regenerated %i bytes \n", (int)decodedBufferSize);
311 
312         errorCode = LZ4F_freeDecompressionContext(dCtx);
313         if (LZ4F_isError(errorCode)) goto _output_error;
314     }
315 
316     DISPLAYLEVEL(3, "without checksum : \n");
317     prefs.frameInfo.contentChecksumFlag = noContentChecksum;
318     cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
319     if (LZ4F_isError(cSize)) goto _output_error;
320     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
321 
322     DISPLAYLEVEL(3, "Using 1 MB block : \n");
323     prefs.frameInfo.blockSizeID = max1MB;
324     prefs.frameInfo.contentChecksumFlag = contentChecksumEnabled;
325     cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
326     if (LZ4F_isError(cSize)) goto _output_error;
327     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
328 
329     DISPLAYLEVEL(3, "without checksum : \n");
330     prefs.frameInfo.contentChecksumFlag = noContentChecksum;
331     cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
332     if (LZ4F_isError(cSize)) goto _output_error;
333     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
334 
335     DISPLAYLEVEL(3, "Using 4 MB block : \n");
336     prefs.frameInfo.blockSizeID = max4MB;
337     prefs.frameInfo.contentChecksumFlag = contentChecksumEnabled;
338     cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
339     if (LZ4F_isError(cSize)) goto _output_error;
340     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
341 
342     DISPLAYLEVEL(3, "without checksum : \n");
343     prefs.frameInfo.contentChecksumFlag = noContentChecksum;
344     cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
345     if (LZ4F_isError(cSize)) goto _output_error;
346     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
347 
348     DISPLAY("Basic tests completed \n");
349 _end:
350     free(CNBuffer);
351     free(compressedBuffer);
352     free(decodedBuffer);
353     return testResult;
354 
355 _output_error:
356     testResult = 1;
357     DISPLAY("Error detected ! \n");
358     goto _end;
359 }
360 
361 
locateBuffDiff(const void * buff1,const void * buff2,size_t size,unsigned nonContiguous)362 static void locateBuffDiff(const void* buff1, const void* buff2, size_t size, unsigned nonContiguous)
363 {
364     int p=0;
365     BYTE* b1=(BYTE*)buff1;
366     BYTE* b2=(BYTE*)buff2;
367     if (nonContiguous)
368     {
369         DISPLAY("Non-contiguous output test (%i bytes)\n", (int)size);
370         return;
371     }
372     while (b1[p]==b2[p]) p++;
373     DISPLAY("Error at pos %i/%i : %02X != %02X \n", p, (int)size, b1[p], b2[p]);
374 }
375 
376 
377 static const U32 srcDataLength = 9 MB;  /* needs to be > 2x4MB to test large blocks */
378 
fuzzerTests(U32 seed,unsigned nbTests,unsigned startTest,double compressibility)379 int fuzzerTests(U32 seed, unsigned nbTests, unsigned startTest, double compressibility)
380 {
381     unsigned testResult = 0;
382     unsigned testNb = 0;
383     void* srcBuffer = NULL;
384     void* compressedBuffer = NULL;
385     void* decodedBuffer = NULL;
386     U32 coreRand = seed;
387     LZ4F_decompressionContext_t dCtx = NULL;
388     LZ4F_compressionContext_t cCtx = NULL;
389     size_t result;
390     XXH64_state_t xxh64;
391 #   define CHECK(cond, ...) if (cond) { DISPLAY("Error => "); DISPLAY(__VA_ARGS__); \
392                             DISPLAY(" (seed %u, test nb %u)  \n", seed, testNb); goto _output_error; }
393 
394     // Create buffers
395     result = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
396     CHECK(LZ4F_isError(result), "Allocation failed (error %i)", (int)result);
397     result = LZ4F_createCompressionContext(&cCtx, LZ4F_VERSION);
398     CHECK(LZ4F_isError(result), "Allocation failed (error %i)", (int)result);
399     srcBuffer = malloc(srcDataLength);
400     CHECK(srcBuffer==NULL, "srcBuffer Allocation failed");
401     compressedBuffer = malloc(LZ4F_compressFrameBound(srcDataLength, NULL));
402     CHECK(compressedBuffer==NULL, "compressedBuffer Allocation failed");
403     decodedBuffer = malloc(srcDataLength);
404     CHECK(decodedBuffer==NULL, "decodedBuffer Allocation failed");
405     FUZ_fillCompressibleNoiseBuffer(srcBuffer, srcDataLength, compressibility, &coreRand);
406 
407     // jump to requested testNb
408     for (testNb =0; testNb < startTest; testNb++) (void)FUZ_rand(&coreRand);   // sync randomizer
409 
410     // main fuzzer loop
411     for ( ; testNb < nbTests; testNb++)
412     {
413         U32 randState = coreRand ^ prime1;
414         unsigned BSId   = 4 + (FUZ_rand(&randState) & 3);
415         unsigned BMId   = FUZ_rand(&randState) & 1;
416         unsigned CCflag = FUZ_rand(&randState) & 1;
417         unsigned autoflush = (FUZ_rand(&randState) & 7) == 2;
418         LZ4F_preferences_t prefs = { 0 };
419         LZ4F_compressOptions_t cOptions = { 0 };
420         LZ4F_decompressOptions_t dOptions = { 0 };
421         unsigned nbBits = (FUZ_rand(&randState) % (FUZ_highbit(srcDataLength-1) - 1)) + 1;
422         size_t srcSize = (FUZ_rand(&randState) & ((1<<nbBits)-1)) + 1;
423         size_t srcStart = FUZ_rand(&randState) % (srcDataLength - srcSize);
424         size_t cSize;
425         U64 crcOrig, crcDecoded;
426         LZ4F_preferences_t* prefsPtr = &prefs;
427 
428         (void)FUZ_rand(&coreRand);   // update rand seed
429         prefs.frameInfo.blockMode = (blockMode_t)BMId;
430         prefs.frameInfo.blockSizeID = (blockSizeID_t)BSId;
431         prefs.frameInfo.contentChecksumFlag = (contentChecksum_t)CCflag;
432         prefs.autoFlush = autoflush;
433         prefs.compressionLevel = FUZ_rand(&randState) % 5;
434         if ((FUZ_rand(&randState)&0xF) == 1) prefsPtr = NULL;
435 
436         DISPLAYUPDATE(2, "\r%5u   ", testNb);
437         crcOrig = XXH64((BYTE*)srcBuffer+srcStart, (U32)srcSize, 1);
438 
439         if ((FUZ_rand(&randState)&0xF) == 2)
440         {
441             cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(srcSize, prefsPtr), (char*)srcBuffer + srcStart, srcSize, prefsPtr);
442             CHECK(LZ4F_isError(cSize), "LZ4F_compressFrame failed : error %i (%s)", (int)cSize, LZ4F_getErrorName(cSize));
443         }
444         else
445         {
446             const BYTE* ip = (const BYTE*)srcBuffer + srcStart;
447             const BYTE* const iend = ip + srcSize;
448             BYTE* op = (BYTE*)compressedBuffer;
449             BYTE* const oend = op + LZ4F_compressFrameBound(srcDataLength, NULL);
450             unsigned maxBits = FUZ_highbit((U32)srcSize);
451             result = LZ4F_compressBegin(cCtx, op, oend-op, prefsPtr);
452             CHECK(LZ4F_isError(result), "Compression header failed (error %i)", (int)result);
453             op += result;
454             while (ip < iend)
455             {
456                 unsigned nbBitsSeg = FUZ_rand(&randState) % maxBits;
457                 size_t iSize = (FUZ_rand(&randState) & ((1<<nbBitsSeg)-1)) + 1;
458                 size_t oSize = LZ4F_compressBound(iSize, prefsPtr);
459                 unsigned forceFlush = ((FUZ_rand(&randState) & 3) == 1);
460                 if (iSize > (size_t)(iend-ip)) iSize = iend-ip;
461                 cOptions.stableSrc = ((FUZ_rand(&randState) & 3) == 1);
462 
463                 result = LZ4F_compressUpdate(cCtx, op, oSize, ip, iSize, &cOptions);
464                 CHECK(LZ4F_isError(result), "Compression failed (error %i)", (int)result);
465                 op += result;
466                 ip += iSize;
467 
468                 if (forceFlush)
469                 {
470                     result = LZ4F_flush(cCtx, op, oend-op, &cOptions);
471                     CHECK(LZ4F_isError(result), "Compression failed (error %i)", (int)result);
472                     op += result;
473                 }
474             }
475             result = LZ4F_compressEnd(cCtx, op, oend-op, &cOptions);
476             CHECK(LZ4F_isError(result), "Compression completion failed (error %i)", (int)result);
477             op += result;
478             cSize = op-(BYTE*)compressedBuffer;
479         }
480 
481         {
482             const BYTE* ip = (const BYTE*)compressedBuffer;
483             const BYTE* const iend = ip + cSize;
484             BYTE* op = (BYTE*)decodedBuffer;
485             BYTE* const oend = op + srcDataLength;
486             unsigned maxBits = FUZ_highbit((U32)cSize);
487             unsigned nonContiguousDst = (FUZ_rand(&randState) & 3) == 1;
488             nonContiguousDst += FUZ_rand(&randState) & nonContiguousDst;   /* 0=>0; 1=>1,2 */
489             XXH64_reset(&xxh64, 1);
490             while (ip < iend)
491             {
492                 unsigned nbBitsI = (FUZ_rand(&randState) % (maxBits-1)) + 1;
493                 unsigned nbBitsO = (FUZ_rand(&randState) % (maxBits)) + 1;
494                 size_t iSize = (FUZ_rand(&randState) & ((1<<nbBitsI)-1)) + 1;
495                 size_t oSize = (FUZ_rand(&randState) & ((1<<nbBitsO)-1)) + 2;
496                 if (iSize > (size_t)(iend-ip)) iSize = iend-ip;
497                 if (oSize > (size_t)(oend-op)) oSize = oend-op;
498                 dOptions.stableDst = FUZ_rand(&randState) & 1;
499                 if (nonContiguousDst==2) dOptions.stableDst = 0;
500                 //if (ip == compressedBuffer+62073)                    DISPLAY("oSize : %i : pos %i \n", (int)oSize, (int)(op-(BYTE*)decodedBuffer));
501                 result = LZ4F_decompress(dCtx, op, &oSize, ip, &iSize, &dOptions);
502                 //if (op+oSize >= (BYTE*)decodedBuffer+94727)                    DISPLAY("iSize : %i : pos %i \n", (int)iSize, (int)(ip-(BYTE*)compressedBuffer));
503                 //if ((int)result<0)                    DISPLAY("iSize : %i : pos %i \n", (int)iSize, (int)(ip-(BYTE*)compressedBuffer));
504                 if (result == (size_t)-ERROR_checksum_invalid) locateBuffDiff((BYTE*)srcBuffer+srcStart, decodedBuffer, srcSize, nonContiguousDst);
505                 CHECK(LZ4F_isError(result), "Decompression failed (error %i:%s)", (int)result, LZ4F_getErrorName((LZ4F_errorCode_t)result));
506                 XXH64_update(&xxh64, op, (U32)oSize);
507                 op += oSize;
508                 ip += iSize;
509                 op += nonContiguousDst;
510                 if (nonContiguousDst==2) op = decodedBuffer;   // overwritten destination
511             }
512             CHECK(result != 0, "Frame decompression failed (error %i)", (int)result);
513             crcDecoded = XXH64_digest(&xxh64);
514             if (crcDecoded != crcOrig) locateBuffDiff((BYTE*)srcBuffer+srcStart, decodedBuffer, srcSize, nonContiguousDst);
515             CHECK(crcDecoded != crcOrig, "Decompression corruption");
516         }
517     }
518 
519     DISPLAYLEVEL(2, "\rAll tests completed   \n");
520 
521 _end:
522     LZ4F_freeDecompressionContext(dCtx);
523     LZ4F_freeCompressionContext(cCtx);
524     free(srcBuffer);
525     free(compressedBuffer);
526     free(decodedBuffer);
527 
528     if (pause)
529     {
530         DISPLAY("press enter to finish \n");
531         getchar();
532     }
533     return testResult;
534 
535 _output_error:
536     testResult = 1;
537     goto _end;
538 }
539 
540 
FUZ_usage(void)541 int FUZ_usage(void)
542 {
543     DISPLAY( "Usage :\n");
544     DISPLAY( "      %s [args]\n", programName);
545     DISPLAY( "\n");
546     DISPLAY( "Arguments :\n");
547     DISPLAY( " -i#    : Nb of tests (default:%u) \n", nbTestsDefault);
548     DISPLAY( " -s#    : Select seed (default:prompt user)\n");
549     DISPLAY( " -t#    : Select starting test number (default:0)\n");
550     DISPLAY( " -p#    : Select compressibility in %% (default:%i%%)\n", FUZ_COMPRESSIBILITY_DEFAULT);
551     DISPLAY( " -v     : verbose\n");
552     DISPLAY( " -h     : display help and exit\n");
553     return 0;
554 }
555 
556 
main(int argc,char ** argv)557 int main(int argc, char** argv)
558 {
559     U32 seed=0;
560     int seedset=0;
561     int argNb;
562     int nbTests = nbTestsDefault;
563     int testNb = 0;
564     int proba = FUZ_COMPRESSIBILITY_DEFAULT;
565     int result=0;
566 
567     // Check command line
568     programName = argv[0];
569     for(argNb=1; argNb<argc; argNb++)
570     {
571         char* argument = argv[argNb];
572 
573         if(!argument) continue;   // Protection if argument empty
574 
575         // Decode command (note : aggregated commands are allowed)
576         if (argument[0]=='-')
577         {
578             if (!strcmp(argument, "--no-prompt"))
579             {
580                 no_prompt=1;
581                 seedset=1;
582                 displayLevel=1;
583                 continue;
584             }
585             argument++;
586 
587             while (*argument!=0)
588             {
589                 switch(*argument)
590                 {
591                 case 'h':
592                     return FUZ_usage();
593                 case 'v':
594                     argument++;
595                     displayLevel=4;
596                     break;
597                 case 'q':
598                     argument++;
599                     displayLevel--;
600                     break;
601                 case 'p': /* pause at the end */
602                     argument++;
603                     pause = 1;
604                     break;
605 
606                 case 'i':
607                     argument++;
608                     nbTests=0;
609                     while ((*argument>='0') && (*argument<='9'))
610                     {
611                         nbTests *= 10;
612                         nbTests += *argument - '0';
613                         argument++;
614                     }
615                     break;
616                 case 's':
617                     argument++;
618                     seed=0;
619                     seedset=1;
620                     while ((*argument>='0') && (*argument<='9'))
621                     {
622                         seed *= 10;
623                         seed += *argument - '0';
624                         argument++;
625                     }
626                     break;
627                 case 't':
628                     argument++;
629                     testNb=0;
630                     while ((*argument>='0') && (*argument<='9'))
631                     {
632                         testNb *= 10;
633                         testNb += *argument - '0';
634                         argument++;
635                     }
636                     break;
637                 case 'P':   /* compressibility % */
638                     argument++;
639                     proba=0;
640                     while ((*argument>='0') && (*argument<='9'))
641                     {
642                         proba *= 10;
643                         proba += *argument - '0';
644                         argument++;
645                     }
646                     if (proba<0) proba=0;
647                     if (proba>100) proba=100;
648                     break;
649                 default:
650                     ;
651                     return FUZ_usage();
652                 }
653             }
654         }
655     }
656 
657     // Get Seed
658     printf("Starting lz4frame tester (%i-bits, %s)\n", (int)(sizeof(size_t)*8), LZ4_VERSION);
659 
660     if (!seedset) seed = FUZ_GetMilliStart() % 10000;
661     printf("Seed = %u\n", seed);
662     if (proba!=FUZ_COMPRESSIBILITY_DEFAULT) printf("Compressibility : %i%%\n", proba);
663 
664     if (nbTests<=0) nbTests=1;
665 
666     if (testNb==0) result = basicTests(seed, ((double)proba) / 100);
667     if (result) return 1;
668     return fuzzerTests(seed, nbTests, testNb, ((double)proba) / 100);
669 }
670