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