31769ce702
streaming decoders, such as ZSTD_decompressStream() or ZSTD_decompress_generic(), may end up making no forward progress, (aka no byte read from input __and__ no byte written to output), due to unusual parameters conditions, such as providing an output buffer already full. In such case, the caller may be caught in an infinite loop, calling the streaming decompression function again and again, without making any progress. This version detects such situation, and generates an error instead : ZSTD_error_dstSize_tooSmall when output buffer is full, ZSTD_error_srcSize_wrong when input buffer is empty. The detection tolerates a number of attempts before triggering an error, controlled by ZSTD_NO_FORWARD_PROGRESS_MAX macro constant, which is set to 16 by default, and can be re-defined at compilation time. This behavior tolerates potentially existing implementations where such cases happen sporadically, like once or twice, which is not dangerous (only infinite loops are), without generating an error, hence without breaking these implementations.
1962 lines
92 KiB
C
1962 lines
92 KiB
C
/*
|
|
* Copyright (c) 2015-present, Yann Collet, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under both the BSD-style license (found in the
|
|
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
|
* in the COPYING file in the root directory of this source tree).
|
|
* You may select, at your option, one of the above-listed licenses.
|
|
*/
|
|
|
|
|
|
/*-************************************
|
|
* Compiler specific
|
|
**************************************/
|
|
#ifdef _MSC_VER /* Visual Studio */
|
|
# define _CRT_SECURE_NO_WARNINGS /* fgets */
|
|
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
|
# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */
|
|
#endif
|
|
|
|
|
|
/*-************************************
|
|
* Includes
|
|
**************************************/
|
|
#include <stdlib.h> /* free */
|
|
#include <stdio.h> /* fgets, sscanf */
|
|
#include <string.h> /* strcmp */
|
|
#include <assert.h>
|
|
#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressContinue, ZSTD_compressBlock */
|
|
#include "zstd.h" /* ZSTD_VERSION_STRING */
|
|
#include "zstd_errors.h" /* ZSTD_getErrorCode */
|
|
#include "zstdmt_compress.h"
|
|
#define ZDICT_STATIC_LINKING_ONLY
|
|
#include "zdict.h" /* ZDICT_trainFromBuffer */
|
|
#include "datagen.h" /* RDG_genBuffer */
|
|
#include "mem.h"
|
|
#define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */
|
|
#include "xxhash.h" /* XXH64 */
|
|
#include "util.h"
|
|
|
|
|
|
/*-************************************
|
|
* Constants
|
|
**************************************/
|
|
#define KB *(1U<<10)
|
|
#define MB *(1U<<20)
|
|
#define GB *(1U<<30)
|
|
|
|
static const U32 FUZ_compressibility_default = 50;
|
|
static const U32 nbTestsDefault = 30000;
|
|
|
|
|
|
/*-************************************
|
|
* Display Macros
|
|
**************************************/
|
|
#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
|
|
#define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); }
|
|
static U32 g_displayLevel = 2;
|
|
|
|
static const U64 g_refreshRate = SEC_TO_MICRO / 6;
|
|
static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER;
|
|
|
|
#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
|
|
if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \
|
|
{ g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \
|
|
if (g_displayLevel>=4) fflush(stderr); } }
|
|
|
|
|
|
/*-*******************************************************
|
|
* Compile time test
|
|
*********************************************************/
|
|
#undef MIN
|
|
#undef MAX
|
|
void FUZ_bug976(void)
|
|
{ /* these constants shall not depend on MIN() macro */
|
|
assert(ZSTD_HASHLOG_MAX < 31);
|
|
assert(ZSTD_CHAINLOG_MAX < 31);
|
|
}
|
|
|
|
|
|
/*-*******************************************************
|
|
* Internal functions
|
|
*********************************************************/
|
|
#define MIN(a,b) ((a)<(b)?(a):(b))
|
|
#define MAX(a,b) ((a)>(b)?(a):(b))
|
|
|
|
#define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r)))
|
|
static unsigned FUZ_rand(unsigned* src)
|
|
{
|
|
static const U32 prime1 = 2654435761U;
|
|
static const U32 prime2 = 2246822519U;
|
|
U32 rand32 = *src;
|
|
rand32 *= prime1;
|
|
rand32 += prime2;
|
|
rand32 = FUZ_rotl32(rand32, 13);
|
|
*src = rand32;
|
|
return rand32 >> 5;
|
|
}
|
|
|
|
static unsigned FUZ_highbit32(U32 v32)
|
|
{
|
|
unsigned nbBits = 0;
|
|
if (v32==0) return 0;
|
|
while (v32) v32 >>= 1, nbBits++;
|
|
return nbBits;
|
|
}
|
|
|
|
|
|
/*=============================================
|
|
* Test macros
|
|
=============================================*/
|
|
#define CHECK_Z(f) { \
|
|
size_t const err = f; \
|
|
if (ZSTD_isError(err)) { \
|
|
DISPLAY("Error => %s : %s ", \
|
|
#f, ZSTD_getErrorName(err)); \
|
|
exit(1); \
|
|
} }
|
|
|
|
#define CHECK_V(var, fn) size_t const var = fn; if (ZSTD_isError(var)) goto _output_error
|
|
#define CHECK(fn) { CHECK_V(err, fn); }
|
|
#define CHECKPLUS(var, fn, more) { CHECK_V(var, fn); more; }
|
|
|
|
#define CHECK_EQ(lhs, rhs) { \
|
|
if ((lhs) != (rhs)) { \
|
|
DISPLAY("Error L%u => %s != %s ", __LINE__, #lhs, #rhs); \
|
|
goto _output_error; \
|
|
} \
|
|
}
|
|
|
|
|
|
/*=============================================
|
|
* Memory Tests
|
|
=============================================*/
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
|
|
#include <malloc/malloc.h> /* malloc_size */
|
|
|
|
typedef struct {
|
|
unsigned long long totalMalloc;
|
|
size_t currentMalloc;
|
|
size_t peakMalloc;
|
|
unsigned nbMalloc;
|
|
unsigned nbFree;
|
|
} mallocCounter_t;
|
|
|
|
static const mallocCounter_t INIT_MALLOC_COUNTER = { 0, 0, 0, 0, 0 };
|
|
|
|
static void* FUZ_mallocDebug(void* counter, size_t size)
|
|
{
|
|
mallocCounter_t* const mcPtr = (mallocCounter_t*)counter;
|
|
void* const ptr = malloc(size);
|
|
if (ptr==NULL) return NULL;
|
|
DISPLAYLEVEL(4, "allocating %u KB => effectively %u KB \n",
|
|
(U32)(size >> 10), (U32)(malloc_size(ptr) >> 10)); /* OS-X specific */
|
|
mcPtr->totalMalloc += size;
|
|
mcPtr->currentMalloc += size;
|
|
if (mcPtr->currentMalloc > mcPtr->peakMalloc)
|
|
mcPtr->peakMalloc = mcPtr->currentMalloc;
|
|
mcPtr->nbMalloc += 1;
|
|
return ptr;
|
|
}
|
|
|
|
static void FUZ_freeDebug(void* counter, void* address)
|
|
{
|
|
mallocCounter_t* const mcPtr = (mallocCounter_t*)counter;
|
|
DISPLAYLEVEL(4, "freeing %u KB \n", (U32)(malloc_size(address) >> 10));
|
|
mcPtr->nbFree += 1;
|
|
mcPtr->currentMalloc -= malloc_size(address); /* OS-X specific */
|
|
free(address);
|
|
}
|
|
|
|
static void FUZ_displayMallocStats(mallocCounter_t count)
|
|
{
|
|
DISPLAYLEVEL(3, "peak:%6u KB, nbMallocs:%2u, total:%6u KB \n",
|
|
(U32)(count.peakMalloc >> 10),
|
|
count.nbMalloc,
|
|
(U32)(count.totalMalloc >> 10));
|
|
}
|
|
|
|
static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part)
|
|
{
|
|
size_t const inSize = 64 MB + 16 MB + 4 MB + 1 MB + 256 KB + 64 KB; /* 85.3 MB */
|
|
size_t const outSize = ZSTD_compressBound(inSize);
|
|
void* const inBuffer = malloc(inSize);
|
|
void* const outBuffer = malloc(outSize);
|
|
|
|
/* test only played in verbose mode, as they are long */
|
|
if (g_displayLevel<3) return 0;
|
|
|
|
/* Create compressible noise */
|
|
if (!inBuffer || !outBuffer) {
|
|
DISPLAY("Not enough memory, aborting\n");
|
|
exit(1);
|
|
}
|
|
RDG_genBuffer(inBuffer, inSize, compressibility, 0. /*auto*/, seed);
|
|
|
|
/* simple compression tests */
|
|
if (part <= 1)
|
|
{ int compressionLevel;
|
|
for (compressionLevel=1; compressionLevel<=6; compressionLevel++) {
|
|
mallocCounter_t malcount = INIT_MALLOC_COUNTER;
|
|
ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount };
|
|
ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem);
|
|
CHECK_Z( ZSTD_compressCCtx(cctx, outBuffer, outSize, inBuffer, inSize, compressionLevel) );
|
|
ZSTD_freeCCtx(cctx);
|
|
DISPLAYLEVEL(3, "compressCCtx level %i : ", compressionLevel);
|
|
FUZ_displayMallocStats(malcount);
|
|
} }
|
|
|
|
/* streaming compression tests */
|
|
if (part <= 2)
|
|
{ int compressionLevel;
|
|
for (compressionLevel=1; compressionLevel<=6; compressionLevel++) {
|
|
mallocCounter_t malcount = INIT_MALLOC_COUNTER;
|
|
ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount };
|
|
ZSTD_CCtx* const cstream = ZSTD_createCStream_advanced(cMem);
|
|
ZSTD_outBuffer out = { outBuffer, outSize, 0 };
|
|
ZSTD_inBuffer in = { inBuffer, inSize, 0 };
|
|
CHECK_Z( ZSTD_initCStream(cstream, compressionLevel) );
|
|
CHECK_Z( ZSTD_compressStream(cstream, &out, &in) );
|
|
CHECK_Z( ZSTD_endStream(cstream, &out) );
|
|
ZSTD_freeCStream(cstream);
|
|
DISPLAYLEVEL(3, "compressStream level %i : ", compressionLevel);
|
|
FUZ_displayMallocStats(malcount);
|
|
} }
|
|
|
|
/* advanced MT API test */
|
|
if (part <= 3)
|
|
{ U32 nbThreads;
|
|
for (nbThreads=1; nbThreads<=4; nbThreads++) {
|
|
int compressionLevel;
|
|
for (compressionLevel=1; compressionLevel<=6; compressionLevel++) {
|
|
mallocCounter_t malcount = INIT_MALLOC_COUNTER;
|
|
ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount };
|
|
ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem);
|
|
ZSTD_outBuffer out = { outBuffer, outSize, 0 };
|
|
ZSTD_inBuffer in = { inBuffer, inSize, 0 };
|
|
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel) );
|
|
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbWorkers, nbThreads) );
|
|
while ( ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end) ) {}
|
|
ZSTD_freeCCtx(cctx);
|
|
DISPLAYLEVEL(3, "compress_generic,-T%u,end level %i : ",
|
|
nbThreads, compressionLevel);
|
|
FUZ_displayMallocStats(malcount);
|
|
} } }
|
|
|
|
/* advanced MT streaming API test */
|
|
if (part <= 4)
|
|
{ U32 nbThreads;
|
|
for (nbThreads=1; nbThreads<=4; nbThreads++) {
|
|
int compressionLevel;
|
|
for (compressionLevel=1; compressionLevel<=6; compressionLevel++) {
|
|
mallocCounter_t malcount = INIT_MALLOC_COUNTER;
|
|
ZSTD_customMem const cMem = { FUZ_mallocDebug, FUZ_freeDebug, &malcount };
|
|
ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(cMem);
|
|
ZSTD_outBuffer out = { outBuffer, outSize, 0 };
|
|
ZSTD_inBuffer in = { inBuffer, inSize, 0 };
|
|
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (U32)compressionLevel) );
|
|
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbWorkers, nbThreads) );
|
|
CHECK_Z( ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_continue) );
|
|
while ( ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end) ) {}
|
|
ZSTD_freeCCtx(cctx);
|
|
DISPLAYLEVEL(3, "compress_generic,-T%u,continue level %i : ",
|
|
nbThreads, compressionLevel);
|
|
FUZ_displayMallocStats(malcount);
|
|
} } }
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part)
|
|
{
|
|
(void)seed; (void)compressibility; (void)part;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*=============================================
|
|
* Unit tests
|
|
=============================================*/
|
|
|
|
static int basicUnitTests(U32 seed, double compressibility)
|
|
{
|
|
size_t const CNBuffSize = 5 MB;
|
|
void* const CNBuffer = malloc(CNBuffSize);
|
|
size_t const compressedBufferSize = ZSTD_compressBound(CNBuffSize);
|
|
void* const compressedBuffer = malloc(compressedBufferSize);
|
|
void* const decodedBuffer = malloc(CNBuffSize);
|
|
ZSTD_DCtx* dctx = ZSTD_createDCtx();
|
|
int testResult = 0;
|
|
U32 testNb=0;
|
|
size_t cSize;
|
|
|
|
/* Create compressible noise */
|
|
if (!CNBuffer || !compressedBuffer || !decodedBuffer) {
|
|
DISPLAY("Not enough memory, aborting\n");
|
|
testResult = 1;
|
|
goto _end;
|
|
}
|
|
RDG_genBuffer(CNBuffer, CNBuffSize, compressibility, 0., seed);
|
|
|
|
/* Basic tests */
|
|
DISPLAYLEVEL(3, "test%3i : ZSTD_getErrorName : ", testNb++);
|
|
{ const char* errorString = ZSTD_getErrorName(0);
|
|
DISPLAYLEVEL(3, "OK : %s \n", errorString);
|
|
}
|
|
|
|
DISPLAYLEVEL(3, "test%3i : ZSTD_getErrorName with wrong value : ", testNb++);
|
|
{ const char* errorString = ZSTD_getErrorName(499);
|
|
DISPLAYLEVEL(3, "OK : %s \n", errorString);
|
|
}
|
|
|
|
|
|
DISPLAYLEVEL(3, "test%3i : compress %u bytes : ", testNb++, (U32)CNBuffSize);
|
|
{ ZSTD_CCtx* cctx = ZSTD_createCCtx();
|
|
if (cctx==NULL) goto _output_error;
|
|
CHECKPLUS(r, ZSTD_compressCCtx(cctx,
|
|
compressedBuffer, compressedBufferSize,
|
|
CNBuffer, CNBuffSize, 1),
|
|
cSize=r );
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : size of cctx for level 1 : ", testNb++);
|
|
{ size_t const cctxSize = ZSTD_sizeof_CCtx(cctx);
|
|
DISPLAYLEVEL(3, "%u bytes \n", (U32)cctxSize);
|
|
}
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
|
|
|
|
DISPLAYLEVEL(3, "test%3i : ZSTD_getFrameContentSize test : ", testNb++);
|
|
{ unsigned long long const rSize = ZSTD_getFrameContentSize(compressedBuffer, cSize);
|
|
if (rSize != CNBuffSize) goto _output_error;
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : ZSTD_findDecompressedSize test : ", testNb++);
|
|
{ unsigned long long const rSize = ZSTD_findDecompressedSize(compressedBuffer, cSize);
|
|
if (rSize != CNBuffSize) goto _output_error;
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress %u bytes : ", testNb++, (U32)CNBuffSize);
|
|
{ size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize);
|
|
if (r != CNBuffSize) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : check decompressed result : ", testNb++);
|
|
{ size_t u;
|
|
for (u=0; u<CNBuffSize; u++) {
|
|
if (((BYTE*)decodedBuffer)[u] != ((BYTE*)CNBuffer)[u]) goto _output_error;;
|
|
} }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress with null dict : ", testNb++);
|
|
{ size_t const r = ZSTD_decompress_usingDict(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize, NULL, 0);
|
|
if (r != CNBuffSize) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress with null DDict : ", testNb++);
|
|
{ size_t const r = ZSTD_decompress_usingDDict(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize, NULL);
|
|
if (r != CNBuffSize) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress with 1 missing byte : ", testNb++);
|
|
{ size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize-1);
|
|
if (!ZSTD_isError(r)) goto _output_error;
|
|
if (ZSTD_getErrorCode((size_t)r) != ZSTD_error_srcSize_wrong) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress with 1 too much byte : ", testNb++);
|
|
{ size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize+1);
|
|
if (!ZSTD_isError(r)) goto _output_error;
|
|
if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress too large input : ", testNb++);
|
|
{ size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, compressedBufferSize);
|
|
if (!ZSTD_isError(r)) goto _output_error;
|
|
if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3d : check CCtx size after compressing empty input : ", testNb++);
|
|
{ ZSTD_CCtx* cctx = ZSTD_createCCtx();
|
|
size_t const r = ZSTD_compressCCtx(cctx, compressedBuffer, compressedBufferSize, NULL, 0, 19);
|
|
if (ZSTD_isError(r)) goto _output_error;
|
|
if (ZSTD_sizeof_CCtx(cctx) > (1U << 20)) goto _output_error;
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3d : re-use CCtx with expanding block size : ", testNb++);
|
|
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
ZSTD_parameters const params = ZSTD_getParams(1, ZSTD_CONTENTSIZE_UNKNOWN, 0);
|
|
assert(params.fParams.contentSizeFlag == 1); /* block size will be adapted if pledgedSrcSize is enabled */
|
|
CHECK_Z( ZSTD_compressBegin_advanced(cctx, NULL, 0, params, 1 /*pledgedSrcSize*/) );
|
|
CHECK_Z( ZSTD_compressEnd(cctx, compressedBuffer, compressedBufferSize, CNBuffer, 1) ); /* creates a block size of 1 */
|
|
|
|
CHECK_Z( ZSTD_compressBegin_advanced(cctx, NULL, 0, params, ZSTD_CONTENTSIZE_UNKNOWN) ); /* re-use same parameters */
|
|
{ size_t const inSize = 2* 128 KB;
|
|
size_t const outSize = ZSTD_compressBound(inSize);
|
|
CHECK_Z( ZSTD_compressEnd(cctx, compressedBuffer, outSize, CNBuffer, inSize) );
|
|
/* will fail if blockSize is not resized */
|
|
}
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3d : ZSTD_CCtx_getParameter() : ", testNb++);
|
|
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
ZSTD_outBuffer out = {NULL, 0, 0};
|
|
ZSTD_inBuffer in = {NULL, 0, 0};
|
|
unsigned value;
|
|
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_compressionLevel, &value));
|
|
CHECK_EQ(value, 3);
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_hashLog, &value));
|
|
CHECK_EQ(value, 0);
|
|
CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_p_hashLog, ZSTD_HASHLOG_MIN));
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_compressionLevel, &value));
|
|
CHECK_EQ(value, 3);
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_hashLog, &value));
|
|
CHECK_EQ(value, ZSTD_HASHLOG_MIN);
|
|
CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, 7));
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_compressionLevel, &value));
|
|
CHECK_EQ(value, 7);
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_hashLog, &value));
|
|
CHECK_EQ(value, ZSTD_HASHLOG_MIN);
|
|
/* Start a compression job */
|
|
ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_continue);
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_compressionLevel, &value));
|
|
CHECK_EQ(value, 7);
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_hashLog, &value));
|
|
CHECK_EQ(value, ZSTD_HASHLOG_MIN);
|
|
/* Reset the CCtx */
|
|
ZSTD_CCtx_reset(cctx);
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_compressionLevel, &value));
|
|
CHECK_EQ(value, 7);
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_hashLog, &value));
|
|
CHECK_EQ(value, ZSTD_HASHLOG_MIN);
|
|
/* Reset the parameters */
|
|
ZSTD_CCtx_resetParameters(cctx);
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_compressionLevel, &value));
|
|
CHECK_EQ(value, 3);
|
|
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_p_hashLog, &value));
|
|
CHECK_EQ(value, 0);
|
|
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
/* this test is really too long, and should be made faster */
|
|
DISPLAYLEVEL(3, "test%3d : overflow protection with large windowLog : ", testNb++);
|
|
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
ZSTD_parameters params = ZSTD_getParams(-9, ZSTD_CONTENTSIZE_UNKNOWN, 0);
|
|
size_t const nbCompressions = ((1U << 31) / CNBuffSize) + 1; /* ensure U32 overflow protection is triggered */
|
|
size_t cnb;
|
|
assert(cctx != NULL);
|
|
params.fParams.contentSizeFlag = 0;
|
|
params.cParams.windowLog = ZSTD_WINDOWLOG_MAX;
|
|
for (cnb = 0; cnb < nbCompressions; ++cnb) {
|
|
DISPLAYLEVEL(6, "run %zu / %zu \n", cnb, nbCompressions);
|
|
CHECK_Z( ZSTD_compressBegin_advanced(cctx, NULL, 0, params, ZSTD_CONTENTSIZE_UNKNOWN) ); /* re-use same parameters */
|
|
CHECK_Z( ZSTD_compressEnd(cctx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize) );
|
|
}
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3d : size down context : ", testNb++);
|
|
{ ZSTD_CCtx* const largeCCtx = ZSTD_createCCtx();
|
|
assert(largeCCtx != NULL);
|
|
CHECK_Z( ZSTD_compressBegin(largeCCtx, 19) ); /* streaming implies ZSTD_CONTENTSIZE_UNKNOWN, which maximizes memory usage */
|
|
CHECK_Z( ZSTD_compressEnd(largeCCtx, compressedBuffer, compressedBufferSize, CNBuffer, 1) );
|
|
{ size_t const largeCCtxSize = ZSTD_sizeof_CCtx(largeCCtx); /* size of context must be measured after compression */
|
|
{ ZSTD_CCtx* const smallCCtx = ZSTD_createCCtx();
|
|
assert(smallCCtx != NULL);
|
|
CHECK_Z(ZSTD_compressCCtx(smallCCtx, compressedBuffer, compressedBufferSize, CNBuffer, 1, 1));
|
|
{ size_t const smallCCtxSize = ZSTD_sizeof_CCtx(smallCCtx);
|
|
DISPLAYLEVEL(5, "(large) %zuKB > 32*%zuKB (small) : ",
|
|
largeCCtxSize>>10, smallCCtxSize>>10);
|
|
assert(largeCCtxSize > 32* smallCCtxSize); /* note : "too large" definition is handled within zstd_compress.c .
|
|
* make this test case extreme, so that it doesn't depend on a possibly fluctuating definition */
|
|
}
|
|
ZSTD_freeCCtx(smallCCtx);
|
|
}
|
|
{ U32 const maxNbAttempts = 1100; /* nb of usages before triggering size down is handled within zstd_compress.c.
|
|
* currently defined as 128x, but could be adjusted in the future.
|
|
* make this test long enough so that it's not too much tied to the current definition within zstd_compress.c */
|
|
U32 u;
|
|
for (u=0; u<maxNbAttempts; u++) {
|
|
CHECK_Z(ZSTD_compressCCtx(largeCCtx, compressedBuffer, compressedBufferSize, CNBuffer, 1, 1));
|
|
if (ZSTD_sizeof_CCtx(largeCCtx) < largeCCtxSize) break; /* sized down */
|
|
}
|
|
DISPLAYLEVEL(5, "size down after %u attempts : ", u);
|
|
if (u==maxNbAttempts) goto _output_error; /* no sizedown happened */
|
|
}
|
|
}
|
|
ZSTD_freeCCtx(largeCCtx);
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
/* Static CCtx tests */
|
|
#define STATIC_CCTX_LEVEL 3
|
|
DISPLAYLEVEL(3, "test%3i : create static CCtx for level %u :", testNb++, STATIC_CCTX_LEVEL);
|
|
{ size_t const staticCCtxSize = ZSTD_estimateCStreamSize(STATIC_CCTX_LEVEL);
|
|
void* const staticCCtxBuffer = malloc(staticCCtxSize);
|
|
size_t const staticDCtxSize = ZSTD_estimateDCtxSize();
|
|
void* const staticDCtxBuffer = malloc(staticDCtxSize);
|
|
if (staticCCtxBuffer==NULL || staticDCtxBuffer==NULL) {
|
|
free(staticCCtxBuffer);
|
|
free(staticDCtxBuffer);
|
|
DISPLAY("Not enough memory, aborting\n");
|
|
testResult = 1;
|
|
goto _end;
|
|
}
|
|
{ ZSTD_CCtx* staticCCtx = ZSTD_initStaticCCtx(staticCCtxBuffer, staticCCtxSize);
|
|
ZSTD_DCtx* staticDCtx = ZSTD_initStaticDCtx(staticDCtxBuffer, staticDCtxSize);
|
|
if ((staticCCtx==NULL) || (staticDCtx==NULL)) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : init CCtx for level %u : ", testNb++, STATIC_CCTX_LEVEL);
|
|
{ size_t const r = ZSTD_compressBegin(staticCCtx, STATIC_CCTX_LEVEL);
|
|
if (ZSTD_isError(r)) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : simple compression test with static CCtx : ", testNb++);
|
|
CHECKPLUS(r, ZSTD_compressCCtx(staticCCtx,
|
|
compressedBuffer, compressedBufferSize,
|
|
CNBuffer, CNBuffSize, STATIC_CCTX_LEVEL),
|
|
cSize=r );
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n",
|
|
(U32)cSize, (double)cSize/CNBuffSize*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : simple decompression test with static DCtx : ", testNb++);
|
|
{ size_t const r = ZSTD_decompressDCtx(staticDCtx,
|
|
decodedBuffer, CNBuffSize,
|
|
compressedBuffer, cSize);
|
|
if (r != CNBuffSize) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : check decompressed result : ", testNb++);
|
|
{ size_t u;
|
|
for (u=0; u<CNBuffSize; u++) {
|
|
if (((BYTE*)decodedBuffer)[u] != ((BYTE*)CNBuffer)[u])
|
|
goto _output_error;;
|
|
} }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : init CCtx for too large level (must fail) : ", testNb++);
|
|
{ size_t const r = ZSTD_compressBegin(staticCCtx, ZSTD_maxCLevel());
|
|
if (!ZSTD_isError(r)) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : init CCtx for small level %u (should work again) : ", testNb++, 1);
|
|
{ size_t const r = ZSTD_compressBegin(staticCCtx, 1);
|
|
if (ZSTD_isError(r)) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : init CStream for small level %u : ", testNb++, 1);
|
|
{ size_t const r = ZSTD_initCStream(staticCCtx, 1);
|
|
if (ZSTD_isError(r)) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : init CStream with dictionary (should fail) : ", testNb++);
|
|
{ size_t const r = ZSTD_initCStream_usingDict(staticCCtx, CNBuffer, 64 KB, 1);
|
|
if (!ZSTD_isError(r)) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : init DStream (should fail) : ", testNb++);
|
|
{ size_t const r = ZSTD_initDStream(staticDCtx);
|
|
if (ZSTD_isError(r)) goto _output_error; }
|
|
{ ZSTD_outBuffer output = { decodedBuffer, CNBuffSize, 0 };
|
|
ZSTD_inBuffer input = { compressedBuffer, ZSTD_FRAMEHEADERSIZE_MAX+1, 0 };
|
|
size_t const r = ZSTD_decompressStream(staticDCtx, &output, &input);
|
|
if (!ZSTD_isError(r)) goto _output_error;
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
}
|
|
free(staticCCtxBuffer);
|
|
free(staticDCtxBuffer);
|
|
}
|
|
|
|
|
|
/* ZSTDMT simple MT compression test */
|
|
DISPLAYLEVEL(3, "test%3i : create ZSTDMT CCtx : ", testNb++);
|
|
{ ZSTDMT_CCtx* mtctx = ZSTDMT_createCCtx(2);
|
|
if (mtctx==NULL) {
|
|
DISPLAY("mtctx : mot enough memory, aborting \n");
|
|
testResult = 1;
|
|
goto _end;
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : compress %u bytes with 2 threads : ", testNb++, (U32)CNBuffSize);
|
|
CHECKPLUS(r, ZSTDMT_compressCCtx(mtctx,
|
|
compressedBuffer, compressedBufferSize,
|
|
CNBuffer, CNBuffSize,
|
|
1),
|
|
cSize=r );
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompressed size test : ", testNb++);
|
|
{ unsigned long long const rSize = ZSTD_getFrameContentSize(compressedBuffer, cSize);
|
|
if (rSize != CNBuffSize) {
|
|
DISPLAY("ZSTD_getFrameContentSize incorrect : %u != %u \n", (U32)rSize, (U32)CNBuffSize);
|
|
goto _output_error;
|
|
} }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress %u bytes : ", testNb++, (U32)CNBuffSize);
|
|
{ size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize);
|
|
if (r != CNBuffSize) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : check decompressed result : ", testNb++);
|
|
{ size_t u;
|
|
for (u=0; u<CNBuffSize; u++) {
|
|
if (((BYTE*)decodedBuffer)[u] != ((BYTE*)CNBuffer)[u]) goto _output_error;;
|
|
} }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : compress -T2 with checksum : ", testNb++);
|
|
{ ZSTD_parameters params = ZSTD_getParams(1, CNBuffSize, 0);
|
|
params.fParams.checksumFlag = 1;
|
|
params.fParams.contentSizeFlag = 1;
|
|
CHECKPLUS(r, ZSTDMT_compress_advanced(mtctx,
|
|
compressedBuffer, compressedBufferSize,
|
|
CNBuffer, CNBuffSize,
|
|
NULL, params, 3 /*overlapRLog*/),
|
|
cSize=r );
|
|
}
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress %u bytes : ", testNb++, (U32)CNBuffSize);
|
|
{ size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize);
|
|
if (r != CNBuffSize) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
ZSTDMT_freeCCtx(mtctx);
|
|
}
|
|
|
|
|
|
/* Simple API multiframe test */
|
|
DISPLAYLEVEL(3, "test%3i : compress multiple frames : ", testNb++);
|
|
{ size_t off = 0;
|
|
int i;
|
|
int const segs = 4;
|
|
/* only use the first half so we don't push against size limit of compressedBuffer */
|
|
size_t const segSize = (CNBuffSize / 2) / segs;
|
|
for (i = 0; i < segs; i++) {
|
|
CHECK_V(r, ZSTD_compress(
|
|
(BYTE *)compressedBuffer + off, CNBuffSize - off,
|
|
(BYTE *)CNBuffer + segSize * i,
|
|
segSize, 5));
|
|
off += r;
|
|
if (i == segs/2) {
|
|
/* insert skippable frame */
|
|
const U32 skipLen = 129 KB;
|
|
MEM_writeLE32((BYTE*)compressedBuffer + off, ZSTD_MAGIC_SKIPPABLE_START);
|
|
MEM_writeLE32((BYTE*)compressedBuffer + off + 4, skipLen);
|
|
off += skipLen + ZSTD_skippableHeaderSize;
|
|
}
|
|
}
|
|
cSize = off;
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : get decompressed size of multiple frames : ", testNb++);
|
|
{ unsigned long long const r = ZSTD_findDecompressedSize(compressedBuffer, cSize);
|
|
if (r != CNBuffSize / 2) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress multiple frames : ", testNb++);
|
|
{ CHECK_V(r, ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize));
|
|
if (r != CNBuffSize / 2) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : check decompressed result : ", testNb++);
|
|
if (memcmp(decodedBuffer, CNBuffer, CNBuffSize / 2) != 0) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
/* Dictionary and CCtx Duplication tests */
|
|
{ ZSTD_CCtx* const ctxOrig = ZSTD_createCCtx();
|
|
ZSTD_CCtx* const ctxDuplicated = ZSTD_createCCtx();
|
|
static const size_t dictSize = 551;
|
|
|
|
DISPLAYLEVEL(3, "test%3i : copy context too soon : ", testNb++);
|
|
{ size_t const copyResult = ZSTD_copyCCtx(ctxDuplicated, ctxOrig, 0);
|
|
if (!ZSTD_isError(copyResult)) goto _output_error; } /* error must be detected */
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : load dictionary into context : ", testNb++);
|
|
CHECK( ZSTD_compressBegin_usingDict(ctxOrig, CNBuffer, dictSize, 2) );
|
|
CHECK( ZSTD_copyCCtx(ctxDuplicated, ctxOrig, 0) ); /* Begin_usingDict implies unknown srcSize, so match that */
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : compress with flat dictionary : ", testNb++);
|
|
cSize = 0;
|
|
CHECKPLUS(r, ZSTD_compressEnd(ctxOrig, compressedBuffer, compressedBufferSize,
|
|
(const char*)CNBuffer + dictSize, CNBuffSize - dictSize),
|
|
cSize += r);
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : frame built with flat dictionary should be decompressible : ", testNb++);
|
|
CHECKPLUS(r, ZSTD_decompress_usingDict(dctx,
|
|
decodedBuffer, CNBuffSize,
|
|
compressedBuffer, cSize,
|
|
CNBuffer, dictSize),
|
|
if (r != CNBuffSize - dictSize) goto _output_error);
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : compress with duplicated context : ", testNb++);
|
|
{ size_t const cSizeOrig = cSize;
|
|
cSize = 0;
|
|
CHECKPLUS(r, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, compressedBufferSize,
|
|
(const char*)CNBuffer + dictSize, CNBuffSize - dictSize),
|
|
cSize += r);
|
|
if (cSize != cSizeOrig) goto _output_error; /* should be identical ==> same size */
|
|
}
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : frame built with duplicated context should be decompressible : ", testNb++);
|
|
CHECKPLUS(r, ZSTD_decompress_usingDict(dctx,
|
|
decodedBuffer, CNBuffSize,
|
|
compressedBuffer, cSize,
|
|
CNBuffer, dictSize),
|
|
if (r != CNBuffSize - dictSize) goto _output_error);
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress with DDict : ", testNb++);
|
|
{ ZSTD_DDict* const ddict = ZSTD_createDDict(CNBuffer, dictSize);
|
|
size_t const r = ZSTD_decompress_usingDDict(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize, ddict);
|
|
if (r != CNBuffSize - dictSize) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK (size of DDict : %u) \n", (U32)ZSTD_sizeof_DDict(ddict));
|
|
ZSTD_freeDDict(ddict);
|
|
}
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress with static DDict : ", testNb++);
|
|
{ size_t const ddictBufferSize = ZSTD_estimateDDictSize(dictSize, ZSTD_dlm_byCopy);
|
|
void* ddictBuffer = malloc(ddictBufferSize);
|
|
if (ddictBuffer == NULL) goto _output_error;
|
|
{ const ZSTD_DDict* const ddict = ZSTD_initStaticDDict(ddictBuffer, ddictBufferSize, CNBuffer, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto);
|
|
size_t const r = ZSTD_decompress_usingDDict(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize, ddict);
|
|
if (r != CNBuffSize - dictSize) goto _output_error;
|
|
}
|
|
free(ddictBuffer);
|
|
DISPLAYLEVEL(3, "OK (size of static DDict : %u) \n", (U32)ddictBufferSize);
|
|
}
|
|
|
|
DISPLAYLEVEL(3, "test%3i : check content size on duplicated context : ", testNb++);
|
|
{ size_t const testSize = CNBuffSize / 3;
|
|
{ ZSTD_parameters p = ZSTD_getParams(2, testSize, dictSize);
|
|
p.fParams.contentSizeFlag = 1;
|
|
CHECK( ZSTD_compressBegin_advanced(ctxOrig, CNBuffer, dictSize, p, testSize-1) );
|
|
}
|
|
CHECK( ZSTD_copyCCtx(ctxDuplicated, ctxOrig, testSize) );
|
|
|
|
CHECKPLUS(r, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, ZSTD_compressBound(testSize),
|
|
(const char*)CNBuffer + dictSize, testSize),
|
|
cSize = r);
|
|
{ ZSTD_frameHeader zfh;
|
|
if (ZSTD_getFrameHeader(&zfh, compressedBuffer, cSize)) goto _output_error;
|
|
if ((zfh.frameContentSize != testSize) && (zfh.frameContentSize != 0)) goto _output_error;
|
|
} }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
ZSTD_freeCCtx(ctxOrig);
|
|
ZSTD_freeCCtx(ctxDuplicated);
|
|
}
|
|
|
|
/* Dictionary and dictBuilder tests */
|
|
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
size_t const dictBufferCapacity = 16 KB;
|
|
void* dictBuffer = malloc(dictBufferCapacity);
|
|
size_t const totalSampleSize = 1 MB;
|
|
size_t const sampleUnitSize = 8 KB;
|
|
U32 const nbSamples = (U32)(totalSampleSize / sampleUnitSize);
|
|
size_t* const samplesSizes = (size_t*) malloc(nbSamples * sizeof(size_t));
|
|
size_t dictSize;
|
|
U32 dictID;
|
|
|
|
if (dictBuffer==NULL || samplesSizes==NULL) {
|
|
free(dictBuffer);
|
|
free(samplesSizes);
|
|
goto _output_error;
|
|
}
|
|
|
|
DISPLAYLEVEL(3, "test%3i : dictBuilder on cyclic data : ", testNb++);
|
|
assert(compressedBufferSize >= totalSampleSize);
|
|
{ U32 u; for (u=0; u<totalSampleSize; u++) ((BYTE*)decodedBuffer)[u] = (BYTE)u; }
|
|
{ U32 u; for (u=0; u<nbSamples; u++) samplesSizes[u] = sampleUnitSize; }
|
|
{ size_t const sDictSize = ZDICT_trainFromBuffer(dictBuffer, dictBufferCapacity,
|
|
decodedBuffer, samplesSizes, nbSamples);
|
|
if (ZDICT_isError(sDictSize)) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK, created dictionary of size %u \n", (U32)sDictSize);
|
|
}
|
|
|
|
DISPLAYLEVEL(3, "test%3i : dictBuilder : ", testNb++);
|
|
{ U32 u; for (u=0; u<nbSamples; u++) samplesSizes[u] = sampleUnitSize; }
|
|
dictSize = ZDICT_trainFromBuffer(dictBuffer, dictBufferCapacity,
|
|
CNBuffer, samplesSizes, nbSamples);
|
|
if (ZDICT_isError(dictSize)) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK, created dictionary of size %u \n", (U32)dictSize);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : check dictID : ", testNb++);
|
|
dictID = ZDICT_getDictID(dictBuffer, dictSize);
|
|
if (dictID==0) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK : %u \n", dictID);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : compress with dictionary : ", testNb++);
|
|
cSize = ZSTD_compress_usingDict(cctx, compressedBuffer, compressedBufferSize,
|
|
CNBuffer, CNBuffSize,
|
|
dictBuffer, dictSize, 4);
|
|
if (ZSTD_isError(cSize)) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : retrieve dictID from dictionary : ", testNb++);
|
|
{ U32 const did = ZSTD_getDictID_fromDict(dictBuffer, dictSize);
|
|
if (did != dictID) goto _output_error; /* non-conformant (content-only) dictionary */
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : retrieve dictID from frame : ", testNb++);
|
|
{ U32 const did = ZSTD_getDictID_fromFrame(compressedBuffer, cSize);
|
|
if (did != dictID) goto _output_error; /* non-conformant (content-only) dictionary */
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : frame built with dictionary should be decompressible : ", testNb++);
|
|
CHECKPLUS(r, ZSTD_decompress_usingDict(dctx,
|
|
decodedBuffer, CNBuffSize,
|
|
compressedBuffer, cSize,
|
|
dictBuffer, dictSize),
|
|
if (r != CNBuffSize) goto _output_error);
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : estimate CDict size : ", testNb++);
|
|
{ ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBuffSize, dictSize);
|
|
size_t const estimatedSize = ZSTD_estimateCDictSize_advanced(dictSize, cParams, ZSTD_dlm_byRef);
|
|
DISPLAYLEVEL(3, "OK : %u \n", (U32)estimatedSize);
|
|
}
|
|
|
|
DISPLAYLEVEL(3, "test%3i : compress with CDict ", testNb++);
|
|
{ ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBuffSize, dictSize);
|
|
ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize,
|
|
ZSTD_dlm_byRef, ZSTD_dct_auto,
|
|
cParams, ZSTD_defaultCMem);
|
|
DISPLAYLEVEL(3, "(size : %u) : ", (U32)ZSTD_sizeof_CDict(cdict));
|
|
cSize = ZSTD_compress_usingCDict(cctx, compressedBuffer, compressedBufferSize,
|
|
CNBuffer, CNBuffSize, cdict);
|
|
ZSTD_freeCDict(cdict);
|
|
if (ZSTD_isError(cSize)) goto _output_error;
|
|
}
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : retrieve dictID from frame : ", testNb++);
|
|
{ U32 const did = ZSTD_getDictID_fromFrame(compressedBuffer, cSize);
|
|
if (did != dictID) goto _output_error; /* non-conformant (content-only) dictionary */
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : frame built with dictionary should be decompressible : ", testNb++);
|
|
CHECKPLUS(r, ZSTD_decompress_usingDict(dctx,
|
|
decodedBuffer, CNBuffSize,
|
|
compressedBuffer, cSize,
|
|
dictBuffer, dictSize),
|
|
if (r != CNBuffSize) goto _output_error);
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : compress with static CDict : ", testNb++);
|
|
{ int const maxLevel = ZSTD_maxCLevel();
|
|
int level;
|
|
for (level = 1; level <= maxLevel; ++level) {
|
|
ZSTD_compressionParameters const cParams = ZSTD_getCParams(level, CNBuffSize, dictSize);
|
|
size_t const cdictSize = ZSTD_estimateCDictSize_advanced(dictSize, cParams, ZSTD_dlm_byCopy);
|
|
void* const cdictBuffer = malloc(cdictSize);
|
|
if (cdictBuffer==NULL) goto _output_error;
|
|
{ const ZSTD_CDict* const cdict = ZSTD_initStaticCDict(
|
|
cdictBuffer, cdictSize,
|
|
dictBuffer, dictSize,
|
|
ZSTD_dlm_byCopy, ZSTD_dct_auto,
|
|
cParams);
|
|
if (cdict == NULL) {
|
|
DISPLAY("ZSTD_initStaticCDict failed ");
|
|
goto _output_error;
|
|
}
|
|
cSize = ZSTD_compress_usingCDict(cctx,
|
|
compressedBuffer, compressedBufferSize,
|
|
CNBuffer, MIN(10 KB, CNBuffSize), cdict);
|
|
if (ZSTD_isError(cSize)) {
|
|
DISPLAY("ZSTD_compress_usingCDict failed ");
|
|
goto _output_error;
|
|
} }
|
|
free(cdictBuffer);
|
|
} }
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : ZSTD_compress_usingCDict_advanced, no contentSize, no dictID : ", testNb++);
|
|
{ ZSTD_frameParameters const fParams = { 0 /* frameSize */, 1 /* checksum */, 1 /* noDictID*/ };
|
|
ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBuffSize, dictSize);
|
|
ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto, cParams, ZSTD_defaultCMem);
|
|
cSize = ZSTD_compress_usingCDict_advanced(cctx, compressedBuffer, compressedBufferSize,
|
|
CNBuffer, CNBuffSize, cdict, fParams);
|
|
ZSTD_freeCDict(cdict);
|
|
if (ZSTD_isError(cSize)) goto _output_error;
|
|
}
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : try retrieving contentSize from frame : ", testNb++);
|
|
{ U64 const contentSize = ZSTD_getFrameContentSize(compressedBuffer, cSize);
|
|
if (contentSize != ZSTD_CONTENTSIZE_UNKNOWN) goto _output_error;
|
|
}
|
|
DISPLAYLEVEL(3, "OK (unknown)\n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : frame built without dictID should be decompressible : ", testNb++);
|
|
CHECKPLUS(r, ZSTD_decompress_usingDict(dctx,
|
|
decodedBuffer, CNBuffSize,
|
|
compressedBuffer, cSize,
|
|
dictBuffer, dictSize),
|
|
if (r != CNBuffSize) goto _output_error);
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : ZSTD_compress_advanced, no dictID : ", testNb++);
|
|
{ ZSTD_parameters p = ZSTD_getParams(3, CNBuffSize, dictSize);
|
|
p.fParams.noDictIDFlag = 1;
|
|
cSize = ZSTD_compress_advanced(cctx, compressedBuffer, compressedBufferSize,
|
|
CNBuffer, CNBuffSize,
|
|
dictBuffer, dictSize, p);
|
|
if (ZSTD_isError(cSize)) goto _output_error;
|
|
}
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : frame built without dictID should be decompressible : ", testNb++);
|
|
CHECKPLUS(r, ZSTD_decompress_usingDict(dctx,
|
|
decodedBuffer, CNBuffSize,
|
|
compressedBuffer, cSize,
|
|
dictBuffer, dictSize),
|
|
if (r != CNBuffSize) goto _output_error);
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : dictionary containing only header should return error : ", testNb++);
|
|
{
|
|
const size_t ret = ZSTD_decompress_usingDict(
|
|
dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize,
|
|
"\x37\xa4\x30\xec\x11\x22\x33\x44", 8);
|
|
if (ZSTD_getErrorCode(ret) != ZSTD_error_dictionary_corrupted) goto _output_error;
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : Building cdict w/ ZSTD_dm_fullDict on a good dictionary : ", testNb++);
|
|
{ ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBuffSize, dictSize);
|
|
ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dct_fullDict, cParams, ZSTD_defaultCMem);
|
|
if (cdict==NULL) goto _output_error;
|
|
ZSTD_freeCDict(cdict);
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : Building cdict w/ ZSTD_dm_fullDict on a rawContent (must fail) : ", testNb++);
|
|
{ ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBuffSize, dictSize);
|
|
ZSTD_CDict* const cdict = ZSTD_createCDict_advanced((const char*)dictBuffer+1, dictSize-1, ZSTD_dlm_byRef, ZSTD_dct_fullDict, cParams, ZSTD_defaultCMem);
|
|
if (cdict!=NULL) goto _output_error;
|
|
ZSTD_freeCDict(cdict);
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : Loading rawContent starting with dict header w/ ZSTD_dm_auto should fail : ", testNb++);
|
|
{
|
|
size_t ret;
|
|
MEM_writeLE32((char*)dictBuffer+2, ZSTD_MAGIC_DICTIONARY);
|
|
ret = ZSTD_CCtx_loadDictionary_advanced(
|
|
cctx, (const char*)dictBuffer+2, dictSize-2, ZSTD_dlm_byRef, ZSTD_dct_auto);
|
|
if (!ZSTD_isError(ret)) goto _output_error;
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : Loading rawContent starting with dict header w/ ZSTD_dm_rawContent should pass : ", testNb++);
|
|
{
|
|
size_t ret;
|
|
MEM_writeLE32((char*)dictBuffer+2, ZSTD_MAGIC_DICTIONARY);
|
|
ret = ZSTD_CCtx_loadDictionary_advanced(
|
|
cctx, (const char*)dictBuffer+2, dictSize-2, ZSTD_dlm_byRef, ZSTD_dct_rawContent);
|
|
if (ZSTD_isError(ret)) goto _output_error;
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : Dictionary with non-default repcodes : ", testNb++);
|
|
{ U32 u; for (u=0; u<nbSamples; u++) samplesSizes[u] = sampleUnitSize; }
|
|
dictSize = ZDICT_trainFromBuffer(dictBuffer, dictSize,
|
|
CNBuffer, samplesSizes, nbSamples);
|
|
if (ZDICT_isError(dictSize)) goto _output_error;
|
|
/* Set all the repcodes to non-default */
|
|
{
|
|
BYTE* dictPtr = (BYTE*)dictBuffer;
|
|
BYTE* dictLimit = dictPtr + dictSize - 12;
|
|
/* Find the repcodes */
|
|
while (dictPtr < dictLimit &&
|
|
(MEM_readLE32(dictPtr) != 1 || MEM_readLE32(dictPtr + 4) != 4 ||
|
|
MEM_readLE32(dictPtr + 8) != 8)) {
|
|
++dictPtr;
|
|
}
|
|
if (dictPtr >= dictLimit) goto _output_error;
|
|
MEM_writeLE32(dictPtr + 0, 10);
|
|
MEM_writeLE32(dictPtr + 4, 10);
|
|
MEM_writeLE32(dictPtr + 8, 10);
|
|
/* Set the last 8 bytes to 'x' */
|
|
memset((BYTE*)dictBuffer + dictSize - 8, 'x', 8);
|
|
}
|
|
/* The optimal parser checks all the repcodes.
|
|
* Make sure at least one is a match >= targetLength so that it is
|
|
* immediately chosen. This will make sure that the compressor and
|
|
* decompressor agree on at least one of the repcodes.
|
|
*/
|
|
{ size_t dSize;
|
|
BYTE data[1024];
|
|
ZSTD_compressionParameters const cParams = ZSTD_getCParams(19, CNBuffSize, dictSize);
|
|
ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize,
|
|
ZSTD_dlm_byRef, ZSTD_dct_auto,
|
|
cParams, ZSTD_defaultCMem);
|
|
memset(data, 'x', sizeof(data));
|
|
cSize = ZSTD_compress_usingCDict(cctx, compressedBuffer, compressedBufferSize,
|
|
data, sizeof(data), cdict);
|
|
ZSTD_freeCDict(cdict);
|
|
if (ZSTD_isError(cSize)) { DISPLAYLEVEL(5, "Compression error %s : ", ZSTD_getErrorName(cSize)); goto _output_error; }
|
|
dSize = ZSTD_decompress_usingDict(dctx, decodedBuffer, sizeof(data), compressedBuffer, cSize, dictBuffer, dictSize);
|
|
if (ZSTD_isError(dSize)) { DISPLAYLEVEL(5, "Decompression error %s : ", ZSTD_getErrorName(dSize)); goto _output_error; }
|
|
if (memcmp(data, decodedBuffer, sizeof(data))) { DISPLAYLEVEL(5, "Data corruption : "); goto _output_error; }
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
ZSTD_freeCCtx(cctx);
|
|
free(dictBuffer);
|
|
free(samplesSizes);
|
|
}
|
|
|
|
/* COVER dictionary builder tests */
|
|
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
size_t dictSize = 16 KB;
|
|
size_t optDictSize = dictSize;
|
|
void* dictBuffer = malloc(dictSize);
|
|
size_t const totalSampleSize = 1 MB;
|
|
size_t const sampleUnitSize = 8 KB;
|
|
U32 const nbSamples = (U32)(totalSampleSize / sampleUnitSize);
|
|
size_t* const samplesSizes = (size_t*) malloc(nbSamples * sizeof(size_t));
|
|
ZDICT_cover_params_t params;
|
|
U32 dictID;
|
|
|
|
if (dictBuffer==NULL || samplesSizes==NULL) {
|
|
free(dictBuffer);
|
|
free(samplesSizes);
|
|
goto _output_error;
|
|
}
|
|
|
|
DISPLAYLEVEL(3, "test%3i : ZDICT_trainFromBuffer_cover : ", testNb++);
|
|
{ U32 u; for (u=0; u<nbSamples; u++) samplesSizes[u] = sampleUnitSize; }
|
|
memset(¶ms, 0, sizeof(params));
|
|
params.d = 1 + (FUZ_rand(&seed) % 16);
|
|
params.k = params.d + (FUZ_rand(&seed) % 256);
|
|
dictSize = ZDICT_trainFromBuffer_cover(dictBuffer, dictSize,
|
|
CNBuffer, samplesSizes, nbSamples,
|
|
params);
|
|
if (ZDICT_isError(dictSize)) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK, created dictionary of size %u \n", (U32)dictSize);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : check dictID : ", testNb++);
|
|
dictID = ZDICT_getDictID(dictBuffer, dictSize);
|
|
if (dictID==0) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK : %u \n", dictID);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : ZDICT_optimizeTrainFromBuffer_cover : ", testNb++);
|
|
memset(¶ms, 0, sizeof(params));
|
|
params.steps = 4;
|
|
optDictSize = ZDICT_optimizeTrainFromBuffer_cover(dictBuffer, optDictSize,
|
|
CNBuffer, samplesSizes,
|
|
nbSamples / 4, ¶ms);
|
|
if (ZDICT_isError(optDictSize)) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK, created dictionary of size %u \n", (U32)optDictSize);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : check dictID : ", testNb++);
|
|
dictID = ZDICT_getDictID(dictBuffer, optDictSize);
|
|
if (dictID==0) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK : %u \n", dictID);
|
|
|
|
ZSTD_freeCCtx(cctx);
|
|
free(dictBuffer);
|
|
free(samplesSizes);
|
|
}
|
|
|
|
/* Decompression defense tests */
|
|
DISPLAYLEVEL(3, "test%3i : Check input length for magic number : ", testNb++);
|
|
{ size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, CNBuffer, 3); /* too small input */
|
|
if (!ZSTD_isError(r)) goto _output_error;
|
|
if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : Check magic Number : ", testNb++);
|
|
((char*)(CNBuffer))[0] = 1;
|
|
{ size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, CNBuffer, 4);
|
|
if (!ZSTD_isError(r)) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
/* content size verification test */
|
|
DISPLAYLEVEL(3, "test%3i : Content size verification : ", testNb++);
|
|
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
size_t const srcSize = 5000;
|
|
size_t const wrongSrcSize = (srcSize + 1000);
|
|
ZSTD_parameters params = ZSTD_getParams(1, wrongSrcSize, 0);
|
|
params.fParams.contentSizeFlag = 1;
|
|
CHECK( ZSTD_compressBegin_advanced(cctx, NULL, 0, params, wrongSrcSize) );
|
|
{ size_t const result = ZSTD_compressEnd(cctx, decodedBuffer, CNBuffSize, CNBuffer, srcSize);
|
|
if (!ZSTD_isError(result)) goto _output_error;
|
|
if (ZSTD_getErrorCode(result) != ZSTD_error_srcSize_wrong) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK : %s \n", ZSTD_getErrorName(result));
|
|
}
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
|
|
/* negative compression level test : ensure simple API and advanced API produce same result */
|
|
DISPLAYLEVEL(3, "test%3i : negative compression level : ", testNb++);
|
|
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
size_t const srcSize = CNBuffSize / 5;
|
|
int const compressionLevel = -1;
|
|
|
|
assert(cctx != NULL);
|
|
{ ZSTD_parameters const params = ZSTD_getParams(compressionLevel, srcSize, 0);
|
|
size_t const cSize_1pass = ZSTD_compress_advanced(cctx,
|
|
compressedBuffer, compressedBufferSize,
|
|
CNBuffer, srcSize,
|
|
NULL, 0,
|
|
params);
|
|
if (ZSTD_isError(cSize_1pass)) goto _output_error;
|
|
|
|
CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, (unsigned)compressionLevel) );
|
|
{ ZSTD_inBuffer in = { CNBuffer, srcSize, 0 };
|
|
ZSTD_outBuffer out = { compressedBuffer, compressedBufferSize, 0 };
|
|
size_t const compressionResult = ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end);
|
|
DISPLAYLEVEL(5, "simple=%zu vs %zu=advanced : ", cSize_1pass, out.pos);
|
|
if (ZSTD_isError(compressionResult)) goto _output_error;
|
|
if (out.pos != cSize_1pass) goto _output_error;
|
|
} }
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
/* parameters order test */
|
|
{ size_t const inputSize = CNBuffSize / 2;
|
|
U64 xxh64;
|
|
|
|
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
DISPLAYLEVEL(3, "test%3i : parameters in order : ", testNb++);
|
|
assert(cctx != NULL);
|
|
CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, 2) );
|
|
CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_enableLongDistanceMatching, 1) );
|
|
CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_windowLog, 18) );
|
|
{ ZSTD_inBuffer in = { CNBuffer, inputSize, 0 };
|
|
ZSTD_outBuffer out = { compressedBuffer, ZSTD_compressBound(inputSize), 0 };
|
|
size_t const result = ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end);
|
|
if (result != 0) goto _output_error;
|
|
if (in.pos != in.size) goto _output_error;
|
|
cSize = out.pos;
|
|
xxh64 = XXH64(out.dst, out.pos, 0);
|
|
}
|
|
DISPLAYLEVEL(3, "OK (compress : %u -> %u bytes)\n", (U32)inputSize, (U32)cSize);
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
|
|
{ ZSTD_CCtx* cctx = ZSTD_createCCtx();
|
|
DISPLAYLEVEL(3, "test%3i : parameters disordered : ", testNb++);
|
|
CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_windowLog, 18) );
|
|
CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_enableLongDistanceMatching, 1) );
|
|
CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_compressionLevel, 2) );
|
|
{ ZSTD_inBuffer in = { CNBuffer, inputSize, 0 };
|
|
ZSTD_outBuffer out = { compressedBuffer, ZSTD_compressBound(inputSize), 0 };
|
|
size_t const result = ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end);
|
|
if (result != 0) goto _output_error;
|
|
if (in.pos != in.size) goto _output_error;
|
|
if (out.pos != cSize) goto _output_error; /* must result in same compressed result, hence same size */
|
|
if (XXH64(out.dst, out.pos, 0) != xxh64) goto _output_error; /* must result in exactly same content, hence same hash */
|
|
DISPLAYLEVEL(3, "OK (compress : %u -> %u bytes)\n", (U32)inputSize, (U32)out.pos);
|
|
}
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
}
|
|
|
|
/* custom formats tests */
|
|
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
size_t const inputSize = CNBuffSize / 2; /* won't cause pb with small dict size */
|
|
|
|
/* basic block compression */
|
|
DISPLAYLEVEL(3, "test%3i : magic-less format test : ", testNb++);
|
|
CHECK( ZSTD_CCtx_setParameter(cctx, ZSTD_p_format, ZSTD_f_zstd1_magicless) );
|
|
{ ZSTD_inBuffer in = { CNBuffer, inputSize, 0 };
|
|
ZSTD_outBuffer out = { compressedBuffer, ZSTD_compressBound(inputSize), 0 };
|
|
size_t const result = ZSTD_compress_generic(cctx, &out, &in, ZSTD_e_end);
|
|
if (result != 0) goto _output_error;
|
|
if (in.pos != in.size) goto _output_error;
|
|
cSize = out.pos;
|
|
}
|
|
DISPLAYLEVEL(3, "OK (compress : %u -> %u bytes)\n", (U32)inputSize, (U32)cSize);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress normally (should fail) : ", testNb++);
|
|
{ size_t const decodeResult = ZSTD_decompressDCtx(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize);
|
|
if (ZSTD_getErrorCode(decodeResult) != ZSTD_error_prefix_unknown) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK : %s \n", ZSTD_getErrorName(decodeResult));
|
|
}
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress of magic-less frame : ", testNb++);
|
|
ZSTD_DCtx_reset(dctx);
|
|
CHECK( ZSTD_DCtx_setFormat(dctx, ZSTD_f_zstd1_magicless) );
|
|
{ ZSTD_frameHeader zfh;
|
|
size_t const zfhrt = ZSTD_getFrameHeader_advanced(&zfh, compressedBuffer, cSize, ZSTD_f_zstd1_magicless);
|
|
if (zfhrt != 0) goto _output_error;
|
|
}
|
|
{ ZSTD_inBuffer in = { compressedBuffer, cSize, 0 };
|
|
ZSTD_outBuffer out = { decodedBuffer, CNBuffSize, 0 };
|
|
size_t const result = ZSTD_decompress_generic(dctx, &out, &in);
|
|
if (result != 0) goto _output_error;
|
|
if (in.pos != in.size) goto _output_error;
|
|
if (out.pos != inputSize) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK : regenerated %u bytes \n", (U32)out.pos);
|
|
}
|
|
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
|
|
/* block API tests */
|
|
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
static const size_t dictSize = 65 KB;
|
|
static const size_t blockSize = 100 KB; /* won't cause pb with small dict size */
|
|
size_t cSize2;
|
|
|
|
/* basic block compression */
|
|
DISPLAYLEVEL(3, "test%3i : Block compression test : ", testNb++);
|
|
CHECK( ZSTD_compressBegin(cctx, 5) );
|
|
CHECK( ZSTD_getBlockSize(cctx) >= blockSize);
|
|
cSize = ZSTD_compressBlock(cctx, compressedBuffer, ZSTD_compressBound(blockSize), CNBuffer, blockSize);
|
|
if (ZSTD_isError(cSize)) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : Block decompression test : ", testNb++);
|
|
CHECK( ZSTD_decompressBegin(dctx) );
|
|
{ CHECK_V(r, ZSTD_decompressBlock(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize) );
|
|
if (r != blockSize) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
/* dictionary block compression */
|
|
DISPLAYLEVEL(3, "test%3i : Dictionary Block compression test : ", testNb++);
|
|
CHECK( ZSTD_compressBegin_usingDict(cctx, CNBuffer, dictSize, 5) );
|
|
cSize = ZSTD_compressBlock(cctx, compressedBuffer, ZSTD_compressBound(blockSize), (char*)CNBuffer+dictSize, blockSize);
|
|
if (ZSTD_isError(cSize)) goto _output_error;
|
|
cSize2 = ZSTD_compressBlock(cctx, (char*)compressedBuffer+cSize, ZSTD_compressBound(blockSize), (char*)CNBuffer+dictSize+blockSize, blockSize);
|
|
if (ZSTD_isError(cSize2)) goto _output_error;
|
|
memcpy((char*)compressedBuffer+cSize, (char*)CNBuffer+dictSize+blockSize, blockSize); /* fake non-compressed block */
|
|
cSize2 = ZSTD_compressBlock(cctx, (char*)compressedBuffer+cSize+blockSize, ZSTD_compressBound(blockSize),
|
|
(char*)CNBuffer+dictSize+2*blockSize, blockSize);
|
|
if (ZSTD_isError(cSize2)) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : Dictionary Block decompression test : ", testNb++);
|
|
CHECK( ZSTD_decompressBegin_usingDict(dctx, CNBuffer, dictSize) );
|
|
{ CHECK_V( r, ZSTD_decompressBlock(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize) );
|
|
if (r != blockSize) goto _output_error; }
|
|
ZSTD_insertBlock(dctx, (char*)decodedBuffer+blockSize, blockSize); /* insert non-compressed block into dctx history */
|
|
{ CHECK_V( r, ZSTD_decompressBlock(dctx, (char*)decodedBuffer+2*blockSize, CNBuffSize, (char*)compressedBuffer+cSize+blockSize, cSize2) );
|
|
if (r != blockSize) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : Block compression with CDict : ", testNb++);
|
|
{ ZSTD_CDict* const cdict = ZSTD_createCDict(CNBuffer, dictSize, 3);
|
|
if (cdict==NULL) goto _output_error;
|
|
CHECK( ZSTD_compressBegin_usingCDict(cctx, cdict) );
|
|
CHECK( ZSTD_compressBlock(cctx, compressedBuffer, ZSTD_compressBound(blockSize), (char*)CNBuffer+dictSize, blockSize) );
|
|
ZSTD_freeCDict(cdict);
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
ZSTD_freeDCtx(dctx);
|
|
|
|
/* long rle test */
|
|
{ size_t sampleSize = 0;
|
|
DISPLAYLEVEL(3, "test%3i : Long RLE test : ", testNb++);
|
|
RDG_genBuffer(CNBuffer, sampleSize, compressibility, 0., seed+1);
|
|
memset((char*)CNBuffer+sampleSize, 'B', 256 KB - 1);
|
|
sampleSize += 256 KB - 1;
|
|
RDG_genBuffer((char*)CNBuffer+sampleSize, 96 KB, compressibility, 0., seed+2);
|
|
sampleSize += 96 KB;
|
|
cSize = ZSTD_compress(compressedBuffer, ZSTD_compressBound(sampleSize), CNBuffer, sampleSize, 1);
|
|
if (ZSTD_isError(cSize)) goto _output_error;
|
|
{ CHECK_V(regenSize, ZSTD_decompress(decodedBuffer, sampleSize, compressedBuffer, cSize));
|
|
if (regenSize!=sampleSize) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
}
|
|
|
|
/* All zeroes test (test bug #137) */
|
|
#define ZEROESLENGTH 100
|
|
DISPLAYLEVEL(3, "test%3i : compress %u zeroes : ", testNb++, ZEROESLENGTH);
|
|
memset(CNBuffer, 0, ZEROESLENGTH);
|
|
{ CHECK_V(r, ZSTD_compress(compressedBuffer, ZSTD_compressBound(ZEROESLENGTH), CNBuffer, ZEROESLENGTH, 1) );
|
|
cSize = r; }
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/ZEROESLENGTH*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress %u zeroes : ", testNb++, ZEROESLENGTH);
|
|
{ CHECK_V(r, ZSTD_decompress(decodedBuffer, ZEROESLENGTH, compressedBuffer, cSize) );
|
|
if (r != ZEROESLENGTH) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
/* nbSeq limit test */
|
|
#define _3BYTESTESTLENGTH 131000
|
|
#define NB3BYTESSEQLOG 9
|
|
#define NB3BYTESSEQ (1 << NB3BYTESSEQLOG)
|
|
#define NB3BYTESSEQMASK (NB3BYTESSEQ-1)
|
|
/* creates a buffer full of 3-bytes sequences */
|
|
{ BYTE _3BytesSeqs[NB3BYTESSEQ][3];
|
|
U32 rSeed = 1;
|
|
|
|
/* create batch of 3-bytes sequences */
|
|
{ int i;
|
|
for (i=0; i < NB3BYTESSEQ; i++) {
|
|
_3BytesSeqs[i][0] = (BYTE)(FUZ_rand(&rSeed) & 255);
|
|
_3BytesSeqs[i][1] = (BYTE)(FUZ_rand(&rSeed) & 255);
|
|
_3BytesSeqs[i][2] = (BYTE)(FUZ_rand(&rSeed) & 255);
|
|
} }
|
|
|
|
/* randomly fills CNBuffer with prepared 3-bytes sequences */
|
|
{ int i;
|
|
for (i=0; i < _3BYTESTESTLENGTH; i += 3) { /* note : CNBuffer size > _3BYTESTESTLENGTH+3 */
|
|
U32 const id = FUZ_rand(&rSeed) & NB3BYTESSEQMASK;
|
|
((BYTE*)CNBuffer)[i+0] = _3BytesSeqs[id][0];
|
|
((BYTE*)CNBuffer)[i+1] = _3BytesSeqs[id][1];
|
|
((BYTE*)CNBuffer)[i+2] = _3BytesSeqs[id][2];
|
|
} } }
|
|
DISPLAYLEVEL(3, "test%3i : compress lots 3-bytes sequences : ", testNb++);
|
|
{ CHECK_V(r, ZSTD_compress(compressedBuffer, ZSTD_compressBound(_3BYTESTESTLENGTH),
|
|
CNBuffer, _3BYTESTESTLENGTH, 19) );
|
|
cSize = r; }
|
|
DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/_3BYTESTESTLENGTH*100);
|
|
|
|
DISPLAYLEVEL(3, "test%3i : decompress lots 3-bytes sequence : ", testNb++);
|
|
{ CHECK_V(r, ZSTD_decompress(decodedBuffer, _3BYTESTESTLENGTH, compressedBuffer, cSize) );
|
|
if (r != _3BYTESTESTLENGTH) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : incompressible data and ill suited dictionary : ", testNb++);
|
|
RDG_genBuffer(CNBuffer, CNBuffSize, 0.0, 0.1, seed);
|
|
{ /* Train a dictionary on low characters */
|
|
size_t dictSize = 16 KB;
|
|
void* const dictBuffer = malloc(dictSize);
|
|
size_t const totalSampleSize = 1 MB;
|
|
size_t const sampleUnitSize = 8 KB;
|
|
U32 const nbSamples = (U32)(totalSampleSize / sampleUnitSize);
|
|
size_t* const samplesSizes = (size_t*) malloc(nbSamples * sizeof(size_t));
|
|
if (!dictBuffer || !samplesSizes) goto _output_error;
|
|
{ U32 u; for (u=0; u<nbSamples; u++) samplesSizes[u] = sampleUnitSize; }
|
|
dictSize = ZDICT_trainFromBuffer(dictBuffer, dictSize, CNBuffer, samplesSizes, nbSamples);
|
|
if (ZDICT_isError(dictSize)) goto _output_error;
|
|
/* Reverse the characters to make the dictionary ill suited */
|
|
{ U32 u;
|
|
for (u = 0; u < CNBuffSize; ++u) {
|
|
((BYTE*)CNBuffer)[u] = 255 - ((BYTE*)CNBuffer)[u];
|
|
}
|
|
}
|
|
{ /* Compress the data */
|
|
size_t const inputSize = 500;
|
|
size_t const outputSize = ZSTD_compressBound(inputSize);
|
|
void* const outputBuffer = malloc(outputSize);
|
|
ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
if (!outputBuffer || !cctx) goto _output_error;
|
|
CHECK(ZSTD_compress_usingDict(cctx, outputBuffer, outputSize, CNBuffer, inputSize, dictBuffer, dictSize, 1));
|
|
free(outputBuffer);
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
|
|
free(dictBuffer);
|
|
free(samplesSizes);
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
|
|
/* findFrameCompressedSize on skippable frames */
|
|
DISPLAYLEVEL(3, "test%3i : frame compressed size of skippable frame : ", testNb++);
|
|
{ const char* frame = "\x50\x2a\x4d\x18\x05\x0\x0\0abcde";
|
|
size_t const frameSrcSize = 13;
|
|
if (ZSTD_findFrameCompressedSize(frame, frameSrcSize) != frameSrcSize) goto _output_error; }
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
/* error string tests */
|
|
DISPLAYLEVEL(3, "test%3i : testing ZSTD error code strings : ", testNb++);
|
|
if (strcmp("No error detected", ZSTD_getErrorName((ZSTD_ErrorCode)(0-ZSTD_error_no_error))) != 0) goto _output_error;
|
|
if (strcmp("No error detected", ZSTD_getErrorString(ZSTD_error_no_error)) != 0) goto _output_error;
|
|
if (strcmp("Unspecified error code", ZSTD_getErrorString((ZSTD_ErrorCode)(0-ZSTD_error_GENERIC))) != 0) goto _output_error;
|
|
if (strcmp("Error (generic)", ZSTD_getErrorName((size_t)0-ZSTD_error_GENERIC)) != 0) goto _output_error;
|
|
if (strcmp("Error (generic)", ZSTD_getErrorString(ZSTD_error_GENERIC)) != 0) goto _output_error;
|
|
if (strcmp("No error detected", ZSTD_getErrorName(ZSTD_error_GENERIC)) != 0) goto _output_error;
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
DISPLAYLEVEL(3, "test%3i : testing ZSTD dictionary sizes : ", testNb++);
|
|
RDG_genBuffer(CNBuffer, CNBuffSize, compressibility, 0., seed);
|
|
{
|
|
size_t const size = MIN(128 KB, CNBuffSize);
|
|
ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
ZSTD_CDict* const lgCDict = ZSTD_createCDict(CNBuffer, size, 1);
|
|
ZSTD_CDict* const smCDict = ZSTD_createCDict(CNBuffer, 1 KB, 1);
|
|
ZSTD_frameHeader lgHeader;
|
|
ZSTD_frameHeader smHeader;
|
|
|
|
CHECK_Z(ZSTD_compress_usingCDict(cctx, compressedBuffer, compressedBufferSize, CNBuffer, size, lgCDict));
|
|
CHECK_Z(ZSTD_getFrameHeader(&lgHeader, compressedBuffer, compressedBufferSize));
|
|
CHECK_Z(ZSTD_compress_usingCDict(cctx, compressedBuffer, compressedBufferSize, CNBuffer, size, smCDict));
|
|
CHECK_Z(ZSTD_getFrameHeader(&smHeader, compressedBuffer, compressedBufferSize));
|
|
|
|
if (lgHeader.windowSize != smHeader.windowSize) goto _output_error;
|
|
|
|
ZSTD_freeCDict(smCDict);
|
|
ZSTD_freeCDict(lgCDict);
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
DISPLAYLEVEL(3, "OK \n");
|
|
|
|
_end:
|
|
free(CNBuffer);
|
|
free(compressedBuffer);
|
|
free(decodedBuffer);
|
|
return testResult;
|
|
|
|
_output_error:
|
|
testResult = 1;
|
|
DISPLAY("Error detected in Unit tests ! \n");
|
|
goto _end;
|
|
}
|
|
|
|
|
|
static size_t findDiff(const void* buf1, const void* buf2, size_t max)
|
|
{
|
|
const BYTE* b1 = (const BYTE*)buf1;
|
|
const BYTE* b2 = (const BYTE*)buf2;
|
|
size_t u;
|
|
for (u=0; u<max; u++) {
|
|
if (b1[u] != b2[u]) break;
|
|
}
|
|
return u;
|
|
}
|
|
|
|
|
|
static ZSTD_parameters FUZ_makeParams(ZSTD_compressionParameters cParams, ZSTD_frameParameters fParams)
|
|
{
|
|
ZSTD_parameters params;
|
|
params.cParams = cParams;
|
|
params.fParams = fParams;
|
|
return params;
|
|
}
|
|
|
|
static size_t FUZ_rLogLength(U32* seed, U32 logLength)
|
|
{
|
|
size_t const lengthMask = ((size_t)1 << logLength) - 1;
|
|
return (lengthMask+1) + (FUZ_rand(seed) & lengthMask);
|
|
}
|
|
|
|
static size_t FUZ_randomLength(U32* seed, U32 maxLog)
|
|
{
|
|
U32 const logLength = FUZ_rand(seed) % maxLog;
|
|
return FUZ_rLogLength(seed, logLength);
|
|
}
|
|
|
|
#undef CHECK
|
|
#define CHECK(cond, ...) { \
|
|
if (cond) { \
|
|
DISPLAY("Error => "); \
|
|
DISPLAY(__VA_ARGS__); \
|
|
DISPLAY(" (seed %u, test nb %u) \n", seed, testNb); \
|
|
goto _output_error; \
|
|
} }
|
|
|
|
#undef CHECK_Z
|
|
#define CHECK_Z(f) { \
|
|
size_t const err = f; \
|
|
if (ZSTD_isError(err)) { \
|
|
DISPLAY("Error => %s : %s ", \
|
|
#f, ZSTD_getErrorName(err)); \
|
|
DISPLAY(" (seed %u, test nb %u) \n", seed, testNb); \
|
|
goto _output_error; \
|
|
} }
|
|
|
|
|
|
static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxDurationS, double compressibility, int bigTests)
|
|
{
|
|
static const U32 maxSrcLog = 23;
|
|
static const U32 maxSampleLog = 22;
|
|
size_t const srcBufferSize = (size_t)1<<maxSrcLog;
|
|
size_t const dstBufferSize = (size_t)1<<maxSampleLog;
|
|
size_t const cBufferSize = ZSTD_compressBound(dstBufferSize);
|
|
BYTE* cNoiseBuffer[5];
|
|
BYTE* srcBuffer; /* jumping pointer */
|
|
BYTE* const cBuffer = (BYTE*) malloc (cBufferSize);
|
|
BYTE* const dstBuffer = (BYTE*) malloc (dstBufferSize);
|
|
BYTE* const mirrorBuffer = (BYTE*) malloc (dstBufferSize);
|
|
ZSTD_CCtx* const refCtx = ZSTD_createCCtx();
|
|
ZSTD_CCtx* const ctx = ZSTD_createCCtx();
|
|
ZSTD_DCtx* const dctx = ZSTD_createDCtx();
|
|
U32 result = 0;
|
|
U32 testNb = 0;
|
|
U32 coreSeed = seed, lseed = 0;
|
|
UTIL_time_t const startClock = UTIL_getTime();
|
|
U64 const maxClockSpan = maxDurationS * SEC_TO_MICRO;
|
|
int const cLevelLimiter = bigTests ? 3 : 2;
|
|
|
|
/* allocation */
|
|
cNoiseBuffer[0] = (BYTE*)malloc (srcBufferSize);
|
|
cNoiseBuffer[1] = (BYTE*)malloc (srcBufferSize);
|
|
cNoiseBuffer[2] = (BYTE*)malloc (srcBufferSize);
|
|
cNoiseBuffer[3] = (BYTE*)malloc (srcBufferSize);
|
|
cNoiseBuffer[4] = (BYTE*)malloc (srcBufferSize);
|
|
CHECK (!cNoiseBuffer[0] || !cNoiseBuffer[1] || !cNoiseBuffer[2] || !cNoiseBuffer[3] || !cNoiseBuffer[4]
|
|
|| !dstBuffer || !mirrorBuffer || !cBuffer || !refCtx || !ctx || !dctx,
|
|
"Not enough memory, fuzzer tests cancelled");
|
|
|
|
/* Create initial samples */
|
|
RDG_genBuffer(cNoiseBuffer[0], srcBufferSize, 0.00, 0., coreSeed); /* pure noise */
|
|
RDG_genBuffer(cNoiseBuffer[1], srcBufferSize, 0.05, 0., coreSeed); /* barely compressible */
|
|
RDG_genBuffer(cNoiseBuffer[2], srcBufferSize, compressibility, 0., coreSeed);
|
|
RDG_genBuffer(cNoiseBuffer[3], srcBufferSize, 0.95, 0., coreSeed); /* highly compressible */
|
|
RDG_genBuffer(cNoiseBuffer[4], srcBufferSize, 1.00, 0., coreSeed); /* sparse content */
|
|
srcBuffer = cNoiseBuffer[2];
|
|
|
|
/* catch up testNb */
|
|
for (testNb=1; testNb < startTest; testNb++) FUZ_rand(&coreSeed);
|
|
|
|
/* main test loop */
|
|
for ( ; (testNb <= nbTests) || (UTIL_clockSpanMicro(startClock) < maxClockSpan); testNb++ ) {
|
|
size_t sampleSize, maxTestSize, totalTestSize;
|
|
size_t cSize, totalCSize, totalGenSize;
|
|
U64 crcOrig;
|
|
BYTE* sampleBuffer;
|
|
const BYTE* dict;
|
|
size_t dictSize;
|
|
|
|
/* notification */
|
|
if (nbTests >= testNb) { DISPLAYUPDATE(2, "\r%6u/%6u ", testNb, nbTests); }
|
|
else { DISPLAYUPDATE(2, "\r%6u ", testNb); }
|
|
|
|
FUZ_rand(&coreSeed);
|
|
{ U32 const prime1 = 2654435761U; lseed = coreSeed ^ prime1; }
|
|
|
|
/* srcBuffer selection [0-4] */
|
|
{ U32 buffNb = FUZ_rand(&lseed) & 0x7F;
|
|
if (buffNb & 7) buffNb=2; /* most common : compressible (P) */
|
|
else {
|
|
buffNb >>= 3;
|
|
if (buffNb & 7) {
|
|
const U32 tnb[2] = { 1, 3 }; /* barely/highly compressible */
|
|
buffNb = tnb[buffNb >> 3];
|
|
} else {
|
|
const U32 tnb[2] = { 0, 4 }; /* not compressible / sparse */
|
|
buffNb = tnb[buffNb >> 3];
|
|
} }
|
|
srcBuffer = cNoiseBuffer[buffNb];
|
|
}
|
|
|
|
/* select src segment */
|
|
sampleSize = FUZ_randomLength(&lseed, maxSampleLog);
|
|
|
|
/* create sample buffer (to catch read error with valgrind & sanitizers) */
|
|
sampleBuffer = (BYTE*)malloc(sampleSize);
|
|
CHECK(sampleBuffer==NULL, "not enough memory for sample buffer");
|
|
{ size_t const sampleStart = FUZ_rand(&lseed) % (srcBufferSize - sampleSize);
|
|
memcpy(sampleBuffer, srcBuffer + sampleStart, sampleSize); }
|
|
crcOrig = XXH64(sampleBuffer, sampleSize, 0);
|
|
|
|
/* compression tests */
|
|
{ int const cLevelPositive =
|
|
( FUZ_rand(&lseed) %
|
|
(ZSTD_maxCLevel() - (FUZ_highbit32((U32)sampleSize) / cLevelLimiter)) )
|
|
+ 1;
|
|
int const cLevel = ((FUZ_rand(&lseed) & 15) == 3) ?
|
|
- (int)((FUZ_rand(&lseed) & 7) + 1) : /* test negative cLevel */
|
|
cLevelPositive;
|
|
DISPLAYLEVEL(5, "fuzzer t%u: Simple compression test (level %i) \n", testNb, cLevel);
|
|
cSize = ZSTD_compressCCtx(ctx, cBuffer, cBufferSize, sampleBuffer, sampleSize, cLevel);
|
|
CHECK(ZSTD_isError(cSize), "ZSTD_compressCCtx failed : %s", ZSTD_getErrorName(cSize));
|
|
|
|
/* compression failure test : too small dest buffer */
|
|
if (cSize > 3) {
|
|
const size_t missing = (FUZ_rand(&lseed) % (cSize-2)) + 1; /* no problem, as cSize > 4 (frameHeaderSizer) */
|
|
const size_t tooSmallSize = cSize - missing;
|
|
const U32 endMark = 0x4DC2B1A9;
|
|
memcpy(dstBuffer+tooSmallSize, &endMark, 4);
|
|
{ size_t const errorCode = ZSTD_compressCCtx(ctx, dstBuffer, tooSmallSize, sampleBuffer, sampleSize, cLevel);
|
|
CHECK(!ZSTD_isError(errorCode), "ZSTD_compressCCtx should have failed ! (buffer too small : %u < %u)", (U32)tooSmallSize, (U32)cSize); }
|
|
{ U32 endCheck; memcpy(&endCheck, dstBuffer+tooSmallSize, 4);
|
|
CHECK(endCheck != endMark, "ZSTD_compressCCtx : dst buffer overflow"); }
|
|
} }
|
|
|
|
/* frame header decompression test */
|
|
{ ZSTD_frameHeader zfh;
|
|
CHECK_Z( ZSTD_getFrameHeader(&zfh, cBuffer, cSize) );
|
|
CHECK(zfh.frameContentSize != sampleSize, "Frame content size incorrect");
|
|
}
|
|
|
|
/* Decompressed size test */
|
|
{ unsigned long long const rSize = ZSTD_findDecompressedSize(cBuffer, cSize);
|
|
CHECK(rSize != sampleSize, "decompressed size incorrect");
|
|
}
|
|
|
|
/* successful decompression test */
|
|
DISPLAYLEVEL(5, "fuzzer t%u: simple decompression test \n", testNb);
|
|
{ size_t const margin = (FUZ_rand(&lseed) & 1) ? 0 : (FUZ_rand(&lseed) & 31) + 1;
|
|
size_t const dSize = ZSTD_decompress(dstBuffer, sampleSize + margin, cBuffer, cSize);
|
|
CHECK(dSize != sampleSize, "ZSTD_decompress failed (%s) (srcSize : %u ; cSize : %u)", ZSTD_getErrorName(dSize), (U32)sampleSize, (U32)cSize);
|
|
{ U64 const crcDest = XXH64(dstBuffer, sampleSize, 0);
|
|
CHECK(crcOrig != crcDest, "decompression result corrupted (pos %u / %u)", (U32)findDiff(sampleBuffer, dstBuffer, sampleSize), (U32)sampleSize);
|
|
} }
|
|
|
|
free(sampleBuffer); /* no longer useful after this point */
|
|
|
|
/* truncated src decompression test */
|
|
DISPLAYLEVEL(5, "fuzzer t%u: decompression of truncated source \n", testNb);
|
|
{ size_t const missing = (FUZ_rand(&lseed) % (cSize-2)) + 1; /* no problem, as cSize > 4 (frameHeaderSizer) */
|
|
size_t const tooSmallSize = cSize - missing;
|
|
void* cBufferTooSmall = malloc(tooSmallSize); /* valgrind will catch read overflows */
|
|
CHECK(cBufferTooSmall == NULL, "not enough memory !");
|
|
memcpy(cBufferTooSmall, cBuffer, tooSmallSize);
|
|
{ size_t const errorCode = ZSTD_decompress(dstBuffer, dstBufferSize, cBufferTooSmall, tooSmallSize);
|
|
CHECK(!ZSTD_isError(errorCode), "ZSTD_decompress should have failed ! (truncated src buffer)"); }
|
|
free(cBufferTooSmall);
|
|
}
|
|
|
|
/* too small dst decompression test */
|
|
DISPLAYLEVEL(5, "fuzzer t%u: decompress into too small dst buffer \n", testNb);
|
|
if (sampleSize > 3) {
|
|
size_t const missing = (FUZ_rand(&lseed) % (sampleSize-2)) + 1; /* no problem, as cSize > 4 (frameHeaderSizer) */
|
|
size_t const tooSmallSize = sampleSize - missing;
|
|
static const BYTE token = 0xA9;
|
|
dstBuffer[tooSmallSize] = token;
|
|
{ size_t const errorCode = ZSTD_decompress(dstBuffer, tooSmallSize, cBuffer, cSize);
|
|
CHECK(!ZSTD_isError(errorCode), "ZSTD_decompress should have failed : %u > %u (dst buffer too small)", (U32)errorCode, (U32)tooSmallSize); }
|
|
CHECK(dstBuffer[tooSmallSize] != token, "ZSTD_decompress : dst buffer overflow");
|
|
}
|
|
|
|
/* noisy src decompression test */
|
|
if (cSize > 6) {
|
|
/* insert noise into src */
|
|
{ U32 const maxNbBits = FUZ_highbit32((U32)(cSize-4));
|
|
size_t pos = 4; /* preserve magic number (too easy to detect) */
|
|
for (;;) {
|
|
/* keep some original src */
|
|
{ U32 const nbBits = FUZ_rand(&lseed) % maxNbBits;
|
|
size_t const mask = (1<<nbBits) - 1;
|
|
size_t const skipLength = FUZ_rand(&lseed) & mask;
|
|
pos += skipLength;
|
|
}
|
|
if (pos >= cSize) break;
|
|
/* add noise */
|
|
{ U32 const nbBitsCodes = FUZ_rand(&lseed) % maxNbBits;
|
|
U32 const nbBits = nbBitsCodes ? nbBitsCodes-1 : 0;
|
|
size_t const mask = (1<<nbBits) - 1;
|
|
size_t const rNoiseLength = (FUZ_rand(&lseed) & mask) + 1;
|
|
size_t const noiseLength = MIN(rNoiseLength, cSize-pos);
|
|
size_t const noiseStart = FUZ_rand(&lseed) % (srcBufferSize - noiseLength);
|
|
memcpy(cBuffer + pos, srcBuffer + noiseStart, noiseLength);
|
|
pos += noiseLength;
|
|
} } }
|
|
|
|
/* decompress noisy source */
|
|
DISPLAYLEVEL(5, "fuzzer t%u: decompress noisy source \n", testNb);
|
|
{ U32 const endMark = 0xA9B1C3D6;
|
|
memcpy(dstBuffer+sampleSize, &endMark, 4);
|
|
{ size_t const decompressResult = ZSTD_decompress(dstBuffer, sampleSize, cBuffer, cSize);
|
|
/* result *may* be an unlikely success, but even then, it must strictly respect dst buffer boundaries */
|
|
CHECK((!ZSTD_isError(decompressResult)) && (decompressResult>sampleSize),
|
|
"ZSTD_decompress on noisy src : result is too large : %u > %u (dst buffer)", (U32)decompressResult, (U32)sampleSize);
|
|
}
|
|
{ U32 endCheck; memcpy(&endCheck, dstBuffer+sampleSize, 4);
|
|
CHECK(endMark!=endCheck, "ZSTD_decompress on noisy src : dst buffer overflow");
|
|
} } } /* noisy src decompression test */
|
|
|
|
/*===== Bufferless streaming compression test, scattered segments and dictionary =====*/
|
|
DISPLAYLEVEL(5, "fuzzer t%u: Bufferless streaming compression test \n", testNb);
|
|
{ U32 const testLog = FUZ_rand(&lseed) % maxSrcLog;
|
|
U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog;
|
|
int const cLevel = (FUZ_rand(&lseed) %
|
|
(ZSTD_maxCLevel() -
|
|
(MAX(testLog, dictLog) / cLevelLimiter))) +
|
|
1;
|
|
maxTestSize = FUZ_rLogLength(&lseed, testLog);
|
|
if (maxTestSize >= dstBufferSize) maxTestSize = dstBufferSize-1;
|
|
|
|
dictSize = FUZ_rLogLength(&lseed, dictLog); /* needed also for decompression */
|
|
dict = srcBuffer + (FUZ_rand(&lseed) % (srcBufferSize - dictSize));
|
|
|
|
DISPLAYLEVEL(6, "fuzzer t%u: Compressing up to <=%u bytes at level %i with dictionary size %u \n",
|
|
testNb, (U32)maxTestSize, cLevel, (U32)dictSize);
|
|
|
|
if (FUZ_rand(&lseed) & 0xF) {
|
|
CHECK_Z ( ZSTD_compressBegin_usingDict(refCtx, dict, dictSize, cLevel) );
|
|
} else {
|
|
ZSTD_compressionParameters const cPar = ZSTD_getCParams(cLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);
|
|
ZSTD_frameParameters const fPar = { FUZ_rand(&lseed)&1 /* contentSizeFlag */,
|
|
!(FUZ_rand(&lseed)&3) /* contentChecksumFlag*/,
|
|
0 /*NodictID*/ }; /* note : since dictionary is fake, dictIDflag has no impact */
|
|
ZSTD_parameters const p = FUZ_makeParams(cPar, fPar);
|
|
CHECK_Z ( ZSTD_compressBegin_advanced(refCtx, dict, dictSize, p, 0) );
|
|
}
|
|
CHECK_Z( ZSTD_copyCCtx(ctx, refCtx, 0) );
|
|
}
|
|
|
|
{ U32 const nbChunks = (FUZ_rand(&lseed) & 127) + 2;
|
|
U32 n;
|
|
XXH64_state_t xxhState;
|
|
XXH64_reset(&xxhState, 0);
|
|
for (totalTestSize=0, cSize=0, n=0 ; n<nbChunks ; n++) {
|
|
size_t const segmentSize = FUZ_randomLength(&lseed, maxSampleLog);
|
|
size_t const segmentStart = FUZ_rand(&lseed) % (srcBufferSize - segmentSize);
|
|
|
|
if (cBufferSize-cSize < ZSTD_compressBound(segmentSize)) break; /* avoid invalid dstBufferTooSmall */
|
|
if (totalTestSize+segmentSize > maxTestSize) break;
|
|
|
|
{ size_t const compressResult = ZSTD_compressContinue(ctx, cBuffer+cSize, cBufferSize-cSize, srcBuffer+segmentStart, segmentSize);
|
|
CHECK (ZSTD_isError(compressResult), "multi-segments compression error : %s", ZSTD_getErrorName(compressResult));
|
|
cSize += compressResult;
|
|
}
|
|
XXH64_update(&xxhState, srcBuffer+segmentStart, segmentSize);
|
|
memcpy(mirrorBuffer + totalTestSize, srcBuffer+segmentStart, segmentSize);
|
|
totalTestSize += segmentSize;
|
|
}
|
|
|
|
{ size_t const flushResult = ZSTD_compressEnd(ctx, cBuffer+cSize, cBufferSize-cSize, NULL, 0);
|
|
CHECK (ZSTD_isError(flushResult), "multi-segments epilogue error : %s", ZSTD_getErrorName(flushResult));
|
|
cSize += flushResult;
|
|
}
|
|
crcOrig = XXH64_digest(&xxhState);
|
|
}
|
|
|
|
/* streaming decompression test */
|
|
DISPLAYLEVEL(5, "fuzzer t%u: Bufferless streaming decompression test \n", testNb);
|
|
/* ensure memory requirement is good enough (should always be true) */
|
|
{ ZSTD_frameHeader zfh;
|
|
CHECK( ZSTD_getFrameHeader(&zfh, cBuffer, ZSTD_frameHeaderSize_max),
|
|
"ZSTD_getFrameHeader(): error retrieving frame information");
|
|
{ size_t const roundBuffSize = ZSTD_decodingBufferSize_min(zfh.windowSize, zfh.frameContentSize);
|
|
CHECK_Z(roundBuffSize);
|
|
CHECK((roundBuffSize > totalTestSize) && (zfh.frameContentSize!=ZSTD_CONTENTSIZE_UNKNOWN),
|
|
"ZSTD_decodingBufferSize_min() requires more memory (%u) than necessary (%u)",
|
|
(U32)roundBuffSize, (U32)totalTestSize );
|
|
} }
|
|
if (dictSize<8) dictSize=0, dict=NULL; /* disable dictionary */
|
|
CHECK_Z( ZSTD_decompressBegin_usingDict(dctx, dict, dictSize) );
|
|
totalCSize = 0;
|
|
totalGenSize = 0;
|
|
while (totalCSize < cSize) {
|
|
size_t const inSize = ZSTD_nextSrcSizeToDecompress(dctx);
|
|
size_t const genSize = ZSTD_decompressContinue(dctx, dstBuffer+totalGenSize, dstBufferSize-totalGenSize, cBuffer+totalCSize, inSize);
|
|
CHECK (ZSTD_isError(genSize), "ZSTD_decompressContinue error : %s", ZSTD_getErrorName(genSize));
|
|
totalGenSize += genSize;
|
|
totalCSize += inSize;
|
|
}
|
|
CHECK (ZSTD_nextSrcSizeToDecompress(dctx) != 0, "frame not fully decoded");
|
|
CHECK (totalGenSize != totalTestSize, "streaming decompressed data : wrong size")
|
|
CHECK (totalCSize != cSize, "compressed data should be fully read")
|
|
{ U64 const crcDest = XXH64(dstBuffer, totalTestSize, 0);
|
|
if (crcDest!=crcOrig) {
|
|
size_t const errorPos = findDiff(mirrorBuffer, dstBuffer, totalTestSize);
|
|
CHECK (1, "streaming decompressed data corrupted : byte %u / %u (%02X!=%02X)",
|
|
(U32)errorPos, (U32)totalTestSize, dstBuffer[errorPos], mirrorBuffer[errorPos]);
|
|
} }
|
|
} /* for ( ; (testNb <= nbTests) */
|
|
DISPLAY("\r%u fuzzer tests completed \n", testNb-1);
|
|
|
|
_cleanup:
|
|
ZSTD_freeCCtx(refCtx);
|
|
ZSTD_freeCCtx(ctx);
|
|
ZSTD_freeDCtx(dctx);
|
|
free(cNoiseBuffer[0]);
|
|
free(cNoiseBuffer[1]);
|
|
free(cNoiseBuffer[2]);
|
|
free(cNoiseBuffer[3]);
|
|
free(cNoiseBuffer[4]);
|
|
free(cBuffer);
|
|
free(dstBuffer);
|
|
free(mirrorBuffer);
|
|
return result;
|
|
|
|
_output_error:
|
|
result = 1;
|
|
goto _cleanup;
|
|
}
|
|
|
|
|
|
/*_*******************************************************
|
|
* Command line
|
|
*********************************************************/
|
|
static int FUZ_usage(const char* programName)
|
|
{
|
|
DISPLAY( "Usage :\n");
|
|
DISPLAY( " %s [args]\n", programName);
|
|
DISPLAY( "\n");
|
|
DISPLAY( "Arguments :\n");
|
|
DISPLAY( " -i# : Nb of tests (default:%u) \n", nbTestsDefault);
|
|
DISPLAY( " -s# : Select seed (default:prompt user)\n");
|
|
DISPLAY( " -t# : Select starting test number (default:0)\n");
|
|
DISPLAY( " -P# : Select compressibility in %% (default:%u%%)\n", FUZ_compressibility_default);
|
|
DISPLAY( " -v : verbose\n");
|
|
DISPLAY( " -p : pause at the end\n");
|
|
DISPLAY( " -h : display help and exit\n");
|
|
return 0;
|
|
}
|
|
|
|
/*! readU32FromChar() :
|
|
@return : unsigned integer value read from input in `char` format
|
|
allows and interprets K, KB, KiB, M, MB and MiB suffix.
|
|
Will also modify `*stringPtr`, advancing it to position where it stopped reading.
|
|
Note : function result can overflow if digit string > MAX_UINT */
|
|
static unsigned readU32FromChar(const char** stringPtr)
|
|
{
|
|
unsigned result = 0;
|
|
while ((**stringPtr >='0') && (**stringPtr <='9'))
|
|
result *= 10, result += **stringPtr - '0', (*stringPtr)++ ;
|
|
if ((**stringPtr=='K') || (**stringPtr=='M')) {
|
|
result <<= 10;
|
|
if (**stringPtr=='M') result <<= 10;
|
|
(*stringPtr)++ ;
|
|
if (**stringPtr=='i') (*stringPtr)++;
|
|
if (**stringPtr=='B') (*stringPtr)++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/** longCommandWArg() :
|
|
* check if *stringPtr is the same as longCommand.
|
|
* If yes, @return 1 and advances *stringPtr to the position which immediately follows longCommand.
|
|
* @return 0 and doesn't modify *stringPtr otherwise.
|
|
*/
|
|
static unsigned longCommandWArg(const char** stringPtr, const char* longCommand)
|
|
{
|
|
size_t const comSize = strlen(longCommand);
|
|
int const result = !strncmp(*stringPtr, longCommand, comSize);
|
|
if (result) *stringPtr += comSize;
|
|
return result;
|
|
}
|
|
|
|
int main(int argc, const char** argv)
|
|
{
|
|
U32 seed = 0;
|
|
int seedset = 0;
|
|
int argNb;
|
|
int nbTests = nbTestsDefault;
|
|
int testNb = 0;
|
|
U32 proba = FUZ_compressibility_default;
|
|
int result = 0;
|
|
U32 mainPause = 0;
|
|
U32 maxDuration = 0;
|
|
int bigTests = 1;
|
|
U32 memTestsOnly = 0;
|
|
const char* const programName = argv[0];
|
|
|
|
/* Check command line */
|
|
for (argNb=1; argNb<argc; argNb++) {
|
|
const char* argument = argv[argNb];
|
|
if(!argument) continue; /* Protection if argument empty */
|
|
|
|
/* Handle commands. Aggregated commands are allowed */
|
|
if (argument[0]=='-') {
|
|
|
|
if (longCommandWArg(&argument, "--memtest=")) { memTestsOnly = readU32FromChar(&argument); continue; }
|
|
|
|
if (!strcmp(argument, "--memtest")) { memTestsOnly=1; continue; }
|
|
if (!strcmp(argument, "--no-big-tests")) { bigTests=0; continue; }
|
|
|
|
argument++;
|
|
while (*argument!=0) {
|
|
switch(*argument)
|
|
{
|
|
case 'h':
|
|
return FUZ_usage(programName);
|
|
|
|
case 'v':
|
|
argument++;
|
|
g_displayLevel++;
|
|
break;
|
|
|
|
case 'q':
|
|
argument++;
|
|
g_displayLevel--;
|
|
break;
|
|
|
|
case 'p': /* pause at the end */
|
|
argument++;
|
|
mainPause = 1;
|
|
break;
|
|
|
|
case 'i':
|
|
argument++; maxDuration = 0;
|
|
nbTests = readU32FromChar(&argument);
|
|
break;
|
|
|
|
case 'T':
|
|
argument++;
|
|
nbTests = 0;
|
|
maxDuration = readU32FromChar(&argument);
|
|
if (*argument=='s') argument++; /* seconds */
|
|
if (*argument=='m') maxDuration *= 60, argument++; /* minutes */
|
|
if (*argument=='n') argument++;
|
|
break;
|
|
|
|
case 's':
|
|
argument++;
|
|
seedset = 1;
|
|
seed = readU32FromChar(&argument);
|
|
break;
|
|
|
|
case 't':
|
|
argument++;
|
|
testNb = readU32FromChar(&argument);
|
|
break;
|
|
|
|
case 'P': /* compressibility % */
|
|
argument++;
|
|
proba = readU32FromChar(&argument);
|
|
if (proba>100) proba = 100;
|
|
break;
|
|
|
|
default:
|
|
return (FUZ_usage(programName), 1);
|
|
} } } } /* for (argNb=1; argNb<argc; argNb++) */
|
|
|
|
/* Get Seed */
|
|
DISPLAY("Starting zstd tester (%i-bits, %s)\n", (int)(sizeof(size_t)*8), ZSTD_VERSION_STRING);
|
|
|
|
if (!seedset) {
|
|
time_t const t = time(NULL);
|
|
U32 const h = XXH32(&t, sizeof(t), 1);
|
|
seed = h % 10000;
|
|
}
|
|
|
|
DISPLAY("Seed = %u\n", seed);
|
|
if (proba!=FUZ_compressibility_default) DISPLAY("Compressibility : %u%%\n", proba);
|
|
|
|
if (memTestsOnly) {
|
|
g_displayLevel = MAX(3, g_displayLevel);
|
|
return FUZ_mallocTests(seed, ((double)proba) / 100, memTestsOnly);
|
|
}
|
|
|
|
if (nbTests < testNb) nbTests = testNb;
|
|
|
|
if (testNb==0)
|
|
result = basicUnitTests(0, ((double)proba) / 100); /* constant seed for predictability */
|
|
if (!result)
|
|
result = fuzzerTests(seed, nbTests, testNb, maxDuration, ((double)proba) / 100, bigTests);
|
|
if (mainPause) {
|
|
int unused;
|
|
DISPLAY("Press Enter \n");
|
|
unused = getchar();
|
|
(void)unused;
|
|
}
|
|
return result;
|
|
}
|