1 /*
2     frameTest - test tool for lizard_frame
3     Copyright (C) Yann Collet 2014-2016
4     Copyright (C) Przemyslaw Skibinski 2016-2017
5 
6     GPL v2 License
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License along
19     with this program; if not, write to the Free Software Foundation, Inc.,
20     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 
22     You can contact the author at :
23     - Lizard source repository : https://github.com/inikep/lizard
24 */
25 
26 /*-************************************
27 *  Compiler specific
28 **************************************/
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 
34 
35 /*-************************************
36 *  Includes
37 **************************************/
38 #include <stdlib.h>     /* malloc, free */
39 #include <stdio.h>      /* fprintf */
40 #include <string.h>     /* strcmp */
41 #include <time.h>       /* clock_t, clock(), CLOCKS_PER_SEC */
42 #include "lizard_frame_static.h"
43 #include "lizard_compress.h"        /* LIZARD_VERSION_STRING */
44 #define XXH_STATIC_LINKING_ONLY
45 #include "xxhash/xxhash.h"     /* XXH64 */
46 
47 
48 /*-************************************
49 *  Basic Types
50 **************************************/
51 #if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)   /* C99 */
52 # include <stdint.h>
53 typedef  uint8_t BYTE;
54 typedef uint16_t U16;
55 typedef uint32_t U32;
56 typedef  int32_t S32;
57 typedef uint64_t U64;
58 #else
59 typedef unsigned char       BYTE;
60 typedef unsigned short      U16;
61 typedef unsigned int        U32;
62 typedef   signed int        S32;
63 typedef unsigned long long  U64;
64 #endif
65 
66 
67 /* unoptimized version; solves endianess & alignment issues */
FUZ_writeLE32(void * dstVoidPtr,U32 value32)68 static void FUZ_writeLE32 (void* dstVoidPtr, U32 value32)
69 {
70     BYTE* dstPtr = (BYTE*)dstVoidPtr;
71     dstPtr[0] = (BYTE)value32;
72     dstPtr[1] = (BYTE)(value32 >> 8);
73     dstPtr[2] = (BYTE)(value32 >> 16);
74     dstPtr[3] = (BYTE)(value32 >> 24);
75 }
76 
77 
78 /*-************************************
79 *  Constants
80 **************************************/
81 #define LIZARDF_MAGIC_SKIPPABLE_START 0x184D2A50U
82 
83 #define KB *(1U<<10)
84 #define MB *(1U<<20)
85 #define GB *(1U<<30)
86 
87 static const U32 nbTestsDefault = 256 KB;
88 #define COMPRESSIBLE_NOISE_LENGTH (2 MB)
89 #define FUZ_COMPRESSIBILITY_DEFAULT 50
90 static const U32 prime1 = 2654435761U;
91 static const U32 prime2 = 2246822519U;
92 
93 
94 
95 /*-************************************
96 *  Macros
97 **************************************/
98 #define DISPLAY(...)          fprintf(stderr, __VA_ARGS__)
99 #define DISPLAYLEVEL(l, ...)  if (displayLevel>=l) { DISPLAY(__VA_ARGS__); }
100 #define DISPLAYUPDATE(l, ...) if (displayLevel>=l) { \
101             if ((FUZ_GetClockSpan(g_clockTime) > refreshRate) || (displayLevel>=4)) \
102             { g_clockTime = clock(); DISPLAY(__VA_ARGS__); \
103             if (displayLevel>=4) fflush(stdout); } }
104 static const clock_t refreshRate = CLOCKS_PER_SEC / 6;
105 static clock_t g_clockTime = 0;
106 
107 
108 /*-***************************************
109 *  Local Parameters
110 *****************************************/
111 static U32 no_prompt = 0;
112 static char* programName;
113 static U32 displayLevel = 2;
114 static U32 pause = 0;
115 
116 
117 /*-*******************************************************
118 *  Fuzzer functions
119 *********************************************************/
FUZ_GetClockSpan(clock_t clockStart)120 static clock_t FUZ_GetClockSpan(clock_t clockStart)
121 {
122     return clock() - clockStart;   /* works even if overflow; max span ~ 30 mn */
123 }
124 
125 
126 #define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r)))
FUZ_rand(unsigned int * src)127 unsigned int FUZ_rand(unsigned int* src)
128 {
129     U32 rand32 = *src;
130     rand32 *= prime1;
131     rand32 += prime2;
132     rand32  = FUZ_rotl32(rand32, 13);
133     *src = rand32;
134     return rand32 >> 5;
135 }
136 
137 
138 #define FUZ_RAND15BITS  (FUZ_rand(seed) & 0x7FFF)
139 #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)140 static void FUZ_fillCompressibleNoiseBuffer(void* buffer, unsigned bufferSize, double proba, U32* seed)
141 {
142     BYTE* BBuffer = (BYTE*)buffer;
143     unsigned pos = 0;
144     U32 P32 = (U32)(32768 * proba);
145 
146     /* First Byte */
147     BBuffer[pos++] = (BYTE)(FUZ_rand(seed));
148 
149     while (pos < bufferSize) {
150         /* Select : Literal (noise) or copy (within 64K) */
151         if (FUZ_RAND15BITS < P32) {
152             /* Copy (within 64K) */
153             unsigned match, end;
154             unsigned length = FUZ_RANDLENGTH + 4;
155             unsigned offset = FUZ_RAND15BITS + 1;
156             if (offset > pos) offset = pos;
157             if (pos + length > bufferSize) length = bufferSize - pos;
158             match = pos - offset;
159             end = pos + length;
160             while (pos < end) BBuffer[pos++] = BBuffer[match++];
161         } else {
162             /* Literal (noise) */
163             unsigned end;
164             unsigned length = FUZ_RANDLENGTH;
165             if (pos + length > bufferSize) length = bufferSize - pos;
166             end = pos + length;
167             while (pos < end) BBuffer[pos++] = (BYTE)(FUZ_rand(seed) >> 5);
168         }
169     }
170 }
171 
172 
FUZ_highbit(U32 v32)173 static unsigned FUZ_highbit(U32 v32)
174 {
175     unsigned nbBits = 0;
176     if (v32==0) return 0;
177     while (v32) v32 >>= 1, nbBits ++;
178     return nbBits;
179 }
180 
181 
basicTests(U32 seed,double compressibility)182 int basicTests(U32 seed, double compressibility)
183 {
184     int testResult = 0;
185     void* CNBuffer;
186     void* compressedBuffer;
187     void* decodedBuffer;
188     U32 randState = seed;
189     size_t cSize, testSize;
190     LizardF_preferences_t prefs;
191     LizardF_decompressionContext_t dCtx = NULL;
192     LizardF_compressionContext_t cctx = NULL;
193     U64 crcOrig;
194 
195     /* Create compressible test buffer */
196     memset(&prefs, 0, sizeof(prefs));
197     CNBuffer = malloc(COMPRESSIBLE_NOISE_LENGTH);
198     compressedBuffer = malloc(LizardF_compressFrameBound(COMPRESSIBLE_NOISE_LENGTH, NULL));
199     decodedBuffer = malloc(COMPRESSIBLE_NOISE_LENGTH);
200     FUZ_fillCompressibleNoiseBuffer(CNBuffer, COMPRESSIBLE_NOISE_LENGTH, compressibility, &randState);
201     crcOrig = XXH64(CNBuffer, COMPRESSIBLE_NOISE_LENGTH, 1);
202 
203     /* Special case : null-content frame */
204     testSize = 0;
205     DISPLAYLEVEL(3, "LizardF_compressFrame, compress null content : \n");
206     cSize = LizardF_compressFrame(compressedBuffer, LizardF_compressFrameBound(testSize, NULL), CNBuffer, testSize, NULL);
207     if (LizardF_isError(cSize)) goto _output_error;
208     DISPLAYLEVEL(3, "Compressed null content into a %i bytes frame \n", (int)cSize);
209 
210     DISPLAYLEVEL(3, "LizardF_createDecompressionContext \n");
211     { LizardF_errorCode_t const errorCode = LizardF_createDecompressionContext(&dCtx, LIZARDF_VERSION);
212       if (LizardF_isError(errorCode)) goto _output_error; }
213 
214     DISPLAYLEVEL(3, "LizardF_getFrameInfo on null-content frame (#157) \n");
215     {   size_t avail_in = cSize;
216         LizardF_frameInfo_t frame_info;
217         LizardF_errorCode_t const errorCode = LizardF_getFrameInfo(dCtx, &frame_info, compressedBuffer, &avail_in);
218         if (LizardF_isError(errorCode)) goto _output_error;
219     }
220 
221     DISPLAYLEVEL(3, "LizardF_freeDecompressionContext \n");
222     { LizardF_errorCode_t const errorCode = LizardF_freeDecompressionContext(dCtx);
223       if (LizardF_isError(errorCode)) goto _output_error; }
224 
225     /* Trivial tests : one-step frame */
226     testSize = COMPRESSIBLE_NOISE_LENGTH;
227     DISPLAYLEVEL(3, "LizardF_compressFrame, using default preferences : \n");
228     cSize = LizardF_compressFrame(compressedBuffer, LizardF_compressFrameBound(testSize, NULL), CNBuffer, testSize, NULL);
229     if (LizardF_isError(cSize)) goto _output_error;
230     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
231 
232     DISPLAYLEVEL(3, "Decompression test : \n");
233     {   size_t decodedBufferSize = COMPRESSIBLE_NOISE_LENGTH;
234         size_t compressedBufferSize = cSize;
235         BYTE* op = (BYTE*)decodedBuffer;
236         BYTE* const oend = (BYTE*)decodedBuffer + COMPRESSIBLE_NOISE_LENGTH;
237         BYTE* ip = (BYTE*)compressedBuffer;
238         BYTE* const iend = (BYTE*)compressedBuffer + cSize;
239         U64 crcDest;
240 
241         LizardF_errorCode_t errorCode = LizardF_createDecompressionContext(&dCtx, LIZARDF_VERSION);
242         if (LizardF_isError(errorCode)) goto _output_error;
243 
244         DISPLAYLEVEL(3, "Single Block : \n");
245         errorCode = LizardF_decompress(dCtx, decodedBuffer, &decodedBufferSize, compressedBuffer, &compressedBufferSize, NULL);
246         if (LizardF_isError(errorCode)) goto _output_error;
247         crcDest = XXH64(decodedBuffer, COMPRESSIBLE_NOISE_LENGTH, 1);
248         if (crcDest != crcOrig) goto _output_error;
249         DISPLAYLEVEL(3, "Regenerated %i bytes \n", (int)decodedBufferSize);
250 
251         DISPLAYLEVEL(3, "Reusing decompression context \n");
252         {   size_t iSize = compressedBufferSize - 4;
253             const BYTE* cBuff = (const BYTE*) compressedBuffer;
254             size_t decResult;
255             DISPLAYLEVEL(3, "Missing last 4 bytes : ");
256             decResult = LizardF_decompress(dCtx, decodedBuffer, &decodedBufferSize, cBuff, &iSize, NULL);
257             if (LizardF_isError(decResult)) goto _output_error;
258             if (!decResult) goto _output_error;   /* not finished */
259             DISPLAYLEVEL(3, "indeed, request %u bytes \n", (unsigned)decResult);
260             cBuff += iSize;
261             iSize = decResult;
262             decResult = LizardF_decompress(dCtx, decodedBuffer, &decodedBufferSize, cBuff, &iSize, NULL);
263             if (decResult != 0) goto _output_error;   /* should finish now */
264             crcDest = XXH64(decodedBuffer, COMPRESSIBLE_NOISE_LENGTH, 1);
265             if (crcDest != crcOrig) goto _output_error;
266         }
267 
268         {   size_t oSize = 0;
269             size_t iSize = 0;
270             LizardF_frameInfo_t fi;
271 
272             DISPLAYLEVEL(3, "Start by feeding 0 bytes, to get next input size : ");
273             errorCode = LizardF_decompress(dCtx, NULL, &oSize, ip, &iSize, NULL);
274             if (LizardF_isError(errorCode)) goto _output_error;
275             DISPLAYLEVEL(3, " %u  \n", (unsigned)errorCode);
276 
277             DISPLAYLEVEL(3, "get FrameInfo on null input : ");
278             errorCode = LizardF_getFrameInfo(dCtx, &fi, ip, &iSize);
279             if (errorCode != (size_t)-LizardF_ERROR_frameHeader_incomplete) goto _output_error;
280             DISPLAYLEVEL(3, " correctly failed : %s \n", LizardF_getErrorName(errorCode));
281 
282             DISPLAYLEVEL(3, "get FrameInfo on not enough input : ");
283             iSize = 6;
284             errorCode = LizardF_getFrameInfo(dCtx, &fi, ip, &iSize);
285             if (errorCode != (size_t)-LizardF_ERROR_frameHeader_incomplete) goto _output_error;
286             DISPLAYLEVEL(3, " correctly failed : %s \n", LizardF_getErrorName(errorCode));
287             ip += iSize;
288 
289             DISPLAYLEVEL(3, "get FrameInfo on enough input : ");
290             iSize = 15 - iSize;
291             errorCode = LizardF_getFrameInfo(dCtx, &fi, ip, &iSize);
292             if (LizardF_isError(errorCode)) goto _output_error;
293             DISPLAYLEVEL(3, " correctly decoded \n");
294             ip += iSize;
295         }
296 
297         DISPLAYLEVEL(3, "Byte after byte : \n");
298         while (ip < iend) {
299             size_t oSize = oend-op;
300             size_t iSize = 1;
301             errorCode = LizardF_decompress(dCtx, op, &oSize, ip, &iSize, NULL);
302             if (LizardF_isError(errorCode)) goto _output_error;
303             op += oSize;
304             ip += iSize;
305         }
306         crcDest = XXH64(decodedBuffer, COMPRESSIBLE_NOISE_LENGTH, 1);
307         if (crcDest != crcOrig) goto _output_error;
308         DISPLAYLEVEL(3, "Regenerated %u/%u bytes \n", (unsigned)(op-(BYTE*)decodedBuffer), COMPRESSIBLE_NOISE_LENGTH);
309 
310         errorCode = LizardF_freeDecompressionContext(dCtx);
311         if (LizardF_isError(errorCode)) goto _output_error;
312     }
313 
314     DISPLAYLEVEL(3, "Using 128 KB block : \n");
315     prefs.frameInfo.blockSizeID = LizardF_max128KB;
316     prefs.frameInfo.contentChecksumFlag = LizardF_contentChecksumEnabled;
317     cSize = LizardF_compressFrame(compressedBuffer, LizardF_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
318     if (LizardF_isError(cSize)) goto _output_error;
319     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
320 
321     DISPLAYLEVEL(3, "without checksum : \n");
322     prefs.frameInfo.contentChecksumFlag = LizardF_noContentChecksum;
323     cSize = LizardF_compressFrame(compressedBuffer, LizardF_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
324     if (LizardF_isError(cSize)) goto _output_error;
325     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
326 
327     DISPLAYLEVEL(3, "Using 256 KB block : \n");
328     prefs.frameInfo.blockSizeID = LizardF_max256KB;
329     prefs.frameInfo.contentChecksumFlag = LizardF_contentChecksumEnabled;
330     cSize = LizardF_compressFrame(compressedBuffer, LizardF_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
331     if (LizardF_isError(cSize)) goto _output_error;
332     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
333 
334     DISPLAYLEVEL(3, "Decompression test : \n");
335     {   size_t decodedBufferSize = COMPRESSIBLE_NOISE_LENGTH;
336         unsigned maxBits = FUZ_highbit((U32)decodedBufferSize);
337         BYTE* op = (BYTE*)decodedBuffer;
338         BYTE* const oend = (BYTE*)decodedBuffer + COMPRESSIBLE_NOISE_LENGTH;
339         BYTE* ip = (BYTE*)compressedBuffer;
340         BYTE* const iend = (BYTE*)compressedBuffer + cSize;
341         U64 crcDest;
342 
343         LizardF_errorCode_t errorCode = LizardF_createDecompressionContext(&dCtx, LIZARDF_VERSION);
344         if (LizardF_isError(errorCode)) goto _output_error;
345 
346         DISPLAYLEVEL(3, "random segment sizes : \n");
347         while (ip < iend) {
348             unsigned nbBits = FUZ_rand(&randState) % maxBits;
349             size_t iSize = (FUZ_rand(&randState) & ((1<<nbBits)-1)) + 1;
350             size_t oSize = oend-op;
351             if (iSize > (size_t)(iend-ip)) iSize = iend-ip;
352             //DISPLAY("%7i : + %6i\n", (int)(ip-(BYTE*)compressedBuffer), (int)iSize);
353             errorCode = LizardF_decompress(dCtx, op, &oSize, ip, &iSize, NULL);
354             if (LizardF_isError(errorCode)) goto _output_error;
355             op += oSize;
356             ip += iSize;
357         }
358         crcDest = XXH64(decodedBuffer, COMPRESSIBLE_NOISE_LENGTH, 1);
359         if (crcDest != crcOrig) goto _output_error;
360         DISPLAYLEVEL(3, "Regenerated %i bytes \n", (int)decodedBufferSize);
361 
362         errorCode = LizardF_freeDecompressionContext(dCtx);
363         if (LizardF_isError(errorCode)) goto _output_error;
364     }
365 
366     DISPLAYLEVEL(3, "without checksum : \n");
367     prefs.frameInfo.contentChecksumFlag = LizardF_noContentChecksum;
368     cSize = LizardF_compressFrame(compressedBuffer, LizardF_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
369     if (LizardF_isError(cSize)) goto _output_error;
370     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
371 
372     DISPLAYLEVEL(3, "Using 1 MB block : \n");
373     prefs.frameInfo.blockSizeID = LizardF_max1MB;
374     prefs.frameInfo.contentChecksumFlag = LizardF_contentChecksumEnabled;
375     cSize = LizardF_compressFrame(compressedBuffer, LizardF_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
376     if (LizardF_isError(cSize)) goto _output_error;
377     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
378 
379     DISPLAYLEVEL(3, "without checksum : \n");
380     prefs.frameInfo.contentChecksumFlag = LizardF_noContentChecksum;
381     cSize = LizardF_compressFrame(compressedBuffer, LizardF_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
382     if (LizardF_isError(cSize)) goto _output_error;
383     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
384 
385     DISPLAYLEVEL(3, "Using 4 MB block : \n");
386     prefs.frameInfo.blockSizeID = LizardF_max4MB;
387     prefs.frameInfo.contentChecksumFlag = LizardF_contentChecksumEnabled;
388     cSize = LizardF_compressFrame(compressedBuffer, LizardF_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
389     if (LizardF_isError(cSize)) goto _output_error;
390     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
391 
392     DISPLAYLEVEL(3, "without checksum : \n");
393     prefs.frameInfo.contentChecksumFlag = LizardF_noContentChecksum;
394     cSize = LizardF_compressFrame(compressedBuffer, LizardF_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs);
395     if (LizardF_isError(cSize)) goto _output_error;
396     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
397 
398     {   size_t errorCode;
399         BYTE* const ostart = (BYTE*)compressedBuffer;
400         BYTE* op = ostart;
401         errorCode = LizardF_createCompressionContext(&cctx, LIZARDF_VERSION);
402         if (LizardF_isError(errorCode)) goto _output_error;
403 
404         DISPLAYLEVEL(3, "compress without frameSize : \n");
405         memset(&(prefs.frameInfo), 0, sizeof(prefs.frameInfo));
406         errorCode = LizardF_compressBegin(cctx, compressedBuffer, testSize, &prefs);
407         if (LizardF_isError(errorCode)) goto _output_error;
408         op += errorCode;
409         errorCode = LizardF_compressUpdate(cctx, op, LizardF_compressBound(testSize, &prefs), CNBuffer, testSize, NULL);
410         if (LizardF_isError(errorCode)) goto _output_error;
411         op += errorCode;
412         errorCode = LizardF_compressEnd(cctx, compressedBuffer, testSize, NULL);
413         if (LizardF_isError(errorCode)) goto _output_error;
414         DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)(op-ostart));
415 
416         DISPLAYLEVEL(3, "compress with frameSize : \n");
417         prefs.frameInfo.contentSize = testSize;
418         op = ostart;
419         errorCode = LizardF_compressBegin(cctx, compressedBuffer, testSize, &prefs);
420         if (LizardF_isError(errorCode)) goto _output_error;
421         op += errorCode;
422         errorCode = LizardF_compressUpdate(cctx, op, LizardF_compressBound(testSize, &prefs), CNBuffer, testSize, NULL);
423         if (LizardF_isError(errorCode)) goto _output_error;
424         op += errorCode;
425         errorCode = LizardF_compressEnd(cctx, compressedBuffer, testSize, NULL);
426         if (LizardF_isError(errorCode)) goto _output_error;
427         DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)(op-ostart));
428 
429         DISPLAYLEVEL(3, "compress with wrong frameSize : \n");
430         prefs.frameInfo.contentSize = testSize+1;
431         op = ostart;
432         errorCode = LizardF_compressBegin(cctx, compressedBuffer, testSize, &prefs);
433         if (LizardF_isError(errorCode)) goto _output_error;
434         op += errorCode;
435         errorCode = LizardF_compressUpdate(cctx, op, LizardF_compressBound(testSize, &prefs), CNBuffer, testSize, NULL);
436         if (LizardF_isError(errorCode)) goto _output_error;
437         op += errorCode;
438         errorCode = LizardF_compressEnd(cctx, op, testSize, NULL);
439         if (LizardF_isError(errorCode)) { DISPLAYLEVEL(3, "Error correctly detected : %s \n", LizardF_getErrorName(errorCode)); }
440         else
441             goto _output_error;
442 
443         errorCode = LizardF_freeCompressionContext(cctx);
444         if (LizardF_isError(errorCode)) goto _output_error;
445         cctx = NULL;
446     }
447 
448     DISPLAYLEVEL(3, "Skippable frame test : \n");
449     {   size_t decodedBufferSize = COMPRESSIBLE_NOISE_LENGTH;
450         unsigned maxBits = FUZ_highbit((U32)decodedBufferSize);
451         BYTE* op = (BYTE*)decodedBuffer;
452         BYTE* const oend = (BYTE*)decodedBuffer + COMPRESSIBLE_NOISE_LENGTH;
453         BYTE* ip = (BYTE*)compressedBuffer;
454         BYTE* iend = (BYTE*)compressedBuffer + cSize + 8;
455 
456         LizardF_errorCode_t errorCode = LizardF_createDecompressionContext(&dCtx, LIZARDF_VERSION);
457         if (LizardF_isError(errorCode)) goto _output_error;
458 
459         /* generate skippable frame */
460         FUZ_writeLE32(ip, LIZARDF_MAGIC_SKIPPABLE_START);
461         FUZ_writeLE32(ip+4, (U32)cSize);
462 
463         DISPLAYLEVEL(3, "random segment sizes : \n");
464         while (ip < iend) {
465             unsigned nbBits = FUZ_rand(&randState) % maxBits;
466             size_t iSize = (FUZ_rand(&randState) & ((1<<nbBits)-1)) + 1;
467             size_t oSize = oend-op;
468             if (iSize > (size_t)(iend-ip)) iSize = iend-ip;
469             errorCode = LizardF_decompress(dCtx, op, &oSize, ip, &iSize, NULL);
470             if (LizardF_isError(errorCode)) goto _output_error;
471             op += oSize;
472             ip += iSize;
473         }
474         DISPLAYLEVEL(3, "Skipped %i bytes \n", (int)decodedBufferSize);
475 
476         /* generate zero-size skippable frame */
477         DISPLAYLEVEL(3, "zero-size skippable frame\n");
478         ip = (BYTE*)compressedBuffer;
479         op = (BYTE*)decodedBuffer;
480         FUZ_writeLE32(ip, LIZARDF_MAGIC_SKIPPABLE_START+1);
481         FUZ_writeLE32(ip+4, 0);
482         iend = ip+8;
483 
484         while (ip < iend) {
485             unsigned nbBits = FUZ_rand(&randState) % maxBits;
486             size_t iSize = (FUZ_rand(&randState) & ((1<<nbBits)-1)) + 1;
487             size_t oSize = oend-op;
488             if (iSize > (size_t)(iend-ip)) iSize = iend-ip;
489             errorCode = LizardF_decompress(dCtx, op, &oSize, ip, &iSize, NULL);
490             if (LizardF_isError(errorCode)) goto _output_error;
491             op += oSize;
492             ip += iSize;
493         }
494         DISPLAYLEVEL(3, "Skipped %i bytes \n", (int)(ip - (BYTE*)compressedBuffer - 8));
495 
496         DISPLAYLEVEL(3, "Skippable frame header complete in first call \n");
497         ip = (BYTE*)compressedBuffer;
498         op = (BYTE*)decodedBuffer;
499         FUZ_writeLE32(ip, LIZARDF_MAGIC_SKIPPABLE_START+2);
500         FUZ_writeLE32(ip+4, 10);
501         iend = ip+18;
502         while (ip < iend) {
503             size_t iSize = 10;
504             size_t oSize = 10;
505             if (iSize > (size_t)(iend-ip)) iSize = iend-ip;
506             errorCode = LizardF_decompress(dCtx, op, &oSize, ip, &iSize, NULL);
507             if (LizardF_isError(errorCode)) goto _output_error;
508             op += oSize;
509             ip += iSize;
510         }
511         DISPLAYLEVEL(3, "Skipped %i bytes \n", (int)(ip - (BYTE*)compressedBuffer - 8));
512     }
513 
514     DISPLAY("Basic tests completed \n");
515 _end:
516     free(CNBuffer);
517     free(compressedBuffer);
518     free(decodedBuffer);
519     LizardF_freeDecompressionContext(dCtx); dCtx = NULL;
520     LizardF_freeCompressionContext(cctx); cctx = NULL;
521     return testResult;
522 
523 _output_error:
524     testResult = 1;
525     DISPLAY("Error detected ! \n");
526     goto _end;
527 }
528 
529 
locateBuffDiff(const void * buff1,const void * buff2,size_t size,unsigned nonContiguous)530 static void locateBuffDiff(const void* buff1, const void* buff2, size_t size, unsigned nonContiguous)
531 {
532     int p=0;
533     const BYTE* b1=(const BYTE*)buff1;
534     const BYTE* b2=(const BYTE*)buff2;
535     if (nonContiguous) {
536         DISPLAY("Non-contiguous output test (%i bytes)\n", (int)size);
537         return;
538     }
539     while (b1[p]==b2[p]) p++;
540     DISPLAY("Error at pos %i/%i : %02X != %02X \n", p, (int)size, b1[p], b2[p]);
541 }
542 
543 
544 static const U32 srcDataLength = 9 MB;  /* needs to be > 2x4MB to test large blocks */
545 
fuzzerTests(U32 seed,unsigned nbTests,unsigned startTest,double compressibility,U32 duration_s)546 int fuzzerTests(U32 seed, unsigned nbTests, unsigned startTest, double compressibility, U32 duration_s)
547 {
548     unsigned testResult = 0;
549     unsigned testNb = 0;
550     void* srcBuffer = NULL;
551     void* compressedBuffer = NULL;
552     void* decodedBuffer = NULL;
553     U32 coreRand = seed;
554     LizardF_decompressionContext_t dCtx = NULL;
555     LizardF_compressionContext_t cCtx = NULL;
556     size_t result;
557     clock_t const startClock = clock();
558     clock_t const clockDuration = duration_s * CLOCKS_PER_SEC;
559     XXH64_state_t xxh64;
560 #   define CHECK(cond, ...) if (cond) { DISPLAY("Error => "); DISPLAY(__VA_ARGS__); \
561                             DISPLAY(" (seed %u, test nb %u)  \n", seed, testNb); goto _output_error; }
562 
563     /* Create buffers */
564     result = LizardF_createDecompressionContext(&dCtx, LIZARDF_VERSION);
565     CHECK(LizardF_isError(result), "Allocation failed (error %i)", (int)result);
566     result = LizardF_createCompressionContext(&cCtx, LIZARDF_VERSION);
567     CHECK(LizardF_isError(result), "Allocation failed (error %i)", (int)result);
568     srcBuffer = malloc(srcDataLength);
569     CHECK(srcBuffer==NULL, "srcBuffer Allocation failed");
570     compressedBuffer = malloc(LizardF_compressFrameBound(srcDataLength, NULL));
571     CHECK(compressedBuffer==NULL, "compressedBuffer Allocation failed");
572     decodedBuffer = calloc(1, srcDataLength);   /* calloc avoids decodedBuffer being considered "garbage" by scan-build */
573     CHECK(decodedBuffer==NULL, "decodedBuffer Allocation failed");
574     FUZ_fillCompressibleNoiseBuffer(srcBuffer, srcDataLength, compressibility, &coreRand);
575 
576     /* jump to requested testNb */
577     for (testNb =0; (testNb < startTest); testNb++) (void)FUZ_rand(&coreRand);   // sync randomizer
578 
579     /* main fuzzer test loop */
580     for ( ; (testNb < nbTests) || (clockDuration > FUZ_GetClockSpan(startClock)) ; testNb++) {
581         U32 randState = coreRand ^ prime1;
582         unsigned BSId   = 4 + (FUZ_rand(&randState) & 3);
583         unsigned BMId   = FUZ_rand(&randState) & 1;
584         unsigned CCflag = FUZ_rand(&randState) & 1;
585         unsigned autoflush = (FUZ_rand(&randState) & 7) == 2;
586         LizardF_preferences_t prefs;
587         LizardF_compressOptions_t cOptions;
588         LizardF_decompressOptions_t dOptions;
589         unsigned nbBits = (FUZ_rand(&randState) % (FUZ_highbit(srcDataLength-1) - 1)) + 1;
590         size_t srcSize = (FUZ_rand(&randState) & ((1<<nbBits)-1)) + 1;
591         size_t srcStart = FUZ_rand(&randState) % (srcDataLength - srcSize);
592         U64 frameContentSize = ((FUZ_rand(&randState) & 0xF) == 1) ? srcSize : 0;
593         size_t cSize;
594         U64 crcOrig, crcDecoded;
595         LizardF_preferences_t* prefsPtr = &prefs;
596 
597         (void)FUZ_rand(&coreRand);   /* update seed */
598         memset(&prefs, 0, sizeof(prefs));
599         memset(&cOptions, 0, sizeof(cOptions));
600         memset(&dOptions, 0, sizeof(dOptions));
601         prefs.frameInfo.blockMode = (LizardF_blockMode_t)BMId;
602         prefs.frameInfo.blockSizeID = (LizardF_blockSizeID_t)BSId;
603         prefs.frameInfo.contentChecksumFlag = (LizardF_contentChecksum_t)CCflag;
604         prefs.frameInfo.contentSize = frameContentSize;
605         prefs.autoFlush = autoflush;
606         prefs.compressionLevel = LIZARD_MIN_CLEVEL + (FUZ_rand(&randState) % (1+LIZARD_MAX_CLEVEL-LIZARD_MIN_CLEVEL));
607         if ((FUZ_rand(&randState) & 0x1F) == 1) prefsPtr = NULL;
608 
609         DISPLAYUPDATE(2, "\r%5u   ", testNb);
610         crcOrig = XXH64((BYTE*)srcBuffer+srcStart, srcSize, 1);
611 
612         if ((FUZ_rand(&randState) & 0xFFF) == 0) {
613             /* create a skippable frame (rare case) */
614             BYTE* op = (BYTE*)compressedBuffer;
615             FUZ_writeLE32(op, LIZARDF_MAGIC_SKIPPABLE_START + (FUZ_rand(&randState) & 15));
616             FUZ_writeLE32(op+4, (U32)srcSize);
617             cSize = srcSize+8;
618         } else if ((FUZ_rand(&randState) & 0xF) == 2) {
619             cSize = LizardF_compressFrame(compressedBuffer, LizardF_compressFrameBound(srcSize, prefsPtr), (char*)srcBuffer + srcStart, srcSize, prefsPtr);
620             CHECK(LizardF_isError(cSize), "LizardF_compressFrame failed : error %i (%s)", (int)cSize, LizardF_getErrorName(cSize));
621         } else {
622             const BYTE* ip = (const BYTE*)srcBuffer + srcStart;
623             const BYTE* const iend = ip + srcSize;
624             BYTE* op = (BYTE*)compressedBuffer;
625             BYTE* const oend = op + LizardF_compressFrameBound(srcDataLength, NULL);
626             unsigned maxBits = FUZ_highbit((U32)srcSize);
627             result = LizardF_compressBegin(cCtx, op, oend-op, prefsPtr);
628             CHECK(LizardF_isError(result), "Compression header failed (error %i)", (int)result);
629             op += result;
630             while (ip < iend) {
631                 unsigned nbBitsSeg = FUZ_rand(&randState) % maxBits;
632                 size_t iSize = (FUZ_rand(&randState) & ((1<<nbBitsSeg)-1)) + 1;
633                 size_t oSize = LizardF_compressBound(iSize, prefsPtr);
634                 unsigned forceFlush = ((FUZ_rand(&randState) & 3) == 1);
635                 if (iSize > (size_t)(iend-ip)) iSize = iend-ip;
636                 cOptions.stableSrc = ((FUZ_rand(&randState) & 3) == 1);
637 
638                 result = LizardF_compressUpdate(cCtx, op, oSize, ip, iSize, &cOptions);
639                 CHECK(LizardF_isError(result), "Compression failed (error %i) iSize=%d oSize=%d", (int)result, (int)iSize, (int)oSize);
640                 op += result;
641                 ip += iSize;
642 
643                 if (forceFlush) {
644                     result = LizardF_flush(cCtx, op, oend-op, &cOptions);
645                     CHECK(LizardF_isError(result), "Compression flush failed (error %i)", (int)result);
646                     op += result;
647                 }
648             }
649             result = LizardF_compressEnd(cCtx, op, oend-op, &cOptions);
650             CHECK(LizardF_isError(result), "Compression completion failed (error %i)", (int)result);
651             op += result;
652             cSize = op-(BYTE*)compressedBuffer;
653         }
654 
655         {   const BYTE* ip = (const BYTE*)compressedBuffer;
656             const BYTE* const iend = ip + cSize;
657             BYTE* op = (BYTE*)decodedBuffer;
658             BYTE* const oend = op + srcDataLength;
659             size_t totalOut = 0;
660             unsigned maxBits = FUZ_highbit((U32)cSize);
661             unsigned nonContiguousDst = (FUZ_rand(&randState) & 3) == 1;
662             nonContiguousDst += FUZ_rand(&randState) & nonContiguousDst;   /* 0=>0; 1=>1,2 */
663             XXH64_reset(&xxh64, 1);
664             if (maxBits < 3) maxBits = 3;
665             while (ip < iend) {
666                 unsigned nbBitsI = (FUZ_rand(&randState) % (maxBits-1)) + 1;
667                 unsigned nbBitsO = (FUZ_rand(&randState) % (maxBits)) + 1;
668                 size_t iSize = (FUZ_rand(&randState) & ((1<<nbBitsI)-1)) + 1;
669                 size_t oSize = (FUZ_rand(&randState) & ((1<<nbBitsO)-1)) + 2;
670                 if (iSize > (size_t)(iend-ip)) iSize = iend-ip;
671                 if (oSize > (size_t)(oend-op)) oSize = oend-op;
672                 dOptions.stableDst = FUZ_rand(&randState) & 1;
673                 if (nonContiguousDst==2) dOptions.stableDst = 0;
674                 result = LizardF_decompress(dCtx, op, &oSize, ip, &iSize, &dOptions);
675                 if (result == (size_t)-LizardF_ERROR_contentChecksum_invalid)
676                     locateBuffDiff((BYTE*)srcBuffer+srcStart, decodedBuffer, srcSize, nonContiguousDst);
677                 CHECK(LizardF_isError(result), "Decompression failed (error %i:%s)", (int)result, LizardF_getErrorName((LizardF_errorCode_t)result));
678                 XXH64_update(&xxh64, op, (U32)oSize);
679                 totalOut += oSize;
680                 op += oSize;
681                 ip += iSize;
682                 op += nonContiguousDst;
683                 if (nonContiguousDst==2) op = (BYTE*)decodedBuffer;   /* overwritten destination */
684             }
685             CHECK(result != 0, "Frame decompression failed (error %i)", (int)result);
686             if (totalOut) {  /* otherwise, it's a skippable frame */
687                 crcDecoded = XXH64_digest(&xxh64);
688                 if (crcDecoded != crcOrig) locateBuffDiff((BYTE*)srcBuffer+srcStart, decodedBuffer, srcSize, nonContiguousDst);
689                 CHECK(crcDecoded != crcOrig, "Decompression corruption");
690             }
691         }
692     }
693 
694     DISPLAYLEVEL(2, "\rAll tests completed   \n");
695 
696 _end:
697     LizardF_freeDecompressionContext(dCtx);
698     LizardF_freeCompressionContext(cCtx);
699     free(srcBuffer);
700     free(compressedBuffer);
701     free(decodedBuffer);
702 
703     if (pause) {
704         DISPLAY("press enter to finish \n");
705         (void)getchar();
706     }
707     return testResult;
708 
709 _output_error:
710     testResult = 1;
711     goto _end;
712 }
713 
714 
FUZ_usage(void)715 int FUZ_usage(void)
716 {
717     DISPLAY( "Usage :\n");
718     DISPLAY( "      %s [args]\n", programName);
719     DISPLAY( "\n");
720     DISPLAY( "Arguments :\n");
721     DISPLAY( " -i#    : Nb of tests (default:%u) \n", nbTestsDefault);
722     DISPLAY( " -T#    : Duration of tests, in seconds (default: use Nb of tests) \n");
723     DISPLAY( " -s#    : Select seed (default:prompt user)\n");
724     DISPLAY( " -t#    : Select starting test number (default:0)\n");
725     DISPLAY( " -p#    : Select compressibility in %% (default:%i%%)\n", FUZ_COMPRESSIBILITY_DEFAULT);
726     DISPLAY( " -v     : verbose\n");
727     DISPLAY( " -h     : display help and exit\n");
728     return 0;
729 }
730 
731 
main(int argc,char ** argv)732 int main(int argc, char** argv)
733 {
734     U32 seed=0;
735     int seedset=0;
736     int argNb;
737     int nbTests = nbTestsDefault;
738     int testNb = 0;
739     int proba = FUZ_COMPRESSIBILITY_DEFAULT;
740     int result=0;
741     U32 duration=0;
742 
743     /* Check command line */
744     programName = argv[0];
745     for(argNb=1; argNb<argc; argNb++) {
746         char* argument = argv[argNb];
747 
748         if(!argument) continue;   /* Protection if argument empty */
749 
750         /* Decode command (note : aggregated commands are allowed) */
751         if (argument[0]=='-') {
752             if (!strcmp(argument, "--no-prompt")) {
753                 no_prompt=1;
754                 seedset=1;
755                 displayLevel=1;
756                 continue;
757             }
758             argument++;
759 
760             while (*argument!=0) {
761                 switch(*argument)
762                 {
763                 case 'h':
764                     return FUZ_usage();
765                 case 'v':
766                     argument++;
767                     displayLevel=4;
768                     break;
769                 case 'q':
770                     argument++;
771                     displayLevel--;
772                     break;
773                 case 'p': /* pause at the end */
774                     argument++;
775                     pause = 1;
776                     break;
777 
778                 case 'i':
779                     argument++;
780                     nbTests=0; duration=0;
781                     while ((*argument>='0') && (*argument<='9')) {
782                         nbTests *= 10;
783                         nbTests += *argument - '0';
784                         argument++;
785                     }
786                     break;
787 
788                 case 'T':
789                     argument++;
790                     nbTests = 0; duration = 0;
791                     for (;;) {
792                         switch(*argument)
793                         {
794                             case 'm': duration *= 60; argument++; continue;
795                             case 's':
796                             case 'n': argument++; continue;
797                             case '0':
798                             case '1':
799                             case '2':
800                             case '3':
801                             case '4':
802                             case '5':
803                             case '6':
804                             case '7':
805                             case '8':
806                             case '9': duration *= 10; duration += *argument++ - '0'; continue;
807                         }
808                         break;
809                     }
810                     break;
811 
812                 case 's':
813                     argument++;
814                     seed=0;
815                     seedset=1;
816                     while ((*argument>='0') && (*argument<='9')) {
817                         seed *= 10;
818                         seed += *argument - '0';
819                         argument++;
820                     }
821                     break;
822                 case 't':
823                     argument++;
824                     testNb=0;
825                     while ((*argument>='0') && (*argument<='9')) {
826                         testNb *= 10;
827                         testNb += *argument - '0';
828                         argument++;
829                     }
830                     break;
831                 case 'P':   /* compressibility % */
832                     argument++;
833                     proba=0;
834                     while ((*argument>='0') && (*argument<='9')) {
835                         proba *= 10;
836                         proba += *argument - '0';
837                         argument++;
838                     }
839                     if (proba<0) proba=0;
840                     if (proba>100) proba=100;
841                     break;
842                 default:
843                     ;
844                     return FUZ_usage();
845                 }
846             }
847         }
848     }
849 
850     /* Get Seed */
851     printf("Starting lizard_frame tester (%i-bits, %s)\n", (int)(sizeof(size_t)*8), LIZARD_VERSION_STRING);
852 
853     if (!seedset) {
854         time_t const t = time(NULL);
855         U32 const h = XXH32(&t, sizeof(t), 1);
856         seed = h % 10000;
857     }
858     printf("Seed = %u\n", seed);
859     if (proba!=FUZ_COMPRESSIBILITY_DEFAULT) printf("Compressibility : %i%%\n", proba);
860 
861     if (nbTests<=0) nbTests=1;
862 
863     if (testNb==0) result = basicTests(seed, ((double)proba) / 100);
864     if (result) return 1;
865     return fuzzerTests(seed, nbTests, testNb, ((double)proba) / 100, duration);
866 }
867