| Yann Collet | d7883a2 | 2016-08-12 16:48:02 +0200 | [diff] [blame^] | 1 | /* | 
|  | 2 | Fuzzer test tool for zstd streaming API | 
|  | 3 | Copyright (C) Yann Collet 2016 | 
|  | 4 |  | 
|  | 5 | GPL v2 License | 
|  | 6 |  | 
|  | 7 | This program is free software; you can redistribute it and/or modify | 
|  | 8 | it under the terms of the GNU General Public License as published by | 
|  | 9 | the Free Software Foundation; either version 2 of the License, or | 
|  | 10 | (at your option) any later version. | 
|  | 11 |  | 
|  | 12 | This program is distributed in the hope that it will be useful, | 
|  | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 15 | GNU General Public License for more details. | 
|  | 16 |  | 
|  | 17 | You should have received a copy of the GNU General Public License along | 
|  | 18 | with this program; if not, write to the Free Software Foundation, Inc., | 
|  | 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 
|  | 20 |  | 
|  | 21 | You can contact the author at : | 
|  | 22 | - ZSTD homepage : https://www.zstd.net/ | 
|  | 23 | */ | 
|  | 24 |  | 
|  | 25 | /*-************************************ | 
|  | 26 | *  Compiler specific | 
|  | 27 | **************************************/ | 
|  | 28 | #ifdef _MSC_VER    /* Visual Studio */ | 
|  | 29 | #  define _CRT_SECURE_NO_WARNINGS     /* fgets */ | 
|  | 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>       /* free */ | 
|  | 39 | #include <stdio.h>        /* fgets, sscanf */ | 
|  | 40 | #include <sys/timeb.h>    /* timeb */ | 
|  | 41 | #include <string.h>       /* strcmp */ | 
|  | 42 | #include "mem.h" | 
|  | 43 | #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_maxCLevel */ | 
|  | 44 | #include "zstd.h"         /* ZSTD_compressBound */ | 
|  | 45 | #include "datagen.h"      /* RDG_genBuffer */ | 
|  | 46 | #define XXH_STATIC_LINKING_ONLY | 
|  | 47 | #include "xxhash.h"       /* XXH64_* */ | 
|  | 48 |  | 
|  | 49 |  | 
|  | 50 | /*-************************************ | 
|  | 51 | *  Constants | 
|  | 52 | **************************************/ | 
|  | 53 | #define KB *(1U<<10) | 
|  | 54 | #define MB *(1U<<20) | 
|  | 55 | #define GB *(1U<<30) | 
|  | 56 |  | 
|  | 57 | static const U32 nbTestsDefault = 10000; | 
|  | 58 | #define COMPRESSIBLE_NOISE_LENGTH (10 MB) | 
|  | 59 | #define FUZ_COMPRESSIBILITY_DEFAULT 50 | 
|  | 60 | static const U32 prime1 = 2654435761U; | 
|  | 61 | static const U32 prime2 = 2246822519U; | 
|  | 62 |  | 
|  | 63 |  | 
|  | 64 |  | 
|  | 65 | /*-************************************ | 
|  | 66 | *  Display Macros | 
|  | 67 | **************************************/ | 
|  | 68 | #define DISPLAY(...)          fprintf(stderr, __VA_ARGS__) | 
|  | 69 | #define DISPLAYLEVEL(l, ...)  if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } | 
|  | 70 | static U32 g_displayLevel = 2; | 
|  | 71 |  | 
|  | 72 | #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ | 
|  | 73 | if ((FUZ_GetMilliSpan(g_displayTime) > g_refreshRate) || (g_displayLevel>=4)) \ | 
|  | 74 | { g_displayTime = FUZ_GetMilliStart(); DISPLAY(__VA_ARGS__); \ | 
|  | 75 | if (g_displayLevel>=4) fflush(stdout); } } | 
|  | 76 | static const U32 g_refreshRate = 150; | 
|  | 77 | static U32 g_displayTime = 0; | 
|  | 78 |  | 
|  | 79 | static U32 g_testTime = 0; | 
|  | 80 |  | 
|  | 81 |  | 
|  | 82 | /*-******************************************************* | 
|  | 83 | *  Fuzzer functions | 
|  | 84 | *********************************************************/ | 
|  | 85 | #define MAX(a,b) ((a)>(b)?(a):(b)) | 
|  | 86 |  | 
|  | 87 | static U32 FUZ_GetMilliStart(void) | 
|  | 88 | { | 
|  | 89 | struct timeb tb; | 
|  | 90 | U32 nCount; | 
|  | 91 | ftime( &tb ); | 
|  | 92 | nCount = (U32) (((tb.time & 0xFFFFF) * 1000) +  tb.millitm); | 
|  | 93 | return nCount; | 
|  | 94 | } | 
|  | 95 |  | 
|  | 96 |  | 
|  | 97 | static U32 FUZ_GetMilliSpan(U32 nTimeStart) | 
|  | 98 | { | 
|  | 99 | U32 const nCurrent = FUZ_GetMilliStart(); | 
|  | 100 | U32 nSpan = nCurrent - nTimeStart; | 
|  | 101 | if (nTimeStart > nCurrent) | 
|  | 102 | nSpan += 0x100000 * 1000; | 
|  | 103 | return nSpan; | 
|  | 104 | } | 
|  | 105 |  | 
|  | 106 | /*! FUZ_rand() : | 
|  | 107 | @return : a 27 bits random value, from a 32-bits `seed`. | 
|  | 108 | `seed` is also modified */ | 
|  | 109 | #  define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r))) | 
|  | 110 | unsigned int FUZ_rand(unsigned int* seedPtr) | 
|  | 111 | { | 
|  | 112 | U32 rand32 = *seedPtr; | 
|  | 113 | rand32 *= prime1; | 
|  | 114 | rand32 += prime2; | 
|  | 115 | rand32  = FUZ_rotl32(rand32, 13); | 
|  | 116 | *seedPtr = rand32; | 
|  | 117 | return rand32 >> 5; | 
|  | 118 | } | 
|  | 119 |  | 
|  | 120 |  | 
|  | 121 | /* | 
|  | 122 | static unsigned FUZ_highbit32(U32 v32) | 
|  | 123 | { | 
|  | 124 | unsigned nbBits = 0; | 
|  | 125 | if (v32==0) return 0; | 
|  | 126 | for ( ; v32 ; v32>>=1) nbBits++; | 
|  | 127 | return nbBits; | 
|  | 128 | } | 
|  | 129 | */ | 
|  | 130 |  | 
|  | 131 | static void* allocFunction(void* opaque, size_t size) | 
|  | 132 | { | 
|  | 133 | void* address = malloc(size); | 
|  | 134 | (void)opaque; | 
|  | 135 | return address; | 
|  | 136 | } | 
|  | 137 |  | 
|  | 138 | static void freeFunction(void* opaque, void* address) | 
|  | 139 | { | 
|  | 140 | (void)opaque; | 
|  | 141 | free(address); | 
|  | 142 | } | 
|  | 143 |  | 
|  | 144 | static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem customMem) | 
|  | 145 | { | 
|  | 146 | int testResult = 0; | 
|  | 147 | size_t CNBufferSize = COMPRESSIBLE_NOISE_LENGTH; | 
|  | 148 | void* CNBuffer = malloc(CNBufferSize); | 
|  | 149 | size_t const skippableFrameSize = 11; | 
|  | 150 | size_t const compressedBufferSize = (8 + skippableFrameSize) + ZSTD_compressBound(COMPRESSIBLE_NOISE_LENGTH); | 
|  | 151 | void* compressedBuffer = malloc(compressedBufferSize); | 
|  | 152 | size_t const decodedBufferSize = CNBufferSize; | 
|  | 153 | void* decodedBuffer = malloc(decodedBufferSize); | 
|  | 154 | size_t cSize; | 
|  | 155 | U32 testNb=0; | 
|  | 156 | ZSTD_CStream* zc = ZSTD_createCStream_advanced(customMem); | 
|  | 157 | ZSTD_DStream* zd = ZSTD_createDStream_advanced(customMem); | 
|  | 158 | ZSTD_rCursor rCursor; | 
|  | 159 | ZSTD_wCursor wCursor; | 
|  | 160 |  | 
|  | 161 | /* Create compressible test buffer */ | 
|  | 162 | if (!CNBuffer || !compressedBuffer || !decodedBuffer || !zc || !zd) { | 
|  | 163 | DISPLAY("Not enough memory, aborting\n"); | 
|  | 164 | goto _output_error; | 
|  | 165 | } | 
|  | 166 | RDG_genBuffer(CNBuffer, CNBufferSize, compressibility, 0., seed); | 
|  | 167 |  | 
|  | 168 | /* generate skippable frame */ | 
|  | 169 | MEM_writeLE32(compressedBuffer, ZSTD_MAGIC_SKIPPABLE_START); | 
|  | 170 | MEM_writeLE32(((char*)compressedBuffer)+4, (U32)skippableFrameSize); | 
|  | 171 | cSize = skippableFrameSize + 8; | 
|  | 172 |  | 
|  | 173 | /* Basic compression test */ | 
|  | 174 | DISPLAYLEVEL(4, "test%3i : compress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); | 
|  | 175 | ZSTD_initCStream_usingDict(zc, CNBuffer, 128 KB, 1); | 
|  | 176 | wCursor.ptr = (char*)(compressedBuffer)+cSize; | 
|  | 177 | wCursor.size = compressedBufferSize; | 
|  | 178 | wCursor.nbBytesWritten = 0; | 
|  | 179 | rCursor.ptr = CNBuffer; | 
|  | 180 | rCursor.size = CNBufferSize; | 
|  | 181 | { size_t const r = ZSTD_compressStream(zc, &wCursor, &rCursor); | 
|  | 182 | if (ZSTD_isError(r)) goto _output_error; } | 
|  | 183 | if (rCursor.size != 0) goto _output_error;   /* entire input should be consumed */ | 
|  | 184 | { size_t const r = ZSTD_endStream(zc, &wCursor); | 
|  | 185 | if (r != 0) goto _output_error; }  /*< error, or some data not flushed */ | 
|  | 186 | cSize += wCursor.nbBytesWritten; | 
|  | 187 | DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/COMPRESSIBLE_NOISE_LENGTH*100); | 
|  | 188 |  | 
|  | 189 | /* skippable frame test */ | 
|  | 190 | DISPLAYLEVEL(4, "test%3i : decompress skippable frame : ", testNb++); | 
|  | 191 | ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB); | 
|  | 192 | rCursor.ptr = compressedBuffer; | 
|  | 193 | rCursor.size = cSize; | 
|  | 194 | wCursor.ptr = decodedBuffer; | 
|  | 195 | wCursor.size = CNBufferSize; | 
|  | 196 | wCursor.nbBytesWritten = 0; | 
|  | 197 | { size_t const r = ZSTD_decompressStream(zd, &wCursor, &rCursor); | 
|  | 198 | if (r != 0) goto _output_error; } | 
|  | 199 | if (wCursor.nbBytesWritten != 0) goto _output_error;   /* skippable frame len is 0 */ | 
|  | 200 | DISPLAYLEVEL(4, "OK \n"); | 
|  | 201 |  | 
|  | 202 | /* Basic decompression test */ | 
|  | 203 | DISPLAYLEVEL(4, "test%3i : decompress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); | 
|  | 204 | ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB); | 
|  | 205 | { size_t const r = ZSTD_decompressStream(zd, &wCursor, &rCursor); | 
|  | 206 | if (r != 0) goto _output_error; }  /* should reach end of frame == 0; otherwise, some data left, or an error */ | 
|  | 207 | if (wCursor.nbBytesWritten != CNBufferSize) goto _output_error;   /* should regenerate the same amount */ | 
|  | 208 | if (rCursor.size != 0) goto _output_error;   /* should have read the entire frame */ | 
|  | 209 | DISPLAYLEVEL(4, "OK \n"); | 
|  | 210 |  | 
|  | 211 | /* check regenerated data is byte exact */ | 
|  | 212 | DISPLAYLEVEL(4, "test%3i : check decompressed result : ", testNb++); | 
|  | 213 | {   size_t i; | 
|  | 214 | for (i=0; i<CNBufferSize; i++) { | 
|  | 215 | if (((BYTE*)decodedBuffer)[i] != ((BYTE*)CNBuffer)[i]) goto _output_error; | 
|  | 216 | }   } | 
|  | 217 | DISPLAYLEVEL(4, "OK \n"); | 
|  | 218 |  | 
|  | 219 | /* Byte-by-byte decompression test */ | 
|  | 220 | DISPLAYLEVEL(4, "test%3i : decompress byte-by-byte : ", testNb++); | 
|  | 221 | {   size_t r = 1; | 
|  | 222 | ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB); | 
|  | 223 | rCursor.ptr = compressedBuffer; | 
|  | 224 | wCursor.ptr = decodedBuffer; | 
|  | 225 | wCursor.nbBytesWritten = 0; | 
|  | 226 | while (r) {   /* skippable frame */ | 
|  | 227 | rCursor.size = 1; | 
|  | 228 | wCursor.size = 1; | 
|  | 229 | r = ZSTD_decompressStream(zd, &wCursor, &rCursor); | 
|  | 230 | if (ZSTD_isError(r)) goto _output_error; | 
|  | 231 | } | 
|  | 232 | ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB); | 
|  | 233 | r=1; | 
|  | 234 | while (r) {   /* normal frame */ | 
|  | 235 | rCursor.size = 1; | 
|  | 236 | wCursor.size = 1; | 
|  | 237 | r = ZSTD_decompressStream(zd, &wCursor, &rCursor); | 
|  | 238 | if (ZSTD_isError(r)) goto _output_error; | 
|  | 239 | } | 
|  | 240 | } | 
|  | 241 | if (wCursor.nbBytesWritten != CNBufferSize) goto _output_error;   /* should regenerate the same amount */ | 
|  | 242 | if ((size_t)(rCursor.ptr - compressedBuffer) != cSize) goto _output_error;   /* should have read the entire frame */ | 
|  | 243 | DISPLAYLEVEL(4, "OK \n"); | 
|  | 244 |  | 
|  | 245 | /* check regenerated data is byte exact */ | 
|  | 246 | DISPLAYLEVEL(4, "test%3i : check decompressed result : ", testNb++); | 
|  | 247 | {   size_t i; | 
|  | 248 | for (i=0; i<CNBufferSize; i++) { | 
|  | 249 | if (((BYTE*)decodedBuffer)[i] != ((BYTE*)CNBuffer)[i]) goto _output_error;; | 
|  | 250 | }   } | 
|  | 251 | DISPLAYLEVEL(4, "OK \n"); | 
|  | 252 |  | 
|  | 253 | _end: | 
|  | 254 | ZSTD_freeCStream(zc); | 
|  | 255 | ZSTD_freeDStream(zd); | 
|  | 256 | free(CNBuffer); | 
|  | 257 | free(compressedBuffer); | 
|  | 258 | free(decodedBuffer); | 
|  | 259 | return testResult; | 
|  | 260 |  | 
|  | 261 | _output_error: | 
|  | 262 | testResult = 1; | 
|  | 263 | DISPLAY("Error detected in Unit tests ! \n"); | 
|  | 264 | goto _end; | 
|  | 265 | } | 
|  | 266 |  | 
|  | 267 |  | 
|  | 268 | static size_t findDiff(const void* buf1, const void* buf2, size_t max) | 
|  | 269 | { | 
|  | 270 | const BYTE* b1 = (const BYTE*)buf1; | 
|  | 271 | const BYTE* b2 = (const BYTE*)buf2; | 
|  | 272 | size_t u; | 
|  | 273 | for (u=0; u<max; u++) { | 
|  | 274 | if (b1[u] != b2[u]) break; | 
|  | 275 | } | 
|  | 276 | return u; | 
|  | 277 | } | 
|  | 278 |  | 
|  | 279 | static size_t FUZ_rLogLength(U32* seed, U32 logLength) | 
|  | 280 | { | 
|  | 281 | size_t const lengthMask = ((size_t)1 << logLength) - 1; | 
|  | 282 | return (lengthMask+1) + (FUZ_rand(seed) & lengthMask); | 
|  | 283 | } | 
|  | 284 |  | 
|  | 285 | static size_t FUZ_randomLength(U32* seed, U32 maxLog) | 
|  | 286 | { | 
|  | 287 | U32 const logLength = FUZ_rand(seed) % maxLog; | 
|  | 288 | return FUZ_rLogLength(seed, logLength); | 
|  | 289 | } | 
|  | 290 |  | 
|  | 291 | #define MIN(a,b)   ( (a) < (b) ? (a) : (b) ) | 
|  | 292 |  | 
|  | 293 | #define CHECK(cond, ...) if (cond) { DISPLAY("Error => "); DISPLAY(__VA_ARGS__); \ | 
|  | 294 | DISPLAY(" (seed %u, test nb %u)  \n", seed, testNb); goto _output_error; } | 
|  | 295 |  | 
|  | 296 | static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compressibility) | 
|  | 297 | { | 
|  | 298 | static const U32 maxSrcLog = 24; | 
|  | 299 | static const U32 maxSampleLog = 19; | 
|  | 300 | BYTE* cNoiseBuffer[5]; | 
|  | 301 | size_t srcBufferSize = (size_t)1<<maxSrcLog; | 
|  | 302 | BYTE* copyBuffer; | 
|  | 303 | size_t copyBufferSize= srcBufferSize + (1<<maxSampleLog); | 
|  | 304 | BYTE* cBuffer; | 
|  | 305 | size_t cBufferSize   = ZSTD_compressBound(srcBufferSize); | 
|  | 306 | BYTE* dstBuffer; | 
|  | 307 | size_t dstBufferSize = srcBufferSize; | 
|  | 308 | U32 result = 0; | 
|  | 309 | U32 testNb = 0; | 
|  | 310 | U32 coreSeed = seed; | 
|  | 311 | ZSTD_CStream* zc; | 
|  | 312 | ZSTD_DStream* zd; | 
|  | 313 | U32 startTime = FUZ_GetMilliStart(); | 
|  | 314 |  | 
|  | 315 | /* allocations */ | 
|  | 316 | zc = ZSTD_createCStream(); | 
|  | 317 | zd = ZSTD_createDStream(); | 
|  | 318 | cNoiseBuffer[0] = (BYTE*)malloc (srcBufferSize); | 
|  | 319 | cNoiseBuffer[1] = (BYTE*)malloc (srcBufferSize); | 
|  | 320 | cNoiseBuffer[2] = (BYTE*)malloc (srcBufferSize); | 
|  | 321 | cNoiseBuffer[3] = (BYTE*)malloc (srcBufferSize); | 
|  | 322 | cNoiseBuffer[4] = (BYTE*)malloc (srcBufferSize); | 
|  | 323 | copyBuffer= (BYTE*)malloc (copyBufferSize); | 
|  | 324 | dstBuffer = (BYTE*)malloc (dstBufferSize); | 
|  | 325 | cBuffer   = (BYTE*)malloc (cBufferSize); | 
|  | 326 | CHECK (!cNoiseBuffer[0] || !cNoiseBuffer[1] || !cNoiseBuffer[2] || !cNoiseBuffer[3] || !cNoiseBuffer[4] || | 
|  | 327 | !copyBuffer || !dstBuffer || !cBuffer || !zc || !zd, | 
|  | 328 | "Not enough memory, fuzzer tests cancelled"); | 
|  | 329 |  | 
|  | 330 | /* Create initial samples */ | 
|  | 331 | RDG_genBuffer(cNoiseBuffer[0], srcBufferSize, 0.00, 0., coreSeed);    /* pure noise */ | 
|  | 332 | RDG_genBuffer(cNoiseBuffer[1], srcBufferSize, 0.05, 0., coreSeed);    /* barely compressible */ | 
|  | 333 | RDG_genBuffer(cNoiseBuffer[2], srcBufferSize, compressibility, 0., coreSeed); | 
|  | 334 | RDG_genBuffer(cNoiseBuffer[3], srcBufferSize, 0.95, 0., coreSeed);    /* highly compressible */ | 
|  | 335 | RDG_genBuffer(cNoiseBuffer[4], srcBufferSize, 1.00, 0., coreSeed);    /* sparse content */ | 
|  | 336 | memset(copyBuffer, 0x65, copyBufferSize);                             /* make copyBuffer considered initialized */ | 
|  | 337 |  | 
|  | 338 | /* catch up testNb */ | 
|  | 339 | for (testNb=1; testNb < startTest; testNb++) | 
|  | 340 | FUZ_rand(&coreSeed); | 
|  | 341 |  | 
|  | 342 | /* test loop */ | 
|  | 343 | for ( ; (testNb <= nbTests) || (FUZ_GetMilliSpan(startTime) < g_testTime) ; testNb++ ) { | 
|  | 344 | U32 lseed; | 
|  | 345 | const BYTE* srcBuffer; | 
|  | 346 | const BYTE* dict; | 
|  | 347 | size_t maxTestSize, dictSize; | 
|  | 348 | size_t cSize, totalTestSize, totalCSize, totalGenSize; | 
|  | 349 | U32 n, nbChunks; | 
|  | 350 | XXH64_state_t xxhState; | 
|  | 351 | U64 crcOrig; | 
|  | 352 |  | 
|  | 353 | /* init */ | 
|  | 354 | DISPLAYUPDATE(2, "\r%6u", testNb); | 
|  | 355 | if (nbTests >= testNb) DISPLAYUPDATE(2, "/%6u   ", nbTests); | 
|  | 356 | FUZ_rand(&coreSeed); | 
|  | 357 | lseed = coreSeed ^ prime1; | 
|  | 358 |  | 
|  | 359 | /* states full reset (unsynchronized) */ | 
|  | 360 | /* some issues only happen when reusing states in a specific sequence of parameters */ | 
|  | 361 | if ((FUZ_rand(&lseed) & 0xFF) == 131) { ZSTD_freeCStream(zc); zc = ZSTD_createCStream(); } | 
|  | 362 | if ((FUZ_rand(&lseed) & 0xFF) == 132) { ZSTD_freeDStream(zd); zd = ZSTD_createDStream(); } | 
|  | 363 |  | 
|  | 364 | /* srcBuffer selection [0-4] */ | 
|  | 365 | {   U32 buffNb = FUZ_rand(&lseed) & 0x7F; | 
|  | 366 | if (buffNb & 7) buffNb=2;   /* most common : compressible (P) */ | 
|  | 367 | else { | 
|  | 368 | buffNb >>= 3; | 
|  | 369 | if (buffNb & 7) { | 
|  | 370 | const U32 tnb[2] = { 1, 3 };   /* barely/highly compressible */ | 
|  | 371 | buffNb = tnb[buffNb >> 3]; | 
|  | 372 | } else { | 
|  | 373 | const U32 tnb[2] = { 0, 4 };   /* not compressible / sparse */ | 
|  | 374 | buffNb = tnb[buffNb >> 3]; | 
|  | 375 | }   } | 
|  | 376 | srcBuffer = cNoiseBuffer[buffNb]; | 
|  | 377 | } | 
|  | 378 |  | 
|  | 379 | /* compression init */ | 
|  | 380 | {   U32 const testLog = FUZ_rand(&lseed) % maxSrcLog; | 
|  | 381 | U32 const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (testLog/3))) + 1; | 
|  | 382 | maxTestSize = FUZ_rLogLength(&lseed, testLog); | 
|  | 383 | dictSize  = (FUZ_rand(&lseed)==1) ? FUZ_randomLength(&lseed, maxSampleLog) : 0; | 
|  | 384 | /* random dictionary selection */ | 
|  | 385 | {   size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize); | 
|  | 386 | dict = srcBuffer + dictStart; | 
|  | 387 | } | 
|  | 388 | {   ZSTD_parameters params = ZSTD_getParams(cLevel, 0, dictSize); | 
|  | 389 | params.fParams.checksumFlag = FUZ_rand(&lseed) & 1; | 
|  | 390 | params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1; | 
|  | 391 | {   size_t const initError = ZSTD_initCStream_advanced(zc, dict, dictSize, params, 0); | 
|  | 392 | CHECK (ZSTD_isError(initError),"init error : %s", ZSTD_getErrorName(initError)); | 
|  | 393 | }   }   } | 
|  | 394 |  | 
|  | 395 | /* multi-segments compression test */ | 
|  | 396 | XXH64_reset(&xxhState, 0); | 
|  | 397 | nbChunks    = (FUZ_rand(&lseed) & 127) + 2; | 
|  | 398 | {   ZSTD_wCursor wCursor = { cBuffer, cBufferSize, 0 } ; | 
|  | 399 | for (n=0, cSize=0, totalTestSize=0 ; (n<nbChunks) && (totalTestSize < maxTestSize) ; n++) { | 
|  | 400 | /* compress random chunk into random size dst buffer */ | 
|  | 401 | {   size_t readChunkSize = FUZ_randomLength(&lseed, maxSampleLog); | 
|  | 402 | size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - readChunkSize); | 
|  | 403 | size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); | 
|  | 404 | size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize); | 
|  | 405 | ZSTD_rCursor rCursor = { srcBuffer+srcStart, readChunkSize }; | 
|  | 406 | wCursor.size = dstBuffSize; | 
|  | 407 |  | 
|  | 408 | { size_t const compressionError = ZSTD_compressStream(zc, &wCursor, &rCursor); | 
|  | 409 | CHECK (ZSTD_isError(compressionError), "compression error : %s", ZSTD_getErrorName(compressionError)); } | 
|  | 410 | readChunkSize -= rCursor.size; | 
|  | 411 |  | 
|  | 412 | XXH64_update(&xxhState, srcBuffer+srcStart, readChunkSize); | 
|  | 413 | memcpy(copyBuffer+totalTestSize, srcBuffer+srcStart, readChunkSize); | 
|  | 414 | totalTestSize += readChunkSize; | 
|  | 415 | } | 
|  | 416 |  | 
|  | 417 | /* random flush operation, to mess around */ | 
|  | 418 | if ((FUZ_rand(&lseed) & 15) == 0) { | 
|  | 419 | size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); | 
|  | 420 | size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize); | 
|  | 421 | wCursor.size = dstBuffSize; | 
|  | 422 | {   size_t const flushError = ZSTD_flushStream(zc, &wCursor); | 
|  | 423 | CHECK (ZSTD_isError(flushError), "flush error : %s", ZSTD_getErrorName(flushError)); | 
|  | 424 | }   }   } | 
|  | 425 |  | 
|  | 426 | /* final frame epilogue */ | 
|  | 427 | {   size_t remainingToFlush = (size_t)(-1); | 
|  | 428 | while (remainingToFlush) { | 
|  | 429 | size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); | 
|  | 430 | size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize); | 
|  | 431 | U32 const enoughDstSize = (adjustedDstSize >= remainingToFlush); | 
|  | 432 | wCursor.size = adjustedDstSize; | 
|  | 433 | remainingToFlush = ZSTD_endStream(zc, &wCursor); | 
|  | 434 | CHECK (ZSTD_isError(remainingToFlush), "flush error : %s", ZSTD_getErrorName(remainingToFlush)); | 
|  | 435 | CHECK (enoughDstSize && remainingToFlush, "ZSTD_endStream() not fully flushed (%u remaining), but enough space available", (U32)remainingToFlush); | 
|  | 436 | }   } | 
|  | 437 | crcOrig = XXH64_digest(&xxhState); | 
|  | 438 | cSize = wCursor.nbBytesWritten; | 
|  | 439 | } | 
|  | 440 |  | 
|  | 441 | /* multi - fragments decompression test */ | 
|  | 442 | ZSTD_initDStream_usingDict(zd, dict, dictSize); | 
|  | 443 | {   size_t decompressionResult = 1; | 
|  | 444 | ZSTD_rCursor rCursor = { cBuffer, cSize }; | 
|  | 445 | ZSTD_wCursor wCursor = { dstBuffer, dstBufferSize, 0 }; | 
|  | 446 | for (totalCSize = 0, totalGenSize = 0 ; decompressionResult ; ) { | 
|  | 447 | size_t const readCSrcSize = FUZ_randomLength(&lseed, maxSampleLog); | 
|  | 448 | size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); | 
|  | 449 | size_t const dstBuffSize = MIN(dstBufferSize - totalGenSize, randomDstSize); | 
|  | 450 | rCursor.size = readCSrcSize; | 
|  | 451 | wCursor.size = dstBuffSize; | 
|  | 452 | decompressionResult = ZSTD_decompressStream(zd, &wCursor, &rCursor); | 
|  | 453 | CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult)); | 
|  | 454 | totalCSize += readCSrcSize - rCursor.size; | 
|  | 455 | } | 
|  | 456 | CHECK (decompressionResult != 0, "frame not fully decoded"); | 
|  | 457 | CHECK (wCursor.nbBytesWritten != totalTestSize, "decompressed data : wrong size") | 
|  | 458 | CHECK (totalCSize != cSize, "compressed data should be fully read") | 
|  | 459 | {   U64 const crcDest = XXH64(dstBuffer, totalTestSize, 0); | 
|  | 460 | if (crcDest!=crcOrig) findDiff(copyBuffer, dstBuffer, totalTestSize); | 
|  | 461 | CHECK (crcDest!=crcOrig, "decompressed data corrupted"); | 
|  | 462 | }   } | 
|  | 463 |  | 
|  | 464 | /*=====   noisy/erroneous src decompression test   =====*/ | 
|  | 465 |  | 
|  | 466 | /* add some noise */ | 
|  | 467 | {   U32 const nbNoiseChunks = (FUZ_rand(&lseed) & 7) + 2; | 
|  | 468 | U32 nn; for (nn=0; nn<nbNoiseChunks; nn++) { | 
|  | 469 | size_t const randomNoiseSize = FUZ_randomLength(&lseed, maxSampleLog); | 
|  | 470 | size_t const noiseSize  = MIN((cSize/3) , randomNoiseSize); | 
|  | 471 | size_t const noiseStart = FUZ_rand(&lseed) % (srcBufferSize - noiseSize); | 
|  | 472 | size_t const cStart = FUZ_rand(&lseed) % (cSize - noiseSize); | 
|  | 473 | memcpy(cBuffer+cStart, srcBuffer+noiseStart, noiseSize); | 
|  | 474 | }   } | 
|  | 475 |  | 
|  | 476 | /* try decompression on noisy data */ | 
|  | 477 | ZSTD_initDStream(zd); | 
|  | 478 | {   ZSTD_rCursor rCursor = { cBuffer, cSize }; | 
|  | 479 | ZSTD_wCursor wCursor = { dstBuffer, dstBufferSize, 0 }; | 
|  | 480 | while (wCursor.nbBytesWritten < dstBufferSize) { | 
|  | 481 | size_t const randomCSrcSize = FUZ_randomLength(&lseed, maxSampleLog); | 
|  | 482 | size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); | 
|  | 483 | size_t const adjustedDstSize = MIN(dstBufferSize - wCursor.nbBytesWritten, randomDstSize); | 
|  | 484 | wCursor.size = adjustedDstSize; | 
|  | 485 | rCursor.size = randomCSrcSize; | 
|  | 486 | {   size_t const decompressError = ZSTD_decompressStream(zd, &wCursor, &rCursor); | 
|  | 487 | if (ZSTD_isError(decompressError)) break;   /* error correctly detected */ | 
|  | 488 | }   }   }   } | 
|  | 489 | DISPLAY("\r%u fuzzer tests completed   \n", testNb); | 
|  | 490 |  | 
|  | 491 | _cleanup: | 
|  | 492 | ZSTD_freeCStream(zc); | 
|  | 493 | ZSTD_freeDStream(zd); | 
|  | 494 | free(cNoiseBuffer[0]); | 
|  | 495 | free(cNoiseBuffer[1]); | 
|  | 496 | free(cNoiseBuffer[2]); | 
|  | 497 | free(cNoiseBuffer[3]); | 
|  | 498 | free(cNoiseBuffer[4]); | 
|  | 499 | free(copyBuffer); | 
|  | 500 | free(cBuffer); | 
|  | 501 | free(dstBuffer); | 
|  | 502 | return result; | 
|  | 503 |  | 
|  | 504 | _output_error: | 
|  | 505 | result = 1; | 
|  | 506 | goto _cleanup; | 
|  | 507 | } | 
|  | 508 |  | 
|  | 509 |  | 
|  | 510 | /*-******************************************************* | 
|  | 511 | *  Command line | 
|  | 512 | *********************************************************/ | 
|  | 513 | int FUZ_usage(const char* programName) | 
|  | 514 | { | 
|  | 515 | DISPLAY( "Usage :\n"); | 
|  | 516 | DISPLAY( "      %s [args]\n", programName); | 
|  | 517 | DISPLAY( "\n"); | 
|  | 518 | DISPLAY( "Arguments :\n"); | 
|  | 519 | DISPLAY( " -i#    : Nb of tests (default:%u) \n", nbTestsDefault); | 
|  | 520 | DISPLAY( " -s#    : Select seed (default:prompt user)\n"); | 
|  | 521 | DISPLAY( " -t#    : Select starting test number (default:0)\n"); | 
|  | 522 | DISPLAY( " -P#    : Select compressibility in %% (default:%i%%)\n", FUZ_COMPRESSIBILITY_DEFAULT); | 
|  | 523 | DISPLAY( " -v     : verbose\n"); | 
|  | 524 | DISPLAY( " -p     : pause at the end\n"); | 
|  | 525 | DISPLAY( " -h     : display help and exit\n"); | 
|  | 526 | return 0; | 
|  | 527 | } | 
|  | 528 |  | 
|  | 529 |  | 
|  | 530 | int main(int argc, const char** argv) | 
|  | 531 | { | 
|  | 532 | U32 seed=0; | 
|  | 533 | int seedset=0; | 
|  | 534 | int argNb; | 
|  | 535 | int nbTests = nbTestsDefault; | 
|  | 536 | int testNb = 0; | 
|  | 537 | int proba = FUZ_COMPRESSIBILITY_DEFAULT; | 
|  | 538 | int result=0; | 
|  | 539 | U32 mainPause = 0; | 
|  | 540 | const char* programName = argv[0]; | 
|  | 541 | ZSTD_customMem customMem = { allocFunction, freeFunction, NULL }; | 
|  | 542 | ZSTD_customMem customNULL = { NULL, NULL, NULL }; | 
|  | 543 |  | 
|  | 544 | /* Check command line */ | 
|  | 545 | for(argNb=1; argNb<argc; argNb++) { | 
|  | 546 | const char* argument = argv[argNb]; | 
|  | 547 | if(!argument) continue;   /* Protection if argument empty */ | 
|  | 548 |  | 
|  | 549 | /* Parsing commands. Aggregated commands are allowed */ | 
|  | 550 | if (argument[0]=='-') { | 
|  | 551 | argument++; | 
|  | 552 |  | 
|  | 553 | while (*argument!=0) { | 
|  | 554 | switch(*argument) | 
|  | 555 | { | 
|  | 556 | case 'h': | 
|  | 557 | return FUZ_usage(programName); | 
|  | 558 | case 'v': | 
|  | 559 | argument++; | 
|  | 560 | g_displayLevel=4; | 
|  | 561 | break; | 
|  | 562 | case 'q': | 
|  | 563 | argument++; | 
|  | 564 | g_displayLevel--; | 
|  | 565 | break; | 
|  | 566 | case 'p': /* pause at the end */ | 
|  | 567 | argument++; | 
|  | 568 | mainPause = 1; | 
|  | 569 | break; | 
|  | 570 |  | 
|  | 571 | case 'i': | 
|  | 572 | argument++; | 
|  | 573 | nbTests=0; g_testTime=0; | 
|  | 574 | while ((*argument>='0') && (*argument<='9')) { | 
|  | 575 | nbTests *= 10; | 
|  | 576 | nbTests += *argument - '0'; | 
|  | 577 | argument++; | 
|  | 578 | } | 
|  | 579 | break; | 
|  | 580 |  | 
|  | 581 | case 'T': | 
|  | 582 | argument++; | 
|  | 583 | nbTests=0; g_testTime=0; | 
|  | 584 | while ((*argument>='0') && (*argument<='9')) { | 
|  | 585 | g_testTime *= 10; | 
|  | 586 | g_testTime += *argument - '0'; | 
|  | 587 | argument++; | 
|  | 588 | } | 
|  | 589 | if (*argument=='m') g_testTime *=60, argument++; | 
|  | 590 | if (*argument=='n') argument++; | 
|  | 591 | g_testTime *= 1000; | 
|  | 592 | break; | 
|  | 593 |  | 
|  | 594 | case 's': | 
|  | 595 | argument++; | 
|  | 596 | seed=0; | 
|  | 597 | seedset=1; | 
|  | 598 | while ((*argument>='0') && (*argument<='9')) { | 
|  | 599 | seed *= 10; | 
|  | 600 | seed += *argument - '0'; | 
|  | 601 | argument++; | 
|  | 602 | } | 
|  | 603 | break; | 
|  | 604 |  | 
|  | 605 | case 't': | 
|  | 606 | argument++; | 
|  | 607 | testNb=0; | 
|  | 608 | while ((*argument>='0') && (*argument<='9')) { | 
|  | 609 | testNb *= 10; | 
|  | 610 | testNb += *argument - '0'; | 
|  | 611 | argument++; | 
|  | 612 | } | 
|  | 613 | break; | 
|  | 614 |  | 
|  | 615 | case 'P':   /* compressibility % */ | 
|  | 616 | argument++; | 
|  | 617 | proba=0; | 
|  | 618 | while ((*argument>='0') && (*argument<='9')) { | 
|  | 619 | proba *= 10; | 
|  | 620 | proba += *argument - '0'; | 
|  | 621 | argument++; | 
|  | 622 | } | 
|  | 623 | if (proba<0) proba=0; | 
|  | 624 | if (proba>100) proba=100; | 
|  | 625 | break; | 
|  | 626 |  | 
|  | 627 | default: | 
|  | 628 | return FUZ_usage(programName); | 
|  | 629 | } | 
|  | 630 | }   }   }   /* for(argNb=1; argNb<argc; argNb++) */ | 
|  | 631 |  | 
|  | 632 | /* Get Seed */ | 
|  | 633 | DISPLAY("Starting zstd_buffered tester (%i-bits, %s)\n", (int)(sizeof(size_t)*8), ZSTD_VERSION_STRING); | 
|  | 634 |  | 
|  | 635 | if (!seedset) seed = FUZ_GetMilliStart() % 10000; | 
|  | 636 | DISPLAY("Seed = %u\n", seed); | 
|  | 637 | if (proba!=FUZ_COMPRESSIBILITY_DEFAULT) DISPLAY("Compressibility : %i%%\n", proba); | 
|  | 638 |  | 
|  | 639 | if (nbTests<=0) nbTests=1; | 
|  | 640 |  | 
|  | 641 | if (testNb==0) { | 
|  | 642 | result = basicUnitTests(0, ((double)proba) / 100, customNULL);  /* constant seed for predictability */ | 
|  | 643 | if (!result) { | 
|  | 644 | DISPLAYLEVEL(4, "Unit tests using customMem :\n") | 
|  | 645 | result = basicUnitTests(0, ((double)proba) / 100, customMem);  /* use custom memory allocation functions */ | 
|  | 646 | }   } | 
|  | 647 |  | 
|  | 648 | if (!result) | 
|  | 649 | result = fuzzerTests(seed, nbTests, testNb, ((double)proba) / 100); | 
|  | 650 |  | 
|  | 651 | if (mainPause) { | 
|  | 652 | int unused; | 
|  | 653 | DISPLAY("Press Enter \n"); | 
|  | 654 | unused = getchar(); | 
|  | 655 | (void)unused; | 
|  | 656 | } | 
|  | 657 | return result; | 
|  | 658 | } |