From 671f28d1e50865bac0662297eb0d2372a705385e Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 13 Dec 2016 12:18:07 +0100 Subject: [PATCH 001/227] added parseCompressionParameters --- programs/zstdcli.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 7ffb0bb62..9bc96e19c 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -204,6 +204,38 @@ static unsigned longCommandWArg(const char** stringPtr, const char* longCommand) return result; } + +/** parseCompressionParameters() : + * reads compression parameters from *stringPtr (e.g. "--zstd=wlog=23,clog=23,hlog=22,slog=6,slen=3,tlen=48,strat=6") into *params + * @return 1 means that compression parameters were correct + * @return 0 in case of malformed parameters + */ +static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressionParameters* params) +{ + for ( ; ;) { + DISPLAY("arg=%s\n", stringPtr); + if (longCommandWArg(&stringPtr, "windowLog=") || longCommandWArg(&stringPtr, "wlog=")) { params->windowLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "chainLog=") || longCommandWArg(&stringPtr, "clog=")) { params->chainLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "hashLog=") || longCommandWArg(&stringPtr, "hlog=")) { params->hashLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "searchLog=") || longCommandWArg(&stringPtr, "slog=")) { params->searchLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "searchLength=") || longCommandWArg(&stringPtr, "slen=")) { params->searchLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "targetLength=") || longCommandWArg(&stringPtr, "tlen=")) { params->targetLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "strategy=") || longCommandWArg(&stringPtr, "strat=")) { params->strategy = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + return 0; + } + + if (stringPtr[0] != 0) return 0; /* check the end of string */ + DISPLAYLEVEL(4, "windowLog=%d\n", params->windowLog); + DISPLAYLEVEL(4, "chainLog=%d\n", params->chainLog); + DISPLAYLEVEL(4, "hashLog=%d\n", params->hashLog); + DISPLAYLEVEL(4, "searchLog=%d\n", params->searchLog); + DISPLAYLEVEL(4, "searchLength=%d\n", params->searchLength); + DISPLAYLEVEL(4, "targetLength=%d\n", params->targetLength); + DISPLAYLEVEL(4, "strategy=%d\n", params->strategy); + return 1; +} + + typedef enum { zom_compress, zom_decompress, zom_test, zom_bench, zom_train } zstd_operation_mode; #define CLEAN_RETURN(i) { operationResult = (i); goto _end; } @@ -222,6 +254,7 @@ int main(int argCount, const char* argv[]) ultra=0, lastCommand = 0; zstd_operation_mode operation = zom_compress; + ZSTD_compressionParameters compressionParams; int cLevel = ZSTDCLI_CLEVEL_DEFAULT; int cLevelLast = 1; unsigned recursive = 0; @@ -258,6 +291,7 @@ int main(int argCount, const char* argv[]) /* preset behaviors */ if (!strcmp(programName, ZSTD_UNZSTD)) operation=zom_decompress; if (!strcmp(programName, ZSTD_CAT)) { operation=zom_decompress; forceStdout=1; FIO_overwriteMode(); outFileName=stdoutmark; displayLevel=1; } + memset(&compressionParams, 0, sizeof(compressionParams)); /* command switches */ for (argNb=1; argNbwindowLog) zparams.cParams.windowLog = comprParams->windowLog; + if (comprParams->chainLog) zparams.cParams.chainLog = comprParams->chainLog; + if (comprParams->hashLog) zparams.cParams.hashLog = comprParams->hashLog; + if (comprParams->searchLog) zparams.cParams.searchLog = comprParams->searchLog; + if (comprParams->searchLength) zparams.cParams.searchLength = comprParams->searchLength; + if (comprParams->targetLength) zparams.cParams.targetLength = comprParams->targetLength; + if (comprParams->strategy) zparams.cParams.strategy = comprParams->strategy; do { U32 blockNb; size_t rSize; @@ -247,9 +255,9 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, cdict); } else { - rSize = ZSTD_compressCCtx (ctx, + rSize = ZSTD_compress_advanced (ctx, blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, - blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, cLevel); + blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, NULL, 0, zparams); } if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_compress_usingCDict() failed : %s", ZSTD_getErrorName(rSize)); blockTable[blockNb].cSize = rSize; @@ -387,7 +395,8 @@ static size_t BMK_findMaxMem(U64 requiredMem) static void BMK_benchCLevel(void* srcBuffer, size_t benchedSize, const char* displayName, int cLevel, int cLevelLast, const size_t* fileSizes, unsigned nbFiles, - const void* dictBuffer, size_t dictBufferSize) + const void* dictBuffer, size_t dictBufferSize, + ZSTD_compressionParameters *compressionParams) { int l; @@ -406,7 +415,7 @@ static void BMK_benchCLevel(void* srcBuffer, size_t benchedSize, BMK_benchMem(srcBuffer, benchedSize, displayName, l, fileSizes, nbFiles, - dictBuffer, dictBufferSize); + dictBuffer, dictBufferSize, compressionParams); } } @@ -443,8 +452,8 @@ static void BMK_loadFiles(void* buffer, size_t bufferSize, if (totalSize == 0) EXM_THROW(12, "no data to bench"); } -static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, - const char* dictFileName, int cLevel, int cLevelLast) +static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, const char* dictFileName, + int cLevel, int cLevelLast, ZSTD_compressionParameters *compressionParams) { void* srcBuffer; size_t benchedSize; @@ -483,7 +492,7 @@ static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, BMK_benchCLevel(srcBuffer, benchedSize, displayName, cLevel, cLevelLast, fileSizes, nbFiles, - dictBuffer, dictBufferSize); + dictBuffer, dictBufferSize, compressionParams); } /* clean up */ @@ -493,7 +502,7 @@ static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, } -static void BMK_syntheticTest(int cLevel, int cLevelLast, double compressibility) +static void BMK_syntheticTest(int cLevel, int cLevelLast, double compressibility, ZSTD_compressionParameters* compressionParams) { char name[20] = {0}; size_t benchedSize = 10000000; @@ -507,15 +516,15 @@ static void BMK_syntheticTest(int cLevel, int cLevelLast, double compressibility /* Bench */ snprintf (name, sizeof(name), "Synthetic %2u%%", (unsigned)(compressibility*100)); - BMK_benchCLevel(srcBuffer, benchedSize, name, cLevel, cLevelLast, &benchedSize, 1, NULL, 0); + BMK_benchCLevel(srcBuffer, benchedSize, name, cLevel, cLevelLast, &benchedSize, 1, NULL, 0, compressionParams); /* clean up */ free(srcBuffer); } -int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, - const char* dictFileName, int cLevel, int cLevelLast) +int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, const char* dictFileName, + int cLevel, int cLevelLast, ZSTD_compressionParameters* compressionParams) { double const compressibility = (double)g_compressibilityDefault / 100; @@ -525,8 +534,8 @@ int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, if (cLevelLast > cLevel) DISPLAYLEVEL(2, "Benchmarking levels from %d to %d\n", cLevel, cLevelLast); if (nbFiles == 0) - BMK_syntheticTest(cLevel, cLevelLast, compressibility); + BMK_syntheticTest(cLevel, cLevelLast, compressibility, compressionParams); else - BMK_benchFileTable(fileNamesTable, nbFiles, dictFileName, cLevel, cLevelLast); + BMK_benchFileTable(fileNamesTable, nbFiles, dictFileName, cLevel, cLevelLast, compressionParams); return 0; } diff --git a/programs/bench.h b/programs/bench.h index 7009dc2f6..314f34655 100644 --- a/programs/bench.h +++ b/programs/bench.h @@ -12,9 +12,11 @@ #define BENCH_H_121279284357 #include /* size_t */ +#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressionParameters */ +#include "zstd.h" /* ZSTD_compressionParameters */ -int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, - const char* dictFileName, int cLevel, int cLevelLast); +int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles,const char* dictFileName, + int cLevel, int cLevelLast, ZSTD_compressionParameters* compressionParams); /* Set Parameters */ void BMK_SetNbSeconds(unsigned nbLoops); diff --git a/programs/zstdcli.c b/programs/zstdcli.c index e11f64842..fb873a867 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -214,7 +214,6 @@ static unsigned longCommandWArg(const char** stringPtr, const char* longCommand) static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressionParameters* params) { for ( ; ;) { - DISPLAY("arg=%s\n", stringPtr); if (longCommandWArg(&stringPtr, "windowLog=") || longCommandWArg(&stringPtr, "wlog=")) { params->windowLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } if (longCommandWArg(&stringPtr, "chainLog=") || longCommandWArg(&stringPtr, "clog=")) { params->chainLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } if (longCommandWArg(&stringPtr, "hashLog=") || longCommandWArg(&stringPtr, "hlog=")) { params->hashLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } @@ -226,13 +225,8 @@ static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressi } if (stringPtr[0] != 0) return 0; /* check the end of string */ - DISPLAYLEVEL(4, "windowLog=%d\n", params->windowLog); - DISPLAYLEVEL(4, "chainLog=%d\n", params->chainLog); - DISPLAYLEVEL(4, "hashLog=%d\n", params->hashLog); - DISPLAYLEVEL(4, "searchLog=%d\n", params->searchLog); - DISPLAYLEVEL(4, "searchLength=%d\n", params->searchLength); - DISPLAYLEVEL(4, "targetLength=%d\n", params->targetLength); - DISPLAYLEVEL(4, "strategy=%d\n", params->strategy); + DISPLAYLEVEL(4, "windowLog=%d\nchainLog=%d\nhashLog=%d\nsearchLog=%d\n", params->windowLog, params->chainLog, params->hashLog, params->searchLog); + DISPLAYLEVEL(4, "searchLength=%d\ntargetLength=%d\nstrategy=%d\n", params->searchLength, params->targetLength, params->strategy); return 1; } @@ -523,7 +517,7 @@ int main(int argCount, const char* argv[]) if (operation==zom_bench) { #ifndef ZSTD_NOBENCH BMK_setNotificationLevel(displayLevel); - BMK_benchFiles(filenameTable, filenameIdx, dictFileName, cLevel, cLevelLast); + BMK_benchFiles(filenameTable, filenameIdx, dictFileName, cLevel, cLevelLast, &compressionParams); #endif goto _end; } From 8349d675e01faee2e1b33582175f4aaea71342bd Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 13 Dec 2016 13:24:59 +0100 Subject: [PATCH 003/227] fileio.c: support advanced compression parameters --- programs/fileio.c | 19 ++++++++++++++----- programs/fileio.h | 9 +++++++-- programs/zstdcli.c | 4 ++-- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index a493e97c5..597422ae8 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -247,7 +247,8 @@ typedef struct { FILE* srcFile; } cRess_t; -static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, U64 srcSize) +static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, + U64 srcSize, ZSTD_compressionParameters* comprParams) { cRess_t ress; memset(&ress, 0, sizeof(ress)); @@ -268,6 +269,13 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, U64 sr params.fParams.contentSizeFlag = 1; params.fParams.checksumFlag = g_checksumFlag; params.fParams.noDictIDFlag = !g_dictIDFlag; + if (comprParams->windowLog) params.cParams.windowLog = comprParams->windowLog; + if (comprParams->chainLog) params.cParams.chainLog = comprParams->chainLog; + if (comprParams->hashLog) params.cParams.hashLog = comprParams->hashLog; + if (comprParams->searchLog) params.cParams.searchLog = comprParams->searchLog; + if (comprParams->searchLength) params.cParams.searchLength = comprParams->searchLength; + if (comprParams->targetLength) params.cParams.targetLength = comprParams->targetLength; + if (comprParams->strategy) params.cParams.strategy = comprParams->strategy; { size_t const errorCode = ZSTD_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, srcSize); if (ZSTD_isError(errorCode)) EXM_THROW(33, "Error initializing CStream : %s", ZSTD_getErrorName(errorCode)); } } @@ -401,12 +409,12 @@ static int FIO_compressFilename_dstFile(cRess_t ress, int FIO_compressFilename(const char* dstFileName, const char* srcFileName, - const char* dictFileName, int compressionLevel) + const char* dictFileName, int compressionLevel, ZSTD_compressionParameters* comprParams) { clock_t const start = clock(); U64 const srcSize = UTIL_getFileSize(srcFileName); - cRess_t const ress = FIO_createCResources(dictFileName, compressionLevel, srcSize); + cRess_t const ress = FIO_createCResources(dictFileName, compressionLevel, srcSize, comprParams); int const result = FIO_compressFilename_dstFile(ress, dstFileName, srcFileName); double const seconds = (double)(clock() - start) / CLOCKS_PER_SEC; @@ -419,14 +427,15 @@ int FIO_compressFilename(const char* dstFileName, const char* srcFileName, int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFiles, const char* suffix, - const char* dictFileName, int compressionLevel) + const char* dictFileName, int compressionLevel, + ZSTD_compressionParameters* comprParams) { int missed_files = 0; size_t dfnSize = FNSPACE; char* dstFileName = (char*)malloc(FNSPACE); size_t const suffixSize = suffix ? strlen(suffix) : 0; U64 const srcSize = (nbFiles != 1) ? 0 : UTIL_getFileSize(inFileNamesTable[0]) ; - cRess_t ress = FIO_createCResources(dictFileName, compressionLevel, srcSize); + cRess_t ress = FIO_createCResources(dictFileName, compressionLevel, srcSize, comprParams); /* init */ if (dstFileName==NULL) EXM_THROW(27, "FIO_compressMultipleFilenames : allocation error for dstFileName"); diff --git a/programs/fileio.h b/programs/fileio.h index 06e0be372..b71658334 100644 --- a/programs/fileio.h +++ b/programs/fileio.h @@ -11,6 +11,9 @@ #ifndef FILEIO_H_23981798732 #define FILEIO_H_23981798732 +#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressionParameters */ +#include "zstd.h" /* ZSTD_compressionParameters */ + #if defined (__cplusplus) extern "C" { #endif @@ -44,7 +47,8 @@ void FIO_setMemLimit(unsigned memLimit); ***************************************/ /** FIO_compressFilename() : @return : 0 == ok; 1 == pb with src file. */ -int FIO_compressFilename (const char* outfilename, const char* infilename, const char* dictFileName, int compressionLevel); +int FIO_compressFilename (const char* outfilename, const char* infilename, const char* dictFileName, + int compressionLevel, ZSTD_compressionParameters* comprParams); /** FIO_decompressFilename() : @return : 0 == ok; 1 == pb with src file. */ @@ -58,7 +62,8 @@ int FIO_decompressFilename (const char* outfilename, const char* infilename, con @return : nb of missing files */ int FIO_compressMultipleFilenames(const char** srcNamesTable, unsigned nbFiles, const char* suffix, - const char* dictFileName, int compressionLevel); + const char* dictFileName, int compressionLevel, + ZSTD_compressionParameters* comprParams); /** FIO_decompressMultipleFilenames() : @return : nb of missing or skipped files */ diff --git a/programs/zstdcli.c b/programs/zstdcli.c index fb873a867..f2a6c71f7 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -569,9 +569,9 @@ int main(int argCount, const char* argv[]) if (operation==zom_compress) { #ifndef ZSTD_NOCOMPRESS if ((filenameIdx==1) && outFileName) - operationResult = FIO_compressFilename(outFileName, filenameTable[0], dictFileName, cLevel); + operationResult = FIO_compressFilename(outFileName, filenameTable[0], dictFileName, cLevel, &compressionParams); else - operationResult = FIO_compressMultipleFilenames(filenameTable, filenameIdx, outFileName ? outFileName : ZSTD_EXTENSION, dictFileName, cLevel); + operationResult = FIO_compressMultipleFilenames(filenameTable, filenameIdx, outFileName ? outFileName : ZSTD_EXTENSION, dictFileName, cLevel, &compressionParams); #else DISPLAY("Compression not supported\n"); #endif From 98ef0f98df98402f94a72c85b59559e2634f5336 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 13 Dec 2016 14:52:21 +0100 Subject: [PATCH 004/227] fixed conversion warning --- programs/zstdcli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index f2a6c71f7..68400f195 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -220,7 +220,7 @@ static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressi if (longCommandWArg(&stringPtr, "searchLog=") || longCommandWArg(&stringPtr, "slog=")) { params->searchLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } if (longCommandWArg(&stringPtr, "searchLength=") || longCommandWArg(&stringPtr, "slen=")) { params->searchLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } if (longCommandWArg(&stringPtr, "targetLength=") || longCommandWArg(&stringPtr, "tlen=")) { params->targetLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "strategy=") || longCommandWArg(&stringPtr, "strat=")) { params->strategy = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "strategy=") || longCommandWArg(&stringPtr, "strat=")) { params->strategy = (ZSTD_strategy)readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } return 0; } From c71e552b2e91c1f95a998887edb9eb8fe5c3f4ce Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 13 Dec 2016 20:04:32 +0100 Subject: [PATCH 005/227] fixed "strategy" in advanced compression parameters --- programs/bench.c | 2 +- programs/fileio.c | 2 +- programs/zstdcli.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 6203af6bd..6c2383a87 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -244,7 +244,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, if (comprParams->searchLog) zparams.cParams.searchLog = comprParams->searchLog; if (comprParams->searchLength) zparams.cParams.searchLength = comprParams->searchLength; if (comprParams->targetLength) zparams.cParams.targetLength = comprParams->targetLength; - if (comprParams->strategy) zparams.cParams.strategy = comprParams->strategy; + if (comprParams->strategy) zparams.cParams.strategy = (ZSTD_strategy)(comprParams->strategy - 1); do { U32 blockNb; size_t rSize; diff --git a/programs/fileio.c b/programs/fileio.c index 597422ae8..555b56141 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -275,7 +275,7 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, if (comprParams->searchLog) params.cParams.searchLog = comprParams->searchLog; if (comprParams->searchLength) params.cParams.searchLength = comprParams->searchLength; if (comprParams->targetLength) params.cParams.targetLength = comprParams->targetLength; - if (comprParams->strategy) params.cParams.strategy = comprParams->strategy; + if (comprParams->strategy) params.cParams.strategy = (ZSTD_strategy)(comprParams->strategy - 1); { size_t const errorCode = ZSTD_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, srcSize); if (ZSTD_isError(errorCode)) EXM_THROW(33, "Error initializing CStream : %s", ZSTD_getErrorName(errorCode)); } } diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 68400f195..c1eb7b732 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -220,7 +220,7 @@ static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressi if (longCommandWArg(&stringPtr, "searchLog=") || longCommandWArg(&stringPtr, "slog=")) { params->searchLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } if (longCommandWArg(&stringPtr, "searchLength=") || longCommandWArg(&stringPtr, "slen=")) { params->searchLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } if (longCommandWArg(&stringPtr, "targetLength=") || longCommandWArg(&stringPtr, "tlen=")) { params->targetLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "strategy=") || longCommandWArg(&stringPtr, "strat=")) { params->strategy = (ZSTD_strategy)readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "strategy=") || longCommandWArg(&stringPtr, "strat=")) { params->strategy = (ZSTD_strategy)(1 + readU32FromChar(&stringPtr)); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } return 0; } From 25314428c975b66891f3414836e117bd4093a4bc Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 14 Dec 2016 16:10:13 +0100 Subject: [PATCH 006/227] zstd.1: added advanced compression options --- programs/zstd.1 | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/programs/zstd.1 b/programs/zstd.1 index 63b60d1e9..9b10b1873 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -264,6 +264,115 @@ and weight typically 100x the target dictionary size (for example, 10 MB for a 1 cut file into independent blocks of size # (default: no block) +.SH ADVANCED COMPRESSION OPTIONS +.TP +.B \--zstd[=\fIoptions\fR] +.PD +\fBzstd\fR provides 22 predefined compression levels. The selected or default predefined compression level can be changed with advanced compression options. +The \fIoptions\fR are provided as a comma-separated list. You may specify only the \fIoptions\fR you want to change and the rest will be taken from the selected or default compression level. +The list of available \fIoptions\fR: +.RS + +.TP +.BI strategy= strat +.PD 0 +.TP +.BI strat= strat +.PD +Specify a strategy used by a match finder. +.IP "" +There are 8 strategies numbered from 0 to 7, from faster to stronger: +0=ZSTD_fast, 1=ZSTD_dfast, 2=ZSTD_greedy, 3=ZSTD_lazy, 4=ZSTD_lazy2, 5=ZSTD_btlazy2, 6=ZSTD_btopt, 7=ZSTD_btopt2. +.IP "" + +.TP +.BI windowLog= wlog +.PD 0 +.TP +.BI wlog= wlog +.PD +Specify the maximum number of bits for a match distance. +.IP "" +The higher number of bits increases the chance to find a match what usually improves compression ratio. +It also increases memory requirements for compressor and decompressor. +.IP "" +The minimum \fIwlog\fR is 10 (1 KiB) and the maximum is 25 (32 MiB) for 32-bit compilation and 27 (128 MiB) for 64-bit compilation. +.IP "" + +.TP +.BI hashLog= hlog +.PD 0 +.TP +.BI hlog= hlog +.PD +Specify the maximum number of bits for a hash table. +.IP "" +The bigger hash table causes less collisions what usually make compression faster but requires more memory during compression. +.IP "" +The minimum \fIhlog\fR is 6 (64 B) and the maximum is 25 (32 MiB) for 32-bit compilation and 27 (128 MiB) for 64-bit compilation. + +.TP +.BI chainLog= clog +.PD 0 +.TP +.BI clog= clog +.PD +Specify the maximum number of bits for a hash chain or a binary tree. +.IP "" +The higher number of bits increases the chance to find a match what usually improves compression ratio. +It also slows down compression speed and increases memory requirements for compression. +This option is ignored for the ZSTD_fast strategy. +.IP "" +The minimum \fIclog\fR is 6 (64 B) and the maximum is 26 (64 MiB) for 32-bit compilation and 28 (256 MiB) for 64-bit compilation. +.IP "" + +.TP +.BI searchLog= slog +.PD 0 +.TP +.BI slog= slog +.PD +Specify the maximum number of searches in a hash chain or a binary tree using logarithmic scale. +.IP "" +The bigger number of searches increases the chance to find a match what usually improves compression ratio but decreases compression speed. +.IP "" +The minimum \fIslog\fR is 1 and the maximum is 24 for 32-bit compilation and 26 for 64-bit compilation. +.IP "" + +.TP +.BI searchLength= slen +.PD 0 +.TP +.BI slen= slen +.PD +Specify the minimum searched length of a match in a hash table. +.IP "" +The bigger search length usually decreases compression ratio but improves decompression speed. +.IP "" +The minimum \fIslen\fR is 3 and the maximum is 7. +.IP "" + +.TP +.BI targetLength= tlen +.PD 0 +.TP +.BI tlen= tlen +.PD +Specify the minimum match length that causes a match finder to interrupt searching of better matches. +.IP "" +The bigger minimum match length usually improves compression ratio but decreases compression speed. +This option is used only with ZSTD_btopt and ZSTD_btopt2 strategies. +.IP "" +The minimum \fItlen\fR is 4 and the maximum is 999. +.IP "" + +.PP +.B An example +.br +The following parameters sets advanced compression options to predefined level 19 for files bigger than 256 KB: +.IP "" +\fB--zstd=\fRwindowLog=23,chainLog=23,hashLog=22,searchLog=6,searchLength=3,targetLength=48,strategy=6 + .SH BUGS Report bugs at:- https://github.com/facebook/zstd/issues From 9b4fa0ddf722cc154e6f2274abaa346e62a4c2b4 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 14 Dec 2016 16:50:00 +0100 Subject: [PATCH 007/227] playTests.sh: added Advanced compression parameters --- tests/playTests.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index abde72c80..c5bc577dd 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -101,6 +101,15 @@ $ZSTD -f tmp && die "tmp not present : should have failed" ls tmp.zst && die "tmp.zst should not be created" +$ECHO "\n**** Advanced compression parameters **** " +$ZSTD --zstd=windowLog=21, && die "wrong parameters not detected!" +$ZSTD --zstd=windowLok=21 && die "wrong parameters not detected!" +$ZSTD --zstd=windowLog=21,slog= && die "wrong parameters not detected!" +roundTripTest -g512K +roundTripTest -g512K " --zstd=windowLog=23,chainLog=23,hashLog=22,searchLog=6,searchLength=3,targetLength=48,strategy=6" +roundTripTest -g512K 19 + + $ECHO "\n**** Pass-Through mode **** " $ECHO "Hello world 1!" | $ZSTD -df $ECHO "Hello world 2!" | $ZSTD -dcf From ab5ed6fa7fa7f1f917c19894e9c444dc8a1a2e48 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 14 Dec 2016 17:10:38 +0100 Subject: [PATCH 008/227] improved playTests.sh --- tests/playTests.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index c5bc577dd..6f7891fbc 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -106,6 +106,8 @@ $ZSTD --zstd=windowLog=21, && die "wrong parameters not detected!" $ZSTD --zstd=windowLok=21 && die "wrong parameters not detected!" $ZSTD --zstd=windowLog=21,slog= && die "wrong parameters not detected!" roundTripTest -g512K +roundTripTest -g512K " --zstd=slen=3,tlen=48,strat=6" +roundTripTest -g512K " --zstd=strat=6,wlog=23,clog=23,hlog=22,slog=6" roundTripTest -g512K " --zstd=windowLog=23,chainLog=23,hashLog=22,searchLog=6,searchLength=3,targetLength=48,strategy=6" roundTripTest -g512K 19 From 24a4236111d28b354b3712de942ae03b3ef8c161 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 14 Dec 2016 18:07:31 +0100 Subject: [PATCH 009/227] improved playTests.sh (2) --- tests/playTests.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index 6f7891fbc..b98acfb93 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -27,7 +27,6 @@ case "$OS" in Windows*) isWindows=true ECHO="echo -e" - INTOVOID="nul" ;; esac @@ -102,9 +101,10 @@ ls tmp.zst && die "tmp.zst should not be created" $ECHO "\n**** Advanced compression parameters **** " -$ZSTD --zstd=windowLog=21, && die "wrong parameters not detected!" -$ZSTD --zstd=windowLok=21 && die "wrong parameters not detected!" -$ZSTD --zstd=windowLog=21,slog= && die "wrong parameters not detected!" +$ECHO "Hello world!" | $ZSTD --zstd=windowLog=21, - -o tmp.zst && die "wrong parameters not detected!" +$ECHO "Hello world!" | $ZSTD --zstd=windowLo=21 - -o tmp.zst && die "wrong parameters not detected!" +$ECHO "Hello world!" | $ZSTD --zstd=windowLog=21,slog= - -o tmp.zst && die "wrong parameters not detected!" +ls tmp.zst && die "tmp.zst should not be created" roundTripTest -g512K roundTripTest -g512K " --zstd=slen=3,tlen=48,strat=6" roundTripTest -g512K " --zstd=strat=6,wlog=23,clog=23,hlog=22,slog=6" From f9a56668a697885d9b7b217c9ef377085f23a87b Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 14 Dec 2016 18:43:06 +0100 Subject: [PATCH 010/227] improved playTests.sh (3 --- tests/playTests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index b98acfb93..89ff45f3f 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -103,7 +103,7 @@ ls tmp.zst && die "tmp.zst should not be created" $ECHO "\n**** Advanced compression parameters **** " $ECHO "Hello world!" | $ZSTD --zstd=windowLog=21, - -o tmp.zst && die "wrong parameters not detected!" $ECHO "Hello world!" | $ZSTD --zstd=windowLo=21 - -o tmp.zst && die "wrong parameters not detected!" -$ECHO "Hello world!" | $ZSTD --zstd=windowLog=21,slog= - -o tmp.zst && die "wrong parameters not detected!" +$ECHO "Hello world!" | $ZSTD --zstd=windowLog=21,slog - -o tmp.zst && die "wrong parameters not detected!" ls tmp.zst && die "tmp.zst should not be created" roundTripTest -g512K roundTripTest -g512K " --zstd=slen=3,tlen=48,strat=6" From 60f10aab6c26dda2224583de84fe64ab7a649f55 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Thu, 15 Dec 2016 11:32:31 +0100 Subject: [PATCH 011/227] introduced ZSTDLIB_VISIBILITY --- .travis.yml | 2 +- lib/zstd.h | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 148a98f1b..c2817c48e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ matrix: # Container-based Ubuntu 12.04 LTS Server Edition 64 bit (doesn't support 32-bit includes) - - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install" + - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install && make -C tests fullbench-dll fullbench-lib" os: linux sudo: false diff --git a/lib/zstd.h b/lib/zstd.h index b02e16b42..976d41e16 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -18,15 +18,19 @@ extern "C" { #include /* size_t */ +/* ===== ZSTDLIB_API : control library symbols visibility ===== */ /* ===== ZSTDLIB_API : control library symbols visibility ===== */ #if defined(__GNUC__) && (__GNUC__ >= 4) -# define ZSTDLIB_API __attribute__ ((visibility ("default"))) -#elif defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) -# define ZSTDLIB_API __declspec(dllexport) -#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) -# define ZSTDLIB_API __declspec(dllimport) /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +# define ZSTDLIB_VISIBILITY __attribute__ ((visibility ("default"))) #else -# define ZSTDLIB_API +# define ZSTDLIB_VISIBILITY +#endif +#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) +# define ZSTDLIB_API __declspec(dllexport) ZSTDLIB_VISIBILITY +#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) +# define ZSTDLIB_API __declspec(dllimport) ZSTDLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +#else +# define ZSTDLIB_API ZSTDLIB_VISIBILITY #endif From 4e10bd339ddaa451aae5de33491251ebbd36a180 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Thu, 15 Dec 2016 12:09:23 +0100 Subject: [PATCH 012/227] appveyor.yml: added tests of fullbench-dll fullbench-lib --- .travis.yml | 2 +- appveyor.yml | 8 ++++---- lib/zstd.h | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index c2817c48e..148a98f1b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ matrix: # Container-based Ubuntu 12.04 LTS Server Edition 64 bit (doesn't support 32-bit includes) - - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install && make -C tests fullbench-dll fullbench-lib" + - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install" os: linux sudo: false diff --git a/appveyor.yml b/appveyor.yml index 27c83c04e..618f76d3c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,10 +2,10 @@ version: 1.0.{build} environment: matrix: - COMPILER: "gcc" - MAKE_PARAMS: "test" + MAKE_PARAMS: "make test && make lib && make -C tests fullbench-dll fullbench-lib" PLATFORM: "mingw64" - COMPILER: "gcc" - MAKE_PARAMS: "test" + MAKE_PARAMS: "make test" PLATFORM: "mingw32" - COMPILER: "visual" CONFIGURATION: "Debug" @@ -62,8 +62,8 @@ build_script: ECHO *** && make -v && cc -v && - ECHO make %MAKE_PARAMS% && - make %MAKE_PARAMS% + ECHO %MAKE_PARAMS% && + sh -c %MAKE_PARAMS% ) - if [%COMPILER%]==[gcc] if [%PLATFORM%]==[mingw64] ( COPY programs\zstd.exe bin\zstd.exe && diff --git a/lib/zstd.h b/lib/zstd.h index 976d41e16..07283f553 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -18,7 +18,6 @@ extern "C" { #include /* size_t */ -/* ===== ZSTDLIB_API : control library symbols visibility ===== */ /* ===== ZSTDLIB_API : control library symbols visibility ===== */ #if defined(__GNUC__) && (__GNUC__ >= 4) # define ZSTDLIB_VISIBILITY __attribute__ ((visibility ("default"))) From 0a1caeef6dcf5b50aca2891424dfd81955e69908 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Thu, 15 Dec 2016 12:12:46 +0100 Subject: [PATCH 013/227] VS: fixed 32-bit DLL compilation --- build/VS2010/fullbench-dll/fullbench-dll.vcxproj | 2 ++ lib/dll/example/fullbench-dll.vcxproj | 2 ++ 2 files changed, 4 insertions(+) diff --git a/build/VS2010/fullbench-dll/fullbench-dll.vcxproj b/build/VS2010/fullbench-dll/fullbench-dll.vcxproj index 3609f3ac0..e697318e0 100644 --- a/build/VS2010/fullbench-dll/fullbench-dll.vcxproj +++ b/build/VS2010/fullbench-dll/fullbench-dll.vcxproj @@ -99,6 +99,7 @@ true $(SolutionDir)bin\$(Platform)_$(Configuration);%(AdditionalLibraryDirectories) libzstd.lib;%(AdditionalDependencies) + false @@ -138,6 +139,7 @@ true $(SolutionDir)bin\$(Platform)_$(Configuration);%(AdditionalLibraryDirectories) libzstd.lib;%(AdditionalDependencies) + false diff --git a/lib/dll/example/fullbench-dll.vcxproj b/lib/dll/example/fullbench-dll.vcxproj index 3faacbae1..44bbaf788 100644 --- a/lib/dll/example/fullbench-dll.vcxproj +++ b/lib/dll/example/fullbench-dll.vcxproj @@ -100,6 +100,7 @@ true $(SolutionDir)..\dll;%(AdditionalLibraryDirectories) libzstd.lib;%(AdditionalDependencies) + false @@ -141,6 +142,7 @@ true $(SolutionDir)..\dll;%(AdditionalLibraryDirectories) libzstd.lib;%(AdditionalDependencies) + false From 06b3f017afce3947919fc74b1938632d8a635db6 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Thu, 15 Dec 2016 12:31:18 +0100 Subject: [PATCH 014/227] appveyor.yml: fixed tests of fullbench-dll fullbench-lib --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 618f76d3c..5dcbc4ba8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ version: 1.0.{build} environment: matrix: - COMPILER: "gcc" - MAKE_PARAMS: "make test && make lib && make -C tests fullbench-dll fullbench-lib" + MAKE_PARAMS: '"make test && make lib && make -C tests fullbench-dll fullbench-lib"' PLATFORM: "mingw64" - COMPILER: "gcc" MAKE_PARAMS: "make test" From b3843afcf5cf7771318d93b1ac9b72b3a0bee0db Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Fri, 16 Dec 2016 14:13:15 +0100 Subject: [PATCH 015/227] introduced platform.h --- programs/platform.h | 69 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 programs/platform.h diff --git a/programs/platform.h b/programs/platform.h new file mode 100644 index 000000000..978db1db4 --- /dev/null +++ b/programs/platform.h @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef PLATFORM_H_MODULE +#define PLATFORM_H_MODULE + +#if defined (__cplusplus) +extern "C" { +#endif + +/* ************************************** +* Compiler Options +****************************************/ +#if defined(__INTEL_COMPILER) +# pragma warning(disable : 177) /* disable: message #177: function was declared but never referenced */ +#endif +#if defined(_MSC_VER) +# define _CRT_SECURE_NO_WARNINGS /* Disable some Visual warning messages for fopen, strncpy */ +# define _CRT_SECURE_NO_DEPRECATE /* VS2005 */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# if _MSC_VER <= 1800 /* (1800 = Visual Studio 2013) */ +# define snprintf sprintf_s /* snprintf unsupported by Visual <= 2013 */ +# endif +#endif + + +/* Unix Large Files support (>4GB) */ +#if !defined(__LP64__) /* No point defining Large file for 64 bit */ +# define _FILE_OFFSET_BITS 64 /* turn off_t into a 64-bit type for ftello, fseeko */ +# if defined(__sun__) && !defined(_LARGEFILE_SOURCE) /* Sun Solaris 32-bits requires specific definitions */ +# define _LARGEFILE_SOURCE /* fseeko, ftello */ +# elif !defined(_LARGEFILE64_SOURCE) +# define _LARGEFILE64_SOURCE /* off64_t, fseeko64, ftello64 */ +# endif +#endif + + +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) || defined(__midipix__)) + /* UNIX-style OS. ------------------------------------------- */ +# if (defined(__APPLE__) && defined(__MACH__) || defined(__SVR4) || defined(_AIX) || defined(__hpux) \ + || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ /* POSIX.1–2001 (SUSv3) conformant */ +# define PLATFORM_POSIX_VERSION 200112L +# else +# if defined(__linux__) || defined(__linux) +# define _POSIX_C_SOURCE 200112L /* use feature test macro */ +# endif +# include /* declares _POSIX_VERSION */ +# if defined(_POSIX_VERSION) /* POSIX compliant */ +# define PLATFORM_POSIX_VERSION _POSIX_VERSION +# endif +# endif +#endif + +#if !defined(PLATFORM_POSIX_VERSION) +# define PLATFORM_POSIX_VERSION 0 +#endif + + +#if defined (__cplusplus) +} +#endif + +#endif /* PLATFORM_H_MODULE */ From b866e72826fdd4b7b6a697b10dfd825b3ba10a79 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Fri, 16 Dec 2016 14:24:01 +0100 Subject: [PATCH 016/227] tools use platform.h --- programs/bench.c | 3 ++- programs/dibio.c | 3 ++- programs/fileio.c | 3 ++- programs/platform.h | 13 ++++++++++++- programs/zstdcli.c | 3 ++- tests/datagencli.c | 2 +- tests/fullbench.c | 3 ++- tests/paramgrill.c | 3 ++- zlibWrapper/examples/zwrapbench.c | 3 ++- 9 files changed, 27 insertions(+), 9 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 9104ea89c..f58f3e5c5 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -11,7 +11,8 @@ /* ************************************* * Includes ***************************************/ -#include "util.h" /* Compiler options, UTIL_GetFileSize, UTIL_sleep */ +#include "platform.h" /* Compiler options */ +#include "util.h" /* UTIL_GetFileSize, UTIL_sleep */ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen, ftello64 */ diff --git a/programs/dibio.c b/programs/dibio.c index a793669cb..74dc54055 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -12,7 +12,8 @@ /*-************************************* * Includes ***************************************/ -#include "util.h" /* Compiler options, UTIL_GetFileSize, UTIL_getTotalFileSize */ +#include "platform.h" /* Compiler options */ +#include "util.h" /* UTIL_GetFileSize, UTIL_getTotalFileSize */ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen, ftello64 */ diff --git a/programs/fileio.c b/programs/fileio.c index a493e97c5..2ff5947f4 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -22,7 +22,8 @@ /*-************************************* * Includes ***************************************/ -#include "util.h" /* Compiler options, UTIL_GetFileSize, _LARGEFILE64_SOURCE */ +#include "platform.h" /* Compiler options */ +#include "util.h" /* UTIL_GetFileSize, _LARGEFILE64_SOURCE */ #include /* fprintf, fopen, fread, _fileno, stdin, stdout */ #include /* malloc, free */ #include /* strcmp, strlen */ diff --git a/programs/platform.h b/programs/platform.h index 978db1db4..095e52b84 100644 --- a/programs/platform.h +++ b/programs/platform.h @@ -30,7 +30,9 @@ extern "C" { #endif -/* Unix Large Files support (>4GB) */ +/* ************************************** +* Unix Large Files support (>4GB) +****************************************/ #if !defined(__LP64__) /* No point defining Large file for 64 bit */ # define _FILE_OFFSET_BITS 64 /* turn off_t into a 64-bit type for ftello, fseeko */ # if defined(__sun__) && !defined(_LARGEFILE_SOURCE) /* Sun Solaris 32-bits requires specific definitions */ @@ -41,6 +43,12 @@ extern "C" { #endif +/* *********************************************************** +* Detect POSIX version +* PLATFORM_POSIX_VERSION = 0 for non-Unix e.g. Windows +* PLATFORM_POSIX_VERSION = 1 for Unix-like +* PLATFORM_POSIX_VERSION > 1 is equal to found _POSIX_VERSION +**************************************************************/ #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) || defined(__midipix__)) /* UNIX-style OS. ------------------------------------------- */ # if (defined(__APPLE__) && defined(__MACH__) || defined(__SVR4) || defined(_AIX) || defined(__hpux) \ @@ -53,6 +61,8 @@ extern "C" { # include /* declares _POSIX_VERSION */ # if defined(_POSIX_VERSION) /* POSIX compliant */ # define PLATFORM_POSIX_VERSION _POSIX_VERSION +# else +# define PLATFORM_POSIX_VERSION 1 # endif # endif #endif @@ -62,6 +72,7 @@ extern "C" { #endif + #if defined (__cplusplus) } #endif diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 561730a5f..fa2956ca4 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -23,7 +23,8 @@ /*-************************************ * Dependencies **************************************/ -#include "util.h" /* Compiler options, UTIL_HAS_CREATEFILELIST */ +#include "platform.h" /* Compiler options, PLATFORM_POSIX_VERSION */ +#include "util.h" /* UTIL_HAS_CREATEFILELIST, UTIL_createFileList */ #include /* strcmp, strlen */ #include /* errno */ #include "fileio.h" diff --git a/tests/datagencli.c b/tests/datagencli.c index 2f3ebc4d6..0729cdd0c 100644 --- a/tests/datagencli.c +++ b/tests/datagencli.c @@ -11,7 +11,7 @@ /*-************************************ * Dependencies **************************************/ -#include "util.h" /* Compiler options */ +#include "platform.h" /* Compiler options */ #include /* fprintf, stderr */ #include "datagen.h" /* RDG_generate */ diff --git a/tests/fullbench.c b/tests/fullbench.c index 233b4e931..e5547d089 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -11,7 +11,8 @@ /*_************************************ * Includes **************************************/ -#include "util.h" /* Compiler options, UTIL_GetFileSize */ +#include "platform.h" /* Compiler options */ +#include "util.h" /* UTIL_GetFileSize */ #include /* malloc */ #include /* fprintf, fopen, ftello64 */ #include /* clock_t, clock, CLOCKS_PER_SEC */ diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 5eabcba2b..19658fddb 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -11,7 +11,8 @@ /*-************************************ * Dependencies **************************************/ -#include "util.h" /* Compiler options, UTIL_GetFileSize */ +#include "platform.h" /* Compiler options */ +#include "util.h" /* UTIL_GetFileSize */ #include /* malloc */ #include /* fprintf, fopen, ftello64 */ #include /* strcmp */ diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c index e0aca0015..c3968dba3 100644 --- a/zlibWrapper/examples/zwrapbench.c +++ b/zlibWrapper/examples/zwrapbench.c @@ -11,7 +11,8 @@ /* ************************************* * Includes ***************************************/ -#include "util.h" /* Compiler options, UTIL_GetFileSize, UTIL_sleep */ +#include "platform.h" /* Compiler options */ +#include "util.h" /* UTIL_GetFileSize, UTIL_sleep */ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen, ftello64 */ From b0e670a054edf3f8f388fe8e53688372c5883071 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Fri, 16 Dec 2016 14:25:12 +0100 Subject: [PATCH 017/227] util.h uses platform.h --- programs/util.h | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/programs/util.h b/programs/util.h index 0fa70577a..45fdacb1a 100644 --- a/programs/util.h +++ b/programs/util.h @@ -14,37 +14,12 @@ extern "C" { #endif -/* ************************************** -* Compiler Options -****************************************/ -#if defined(__INTEL_COMPILER) -# pragma warning(disable : 177) /* disable: message #177: function was declared but never referenced */ -#endif -#if defined(_MSC_VER) -# define _CRT_SECURE_NO_WARNINGS /* Disable some Visual warning messages for fopen, strncpy */ -# define _CRT_SECURE_NO_DEPRECATE /* VS2005 */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -#if _MSC_VER <= 1800 /* (1800 = Visual Studio 2013) */ -# define snprintf sprintf_s /* snprintf unsupported by Visual <= 2013 */ -#endif -#endif - - -/* Unix Large Files support (>4GB) */ -#if !defined(__LP64__) /* No point defining Large file for 64 bit */ -# define _FILE_OFFSET_BITS 64 /* turn off_t into a 64-bit type for ftello, fseeko */ -# if defined(__sun__) && !defined(_LARGEFILE_SOURCE) /* Sun Solaris 32-bits requires specific definitions */ -# define _LARGEFILE_SOURCE /* fseeko, ftello */ -# elif !defined(_LARGEFILE64_SOURCE) -# define _LARGEFILE64_SOURCE /* off64_t, fseeko64, ftello64 */ -# endif -#endif - /*-**************************************** * Dependencies ******************************************/ -#include /* features.h with _POSIX_C_SOURCE, malloc */ +#include "platform.h" /* Compiler options, PLATFORM_POSIX_VERSION */ +#include /* malloc */ #include /* fprintf */ #include /* stat, utime */ #include /* stat */ From 3cdfe266cfc73333f67d0a63baa8d98ce27232f8 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Fri, 16 Dec 2016 15:00:50 +0100 Subject: [PATCH 018/227] use PLATFORM_POSIX_VERSION --- programs/platform.h | 14 +++++++------- programs/util.h | 2 +- programs/zstdcli.c | 3 +-- tests/datagencli.c | 1 + 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/programs/platform.h b/programs/platform.h index 095e52b84..6135cb582 100644 --- a/programs/platform.h +++ b/programs/platform.h @@ -43,12 +43,12 @@ extern "C" { #endif -/* *********************************************************** +/* ************************************************************ * Detect POSIX version -* PLATFORM_POSIX_VERSION = 0 for non-Unix e.g. Windows -* PLATFORM_POSIX_VERSION = 1 for Unix-like -* PLATFORM_POSIX_VERSION > 1 is equal to found _POSIX_VERSION -**************************************************************/ +* PLATFORM_POSIX_VERSION = -1 for non-Unix e.g. Windows +* PLATFORM_POSIX_VERSION = 0 for Unix-like non-POSIX +* PLATFORM_POSIX_VERSION >= 1 is equal to found _POSIX_VERSION +***************************************************************/ #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) || defined(__midipix__)) /* UNIX-style OS. ------------------------------------------- */ # if (defined(__APPLE__) && defined(__MACH__) || defined(__SVR4) || defined(_AIX) || defined(__hpux) \ @@ -62,13 +62,13 @@ extern "C" { # if defined(_POSIX_VERSION) /* POSIX compliant */ # define PLATFORM_POSIX_VERSION _POSIX_VERSION # else -# define PLATFORM_POSIX_VERSION 1 +# define PLATFORM_POSIX_VERSION 0 # endif # endif #endif #if !defined(PLATFORM_POSIX_VERSION) -# define PLATFORM_POSIX_VERSION 0 +# define PLATFORM_POSIX_VERSION -1 #endif diff --git a/programs/util.h b/programs/util.h index 45fdacb1a..179d5ca98 100644 --- a/programs/util.h +++ b/programs/util.h @@ -73,7 +73,7 @@ extern "C" { # define SET_HIGH_PRIORITY /* disabled */ # endif # define UTIL_sleep(s) sleep(s) -# if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 199309L)) +# if PLATFORM_POSIX_VERSION >= 200112L /* nanosleep requires POSIX.1-2001 */ # define UTIL_sleepMilli(milli) { struct timespec t; t.tv_sec=0; t.tv_nsec=milli*1000000ULL; nanosleep(&t, NULL); } # else # define UTIL_sleepMilli(milli) /* disabled */ diff --git a/programs/zstdcli.c b/programs/zstdcli.c index fa2956ca4..fad237530 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -44,8 +44,7 @@ #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) # include /* _isatty */ # define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) -#elif defined(_POSIX_C_SOURCE) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || (defined(__APPLE__) && defined(__MACH__)) || \ - defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) /* https://sourceforge.net/p/predef/wiki/OperatingSystems/ */ +#elif PLATFORM_POSIX_VERSION >= 200112L /* isatty requires POSIX.1-2001 */ # include /* isatty */ # define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) #else diff --git a/tests/datagencli.c b/tests/datagencli.c index 0729cdd0c..2b3d3b511 100644 --- a/tests/datagencli.c +++ b/tests/datagencli.c @@ -14,6 +14,7 @@ #include "platform.h" /* Compiler options */ #include /* fprintf, stderr */ #include "datagen.h" /* RDG_generate */ +#include "mem.h" /* U32, U64 */ /*-************************************ From b0f3663edc00c775beae7cf67e618347d48a77e9 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Fri, 16 Dec 2016 15:41:18 +0100 Subject: [PATCH 019/227] imporved support for POSIX-type OSes --- programs/platform.h | 6 +++--- programs/util.h | 9 +++------ programs/zstdcli.c | 11 ++++++++++- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/programs/platform.h b/programs/platform.h index 6135cb582..c69dcb2dd 100644 --- a/programs/platform.h +++ b/programs/platform.h @@ -49,10 +49,10 @@ extern "C" { * PLATFORM_POSIX_VERSION = 0 for Unix-like non-POSIX * PLATFORM_POSIX_VERSION >= 1 is equal to found _POSIX_VERSION ***************************************************************/ -#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) || defined(__midipix__)) +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) || defined(__midipix__) || defined(__VMS)) /* UNIX-style OS. ------------------------------------------- */ -# if (defined(__APPLE__) && defined(__MACH__) || defined(__SVR4) || defined(_AIX) || defined(__hpux) \ - || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ /* POSIX.1–2001 (SUSv3) conformant */ +# if (defined(__APPLE__) && defined(__MACH__)) || defined(__SVR4) || defined(_AIX) || defined(__hpux) \ + || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) /* POSIX.1–2001 (SUSv3) conformant */ # define PLATFORM_POSIX_VERSION 200112L # else # if defined(__linux__) || defined(__linux) diff --git a/programs/util.h b/programs/util.h index 179d5ca98..8c171c3b1 100644 --- a/programs/util.h +++ b/programs/util.h @@ -63,7 +63,7 @@ extern "C" { # define SET_HIGH_PRIORITY SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS) # define UTIL_sleep(s) Sleep(1000*s) # define UTIL_sleepMilli(milli) Sleep(milli) -#elif (defined(__unix__) || defined(__unix) || defined(__VMS) || defined(__midipix__) || (defined(__APPLE__) && defined(__MACH__))) +#elif PLATFORM_POSIX_VERSION >= 0 /* Unix-like operating system */ # include # include /* setpriority */ # include /* clock_t, nanosleep, clock, CLOCKS_PER_SEC */ @@ -73,7 +73,7 @@ extern "C" { # define SET_HIGH_PRIORITY /* disabled */ # endif # define UTIL_sleep(s) sleep(s) -# if PLATFORM_POSIX_VERSION >= 200112L /* nanosleep requires POSIX.1-2001 */ +# if (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 199309L)) || (PLATFORM_POSIX_VERSION >= 200112L) /* nanosleep requires POSIX.1-2001 */ # define UTIL_sleepMilli(milli) { struct timespec t; t.tv_sec=0; t.tv_nsec=milli*1000000ULL; nanosleep(&t, NULL); } # else # define UTIL_sleepMilli(milli) /* disabled */ @@ -300,10 +300,7 @@ UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_ return nbFiles; } -#elif (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) || \ - (defined(__APPLE__) && defined(__MACH__)) || defined(__SVR4) || defined(_AIX) || defined(__hpux) || \ - defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ - (defined(__linux__) && defined(_POSIX_C_SOURCE)) /* opendir */ +#elif (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) || (PLATFORM_POSIX_VERSION >= 200112L) /* opendir, readdir require POSIX.1-2001 */ # define UTIL_HAS_CREATEFILELIST # include /* opendir, readdir */ diff --git a/programs/zstdcli.c b/programs/zstdcli.c index fad237530..197dd3ff9 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -44,7 +44,7 @@ #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) # include /* _isatty */ # define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) -#elif PLATFORM_POSIX_VERSION >= 200112L /* isatty requires POSIX.1-2001 */ +#elif (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) || PLATFORM_POSIX_VERSION >= 200112L /* isatty requires POSIX.1-2001 */ # include /* isatty */ # define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) #else @@ -470,6 +470,15 @@ int main(int argCount, const char* argv[]) /* Welcome message (if verbose) */ DISPLAYLEVEL(3, WELCOME_MESSAGE); +#ifdef _POSIX_C_SOURCE + DISPLAYLEVEL(4, "_POSIX_C_SOURCE defined: %ldL\n", (long) _POSIX_C_SOURCE); +#endif +#ifdef _POSIX_VERSION + DISPLAYLEVEL(4, "_POSIX_VERSION defined: %ldL\n", (long) _POSIX_VERSION); +#endif +#ifdef PLATFORM_POSIX_VERSION + DISPLAYLEVEL(4, "PLATFORM_POSIX_VERSION defined: %ldL\n", (long) PLATFORM_POSIX_VERSION); +#endif #ifdef UTIL_HAS_CREATEFILELIST if (recursive) { /* at this stage, filenameTable is a list of paths, which can contain both files and directories */ From 0b37205098bfcaf9e2ddca670c1bd42d29226a73 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Fri, 16 Dec 2016 17:12:23 +0100 Subject: [PATCH 020/227] util.h: minor improvement --- programs/util.h | 2 +- programs/zstdcli.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/util.h b/programs/util.h index 8c171c3b1..9dbd7fb1f 100644 --- a/programs/util.h +++ b/programs/util.h @@ -300,7 +300,7 @@ UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_ return nbFiles; } -#elif (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) || (PLATFORM_POSIX_VERSION >= 200112L) /* opendir, readdir require POSIX.1-2001 */ +#elif defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L) /* opendir, readdir require POSIX.1-2001 */ # define UTIL_HAS_CREATEFILELIST # include /* opendir, readdir */ diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 197dd3ff9..68ae3c201 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -44,7 +44,7 @@ #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) # include /* _isatty */ # define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) -#elif (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) || PLATFORM_POSIX_VERSION >= 200112L /* isatty requires POSIX.1-2001 */ +#elif (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) || (PLATFORM_POSIX_VERSION >= 200112L) /* isatty requires POSIX.1-2001 */ # include /* isatty */ # define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) #else From 8de46ab51ab7c2c54734c7fe9f2c1cfbaa3e3bc3 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 16 Dec 2016 13:27:30 -0800 Subject: [PATCH 021/227] Export all API functions --- build/VS2005/zstdlib/zstdlib.vcproj | 4 +++ build/VS2010/libzstd-dll/libzstd-dll.vcxproj | 1 + build/VS2010/libzstd/libzstd.vcxproj | 1 + build/cmake/lib/CMakeLists.txt | 1 + lib/common/zstd_common.c | 4 --- lib/common/zstd_errors.h | 18 ++++++++++++-- lib/deprecated/zbuff_common.c | 26 ++++++++++++++++++++ lib/dictBuilder/zdict.h | 23 +++++++++-------- lib/zstd.h | 21 +++++++++------- 9 files changed, 74 insertions(+), 25 deletions(-) create mode 100644 lib/deprecated/zbuff_common.c diff --git a/build/VS2005/zstdlib/zstdlib.vcproj b/build/VS2005/zstdlib/zstdlib.vcproj index 1b78986b2..d95212b33 100644 --- a/build/VS2005/zstdlib/zstdlib.vcproj +++ b/build/VS2005/zstdlib/zstdlib.vcproj @@ -359,6 +359,10 @@ RelativePath="..\..\..\lib\common\xxhash.c" > + + diff --git a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj index f85271970..f1ea5c822 100644 --- a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj +++ b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj @@ -29,6 +29,7 @@ + diff --git a/build/VS2010/libzstd/libzstd.vcxproj b/build/VS2010/libzstd/libzstd.vcxproj index f5f30d733..228e83da4 100644 --- a/build/VS2010/libzstd/libzstd.vcxproj +++ b/build/VS2010/libzstd/libzstd.vcxproj @@ -29,6 +29,7 @@ + diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index abcf4ffe2..65942b412 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -69,6 +69,7 @@ SET(Sources ${LIBRARY_DIR}/decompress/zstd_decompress.c ${LIBRARY_DIR}/dictBuilder/divsufsort.c ${LIBRARY_DIR}/dictBuilder/zdict.c + ${LIBRARY_DIR}/deprecated/zbuff_common.c ${LIBRARY_DIR}/deprecated/zbuff_compress.c ${LIBRARY_DIR}/deprecated/zbuff_decompress.c) diff --git a/lib/common/zstd_common.c b/lib/common/zstd_common.c index f30128c79..749b2870c 100644 --- a/lib/common/zstd_common.c +++ b/lib/common/zstd_common.c @@ -43,10 +43,6 @@ ZSTD_ErrorCode ZSTD_getErrorCode(size_t code) { return ERR_getErrorCode(code); } * provides error code string from enum */ const char* ZSTD_getErrorString(ZSTD_ErrorCode code) { return ERR_getErrorName(code); } -/* --- ZBUFF Error Management (deprecated) --- */ -unsigned ZBUFF_isError(size_t errorCode) { return ERR_isError(errorCode); } -const char* ZBUFF_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); } - /*=************************************************************** * Custom allocator diff --git a/lib/common/zstd_errors.h b/lib/common/zstd_errors.h index 50dc4f720..949dbd0ff 100644 --- a/lib/common/zstd_errors.h +++ b/lib/common/zstd_errors.h @@ -18,6 +18,20 @@ extern "C" { #include /* size_t */ +/* ===== ZSTDERRORLIB_API : control library symbols visibility ===== */ +#if defined(__GNUC__) && (__GNUC__ >= 4) +# define ZSTDERRORLIB_VISIBILITY __attribute__ ((visibility ("default"))) +#else +# define ZSTDERRORLIB_VISIBILITY +#endif +#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) +# define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBILITY +#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) +# define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +#else +# define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBILITY +#endif + /*-**************************************** * error codes list ******************************************/ @@ -49,8 +63,8 @@ typedef enum { /*! ZSTD_getErrorCode() : convert a `size_t` function result into a `ZSTD_ErrorCode` enum type, which can be used to compare directly with enum list published into "error_public.h" */ -ZSTD_ErrorCode ZSTD_getErrorCode(size_t functionResult); -const char* ZSTD_getErrorString(ZSTD_ErrorCode code); +ZSTDERRORLIB_API ZSTD_ErrorCode ZSTD_getErrorCode(size_t functionResult); +ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); #if defined (__cplusplus) diff --git a/lib/deprecated/zbuff_common.c b/lib/deprecated/zbuff_common.c new file mode 100644 index 000000000..9fff6eb20 --- /dev/null +++ b/lib/deprecated/zbuff_common.c @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/*-************************************* +* Dependencies +***************************************/ +#include "error_private.h" +#include "zbuff.h" + +/*-**************************************** +* ZBUFF Error Management (deprecated) +******************************************/ + +/*! ZBUFF_isError() : +* tells if a return value is an error code */ +unsigned ZBUFF_isError(size_t errorCode) { return ERR_isError(errorCode); } +/*! ZBUFF_getErrorName() : +* provides error code string from function result (useful for debugging) */ +const char* ZBUFF_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); } + diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index 642a43516..d6cf1839e 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -19,15 +19,18 @@ extern "C" { #include /* size_t */ -/*====== Export for Windows ======*/ -/*! -* ZSTD_DLL_EXPORT : -* Enable exporting of functions when building a Windows DLL -*/ -#if defined(_WIN32) && defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) -# define ZDICTLIB_API __declspec(dllexport) +/* ===== ZDICTLIB_API : control library symbols visibility ===== */ +#if defined(__GNUC__) && (__GNUC__ >= 4) +# define ZDICTLIB_VISIBILITY __attribute__ ((visibility ("default"))) #else -# define ZDICTLIB_API +# define ZDICTLIB_VISIBILITY +#endif +#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) +# define ZDICTLIB_API __declspec(dllexport) ZDICTLIB_VISIBILITY +#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) +# define ZDICTLIB_API __declspec(dllimport) ZDICTLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +#else +# define ZDICTLIB_API ZDICTLIB_VISIBILITY #endif @@ -79,7 +82,7 @@ typedef struct { or an error code, which can be tested by ZDICT_isError(). note : ZDICT_trainFromBuffer_advanced() will send notifications into stderr if instructed to, using notificationLevel>0. */ -size_t ZDICT_trainFromBuffer_advanced(void* dictBuffer, size_t dictBufferCapacity, +ZDICTLIB_API size_t ZDICT_trainFromBuffer_advanced(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_params_t parameters); @@ -97,7 +100,7 @@ size_t ZDICT_trainFromBuffer_advanced(void* dictBuffer, size_t dictBufferCapacit starting from its beginning. @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`). */ -size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, +ZDICTLIB_API size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); diff --git a/lib/zstd.h b/lib/zstd.h index b02e16b42..64e782b58 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -20,13 +20,16 @@ extern "C" { /* ===== ZSTDLIB_API : control library symbols visibility ===== */ #if defined(__GNUC__) && (__GNUC__ >= 4) -# define ZSTDLIB_API __attribute__ ((visibility ("default"))) -#elif defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) -# define ZSTDLIB_API __declspec(dllexport) -#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) -# define ZSTDLIB_API __declspec(dllimport) /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +# define ZSTDLIB_VISIBILITY __attribute__ ((visibility ("default"))) #else -# define ZSTDLIB_API +# define ZSTDLIB_VISIBILITY +#endif +#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1) +# define ZSTDLIB_API __declspec(dllexport) ZSTDLIB_VISIBILITY +#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1) +# define ZSTDLIB_API __declspec(dllimport) ZSTDLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +#else +# define ZSTDLIB_API ZSTDLIB_VISIBILITY #endif @@ -463,13 +466,13 @@ ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict); * Provides the dictID stored within dictionary. * if @return == 0, the dictionary is not conformant with Zstandard specification. * It can still be loaded, but as a content-only dictionary. */ -unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize); +ZSTDLIB_API unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize); /*! ZSTD_getDictID_fromDDict() : * Provides the dictID of the dictionary loaded into `ddict`. * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty. * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */ -unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict); +ZSTDLIB_API unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict); /*! ZSTD_getDictID_fromFrame() : * Provides the dictID required to decompressed the frame stored within `src`. @@ -481,7 +484,7 @@ unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict); * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`). * - This is not a Zstandard frame. * When identifying the exact failure cause, it's possible to used ZSTD_getFrameParams(), which will provide a more precise error code. */ -unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); +ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); /******************************************************************** From 61e62c014f59757ae4af814ef3a096d060fe4781 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 16 Dec 2016 13:29:23 -0800 Subject: [PATCH 022/227] Test that all API symbols are exported --- .travis.yml | 2 +- appveyor.yml | 3 +- tests/.gitignore | 1 + tests/Makefile | 19 ++++++- tests/symbols.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 tests/symbols.c diff --git a/.travis.yml b/.travis.yml index 148a98f1b..92c07718f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ matrix: os: linux sudo: false - - env: Ubu=12.04cont Cmd="make zlibwrapper && make clean && make -C tests test-zstd-nolegacy && make clean && make cmaketest && make clean && make -C contrib/pzstd googletest pzstd tests check && make -C contrib/pzstd clean" + - env: Ubu=12.04cont Cmd="make zlibwrapper && make clean && make -C tests test-symbols && make clean && make -C tests test-zstd-nolegacy && make clean && make cmaketest && make clean && make -C contrib/pzstd googletest pzstd tests check && make -C contrib/pzstd clean" os: linux sudo: false language: cpp diff --git a/appveyor.yml b/appveyor.yml index 27c83c04e..1af9da711 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -63,7 +63,8 @@ build_script: make -v && cc -v && ECHO make %MAKE_PARAMS% && - make %MAKE_PARAMS% + make %MAKE_PARAMS% && + make -C tests test-symbols ) - if [%COMPILER%]==[gcc] if [%PLATFORM%]==[mingw64] ( COPY programs\zstd.exe bin\zstd.exe && diff --git a/tests/.gitignore b/tests/.gitignore index b558ac258..59a7a1bd2 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -12,6 +12,7 @@ paramgrill paramgrill32 roundTripCrash longmatch +symbols # Tmp test directory zstdtest diff --git a/tests/Makefile b/tests/Makefile index d7d25026a..b27d1cc60 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -28,7 +28,7 @@ PYTHON ?= python3 TESTARTEFACT := versionsTest namespaceTest -CPPFLAGS= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/dictBuilder -I$(PRGDIR) +CPPFLAGS= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) CFLAGS ?= -O3 CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef @@ -127,6 +127,15 @@ roundTripCrash : $(ZSTD_FILES) roundTripCrash.c longmatch : $(ZSTD_FILES) longmatch.c $(CC) $(FLAGS) $^ -o $@$(EXT) +symbols : symbols.c + $(MAKE) -C $(ZSTDDIR) libzstd +ifneq (,$(filter Windows%,$(OS))) + cp $(ZSTDDIR)/dll/libzstd.dll . + $(CC) $(FLAGS) $^ -o $@$(EXT) -DZSTD_DLL_IMPORT=1 libzstd.dll +else + $(CC) $(FLAGS) $^ -o $@$(EXT) -Wl,-rpath=$(ZSTDDIR) $(ZSTDDIR)/libzstd.so +endif + namespaceTest: if $(CC) namespaceTest.c ../lib/common/xxhash.c -o $@ ; then echo compilation should fail; exit 1 ; fi $(RM) $@ @@ -142,8 +151,9 @@ clean: fullbench$(EXT) fullbench32$(EXT) \ fullbench-lib$(EXT) fullbench-dll$(EXT) \ fuzzer$(EXT) fuzzer32$(EXT) zbufftest$(EXT) zbufftest32$(EXT) \ - zstreamtest$(EXT) zstreamtest32$(EXT) \ - datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) + zstreamtest$(EXT) zstreamtest32$(EXT) \ + datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) \ + symbols$(EXT) @echo Cleaning completed @@ -243,4 +253,7 @@ test-zstream32: zstreamtest32 test-longmatch: longmatch $(QEMU_SYS) ./longmatch +test-symbols: symbols + $(QEMU_SYS) ./symbols + endif diff --git a/tests/symbols.c b/tests/symbols.c new file mode 100644 index 000000000..8d03df2fd --- /dev/null +++ b/tests/symbols.c @@ -0,0 +1,144 @@ +#include +#include "zstd_errors.h" +#define ZSTD_STATIC_LINKING_ONLY +#include "zstd.h" +#define ZBUFF_DISABLE_DEPRECATE_WARNINGS +#define ZBUFF_STATIC_LINKING_ONLY +#include "zbuff.h" +#define ZDICT_STATIC_LINKING_ONLY +#include "zdict.h" + +static const void *symbols[] = { +/* zstd.h */ + &ZSTD_versionNumber, + &ZSTD_compress, + &ZSTD_decompress, + &ZSTD_getDecompressedSize, + &ZSTD_maxCLevel, + &ZSTD_compressBound, + &ZSTD_isError, + &ZSTD_getErrorName, + &ZSTD_createCCtx, + &ZSTD_freeCCtx, + &ZSTD_compressCCtx, + &ZSTD_createDCtx, + &ZSTD_freeDCtx, + &ZSTD_decompressDCtx, + &ZSTD_compress_usingDict, + &ZSTD_decompress_usingDict, + &ZSTD_createCDict, + &ZSTD_freeCDict, + &ZSTD_compress_usingCDict, + &ZSTD_createDDict, + &ZSTD_freeDDict, + &ZSTD_decompress_usingDDict, + &ZSTD_createCStream, + &ZSTD_freeCStream, + &ZSTD_initCStream, + &ZSTD_compressStream, + &ZSTD_flushStream, + &ZSTD_endStream, + &ZSTD_CStreamInSize, + &ZSTD_CStreamOutSize, + &ZSTD_createDStream, + &ZSTD_freeDStream, + &ZSTD_initDStream, + &ZSTD_decompressStream, + &ZSTD_DStreamInSize, + &ZSTD_DStreamOutSize, +/* zstd.h: advanced functions */ + &ZSTD_estimateCCtxSize, + &ZSTD_createCCtx_advanced, + &ZSTD_sizeof_CCtx, + &ZSTD_createCDict_advanced, + &ZSTD_sizeof_CDict, + &ZSTD_getCParams, + &ZSTD_getParams, + &ZSTD_checkCParams, + &ZSTD_adjustCParams, + &ZSTD_compress_advanced, + &ZSTD_isFrame, + &ZSTD_estimateDCtxSize, + &ZSTD_createDCtx_advanced, + &ZSTD_sizeof_DCtx, + &ZSTD_sizeof_DDict, + &ZSTD_getDictID_fromDict, + &ZSTD_getDictID_fromDDict, + &ZSTD_getDictID_fromFrame, + &ZSTD_createCStream_advanced, + &ZSTD_initCStream_srcSize, + &ZSTD_initCStream_usingDict, + &ZSTD_initCStream_advanced, + &ZSTD_initCStream_usingCDict, + &ZSTD_resetCStream, + &ZSTD_sizeof_CStream, + &ZSTD_createDStream_advanced, + &ZSTD_initDStream_usingDict, + &ZSTD_setDStreamParameter, + &ZSTD_initDStream_usingDDict, + &ZSTD_resetDStream, + &ZSTD_sizeof_DStream, + &ZSTD_compressBegin, + &ZSTD_compressBegin_usingDict, + &ZSTD_compressBegin_advanced, + &ZSTD_copyCCtx, + &ZSTD_compressContinue, + &ZSTD_compressEnd, + &ZSTD_getFrameParams, + &ZSTD_decompressBegin, + &ZSTD_decompressBegin_usingDict, + &ZSTD_copyDCtx, + &ZSTD_nextSrcSizeToDecompress, + &ZSTD_decompressContinue, + &ZSTD_nextInputType, + &ZSTD_getBlockSizeMax, + &ZSTD_compressBlock, + &ZSTD_decompressBlock, + &ZSTD_insertBlock, +/* zstd_errors.h */ + &ZSTD_getErrorCode, + &ZSTD_getErrorString, +/* zbuff.h */ + &ZBUFF_createCCtx, + &ZBUFF_freeCCtx, + &ZBUFF_compressInit, + &ZBUFF_compressInitDictionary, + &ZBUFF_compressContinue, + &ZBUFF_compressFlush, + &ZBUFF_compressEnd, + &ZBUFF_createDCtx, + &ZBUFF_freeDCtx, + &ZBUFF_decompressInit, + &ZBUFF_decompressInitDictionary, + &ZBUFF_decompressContinue, + &ZBUFF_isError, + &ZBUFF_getErrorName, + &ZBUFF_recommendedCInSize, + &ZBUFF_recommendedCOutSize, + &ZBUFF_recommendedDInSize, + &ZBUFF_recommendedDOutSize, +/* zbuff.h: advanced functions */ + &ZBUFF_createCCtx_advanced, + &ZBUFF_createDCtx_advanced, + &ZBUFF_compressInit_advanced, +/* zdict.h */ + &ZDICT_trainFromBuffer, + &ZDICT_getDictID, + &ZDICT_isError, + &ZDICT_getErrorName, +/* zdict.h: advanced functions */ + &ZDICT_trainFromBuffer_advanced, + &ZDICT_addEntropyTablesFromBuffer, + NULL, +}; + +int main(int argc, const char** argv) { + const void **symbol; + (void)argc; + (void)argv; + + for (symbol = symbols; *symbol != NULL; ++symbol) { + printf("%p\n", *symbol); + } + return 0; +} From d46ecb58a5d537fad672fdc15b3f91da8e0d17be Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 17 Dec 2016 16:28:12 +0100 Subject: [PATCH 023/227] added dll compilation tests --- .travis.yml | 22 ++++++++++++++++++---- NEWS | 3 +++ lib/dictBuilder/zdict.c | 5 ++--- lib/zstd.h | 2 +- programs/dibio.c | 2 +- tests/.gitignore | 2 ++ tests/Makefile | 27 ++++++++++++++++++++------- 7 files changed, 47 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 92c07718f..ef1e67e35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,7 +69,7 @@ matrix: # Ubuntu 14.04 LTS Server Edition 64 bit - - env: Ubu=14.04 Cmd="make armtest && make clean && make aarch64test" + - env: Ubu=14.04 Cmd="make armtest" dist: trusty sudo: required addons: @@ -78,7 +78,17 @@ matrix: - qemu-system-arm - qemu-user-static - gcc-arm-linux-gnueabi - - libc6-dev-armel-cross + - libc6-dev-armel-cross + + # Ubuntu 14.04 LTS Server Edition 64 bit + - env: Ubu=14.04 Cmd="make aarch64test" + dist: trusty + sudo: required + addons: + apt: + packages: + - qemu-system-arm + - qemu-user-static - gcc-aarch64-linux-gnu - libc6-dev-arm64-cross @@ -91,6 +101,7 @@ matrix: - qemu-system-ppc - qemu-user-static - gcc-powerpc-linux-gnu + - libc6-dev-armel-cross - env: Ubu=14.04 Cmd='make -C lib all && CFLAGS="-O1 -g" make -C zlibWrapper valgrindTest && make -C tests valgrindTest' os: linux @@ -101,7 +112,10 @@ matrix: packages: - valgrind - - env: Ubu=14.04 Cmd="make gpptest && make clean && make gnu90test && make clean && make c99test && make clean && make gnu99test && make clean && make clangtest && make clean && make -C contrib/pzstd googletest32 && make -C contrib/pzstd all32 && make -C contrib/pzstd check && make -C contrib/pzstd clean" + - env: Ubu=14.04 Cmd="make gpptest && make clean && make gnu90test && make clean \ + && make c99test && make clean && make gnu99test && make clean \ + && make clangtest && make clean && make -C contrib/pzstd googletest32 \ + && make -C contrib/pzstd all32 && make -C contrib/pzstd check && make -C contrib/pzstd clean" os: linux dist: trusty sudo: required @@ -127,7 +141,7 @@ matrix: - libc6-dev-i386 - gcc-multilib - - env: Ubu=14.04 Cmd="make gcc5test && make clean && make gcc6test" + - env: Ubu=14.04 Cmd="make gcc5test && make clean && make gcc6test && make clean && make -C tests dll" os: linux dist: trusty sudo: required diff --git a/NEWS b/NEWS index 9b0001636..0fc90aff2 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,6 @@ +v1.1.3 +API : fix : all symbols properly exposed in libzstd, by Nick Terrell + v1.1.2 API : streaming : decompression : changed : automatic implicit reset when chain-decoding new frames without init API : experimental : added : dictID retrieval functions, and ZSTD_initCStream_srcSize() diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index ea50b54ad..b539c09fa 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -36,12 +36,11 @@ #include /* clock */ #include "mem.h" /* read */ -#include "error_private.h" #include "fse.h" /* FSE_normalizeCount, FSE_writeNCount */ #define HUF_STATIC_LINKING_ONLY -#include "huf.h" +#include "huf.h" /* HUF_buildCTable, HUF_writeCTable */ #include "zstd_internal.h" /* includes zstd.h */ -#include "xxhash.h" +#include "xxhash.h" /* XXH64 */ #include "divsufsort.h" #ifndef ZDICT_STATIC_LINKING_ONLY # define ZDICT_STATIC_LINKING_ONLY diff --git a/lib/zstd.h b/lib/zstd.h index 64e782b58..7eda69877 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -56,7 +56,7 @@ extern "C" { /*------ Version ------*/ #define ZSTD_VERSION_MAJOR 1 #define ZSTD_VERSION_MINOR 1 -#define ZSTD_VERSION_RELEASE 2 +#define ZSTD_VERSION_RELEASE 3 #define ZSTD_LIB_VERSION ZSTD_VERSION_MAJOR.ZSTD_VERSION_MINOR.ZSTD_VERSION_RELEASE #define ZSTD_QUOTE(str) #str diff --git a/programs/dibio.c b/programs/dibio.c index a793669cb..6fce405b9 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -183,7 +183,7 @@ int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, size_t* const fileSizes = (size_t*)malloc(nbFiles * sizeof(size_t)); unsigned long long const totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, nbFiles); size_t const maxMem = DiB_findMaxMem(totalSizeToLoad * MEMMULT) / MEMMULT; - size_t benchedSize = MIN (maxMem, (size_t)totalSizeToLoad); + size_t benchedSize = (size_t) MIN ((unsigned long long)maxMem, totalSizeToLoad); void* const srcBuffer = malloc(benchedSize+NOISELENGTH); int result = 0; diff --git a/tests/.gitignore b/tests/.gitignore index 59a7a1bd2..586c17b6c 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -3,10 +3,12 @@ fullbench fullbench32 fuzzer fuzzer32 +fuzzer-dll zbufftest zbufftest32 zstreamtest zstreamtest32 +zstreamtest-dll datagen paramgrill paramgrill32 diff --git a/tests/Makefile b/tests/Makefile index b27d1cc60..6e37d160e 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -28,12 +28,12 @@ PYTHON ?= python3 TESTARTEFACT := versionsTest namespaceTest -CPPFLAGS= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) -CFLAGS ?= -O3 -CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ +CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) +CFLAGS ?= -O3 +CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef -CFLAGS += $(MOREFLAGS) -FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) +CFLAGS += $(MOREFLAGS) +FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c @@ -65,6 +65,8 @@ all: fullbench fuzzer zstreamtest paramgrill datagen zbufftest all32: fullbench32 fuzzer32 zstreamtest32 zbufftest32 +dll: fuzzer-dll zstreamtest-dll + zstd: @@ -93,11 +95,16 @@ fullbench-dll: $(PRGDIR)/datagen.c fullbench.c $(MAKE) -C $(ZSTDDIR) libzstd $(CC) $(FLAGS) $^ -o $@$(EXT) -DZSTD_DLL_IMPORT=1 $(ZSTDDIR)/dll/libzstd.dll -fuzzer : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c fuzzer.c +fuzzer : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c fuzzer.c $(CC) $(FLAGS) $^ -o $@$(EXT) fuzzer32 : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c fuzzer.c - $(CC) -m32 $(FLAGS) $^ -o $@$(EXT) + $(CC) -m32 $(FLAGS) $^ -o $@$(EXT) + +fuzzer-dll : LDFLAGS+= -L$(ZSTDDIR) -lzstd +fuzzer-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c fuzzer.c + $(MAKE) -C $(ZSTDDIR) libzstd + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@$(EXT) zbufftest : CPPFLAGS += -I$(ZSTDDIR)/deprecated zbufftest : CFLAGS += -Wno-deprecated-declarations # required to silence deprecation warnings @@ -115,6 +122,11 @@ zstreamtest : $(ZSTD_FILES) $(PRGDIR)/datagen.c zstreamtest.c zstreamtest32 : $(ZSTD_FILES) $(PRGDIR)/datagen.c zstreamtest.c $(CC) -m32 $(FLAGS) $^ -o $@$(EXT) +zstreamtest-dll : LDFLAGS+= -L$(ZSTDDIR) -lzstd +zstreamtest-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c zstreamtest.c + $(MAKE) -C $(ZSTDDIR) libzstd + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@$(EXT) + paramgrill : $(ZSTD_FILES) $(PRGDIR)/datagen.c paramgrill.c $(CC) $(FLAGS) $^ -lm -o $@$(EXT) @@ -151,6 +163,7 @@ clean: fullbench$(EXT) fullbench32$(EXT) \ fullbench-lib$(EXT) fullbench-dll$(EXT) \ fuzzer$(EXT) fuzzer32$(EXT) zbufftest$(EXT) zbufftest32$(EXT) \ + fuzzer-dll$(EXT) zstreamtest-dll$(EXT) \ zstreamtest$(EXT) zstreamtest32$(EXT) \ datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) \ symbols$(EXT) From 31ff2a23be05bd01f8dfebb2015eaec34b9aa1a3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 17 Dec 2016 19:10:10 +0100 Subject: [PATCH 024/227] fix Travis long test list; added zbufftest-dll --- .travis.yml | 6 +++--- tests/Makefile | 13 ++++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index ef1e67e35..6bf99f1bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -112,9 +112,9 @@ matrix: packages: - valgrind - - env: Ubu=14.04 Cmd="make gpptest && make clean && make gnu90test && make clean \ - && make c99test && make clean && make gnu99test && make clean \ - && make clangtest && make clean && make -C contrib/pzstd googletest32 \ + - env: Ubu=14.04 Cmd="make gpptest && make clean && make gnu90test && make clean + && make c99test && make clean && make gnu99test && make clean + && make clangtest && make clean && make -C contrib/pzstd googletest32 && make -C contrib/pzstd all32 && make -C contrib/pzstd check && make -C contrib/pzstd clean" os: linux dist: trusty diff --git a/tests/Makefile b/tests/Makefile index 6e37d160e..abf89f2f6 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -57,7 +57,7 @@ ZSTREAM_TESTTIME = -T2mn FUZZERTEST= -T5mn ZSTDRTTEST= --test-large-data -.PHONY: default all all32 clean test test32 test-all namespaceTest versionsTest +.PHONY: default all all32 dll clean test test32 test-all namespaceTest versionsTest default: fullbench @@ -65,7 +65,7 @@ all: fullbench fuzzer zstreamtest paramgrill datagen zbufftest all32: fullbench32 fuzzer32 zstreamtest32 zbufftest32 -dll: fuzzer-dll zstreamtest-dll +dll: fuzzer-dll zstreamtest-dll zbufftest-dll @@ -116,6 +116,13 @@ zbufftest32 : CFLAGS += -Wno-deprecated-declarations -m32 zbufftest32 : $(ZSTD_FILES) $(ZBUFF_FILES) $(PRGDIR)/datagen.c zbufftest.c $(CC) $(FLAGS) $^ -o $@$(EXT) +zbufftest-dll : CPPFLAGS += -I$(ZSTDDIR)/deprecated +zbufftest-dll : CFLAGS += -Wno-deprecated-declarations # required to silence deprecation warnings +zbufftest-dll : LDFLAGS+= -L$(ZSTDDIR) -lzstd +zbufftest-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c zbufftest.c + $(MAKE) -C $(ZSTDDIR) libzstd + $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@$(EXT) + zstreamtest : $(ZSTD_FILES) $(PRGDIR)/datagen.c zstreamtest.c $(CC) $(FLAGS) $^ -o $@$(EXT) @@ -163,7 +170,7 @@ clean: fullbench$(EXT) fullbench32$(EXT) \ fullbench-lib$(EXT) fullbench-dll$(EXT) \ fuzzer$(EXT) fuzzer32$(EXT) zbufftest$(EXT) zbufftest32$(EXT) \ - fuzzer-dll$(EXT) zstreamtest-dll$(EXT) \ + fuzzer-dll$(EXT) zstreamtest-dll$(EXT) zbufftest-dll$(EXT)\ zstreamtest$(EXT) zstreamtest32$(EXT) \ datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) \ symbols$(EXT) From 36321d2e329900d09c57c5ee6a4080234f8e333d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 17 Dec 2016 19:37:55 +0100 Subject: [PATCH 025/227] updated NEWS --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 0fc90aff2..9a341781e 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,5 @@ v1.1.3 +cli : new : commands for advanced parameters, by Przemyslaw Skibinski API : fix : all symbols properly exposed in libzstd, by Nick Terrell v1.1.2 From 1496c3dc47fc0cf849fb82d7e6b13a1c19fdd34a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 18 Dec 2016 11:58:23 +0100 Subject: [PATCH 026/227] Fix : size estimation when some samples are very large --- lib/dictBuilder/zdict.c | 2 +- programs/dibio.c | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index b539c09fa..921e37886 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -569,7 +569,7 @@ static void ZDICT_countEStats(EStats_ress_t esr, ZSTD_parameters params, if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_copyCCtx failed \n"); return; } } cSize = ZSTD_compressBlock(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_ABSOLUTEMAX, src, srcSize); - if (ZSTD_isError(cSize)) { DISPLAYLEVEL(1, "warning : could not compress sample size %u \n", (U32)srcSize); return; } + if (ZSTD_isError(cSize)) { DISPLAYLEVEL(3, "warning : could not compress sample size %u \n", (U32)srcSize); return; } if (cSize) { /* if == 0; block is not compressible */ const seqStore_t* seqStorePtr = ZSTD_getSeqStore(esr.zc); diff --git a/programs/dibio.c b/programs/dibio.c index 6fce405b9..fa4241b14 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -31,7 +31,8 @@ #define MB *(1 <<20) #define GB *(1U<<30) -#define MEMMULT 11 +#define SAMPLESIZE_MAX (128 KB) +#define MEMMULT 11 /* rough estimation : memory cost to analyze 1 byte of sample */ static const size_t maxMemory = (sizeof(size_t) == 4) ? (2 GB - 64 MB) : ((size_t)(512 MB) << sizeof(size_t)); #define NOISELENGTH 32 @@ -97,7 +98,7 @@ static unsigned DiB_loadFiles(void* buffer, size_t* bufferSizePtr, for (n=0; n *bufferSizePtr-pos) break; { FILE* const f = fopen(fileName, "rb"); if (f==NULL) EXM_THROW(10, "zstd: dictBuilder: %s %s ", fileName, strerror(errno)); @@ -163,6 +164,21 @@ static void DiB_saveDict(const char* dictFileName, } +static int g_tooLargeSamples = 0; +static U64 DiB_getTotalCappedFileSize(const char** fileNamesTable, unsigned nbFiles) +{ + U64 total = 0; + unsigned n; + for (n=0; n 2*SAMPLESIZE_MAX); + } + return total; +} + + /*! ZDICT_trainFromBuffer_unsafe() : Strictly Internal use only !! Same as ZDICT_trainFromBuffer_advanced(), but does not control `samplesBuffer`. @@ -181,7 +197,7 @@ int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, { void* const dictBuffer = malloc(maxDictSize); size_t* const fileSizes = (size_t*)malloc(nbFiles * sizeof(size_t)); - unsigned long long const totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, nbFiles); + unsigned long long const totalSizeToLoad = DiB_getTotalCappedFileSize(fileNamesTable, nbFiles); size_t const maxMem = DiB_findMaxMem(totalSizeToLoad * MEMMULT) / MEMMULT; size_t benchedSize = (size_t) MIN ((unsigned long long)maxMem, totalSizeToLoad); void* const srcBuffer = malloc(benchedSize+NOISELENGTH); @@ -190,7 +206,12 @@ int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, /* Checks */ if ((!fileSizes) || (!srcBuffer) || (!dictBuffer)) EXM_THROW(12, "not enough memory for DiB_trainFiles"); /* should not happen */ g_displayLevel = params.notificationLevel; - if (nbFiles < 5) { + if (g_tooLargeSamples) { + DISPLAYLEVEL(2, "! Warning : some samples are very large \n"); + DISPLAYLEVEL(2, "! Note that dictionary is only useful for small files or beginning of large files. \n"); + DISPLAYLEVEL(2, "! As a consequence, only the first %u bytes of each file are loaded \n", SAMPLESIZE_MAX); + } + if ((nbFiles < 5) || (totalSizeToLoad < 9 * (unsigned long long)maxDictSize)) { DISPLAYLEVEL(2, "! Warning : nb of samples too low for proper processing ! \n"); DISPLAYLEVEL(2, "! Please provide _one file per sample_. \n"); DISPLAYLEVEL(2, "! Do not concatenate samples together into a single file, \n"); From d564faa3c6222c9c485f83160c3f2be3b4a82495 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 18 Dec 2016 21:39:15 +0100 Subject: [PATCH 027/227] fix : ZSTD_initCStream_srcSize() correctly set srcSize in frame header --- lib/compress/zstd_compress.c | 5 +++-- tests/zstreamtest.c | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 3d10fbd96..7fd73baa0 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2900,7 +2900,7 @@ size_t ZSTD_CStreamOutSize(void) { return ZSTD_compressBound(ZSTD_BLOCKSIZE_ABSO size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize) { - if (zcs->inBuffSize==0) return ERROR(stage_wrong); /* zcs has not been init at least once */ + if (zcs->inBuffSize==0) return ERROR(stage_wrong); /* zcs has not been init at least once => can't reset */ if (zcs->cdict) CHECK_F(ZSTD_compressBegin_usingCDict(zcs->cctx, zcs->cdict, pledgedSrcSize)) else CHECK_F(ZSTD_compressBegin_advanced(zcs->cctx, NULL, 0, zcs->params, pledgedSrcSize)); @@ -2967,7 +2967,8 @@ size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t di size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize) { - ZSTD_parameters const params = ZSTD_getParams(compressionLevel, pledgedSrcSize, 0); + ZSTD_parameters params = ZSTD_getParams(compressionLevel, pledgedSrcSize, 0); + if (pledgedSrcSize) params.fParams.contentSizeFlag = 1; return ZSTD_initCStream_advanced(zcs, NULL, 0, params, pledgedSrcSize); } diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 7783fe11b..ce6193085 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -248,7 +248,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo /* _srcSize compression test */ DISPLAYLEVEL(4, "test%3i : compress_srcSize %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); ZSTD_initCStream_srcSize(zc, 1, CNBufferSize); - outBuff.dst = (char*)(compressedBuffer)+cSize; + outBuff.dst = (char*)(compressedBuffer); outBuff.size = compressedBufferSize; outBuff.pos = 0; inBuff.src = CNBuffer; @@ -259,12 +259,16 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */ { size_t const r = ZSTD_endStream(zc, &outBuff); if (r != 0) goto _output_error; } /* error, or some data not flushed */ + { unsigned long long origSize = ZSTD_getDecompressedSize(outBuff.dst, outBuff.pos); + DISPLAY("outBuff.pos : %u \n", (U32)outBuff.pos); + DISPLAY("origSize = %u \n", (U32)origSize); + if ((size_t)origSize != CNBufferSize) goto _output_error; } /* exact original size must be present */ DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/COMPRESSIBLE_NOISE_LENGTH*100); /* wrong _srcSize compression test */ DISPLAYLEVEL(4, "test%3i : wrong srcSize : %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH-1); ZSTD_initCStream_srcSize(zc, 1, CNBufferSize-1); - outBuff.dst = (char*)(compressedBuffer)+cSize; + outBuff.dst = (char*)(compressedBuffer); outBuff.size = compressedBufferSize; outBuff.pos = 0; inBuff.src = CNBuffer; From 08e8d30d1b8c8fac18aa085c26cba53336917557 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Mon, 19 Dec 2016 09:28:55 +0100 Subject: [PATCH 028/227] fixed CMake compilation with Visual Studio --- appveyor.yml | 7 +++++++ build/cmake/lib/CMakeLists.txt | 4 ++-- build/cmake/programs/CMakeLists.txt | 7 ++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 1af9da711..434d80f7d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -55,6 +55,13 @@ build_script: make -C contrib\pzstd tests && make -C contrib\pzstd check && make -C contrib\pzstd clean + ECHO *** && + ECHO *** Building cmake for %PLATFORM% && + ECHO *** && + mkdir build\cmake\build && + cd build\cmake\build && + cmake -G "Visual Studio 14 2015 Win64" .. && + cd ..\..\.. ) - if [%COMPILER%]==[gcc] ( ECHO *** && diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index 65942b412..b8510f7be 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -110,8 +110,8 @@ IF (ZSTD_LEGACY_SUPPORT) ENDIF (ZSTD_LEGACY_SUPPORT) IF (MSVC) - SET(MSVC_RESOURCE_DIR ${ROOT_DIR}/build/VS2010/zstdlib) - SET(PlatformDependResources ${MSVC_RESOURCE_DIR}/zstdlib.rc) + SET(MSVC_RESOURCE_DIR ${ROOT_DIR}/build/VS2010/libzstd-dll) + SET(PlatformDependResources ${MSVC_RESOURCE_DIR}/libzstd-dll.rc) ENDIF (MSVC) # Split project to static and shared libraries build diff --git a/build/cmake/programs/CMakeLists.txt b/build/cmake/programs/CMakeLists.txt index 26cd6c0cc..c2931b096 100644 --- a/build/cmake/programs/CMakeLists.txt +++ b/build/cmake/programs/CMakeLists.txt @@ -47,7 +47,12 @@ IF (ZSTD_LEGACY_SUPPORT) INCLUDE_DIRECTORIES(${PROGRAMS_LEGACY_DIR} ${LIBRARY_DIR}/legacy) ENDIF (ZSTD_LEGACY_SUPPORT) -ADD_EXECUTABLE(zstd ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/fileio.c ${PROGRAMS_DIR}/bench.c ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/dibio.c) +IF (MSVC) + SET(MSVC_RESOURCE_DIR ${ROOT_DIR}/build/VS2010/zstd) + SET(PlatformDependResources ${MSVC_RESOURCE_DIR}/zstd.rc) +ENDIF (MSVC) + +ADD_EXECUTABLE(zstd ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/fileio.c ${PROGRAMS_DIR}/bench.c ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/dibio.c ${PlatformDependResources}) TARGET_LINK_LIBRARIES(zstd libzstd_static) IF (UNIX) From 6914864f4016bf9173a2f969ee8d6faaaea94b56 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Mon, 19 Dec 2016 09:47:45 +0100 Subject: [PATCH 029/227] updated libzstd-dll.rc --- appveyor.yml | 16 ++++++++-------- build/VS2010/libzstd-dll/libzstd-dll.rc | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 434d80f7d..cd082a415 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,7 +25,6 @@ install: - MKDIR bin - if [%COMPILER%]==[gcc] SET PATH_ORIGINAL=%PATH% - if [%COMPILER%]==[gcc] ( - SET "CLANG_PARAMS=-C tests zstd fullbench fuzzer paramgrill datagen CC=clang MOREFLAGS="--target=x86_64-w64-mingw32 -Werror -Wconversion -Wno-sign-conversion"" && SET "PATH_MINGW32=c:\MinGW\bin;c:\MinGW\usr\bin" && SET "PATH_MINGW64=c:\msys64\mingw64\bin;c:\msys64\usr\bin" && COPY C:\msys64\usr\bin\make.exe C:\MinGW\bin\make.exe && @@ -43,9 +42,17 @@ build_script: ECHO *** && ECHO *** Building clang && ECHO *** && + SET "CLANG_PARAMS=-C tests zstd fullbench fuzzer paramgrill datagen CC=clang MOREFLAGS="--target=x86_64-w64-mingw32 -Werror -Wconversion -Wno-sign-conversion"" && ECHO make %CLANG_PARAMS% && make %CLANG_PARAMS% && COPY tests\fuzzer.exe tests\fuzzer_clang.exe && + ECHO *** && + ECHO *** Building cmake for %PLATFORM% && + ECHO *** && + mkdir build\cmake\build && + cd build\cmake\build && + cmake -G "Visual Studio 14 2015 Win64" .. && + cd ..\..\.. && make clean && ECHO *** && ECHO *** Building pzstd for %PLATFORM% && @@ -55,13 +62,6 @@ build_script: make -C contrib\pzstd tests && make -C contrib\pzstd check && make -C contrib\pzstd clean - ECHO *** && - ECHO *** Building cmake for %PLATFORM% && - ECHO *** && - mkdir build\cmake\build && - cd build\cmake\build && - cmake -G "Visual Studio 14 2015 Win64" .. && - cd ..\..\.. ) - if [%COMPILER%]==[gcc] ( ECHO *** && diff --git a/build/VS2010/libzstd-dll/libzstd-dll.rc b/build/VS2010/libzstd-dll/libzstd-dll.rc index 72ea168d2..ee9f56280 100644 --- a/build/VS2010/libzstd-dll/libzstd-dll.rc +++ b/build/VS2010/libzstd-dll/libzstd-dll.rc @@ -32,11 +32,11 @@ BEGIN BEGIN BLOCK "040904B0" BEGIN - VALUE "CompanyName", "Yann Collet" - VALUE "FileDescription", "Fast and efficient compression algorithm" + VALUE "CompanyName", "Yann Collet, Facebook, Inc." + VALUE "FileDescription", "Zstandard - Fast and efficient compression algorithm" VALUE "FileVersion", ZSTD_VERSION_STRING VALUE "InternalName", "libzstd.dll" - VALUE "LegalCopyright", "Copyright (C) 2013-2016, Yann Collet" + VALUE "LegalCopyright", "Copyright (c) 2013-present, Yann Collet, Facebook, Inc." VALUE "OriginalFilename", "libzstd.dll" VALUE "ProductName", "Zstandard" VALUE "ProductVersion", ZSTD_VERSION_STRING From 95ff9a1ed77e31c2a9c76611245a5691d6879358 Mon Sep 17 00:00:00 2001 From: Jacques Germishuys Date: Sun, 18 Dec 2016 18:17:25 +0200 Subject: [PATCH 030/227] cmake msvc: correct resources --- build/cmake/lib/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index 65942b412..b8510f7be 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -110,8 +110,8 @@ IF (ZSTD_LEGACY_SUPPORT) ENDIF (ZSTD_LEGACY_SUPPORT) IF (MSVC) - SET(MSVC_RESOURCE_DIR ${ROOT_DIR}/build/VS2010/zstdlib) - SET(PlatformDependResources ${MSVC_RESOURCE_DIR}/zstdlib.rc) + SET(MSVC_RESOURCE_DIR ${ROOT_DIR}/build/VS2010/libzstd-dll) + SET(PlatformDependResources ${MSVC_RESOURCE_DIR}/libzstd-dll.rc) ENDIF (MSVC) # Split project to static and shared libraries build From 7ee2bd7840e5205cb0fa0481f8f99d15c5bac526 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Mon, 19 Dec 2016 10:58:10 +0100 Subject: [PATCH 031/227] updated appveyor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index cd082a415..c95be3b96 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,6 +25,7 @@ install: - MKDIR bin - if [%COMPILER%]==[gcc] SET PATH_ORIGINAL=%PATH% - if [%COMPILER%]==[gcc] ( + SET "CLANG_PARAMS=-C tests zstd fullbench fuzzer paramgrill datagen CC=clang MOREFLAGS="--target=x86_64-w64-mingw32 -Werror -Wconversion -Wno-sign-conversion"" && SET "PATH_MINGW32=c:\MinGW\bin;c:\MinGW\usr\bin" && SET "PATH_MINGW64=c:\msys64\mingw64\bin;c:\msys64\usr\bin" && COPY C:\msys64\usr\bin\make.exe C:\MinGW\bin\make.exe && @@ -42,7 +43,6 @@ build_script: ECHO *** && ECHO *** Building clang && ECHO *** && - SET "CLANG_PARAMS=-C tests zstd fullbench fuzzer paramgrill datagen CC=clang MOREFLAGS="--target=x86_64-w64-mingw32 -Werror -Wconversion -Wno-sign-conversion"" && ECHO make %CLANG_PARAMS% && make %CLANG_PARAMS% && COPY tests\fuzzer.exe tests\fuzzer_clang.exe && From 85a150eab7fe505b53438f8df04c577eda7a4022 Mon Sep 17 00:00:00 2001 From: Jacques Germishuys Date: Mon, 19 Dec 2016 14:15:47 +0200 Subject: [PATCH 032/227] cmake msvc: resources are not required for the static library --- build/cmake/lib/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index b8510f7be..41fe2733f 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -115,7 +115,7 @@ IF (MSVC) ENDIF (MSVC) # Split project to static and shared libraries build -ADD_LIBRARY(libzstd_static STATIC ${Sources} ${Headers} ${PlatformDependResources}) +ADD_LIBRARY(libzstd_static STATIC ${Sources} ${Headers}) ADD_LIBRARY(libzstd_shared SHARED ${Sources} ${Headers} ${PlatformDependResources}) # Add specific compile definitions for MSVC project From 4beb51f17ceb14451845fc6397f53c9858e74a50 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 20 Dec 2016 10:17:21 +0100 Subject: [PATCH 033/227] tests of ZSTD_compressContinue_extDict --- tests/fullbench.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/fullbench.c b/tests/fullbench.c index 233b4e931..5c7e2cc32 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -179,6 +179,23 @@ size_t local_ZSTD_compressContinue(void* dst, size_t dstCapacity, void* buff2, c return ZSTD_compressEnd(g_zcc, dst, dstCapacity, src, srcSize); } +#define FIRST_BLOCK_SIZE 8 +size_t local_ZSTD_compressContinue_extDict(void* dst, size_t dstCapacity, void* buff2, const void* src, size_t srcSize) +{ + BYTE firstBlockBuf[FIRST_BLOCK_SIZE]; + + (void)buff2; + memcpy(firstBlockBuf, src, FIRST_BLOCK_SIZE); + ZSTD_compressBegin(g_zcc, 1); + + { size_t const compressResult = ZSTD_compressContinue(g_zcc, dst, dstCapacity, firstBlockBuf, FIRST_BLOCK_SIZE); + if (ZSTD_isError(compressResult)) { DISPLAY("local_ZSTD_compressContinue_extDict error : %s\n", ZSTD_getErrorName(compressResult)); return compressResult; } + dst = (BYTE*)dst + compressResult; + dstCapacity -= compressResult; + } + return ZSTD_compressEnd(g_zcc, dst, dstCapacity, (const BYTE*)src + FIRST_BLOCK_SIZE, srcSize - FIRST_BLOCK_SIZE); +} + size_t local_ZSTD_decompressContinue(void* dst, size_t dstCapacity, void* buff2, const void* src, size_t srcSize) { size_t regeneratedSize = 0; @@ -229,6 +246,9 @@ static size_t benchMem(const void* src, size_t srcSize, U32 benchNb) benchFunction = local_ZSTD_compressContinue; benchName = "ZSTD_compressContinue"; break; case 12: + benchFunction = local_ZSTD_compressContinue_extDict; benchName = "ZSTD_compressContinue_extDict"; + break; + case 13: benchFunction = local_ZSTD_decompressContinue; benchName = "ZSTD_decompressContinue"; break; case 31: @@ -268,6 +288,9 @@ static size_t benchMem(const void* src, size_t srcSize, U32 benchNb) if (g_zcc==NULL) g_zcc = ZSTD_createCCtx(); break; case 12 : + if (g_zcc==NULL) g_zcc = ZSTD_createCCtx(); + break; + case 13 : if (g_zdc==NULL) g_zdc = ZSTD_createDCtx(); g_cSize = ZSTD_compress(buff2, dstBuffSize, src, srcSize, 1); break; From 1c1db6b8454e15d772983343c278905dc14655d0 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 20 Dec 2016 11:21:26 +0100 Subject: [PATCH 034/227] windres updated to v1.1.3 --- programs/windres/zstd32.res | Bin 1044 -> 1044 bytes programs/windres/zstd64.res | Bin 1044 -> 1044 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/programs/windres/zstd32.res b/programs/windres/zstd32.res index 6135dc431fed231ce2143b394625bc0b52b0615a..748192a988648a0be3b17ce47353f28015003815 100644 GIT binary patch delta 33 pcmbQjF@ Date: Tue, 20 Dec 2016 10:47:52 -0800 Subject: [PATCH 035/227] Fix dictionary loading bug causing an MSAN failure Offset rep codes must be in the range `[1, dictSize)`. Fix dictionary loading to reject `0` as a offset rep code. --- lib/compress/zstd_compress.c | 6 +++--- lib/decompress/zstd_decompress.c | 6 +++--- lib/legacy/zstd_v07.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 7fd73baa0..e88f6a1d0 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2557,9 +2557,9 @@ static size_t ZSTD_loadDictEntropyStats(ZSTD_CCtx* cctx, const void* dict, size_ } if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted); - cctx->rep[0] = MEM_readLE32(dictPtr+0); if (cctx->rep[0] >= dictSize) return ERROR(dictionary_corrupted); - cctx->rep[1] = MEM_readLE32(dictPtr+4); if (cctx->rep[1] >= dictSize) return ERROR(dictionary_corrupted); - cctx->rep[2] = MEM_readLE32(dictPtr+8); if (cctx->rep[2] >= dictSize) return ERROR(dictionary_corrupted); + cctx->rep[0] = MEM_readLE32(dictPtr+0); if (cctx->rep[0] == 0 || cctx->rep[0] >= dictSize) return ERROR(dictionary_corrupted); + cctx->rep[1] = MEM_readLE32(dictPtr+4); if (cctx->rep[1] == 0 || cctx->rep[1] >= dictSize) return ERROR(dictionary_corrupted); + cctx->rep[2] = MEM_readLE32(dictPtr+8); if (cctx->rep[2] == 0 || cctx->rep[2] >= dictSize) return ERROR(dictionary_corrupted); dictPtr += 12; { U32 offcodeMax = MaxOff; diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 085477bf4..7addff8d0 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1671,9 +1671,9 @@ static size_t ZSTD_loadEntropy(ZSTD_DCtx* dctx, const void* const dict, size_t c } if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted); - dctx->rep[0] = MEM_readLE32(dictPtr+0); if (dctx->rep[0] >= dictSize) return ERROR(dictionary_corrupted); - dctx->rep[1] = MEM_readLE32(dictPtr+4); if (dctx->rep[1] >= dictSize) return ERROR(dictionary_corrupted); - dctx->rep[2] = MEM_readLE32(dictPtr+8); if (dctx->rep[2] >= dictSize) return ERROR(dictionary_corrupted); + dctx->rep[0] = MEM_readLE32(dictPtr+0); if (dctx->rep[0] == 0 || dctx->rep[0] >= dictSize) return ERROR(dictionary_corrupted); + dctx->rep[1] = MEM_readLE32(dictPtr+4); if (dctx->rep[1] == 0 || dctx->rep[1] >= dictSize) return ERROR(dictionary_corrupted); + dctx->rep[2] = MEM_readLE32(dictPtr+8); if (dctx->rep[2] == 0 || dctx->rep[2] >= dictSize) return ERROR(dictionary_corrupted); dictPtr += 12; dctx->litEntropy = dctx->fseEntropy = 1; diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c index b607ec372..b1fcdc766 100644 --- a/lib/legacy/zstd_v07.c +++ b/lib/legacy/zstd_v07.c @@ -4134,9 +4134,9 @@ static size_t ZSTDv07_loadEntropy(ZSTDv07_DCtx* dctx, const void* const dict, si } if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted); - dctx->rep[0] = MEM_readLE32(dictPtr+0); if (dctx->rep[0] >= dictSize) return ERROR(dictionary_corrupted); - dctx->rep[1] = MEM_readLE32(dictPtr+4); if (dctx->rep[1] >= dictSize) return ERROR(dictionary_corrupted); - dctx->rep[2] = MEM_readLE32(dictPtr+8); if (dctx->rep[2] >= dictSize) return ERROR(dictionary_corrupted); + dctx->rep[0] = MEM_readLE32(dictPtr+0); if (dctx->rep[0] == 0 || dctx->rep[0] >= dictSize) return ERROR(dictionary_corrupted); + dctx->rep[1] = MEM_readLE32(dictPtr+4); if (dctx->rep[1] == 0 || dctx->rep[1] >= dictSize) return ERROR(dictionary_corrupted); + dctx->rep[2] = MEM_readLE32(dictPtr+8); if (dctx->rep[2] == 0 || dctx->rep[2] >= dictSize) return ERROR(dictionary_corrupted); dictPtr += 12; dctx->litEntropy = dctx->fseEntropy = 1; From 9d085973646782eb2e74c8b19538437860c8aa5e Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 20 Dec 2016 11:13:45 -0800 Subject: [PATCH 036/227] Add test for invalid offset rep codes --- tests/.gitignore | 1 + tests/Makefile | 10 ++++++-- tests/invalidDictionaries.c | 51 +++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/invalidDictionaries.c diff --git a/tests/.gitignore b/tests/.gitignore index 586c17b6c..e932ad91c 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -15,6 +15,7 @@ paramgrill32 roundTripCrash longmatch symbols +invalidDictionaries # Tmp test directory zstdtest diff --git a/tests/Makefile b/tests/Makefile index abf89f2f6..f1c196ba4 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -146,6 +146,9 @@ roundTripCrash : $(ZSTD_FILES) roundTripCrash.c longmatch : $(ZSTD_FILES) longmatch.c $(CC) $(FLAGS) $^ -o $@$(EXT) +invalidDictionaries : $(ZSTD_FILES) invalidDictionaries.c + $(CC) $(FLAGS) $^ -o $@$(EXT) + symbols : symbols.c $(MAKE) -C $(ZSTDDIR) libzstd ifneq (,$(filter Windows%,$(OS))) @@ -173,7 +176,7 @@ clean: fuzzer-dll$(EXT) zstreamtest-dll$(EXT) zbufftest-dll$(EXT)\ zstreamtest$(EXT) zstreamtest32$(EXT) \ datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) \ - symbols$(EXT) + symbols$(EXT) invalidDictionaries$(EXT) @echo Cleaning completed @@ -213,7 +216,7 @@ zstd-playTests: datagen file $(ZSTD) ZSTD="$(QEMU_SYS) $(ZSTD)" ./playTests.sh $(ZSTDRTTEST) -test: test-zstd test-fullbench test-fuzzer test-zstream test-longmatch +test: test-zstd test-fullbench test-fuzzer test-zstream test-longmatch test-invalidDictionaries test32: test-zstd32 test-fullbench32 test-fuzzer32 test-zstream32 @@ -273,6 +276,9 @@ test-zstream32: zstreamtest32 test-longmatch: longmatch $(QEMU_SYS) ./longmatch +test-invalidDictionaries: invalidDictionaries + $(QEMU_SYS) ./invalidDictionaries + test-symbols: symbols $(QEMU_SYS) ./symbols diff --git a/tests/invalidDictionaries.c b/tests/invalidDictionaries.c new file mode 100644 index 000000000..fe8b23b5e --- /dev/null +++ b/tests/invalidDictionaries.c @@ -0,0 +1,51 @@ +#include +#include "zstd.h" + +static const char invalidRepCode[] = { + 0x37, 0xa4, 0x30, 0xec, 0x2a, 0x00, 0x00, 0x00, 0x39, 0x10, 0xc0, 0xc2, + 0xa6, 0x00, 0x0c, 0x30, 0xc0, 0x00, 0x03, 0x0c, 0x30, 0x20, 0x72, 0xf8, + 0xb4, 0x6d, 0x4b, 0x9f, 0xfc, 0x97, 0x29, 0x49, 0xb2, 0xdf, 0x4b, 0x29, + 0x7d, 0x4a, 0xfc, 0x83, 0x18, 0x22, 0x75, 0x23, 0x24, 0x44, 0x4d, 0x02, + 0xb7, 0x97, 0x96, 0xf6, 0xcb, 0xd1, 0xcf, 0xe8, 0x22, 0xea, 0x27, 0x36, + 0xb7, 0x2c, 0x40, 0x46, 0x01, 0x08, 0x23, 0x01, 0x00, 0x00, 0x06, 0x1e, + 0x3c, 0x83, 0x81, 0xd6, 0x18, 0xd4, 0x12, 0x3a, 0x04, 0x00, 0x80, 0x03, + 0x08, 0x0e, 0x12, 0x1c, 0x12, 0x11, 0x0d, 0x0e, 0x0a, 0x0b, 0x0a, 0x09, + 0x10, 0x0c, 0x09, 0x05, 0x04, 0x03, 0x06, 0x06, 0x06, 0x02, 0x00, 0x03, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x04, 0x06, 0x03, 0x06, 0x08, 0x24, 0x6b, + 0x0d, 0x01, 0x10, 0x04, 0x81, 0x07, 0x00, 0x00, 0x04, 0xb9, 0x58, 0x18, + 0x06, 0x59, 0x92, 0x43, 0xce, 0x28, 0xa5, 0x08, 0x88, 0xc0, 0x80, 0x88, + 0x8c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00 +}; + +typedef struct dictionary_s { + const char *data; + size_t size; +} dictionary; + +static const dictionary dictionaries[] = { + {invalidRepCode, sizeof(invalidRepCode)}, + {NULL, 0}, +}; + +int main(int argc, const char** argv) { + const dictionary *dict; + for (dict = dictionaries; dict->data != NULL; ++dict) { + ZSTD_CDict *cdict; + ZSTD_DDict *ddict; + cdict = ZSTD_createCDict(dict->data, dict->size, 1); + if (cdict) { + ZSTD_freeCDict(cdict); + return 1; + } + ddict = ZSTD_createDDict(dict->data, dict->size); + if (ddict) { + ZSTD_freeDDict(ddict); + return 2; + } + } + + (void)argc; + (void)argv; + return 0; +} From ead350bdc0b3e20acd282747f2f6a4560e42e3c0 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 21 Dec 2016 09:04:59 +0100 Subject: [PATCH 037/227] improved util.h and platform.h --- programs/platform.h | 84 +++++++++++++++++++++++++++++++++------- programs/util.h | 94 ++++++++++++++++++++++++++++++--------------- 2 files changed, 135 insertions(+), 43 deletions(-) diff --git a/programs/platform.h b/programs/platform.h index c69dcb2dd..b8911f2c7 100644 --- a/programs/platform.h +++ b/programs/platform.h @@ -14,6 +14,7 @@ extern "C" { #endif + /* ************************************** * Compiler Options ****************************************/ @@ -24,21 +25,45 @@ extern "C" { # define _CRT_SECURE_NO_WARNINGS /* Disable some Visual warning messages for fopen, strncpy */ # define _CRT_SECURE_NO_DEPRECATE /* VS2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# if _MSC_VER <= 1800 /* (1800 = Visual Studio 2013) */ +# if (_MSC_VER <= 1800) /* (1800 = Visual Studio 2013) */ # define snprintf sprintf_s /* snprintf unsupported by Visual <= 2013 */ # endif +# if (_MSC_VER >= 1400) /* Avoid MSVC fseek()'s 2GiB barrier */ +# define fseek _fseeki64 +# endif #endif /* ************************************** -* Unix Large Files support (>4GB) +* Detect 64-bit OS +* http://nadeausoftware.com/articles/2012/02/c_c_tip_how_detect_processor_type_using_compiler_predefined_macros ****************************************/ -#if !defined(__LP64__) /* No point defining Large file for 64 bit */ -# define _FILE_OFFSET_BITS 64 /* turn off_t into a 64-bit type for ftello, fseeko */ -# if defined(__sun__) && !defined(_LARGEFILE_SOURCE) /* Sun Solaris 32-bits requires specific definitions */ -# define _LARGEFILE_SOURCE /* fseeko, ftello */ -# elif !defined(_LARGEFILE64_SOURCE) -# define _LARGEFILE64_SOURCE /* off64_t, fseeko64, ftello64 */ +#if defined __ia64 || defined _M_IA64 /* Intel Itanium */ \ + || defined __powerpc64__ || defined __ppc64__ || defined __PPC64__ /* POWER 64-bit */ \ + || (defined __sparc && (defined __sparcv9 || defined __sparc_v9__ || defined __arch64__)) || defined __sparc64__ /* SPARC 64-bit */ \ + || defined __x86_64__s || defined _M_X64 /* x86 64-bit */ \ + || defined __arm64__ || defined __aarch64__ || defined __ARM64_ARCH_8__ /* ARM 64-bit */ \ + || (defined __mips && (__mips == 64 || __mips == 4 || __mips == 3)) /* MIPS 64-bit */ \ + || defined _LP64 || defined __LP64__ /* NetBSD, OpenBSD */ || defined __64BIT__ /* AIX */ || defined _ADDR64 /* Cray */ \ + || (defined __SIZEOF_POINTER__ && __SIZEOF_POINTER__ == 8) /* gcc */ +# if !defined(__64BIT__) +# define __64BIT__ 1 +# endif +#endif + + +/* ********************************************************* +* Turn on Large Files support (>4GB) for 32-bit Linux/Unix +***********************************************************/ +#if !defined(__64BIT__) /* No point defining Large file for 64 bit */ +# if !defined(_FILE_OFFSET_BITS) +# define _FILE_OFFSET_BITS 64 /* turn off_t into a 64-bit type for ftello, fseeko */ +# endif +# if !defined(_LARGEFILE_SOURCE) /* obsolete macro, replaced with _FILE_OFFSET_BITS */ +# define _LARGEFILE_SOURCE 1 /* Large File Support extension (LFS) - fseeko, ftello */ +# endif +# if defined(_AIX) || defined(__hpux) +# define _LARGE_FILES /* Large file support on 32-bits AIX and HP-UX */ # endif #endif @@ -49,10 +74,10 @@ extern "C" { * PLATFORM_POSIX_VERSION = 0 for Unix-like non-POSIX * PLATFORM_POSIX_VERSION >= 1 is equal to found _POSIX_VERSION ***************************************************************/ -#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) || defined(__midipix__) || defined(__VMS)) - /* UNIX-style OS. ------------------------------------------- */ -# if (defined(__APPLE__) && defined(__MACH__)) || defined(__SVR4) || defined(_AIX) || defined(__hpux) \ - || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) /* POSIX.1–2001 (SUSv3) conformant */ +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) /* UNIX-like OS */ \ + || defined(__midipix__) || defined(__VMS)) +# if (defined(__APPLE__) && defined(__MACH__)) || defined(__SVR4) || defined(_AIX) || defined(__hpux) /* POSIX.1–2001 (SUSv3) conformant */ \ + || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) /* BSD distros */ # define PLATFORM_POSIX_VERSION 200112L # else # if defined(__linux__) || defined(__linux) @@ -66,12 +91,45 @@ extern "C" { # endif # endif #endif - #if !defined(PLATFORM_POSIX_VERSION) # define PLATFORM_POSIX_VERSION -1 #endif +/*-********************************************* +* Detect if isatty() and fileno() are available +************************************************/ +#if (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) || (PLATFORM_POSIX_VERSION >= 200112L) || defined(__DJGPP__) +# define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) +#elif defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) +# include /* _isatty */ +# define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) +#else +# define IS_CONSOLE(stdStream) 0 +#endif + + +/****************************** +* OS-specific Includes +******************************/ +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) +# include /* _O_BINARY */ +# include /* _setmode, _fileno, _get_osfhandle */ +# if !defined(__DJGPP__) +# include /* DeviceIoControl, HANDLE, FSCTL_SET_SPARSE */ +# include /* FSCTL_SET_SPARSE */ +# define SET_BINARY_MODE(file) { int unused=_setmode(_fileno(file), _O_BINARY); (void)unused; } +# define SET_SPARSE_FILE_MODE(file) { DWORD dw; DeviceIoControl((HANDLE) _get_osfhandle(_fileno(file)), FSCTL_SET_SPARSE, 0, 0, 0, 0, &dw, 0); } +# else +# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +# define SET_SPARSE_FILE_MODE(file) +# endif +#else +# define SET_BINARY_MODE(file) +# define SET_SPARSE_FILE_MODE(file) +#endif + + #if defined (__cplusplus) } diff --git a/programs/util.h b/programs/util.h index 9dbd7fb1f..3e8aaf5d6 100644 --- a/programs/util.h +++ b/programs/util.h @@ -1,11 +1,21 @@ -/** - * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ +/* + util.h - utility functions + Copyright (C) 2016-present, Przemyslaw Skibinski, Yann Collet + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ #ifndef UTIL_H_MODULE #define UTIL_H_MODULE @@ -20,6 +30,7 @@ extern "C" { ******************************************/ #include "platform.h" /* Compiler options, PLATFORM_POSIX_VERSION */ #include /* malloc */ +#include /* size_t, ptrdiff_t */ #include /* fprintf */ #include /* stat, utime */ #include /* stat */ @@ -32,27 +43,6 @@ extern "C" { #endif #include /* time */ #include -#include "mem.h" /* U32, U64 */ - - -/* ************************************* -* Constants -***************************************/ -#define LIST_SIZE_INCREASE (8*1024) - - -/*-**************************************** -* Compiler specifics -******************************************/ -#if defined(__GNUC__) -# define UTIL_STATIC static __attribute__((unused)) -#elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -# define UTIL_STATIC static inline -#elif defined(_MSC_VER) -# define UTIL_STATIC static __inline -#else -# define UTIL_STATIC static /* this version may generate warnings for unused static functions; disable the relevant warning */ -#endif /*-**************************************** @@ -85,6 +75,49 @@ extern "C" { #endif +/* ************************************* +* Constants +***************************************/ +#define LIST_SIZE_INCREASE (8*1024) + + +/*-************************************************************** +* Basic Types +*****************************************************************/ +#if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef int16_t S16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; + typedef int64_t S64; +#else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef signed short S16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; + typedef signed long long S64; +#endif + + +/*-**************************************** +* Compiler specifics +******************************************/ +#if defined(__GNUC__) +# define UTIL_STATIC static __attribute__((unused)) +#elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define UTIL_STATIC static inline +#elif defined(_MSC_VER) +# define UTIL_STATIC static __inline +#else +# define UTIL_STATIC static /* this version may generate warnings for unused static functions; disable the relevant warning */ +#endif + + /*-**************************************** * Time functions ******************************************/ @@ -303,6 +336,7 @@ UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_ #elif defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L) /* opendir, readdir require POSIX.1-2001 */ # define UTIL_HAS_CREATEFILELIST # include /* opendir, readdir */ +# include /* strerror, memcpy */ UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_t* pos, char** bufEnd) { @@ -364,7 +398,7 @@ UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_ UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_t* pos, char** bufEnd) { (void)bufStart; (void)bufEnd; (void)pos; - fprintf(stderr, "Directory %s ignored (zstd compiled without _WIN32 or _POSIX_C_SOURCE)\n", dirName); + fprintf(stderr, "Directory %s ignored (compiled without _WIN32 or _POSIX_C_SOURCE)\n", dirName); return 0; } From 16ae6563a22e513b6e90ca49975e55852faa5c50 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 21 Dec 2016 09:06:14 +0100 Subject: [PATCH 038/227] executables use new util.h and platform.h --- programs/bench.c | 1 - programs/datagen.c | 14 +------------- programs/dibio.c | 1 - programs/fileio.c | 14 -------------- tests/datagencli.c | 2 +- tests/fullbench.c | 3 +-- tests/fuzzer.c | 5 ++--- tests/paramgrill.c | 4 ++-- tests/zbufftest.c | 5 ++--- tests/zstreamtest.c | 5 ++--- zlibWrapper/examples/zwrapbench.c | 3 +-- 11 files changed, 12 insertions(+), 45 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index a4b57efcb..2b7436a4f 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -18,7 +18,6 @@ #include /* fprintf, fopen, ftello64 */ #include /* clock_t, clock, CLOCKS_PER_SEC */ -#include "mem.h" #define ZSTD_STATIC_LINKING_ONLY #include "zstd.h" #include "datagen.h" /* RDG_genBuffer */ diff --git a/programs/datagen.c b/programs/datagen.c index 1af5a38a5..da5124cfc 100644 --- a/programs/datagen.c +++ b/programs/datagen.c @@ -20,22 +20,10 @@ /*-************************************ * Dependencies **************************************/ +#include "util.h" /* U32 */ #include /* malloc, free */ #include /* FILE, fwrite, fprintf */ #include /* memcpy */ -#include "mem.h" /* U32 */ - - -/*-************************************ -* OS-specific Includes -**************************************/ -#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) -# include /* _O_BINARY */ -# include /* _setmode, _isatty */ -# define SET_BINARY_MODE(file) {int unused = _setmode(_fileno(file), _O_BINARY); (void)unused; } -#else -# define SET_BINARY_MODE(file) -#endif /*-************************************ diff --git a/programs/dibio.c b/programs/dibio.c index 152ea4d09..1c22949f1 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -20,7 +20,6 @@ #include /* clock_t, clock, CLOCKS_PER_SEC */ #include /* errno */ -#include "mem.h" /* read */ #include "error_private.h" #include "dibio.h" diff --git a/programs/fileio.c b/programs/fileio.c index 325a17a59..58fe7e355 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -30,7 +30,6 @@ #include /* clock */ #include /* errno */ -#include "mem.h" #include "fileio.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */ #include "zstd.h" @@ -42,19 +41,6 @@ #endif -/*-************************************* -* OS-specific Includes -***************************************/ -#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) -# include /* _O_BINARY */ -# include /* _setmode, _isatty */ -# define SET_BINARY_MODE(file) { if (_setmode(_fileno(file), _O_BINARY) == -1) perror("Cannot set _O_BINARY"); } -#else -# include /* isatty */ -# define SET_BINARY_MODE(file) -#endif - - /*-************************************* * Constants ***************************************/ diff --git a/tests/datagencli.c b/tests/datagencli.c index 2b3d3b511..c4bf48934 100644 --- a/tests/datagencli.c +++ b/tests/datagencli.c @@ -12,9 +12,9 @@ * Dependencies **************************************/ #include "platform.h" /* Compiler options */ +#include "util.h" /* U32 */ #include /* fprintf, stderr */ #include "datagen.h" /* RDG_generate */ -#include "mem.h" /* U32, U64 */ /*-************************************ diff --git a/tests/fullbench.c b/tests/fullbench.c index e5547d089..439de5c76 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -12,12 +12,11 @@ * Includes **************************************/ #include "platform.h" /* Compiler options */ -#include "util.h" /* UTIL_GetFileSize */ +#include "util.h" /* U32 */ #include /* malloc */ #include /* fprintf, fopen, ftello64 */ #include /* clock_t, clock, CLOCKS_PER_SEC */ -#include "mem.h" #ifndef ZSTD_DLL_IMPORT #include "zstd_internal.h" /* ZSTD_blockHeaderSize, blockType_e, KB, MB */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressBegin, ZSTD_compressContinue, etc. */ diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 86d4c6beb..9088e2483 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -12,8 +12,6 @@ * 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 @@ -21,6 +19,8 @@ /*-************************************ * Includes **************************************/ +#include "platform.h" /* Compiler options */ +#include "util.h" /* U32 */ #include /* free */ #include /* fgets, sscanf */ #include /* strcmp */ @@ -30,7 +30,6 @@ #include "zstd_errors.h" /* ZSTD_getErrorCode */ #include "zdict.h" /* ZDICT_trainFromBuffer */ #include "datagen.h" /* RDG_genBuffer */ -#include "mem.h" #define XXH_STATIC_LINKING_ONLY #include "xxhash.h" /* XXH64 */ diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 19658fddb..0825b37a6 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -12,14 +12,14 @@ * Dependencies **************************************/ #include "platform.h" /* Compiler options */ -#include "util.h" /* UTIL_GetFileSize */ +#include "util.h" /* UTIL_getFileSize */ #include /* malloc */ #include /* fprintf, fopen, ftello64 */ #include /* strcmp */ #include /* log */ #include /* clock_t */ -#include "mem.h" +#include "mem.h" /* MEM_32bits() */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters, ZSTD_estimateCCtxSize */ #include "zstd.h" #include "datagen.h" diff --git a/tests/zbufftest.c b/tests/zbufftest.c index 14b739233..87cf80b5e 100644 --- a/tests/zbufftest.c +++ b/tests/zbufftest.c @@ -12,8 +12,6 @@ * 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 : 4146) /* disable: C4146: minus unsigned expression */ #endif @@ -21,11 +19,12 @@ /*-************************************ * Includes **************************************/ +#include "platform.h" /* Compiler options */ #include /* free */ #include /* fgets, sscanf */ #include /* clock_t, clock() */ #include /* strcmp */ -#include "mem.h" +#include "mem.h" /* MEM_writeLE32 */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel */ #include "zstd.h" /* ZSTD_compressBound */ #define ZBUFF_STATIC_LINKING_ONLY /* ZBUFF_createCCtx_advanced */ diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index ce6193085..9da0010be 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -12,8 +12,6 @@ * 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 : 4146) /* disable: C4146: minus unsigned expression */ #endif @@ -21,11 +19,12 @@ /*-************************************ * Includes **************************************/ +#include "platform.h" /* Compiler options */ #include /* free */ #include /* fgets, sscanf */ #include /* clock_t, clock() */ #include /* strcmp */ -#include "mem.h" +#include "mem.h" /* MEM_writeLE32 */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel, ZSTD_customMem */ #include "zstd.h" /* ZSTD_compressBound */ #include "zstd_errors.h" /* ZSTD_error_srcSize_wrong */ diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c index c3968dba3..8999657fa 100644 --- a/zlibWrapper/examples/zwrapbench.c +++ b/zlibWrapper/examples/zwrapbench.c @@ -12,14 +12,13 @@ * Includes ***************************************/ #include "platform.h" /* Compiler options */ -#include "util.h" /* UTIL_GetFileSize, UTIL_sleep */ +#include "util.h" /* U32, UTIL_GetFileSize, UTIL_sleep */ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen, ftello64 */ #include /* clock_t, clock, CLOCKS_PER_SEC */ #include /* toupper */ -#include "mem.h" #define ZSTD_STATIC_LINKING_ONLY #include "zstd.h" #include "datagen.h" /* RDG_genBuffer */ From 20b089e53d855bfc1446d1217e30709701af2f87 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 21 Dec 2016 09:19:15 +0100 Subject: [PATCH 039/227] simplified zstdcli.c --- programs/datagen.c | 9 +-------- programs/fileio.c | 1 - programs/zstdcli.c | 14 -------------- 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/programs/datagen.c b/programs/datagen.c index da5124cfc..8209c8517 100644 --- a/programs/datagen.c +++ b/programs/datagen.c @@ -9,17 +9,10 @@ -/* ************************************* -* Compiler Options -***************************************/ -#if defined(_MSC_VER) -# define _CRT_SECURE_NO_WARNINGS /* removes Visual warning on strerror() */ -# define _CRT_SECURE_NO_DEPRECATE /* removes VS2005 warning on strerror() */ -#endif - /*-************************************ * Dependencies **************************************/ +#include "platform.h" /* Compiler options */ #include "util.h" /* U32 */ #include /* malloc, free */ #include /* FILE, fwrite, fprintf */ diff --git a/programs/fileio.c b/programs/fileio.c index 58fe7e355..4f6db992f 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -11,7 +11,6 @@ * Compiler Options ***************************************/ #ifdef _MSC_VER /* Visual */ -# define _CRT_SECURE_NO_WARNINGS /* removes Visual warning on strerror() */ # pragma warning(disable : 4204) /* non-constant aggregate initializer */ #endif #if defined(__MINGW32__) && !defined(_POSIX_SOURCE) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 0d7ce8b3b..29fcb746b 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -38,20 +38,6 @@ #include "zstd.h" /* ZSTD_VERSION_STRING */ -/*-************************************ -* OS-specific Includes -**************************************/ -#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) -# include /* _isatty */ -# define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) -#elif (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) || (PLATFORM_POSIX_VERSION >= 200112L) /* isatty requires POSIX.1-2001 */ -# include /* isatty */ -# define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) -#else -# define IS_CONSOLE(stdStream) 0 -#endif - - /*-************************************ * Constants **************************************/ From 5736db219e7ec71bf50ad0d66e436aa8724cd9df Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 21 Dec 2016 09:26:00 +0100 Subject: [PATCH 040/227] fix basic types redefinition --- lib/common/mem.h | 3 +++ programs/util.h | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/common/mem.h b/lib/common/mem.h index 32c63dd17..fb73ef093 100644 --- a/lib/common/mem.h +++ b/lib/common/mem.h @@ -46,6 +46,8 @@ MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (size /*-************************************************************** * Basic Types *****************************************************************/ +#ifndef BASIC_TYPES_DEFINED +#define BASIC_TYPES_DEFINED #if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint8_t BYTE; @@ -66,6 +68,7 @@ MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (size typedef signed long long S64; typedef ptrdiff_t iPtrDiff; #endif +#endif /*-************************************************************** diff --git a/programs/util.h b/programs/util.h index 3e8aaf5d6..409f6d7c1 100644 --- a/programs/util.h +++ b/programs/util.h @@ -84,7 +84,9 @@ extern "C" { /*-************************************************************** * Basic Types *****************************************************************/ -#if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +#ifndef BASIC_TYPES_DEFINED +#define BASIC_TYPES_DEFINED +#if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint8_t BYTE; typedef uint16_t U16; @@ -101,7 +103,8 @@ extern "C" { typedef signed int S32; typedef unsigned long long U64; typedef signed long long S64; -#endif +#endif +#endif /*-**************************************** From a35b9448acf0a1f06e413389dd22c82d9e746bf5 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 21 Dec 2016 11:18:45 +0100 Subject: [PATCH 041/227] improved MinGW support --- programs/fileio.c | 3 --- programs/platform.h | 29 +++++++++++++++++++++-------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 4f6db992f..fa52f9aff 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -13,9 +13,6 @@ #ifdef _MSC_VER /* Visual */ # pragma warning(disable : 4204) /* non-constant aggregate initializer */ #endif -#if defined(__MINGW32__) && !defined(_POSIX_SOURCE) -# define _POSIX_SOURCE 1 /* disable %llu warnings with MinGW on Windows */ -#endif /*-************************************* diff --git a/programs/platform.h b/programs/platform.h index b8911f2c7..02ebd4c02 100644 --- a/programs/platform.h +++ b/programs/platform.h @@ -1,11 +1,21 @@ -/** - * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ +/* + platform.h - compiler and OS detection + Copyright (C) 2016-present, Przemyslaw Skibinski, Yann Collet + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ #ifndef PLATFORM_H_MODULE #define PLATFORM_H_MODULE @@ -32,6 +42,9 @@ extern "C" { # define fseek _fseeki64 # endif #endif +#if defined(__MINGW32__) && !defined(_POSIX_SOURCE) +# define _POSIX_C_SOURCE 1 /* enable __VA_ARGS__ and disable %llu warnings with MinGW on Windows */ +#endif /* ************************************** From 101df4f6360dd97e8a2ef96510c15200d9af77bd Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 21 Dec 2016 11:43:11 +0100 Subject: [PATCH 042/227] fixed Visual Studio compilation --- programs/platform.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/programs/platform.h b/programs/platform.h index 02ebd4c02..fe4bfded6 100644 --- a/programs/platform.h +++ b/programs/platform.h @@ -38,9 +38,6 @@ extern "C" { # if (_MSC_VER <= 1800) /* (1800 = Visual Studio 2013) */ # define snprintf sprintf_s /* snprintf unsupported by Visual <= 2013 */ # endif -# if (_MSC_VER >= 1400) /* Avoid MSVC fseek()'s 2GiB barrier */ -# define fseek _fseeki64 -# endif #endif #if defined(__MINGW32__) && !defined(_POSIX_SOURCE) # define _POSIX_C_SOURCE 1 /* enable __VA_ARGS__ and disable %llu warnings with MinGW on Windows */ From 2f6ccee6af2cdf539c5798566dd7c27fc167673f Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 21 Dec 2016 13:23:34 +0100 Subject: [PATCH 043/227] platform.h: removed Compiler Options --- lib/common/mem.h | 3 -- programs/bench.c | 15 ++++++-- programs/datagen.c | 12 +++++-- programs/dibio.c | 14 ++++++-- programs/fileio.c | 9 +++-- programs/platform.h | 20 +---------- programs/util.h | 58 ++++++++++++------------------- programs/zstdcli.c | 2 +- tests/datagencli.c | 3 +- tests/fullbench.c | 4 +-- tests/fuzzer.c | 5 +-- tests/paramgrill.c | 5 ++- tests/zbufftest.c | 5 +-- tests/zstreamtest.c | 5 +-- zlibWrapper/examples/zwrapbench.c | 4 +-- 15 files changed, 83 insertions(+), 81 deletions(-) diff --git a/lib/common/mem.h b/lib/common/mem.h index fb73ef093..32c63dd17 100644 --- a/lib/common/mem.h +++ b/lib/common/mem.h @@ -46,8 +46,6 @@ MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (size /*-************************************************************** * Basic Types *****************************************************************/ -#ifndef BASIC_TYPES_DEFINED -#define BASIC_TYPES_DEFINED #if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint8_t BYTE; @@ -68,7 +66,6 @@ MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (size typedef signed long long S64; typedef ptrdiff_t iPtrDiff; #endif -#endif /*-************************************************************** diff --git a/programs/bench.c b/programs/bench.c index 2b7436a4f..9b7f98884 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -8,16 +8,27 @@ */ + +/* ************************************** +* Compiler Warnings +****************************************/ +#ifdef _MSC_VER +# define _CRT_SECURE_NO_WARNINGS /* fopen */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +#endif + + /* ************************************* * Includes ***************************************/ -#include "platform.h" /* Compiler options */ -#include "util.h" /* UTIL_GetFileSize, UTIL_sleep */ +#include "platform.h" /* Large Files support */ +#include "util.h" /* UTIL_getFileSize, UTIL_sleep */ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen, ftello64 */ #include /* clock_t, clock, CLOCKS_PER_SEC */ +#include "mem.h" #define ZSTD_STATIC_LINKING_ONLY #include "zstd.h" #include "datagen.h" /* RDG_genBuffer */ diff --git a/programs/datagen.c b/programs/datagen.c index 8209c8517..06b5ab977 100644 --- a/programs/datagen.c +++ b/programs/datagen.c @@ -9,14 +9,22 @@ +/* ************************************* +* Compiler Options +***************************************/ +#if defined(_MSC_VER) +# define _CRT_SECURE_NO_WARNINGS /* removes Visual warning on strerror() */ +# define _CRT_SECURE_NO_DEPRECATE /* removes VS2005 warning on strerror() */ +#endif + /*-************************************ * Dependencies **************************************/ -#include "platform.h" /* Compiler options */ -#include "util.h" /* U32 */ +#include "platform.h" /* SET_BINARY_MODE */ #include /* malloc, free */ #include /* FILE, fwrite, fprintf */ #include /* memcpy */ +#include "mem.h" /* U32 */ /*-************************************ diff --git a/programs/dibio.c b/programs/dibio.c index 1c22949f1..b99e717c2 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -9,17 +9,27 @@ +/* ************************************** +* Compiler Warnings +****************************************/ +#ifdef _MSC_VER +# define _CRT_SECURE_NO_WARNINGS /* fopen */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +#endif + + /*-************************************* * Includes ***************************************/ -#include "platform.h" /* Compiler options */ -#include "util.h" /* UTIL_GetFileSize, UTIL_getTotalFileSize */ +#include "platform.h" /* Large Files support */ +#include "util.h" /* UTIL_getFileSize, UTIL_getTotalFileSize */ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen, ftello64 */ #include /* clock_t, clock, CLOCKS_PER_SEC */ #include /* errno */ +#include "mem.h" /* read */ #include "error_private.h" #include "dibio.h" diff --git a/programs/fileio.c b/programs/fileio.c index fa52f9aff..e2a90a678 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -11,21 +11,26 @@ * Compiler Options ***************************************/ #ifdef _MSC_VER /* Visual */ +# define _CRT_SECURE_NO_WARNINGS /* removes Visual warning on strerror() */ # pragma warning(disable : 4204) /* non-constant aggregate initializer */ #endif +#if defined(__MINGW32__) && !defined(_POSIX_SOURCE) +# define _POSIX_SOURCE 1 /* disable %llu warnings with MinGW on Windows */ +#endif /*-************************************* * Includes ***************************************/ -#include "platform.h" /* Compiler options */ -#include "util.h" /* UTIL_GetFileSize, _LARGEFILE64_SOURCE */ +#include "platform.h" /* Large Files support, SET_BINARY_MODE */ +#include "util.h" /* UTIL_getFileSize */ #include /* fprintf, fopen, fread, _fileno, stdin, stdout */ #include /* malloc, free */ #include /* strcmp, strlen */ #include /* clock */ #include /* errno */ +#include "mem.h" #include "fileio.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */ #include "zstd.h" diff --git a/programs/platform.h b/programs/platform.h index fe4bfded6..0b82e46bf 100644 --- a/programs/platform.h +++ b/programs/platform.h @@ -25,25 +25,6 @@ extern "C" { #endif -/* ************************************** -* Compiler Options -****************************************/ -#if defined(__INTEL_COMPILER) -# pragma warning(disable : 177) /* disable: message #177: function was declared but never referenced */ -#endif -#if defined(_MSC_VER) -# define _CRT_SECURE_NO_WARNINGS /* Disable some Visual warning messages for fopen, strncpy */ -# define _CRT_SECURE_NO_DEPRECATE /* VS2005 */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# if (_MSC_VER <= 1800) /* (1800 = Visual Studio 2013) */ -# define snprintf sprintf_s /* snprintf unsupported by Visual <= 2013 */ -# endif -#endif -#if defined(__MINGW32__) && !defined(_POSIX_SOURCE) -# define _POSIX_C_SOURCE 1 /* enable __VA_ARGS__ and disable %llu warnings with MinGW on Windows */ -#endif - - /* ************************************** * Detect 64-bit OS * http://nadeausoftware.com/articles/2012/02/c_c_tip_how_detect_processor_type_using_compiler_predefined_macros @@ -110,6 +91,7 @@ extern "C" { * Detect if isatty() and fileno() are available ************************************************/ #if (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) || (PLATFORM_POSIX_VERSION >= 200112L) || defined(__DJGPP__) +# include /* isatty */ # define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) #elif defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) # include /* _isatty */ diff --git a/programs/util.h b/programs/util.h index 409f6d7c1..8b480b226 100644 --- a/programs/util.h +++ b/programs/util.h @@ -25,24 +25,35 @@ extern "C" { #endif +/* ************************************** +* Compiler Options +****************************************/ +#if defined(_MSC_VER) +# if (_MSC_VER <= 1800) /* (1800 = Visual Studio 2013) */ +# define snprintf sprintf_s /* snprintf unsupported by Visual <= 2013 */ +# endif +#endif + + /*-**************************************** * Dependencies ******************************************/ -#include "platform.h" /* Compiler options, PLATFORM_POSIX_VERSION */ -#include /* malloc */ -#include /* size_t, ptrdiff_t */ -#include /* fprintf */ -#include /* stat, utime */ -#include /* stat */ +#include "platform.h" /* PLATFORM_POSIX_VERSION */ +#include /* malloc */ +#include /* size_t, ptrdiff_t */ +#include /* fprintf */ +#include /* stat, utime */ +#include /* stat */ #if defined(_MSC_VER) -# include /* utime */ -# include /* _chmod */ +# include /* utime */ +# include /* _chmod */ #else # include /* chown, stat */ # include /* utime */ #endif -#include /* time */ +#include /* time */ #include +#include "mem.h" /* U32, U64 */ /*-**************************************** @@ -81,35 +92,12 @@ extern "C" { #define LIST_SIZE_INCREASE (8*1024) -/*-************************************************************** -* Basic Types -*****************************************************************/ -#ifndef BASIC_TYPES_DEFINED -#define BASIC_TYPES_DEFINED -#if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) -# include - typedef uint8_t BYTE; - typedef uint16_t U16; - typedef int16_t S16; - typedef uint32_t U32; - typedef int32_t S32; - typedef uint64_t U64; - typedef int64_t S64; -#else - typedef unsigned char BYTE; - typedef unsigned short U16; - typedef signed short S16; - typedef unsigned int U32; - typedef signed int S32; - typedef unsigned long long U64; - typedef signed long long S64; -#endif -#endif - - /*-**************************************** * Compiler specifics ******************************************/ +#if defined(__INTEL_COMPILER) +# pragma warning(disable : 177) /* disable: message #177: function was declared but never referenced, useful with UTIL_STATIC */ +#endif #if defined(__GNUC__) # define UTIL_STATIC static __attribute__((unused)) #elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 29fcb746b..978ffcfe0 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -23,7 +23,7 @@ /*-************************************ * Dependencies **************************************/ -#include "platform.h" /* Compiler options, PLATFORM_POSIX_VERSION */ +#include "platform.h" /* IS_CONSOLE, PLATFORM_POSIX_VERSION */ #include "util.h" /* UTIL_HAS_CREATEFILELIST, UTIL_createFileList */ #include /* strcmp, strlen */ #include /* errno */ diff --git a/tests/datagencli.c b/tests/datagencli.c index c4bf48934..2f3ebc4d6 100644 --- a/tests/datagencli.c +++ b/tests/datagencli.c @@ -11,8 +11,7 @@ /*-************************************ * Dependencies **************************************/ -#include "platform.h" /* Compiler options */ -#include "util.h" /* U32 */ +#include "util.h" /* Compiler options */ #include /* fprintf, stderr */ #include "datagen.h" /* RDG_generate */ diff --git a/tests/fullbench.c b/tests/fullbench.c index 439de5c76..233b4e931 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -11,12 +11,12 @@ /*_************************************ * Includes **************************************/ -#include "platform.h" /* Compiler options */ -#include "util.h" /* U32 */ +#include "util.h" /* Compiler options, UTIL_GetFileSize */ #include /* malloc */ #include /* fprintf, fopen, ftello64 */ #include /* clock_t, clock, CLOCKS_PER_SEC */ +#include "mem.h" #ifndef ZSTD_DLL_IMPORT #include "zstd_internal.h" /* ZSTD_blockHeaderSize, blockType_e, KB, MB */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressBegin, ZSTD_compressContinue, etc. */ diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 9088e2483..86d4c6beb 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -12,6 +12,8 @@ * 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 @@ -19,8 +21,6 @@ /*-************************************ * Includes **************************************/ -#include "platform.h" /* Compiler options */ -#include "util.h" /* U32 */ #include /* free */ #include /* fgets, sscanf */ #include /* strcmp */ @@ -30,6 +30,7 @@ #include "zstd_errors.h" /* ZSTD_getErrorCode */ #include "zdict.h" /* ZDICT_trainFromBuffer */ #include "datagen.h" /* RDG_genBuffer */ +#include "mem.h" #define XXH_STATIC_LINKING_ONLY #include "xxhash.h" /* XXH64 */ diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 0825b37a6..5eabcba2b 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -11,15 +11,14 @@ /*-************************************ * Dependencies **************************************/ -#include "platform.h" /* Compiler options */ -#include "util.h" /* UTIL_getFileSize */ +#include "util.h" /* Compiler options, UTIL_GetFileSize */ #include /* malloc */ #include /* fprintf, fopen, ftello64 */ #include /* strcmp */ #include /* log */ #include /* clock_t */ -#include "mem.h" /* MEM_32bits() */ +#include "mem.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters, ZSTD_estimateCCtxSize */ #include "zstd.h" #include "datagen.h" diff --git a/tests/zbufftest.c b/tests/zbufftest.c index 87cf80b5e..14b739233 100644 --- a/tests/zbufftest.c +++ b/tests/zbufftest.c @@ -12,6 +12,8 @@ * 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 : 4146) /* disable: C4146: minus unsigned expression */ #endif @@ -19,12 +21,11 @@ /*-************************************ * Includes **************************************/ -#include "platform.h" /* Compiler options */ #include /* free */ #include /* fgets, sscanf */ #include /* clock_t, clock() */ #include /* strcmp */ -#include "mem.h" /* MEM_writeLE32 */ +#include "mem.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel */ #include "zstd.h" /* ZSTD_compressBound */ #define ZBUFF_STATIC_LINKING_ONLY /* ZBUFF_createCCtx_advanced */ diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 9da0010be..ce6193085 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -12,6 +12,8 @@ * 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 : 4146) /* disable: C4146: minus unsigned expression */ #endif @@ -19,12 +21,11 @@ /*-************************************ * Includes **************************************/ -#include "platform.h" /* Compiler options */ #include /* free */ #include /* fgets, sscanf */ #include /* clock_t, clock() */ #include /* strcmp */ -#include "mem.h" /* MEM_writeLE32 */ +#include "mem.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel, ZSTD_customMem */ #include "zstd.h" /* ZSTD_compressBound */ #include "zstd_errors.h" /* ZSTD_error_srcSize_wrong */ diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c index 8999657fa..e0aca0015 100644 --- a/zlibWrapper/examples/zwrapbench.c +++ b/zlibWrapper/examples/zwrapbench.c @@ -11,14 +11,14 @@ /* ************************************* * Includes ***************************************/ -#include "platform.h" /* Compiler options */ -#include "util.h" /* U32, UTIL_GetFileSize, UTIL_sleep */ +#include "util.h" /* Compiler options, UTIL_GetFileSize, UTIL_sleep */ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen, ftello64 */ #include /* clock_t, clock, CLOCKS_PER_SEC */ #include /* toupper */ +#include "mem.h" #define ZSTD_STATIC_LINKING_ONLY #include "zstd.h" #include "datagen.h" /* RDG_genBuffer */ From e679741b188a29631aabd226f3653ed90552d78c Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 21 Dec 2016 13:47:11 +0100 Subject: [PATCH 044/227] _CRT_SECURE_NO_WARNINGS moved to util.h --- programs/bench.c | 2 -- programs/datagen.c | 8 -------- programs/dibio.c | 2 -- programs/fileio.c | 3 +-- programs/util.h | 2 ++ programs/zstdcli.c | 1 - tests/fuzzer.c | 1 - tests/zbufftest.c | 1 - tests/zstreamtest.c | 1 - 9 files changed, 3 insertions(+), 18 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 9b7f98884..5d142529f 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -13,7 +13,6 @@ * Compiler Warnings ****************************************/ #ifdef _MSC_VER -# define _CRT_SECURE_NO_WARNINGS /* fopen */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif @@ -21,7 +20,6 @@ /* ************************************* * Includes ***************************************/ -#include "platform.h" /* Large Files support */ #include "util.h" /* UTIL_getFileSize, UTIL_sleep */ #include /* malloc, free */ #include /* memset */ diff --git a/programs/datagen.c b/programs/datagen.c index 06b5ab977..d0116b972 100644 --- a/programs/datagen.c +++ b/programs/datagen.c @@ -9,14 +9,6 @@ -/* ************************************* -* Compiler Options -***************************************/ -#if defined(_MSC_VER) -# define _CRT_SECURE_NO_WARNINGS /* removes Visual warning on strerror() */ -# define _CRT_SECURE_NO_DEPRECATE /* removes VS2005 warning on strerror() */ -#endif - /*-************************************ * Dependencies **************************************/ diff --git a/programs/dibio.c b/programs/dibio.c index b99e717c2..743d3ef93 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -13,7 +13,6 @@ * Compiler Warnings ****************************************/ #ifdef _MSC_VER -# define _CRT_SECURE_NO_WARNINGS /* fopen */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif @@ -21,7 +20,6 @@ /*-************************************* * Includes ***************************************/ -#include "platform.h" /* Large Files support */ #include "util.h" /* UTIL_getFileSize, UTIL_getTotalFileSize */ #include /* malloc, free */ #include /* memset */ diff --git a/programs/fileio.c b/programs/fileio.c index e2a90a678..5544fe30a 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -11,7 +11,7 @@ * Compiler Options ***************************************/ #ifdef _MSC_VER /* Visual */ -# define _CRT_SECURE_NO_WARNINGS /* removes Visual warning on strerror() */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4204) /* non-constant aggregate initializer */ #endif #if defined(__MINGW32__) && !defined(_POSIX_SOURCE) @@ -22,7 +22,6 @@ /*-************************************* * Includes ***************************************/ -#include "platform.h" /* Large Files support, SET_BINARY_MODE */ #include "util.h" /* UTIL_getFileSize */ #include /* fprintf, fopen, fread, _fileno, stdin, stdout */ #include /* malloc, free */ diff --git a/programs/util.h b/programs/util.h index 8b480b226..5779fa772 100644 --- a/programs/util.h +++ b/programs/util.h @@ -29,6 +29,8 @@ extern "C" { * Compiler Options ****************************************/ #if defined(_MSC_VER) +# define _CRT_SECURE_NO_WARNINGS /* Disable Visual Studio warning messages for fopen, strncpy, strerror */ +# define _CRT_SECURE_NO_DEPRECATE /* VS2005 */ # if (_MSC_VER <= 1800) /* (1800 = Visual Studio 2013) */ # define snprintf sprintf_s /* snprintf unsupported by Visual <= 2013 */ # endif diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 978ffcfe0..20698c78d 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -23,7 +23,6 @@ /*-************************************ * Dependencies **************************************/ -#include "platform.h" /* IS_CONSOLE, PLATFORM_POSIX_VERSION */ #include "util.h" /* UTIL_HAS_CREATEFILELIST, UTIL_createFileList */ #include /* strcmp, strlen */ #include /* errno */ diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 86d4c6beb..b9b0158ed 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -12,7 +12,6 @@ * 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 diff --git a/tests/zbufftest.c b/tests/zbufftest.c index 14b739233..0fda993fc 100644 --- a/tests/zbufftest.c +++ b/tests/zbufftest.c @@ -12,7 +12,6 @@ * 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 : 4146) /* disable: C4146: minus unsigned expression */ #endif diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index ce6193085..b52845045 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -12,7 +12,6 @@ * 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 : 4146) /* disable: C4146: minus unsigned expression */ #endif From 97a258d71ddf6c7a7723b680f356bc9721d2e0a8 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 21 Dec 2016 14:00:41 +0100 Subject: [PATCH 045/227] updated comments --- programs/fileio.c | 2 +- programs/zstdcli.c | 2 +- tests/fuzzer.c | 1 + tests/zbufftest.c | 1 + tests/zstreamtest.c | 1 + 5 files changed, 5 insertions(+), 2 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 5544fe30a..8305fbd76 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -22,7 +22,7 @@ /*-************************************* * Includes ***************************************/ -#include "util.h" /* UTIL_getFileSize */ +#include "util.h" /* SET_BINARY_MODE, UTIL_getFileSize */ #include /* fprintf, fopen, fread, _fileno, stdin, stdout */ #include /* malloc, free */ #include /* strcmp, strlen */ diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 20698c78d..0b6f9241c 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -23,7 +23,7 @@ /*-************************************ * Dependencies **************************************/ -#include "util.h" /* UTIL_HAS_CREATEFILELIST, UTIL_createFileList */ +#include "util.h" /* IS_CONSOLE, UTIL_HAS_CREATEFILELIST, UTIL_createFileList */ #include /* strcmp, strlen */ #include /* errno */ #include "fileio.h" diff --git a/tests/fuzzer.c b/tests/fuzzer.c index b9b0158ed..86d4c6beb 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -12,6 +12,7 @@ * 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 diff --git a/tests/zbufftest.c b/tests/zbufftest.c index 0fda993fc..14b739233 100644 --- a/tests/zbufftest.c +++ b/tests/zbufftest.c @@ -12,6 +12,7 @@ * 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 : 4146) /* disable: C4146: minus unsigned expression */ #endif diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index b52845045..ce6193085 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -12,6 +12,7 @@ * 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 : 4146) /* disable: C4146: minus unsigned expression */ #endif From 7a8a03c20d71ede3896a6e3ed9820baae5b0ccf9 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 21 Dec 2016 15:08:44 +0100 Subject: [PATCH 046/227] util.h: restore BSD license for Facebook Open-Source --- programs/bench.c | 1 + programs/dibio.c | 1 + programs/fileio.c | 3 ++- programs/platform.h | 41 +++++++++++++++++++++++------------------ programs/util.h | 39 ++++++++++----------------------------- programs/zstdcli.c | 3 ++- 6 files changed, 39 insertions(+), 49 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 5d142529f..4089d6ba7 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -20,6 +20,7 @@ /* ************************************* * Includes ***************************************/ +#include "platform.h" /* Large Files support */ #include "util.h" /* UTIL_getFileSize, UTIL_sleep */ #include /* malloc, free */ #include /* memset */ diff --git a/programs/dibio.c b/programs/dibio.c index 743d3ef93..b95bab34e 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -20,6 +20,7 @@ /*-************************************* * Includes ***************************************/ +#include "platform.h" /* Large Files support */ #include "util.h" /* UTIL_getFileSize, UTIL_getTotalFileSize */ #include /* malloc, free */ #include /* memset */ diff --git a/programs/fileio.c b/programs/fileio.c index 8305fbd76..a112cc049 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -22,7 +22,8 @@ /*-************************************* * Includes ***************************************/ -#include "util.h" /* SET_BINARY_MODE, UTIL_getFileSize */ +#include "platform.h" /* Large Files support, SET_BINARY_MODE */ +#include "util.h" /* UTIL_getFileSize */ #include /* fprintf, fopen, fread, _fileno, stdin, stdout */ #include /* malloc, free */ #include /* strcmp, strlen */ diff --git a/programs/platform.h b/programs/platform.h index 0b82e46bf..f30528aa9 100644 --- a/programs/platform.h +++ b/programs/platform.h @@ -1,21 +1,13 @@ -/* - platform.h - compiler and OS detection - Copyright (C) 2016-present, Przemyslaw Skibinski, Yann Collet - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ +/** + * platform.h - compiler and OS detection + * + * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #ifndef PLATFORM_H_MODULE #define PLATFORM_H_MODULE @@ -25,6 +17,19 @@ extern "C" { #endif + +/* ************************************** +* Compiler Options +****************************************/ +#if defined(_MSC_VER) +# define _CRT_SECURE_NO_WARNINGS /* Disable Visual Studio warning messages for fopen, strncpy, strerror */ +# define _CRT_SECURE_NO_DEPRECATE /* VS2005 - must be declared before and */ +# if (_MSC_VER <= 1800) /* (1800 = Visual Studio 2013) */ +# define snprintf sprintf_s /* snprintf unsupported by Visual <= 2013 */ +# endif +#endif + + /* ************************************** * Detect 64-bit OS * http://nadeausoftware.com/articles/2012/02/c_c_tip_how_detect_processor_type_using_compiler_predefined_macros diff --git a/programs/util.h b/programs/util.h index 5779fa772..aaa4b7c1e 100644 --- a/programs/util.h +++ b/programs/util.h @@ -1,21 +1,13 @@ -/* - util.h - utility functions - Copyright (C) 2016-present, Przemyslaw Skibinski, Yann Collet - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ +/** + * util.h - utility functions + * + * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #ifndef UTIL_H_MODULE #define UTIL_H_MODULE @@ -25,17 +17,6 @@ extern "C" { #endif -/* ************************************** -* Compiler Options -****************************************/ -#if defined(_MSC_VER) -# define _CRT_SECURE_NO_WARNINGS /* Disable Visual Studio warning messages for fopen, strncpy, strerror */ -# define _CRT_SECURE_NO_DEPRECATE /* VS2005 */ -# if (_MSC_VER <= 1800) /* (1800 = Visual Studio 2013) */ -# define snprintf sprintf_s /* snprintf unsupported by Visual <= 2013 */ -# endif -#endif - /*-**************************************** * Dependencies diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 0b6f9241c..978ffcfe0 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -23,7 +23,8 @@ /*-************************************ * Dependencies **************************************/ -#include "util.h" /* IS_CONSOLE, UTIL_HAS_CREATEFILELIST, UTIL_createFileList */ +#include "platform.h" /* IS_CONSOLE, PLATFORM_POSIX_VERSION */ +#include "util.h" /* UTIL_HAS_CREATEFILELIST, UTIL_createFileList */ #include /* strcmp, strlen */ #include /* errno */ #include "fileio.h" From 1f57c2ed32369b491ae1c47774d14ec29e199dd0 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 21 Dec 2016 16:20:11 +0100 Subject: [PATCH 047/227] added : ZSTD_createCDict_byReference() --- Makefile | 3 ++ lib/compress/zstd_compress.c | 48 +++++++++++++++++++++---------- lib/zstd.h | 12 ++++++-- programs/bench.c | 6 ++-- zlibWrapper/examples/zwrapbench.c | 2 +- 5 files changed, 49 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index bb3a4e4ce..7d9ff4f8d 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,8 @@ all: $(MAKE) -C $(ZSTDDIR) $@ $(MAKE) -C $(PRGDIR) $@ zstd32 $(MAKE) -C $(TESTDIR) $@ all32 + $(MAKE) -C $(ZWRAPDIR) $@ + CPPFLAGS=-I../lib LDFLAGS=-L../lib $(MAKE) -C examples/ $@ .PHONY: lib lib: @@ -54,6 +56,7 @@ clean: @$(MAKE) -C $(PRGDIR) $@ > $(VOID) @$(MAKE) -C $(TESTDIR) $@ > $(VOID) @$(MAKE) -C $(ZWRAPDIR) $@ > $(VOID) + @$(MAKE) -C examples/ $@ > $(VOID) @$(RM) zstd$(EXT) tmp* @echo Cleaning completed diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e88f6a1d0..2a9ddd669 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2733,8 +2733,10 @@ size_t ZSTD_compress(void* dst, size_t dstCapacity, const void* src, size_t srcS /* ===== Dictionary API ===== */ struct ZSTD_CDict_s { - void* dictContent; + void* dictBuffer; + const void* dictContent; size_t dictContentSize; + unsigned byReference; ZSTD_CCtx* refContext; }; /* typedef'd tp ZSTD_CDict within "zstd.h" */ @@ -2744,36 +2746,44 @@ size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict) return ZSTD_sizeof_CCtx(cdict->refContext) + cdict->dictContentSize; } -ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, ZSTD_parameters params, ZSTD_customMem customMem) +ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize, unsigned byReference, + ZSTD_parameters params, ZSTD_customMem customMem) { if (!customMem.customAlloc && !customMem.customFree) customMem = defaultCustomMem; if (!customMem.customAlloc || !customMem.customFree) return NULL; { ZSTD_CDict* const cdict = (ZSTD_CDict*) ZSTD_malloc(sizeof(ZSTD_CDict), customMem); - void* const dictContent = ZSTD_malloc(dictSize, customMem); ZSTD_CCtx* const cctx = ZSTD_createCCtx_advanced(customMem); - if (!dictContent || !cdict || !cctx) { - ZSTD_free(dictContent, customMem); + if (!cdict || !cctx) { ZSTD_free(cdict, customMem); ZSTD_free(cctx, customMem); return NULL; } - if (dictSize) { - memcpy(dictContent, dict, dictSize); + if ((byReference) || (!dictBuffer) || (!dictSize)) { + cdict->dictBuffer = NULL; + cdict->dictContent = dictBuffer; + cdict->byReference = 1; + } else { + void* const internalBuffer = ZSTD_malloc(dictSize, customMem); + if (!internalBuffer) return NULL; + memcpy(internalBuffer, dictBuffer, dictSize); + cdict->dictBuffer = internalBuffer; + cdict->dictContent = internalBuffer; + cdict->byReference = 0; } - { size_t const errorCode = ZSTD_compressBegin_advanced(cctx, dictContent, dictSize, params, 0); + + { size_t const errorCode = ZSTD_compressBegin_advanced(cctx, cdict->dictContent, dictSize, params, 0); if (ZSTD_isError(errorCode)) { - ZSTD_free(dictContent, customMem); - ZSTD_free(cdict, customMem); + ZSTD_free(cdict->dictBuffer, customMem); ZSTD_free(cctx, customMem); + ZSTD_free(cdict, customMem); return NULL; } } - cdict->dictContent = dictContent; - cdict->dictContentSize = dictSize; cdict->refContext = cctx; + cdict->dictContentSize = dictSize; return cdict; } } @@ -2783,7 +2793,15 @@ ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionL ZSTD_customMem const allocator = { NULL, NULL, NULL }; ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, dictSize); params.fParams.contentSizeFlag = 1; - return ZSTD_createCDict_advanced(dict, dictSize, params, allocator); + return ZSTD_createCDict_advanced(dict, dictSize, 0, params, allocator); +} + +ZSTD_CDict* ZSTD_createCDict_byReference(const void* dict, size_t dictSize, int compressionLevel) +{ + ZSTD_customMem const allocator = { NULL, NULL, NULL }; + ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, dictSize); + params.fParams.contentSizeFlag = 1; + return ZSTD_createCDict_advanced(dict, dictSize, 1, params, allocator); } size_t ZSTD_freeCDict(ZSTD_CDict* cdict) @@ -2791,7 +2809,7 @@ size_t ZSTD_freeCDict(ZSTD_CDict* cdict) if (cdict==NULL) return 0; /* support free on NULL */ { ZSTD_customMem const cMem = cdict->refContext->customMem; ZSTD_freeCCtx(cdict->refContext); - ZSTD_free(cdict->dictContent, cMem); + if (!cdict->byReference) ZSTD_free(cdict->dictBuffer, cMem); ZSTD_free(cdict, cMem); return 0; } @@ -2939,7 +2957,7 @@ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, if (dict) { ZSTD_freeCDict(zcs->cdictLocal); - zcs->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize, params, zcs->customMem); + zcs->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize, 0, params, zcs->customMem); if (zcs->cdictLocal == NULL) return ERROR(memory_allocation); zcs->cdict = zcs->cdictLocal; } else zcs->cdict = NULL; diff --git a/lib/zstd.h b/lib/zstd.h index 7eda69877..20682b494 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -173,8 +173,8 @@ typedef struct ZSTD_CDict_s ZSTD_CDict; * When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once. * ZSTD_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay. * ZSTD_CDict can be created once and used by multiple threads concurrently, as its usage is read-only. -* `dict` can be released after ZSTD_CDict creation. */ -ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionLevel); +* `dictBuffer` can be released after ZSTD_CDict creation, as its content is copied within CDict */ +ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict(const void* dictBuffer, size_t dictSize, int compressionLevel); /*! ZSTD_freeCDict() : * Function frees memory allocated by ZSTD_createCDict(). */ @@ -400,9 +400,15 @@ ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem); * Gives the amount of memory used by a given ZSTD_CCtx */ ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx); +/*! ZSTD_createCDict_byReference() : + * Create a digested dictionary for compression + * Dictionary content is simply referenced, and therefore stays in dictBuffer. + * It is important that dictBuffer outlives CDict, it must remain read accessible throughout the lifetime of CDict */ +ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel); + /*! ZSTD_createCDict_advanced() : * Create a ZSTD_CDict using external alloc and free, and customized compression parameters */ -ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, +ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, unsigned byReference, ZSTD_parameters params, ZSTD_customMem customMem); /*! ZSTD_sizeof_CDict() : diff --git a/programs/bench.c b/programs/bench.c index 6c2383a87..9eac10b74 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -236,7 +236,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, ZSTD_customMem const cmem = { NULL, NULL, NULL }; U64 clockLoop = g_nbSeconds ? TIMELOOP_MICROSEC : 1; U32 nbLoops = 0; - ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, zparams, cmem); + ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, 1, zparams, cmem); if (cdict==NULL) EXM_THROW(1, "ZSTD_createCDict_advanced() allocation failure"); if (comprParams->windowLog) zparams.cParams.windowLog = comprParams->windowLog; if (comprParams->chainLog) zparams.cParams.chainLog = comprParams->chainLog; @@ -452,7 +452,7 @@ static void BMK_loadFiles(void* buffer, size_t bufferSize, if (totalSize == 0) EXM_THROW(12, "no data to bench"); } -static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, const char* dictFileName, +static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, const char* dictFileName, int cLevel, int cLevelLast, ZSTD_compressionParameters *compressionParams) { void* srcBuffer; @@ -523,7 +523,7 @@ static void BMK_syntheticTest(int cLevel, int cLevelLast, double compressibility } -int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, const char* dictFileName, +int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, const char* dictFileName, int cLevel, int cLevelLast, ZSTD_compressionParameters* compressionParams) { double const compressibility = (double)g_compressibilityDefault / 100; diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c index e0aca0015..49a2632e1 100644 --- a/zlibWrapper/examples/zwrapbench.c +++ b/zlibWrapper/examples/zwrapbench.c @@ -234,7 +234,7 @@ static int BMK_benchMem(z_const void* srcBuffer, size_t srcSize, if (compressor == BMK_ZSTD) { ZSTD_parameters const zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize); ZSTD_customMem const cmem = { NULL, NULL, NULL }; - ZSTD_CDict* cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, zparams, cmem); + ZSTD_CDict* cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, 1, zparams, cmem); if (cdict==NULL) EXM_THROW(1, "ZSTD_createCDict_advanced() allocation failure"); do { From 4e5eea61a8646a985b9f1fc7421178ef8c59182d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 21 Dec 2016 16:44:35 +0100 Subject: [PATCH 048/227] added ZSTD_createDDict_byReference() --- NEWS | 1 + lib/compress/zstd_compress.c | 7 ++----- lib/decompress/zstd_decompress.c | 35 ++++++++++++++++++-------------- lib/zstd.h | 15 +++++++++++--- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/NEWS b/NEWS index 9a341781e..5e9271817 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ v1.1.3 cli : new : commands for advanced parameters, by Przemyslaw Skibinski API : fix : all symbols properly exposed in libzstd, by Nick Terrell +API : new : ZSTD_create?Dict_byReference(), requested by Bartosz Taudul v1.1.2 API : streaming : decompression : changed : automatic implicit reset when chain-decoding new frames without init diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 2a9ddd669..afac869c8 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2736,7 +2736,6 @@ struct ZSTD_CDict_s { void* dictBuffer; const void* dictContent; size_t dictContentSize; - unsigned byReference; ZSTD_CCtx* refContext; }; /* typedef'd tp ZSTD_CDict within "zstd.h" */ @@ -2764,14 +2763,12 @@ ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize, u if ((byReference) || (!dictBuffer) || (!dictSize)) { cdict->dictBuffer = NULL; cdict->dictContent = dictBuffer; - cdict->byReference = 1; } else { void* const internalBuffer = ZSTD_malloc(dictSize, customMem); - if (!internalBuffer) return NULL; + if (!internalBuffer) { ZSTD_free(cctx, customMem); ZSTD_free(cdict, customMem); return NULL; } memcpy(internalBuffer, dictBuffer, dictSize); cdict->dictBuffer = internalBuffer; cdict->dictContent = internalBuffer; - cdict->byReference = 0; } { size_t const errorCode = ZSTD_compressBegin_advanced(cctx, cdict->dictContent, dictSize, params, 0); @@ -2809,7 +2806,7 @@ size_t ZSTD_freeCDict(ZSTD_CDict* cdict) if (cdict==NULL) return 0; /* support free on NULL */ { ZSTD_customMem const cMem = cdict->refContext->customMem; ZSTD_freeCCtx(cdict->refContext); - if (!cdict->byReference) ZSTD_free(cdict->dictBuffer, cMem); + ZSTD_free(cdict->dictBuffer, cMem); ZSTD_free(cdict, cMem); return 0; } diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 7addff8d0..19e8287e2 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1713,39 +1713,44 @@ size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t /* ====== ZSTD_DDict ====== */ struct ZSTD_DDict_s { - void* dict; + void* dictBuffer; + const void* dictContent; size_t dictSize; ZSTD_DCtx* refContext; }; /* typedef'd to ZSTD_DDict within "zstd.h" */ -ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_customMem customMem) +ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, unsigned byReference, ZSTD_customMem customMem) { if (!customMem.customAlloc && !customMem.customFree) customMem = defaultCustomMem; if (!customMem.customAlloc || !customMem.customFree) return NULL; { ZSTD_DDict* const ddict = (ZSTD_DDict*) ZSTD_malloc(sizeof(ZSTD_DDict), customMem); - void* const dictContent = ZSTD_malloc(dictSize, customMem); ZSTD_DCtx* const dctx = ZSTD_createDCtx_advanced(customMem); - if (!dictContent || !ddict || !dctx) { - ZSTD_free(dictContent, customMem); + if (!ddict || !dctx) { ZSTD_free(ddict, customMem); ZSTD_free(dctx, customMem); return NULL; } - if (dictSize) { - memcpy(dictContent, dict, dictSize); + if ((byReference) || (!dict) || (!dictSize)) { + ddict->dictBuffer = NULL; + ddict->dictContent = dict; + } else { + void* const internalBuffer = ZSTD_malloc(dictSize, customMem); + if (!internalBuffer) { ZSTD_free(dctx, customMem); ZSTD_free(ddict, customMem); return NULL; } + memcpy(internalBuffer, dict, dictSize); + ddict->dictBuffer = internalBuffer; + ddict->dictContent = internalBuffer; } - { size_t const errorCode = ZSTD_decompressBegin_usingDict(dctx, dictContent, dictSize); + { size_t const errorCode = ZSTD_decompressBegin_usingDict(dctx, ddict->dictContent, dictSize); if (ZSTD_isError(errorCode)) { - ZSTD_free(dictContent, customMem); + ZSTD_free(ddict->dictBuffer, customMem); ZSTD_free(ddict, customMem); ZSTD_free(dctx, customMem); return NULL; } } - ddict->dict = dictContent; ddict->dictSize = dictSize; ddict->refContext = dctx; return ddict; @@ -1758,7 +1763,7 @@ ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, ZSTD_cu ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize) { ZSTD_customMem const allocator = { NULL, NULL, NULL }; - return ZSTD_createDDict_advanced(dict, dictSize, allocator); + return ZSTD_createDDict_advanced(dict, dictSize, 0, allocator); } size_t ZSTD_freeDDict(ZSTD_DDict* ddict) @@ -1766,7 +1771,7 @@ size_t ZSTD_freeDDict(ZSTD_DDict* ddict) if (ddict==NULL) return 0; /* support free on NULL */ { ZSTD_customMem const cMem = ddict->refContext->customMem; ZSTD_freeDCtx(ddict->refContext); - ZSTD_free(ddict->dict, cMem); + ZSTD_free(ddict->dictBuffer, cMem); ZSTD_free(ddict, cMem); return 0; } @@ -1796,7 +1801,7 @@ unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize) unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict) { if (ddict==NULL) return 0; - return ZSTD_getDictID_fromDict(ddict->dict, ddict->dictSize); + return ZSTD_getDictID_fromDict(ddict->dictContent, ddict->dictSize); } /*! ZSTD_getDictID_fromFrame() : @@ -1827,7 +1832,7 @@ size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) { #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT==1) - if (ZSTD_isLegacy(src, srcSize)) return ZSTD_decompressLegacy(dst, dstCapacity, src, srcSize, ddict->dict, ddict->dictSize); + if (ZSTD_isLegacy(src, srcSize)) return ZSTD_decompressLegacy(dst, dstCapacity, src, srcSize, ddict->dictContent, ddict->dictSize); #endif ZSTD_refDCtx(dctx, ddict->refContext); ZSTD_checkContinuity(dctx, dst); @@ -2007,7 +2012,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) { U32 const legacyVersion = ZSTD_isLegacy(istart, iend-istart); if (legacyVersion) { - const void* const dict = zds->ddict ? zds->ddict->dict : NULL; + const void* const dict = zds->ddict ? zds->ddict->dictContent : NULL; size_t const dictSize = zds->ddict ? zds->ddict->dictSize : 0; CHECK_F(ZSTD_initLegacyStream(&zds->legacyContext, zds->previousLegacyVersion, legacyVersion, dict, dictSize)); diff --git a/lib/zstd.h b/lib/zstd.h index 20682b494..187ee5c28 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -194,8 +194,8 @@ typedef struct ZSTD_DDict_s ZSTD_DDict; /*! ZSTD_createDDict() : * Create a digested dictionary, ready to start decompression operation without startup delay. -* `dict` can be released after creation. */ -ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize); +* dictBuffer can be released after DDict creation, as its content is copied inside DDict */ +ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict(const void* dictBuffer, size_t dictSize); /*! ZSTD_freeDDict() : * Function frees memory allocated with ZSTD_createDDict() */ @@ -328,7 +328,7 @@ ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output * ***************************************************************************************/ /* --- Constants ---*/ -#define ZSTD_MAGICNUMBER 0xFD2FB528 /* v0.8 */ +#define ZSTD_MAGICNUMBER 0xFD2FB528 /* >= v0.8.0 */ #define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50U #define ZSTD_WINDOWLOG_MAX_32 25 @@ -464,6 +464,15 @@ ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem); * Gives the amount of memory used by a given ZSTD_DCtx */ ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx); +/*! ZSTD_createDDict_byReference() : + * Create a digested dictionary, ready to start decompression operation without startup delay. + * Dictionary content is simply referenced, and therefore stays in dictBuffer. + * It is important that dictBuffer outlives DDict, it must remain read accessible throughout the lifetime of DDict */ +ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize); + +ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, + unsigned byReference, ZSTD_customMem customMem); + /*! ZSTD_sizeof_DDict() : * Gives the amount of memory used by a given ZSTD_DDict */ ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict); From 0819abe3c17d285394c16878e67748c060007921 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 21 Dec 2016 19:25:15 +0100 Subject: [PATCH 049/227] added ZSTD_createDDict_byReference() body --- lib/common/mem.h | 2 +- lib/decompress/zstd_decompress.c | 12 ++++++++++++ lib/dictBuilder/zdict.c | 14 +++++++------- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/common/mem.h b/lib/common/mem.h index 32c63dd17..aff044de1 100644 --- a/lib/common/mem.h +++ b/lib/common/mem.h @@ -39,7 +39,7 @@ extern "C" { #endif /* code only tested on 32 and 64 bits systems */ -#define MEM_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(int)(!!(c)) }; } +#define MEM_STATIC_ASSERT(c) { enum { MEM_static_assert = 1/(int)(!!(c)) }; } MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (sizeof(size_t)==8)); } diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 19e8287e2..e976cd26d 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1766,6 +1766,18 @@ ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize) return ZSTD_createDDict_advanced(dict, dictSize, 0, allocator); } + +/*! ZSTD_createDDict_byReference() : + * Create a digested dictionary, ready to start decompression operation without startup delay. + * Dictionary content is simply referenced, and therefore stays in dictBuffer. + * It is important that dictBuffer outlives DDict, it must remain read accessible throughout the lifetime of DDict */ +ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize) +{ + ZSTD_customMem const allocator = { NULL, NULL, NULL }; + return ZSTD_createDDict_advanced(dictBuffer, dictSize, 1, allocator); +} + + size_t ZSTD_freeDDict(ZSTD_DDict* ddict) { if (ddict==NULL) return 0; /* support free on NULL */ diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 921e37886..ac22e8705 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -306,13 +306,13 @@ static dictItem ZDICT_analyzePos( } while (length >=MINMATCHLENGTH); /* look backward */ - length = MINMATCHLENGTH; - while ((length >= MINMATCHLENGTH) & (start > 0)) { - length = ZDICT_count(b + pos, b + suffix[start - 1]); - if (length >= LLIMIT) length = LLIMIT - 1; - lengthList[length]++; - if (length >= MINMATCHLENGTH) start--; - } + length = MINMATCHLENGTH; + while ((length >= MINMATCHLENGTH) & (start > 0)) { + length = ZDICT_count(b + pos, b + suffix[start - 1]); + if (length >= LLIMIT) length = LLIMIT - 1; + lengthList[length]++; + if (length >= MINMATCHLENGTH) start--; + } /* largest useful length */ memset(cumulLength, 0, sizeof(cumulLength)); From ba75e9d8c39c26269dab74e2d512436c8d38d2e8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 21 Dec 2016 19:57:18 +0100 Subject: [PATCH 050/227] fix : zlib wrapper compile in gnu90 mode --- lib/zstd.h | 3 ++- zlibWrapper/zstd_zlibwrapper.c | 34 +++++++++++++++++----------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/zstd.h b/lib/zstd.h index 187ee5c28..333feff7d 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -348,8 +348,9 @@ ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output #define ZSTD_TARGETLENGTH_MAX 999 #define ZSTD_FRAMEHEADERSIZE_MAX 18 /* for static allocation */ +#define ZSTD_FRAMEHEADERSIZE_MIN 6 static const size_t ZSTD_frameHeaderSize_prefix = 5; -static const size_t ZSTD_frameHeaderSize_min = 6; +static const size_t ZSTD_frameHeaderSize_min = ZSTD_FRAMEHEADERSIZE_MIN; static const size_t ZSTD_frameHeaderSize_max = ZSTD_FRAMEHEADERSIZE_MAX; static const size_t ZSTD_skippableHeaderSize = 8; /* magic number + skippable frame length */ diff --git a/zlibWrapper/zstd_zlibwrapper.c b/zlibWrapper/zstd_zlibwrapper.c index d26663142..b25d92066 100644 --- a/zlibWrapper/zstd_zlibwrapper.c +++ b/zlibWrapper/zstd_zlibwrapper.c @@ -130,7 +130,7 @@ int ZWRAP_initializeCStream(ZWRAP_CCtx* zwc, const void* dict, size_t dictSize, { LOG_WRAPPERC("- ZWRAP_initializeCStream=%p\n", zwc); if (zwc == NULL || zwc->zbc == NULL) return Z_STREAM_ERROR; - + if (!pledgedSrcSize) pledgedSrcSize = zwc->pledgedSrcSize; { ZSTD_parameters const params = ZSTD_getParams(zwc->compressionLevel, pledgedSrcSize, dictSize); size_t errorCode; @@ -156,7 +156,7 @@ int ZWRAPC_finishWithErrorMsg(z_streamp strm, char* message) ZWRAP_CCtx* zwc = (ZWRAP_CCtx*) strm->state; strm->msg = message; if (zwc == NULL) return Z_STREAM_ERROR; - + return ZWRAPC_finishWithError(zwc, strm, 0); } @@ -165,7 +165,7 @@ int ZWRAP_setPledgedSrcSize(z_streamp strm, unsigned long long pledgedSrcSize) { ZWRAP_CCtx* zwc = (ZWRAP_CCtx*) strm->state; if (zwc == NULL) return Z_STREAM_ERROR; - + zwc->pledgedSrcSize = pledgedSrcSize; zwc->comprState = ZWRAP_useInit; return Z_OK; @@ -232,7 +232,7 @@ ZEXTERN int ZEXPORT z_deflateReset OF((z_streamp strm)) LOG_WRAPPERC("- deflateReset\n"); if (!g_ZWRAP_useZSTDcompression) return deflateReset(strm); - + ZWRAP_deflateReset_keepDict(strm); { ZWRAP_CCtx* zwc = (ZWRAP_CCtx*) strm->state; @@ -284,7 +284,7 @@ ZEXTERN int ZEXPORT z_deflate OF((z_streamp strm, int flush)) if (zwc->zbc == NULL) { int res; zwc->zbc = ZSTD_createCStream_advanced(zwc->customMem); - if (zwc->zbc == NULL) return ZWRAPC_finishWithError(zwc, strm, 0); + if (zwc->zbc == NULL) return ZWRAPC_finishWithError(zwc, strm, 0); res = ZWRAP_initializeCStream(zwc, NULL, 0, (flush == Z_FINISH) ? strm->avail_in : 0); if (res != Z_OK) return ZWRAPC_finishWithError(zwc, strm, res); if (flush != Z_FINISH) zwc->comprState = ZWRAP_useReset; @@ -321,9 +321,9 @@ ZEXTERN int ZEXPORT z_deflate OF((z_streamp strm, int flush)) strm->avail_in -= zwc->inBuffer.pos; } - if (flush == Z_FULL_FLUSH + if (flush == Z_FULL_FLUSH #if ZLIB_VERNUM >= 0x1240 - || flush == Z_TREES + || flush == Z_TREES #endif || flush == Z_BLOCK) return ZWRAPC_finishWithErrorMsg(strm, "Z_FULL_FLUSH, Z_BLOCK and Z_TREES are not supported!"); @@ -424,7 +424,7 @@ typedef struct { } ZWRAP_DCtx; -int ZWRAP_isUsingZSTDdecompression(z_streamp strm) +int ZWRAP_isUsingZSTDdecompression(z_streamp strm) { if (strm == NULL) return 0; return (strm->reserved == ZWRAP_ZSTD_STREAM); @@ -458,7 +458,7 @@ ZWRAP_DCtx* ZWRAP_createDCtx(z_streamp strm) memcpy(&zwd->customMem, &defaultCustomMem, sizeof(ZSTD_customMem)); } - MEM_STATIC_ASSERT(sizeof(zwd->headerBuf) >= ZSTD_frameHeaderSize_min); /* if compilation fails here, assertion is false */ + MEM_STATIC_ASSERT(sizeof(zwd->headerBuf) >= ZSTD_FRAMEHEADERSIZE_MIN); /* if compilation fails here, assertion is false */ ZWRAP_initDCtx(zwd); return zwd; } @@ -488,7 +488,7 @@ int ZWRAPD_finishWithErrorMsg(z_streamp strm, char* message) ZWRAP_DCtx* zwd = (ZWRAP_DCtx*) strm->state; strm->msg = message; if (zwd == NULL) return Z_STREAM_ERROR; - + return ZWRAPD_finishWithError(zwd, strm, 0); } @@ -528,7 +528,7 @@ ZEXTERN int ZEXPORT z_inflateInit2_ OF((z_streamp strm, int windowBits, if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB) { return inflateInit2_(strm, windowBits, version, stream_size); } - + { int ret = z_inflateInit_ (strm, version, stream_size); LOG_WRAPPERD("- inflateInit2 windowBits=%d\n", windowBits); @@ -552,7 +552,7 @@ int ZWRAP_inflateReset_keepDict(z_streamp strm) ZWRAP_initDCtx(zwd); zwd->decompState = ZWRAP_useReset; } - + strm->total_in = 0; strm->total_out = 0; return Z_OK; @@ -569,7 +569,7 @@ ZEXTERN int ZEXPORT z_inflateReset OF((z_streamp strm)) if (ret != Z_OK) return ret; } { ZWRAP_DCtx* zwd = (ZWRAP_DCtx*) strm->state; - if (zwd == NULL) return Z_STREAM_ERROR; + if (zwd == NULL) return Z_STREAM_ERROR; zwd->decompState = ZWRAP_useInit; } return Z_OK; @@ -608,7 +608,7 @@ ZEXTERN int ZEXPORT z_inflateSetDictionary OF((z_streamp strm, if (zwd == NULL || zwd->zbd == NULL) return Z_STREAM_ERROR; errorCode = ZSTD_initDStream_usingDict(zwd->zbd, dictionary, dictLength); if (ZSTD_isError(errorCode)) return ZWRAPD_finishWithError(zwd, strm, 0); - zwd->decompState = ZWRAP_useReset; + zwd->decompState = ZWRAP_useReset; if (strm->total_in == ZSTD_HEADERSIZE) { zwd->inBuffer.src = zwd->headerBuf; @@ -787,9 +787,9 @@ ZEXTERN int ZEXPORT z_inflate OF((z_streamp strm, int flush)) strm->total_in += zwd->inBuffer.pos; strm->next_in += zwd->inBuffer.pos; strm->avail_in -= zwd->inBuffer.pos; - if (errorCode == 0) { - LOG_WRAPPERD("inflate Z_STREAM_END1 avail_in=%d avail_out=%d total_in=%d total_out=%d\n", (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out); - zwd->decompState = ZWRAP_streamEnd; + if (errorCode == 0) { + LOG_WRAPPERD("inflate Z_STREAM_END1 avail_in=%d avail_out=%d total_in=%d total_out=%d\n", (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out); + zwd->decompState = ZWRAP_streamEnd; return Z_STREAM_END; } } From a86a09ea0dcaca40a00d13f973dbde54afe9f1fb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 22 Dec 2016 11:31:39 +0100 Subject: [PATCH 051/227] removed examples from standard C tests, since they contain some POSIX elements --- Makefile | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 7d9ff4f8d..691d5d82b 100644 --- a/Makefile +++ b/Makefile @@ -26,12 +26,15 @@ endif default: lib zstd .PHONY: all -all: +all: allmost + CPPFLAGS=-I../lib LDFLAGS=-L../lib $(MAKE) -C examples/ $@ + +.PHONY: allmost +allmost: # without examples $(MAKE) -C $(ZSTDDIR) $@ $(MAKE) -C $(PRGDIR) $@ zstd32 $(MAKE) -C $(TESTDIR) $@ all32 $(MAKE) -C $(ZWRAPDIR) $@ - CPPFLAGS=-I../lib LDFLAGS=-L../lib $(MAKE) -C examples/ $@ .PHONY: lib lib: @@ -151,13 +154,13 @@ gnu90test: clean CFLAGS="-std=gnu90" $(MAKE) all c99test: clean - CFLAGS="-std=c99" $(MAKE) all + CFLAGS="-std=c99" $(MAKE) allmost gnu99test: clean CFLAGS="-std=gnu99" $(MAKE) all c11test: clean - CFLAGS="-std=c11" $(MAKE) all + CFLAGS="-std=c11" $(MAKE) allmost bmix64test: clean CFLAGS="-O3 -mbmi -Werror" $(MAKE) -C $(TESTDIR) test From 7cedbd1936dcc9ac4c761c30f01ebb3868b46321 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 22 Dec 2016 12:43:00 +0100 Subject: [PATCH 052/227] fixed allmost target --- Makefile | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 691d5d82b..19b12d0ef 100644 --- a/Makefile +++ b/Makefile @@ -30,11 +30,16 @@ all: allmost CPPFLAGS=-I../lib LDFLAGS=-L../lib $(MAKE) -C examples/ $@ .PHONY: allmost -allmost: # without examples - $(MAKE) -C $(ZSTDDIR) $@ - $(MAKE) -C $(PRGDIR) $@ zstd32 - $(MAKE) -C $(TESTDIR) $@ all32 - $(MAKE) -C $(ZWRAPDIR) $@ +allmost: + $(MAKE) -C $(ZSTDDIR) all + $(MAKE) -C $(PRGDIR) all + $(MAKE) -C $(TESTDIR) all + $(MAKE) -C $(ZWRAPDIR) all + +.PHONY: all32 +all32: + $(MAKE) -C $(PRGDIR) zstd32 + $(MAKE) -C $(TESTDIR) all32 .PHONY: lib lib: From 9ceb49e0975cc18c03c45190a397cd365b6ae579 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 22 Dec 2016 15:26:33 +0100 Subject: [PATCH 053/227] fixed zlib_wrapper conversion warnings --- zlibWrapper/examples/fitblk.c | 4 ++-- zlibWrapper/examples/zwrapbench.c | 16 ++++++++-------- zlibWrapper/gzread.c | 6 +++--- zlibWrapper/gzwrite.c | 8 ++++---- zlibWrapper/zstd_zlibwrapper.c | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/zlibWrapper/examples/fitblk.c b/zlibWrapper/examples/fitblk.c index f389c3a4f..ee413c3ae 100644 --- a/zlibWrapper/examples/fitblk.c +++ b/zlibWrapper/examples/fitblk.c @@ -82,7 +82,7 @@ local int partcompress(FILE *in, z_streamp def) flush = Z_SYNC_FLUSH; do { - def->avail_in = fread(raw, 1, RAWLEN, in); + def->avail_in = (uInt)fread(raw, 1, RAWLEN, in); if (ferror(in)) return Z_ERRNO; def->next_in = raw; @@ -148,7 +148,7 @@ int main(int argc, char **argv) /* get requested output size */ if (argc != 2) quit("need one argument: size of output block"); - ret = strtol(argv[1], argv + 1, 10); + ret = (int)strtol(argv[1], argv + 1, 10); if (argv[1][0] != 0) quit("argument must be a number"); if (ret < 8) /* 8 is minimum zlib stream size */ diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c index 49a2632e1..e5c54438b 100644 --- a/zlibWrapper/examples/zwrapbench.c +++ b/zlibWrapper/examples/zwrapbench.c @@ -315,10 +315,10 @@ static int BMK_benchMem(z_const void* srcBuffer, size_t srcSize, if (ZWRAP_isUsingZSTDcompression()) useSetDict = 0; /* zstd doesn't require deflateSetDictionary after ZWRAP_deflateReset_keepDict */ } def.next_in = (z_const void*) blockTable[blockNb].srcPtr; - def.avail_in = blockTable[blockNb].srcSize; + def.avail_in = (uInt)blockTable[blockNb].srcSize; def.total_in = 0; def.next_out = (void*) blockTable[blockNb].cPtr; - def.avail_out = blockTable[blockNb].cRoom; + def.avail_out = (uInt)blockTable[blockNb].cRoom; def.total_out = 0; ret = deflate(&def, Z_FINISH); if (ret != Z_STREAM_END) EXM_THROW(1, "deflate failure ret=%d srcSize=%d" , ret, (int)blockTable[blockNb].srcSize); @@ -346,10 +346,10 @@ static int BMK_benchMem(z_const void* srcBuffer, size_t srcSize, if (ret != Z_OK) EXM_THROW(1, "deflateSetDictionary failure"); } def.next_in = (z_const void*) blockTable[blockNb].srcPtr; - def.avail_in = blockTable[blockNb].srcSize; + def.avail_in = (uInt)blockTable[blockNb].srcSize; def.total_in = 0; def.next_out = (void*) blockTable[blockNb].cPtr; - def.avail_out = blockTable[blockNb].cRoom; + def.avail_out = (uInt)blockTable[blockNb].cRoom; def.total_out = 0; ret = deflate(&def, Z_FINISH); if (ret != Z_STREAM_END) EXM_THROW(1, "deflate failure"); @@ -451,10 +451,10 @@ static int BMK_benchMem(z_const void* srcBuffer, size_t srcSize, ret = inflateReset(&inf); if (ret != Z_OK) EXM_THROW(1, "inflateReset failure"); inf.next_in = (z_const void*) blockTable[blockNb].cPtr; - inf.avail_in = blockTable[blockNb].cSize; + inf.avail_in = (uInt)blockTable[blockNb].cSize; inf.total_in = 0; inf.next_out = (void*) blockTable[blockNb].resPtr; - inf.avail_out = blockTable[blockNb].srcSize; + inf.avail_out = (uInt)blockTable[blockNb].srcSize; inf.total_out = 0; ret = inflate(&inf, Z_FINISH); if (ret == Z_NEED_DICT) { @@ -483,10 +483,10 @@ static int BMK_benchMem(z_const void* srcBuffer, size_t srcSize, ret = inflateInit(&inf); if (ret != Z_OK) EXM_THROW(1, "inflateInit failure"); inf.next_in = (z_const void*) blockTable[blockNb].cPtr; - inf.avail_in = blockTable[blockNb].cSize; + inf.avail_in = (uInt)blockTable[blockNb].cSize; inf.total_in = 0; inf.next_out = (void*) blockTable[blockNb].resPtr; - inf.avail_out = blockTable[blockNb].srcSize; + inf.avail_out = (uInt)blockTable[blockNb].srcSize; inf.total_out = 0; ret = inflate(&inf, Z_FINISH); if (ret == Z_NEED_DICT) { diff --git a/zlibWrapper/gzread.c b/zlibWrapper/gzread.c index bf1a3cc6c..f251e2fe4 100644 --- a/zlibWrapper/gzread.c +++ b/zlibWrapper/gzread.c @@ -30,7 +30,7 @@ local int gz_load(state, buf, len, have) *have = 0; do { - ret = read(state.state->fd, buf + *have, len - *have); + ret = (int)read(state.state->fd, buf + *have, len - *have); if (ret <= 0) break; *have += ret; @@ -469,7 +469,7 @@ int ZEXPORT gzungetc(c, file) if (state.state->x.have == 0) { state.state->x.have = 1; state.state->x.next = state.state->out + (state.state->size << 1) - 1; - state.state->x.next[0] = c; + state.state->x.next[0] = (unsigned char)c; state.state->x.pos--; state.state->past = 0; return c; @@ -491,7 +491,7 @@ int ZEXPORT gzungetc(c, file) } state.state->x.have++; state.state->x.next--; - state.state->x.next[0] = c; + state.state->x.next[0] = (unsigned char)c; state.state->x.pos--; state.state->past = 0; return c; diff --git a/zlibWrapper/gzwrite.c b/zlibWrapper/gzwrite.c index 13c8fb264..6f3c9658b 100644 --- a/zlibWrapper/gzwrite.c +++ b/zlibWrapper/gzwrite.c @@ -84,7 +84,7 @@ local int gz_comp(state, flush) /* write directly if requested */ if (state.state->direct) { - got = write(state.state->fd, strm->next_in, strm->avail_in); + got = (int)write(state.state->fd, strm->next_in, strm->avail_in); if (got < 0 || (unsigned)got != strm->avail_in) { gz_error(state, Z_ERRNO, zstrerror()); return -1; @@ -101,7 +101,7 @@ local int gz_comp(state, flush) if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && (flush != Z_FINISH || ret == Z_STREAM_END))) { have = (unsigned)(strm->next_out - state.state->x.next); - if (have && ((got = write(state.state->fd, state.state->x.next, have)) < 0 || + if (have && ((got = (int)write(state.state->fd, state.state->x.next, have)) < 0 || (unsigned)got != have)) { gz_error(state, Z_ERRNO, zstrerror()); return -1; @@ -278,7 +278,7 @@ int ZEXPORT gzputc(file, c) strm->next_in = state.state->in; have = (unsigned)((strm->next_in + strm->avail_in) - state.state->in); if (have < state.state->size) { - state.state->in[have] = c; + state.state->in[have] = (unsigned char)c; strm->avail_in++; state.state->x.pos++; return c & 0xff; @@ -286,7 +286,7 @@ int ZEXPORT gzputc(file, c) } /* no room in buffer or not initialized, use gz_write() */ - buf[0] = c; + buf[0] = (unsigned char)c; if (gzwrite(file, buf, 1) != 1) return -1; return c & 0xff; diff --git a/zlibWrapper/zstd_zlibwrapper.c b/zlibWrapper/zstd_zlibwrapper.c index b25d92066..238510523 100644 --- a/zlibWrapper/zstd_zlibwrapper.c +++ b/zlibWrapper/zstd_zlibwrapper.c @@ -61,7 +61,7 @@ ZEXTERN const char * ZEXPORT z_zlibVersion OF((void)) { return zlibVersion(); } static void* ZWRAP_allocFunction(void* opaque, size_t size) { z_streamp strm = (z_streamp) opaque; - void* address = strm->zalloc(strm->opaque, 1, size); + void* address = strm->zalloc(strm->opaque, 1, (uInt)size); /* printf("ZWRAP alloc %p, %d \n", address, (int)size); */ return address; } From 5f5a902453c9e475746b1639733e51b99a0bd5a8 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Thu, 22 Dec 2016 18:05:07 +0100 Subject: [PATCH 054/227] "make test" is now compatible with Solaris --- tests/Makefile | 18 ++++++++++++------ tests/playTests.sh | 38 ++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index f1c196ba4..c080fe34a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -181,9 +181,9 @@ clean: #---------------------------------------------------------------------------------- -#make valgrindTest is validated only for Linux, OSX, kFreeBSD, Hurd and some BSD targets +#make valgrindTest is validated only for Linux, OSX, BSD, Hurd and Solaris targets #---------------------------------------------------------------------------------- -ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD DragonFly)) +ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS)) HOST_OS = POSIX valgrindTest: VALGRIND = valgrind --leak-check=full --error-exitcode=1 @@ -208,10 +208,16 @@ HOST_OS = MSYS endif -#------------------------------------------------------------------------ -#make tests validated only for MSYS, Linux, OSX, kFreeBSD and Hurd targets -#------------------------------------------------------------------------ +#----------------------------------------------------------------------------- +#make tests validated only for MSYS, Linux, OSX, BSD, Hurd and Solaris targets +#----------------------------------------------------------------------------- ifneq (,$(filter $(HOST_OS),MSYS POSIX)) + +DIFF:=diff +ifneq (,$(filter $(shell uname),SunOS)) +DIFF:=gdiff +endif + zstd-playTests: datagen file $(ZSTD) ZSTD="$(QEMU_SYS) $(ZSTD)" ./playTests.sh $(ZSTDRTTEST) @@ -239,7 +245,7 @@ test-gzstd: gzstd $(PRGDIR)/zstd -d README.md.gz -o README2.md $(PRGDIR)/zstd -d README.md.gz test-zstd-speed.py.gz $(PRGDIR)/zstd -d zstd_gz.zst gz_zstd.gz - diff -q zstd_gz gz_zstd + $(DIFF) -q zstd_gz gz_zstd echo Hello World ZSTD | $(PRGDIR)/zstd -c - >hello.zst echo Hello World GZIP | gzip -c - >hello.gz echo Hello World TEXT >hello.txt diff --git a/tests/playTests.sh b/tests/playTests.sh index 89ff45f3f..dfc90c338 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -7,17 +7,17 @@ die() { roundTripTest() { if [ -n "$3" ]; then - local c="$3" - local p="$2" + local_c="$3" + local_p="$2" else - local c="$2" + local_c="$2" fi rm -f tmp1 tmp2 - $ECHO "roundTripTest: ./datagen $1 $p | $ZSTD -v$c | $ZSTD -d" - ./datagen $1 $p | $MD5SUM > tmp1 - ./datagen $1 $p | $ZSTD --ultra -v$c | $ZSTD -d | $MD5SUM > tmp2 - diff -q tmp1 tmp2 + $ECHO "roundTripTest: ./datagen $1 $local_p | $ZSTD -v$local_c | $ZSTD -d" + ./datagen $1 $local_p | $MD5SUM > tmp1 + ./datagen $1 $local_p | $ZSTD --ultra -v$local_c | $ZSTD -d | $MD5SUM > tmp2 + $DIFF -q tmp1 tmp2 } isWindows=false @@ -37,6 +37,12 @@ case "$UNAME" in *) MD5SUM="md5sum" ;; esac +DIFF="diff" +case "$UNAME" in + SunOS) DIFF="gdiff" ;; +esac + + $ECHO "\nStarting playTests.sh isWindows=$isWindows ZSTD='$ZSTD'" [ -n "$ZSTD" ] || die "ZSTD variable must be defined!" @@ -141,7 +147,7 @@ rm ./*.tmp ./*.zstd $ECHO "frame concatenation tests completed" -if [ "$isWindows" = false ] ; then +if [ "$isWindows" = false ] && [ "$UNAME" != 'SunOS' ] ; then $ECHO "\n**** flush write error test **** " $ECHO "$ECHO foo | $ZSTD > /dev/full" @@ -155,14 +161,14 @@ $ECHO "\n**** test sparse file support **** " ./datagen -g5M -P100 > tmpSparse $ZSTD tmpSparse -c | $ZSTD -dv -o tmpSparseRegen -diff -s tmpSparse tmpSparseRegen +$DIFF -s tmpSparse tmpSparseRegen $ZSTD tmpSparse -c | $ZSTD -dv --sparse -c > tmpOutSparse -diff -s tmpSparse tmpOutSparse +$DIFF -s tmpSparse tmpOutSparse $ZSTD tmpSparse -c | $ZSTD -dv --no-sparse -c > tmpOutNoSparse -diff -s tmpSparse tmpOutNoSparse +$DIFF -s tmpSparse tmpOutNoSparse ls -ls tmpSparse* ./datagen -s1 -g1200007 -P100 | $ZSTD | $ZSTD -dv --sparse -c > tmpSparseOdd # Odd size file (to not finish on an exact nb of blocks) -./datagen -s1 -g1200007 -P100 | diff -s - tmpSparseOdd +./datagen -s1 -g1200007 -P100 | $DIFF -s - tmpSparseOdd ls -ls tmpSparseOdd $ECHO "\n Sparse Compatibility with Console :" $ECHO "Hello World 1 !" | $ZSTD | $ZSTD -d -c @@ -174,7 +180,7 @@ $ZSTD -v -f tmpSparse1M -o tmpSparseCompressed $ZSTD -d -v -f tmpSparseCompressed -o tmpSparseRegenerated $ZSTD -d -v -f tmpSparseCompressed -c >> tmpSparseRegenerated ls -ls tmpSparse* -diff tmpSparse2M tmpSparseRegenerated +$DIFF tmpSparse2M tmpSparseRegenerated rm tmpSparse* @@ -207,13 +213,13 @@ TESTFILE=../programs/zstdcli.c ./datagen > tmpDict ./datagen -g1M | $MD5SUM > tmp1 ./datagen -g1M | $ZSTD -D tmpDict | $ZSTD -D tmpDict -dvq | $MD5SUM > tmp2 -diff -q tmp1 tmp2 +$DIFF -q tmp1 tmp2 $ECHO "- Create first dictionary" $ZSTD --train *.c ../programs/*.c -o tmpDict cp $TESTFILE tmp $ZSTD -f tmp -D tmpDict $ZSTD -d tmp.zst -D tmpDict -fo result -diff $TESTFILE result +$DIFF $TESTFILE result $ECHO "- Create second (different) dictionary" $ZSTD --train *.c ../programs/*.c ../programs/*.h -o tmpDictC $ZSTD -d tmp.zst -D tmpDictC -fo result && die "wrong dictionary not detected!" @@ -229,7 +235,7 @@ $ZSTD --train *.c ../programs/*.c -o tmpDict2 --maxdict -v 4K && die "wrong orde $ECHO "- Compress without dictID" $ZSTD -f tmp -D tmpDict1 --no-dictID $ZSTD -d tmp.zst -D tmpDict -fo result -diff $TESTFILE result +$DIFF $TESTFILE result $ECHO "- Compress with wrong argument order (must fail)" $ZSTD tmp -Df tmpDict1 -c > /dev/null && die "-D must be followed by dictionary name " $ECHO "- Compress multiple files with dictionary" From aab442133db88038623ecb4bcbb364ec4b7c77c6 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Thu, 22 Dec 2016 19:26:01 +0100 Subject: [PATCH 055/227] Solaris: working "make -C programs install" --- programs/Makefile | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/programs/Makefile b/programs/Makefile index 2b89ddb57..71fe591b7 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -14,11 +14,6 @@ # zstd-decompress : decompressor-only version of zstd # ########################################################################## -DESTDIR?= -PREFIX ?= /usr/local -BINDIR = $(PREFIX)/bin -MANDIR = $(PREFIX)/share/man/man1 - ZSTDDIR = ../lib ifeq ($(shell $(CC) -v 2>&1 | grep -c "gcc version "), 1) @@ -153,17 +148,36 @@ clean_decomp_o: #---------------------------------------------------------------------------------- #make install is validated only for Linux, OSX, kFreeBSD, Hurd and some BSD targets #---------------------------------------------------------------------------------- -ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD DragonFly NetBSD)) +ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS)) + +DESTDIR?= +ifneq (,$(filter $(shell uname),SunOS)) +PREFIX ?= /usr +else +PREFIX ?= /usr/local +endif +BINDIR = $(PREFIX)/bin +MANDIR = $(PREFIX)/share/man/man1 + +INSTALL:=install +ifneq (,$(filter $(shell uname),SunOS)) +INSTALL:=ginstall +endif + +INSTALL_PROGRAM ?= $(INSTALL) -m 755 +INSTALL_SCRIPT ?= $(INSTALL) -m 755 +INSTALL_MAN ?= $(INSTALL) -m 644 + install: zstd @echo Installing binaries - @install -d -m 755 $(DESTDIR)$(BINDIR)/ $(DESTDIR)$(MANDIR)/ - @install -m 755 zstd $(DESTDIR)$(BINDIR)/zstd + @$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)/ $(DESTDIR)$(MANDIR)/ + @$(INSTALL_PROGRAM) zstd $(DESTDIR)$(BINDIR)/zstd @ln -sf zstd $(DESTDIR)$(BINDIR)/zstdcat @ln -sf zstd $(DESTDIR)$(BINDIR)/unzstd - @install -m 755 zstdless $(DESTDIR)$(BINDIR)/zstdless - @install -m 755 zstdgrep $(DESTDIR)$(BINDIR)/zstdgrep + @$(INSTALL_SCRIPT) zstdless $(DESTDIR)$(BINDIR)/zstdless + @$(INSTALL_SCRIPT) zstdgrep $(DESTDIR)$(BINDIR)/zstdgrep @echo Installing man pages - @install -m 644 zstd.1 $(DESTDIR)$(MANDIR)/zstd.1 + @$(INSTALL_MAN) zstd.1 $(DESTDIR)$(MANDIR)/zstd.1 @ln -sf zstd.1 $(DESTDIR)$(MANDIR)/zstdcat.1 @ln -sf zstd.1 $(DESTDIR)$(MANDIR)/unzstd.1 @echo zstd installation completed From b999170311cf0e60deb9087ea9ba6e771eadabaf Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Thu, 22 Dec 2016 20:14:37 +0100 Subject: [PATCH 056/227] Solaris: working "make -C lib install" --- lib/Makefile | 51 +++++++++++++++++++++++++++++++---------------- programs/Makefile | 9 +++------ 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index fcc0d099d..ba3997771 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -18,11 +18,6 @@ LIBVER_PATCH := $(shell echo $(LIBVER_PATCH_SCRIPT)) LIBVER := $(shell echo $(LIBVER_SCRIPT)) VERSION?= $(LIBVER) -DESTDIR?= -PREFIX ?= /usr/local -LIBDIR ?= $(PREFIX)/lib -INCLUDEDIR=$(PREFIX)/include - CPPFLAGS+= -I. -I./common -DXXH_NAMESPACE=ZSTD_ CFLAGS ?= -O3 CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ @@ -94,7 +89,27 @@ clean: #------------------------------------------------------------------------ #make install is validated only for Linux, OSX, kFreeBSD, Hurd and some BSD targets -ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU FreeBSD DragonFly NetBSD)) +ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS)) + +ifneq (,$(filter $(shell uname),SunOS)) +PREFIX ?= /usr +INSTALL ?= ginstall +else +PREFIX ?= /usr/local +INSTALL ?= install +endif +DESTDIR ?= +LIBDIR ?= $(PREFIX)/lib +INCLUDEDIR=$(PREFIX)/include + +ifneq (,$(filter $(shell uname),OpenBSD FreeBSD NetBSD DragonFly)) +PKGCONFIGDIR ?= $(PREFIX)/libdata/pkgconfig +else +PKGCONFIGDIR ?= $(LIBDIR)/pkgconfig +endif + +INSTALL_LIB ?= $(INSTALL) -m 755 +INSTALL_DATA ?= $(INSTALL) -m 644 libzstd.pc: libzstd.pc: libzstd.pc.in @@ -106,16 +121,18 @@ libzstd.pc: libzstd.pc.in $< >$@ install: libzstd.a libzstd libzstd.pc - @install -d -m 755 $(DESTDIR)$(LIBDIR)/pkgconfig/ $(DESTDIR)$(INCLUDEDIR)/ - @install -m 755 libzstd.$(SHARED_EXT_VER) $(DESTDIR)$(LIBDIR) - @cp -a libzstd.$(SHARED_EXT_MAJOR) $(DESTDIR)$(LIBDIR) - @cp -a libzstd.$(SHARED_EXT) $(DESTDIR)$(LIBDIR) - @cp -a libzstd.pc $(DESTDIR)$(LIBDIR)/pkgconfig/ - @install -m 644 libzstd.a $(DESTDIR)$(LIBDIR) - @install -m 644 zstd.h $(DESTDIR)$(INCLUDEDIR) - @install -m 644 common/zstd_errors.h $(DESTDIR)$(INCLUDEDIR) - @install -m 644 deprecated/zbuff.h $(DESTDIR)$(INCLUDEDIR) # prototypes generate deprecation warnings - @install -m 644 dictBuilder/zdict.h $(DESTDIR)$(INCLUDEDIR) + @$(INSTALL) -d -m 755 $(DESTDIR)$(PKGCONFIGDIR)/ $(DESTDIR)$(INCLUDEDIR)/ + @$(INSTALL_DATA) libzstd.pc $(DESTDIR)$(PKGCONFIGDIR)/ + @echo Installing libraries + @$(INSTALL_LIB) libzstd.a $(DESTDIR)$(LIBDIR) + @$(INSTALL_LIB) libzstd.$(SHARED_EXT_VER) $(DESTDIR)$(LIBDIR) + @ln -sf libzstd.$(SHARED_EXT_VER) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT_MAJOR) + @ln -sf libzstd.$(SHARED_EXT_VER) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT) + @echo Installing includes + @$(INSTALL_DATA) zstd.h $(DESTDIR)$(INCLUDEDIR) + @$(INSTALL_DATA) common/zstd_errors.h $(DESTDIR)$(INCLUDEDIR) + @$(INSTALL_DATA) deprecated/zbuff.h $(DESTDIR)$(INCLUDEDIR) # prototypes generate deprecation warnings + @$(INSTALL_DATA) dictBuilder/zdict.h $(DESTDIR)$(INCLUDEDIR) @echo zstd static and shared library installed uninstall: @@ -123,7 +140,7 @@ uninstall: @$(RM) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT) @$(RM) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT_MAJOR) @$(RM) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT_VER) - @$(RM) $(DESTDIR)$(LIBDIR)/pkgconfig/libzstd.pc + @$(RM) $(DESTDIR)$(PKGCONFIGDIR)/libzstd.pc @$(RM) $(DESTDIR)$(INCLUDEDIR)/zstd.h @$(RM) $(DESTDIR)$(INCLUDEDIR)/zstd_errors.h @$(RM) $(DESTDIR)$(INCLUDEDIR)/zbuff.h # Deprecated streaming functions diff --git a/programs/Makefile b/programs/Makefile index 71fe591b7..34b1be292 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -150,20 +150,17 @@ clean_decomp_o: #---------------------------------------------------------------------------------- ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS)) -DESTDIR?= ifneq (,$(filter $(shell uname),SunOS)) PREFIX ?= /usr +INSTALL ?= ginstall else PREFIX ?= /usr/local +INSTALL ?= install endif +DESTDIR ?= BINDIR = $(PREFIX)/bin MANDIR = $(PREFIX)/share/man/man1 -INSTALL:=install -ifneq (,$(filter $(shell uname),SunOS)) -INSTALL:=ginstall -endif - INSTALL_PROGRAM ?= $(INSTALL) -m 755 INSTALL_SCRIPT ?= $(INSTALL) -m 755 INSTALL_MAN ?= $(INSTALL) -m 644 From d76d1a9ef08e9baaec9beec40bd354b2b2e1e893 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 22 Dec 2016 20:18:43 +0100 Subject: [PATCH 057/227] added ZDICT_finalizeDictionary() --- NEWS | 3 ++- lib/dictBuilder/zdict.c | 49 +++++++++++++++++++++++++++++++++++ lib/dictBuilder/zdict.h | 57 +++++++++++++++++++++++++++++++++-------- 3 files changed, 97 insertions(+), 12 deletions(-) diff --git a/NEWS b/NEWS index 5e9271817..6d5dffc63 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,8 @@ v1.1.3 -cli : new : commands for advanced parameters, by Przemyslaw Skibinski +cli : new : advanced commands for detailed parameters, by Przemyslaw Skibinski API : fix : all symbols properly exposed in libzstd, by Nick Terrell API : new : ZSTD_create?Dict_byReference(), requested by Bartosz Taudul +API : new : ZDICT_finalizeDictionary() v1.1.2 API : streaming : decompression : changed : automatic implicit reset when chain-decoding new frames without init diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index ac22e8705..c3b227d74 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -824,6 +824,55 @@ _cleanup: } + +size_t ZDICT_finalizeDictionary(void* dictBuffer, size_t dictBufferCapacity, + const void* customDictContent, size_t dictContentSize, + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, + ZDICT_params_t params) +{ + size_t hSize; +#define HBUFFSIZE 256 + BYTE header[HBUFFSIZE]; + int const compressionLevel = (params.compressionLevel <= 0) ? g_compressionLevel_default : params.compressionLevel; + U32 const notificationLevel = params.notificationLevel; + + /* check conditions */ + if (dictBufferCapacity <= dictContentSize) return ERROR(dstSize_tooSmall); + if (dictContentSize < ZDICT_CONTENTSIZE_MIN) return ERROR(srcSize_wrong); + if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) return ERROR(dstSize_tooSmall); + + /* dictionary header */ + MEM_writeLE32(header, ZSTD_DICT_MAGIC); + { U64 const randomID = XXH64(customDictContent, dictContentSize, 0); + U32 const compliantID = (randomID % ((1U<<31)-32768)) + 32768; + U32 const dictID = params.dictID ? params.dictID : compliantID; + MEM_writeLE32(header+4, dictID); + } + hSize = 8; + + /* entropy tables */ + DISPLAYLEVEL(2, "\r%70s\r", ""); /* clean display line */ + DISPLAYLEVEL(2, "statistics ... \n"); + { size_t const eSize = ZDICT_analyzeEntropy(header+hSize, HBUFFSIZE-hSize, + compressionLevel, + samplesBuffer, samplesSizes, nbSamples, + customDictContent, dictContentSize, + notificationLevel); + if (ZDICT_isError(eSize)) return eSize; + hSize += eSize; + } + + /* copy elements in final buffer ; note : src and dst buffer can overlap */ + if (hSize + dictContentSize < dictBufferCapacity) dictContentSize = dictBufferCapacity - hSize; + { size_t const dictSize = hSize + dictContentSize; + char* dictEnd = (char*)dictBuffer + dictSize; + memmove(dictEnd - dictContentSize, customDictContent, dictContentSize); + memcpy(dictBuffer, header, hSize); + return dictSize; + } +} + + size_t ZDICT_addEntropyTablesFromBuffer_advanced(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_params_t params) diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index d6cf1839e..8dabfd5e5 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -87,22 +87,57 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_advanced(void* dictBuffer, size_t dict ZDICT_params_t parameters); -/*! ZDICT_addEntropyTablesFromBuffer() : +/*! ZDICT_finalizeDictionary() : + + Given a custom content as a basis for dictionary, and a set of samples, + finalize dictionary by adding headers and statistics. - Given a content-only dictionary (built using any 3rd party algorithm), - add entropy tables computed from an array of samples. Samples must be stored concatenated in a flat buffer `samplesBuffer`, supplied with an array of sizes `samplesSizes`, providing the size of each sample in order. - The input dictionary content must be stored *at the end* of `dictBuffer`. - Its size is `dictContentSize`. - The resulting dictionary with added entropy tables will be *written back to `dictBuffer`*, - starting from its beginning. - @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`). -*/ -ZDICTLIB_API size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, - const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); + dictContentSize must be > ZDICT_CONTENTSIZE_MIN bytes. + maxDictSize must be > dictContentSize, and must be > ZDICT_DICTSIZE_MIN bytes. + @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`), + or an error code, which can be tested by ZDICT_isError(). + note : ZDICT_finalizeDictionary() will push notifications into stderr if instructed to, using notificationLevel>0. + note 2 : dictBuffer and customDictContent can overlap +*/ +#define ZDICT_CONTENTSIZE_MIN 256 +#define ZDICT_DICTSIZE_MIN 512 +ZDICTLIB_API size_t ZDICT_finalizeDictionary(void* dictBuffer, size_t dictBufferCapacity, + const void* customDictContent, size_t dictContentSize, + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, + ZDICT_params_t parameters); + + + +/* Deprecation warnings */ +/* It is generally possible to disable deprecation warnings from compiler, + for example with -Wno-deprecated-declarations for gcc + or _CRT_SECURE_NO_WARNINGS in Visual. + Otherwise, it's also possible to manually define ZDICT_DISABLE_DEPRECATE_WARNINGS */ +#ifdef ZDICT_DISABLE_DEPRECATE_WARNINGS +# define ZDICT_DEPRECATED(message) /* disable deprecation warnings */ +#else +# define ZDICT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ +# define ZDICT_DEPRECATED(message) [[deprecated(message)]] +# elif (ZDICT_GCC_VERSION >= 405) || defined(__clang__) +# define ZDICT_DEPRECATED(message) __attribute__((deprecated(message))) +# elif (ZDICT_GCC_VERSION >= 301) +# define ZDICT_DEPRECATED(message) __attribute__((deprecated)) +# elif defined(_MSC_VER) +# define ZDICT_DEPRECATED(message) __declspec(deprecated(message)) +# else +# pragma message("WARNING: You need to implement ZDICT_DEPRECATED for this compiler") +# define ZDICT_DEPRECATED(message) +# endif +#endif /* ZDICT_DISABLE_DEPRECATE_WARNINGS */ + +ZDICT_DEPRECATED("use ZDICT_finalizeDictionary() instead") +size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); #endif /* ZDICT_STATIC_LINKING_ONLY */ From fce374a10015a88ac7fbbad647aa3fdd747d5558 Mon Sep 17 00:00:00 2001 From: Andrew Janke Date: Thu, 22 Dec 2016 17:40:10 -0500 Subject: [PATCH 058/227] zstdless: add shebang and quote $@ --- programs/zstdless | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programs/zstdless b/programs/zstdless index ab021405c..893799e7d 100755 --- a/programs/zstdless +++ b/programs/zstdless @@ -1 +1,2 @@ -zstdcat $@ | less +#!/bin/sh +zstdcat "$@" | less From 78a0072d5aae129c6f09d9fa31442c30bdefd5d9 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 22 Dec 2016 17:28:49 -0800 Subject: [PATCH 059/227] Fix failing test due to deprecation warning --- lib/dictBuilder/zdict.h | 12 ++++++------ tests/symbols.c | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index 8dabfd5e5..c7a0f575d 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -118,20 +118,20 @@ ZDICTLIB_API size_t ZDICT_finalizeDictionary(void* dictBuffer, size_t dictBuffer or _CRT_SECURE_NO_WARNINGS in Visual. Otherwise, it's also possible to manually define ZDICT_DISABLE_DEPRECATE_WARNINGS */ #ifdef ZDICT_DISABLE_DEPRECATE_WARNINGS -# define ZDICT_DEPRECATED(message) /* disable deprecation warnings */ +# define ZDICT_DEPRECATED(message) ZDICTLIB_API /* disable deprecation warnings */ #else # define ZDICT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ -# define ZDICT_DEPRECATED(message) [[deprecated(message)]] +# define ZDICT_DEPRECATED(message) ZDICTLIB_API [[deprecated(message)]] # elif (ZDICT_GCC_VERSION >= 405) || defined(__clang__) -# define ZDICT_DEPRECATED(message) __attribute__((deprecated(message))) +# define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated(message))) # elif (ZDICT_GCC_VERSION >= 301) -# define ZDICT_DEPRECATED(message) __attribute__((deprecated)) +# define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated)) # elif defined(_MSC_VER) -# define ZDICT_DEPRECATED(message) __declspec(deprecated(message)) +# define ZDICT_DEPRECATED(message) ZDICTLIB_API __declspec(deprecated(message)) # else # pragma message("WARNING: You need to implement ZDICT_DEPRECATED for this compiler") -# define ZDICT_DEPRECATED(message) +# define ZDICT_DEPRECATED(message) ZDICTLIB_API # endif #endif /* ZDICT_DISABLE_DEPRECATE_WARNINGS */ diff --git a/tests/symbols.c b/tests/symbols.c index 8d03df2fd..e007148f9 100644 --- a/tests/symbols.c +++ b/tests/symbols.c @@ -5,6 +5,7 @@ #define ZBUFF_DISABLE_DEPRECATE_WARNINGS #define ZBUFF_STATIC_LINKING_ONLY #include "zbuff.h" +#define ZDICT_DISABLE_DEPRECATE_WARNINGS #define ZDICT_STATIC_LINKING_ONLY #include "zdict.h" From bcbe77e9944694f71c8a54d687dea297cd3f7465 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 22 Dec 2016 18:01:14 -0800 Subject: [PATCH 060/227] ZDICT_finalizeDictionary() flipped comparison `ZDICT_finalizeDictionary()` had a flipped comparison. I also allowed `dictBufferCapacity == dictContentSize`. It might be the case that the user wants to fill the dictionary completely up, and then let zstd take exactly the space it needs for the entropy tables. --- lib/dictBuilder/zdict.c | 4 ++-- lib/dictBuilder/zdict.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index c3b227d74..c5cf6f801 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -837,7 +837,7 @@ size_t ZDICT_finalizeDictionary(void* dictBuffer, size_t dictBufferCapacity, U32 const notificationLevel = params.notificationLevel; /* check conditions */ - if (dictBufferCapacity <= dictContentSize) return ERROR(dstSize_tooSmall); + if (dictBufferCapacity < dictContentSize) return ERROR(dstSize_tooSmall); if (dictContentSize < ZDICT_CONTENTSIZE_MIN) return ERROR(srcSize_wrong); if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) return ERROR(dstSize_tooSmall); @@ -863,7 +863,7 @@ size_t ZDICT_finalizeDictionary(void* dictBuffer, size_t dictBufferCapacity, } /* copy elements in final buffer ; note : src and dst buffer can overlap */ - if (hSize + dictContentSize < dictBufferCapacity) dictContentSize = dictBufferCapacity - hSize; + if (hSize + dictContentSize > dictBufferCapacity) dictContentSize = dictBufferCapacity - hSize; { size_t const dictSize = hSize + dictContentSize; char* dictEnd = (char*)dictBuffer + dictSize; memmove(dictEnd - dictContentSize, customDictContent, dictContentSize); diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index 8dabfd5e5..0641e36f6 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -96,7 +96,7 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_advanced(void* dictBuffer, size_t dict supplied with an array of sizes `samplesSizes`, providing the size of each sample in order. dictContentSize must be > ZDICT_CONTENTSIZE_MIN bytes. - maxDictSize must be > dictContentSize, and must be > ZDICT_DICTSIZE_MIN bytes. + maxDictSize must be >= dictContentSize, and must be > ZDICT_DICTSIZE_MIN bytes. @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`), or an error code, which can be tested by ZDICT_isError(). From 63b0014b96a3a71c68fdc4b05c0b894d936587f3 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Fri, 23 Dec 2016 10:05:49 +0100 Subject: [PATCH 061/227] BSD: improved "make install" --- lib/Makefile | 23 +++++++++++++++-------- programs/Makefile | 23 ++++++++++++++--------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index ba3997771..cd87e7756 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -87,20 +87,26 @@ clean: @$(RM) decompress/*.o @echo Cleaning library completed -#------------------------------------------------------------------------ -#make install is validated only for Linux, OSX, kFreeBSD, Hurd and some BSD targets +#----------------------------------------------------------------------------- +# make install is validated only for Linux, OSX, BSD, Hurd and Solaris targets +#----------------------------------------------------------------------------- ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS)) ifneq (,$(filter $(shell uname),SunOS)) -PREFIX ?= /usr INSTALL ?= ginstall else -PREFIX ?= /usr/local INSTALL ?= install endif -DESTDIR ?= -LIBDIR ?= $(PREFIX)/lib -INCLUDEDIR=$(PREFIX)/include + +ifneq (,$(filter $(shell uname),OpenBSD FreeBSD NetBSD DragonFly SunOS)) +PREFIX ?= /usr +else +PREFIX ?= /usr/local +endif + +DESTDIR ?= +LIBDIR ?= $(PREFIX)/lib +INCLUDEDIR ?= $(PREFIX)/include ifneq (,$(filter $(shell uname),OpenBSD FreeBSD NetBSD DragonFly)) PKGCONFIGDIR ?= $(PREFIX)/libdata/pkgconfig @@ -108,9 +114,10 @@ else PKGCONFIGDIR ?= $(LIBDIR)/pkgconfig endif -INSTALL_LIB ?= $(INSTALL) -m 755 +INSTALL_LIB ?= $(INSTALL) -m 755 INSTALL_DATA ?= $(INSTALL) -m 644 + libzstd.pc: libzstd.pc: libzstd.pc.in @echo creating pkgconfig diff --git a/programs/Makefile b/programs/Makefile index 34b1be292..8ec9fc698 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -145,25 +145,30 @@ clean_decomp_o: @$(RM) $(ZSTDDECOMP_O) -#---------------------------------------------------------------------------------- -#make install is validated only for Linux, OSX, kFreeBSD, Hurd and some BSD targets -#---------------------------------------------------------------------------------- +#----------------------------------------------------------------------------- +# make install is validated only for Linux, OSX, BSD, Hurd and Solaris targets +#----------------------------------------------------------------------------- ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD NetBSD DragonFly SunOS)) ifneq (,$(filter $(shell uname),SunOS)) -PREFIX ?= /usr INSTALL ?= ginstall else -PREFIX ?= /usr/local INSTALL ?= install endif + +ifneq (,$(filter $(shell uname),OpenBSD FreeBSD NetBSD DragonFly SunOS)) +PREFIX ?= /usr +else +PREFIX ?= /usr/local +endif + DESTDIR ?= -BINDIR = $(PREFIX)/bin -MANDIR = $(PREFIX)/share/man/man1 +BINDIR ?= $(PREFIX)/bin +MANDIR ?= $(PREFIX)/share/man/man1 INSTALL_PROGRAM ?= $(INSTALL) -m 755 -INSTALL_SCRIPT ?= $(INSTALL) -m 755 -INSTALL_MAN ?= $(INSTALL) -m 644 +INSTALL_SCRIPT ?= $(INSTALL) -m 755 +INSTALL_MAN ?= $(INSTALL) -m 644 install: zstd @echo Installing binaries From aca113f4f58791ace39e1d9e733f78ed60449eb7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 23 Dec 2016 22:25:03 +0100 Subject: [PATCH 062/227] fixed ZSTD_sizeof_?Dict() --- NEWS | 1 + lib/compress/zstd_compress.c | 5 ++--- lib/decompress/zstd_decompress.c | 2 +- lib/dictBuilder/zdict.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 6d5dffc63..1b132ca94 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,6 @@ v1.1.3 cli : new : advanced commands for detailed parameters, by Przemyslaw Skibinski +cli : fix zstdless on Mac OS-X, by Andrew Janke API : fix : all symbols properly exposed in libzstd, by Nick Terrell API : new : ZSTD_create?Dict_byReference(), requested by Bartosz Taudul API : new : ZDICT_finalizeDictionary() diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index afac869c8..7626b33a6 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -51,8 +51,7 @@ static void ZSTD_resetSeqStore(seqStore_t* ssPtr) /*-************************************* * Context memory management ***************************************/ -struct ZSTD_CCtx_s -{ +struct ZSTD_CCtx_s { const BYTE* nextSrc; /* next block here to continue on current prefix */ const BYTE* base; /* All regular indexes relative to this position */ const BYTE* dictBase; /* extDict indexes relative to this position */ @@ -2742,7 +2741,7 @@ struct ZSTD_CDict_s { size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict) { if (cdict==NULL) return 0; /* support sizeof on NULL */ - return ZSTD_sizeof_CCtx(cdict->refContext) + cdict->dictContentSize; + return ZSTD_sizeof_CCtx(cdict->refContext) + (cdict->dictBuffer ? cdict->dictContentSize : 0) + sizeof(*cdict); } ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize, unsigned byReference, diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index e976cd26d..02f3bf455 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1792,7 +1792,7 @@ size_t ZSTD_freeDDict(ZSTD_DDict* ddict) size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict) { if (ddict==NULL) return 0; /* support sizeof on NULL */ - return sizeof(*ddict) + sizeof(ddict->refContext) + ddict->dictSize; + return sizeof(*ddict) + ZSTD_sizeof_DCtx(ddict->refContext) + (ddict->dictBuffer ? ddict->dictSize : 0) ; } /*! ZSTD_getDictID_fromDict() : diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index c5cf6f801..0757dbbbb 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -60,7 +60,7 @@ #define NOISELENGTH 32 #define MINRATIO 4 -static const int g_compressionLevel_default = 5; +static const int g_compressionLevel_default = 6; static const U32 g_selectivity_default = 9; static const size_t g_provision_entropySize = 200; static const size_t g_min_fast_dictContent = 192; From 37a2fb4ce128f0faffbbddf9b392e20a1b1b7226 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Mon, 26 Dec 2016 23:04:59 +0800 Subject: [PATCH 063/227] Move -std=c++11 cxxflag to PZSTD_CXXFLAGS Fixes the problem that the compiler doesn't enable c++11 mode by default and the package build system has its own CXXFLAGS --- contrib/pzstd/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile index 99d955e94..f148bfd8e 100644 --- a/contrib/pzstd/Makefile +++ b/contrib/pzstd/Makefile @@ -26,7 +26,7 @@ POSTCOMPILE = mv -f $*.Td $*.d # CFLAGS, CXXFLAGS, CPPFLAGS, and LDFLAGS are for the users to override CFLAGS ?= -O3 -Wall -Wextra -CXXFLAGS ?= -O3 -Wall -Wextra -pedantic -std=c++11 +CXXFLAGS ?= -O3 -Wall -Wextra -pedantic CPPFLAGS ?= LDFLAGS ?= @@ -37,7 +37,7 @@ GTEST_INC = -isystem googletest/googletest/include PZSTD_CPPFLAGS = $(PZSTD_INC) PZSTD_CCXXFLAGS = PZSTD_CFLAGS = $(PZSTD_CCXXFLAGS) -PZSTD_CXXFLAGS = $(PZSTD_CCXXFLAGS) +PZSTD_CXXFLAGS = $(PZSTD_CCXXFLAGS) -std=c++11 PZSTD_LDFLAGS = EXTRA_FLAGS = ALL_CFLAGS = $(EXTRA_FLAGS) $(CPPFLAGS) $(PZSTD_CPPFLAGS) $(CFLAGS) $(PZSTD_CFLAGS) From 3d93f2fce75bb33374dece67cf16e2cffac846cb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 27 Dec 2016 07:19:36 +0100 Subject: [PATCH 064/227] first zstdmt sketch --- lib/compress/zstdmt_compress.c | 310 +++++++++++++++++++++++++++++++++ lib/compress/zstdmt_compress.h | 12 ++ programs/Makefile | 2 +- programs/bench.c | 14 +- 4 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 lib/compress/zstdmt_compress.c create mode 100644 lib/compress/zstdmt_compress.h diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c new file mode 100644 index 000000000..13cc19488 --- /dev/null +++ b/lib/compress/zstdmt_compress.c @@ -0,0 +1,310 @@ +#include /* malloc */ +#include +#include "zstd_internal.h" /* MIN, ERROR */ +#include "zstdmt_compress.h" + +#if 0 +# include + static unsigned g_debugLevel = 4; +# define DEBUGLOG(l, ...) if (l<=g_debugLevel) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, " \n"); } +#else +# define DEBUGLOG(l, ...) /* disabled */ +#endif + +#define ZSTDMT_NBTHREADS_MAX 128 +#define ZSTDMT_NBSTACKEDFRAMES_MAX (2*ZSTDMT_NBTHREADS_MAX) + +typedef struct frameToWrite_s { + const void* start; + size_t frameSize; + unsigned frameID; + unsigned isLastFrame; +} frameToWrite_t; + +typedef struct ZSTDMT_dstBuffer_s { + ZSTD_outBuffer out; + unsigned frameIDToWrite; + pthread_mutex_t frameTable_mutex; + pthread_mutex_t allFramesWritten_mutex; + frameToWrite_t stackedFrame[ZSTDMT_NBSTACKEDFRAMES_MAX]; + unsigned nbStackedFrames; +} ZSTDMT_dstBufferManager; + +static ZSTDMT_dstBufferManager ZSTDMT_createDstBufferManager(void* dst, size_t dstCapacity) +{ + ZSTDMT_dstBufferManager dbm; + dbm.out.dst = dst; + dbm.out.size = dstCapacity; + dbm.out.pos = 0; + dbm.frameIDToWrite = 0; + pthread_mutex_init(&dbm.frameTable_mutex, NULL); + pthread_mutex_init(&dbm.allFramesWritten_mutex, NULL); + pthread_mutex_lock(&dbm.allFramesWritten_mutex); + dbm.nbStackedFrames = 0; + return dbm; +} + +/* note : can fail if nbStackedFrames > ZSTDMT_NBSTACKEDFRAMES_MAX. + * note2 : can only be called from a section with frameTable_mutex already locked */ +static void ZSTDMT_stackFrameToWrite(ZSTDMT_dstBufferManager* dstBufferManager, frameToWrite_t frame) { + dstBufferManager->stackedFrame[dstBufferManager->nbStackedFrames++] = frame; +} + + +typedef struct buffer_s { + void* start; + size_t bufferSize; +} buffer_t; + +static buffer_t ZSTDMT_getDstBuffer(const ZSTDMT_dstBufferManager* dstBufferManager) +{ + ZSTD_outBuffer const out = dstBufferManager->out; + buffer_t buf; + buf.start = (char*)(out.dst) + out.pos; + buf.bufferSize = out.size - out.pos; + return buf; +} + +/* condition : stackNumber < dstBufferManager->nbStackedFrames. + * note : there can only be one write at a time, due to frameID condition */ +static size_t ZSTDMT_writeFrame(ZSTDMT_dstBufferManager* dstBufferManager, unsigned stackNumber) +{ + ZSTD_outBuffer const out = dstBufferManager->out; + size_t const frameSize = dstBufferManager->stackedFrame[stackNumber].frameSize; + const void* const frameStart = dstBufferManager->stackedFrame[stackNumber].start; + if (out.pos + frameSize > out.size) + return ERROR(dstSize_tooSmall); + DEBUGLOG(3, "writing frame %u (%u bytes) ", dstBufferManager->stackedFrame[stackNumber].frameID, (U32)frameSize); + memcpy((char*)out.dst + out.pos, frameStart, frameSize); + dstBufferManager->out.pos += frameSize; + dstBufferManager->frameIDToWrite = dstBufferManager->stackedFrame[stackNumber].frameID + 1; + return 0; +} + +static size_t ZSTDMT_tryWriteFrame(ZSTDMT_dstBufferManager* dstBufferManager, + const void* src, size_t srcSize, + unsigned frameID, unsigned isLastFrame) +{ + unsigned lastFrameWritten = 0; + + /* check if correct frame ordering; stack otherwise */ + DEBUGLOG(5, "considering writing frame %u ", frameID); + pthread_mutex_lock(&dstBufferManager->frameTable_mutex); + if (frameID != dstBufferManager->frameIDToWrite) { + DEBUGLOG(4, "writing frameID %u : not possible, waiting for %u ", frameID, dstBufferManager->frameIDToWrite); + frameToWrite_t frame = { src, srcSize, frameID, isLastFrame }; + ZSTDMT_stackFrameToWrite(dstBufferManager, frame); + pthread_mutex_unlock(&dstBufferManager->frameTable_mutex); + return 0; + } + pthread_mutex_unlock(&dstBufferManager->frameTable_mutex); + + /* write frame + * note : only one write possible due to frameID condition */ + DEBUGLOG(3, "writing frame %u (%u bytes) ", frameID, (U32)srcSize); + ZSTD_outBuffer const out = dstBufferManager->out; + if (out.pos + srcSize > out.size) + return ERROR(dstSize_tooSmall); + if (frameID) /* frameID==0 compress directly in dst buffer */ + memcpy((char*)out.dst + out.pos, src, srcSize); + dstBufferManager->out.pos += srcSize; + dstBufferManager->frameIDToWrite = frameID+1; + lastFrameWritten = isLastFrame; + + /* check if more frames are stacked */ + pthread_mutex_lock(&dstBufferManager->frameTable_mutex); + unsigned frameWritten = dstBufferManager->nbStackedFrames>0; + while (frameWritten) { + unsigned u; + frameID++; + frameWritten = 0; + for (u=0; unbStackedFrames; u++) { + if (dstBufferManager->stackedFrame[u].frameID == frameID) { + pthread_mutex_unlock(&dstBufferManager->frameTable_mutex); + { size_t const writeError = ZSTDMT_writeFrame(dstBufferManager, u); + if (ZSTD_isError(writeError)) return writeError; } + lastFrameWritten = dstBufferManager->stackedFrame[u].isLastFrame; + /* remove frame from stack */ + pthread_mutex_lock(&dstBufferManager->frameTable_mutex); + dstBufferManager->stackedFrame[u] = dstBufferManager->stackedFrame[dstBufferManager->nbStackedFrames-1]; + dstBufferManager->nbStackedFrames -= 1; + frameWritten = dstBufferManager->nbStackedFrames>0; + break; + } } } + pthread_mutex_unlock(&dstBufferManager->frameTable_mutex); + + /* end reached : last frame written */ + if (lastFrameWritten) pthread_mutex_unlock(&dstBufferManager->allFramesWritten_mutex); + return 0; +} + + + +typedef struct ZSTDMT_jobDescription_s { + const void* src; /* NULL means : kill thread */ + size_t srcSize; + int compressionLevel; + ZSTDMT_dstBufferManager* dstManager; + unsigned frameNumber; + unsigned isLastFrame; +} ZSTDMT_jobDescription; + +typedef struct ZSTDMT_jobAgency_s { + pthread_mutex_t jobAnnounce_mutex; + pthread_mutex_t jobApply_mutex; + ZSTDMT_jobDescription jobAnnounce; +} ZSTDMT_jobAgency; + +/* ZSTDMT_postjob() : + * This function is blocking as long as previous posted job is not taken. + * It could be made non-blocking, with a storage queue. + * But blocking has benefits : on top of memory savings, + * the caller will be able to measure delay, allowing dynamic speed throttle (via compression level). + */ +static void ZSTDMT_postjob(ZSTDMT_jobAgency* jobAgency, ZSTDMT_jobDescription job) +{ + DEBUGLOG(5, "starting job posting "); + pthread_mutex_lock(&jobAgency->jobApply_mutex); /* wait for a thread to take previous job */ + DEBUGLOG(5, "job posting mutex acquired "); + jobAgency->jobAnnounce = job; /* post job */ + pthread_mutex_unlock(&jobAgency->jobAnnounce_mutex); /* announce */ + DEBUGLOG(5, "job available now "); +} + +static ZSTDMT_jobDescription ZSTDMT_getjob(ZSTDMT_jobAgency* jobAgency) +{ + pthread_mutex_lock(&jobAgency->jobAnnounce_mutex); /* should check return code */ + ZSTDMT_jobDescription const job = jobAgency->jobAnnounce; + pthread_mutex_unlock(&jobAgency->jobApply_mutex); + return job; +} + + + +#define ZSTDMT_NBBUFFERSPOOLED_MAX ZSTDMT_NBTHREADS_MAX +typedef struct ZSTDMT_bufferPool_s { + buffer_t bTable[ZSTDMT_NBBUFFERSPOOLED_MAX]; + unsigned nbBuffers; +} ZSTDMT_bufferPool; + +static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) +{ + if (pool->nbBuffers) { /* try to use an existing buffer */ + pool->nbBuffers--; + buffer_t const buf = pool->bTable[pool->nbBuffers]; + size_t const availBufferSize = buf.bufferSize; + if ((availBufferSize >= bSize) & (availBufferSize <= 10*bSize)) /* large enough, but not too much */ + return buf; + free(buf.start); /* size conditions not respected : create a new buffer */ + } + /* create new buffer */ + buffer_t buf; + buf.bufferSize = bSize; + buf.start = calloc(1, bSize); + return buf; +} + +/* effectively store buffer for later re-use, up to pool capacity */ +static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf) +{ + if (pool->nbBuffers >= ZSTDMT_NBBUFFERSPOOLED_MAX) { + free(buf.start); + return; + } + pool->bTable[pool->nbBuffers++] = buf; /* store for later re-use */ +} + + + +struct ZSTDMT_CCtx_s { + pthread_t pthread[ZSTDMT_NBTHREADS_MAX]; + unsigned nbThreads; + ZSTDMT_jobAgency jobAgency; + ZSTDMT_bufferPool bufferPool; +}; + +static void* ZSTDMT_compressionThread(void* arg) +{ + if (arg==NULL) return NULL; /* error : should not be possible */ + ZSTDMT_CCtx* const cctx = (ZSTDMT_CCtx*) arg; + ZSTDMT_jobAgency* const jobAgency = &cctx->jobAgency; + ZSTDMT_bufferPool* const pool = &cctx->bufferPool; + for (;;) { + ZSTDMT_jobDescription const job = ZSTDMT_getjob(jobAgency); + if (job.src == NULL) { + DEBUGLOG(4, "thread exit ") + return NULL; + } + ZSTDMT_dstBufferManager* dstBufferManager = job.dstManager; + size_t const dstBufferCapacity = ZSTD_compressBound(job.srcSize); + DEBUGLOG(4, "requesting a dstBuffer for frame %u", job.frameNumber); + buffer_t const dstBuffer = job.frameNumber ? ZSTDMT_getBuffer(pool, dstBufferCapacity) : ZSTDMT_getDstBuffer(dstBufferManager); /* lack params */ + DEBUGLOG(4, "start compressing frame %u", job.frameNumber); + size_t const cSize = ZSTD_compress(dstBuffer.start, dstBuffer.bufferSize, job.src, job.srcSize, job.compressionLevel); + if (ZSTD_isError(cSize)) return (void*)(cSize); /* error */ + size_t const writeError = ZSTDMT_tryWriteFrame(dstBufferManager, dstBuffer.start, cSize, job.frameNumber, job.isLastFrame); /* pas clair */ + if (ZSTD_isError(writeError)) return (void*)writeError; + if (job.frameNumber) ZSTDMT_releaseBuffer(pool, dstBuffer); + } +} + +ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) +{ + if ((nbThreads < 1) | (nbThreads > ZSTDMT_NBTHREADS_MAX)) return NULL; + ZSTDMT_CCtx* const cctx = (ZSTDMT_CCtx*) calloc(1, sizeof(ZSTDMT_CCtx)); + if (!cctx) return NULL; + pthread_mutex_init(&cctx->jobAgency.jobAnnounce_mutex, NULL); /* check return value ? */ + pthread_mutex_init(&cctx->jobAgency.jobApply_mutex, NULL); + pthread_mutex_lock(&cctx->jobAgency.jobAnnounce_mutex); /* no job at beginning */ + /* start all workers */ + cctx->nbThreads = nbThreads; + DEBUGLOG(2, "nbThreads : %u \n", nbThreads); + unsigned t; + for (t = 0; t < nbThreads; t++) { + pthread_create(&cctx->pthread[t], NULL, ZSTDMT_compressionThread, cctx); /* check return value ? */ + } + return cctx; +} + +size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* cctx) +{ + /* free threads */ + /* free mutex (if necessary) */ + /* free bufferPool */ + free(cctx); /* incompleted ! */ + return 0; +} + +size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + int compressionLevel) +{ + ZSTDMT_jobAgency* jobAgency = &cctx->jobAgency; + ZSTD_parameters const params = ZSTD_getParams(compressionLevel, srcSize, 0); + size_t const frameSizeTarget = (size_t)1 << (params.cParams.windowLog + 2); + unsigned const nbFrames = (unsigned)(srcSize / frameSizeTarget) + (srcSize < frameSizeTarget) /* min 1 */; + size_t const avgFrameSize = (srcSize + (nbFrames-1)) / nbFrames; + size_t remainingSrcSize = srcSize; + const char* const srcStart = (const char*)src; + size_t frameStartPos = 0; + ZSTDMT_dstBufferManager dbm = ZSTDMT_createDstBufferManager(dst, dstCapacity); + + DEBUGLOG(2, "windowLog : %u => frameSizeTarget : %u ", params.cParams.windowLog, (U32)frameSizeTarget); + DEBUGLOG(2, "nbFrames : %u (size : %u bytes) ", nbFrames, (U32)avgFrameSize); + + { unsigned u; + for (u=0; u /* size_t */ + +typedef struct ZSTDMT_CCtx_s ZSTDMT_CCtx; + +ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads); +size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* cctx); + +size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + int compressionLevel); diff --git a/programs/Makefile b/programs/Makefile index 8ec9fc698..156bf8980 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -32,7 +32,7 @@ FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c -ZSTDCOMP_FILES := $(ZSTDDIR)/compress/zstd_compress.c $(ZSTDDIR)/compress/fse_compress.c $(ZSTDDIR)/compress/huf_compress.c +ZSTDCOMP_FILES := $(ZSTDDIR)/compress/*.c ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/huf_decompress.c ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) ZDICT_FILES := $(ZSTDDIR)/dictBuilder/*.c diff --git a/programs/bench.c b/programs/bench.c index 9a4732a31..4059072f0 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -115,6 +115,7 @@ void BMK_SetBlockSize(size_t blockSize) void BMK_setDecodeOnly(unsigned decodeFlag) { g_decodeOnly = (decodeFlag>0); } + /* ******************************************************** * Bench functions **********************************************************/ @@ -132,6 +133,8 @@ typedef struct { #define MIN(a,b) ((a)<(b) ? (a) : (b)) #define MAX(a,b) ((a)>(b) ? (a) : (b)) +#include "compress/zstdmt_compress.h" + static int BMK_benchMem(const void* srcBuffer, size_t srcSize, const char* displayName, int cLevel, const size_t* fileSizes, U32 nbFiles, @@ -153,6 +156,8 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, U32 nbBlocks; UTIL_time_t ticksPerSecond; + ZSTDMT_CCtx* const mtcctx = ZSTDMT_createCCtx(1); + /* checks */ if (!compressedBuffer || !resultBuffer || !blockTable || !ctx || !dctx) EXM_THROW(31, "allocation error : not enough memory"); @@ -264,6 +269,11 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, cdict); + } else if (1) { + rSize = ZSTDMT_compressCCtx(mtcctx, + blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, + blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, + cLevel); } else { rSize = ZSTD_compress_advanced (ctx, blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, @@ -292,8 +302,10 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, memcpy(compressedBuffer, srcBuffer, loadedCompressedSize); } - (void)fastestD; (void)crcOrig; /* unused when decompression disabled */ #if 1 + dCompleted=1; + (void)totalDTime; (void)fastestD; (void)crcOrig; /* unused when decompression disabled */ +#else /* Decompression */ if (!dCompleted) memset(resultBuffer, 0xD6, srcSize); /* warm result buffer */ From 75f3a3a335c0833ac99b873c4b5e93984e4dcd26 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 28 Dec 2016 12:32:41 +0100 Subject: [PATCH 065/227] changed default PREFIX and MANDIR --- lib/Makefile | 7 +------ programs/Makefile | 10 +++++----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index cd87e7756..efd3b87fe 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -98,12 +98,7 @@ else INSTALL ?= install endif -ifneq (,$(filter $(shell uname),OpenBSD FreeBSD NetBSD DragonFly SunOS)) -PREFIX ?= /usr -else -PREFIX ?= /usr/local -endif - +PREFIX ?= /usr/local DESTDIR ?= LIBDIR ?= $(PREFIX)/lib INCLUDEDIR ?= $(PREFIX)/include diff --git a/programs/Makefile b/programs/Makefile index 8ec9fc698..15ae01096 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -156,15 +156,15 @@ else INSTALL ?= install endif -ifneq (,$(filter $(shell uname),OpenBSD FreeBSD NetBSD DragonFly SunOS)) -PREFIX ?= /usr -else PREFIX ?= /usr/local -endif - DESTDIR ?= BINDIR ?= $(PREFIX)/bin + +ifneq (,$(filter $(shell uname),OpenBSD FreeBSD NetBSD DragonFly SunOS)) +MANDIR ?= $(PREFIX)/man/man1 +else MANDIR ?= $(PREFIX)/share/man/man1 +endif INSTALL_PROGRAM ?= $(INSTALL) -m 755 INSTALL_SCRIPT ?= $(INSTALL) -m 755 From ce9e1452fd0ab175d0f2c8b37639c05978b08fdd Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 28 Dec 2016 15:31:19 +0100 Subject: [PATCH 066/227] protect buffer pool with a mutex --- lib/compress/zstdmt_compress.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 13cc19488..c698dce0e 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1,5 +1,5 @@ #include /* malloc */ -#include +#include /* posix only, to be replaced by a more portable version */ #include "zstd_internal.h" /* MIN, ERROR */ #include "zstdmt_compress.h" @@ -39,7 +39,7 @@ static ZSTDMT_dstBufferManager ZSTDMT_createDstBufferManager(void* dst, size_t d dbm.frameIDToWrite = 0; pthread_mutex_init(&dbm.frameTable_mutex, NULL); pthread_mutex_init(&dbm.allFramesWritten_mutex, NULL); - pthread_mutex_lock(&dbm.allFramesWritten_mutex); + pthread_mutex_lock(&dbm.allFramesWritten_mutex); /* maybe could be merged into init ? */ dbm.nbStackedFrames = 0; return dbm; } @@ -92,7 +92,7 @@ static size_t ZSTDMT_tryWriteFrame(ZSTDMT_dstBufferManager* dstBufferManager, pthread_mutex_lock(&dstBufferManager->frameTable_mutex); if (frameID != dstBufferManager->frameIDToWrite) { DEBUGLOG(4, "writing frameID %u : not possible, waiting for %u ", frameID, dstBufferManager->frameIDToWrite); - frameToWrite_t frame = { src, srcSize, frameID, isLastFrame }; + frameToWrite_t const frame = { src, srcSize, frameID, isLastFrame }; ZSTDMT_stackFrameToWrite(dstBufferManager, frame); pthread_mutex_unlock(&dstBufferManager->frameTable_mutex); return 0; @@ -121,9 +121,11 @@ static size_t ZSTDMT_tryWriteFrame(ZSTDMT_dstBufferManager* dstBufferManager, for (u=0; unbStackedFrames; u++) { if (dstBufferManager->stackedFrame[u].frameID == frameID) { pthread_mutex_unlock(&dstBufferManager->frameTable_mutex); + DEBUGLOG(4, "catch up frame %u ", frameID); { size_t const writeError = ZSTDMT_writeFrame(dstBufferManager, u); if (ZSTD_isError(writeError)) return writeError; } lastFrameWritten = dstBufferManager->stackedFrame[u].isLastFrame; + dstBufferManager->frameIDToWrite = frameID+1; /* remove frame from stack */ pthread_mutex_lock(&dstBufferManager->frameTable_mutex); dstBufferManager->stackedFrame[u] = dstBufferManager->stackedFrame[dstBufferManager->nbStackedFrames-1]; @@ -183,20 +185,24 @@ static ZSTDMT_jobDescription ZSTDMT_getjob(ZSTDMT_jobAgency* jobAgency) #define ZSTDMT_NBBUFFERSPOOLED_MAX ZSTDMT_NBTHREADS_MAX typedef struct ZSTDMT_bufferPool_s { + pthread_mutex_t bufferPool_mutex; buffer_t bTable[ZSTDMT_NBBUFFERSPOOLED_MAX]; unsigned nbBuffers; } ZSTDMT_bufferPool; static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) { + pthread_mutex_lock(&pool->bufferPool_mutex); if (pool->nbBuffers) { /* try to use an existing buffer */ pool->nbBuffers--; buffer_t const buf = pool->bTable[pool->nbBuffers]; + pthread_mutex_unlock(&pool->bufferPool_mutex); size_t const availBufferSize = buf.bufferSize; if ((availBufferSize >= bSize) & (availBufferSize <= 10*bSize)) /* large enough, but not too much */ return buf; free(buf.start); /* size conditions not respected : create a new buffer */ } + pthread_mutex_unlock(&pool->bufferPool_mutex); /* create new buffer */ buffer_t buf; buf.bufferSize = bSize; @@ -207,11 +213,14 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) /* effectively store buffer for later re-use, up to pool capacity */ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf) { + pthread_mutex_lock(&pool->bufferPool_mutex); if (pool->nbBuffers >= ZSTDMT_NBBUFFERSPOOLED_MAX) { + pthread_mutex_unlock(&pool->bufferPool_mutex); free(buf.start); return; } pool->bTable[pool->nbBuffers++] = buf; /* store for later re-use */ + pthread_mutex_unlock(&pool->bufferPool_mutex); } @@ -253,9 +262,12 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) if ((nbThreads < 1) | (nbThreads > ZSTDMT_NBTHREADS_MAX)) return NULL; ZSTDMT_CCtx* const cctx = (ZSTDMT_CCtx*) calloc(1, sizeof(ZSTDMT_CCtx)); if (!cctx) return NULL; + /* init jobAgency */ pthread_mutex_init(&cctx->jobAgency.jobAnnounce_mutex, NULL); /* check return value ? */ pthread_mutex_init(&cctx->jobAgency.jobApply_mutex, NULL); pthread_mutex_lock(&cctx->jobAgency.jobAnnounce_mutex); /* no job at beginning */ + /* init bufferPool */ + pthread_mutex_init(&cctx->bufferPool.bufferPool_mutex, NULL); /* start all workers */ cctx->nbThreads = nbThreads; DEBUGLOG(2, "nbThreads : %u \n", nbThreads); From ab7a579180bb26b51358bfc9acddc561732d16f4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 28 Dec 2016 16:11:09 +0100 Subject: [PATCH 067/227] added -T command , to set nb of threads --- programs/bench.c | 7 +++++-- programs/bench.h | 7 ++++--- programs/zstdcli.c | 8 +++++++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 4059072f0..6009ebc7b 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -96,6 +96,7 @@ static U32 g_nbSeconds = NBSECONDS; static size_t g_blockSize = 0; static int g_additionalParam = 0; static U32 g_decodeOnly = 0; +static U32 g_nbThreads = 1; void BMK_setNotificationLevel(unsigned level) { g_displayLevel=level; } @@ -113,7 +114,9 @@ void BMK_SetBlockSize(size_t blockSize) DISPLAYLEVEL(2, "using blocks of size %u KB \n", (U32)(blockSize>>10)); } -void BMK_setDecodeOnly(unsigned decodeFlag) { g_decodeOnly = (decodeFlag>0); } +void BMK_setDecodeOnlyMode(unsigned decodeFlag) { g_decodeOnly = (decodeFlag>0); } + +void BMK_SetNbThreads(unsigned nbThreads) { g_nbThreads = nbThreads; } /* ******************************************************** @@ -156,7 +159,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, U32 nbBlocks; UTIL_time_t ticksPerSecond; - ZSTDMT_CCtx* const mtcctx = ZSTDMT_createCCtx(1); + ZSTDMT_CCtx* const mtcctx = ZSTDMT_createCCtx(g_nbThreads); /* checks */ if (!compressedBuffer || !resultBuffer || !blockTable || !ctx || !dctx) diff --git a/programs/bench.h b/programs/bench.h index 314f34655..87850bcc3 100644 --- a/programs/bench.h +++ b/programs/bench.h @@ -15,14 +15,15 @@ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressionParameters */ #include "zstd.h" /* ZSTD_compressionParameters */ -int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles,const char* dictFileName, +int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles,const char* dictFileName, int cLevel, int cLevelLast, ZSTD_compressionParameters* compressionParams); /* Set Parameters */ void BMK_SetNbSeconds(unsigned nbLoops); void BMK_SetBlockSize(size_t blockSize); -void BMK_setAdditionalParam(int additionalParam); +void BMK_SetNbThreads(unsigned nbThreads); void BMK_setNotificationLevel(unsigned level); -void BMK_setDecodeOnly(unsigned decodeFlag); +void BMK_setAdditionalParam(int additionalParam); +void BMK_setDecodeOnlyMode(unsigned decodeFlag); #endif /* BENCH_H_121279284357 */ diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 978ffcfe0..03ad1ac7e 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -352,7 +352,7 @@ int main(int argCount, const char* argv[]) /* Decoding */ case 'd': #ifndef ZSTD_NOBENCH - if (operation==zom_bench) { BMK_setDecodeOnly(1); argument++; break; } /* benchmark decode (hidden option) */ + if (operation==zom_bench) { BMK_setDecodeOnlyMode(1); argument++; break; } /* benchmark decode (hidden option) */ #endif operation=zom_decompress; argument++; break; @@ -430,6 +430,12 @@ int main(int argCount, const char* argv[]) dictSelect = readU32FromChar(&argument); break; + /* nb of threads (hidden option) */ + case 'T': + argument++; + BMK_SetNbThreads(readU32FromChar(&argument)); + break; + /* Pause at the end (-p) or set an additional param (-p#) (hidden option) */ case 'p': argument++; #ifndef ZSTD_NOBENCH From 6c0ed9483aab0b0d95e593723f2ef0bdd55160e7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 28 Dec 2016 17:08:28 +0100 Subject: [PATCH 068/227] compression threads use ZSTD_compressCCtx() --- lib/compress/zstdmt_compress.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index c698dce0e..0f14dbf31 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -235,13 +235,16 @@ struct ZSTDMT_CCtx_s { static void* ZSTDMT_compressionThread(void* arg) { if (arg==NULL) return NULL; /* error : should not be possible */ - ZSTDMT_CCtx* const cctx = (ZSTDMT_CCtx*) arg; - ZSTDMT_jobAgency* const jobAgency = &cctx->jobAgency; - ZSTDMT_bufferPool* const pool = &cctx->bufferPool; + ZSTDMT_CCtx* const mtctx = (ZSTDMT_CCtx*) arg; + ZSTDMT_jobAgency* const jobAgency = &mtctx->jobAgency; + ZSTDMT_bufferPool* const pool = &mtctx->bufferPool; + ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + if (cctx==NULL) return NULL; /* allocation failure : thread not started */ for (;;) { ZSTDMT_jobDescription const job = ZSTDMT_getjob(jobAgency); if (job.src == NULL) { - DEBUGLOG(4, "thread exit ") + DEBUGLOG(4, "thread exit "); + ZSTD_freeCCtx(cctx); return NULL; } ZSTDMT_dstBufferManager* dstBufferManager = job.dstManager; @@ -249,7 +252,8 @@ static void* ZSTDMT_compressionThread(void* arg) DEBUGLOG(4, "requesting a dstBuffer for frame %u", job.frameNumber); buffer_t const dstBuffer = job.frameNumber ? ZSTDMT_getBuffer(pool, dstBufferCapacity) : ZSTDMT_getDstBuffer(dstBufferManager); /* lack params */ DEBUGLOG(4, "start compressing frame %u", job.frameNumber); - size_t const cSize = ZSTD_compress(dstBuffer.start, dstBuffer.bufferSize, job.src, job.srcSize, job.compressionLevel); + //size_t const cSize = ZSTD_compress(dstBuffer.start, dstBuffer.bufferSize, job.src, job.srcSize, job.compressionLevel); + size_t const cSize = ZSTD_compressCCtx(cctx, dstBuffer.start, dstBuffer.bufferSize, job.src, job.srcSize, job.compressionLevel); if (ZSTD_isError(cSize)) return (void*)(cSize); /* error */ size_t const writeError = ZSTDMT_tryWriteFrame(dstBufferManager, dstBuffer.start, cSize, job.frameNumber, job.isLastFrame); /* pas clair */ if (ZSTD_isError(writeError)) return (void*)writeError; From e70912c72bcafdf4f8b43a25017eb579a85b97f6 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 29 Dec 2016 01:24:01 +0100 Subject: [PATCH 069/227] Changed : input divided into roughly equal parts. Debug : can measure time waiting for mutexes to unlock. --- lib/compress/zstdmt_compress.c | 60 ++++++++++++++++++++++++++-------- programs/bench.c | 4 +-- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 0f14dbf31..c86be8701 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -5,12 +5,41 @@ #if 0 # include - static unsigned g_debugLevel = 4; +# include +# include + static unsigned g_debugLevel = 2; # define DEBUGLOG(l, ...) if (l<=g_debugLevel) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, " \n"); } + +static unsigned long long GetCurrentClockTimeMicroseconds() +{ + static clock_t _ticksPerSecond = 0; + if (_ticksPerSecond <= 0) _ticksPerSecond = sysconf(_SC_CLK_TCK); + + struct tms junk; clock_t newTicks = (clock_t) times(&junk); + return ((((unsigned long long)newTicks)*(1000000))/_ticksPerSecond); +} + +#define MUTEX_WAIT_TIME_DLEVEL 5 +#define PTHREAD_MUTEX_LOCK(mutex) \ +if (g_debugLevel>=MUTEX_WAIT_TIME_DLEVEL) { \ + unsigned long long beforeTime = GetCurrentClockTimeMicroseconds(); \ + pthread_mutex_lock(mutex); \ + unsigned long long afterTime = GetCurrentClockTimeMicroseconds(); \ + unsigned long long elapsedTime = (afterTime-beforeTime); \ + if (elapsedTime > 1000) { /* or whatever threshold you like; I'm using 1 millisecond here */ \ + DEBUGLOG(MUTEX_WAIT_TIME_DLEVEL, "Thread %li took %llu microseconds to acquire mutex %s \n", \ + (long int) pthread_self(), elapsedTime, #mutex); \ + } \ +} else pthread_mutex_lock(mutex); + #else + # define DEBUGLOG(l, ...) /* disabled */ +# define PTHREAD_MUTEX_LOCK(m) pthread_mutex_lock(m) + #endif + #define ZSTDMT_NBTHREADS_MAX 128 #define ZSTDMT_NBSTACKEDFRAMES_MAX (2*ZSTDMT_NBTHREADS_MAX) @@ -38,8 +67,9 @@ static ZSTDMT_dstBufferManager ZSTDMT_createDstBufferManager(void* dst, size_t d dbm.out.pos = 0; dbm.frameIDToWrite = 0; pthread_mutex_init(&dbm.frameTable_mutex, NULL); - pthread_mutex_init(&dbm.allFramesWritten_mutex, NULL); - pthread_mutex_lock(&dbm.allFramesWritten_mutex); /* maybe could be merged into init ? */ + pthread_mutex_t* const allFramesWritten_mutex = &dbm.allFramesWritten_mutex; + pthread_mutex_init(allFramesWritten_mutex, NULL); + PTHREAD_MUTEX_LOCK(allFramesWritten_mutex); /* maybe could be merged into init ? */ dbm.nbStackedFrames = 0; return dbm; } @@ -89,7 +119,7 @@ static size_t ZSTDMT_tryWriteFrame(ZSTDMT_dstBufferManager* dstBufferManager, /* check if correct frame ordering; stack otherwise */ DEBUGLOG(5, "considering writing frame %u ", frameID); - pthread_mutex_lock(&dstBufferManager->frameTable_mutex); + PTHREAD_MUTEX_LOCK(&dstBufferManager->frameTable_mutex); if (frameID != dstBufferManager->frameIDToWrite) { DEBUGLOG(4, "writing frameID %u : not possible, waiting for %u ", frameID, dstBufferManager->frameIDToWrite); frameToWrite_t const frame = { src, srcSize, frameID, isLastFrame }; @@ -112,7 +142,7 @@ static size_t ZSTDMT_tryWriteFrame(ZSTDMT_dstBufferManager* dstBufferManager, lastFrameWritten = isLastFrame; /* check if more frames are stacked */ - pthread_mutex_lock(&dstBufferManager->frameTable_mutex); + PTHREAD_MUTEX_LOCK(&dstBufferManager->frameTable_mutex); unsigned frameWritten = dstBufferManager->nbStackedFrames>0; while (frameWritten) { unsigned u; @@ -127,7 +157,7 @@ static size_t ZSTDMT_tryWriteFrame(ZSTDMT_dstBufferManager* dstBufferManager, lastFrameWritten = dstBufferManager->stackedFrame[u].isLastFrame; dstBufferManager->frameIDToWrite = frameID+1; /* remove frame from stack */ - pthread_mutex_lock(&dstBufferManager->frameTable_mutex); + PTHREAD_MUTEX_LOCK(&dstBufferManager->frameTable_mutex); dstBufferManager->stackedFrame[u] = dstBufferManager->stackedFrame[dstBufferManager->nbStackedFrames-1]; dstBufferManager->nbStackedFrames -= 1; frameWritten = dstBufferManager->nbStackedFrames>0; @@ -166,7 +196,7 @@ typedef struct ZSTDMT_jobAgency_s { static void ZSTDMT_postjob(ZSTDMT_jobAgency* jobAgency, ZSTDMT_jobDescription job) { DEBUGLOG(5, "starting job posting "); - pthread_mutex_lock(&jobAgency->jobApply_mutex); /* wait for a thread to take previous job */ + PTHREAD_MUTEX_LOCK(&jobAgency->jobApply_mutex); /* wait for a thread to take previous job */ DEBUGLOG(5, "job posting mutex acquired "); jobAgency->jobAnnounce = job; /* post job */ pthread_mutex_unlock(&jobAgency->jobAnnounce_mutex); /* announce */ @@ -175,7 +205,7 @@ static void ZSTDMT_postjob(ZSTDMT_jobAgency* jobAgency, ZSTDMT_jobDescription jo static ZSTDMT_jobDescription ZSTDMT_getjob(ZSTDMT_jobAgency* jobAgency) { - pthread_mutex_lock(&jobAgency->jobAnnounce_mutex); /* should check return code */ + PTHREAD_MUTEX_LOCK(&jobAgency->jobAnnounce_mutex); /* should check return code */ ZSTDMT_jobDescription const job = jobAgency->jobAnnounce; pthread_mutex_unlock(&jobAgency->jobApply_mutex); return job; @@ -192,7 +222,7 @@ typedef struct ZSTDMT_bufferPool_s { static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) { - pthread_mutex_lock(&pool->bufferPool_mutex); + PTHREAD_MUTEX_LOCK(&pool->bufferPool_mutex); if (pool->nbBuffers) { /* try to use an existing buffer */ pool->nbBuffers--; buffer_t const buf = pool->bTable[pool->nbBuffers]; @@ -213,7 +243,7 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) /* effectively store buffer for later re-use, up to pool capacity */ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf) { - pthread_mutex_lock(&pool->bufferPool_mutex); + PTHREAD_MUTEX_LOCK(&pool->bufferPool_mutex); if (pool->nbBuffers >= ZSTDMT_NBBUFFERSPOOLED_MAX) { pthread_mutex_unlock(&pool->bufferPool_mutex); free(buf.start); @@ -240,6 +270,7 @@ static void* ZSTDMT_compressionThread(void* arg) ZSTDMT_bufferPool* const pool = &mtctx->bufferPool; ZSTD_CCtx* const cctx = ZSTD_createCCtx(); if (cctx==NULL) return NULL; /* allocation failure : thread not started */ + DEBUGLOG(3, "thread %li created ", (long int)pthread_self()); for (;;) { ZSTDMT_jobDescription const job = ZSTDMT_getjob(jobAgency); if (job.src == NULL) { @@ -254,7 +285,7 @@ static void* ZSTDMT_compressionThread(void* arg) DEBUGLOG(4, "start compressing frame %u", job.frameNumber); //size_t const cSize = ZSTD_compress(dstBuffer.start, dstBuffer.bufferSize, job.src, job.srcSize, job.compressionLevel); size_t const cSize = ZSTD_compressCCtx(cctx, dstBuffer.start, dstBuffer.bufferSize, job.src, job.srcSize, job.compressionLevel); - if (ZSTD_isError(cSize)) return (void*)(cSize); /* error */ + if (ZSTD_isError(cSize)) return (void*)(cSize); /* error - find a better way */ size_t const writeError = ZSTDMT_tryWriteFrame(dstBufferManager, dstBuffer.start, cSize, job.frameNumber, job.isLastFrame); /* pas clair */ if (ZSTD_isError(writeError)) return (void*)writeError; if (job.frameNumber) ZSTDMT_releaseBuffer(pool, dstBuffer); @@ -269,7 +300,7 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) /* init jobAgency */ pthread_mutex_init(&cctx->jobAgency.jobAnnounce_mutex, NULL); /* check return value ? */ pthread_mutex_init(&cctx->jobAgency.jobApply_mutex, NULL); - pthread_mutex_lock(&cctx->jobAgency.jobAnnounce_mutex); /* no job at beginning */ + PTHREAD_MUTEX_LOCK(&cctx->jobAgency.jobAnnounce_mutex); /* no job at beginning */ /* init bufferPool */ pthread_mutex_init(&cctx->bufferPool.bufferPool_mutex, NULL); /* start all workers */ @@ -299,7 +330,8 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, ZSTDMT_jobAgency* jobAgency = &cctx->jobAgency; ZSTD_parameters const params = ZSTD_getParams(compressionLevel, srcSize, 0); size_t const frameSizeTarget = (size_t)1 << (params.cParams.windowLog + 2); - unsigned const nbFrames = (unsigned)(srcSize / frameSizeTarget) + (srcSize < frameSizeTarget) /* min 1 */; + unsigned const nbFramesMax = (unsigned)(srcSize / frameSizeTarget) + (srcSize < frameSizeTarget) /* min 1 */; + unsigned const nbFrames = MIN(nbFramesMax, cctx->nbThreads); size_t const avgFrameSize = (srcSize + (nbFrames-1)) / nbFrames; size_t remainingSrcSize = srcSize; const char* const srcStart = (const char*)src; @@ -320,7 +352,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, remainingSrcSize -= frameSize; } } - pthread_mutex_lock(&dbm.allFramesWritten_mutex); + PTHREAD_MUTEX_LOCK(&dbm.allFramesWritten_mutex); DEBUGLOG(4, "compressed size : %u ", (U32)dbm.out.pos); return dbm.out.pos; } diff --git a/programs/bench.c b/programs/bench.c index 6009ebc7b..b5cc77eed 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -159,8 +159,6 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, U32 nbBlocks; UTIL_time_t ticksPerSecond; - ZSTDMT_CCtx* const mtcctx = ZSTDMT_createCCtx(g_nbThreads); - /* checks */ if (!compressedBuffer || !resultBuffer || !blockTable || !ctx || !dctx) EXM_THROW(31, "allocation error : not enough memory"); @@ -228,6 +226,8 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, const char* const marks[NB_MARKS] = { " |", " /", " =", "\\" }; U32 markNb = 0; + ZSTDMT_CCtx* const mtcctx = ZSTDMT_createCCtx(g_nbThreads); + UTIL_getTime(&coolTime); DISPLAYLEVEL(2, "\r%79s\r", ""); while (!cCompleted || !dCompleted) { From e777a5be6bbf1981b91009feddf3daf9f8051d2c Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 29 Dec 2016 23:39:44 -0800 Subject: [PATCH 070/227] Add a thread pool for ZSTDMT and COVER --- lib/common/pool.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++ lib/common/pool.h | 45 +++++++++++ 2 files changed, 235 insertions(+) create mode 100644 lib/common/pool.c create mode 100644 lib/common/pool.h diff --git a/lib/common/pool.c b/lib/common/pool.c new file mode 100644 index 000000000..bea48f318 --- /dev/null +++ b/lib/common/pool.c @@ -0,0 +1,190 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "pool.h" +#include /* size_t */ +#include /* malloc, calloc, free */ + +#ifdef ZSTD_PTHREAD + +#include + +/* A job is a function and an opaque argument */ +typedef struct POOL_job_s { + POOL_function function; + void *opaque; +} POOL_job; + +struct POOL_ctx_s { + /* Keep track of the threads */ + pthread_t *threads; + size_t numThreads; + + /* The queue is a circular buffer */ + POOL_job *queue; + size_t queueHead; + size_t queueTail; + size_t queueSize; + /* The mutex protects the queue */ + pthread_mutex_t queueMutex; + /* Condition variable for pushers to wait on when the queue is full */ + pthread_cond_t queuePushCond; + /* Condition variables for poppers to wait on when the queue is empty */ + pthread_cond_t queuePopCond; + /* Indicates if the queue is shutting down */ + int shutdown; +}; + +/* POOL_thread() : + Work thread for the thread pool. + Waits for jobs and executes them. + @returns : NULL on failure else non-null. +*/ +static void *POOL_thread(void *opaque) { + POOL_ctx *ctx = (POOL_ctx *)opaque; + if (!ctx) { return NULL; } + for (;;) { + /* Lock the mutex and wait for a non-empty queue or until shutdown */ + if (pthread_mutex_lock(&ctx->queueMutex)) { return NULL; } + while (ctx->queueHead == ctx->queueTail && !ctx->shutdown) { + if (pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex)) { return NULL; } + } + /* empty => shutting down: so stop */ + if (ctx->queueHead == ctx->queueTail) { + if (pthread_mutex_unlock(&ctx->queueMutex)) { return NULL; } + return opaque; + } + { + /* Pop a job off the queue */ + POOL_job job = ctx->queue[ctx->queueHead]; + ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize; + /* Unlock the mutex, signal a pusher, and run the job */ + if (pthread_mutex_unlock(&ctx->queueMutex)) { return NULL; } + if (pthread_cond_signal(&ctx->queuePushCond)) { return NULL; } + job.function(job.opaque); + } + } + /* Unreachable */ +} + +POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { + int err = 0; + POOL_ctx *ctx; + /* Check the parameters */ + if (!numThreads || !queueSize) { return NULL; } + /* Allocate the context and zero initialize */ + ctx = (POOL_ctx *)calloc(1, sizeof(POOL_ctx)); + if (!ctx) { return NULL; } + /* Initialize the job queue. + * It needs one extra space since one space is wasted to differentiate empty + * and full queues. + */ + ctx->queueSize = queueSize + 1; + ctx->queue = (POOL_job *)malloc(ctx->queueSize * sizeof(POOL_job)); + ctx->queueHead = 0; + ctx->queueTail = 0; + err |= pthread_mutex_init(&ctx->queueMutex, NULL); + err |= pthread_cond_init(&ctx->queuePushCond, NULL); + err |= pthread_cond_init(&ctx->queuePopCond, NULL); + ctx->shutdown = 0; + /* Allocate space for the thread handles */ + ctx->threads = (pthread_t *)malloc(numThreads * sizeof(pthread_t)); + ctx->numThreads = 0; + /* Check for errors */ + if (!ctx->threads || !ctx->queue || err) { POOL_free(ctx); return NULL; } + /* Initialize the threads */ + { size_t i; + for (i = 0; i < numThreads; ++i) { + if (pthread_create(&ctx->threads[i], NULL, &POOL_thread, ctx)) { + ctx->numThreads = i; + POOL_free(ctx); + return NULL; + } + } + ctx->numThreads = numThreads; + } + return ctx; +} + +/*! POOL_join() : + Shutdown the queue, wake any sleeping threads, and join all of the threads. +*/ +static void POOL_join(POOL_ctx *ctx) { + /* Shut down the queue */ + pthread_mutex_lock(&ctx->queueMutex); + ctx->shutdown = 1; + pthread_mutex_unlock(&ctx->queueMutex); + /* Wake up sleeping threads */ + pthread_cond_broadcast(&ctx->queuePushCond); + pthread_cond_broadcast(&ctx->queuePopCond); + /* Join all of the threads */ + { size_t i; + for (i = 0; i < ctx->numThreads; ++i) { + pthread_join(ctx->threads[i], NULL); + } + } +} + +void POOL_free(POOL_ctx *ctx) { + if (!ctx) { return; } + POOL_join(ctx); + pthread_mutex_destroy(&ctx->queueMutex); + pthread_cond_destroy(&ctx->queuePushCond); + pthread_cond_destroy(&ctx->queuePopCond); + if (ctx->queue) free(ctx->queue); + if (ctx->threads) free(ctx->threads); + free(ctx); +} + +void POOL_add(void *ctxVoid, POOL_function function, void *opaque) { + POOL_ctx *ctx = (POOL_ctx *)ctxVoid; + if (!ctx) { return; } + + pthread_mutex_lock(&ctx->queueMutex); + { + POOL_job job = {function, opaque}; + /* Wait until there is space in the queue for the new job */ + size_t newTail = (ctx->queueTail + 1) % ctx->queueSize; + while (ctx->queueHead == newTail && !ctx->shutdown) { + pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex); + newTail = (ctx->queueTail + 1) % ctx->queueSize; + } + /* The queue is still going => there is space */ + if (!ctx->shutdown) { + ctx->queue[ctx->queueTail] = job; + ctx->queueTail = newTail; + } + } + pthread_mutex_unlock(&ctx->queueMutex); + pthread_cond_signal(&ctx->queuePopCond); +} + +#else + +/* We don't need any data, but if it is empty malloc() might return NULL. */ +struct POOL_ctx_s { + int data; +}; + +POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { + (void)numThreads; + (void)queueSize; + return (POOL_ctx *)malloc(sizeof(POOL_ctx)); +} + +void POOL_free(POOL_ctx *ctx) { + if (ctx) free(ctx); +} + +void POOL_add(void *ctx, POOL_function function, void *opaque) { + (void)ctx; + function(opaque); +} + +#endif diff --git a/lib/common/pool.h b/lib/common/pool.h new file mode 100644 index 000000000..f4afc1ee3 --- /dev/null +++ b/lib/common/pool.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef POOL_H +#define POOL_H + +#include /* size_t */ + +typedef struct POOL_ctx_s POOL_ctx; + +/*! POOL_create() : + Create a thread pool with at most `numThreads` threads. + `numThreads` must be at least 1. + The maximum number of queued jobs before blocking is `queueSize`. + `queueSize` must be at least 1. + @return : The POOL_ctx pointer on success else NULL. +*/ +POOL_ctx *POOL_create(size_t numThreads, size_t queueSize); + +/*! POOL_free() : + Free a thread pool returned by POOL_create(). +*/ +void POOL_free(POOL_ctx *ctx); + +/*! POOL_function : + The function type that can be added to a thread pool. +*/ +typedef void (*POOL_function)(void *); +/*! POOL_add_function : + The function type for a generic thread pool add function. +*/ +typedef void (*POOL_add_function)(void *, POOL_function, void *); + +/*! POOL_add() : + Add the job `function(opaque)` to the thread pool. + Possibly blocks until there is room in the queue. +*/ +void POOL_add(void *ctx, POOL_function function, void *opaque); + +#endif From 9c499648e3097fb8e92e74a242fc83d6e6d09776 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 29 Dec 2016 23:41:03 -0800 Subject: [PATCH 071/227] Add thread pool tests --- .travis.yml | 2 +- tests/.gitignore | 1 + tests/Makefile | 8 +++++- tests/pool.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 tests/pool.c diff --git a/.travis.yml b/.travis.yml index 6bf99f1bf..36537cbee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ matrix: os: linux sudo: false - - env: Ubu=12.04cont Cmd="make zlibwrapper && make clean && make -C tests test-symbols && make clean && make -C tests test-zstd-nolegacy && make clean && make cmaketest && make clean && make -C contrib/pzstd googletest pzstd tests check && make -C contrib/pzstd clean" + - env: Ubu=12.04cont Cmd="make zlibwrapper && make clean && make -C tests test-pool && make -C tests test-symbols && make clean && make -C tests test-zstd-nolegacy && make clean && make cmaketest && make clean && make -C contrib/pzstd googletest pzstd tests check && make -C contrib/pzstd clean" os: linux sudo: false language: cpp diff --git a/tests/.gitignore b/tests/.gitignore index e932ad91c..5041404dd 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -15,6 +15,7 @@ paramgrill32 roundTripCrash longmatch symbols +pool invalidDictionaries # Tmp test directory diff --git a/tests/Makefile b/tests/Makefile index c080fe34a..739944de8 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -158,6 +158,9 @@ else $(CC) $(FLAGS) $^ -o $@$(EXT) -Wl,-rpath=$(ZSTDDIR) $(ZSTDDIR)/libzstd.so endif +pool : pool.c $(ZSTDDIR)/common/pool.c + $(CC) $(FLAGS) -pthread -DZSTD_PTHREAD $^ -o $@$(EXT) + namespaceTest: if $(CC) namespaceTest.c ../lib/common/xxhash.c -o $@ ; then echo compilation should fail; exit 1 ; fi $(RM) $@ @@ -176,7 +179,7 @@ clean: fuzzer-dll$(EXT) zstreamtest-dll$(EXT) zbufftest-dll$(EXT)\ zstreamtest$(EXT) zstreamtest32$(EXT) \ datagen$(EXT) paramgrill$(EXT) roundTripCrash$(EXT) longmatch$(EXT) \ - symbols$(EXT) invalidDictionaries$(EXT) + symbols$(EXT) invalidDictionaries$(EXT) pool$(EXT) @echo Cleaning completed @@ -288,4 +291,7 @@ test-invalidDictionaries: invalidDictionaries test-symbols: symbols $(QEMU_SYS) ./symbols +test-pool: pool + $(QEMU_SYS) ./pool + endif diff --git a/tests/pool.c b/tests/pool.c new file mode 100644 index 000000000..ce38075d0 --- /dev/null +++ b/tests/pool.c @@ -0,0 +1,70 @@ +#include "pool.h" +#include +#include +#include + +#define ASSERT_TRUE(p) \ + do { \ + if (!(p)) { \ + return 1; \ + } \ + } while (0) +#define ASSERT_FALSE(p) ASSERT_TRUE(!(p)) +#define ASSERT_EQ(lhs, rhs) ASSERT_TRUE((lhs) == (rhs)) + +struct data { + pthread_mutex_t mutex; + unsigned data[1024]; + size_t i; +}; + +void fn(void *opaque) { + struct data *data = (struct data *)opaque; + pthread_mutex_lock(&data->mutex); + data->data[data->i] = data->i; + ++data->i; + pthread_mutex_unlock(&data->mutex); +} + +int testOrder(size_t numThreads, size_t queueLog) { + struct data data; + POOL_ctx *ctx = POOL_create(numThreads, queueLog); + ASSERT_TRUE(ctx); + data.i = 0; + ASSERT_FALSE(pthread_mutex_init(&data.mutex, NULL)); + { + size_t i; + for (i = 0; i < 1024; ++i) { + POOL_add(ctx, &fn, &data); + } + } + POOL_free(ctx); + ASSERT_EQ(1024, data.i); + { + size_t i; + for (i = 0; i < data.i; ++i) { + ASSERT_EQ(i, data.data[i]); + } + } + ASSERT_FALSE(pthread_mutex_destroy(&data.mutex)); + return 0; +} + +int main(int argc, const char **argv) { + size_t numThreads; + for (numThreads = 1; numThreads <= 8; ++numThreads) { + size_t queueLog; + for (queueLog = 1; queueLog <= 8; ++queueLog) { + if (testOrder(numThreads, queueLog)) { + printf("FAIL: testOrder\n"); + return 1; + } + } + } + printf("PASS: testOrder\n"); + (POOL_create(0, 1) || POOL_create(1, 0)) ? printf("FAIL: testInvalid\n") + : printf("PASS: testInvalid\n"); + (void)argc; + (void)argv; + return 0; +} From c6a6417458751512380f4b284e15f2930dc8026d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 31 Dec 2016 03:31:26 +0100 Subject: [PATCH 072/227] bench correctly measures time for multi-threaded compression (posix only) --- lib/common/pool.c | 14 +++++--------- lib/compress/zstdmt_compress.c | 6 +++--- programs/bench.c | 31 ++++++++++++++++++++++++------- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index bea48f318..e38881949 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -60,9 +60,8 @@ static void *POOL_thread(void *opaque) { if (pthread_mutex_unlock(&ctx->queueMutex)) { return NULL; } return opaque; } - { - /* Pop a job off the queue */ - POOL_job job = ctx->queue[ctx->queueHead]; + /* Pop a job off the queue */ + { POOL_job job = ctx->queue[ctx->queueHead]; ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize; /* Unlock the mutex, signal a pusher, and run the job */ if (pthread_mutex_unlock(&ctx->queueMutex)) { return NULL; } @@ -105,8 +104,7 @@ POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { ctx->numThreads = i; POOL_free(ctx); return NULL; - } - } + } } ctx->numThreads = numThreads; } return ctx; @@ -127,8 +125,7 @@ static void POOL_join(POOL_ctx *ctx) { { size_t i; for (i = 0; i < ctx->numThreads; ++i) { pthread_join(ctx->threads[i], NULL); - } - } + } } } void POOL_free(POOL_ctx *ctx) { @@ -147,8 +144,7 @@ void POOL_add(void *ctxVoid, POOL_function function, void *opaque) { if (!ctx) { return; } pthread_mutex_lock(&ctx->queueMutex); - { - POOL_job job = {function, opaque}; + { POOL_job const job = {function, opaque}; /* Wait until there is space in the queue for the new job */ size_t newTail = (ctx->queueTail + 1) % ctx->queueSize; while (ctx->queueHead == newTail && !ctx->shutdown) { diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index c86be8701..a6a497285 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -322,16 +322,16 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* cctx) return 0; } -size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, +size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) { - ZSTDMT_jobAgency* jobAgency = &cctx->jobAgency; + ZSTDMT_jobAgency* jobAgency = &mtctx->jobAgency; ZSTD_parameters const params = ZSTD_getParams(compressionLevel, srcSize, 0); size_t const frameSizeTarget = (size_t)1 << (params.cParams.windowLog + 2); unsigned const nbFramesMax = (unsigned)(srcSize / frameSizeTarget) + (srcSize < frameSizeTarget) /* min 1 */; - unsigned const nbFrames = MIN(nbFramesMax, cctx->nbThreads); + unsigned const nbFrames = MIN(nbFramesMax, mtctx->nbThreads); size_t const avgFrameSize = (srcSize + (nbFrames-1)) / nbFrames; size_t remainingSrcSize = srcSize; const char* const srcStart = (const char*)src; diff --git a/programs/bench.c b/programs/bench.c index b5cc77eed..c718e2199 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -24,7 +24,7 @@ #include "util.h" /* UTIL_getFileSize, UTIL_sleep */ #include /* malloc, free */ #include /* memset */ -#include /* fprintf, fopen, ftello64 */ +#include /* fprintf, fopen */ #include /* clock_t, clock, CLOCKS_PER_SEC */ #include "mem.h" @@ -88,6 +88,23 @@ static clock_t g_time = 0; exit(error); \ } +/* ************************************* +* Time +***************************************/ +/* for posix only - proper detection macros to setup */ +#include +#include + +typedef unsigned long long clock_us_t; +static clock_us_t BMK_clockMicroSec() +{ + static clock_t _ticksPerSecond = 0; + if (_ticksPerSecond <= 0) _ticksPerSecond = sysconf(_SC_CLK_TCK); + + struct tms junk; clock_t newTicks = (clock_t) times(&junk); (void)junk; + return ((((clock_us_t)newTicks)*(1000000))/_ticksPerSecond); +} + /* ************************************* * Benchmark Parameters @@ -231,7 +248,6 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, UTIL_getTime(&coolTime); DISPLAYLEVEL(2, "\r%79s\r", ""); while (!cCompleted || !dCompleted) { - UTIL_time_t clockStart; /* overheat protection */ if (UTIL_clockSpanMicro(coolTime, ticksPerSecond) > ACTIVEPERIOD_MICROSEC) { @@ -241,13 +257,14 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, } if (!g_decodeOnly) { + clock_us_t clockStart; /* Compression */ DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->\r", marks[markNb], displayName, (U32)srcSize); if (!cCompleted) memset(compressedBuffer, 0xE5, maxCompressedSize); /* warm up and erase result buffer */ UTIL_sleepMilli(1); /* give processor time to other processes */ UTIL_waitForNextTick(ticksPerSecond); - UTIL_getTime(&clockStart); + clockStart = BMK_clockMicroSec(); if (!cCompleted) { /* still some time to do compression tests */ ZSTD_parameters zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize); @@ -286,11 +303,11 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, blockTable[blockNb].cSize = rSize; } nbLoops++; - } while (UTIL_clockSpanMicro(clockStart, ticksPerSecond) < clockLoop); + } while (BMK_clockMicroSec() - clockStart < clockLoop); ZSTD_freeCDict(cdict); - { U64 const clockSpan = UTIL_clockSpanMicro(clockStart, ticksPerSecond); - if (clockSpan < fastestC*nbLoops) fastestC = clockSpan / nbLoops; - totalCTime += clockSpan; + { clock_us_t const clockSpanMicro = BMK_clockMicroSec() - clockStart; + if (clockSpanMicro < fastestC*nbLoops) fastestC = clockSpanMicro / nbLoops; + totalCTime += clockSpanMicro; cCompleted = (totalCTime >= maxTime); } } From 3b29dbd9e885c84842d58bcf2b9ca2843e8e483c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 31 Dec 2016 06:04:25 +0100 Subject: [PATCH 073/227] new zstdmt version using generic treadpool --- lib/common/pool.c | 6 +- lib/compress/zstdmt_compress.c | 361 ++++++++++++--------------------- 2 files changed, 137 insertions(+), 230 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index e38881949..4ec1dfffb 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -46,8 +46,8 @@ struct POOL_ctx_s { Waits for jobs and executes them. @returns : NULL on failure else non-null. */ -static void *POOL_thread(void *opaque) { - POOL_ctx *ctx = (POOL_ctx *)opaque; +static void* POOL_thread(void* opaque) { + POOL_ctx* const ctx = (POOL_ctx*)opaque; if (!ctx) { return NULL; } for (;;) { /* Lock the mutex and wait for a non-empty queue or until shutdown */ @@ -61,7 +61,7 @@ static void *POOL_thread(void *opaque) { return opaque; } /* Pop a job off the queue */ - { POOL_job job = ctx->queue[ctx->queueHead]; + { POOL_job const job = ctx->queue[ctx->queueHead]; ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize; /* Unlock the mutex, signal a pusher, and run the job */ if (pthread_mutex_unlock(&ctx->queueMutex)) { return NULL; } diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index a6a497285..1b925914a 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1,5 +1,6 @@ #include /* malloc */ -#include /* posix only, to be replaced by a more portable version */ +#include /* threadpool */ +#include /* mutex */ #include "zstd_internal.h" /* MIN, ERROR */ #include "zstdmt_compress.h" @@ -43,176 +44,11 @@ if (g_debugLevel>=MUTEX_WAIT_TIME_DLEVEL) { \ #define ZSTDMT_NBTHREADS_MAX 128 #define ZSTDMT_NBSTACKEDFRAMES_MAX (2*ZSTDMT_NBTHREADS_MAX) -typedef struct frameToWrite_s { - const void* start; - size_t frameSize; - unsigned frameID; - unsigned isLastFrame; -} frameToWrite_t; - -typedef struct ZSTDMT_dstBuffer_s { - ZSTD_outBuffer out; - unsigned frameIDToWrite; - pthread_mutex_t frameTable_mutex; - pthread_mutex_t allFramesWritten_mutex; - frameToWrite_t stackedFrame[ZSTDMT_NBSTACKEDFRAMES_MAX]; - unsigned nbStackedFrames; -} ZSTDMT_dstBufferManager; - -static ZSTDMT_dstBufferManager ZSTDMT_createDstBufferManager(void* dst, size_t dstCapacity) -{ - ZSTDMT_dstBufferManager dbm; - dbm.out.dst = dst; - dbm.out.size = dstCapacity; - dbm.out.pos = 0; - dbm.frameIDToWrite = 0; - pthread_mutex_init(&dbm.frameTable_mutex, NULL); - pthread_mutex_t* const allFramesWritten_mutex = &dbm.allFramesWritten_mutex; - pthread_mutex_init(allFramesWritten_mutex, NULL); - PTHREAD_MUTEX_LOCK(allFramesWritten_mutex); /* maybe could be merged into init ? */ - dbm.nbStackedFrames = 0; - return dbm; -} - -/* note : can fail if nbStackedFrames > ZSTDMT_NBSTACKEDFRAMES_MAX. - * note2 : can only be called from a section with frameTable_mutex already locked */ -static void ZSTDMT_stackFrameToWrite(ZSTDMT_dstBufferManager* dstBufferManager, frameToWrite_t frame) { - dstBufferManager->stackedFrame[dstBufferManager->nbStackedFrames++] = frame; -} - - typedef struct buffer_s { void* start; - size_t bufferSize; + size_t size; } buffer_t; -static buffer_t ZSTDMT_getDstBuffer(const ZSTDMT_dstBufferManager* dstBufferManager) -{ - ZSTD_outBuffer const out = dstBufferManager->out; - buffer_t buf; - buf.start = (char*)(out.dst) + out.pos; - buf.bufferSize = out.size - out.pos; - return buf; -} - -/* condition : stackNumber < dstBufferManager->nbStackedFrames. - * note : there can only be one write at a time, due to frameID condition */ -static size_t ZSTDMT_writeFrame(ZSTDMT_dstBufferManager* dstBufferManager, unsigned stackNumber) -{ - ZSTD_outBuffer const out = dstBufferManager->out; - size_t const frameSize = dstBufferManager->stackedFrame[stackNumber].frameSize; - const void* const frameStart = dstBufferManager->stackedFrame[stackNumber].start; - if (out.pos + frameSize > out.size) - return ERROR(dstSize_tooSmall); - DEBUGLOG(3, "writing frame %u (%u bytes) ", dstBufferManager->stackedFrame[stackNumber].frameID, (U32)frameSize); - memcpy((char*)out.dst + out.pos, frameStart, frameSize); - dstBufferManager->out.pos += frameSize; - dstBufferManager->frameIDToWrite = dstBufferManager->stackedFrame[stackNumber].frameID + 1; - return 0; -} - -static size_t ZSTDMT_tryWriteFrame(ZSTDMT_dstBufferManager* dstBufferManager, - const void* src, size_t srcSize, - unsigned frameID, unsigned isLastFrame) -{ - unsigned lastFrameWritten = 0; - - /* check if correct frame ordering; stack otherwise */ - DEBUGLOG(5, "considering writing frame %u ", frameID); - PTHREAD_MUTEX_LOCK(&dstBufferManager->frameTable_mutex); - if (frameID != dstBufferManager->frameIDToWrite) { - DEBUGLOG(4, "writing frameID %u : not possible, waiting for %u ", frameID, dstBufferManager->frameIDToWrite); - frameToWrite_t const frame = { src, srcSize, frameID, isLastFrame }; - ZSTDMT_stackFrameToWrite(dstBufferManager, frame); - pthread_mutex_unlock(&dstBufferManager->frameTable_mutex); - return 0; - } - pthread_mutex_unlock(&dstBufferManager->frameTable_mutex); - - /* write frame - * note : only one write possible due to frameID condition */ - DEBUGLOG(3, "writing frame %u (%u bytes) ", frameID, (U32)srcSize); - ZSTD_outBuffer const out = dstBufferManager->out; - if (out.pos + srcSize > out.size) - return ERROR(dstSize_tooSmall); - if (frameID) /* frameID==0 compress directly in dst buffer */ - memcpy((char*)out.dst + out.pos, src, srcSize); - dstBufferManager->out.pos += srcSize; - dstBufferManager->frameIDToWrite = frameID+1; - lastFrameWritten = isLastFrame; - - /* check if more frames are stacked */ - PTHREAD_MUTEX_LOCK(&dstBufferManager->frameTable_mutex); - unsigned frameWritten = dstBufferManager->nbStackedFrames>0; - while (frameWritten) { - unsigned u; - frameID++; - frameWritten = 0; - for (u=0; unbStackedFrames; u++) { - if (dstBufferManager->stackedFrame[u].frameID == frameID) { - pthread_mutex_unlock(&dstBufferManager->frameTable_mutex); - DEBUGLOG(4, "catch up frame %u ", frameID); - { size_t const writeError = ZSTDMT_writeFrame(dstBufferManager, u); - if (ZSTD_isError(writeError)) return writeError; } - lastFrameWritten = dstBufferManager->stackedFrame[u].isLastFrame; - dstBufferManager->frameIDToWrite = frameID+1; - /* remove frame from stack */ - PTHREAD_MUTEX_LOCK(&dstBufferManager->frameTable_mutex); - dstBufferManager->stackedFrame[u] = dstBufferManager->stackedFrame[dstBufferManager->nbStackedFrames-1]; - dstBufferManager->nbStackedFrames -= 1; - frameWritten = dstBufferManager->nbStackedFrames>0; - break; - } } } - pthread_mutex_unlock(&dstBufferManager->frameTable_mutex); - - /* end reached : last frame written */ - if (lastFrameWritten) pthread_mutex_unlock(&dstBufferManager->allFramesWritten_mutex); - return 0; -} - - - -typedef struct ZSTDMT_jobDescription_s { - const void* src; /* NULL means : kill thread */ - size_t srcSize; - int compressionLevel; - ZSTDMT_dstBufferManager* dstManager; - unsigned frameNumber; - unsigned isLastFrame; -} ZSTDMT_jobDescription; - -typedef struct ZSTDMT_jobAgency_s { - pthread_mutex_t jobAnnounce_mutex; - pthread_mutex_t jobApply_mutex; - ZSTDMT_jobDescription jobAnnounce; -} ZSTDMT_jobAgency; - -/* ZSTDMT_postjob() : - * This function is blocking as long as previous posted job is not taken. - * It could be made non-blocking, with a storage queue. - * But blocking has benefits : on top of memory savings, - * the caller will be able to measure delay, allowing dynamic speed throttle (via compression level). - */ -static void ZSTDMT_postjob(ZSTDMT_jobAgency* jobAgency, ZSTDMT_jobDescription job) -{ - DEBUGLOG(5, "starting job posting "); - PTHREAD_MUTEX_LOCK(&jobAgency->jobApply_mutex); /* wait for a thread to take previous job */ - DEBUGLOG(5, "job posting mutex acquired "); - jobAgency->jobAnnounce = job; /* post job */ - pthread_mutex_unlock(&jobAgency->jobAnnounce_mutex); /* announce */ - DEBUGLOG(5, "job available now "); -} - -static ZSTDMT_jobDescription ZSTDMT_getjob(ZSTDMT_jobAgency* jobAgency) -{ - PTHREAD_MUTEX_LOCK(&jobAgency->jobAnnounce_mutex); /* should check return code */ - ZSTDMT_jobDescription const job = jobAgency->jobAnnounce; - pthread_mutex_unlock(&jobAgency->jobApply_mutex); - return job; -} - - - #define ZSTDMT_NBBUFFERSPOOLED_MAX ZSTDMT_NBTHREADS_MAX typedef struct ZSTDMT_bufferPool_s { pthread_mutex_t bufferPool_mutex; @@ -227,7 +63,7 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) pool->nbBuffers--; buffer_t const buf = pool->bTable[pool->nbBuffers]; pthread_mutex_unlock(&pool->bufferPool_mutex); - size_t const availBufferSize = buf.bufferSize; + size_t const availBufferSize = buf.size; if ((availBufferSize >= bSize) & (availBufferSize <= 10*bSize)) /* large enough, but not too much */ return buf; free(buf.start); /* size conditions not respected : create a new buffer */ @@ -235,7 +71,7 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) pthread_mutex_unlock(&pool->bufferPool_mutex); /* create new buffer */ buffer_t buf; - buf.bufferSize = bSize; + buf.size = bSize; buf.start = calloc(1, bSize); return buf; } @@ -255,79 +91,119 @@ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf) -struct ZSTDMT_CCtx_s { - pthread_t pthread[ZSTDMT_NBTHREADS_MAX]; - unsigned nbThreads; - ZSTDMT_jobAgency jobAgency; - ZSTDMT_bufferPool bufferPool; -}; +typedef struct { + ZSTD_CCtx* cctx; + const void* srcStart; + size_t srcSize; + buffer_t dstBuff; + int compressionLevel; + unsigned frameID; + size_t cSize; + unsigned jobCompleted; + pthread_mutex_t* jobCompleted_mutex; +} ZSTDMT_jobDescription; -static void* ZSTDMT_compressionThread(void* arg) +/* ZSTDMT_compressFrame() : POOL_function type */ +void ZSTDMT_compressFrame(void* jobDescription) { - if (arg==NULL) return NULL; /* error : should not be possible */ - ZSTDMT_CCtx* const mtctx = (ZSTDMT_CCtx*) arg; - ZSTDMT_jobAgency* const jobAgency = &mtctx->jobAgency; - ZSTDMT_bufferPool* const pool = &mtctx->bufferPool; - ZSTD_CCtx* const cctx = ZSTD_createCCtx(); - if (cctx==NULL) return NULL; /* allocation failure : thread not started */ - DEBUGLOG(3, "thread %li created ", (long int)pthread_self()); - for (;;) { - ZSTDMT_jobDescription const job = ZSTDMT_getjob(jobAgency); - if (job.src == NULL) { - DEBUGLOG(4, "thread exit "); - ZSTD_freeCCtx(cctx); - return NULL; - } - ZSTDMT_dstBufferManager* dstBufferManager = job.dstManager; - size_t const dstBufferCapacity = ZSTD_compressBound(job.srcSize); - DEBUGLOG(4, "requesting a dstBuffer for frame %u", job.frameNumber); - buffer_t const dstBuffer = job.frameNumber ? ZSTDMT_getBuffer(pool, dstBufferCapacity) : ZSTDMT_getDstBuffer(dstBufferManager); /* lack params */ - DEBUGLOG(4, "start compressing frame %u", job.frameNumber); - //size_t const cSize = ZSTD_compress(dstBuffer.start, dstBuffer.bufferSize, job.src, job.srcSize, job.compressionLevel); - size_t const cSize = ZSTD_compressCCtx(cctx, dstBuffer.start, dstBuffer.bufferSize, job.src, job.srcSize, job.compressionLevel); - if (ZSTD_isError(cSize)) return (void*)(cSize); /* error - find a better way */ - size_t const writeError = ZSTDMT_tryWriteFrame(dstBufferManager, dstBuffer.start, cSize, job.frameNumber, job.isLastFrame); /* pas clair */ - if (ZSTD_isError(writeError)) return (void*)writeError; - if (job.frameNumber) ZSTDMT_releaseBuffer(pool, dstBuffer); - } + DEBUGLOG(5, "Entering ZSTDMT_compressFrame() "); + ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; + DEBUGLOG(5, "compressing %u bytes with ZSTD_compressCCtx : ", (unsigned)job->srcSize); + job->cSize = ZSTD_compressCCtx(job->cctx, job->dstBuff.start, job->dstBuff.size, job->srcStart, job->srcSize, job->compressionLevel); + DEBUGLOG(5, "compressed to %u bytes ", (unsigned)job->cSize); + job->jobCompleted = 1; + DEBUGLOG(5, "unlocking mutex jobCompleted_mutex"); + pthread_mutex_unlock(job->jobCompleted_mutex); + DEBUGLOG(5, "ZSTDMT_compressFrame completed"); } + +/* note : calls to CCtxPool only from main thread */ + +typedef struct { + unsigned totalCCtx; + unsigned availCCtx; + ZSTD_CCtx* cctx[1]; /* variable size */ +} ZSTDMT_CCtxPool; + +static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads) +{ + ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) calloc(1, sizeof(ZSTDMT_CCtxPool) + nbThreads*sizeof(ZSTD_CCtx*)); + if (!cctxPool) return NULL; + { unsigned u; + for (u=0; ucctx[u] = ZSTD_createCCtx(); /* check for NULL result ! */ + } + cctxPool->totalCCtx = cctxPool->availCCtx = nbThreads; + return cctxPool; +} + +static ZSTD_CCtx* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* pool) +{ + if (pool->availCCtx) { + pool->availCCtx--; + return pool->cctx[pool->availCCtx]; + } + /* should not be possible, since totalCCtx==nbThreads */ + return ZSTD_createCCtx(); +} + +static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) +{ + if (pool->availCCtx < pool->totalCCtx) + pool->cctx[pool->availCCtx++] = cctx; + else + /* should not be possible, since totalCCtx==nbThreads */ + ZSTD_freeCCtx(cctx); +} + +static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool) +{ + unsigned u; + for (u=0; utotalCCtx; u++) + ZSTD_freeCCtx(pool->cctx[u]); + free(pool); +} + + +struct ZSTDMT_CCtx_s { + POOL_ctx* factory; + ZSTDMT_bufferPool buffPool; + ZSTDMT_CCtxPool* cctxPool; + unsigned nbThreads; + pthread_mutex_t jobCompleted_mutex; + ZSTDMT_jobDescription jobs[1]; /* variable size */ +}; + ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) { if ((nbThreads < 1) | (nbThreads > ZSTDMT_NBTHREADS_MAX)) return NULL; - ZSTDMT_CCtx* const cctx = (ZSTDMT_CCtx*) calloc(1, sizeof(ZSTDMT_CCtx)); + ZSTDMT_CCtx* const cctx = (ZSTDMT_CCtx*) calloc(1, sizeof(ZSTDMT_CCtx) + nbThreads*sizeof(ZSTDMT_jobDescription)); if (!cctx) return NULL; - /* init jobAgency */ - pthread_mutex_init(&cctx->jobAgency.jobAnnounce_mutex, NULL); /* check return value ? */ - pthread_mutex_init(&cctx->jobAgency.jobApply_mutex, NULL); - PTHREAD_MUTEX_LOCK(&cctx->jobAgency.jobAnnounce_mutex); /* no job at beginning */ - /* init bufferPool */ - pthread_mutex_init(&cctx->bufferPool.bufferPool_mutex, NULL); - /* start all workers */ cctx->nbThreads = nbThreads; - DEBUGLOG(2, "nbThreads : %u \n", nbThreads); - unsigned t; - for (t = 0; t < nbThreads; t++) { - pthread_create(&cctx->pthread[t], NULL, ZSTDMT_compressionThread, cctx); /* check return value ? */ - } + cctx->factory = POOL_create(nbThreads, 1); + pthread_mutex_init(&cctx->buffPool.bufferPool_mutex, NULL); + cctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads); + pthread_mutex_init(&cctx->jobCompleted_mutex, NULL); return cctx; } -size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* cctx) +size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) /* incompleted ! */ { - /* free threads */ - /* free mutex (if necessary) */ + POOL_free(mtctx->factory); + /* free mutexes (if necessary) */ /* free bufferPool */ - free(cctx); /* incompleted ! */ + ZSTDMT_freeCCtxPool(mtctx->cctxPool); + free(mtctx); return 0; } + size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) { - ZSTDMT_jobAgency* jobAgency = &mtctx->jobAgency; ZSTD_parameters const params = ZSTD_getParams(compressionLevel, srcSize, 0); size_t const frameSizeTarget = (size_t)1 << (params.cParams.windowLog + 2); unsigned const nbFramesMax = (unsigned)(srcSize / frameSizeTarget) + (srcSize < frameSizeTarget) /* min 1 */; @@ -336,7 +212,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, size_t remainingSrcSize = srcSize; const char* const srcStart = (const char*)src; size_t frameStartPos = 0; - ZSTDMT_dstBufferManager dbm = ZSTDMT_createDstBufferManager(dst, dstCapacity); + DEBUGLOG(2, "windowLog : %u => frameSizeTarget : %u ", params.cParams.windowLog, (U32)frameSizeTarget); DEBUGLOG(2, "nbFrames : %u (size : %u bytes) ", nbFrames, (U32)avgFrameSize); @@ -344,15 +220,46 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, { unsigned u; for (u=0; ubuffPool, dstBufferCapacity) : (buffer_t){ dst, dstCapacity }; + ZSTD_CCtx* cctx = ZSTDMT_getCCtx(mtctx->cctxPool); + + mtctx->jobs[u].srcStart = srcStart + frameStartPos; + mtctx->jobs[u].srcSize = frameSize; + mtctx->jobs[u].compressionLevel = compressionLevel; + mtctx->jobs[u].dstBuff = dstBuffer; + mtctx->jobs[u].cctx = cctx; + mtctx->jobs[u].frameID = u; + mtctx->jobs[u].jobCompleted = 0; + mtctx->jobs[u].jobCompleted_mutex = &mtctx->jobCompleted_mutex; + DEBUGLOG(3, "posting job %u (%u bytes)", u, (U32)frameSize); - ZSTDMT_jobDescription const job = { srcStart+frameStartPos, frameSize, compressionLevel, - &dbm, u, u==(nbFrames-1) }; - ZSTDMT_postjob(jobAgency, job); + POOL_add(mtctx->factory, ZSTDMT_compressFrame, &mtctx->jobs[u]); + frameStartPos += frameSize; remainingSrcSize -= frameSize; } } + /* note : since nbFrames <= nbThreads, all jobs should be running immediately in parallel */ + + { unsigned frameID; + size_t dstPos = 0; + for (frameID=0; frameIDjobs[frameID].jobCompleted==0) { + DEBUGLOG(4, "waiting for signal jobCompleted_mutex") + pthread_mutex_lock(&mtctx->jobCompleted_mutex); + } + { size_t const cSize = mtctx->jobs[frameID].cSize; + if (ZSTD_isError(cSize)) return cSize; + if (dstPos + cSize > dstCapacity) return ERROR(dstSize_tooSmall); + if (frameID) memcpy((char*)dst + dstPos, mtctx->jobs[frameID].dstBuff.start, mtctx->jobs[frameID].cSize); + dstPos += cSize ; + } + ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[frameID].cctx); + ZSTDMT_releaseBuffer(&mtctx->buffPool, mtctx->jobs[frameID].dstBuff); + } + DEBUGLOG(4, "compressed size : %u ", (U32)dstPos); + return dstPos; + } - PTHREAD_MUTEX_LOCK(&dbm.allFramesWritten_mutex); - DEBUGLOG(4, "compressed size : %u ", (U32)dbm.out.pos); - return dbm.out.pos; } From c8efc1c8749a44af022f619b1a5f5672f66af24f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 31 Dec 2016 14:45:33 +0100 Subject: [PATCH 074/227] simplified Buffer Pool --- lib/compress/zstdmt_compress.c | 64 ++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 1b925914a..97de6e645 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -42,51 +42,65 @@ if (g_debugLevel>=MUTEX_WAIT_TIME_DLEVEL) { \ #define ZSTDMT_NBTHREADS_MAX 128 -#define ZSTDMT_NBSTACKEDFRAMES_MAX (2*ZSTDMT_NBTHREADS_MAX) + +/* === Buffer Pool === */ typedef struct buffer_s { void* start; size_t size; } buffer_t; -#define ZSTDMT_NBBUFFERSPOOLED_MAX ZSTDMT_NBTHREADS_MAX typedef struct ZSTDMT_bufferPool_s { - pthread_mutex_t bufferPool_mutex; - buffer_t bTable[ZSTDMT_NBBUFFERSPOOLED_MAX]; + unsigned totalBuffers;; unsigned nbBuffers; + buffer_t bTable[1]; /* variable size */ } ZSTDMT_bufferPool; +static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbThreads) +{ + unsigned const maxNbBuffers = 2*nbThreads + 2; + ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)calloc(1, sizeof(ZSTDMT_bufferPool) + maxNbBuffers * sizeof(buffer_t)); + if (bufPool==NULL) return NULL; + bufPool->totalBuffers = maxNbBuffers; + return bufPool; +} + +static void ZSTDMT_freeBufferPool(ZSTDMT_bufferPool* bufPool) +{ + unsigned u; + if (!bufPool) return; /* compatibility with free on NULL */ + for (u=0; utotalBuffers; u++) + free(bufPool->bTable[u].start); + free(bufPool); +} + +/* note : invocation only from main thread ! */ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) { - PTHREAD_MUTEX_LOCK(&pool->bufferPool_mutex); if (pool->nbBuffers) { /* try to use an existing buffer */ pool->nbBuffers--; buffer_t const buf = pool->bTable[pool->nbBuffers]; - pthread_mutex_unlock(&pool->bufferPool_mutex); size_t const availBufferSize = buf.size; if ((availBufferSize >= bSize) & (availBufferSize <= 10*bSize)) /* large enough, but not too much */ return buf; free(buf.start); /* size conditions not respected : create a new buffer */ } - pthread_mutex_unlock(&pool->bufferPool_mutex); /* create new buffer */ buffer_t buf; buf.size = bSize; - buf.start = calloc(1, bSize); + buf.start = malloc(bSize); return buf; } /* effectively store buffer for later re-use, up to pool capacity */ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf) { - PTHREAD_MUTEX_LOCK(&pool->bufferPool_mutex); - if (pool->nbBuffers >= ZSTDMT_NBBUFFERSPOOLED_MAX) { - pthread_mutex_unlock(&pool->bufferPool_mutex); - free(buf.start); + if (pool->nbBuffers < pool->totalBuffers) { + pool->bTable[pool->nbBuffers++] = buf; /* store for later re-use */ return; } - pool->bTable[pool->nbBuffers++] = buf; /* store for later re-use */ - pthread_mutex_unlock(&pool->bufferPool_mutex); + /* Reached bufferPool capacity (should not happen) */ + free(buf.start); } @@ -118,7 +132,7 @@ void ZSTDMT_compressFrame(void* jobDescription) } -/* note : calls to CCtxPool only from main thread */ +/* === CCtx Pool === */ typedef struct { unsigned totalCCtx; @@ -126,6 +140,8 @@ typedef struct { ZSTD_CCtx* cctx[1]; /* variable size */ } ZSTDMT_CCtxPool; +/* note : CCtxPool invocation only from main thread */ + static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads) { ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) calloc(1, sizeof(ZSTDMT_CCtxPool) + nbThreads*sizeof(ZSTD_CCtx*)); @@ -168,7 +184,7 @@ static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool) struct ZSTDMT_CCtx_s { POOL_ctx* factory; - ZSTDMT_bufferPool buffPool; + ZSTDMT_bufferPool* buffPool; ZSTDMT_CCtxPool* cctxPool; unsigned nbThreads; pthread_mutex_t jobCompleted_mutex; @@ -182,7 +198,7 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) if (!cctx) return NULL; cctx->nbThreads = nbThreads; cctx->factory = POOL_create(nbThreads, 1); - pthread_mutex_init(&cctx->buffPool.bufferPool_mutex, NULL); + cctx->buffPool = ZSTDMT_createBufferPool(nbThreads); cctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads); pthread_mutex_init(&cctx->jobCompleted_mutex, NULL); return cctx; @@ -191,9 +207,9 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) /* incompleted ! */ { POOL_free(mtctx->factory); - /* free mutexes (if necessary) */ - /* free bufferPool */ + ZSTDMT_freeBufferPool(mtctx->buffPool); ZSTDMT_freeCCtxPool(mtctx->cctxPool); + pthread_mutex_destroy(&mtctx->jobCompleted_mutex); free(mtctx); return 0; } @@ -221,7 +237,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, for (u=0; ubuffPool, dstBufferCapacity) : (buffer_t){ dst, dstCapacity }; + buffer_t const dstBuffer = u ? ZSTDMT_getBuffer(mtctx->buffPool, dstBufferCapacity) : (buffer_t){ dst, dstCapacity }; ZSTD_CCtx* cctx = ZSTDMT_getCCtx(mtctx->cctxPool); mtctx->jobs[u].srcStart = srcStart + frameStartPos; @@ -252,13 +268,15 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, { size_t const cSize = mtctx->jobs[frameID].cSize; if (ZSTD_isError(cSize)) return cSize; if (dstPos + cSize > dstCapacity) return ERROR(dstSize_tooSmall); - if (frameID) memcpy((char*)dst + dstPos, mtctx->jobs[frameID].dstBuff.start, mtctx->jobs[frameID].cSize); + if (frameID) { + memcpy((char*)dst + dstPos, mtctx->jobs[frameID].dstBuff.start, cSize); + ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[frameID].dstBuff); + } dstPos += cSize ; } ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[frameID].cctx); - ZSTDMT_releaseBuffer(&mtctx->buffPool, mtctx->jobs[frameID].dstBuff); } - DEBUGLOG(4, "compressed size : %u ", (U32)dstPos); + DEBUGLOG(3, "compressed size : %u ", (U32)dstPos); return dstPos; } From 3b9d4343564233db19992950bbdb4a301d97f8f4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 31 Dec 2016 16:32:19 +0100 Subject: [PATCH 075/227] extended ZSTDMT code support for non-MT systems and WIN32 (preliminary) --- lib/common/pool.c | 7 +-- lib/common/threading.c | 73 +++++++++++++++++++++++++++++++ lib/common/threading.h | 79 ++++++++++++++++++++++++++++++++++ lib/compress/zstdmt_compress.c | 2 +- 4 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 lib/common/threading.c create mode 100644 lib/common/threading.h diff --git a/lib/common/pool.c b/lib/common/pool.c index 4ec1dfffb..97ca7ddab 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -13,7 +13,7 @@ #ifdef ZSTD_PTHREAD -#include +#include /* A job is a function and an opaque argument */ typedef struct POOL_job_s { @@ -161,7 +161,8 @@ void POOL_add(void *ctxVoid, POOL_function function, void *opaque) { pthread_cond_signal(&ctx->queuePopCond); } -#else +#else /* ZSTD_PTHREAD not defined */ +/* No multi-threading support */ /* We don't need any data, but if it is empty malloc() might return NULL. */ struct POOL_ctx_s { @@ -183,4 +184,4 @@ void POOL_add(void *ctx, POOL_function function, void *opaque) { function(opaque); } -#endif +#endif /* ZSTD_PTHREAD */ diff --git a/lib/common/threading.c b/lib/common/threading.c new file mode 100644 index 000000000..1725650c0 --- /dev/null +++ b/lib/common/threading.c @@ -0,0 +1,73 @@ + +/** + * Copyright (c) 2016 Tino Reichardt + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * You can contact the author at: + * - zstdmt source repository: https://github.com/mcmilk/zstdmt + */ + +/** + * This file will hold wrapper for systems, which do not support Pthreads + */ + +#ifdef _WIN32 + +/** + * Windows minimalist Pthread Wrapper, based on : + * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html + */ + + +/* === Dependencies === */ +#include +#include +#include "threading.h" + + +/* === Implementation === */ + +static unsigned __stdcall worker(void *arg) +{ + pthread_t* const thread = (pthread_t*) arg; + thread->arg = thread->start_routine(thread->arg); + return 0; +} + +int pthread_create(pthread_t* thread, const void* unused, + void* (*start_routine) (void*), void* arg) +{ + (void)unused; + thread->arg = arg; + thread->start_routine = start_routine; + thread->handle = (HANDLE) _beginthreadex(NULL, 0, worker, thread, 0, NULL); + + if (!thread->handle) + return errno; + else + return 0; +} + +int _pthread_join(pthread_t * thread, void **value_ptr) +{ + DWORD result; + + if (!thread->handle) return 0; + + result = WaitForSingleObject(thread->handle, INFINITE); + switch (result) { + case WAIT_OBJECT_0: + if (value_ptr) *value_ptr = thread->arg; + return 0; + case WAIT_ABANDONED: + return EINVAL; + default: + return GetLastError(); + } +} + +#endif diff --git a/lib/common/threading.h b/lib/common/threading.h new file mode 100644 index 000000000..a8126eb7b --- /dev/null +++ b/lib/common/threading.h @@ -0,0 +1,79 @@ + +/** + * Copyright (c) 2016 Tino Reichardt + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * You can contact the author at: + * - zstdmt source repository: https://github.com/mcmilk/zstdmt + */ + +#ifndef THREADING_H_938743 +#define THREADING_H_938743 + +#if defined (__cplusplus) +extern "C" { +#endif + +#if defined(ZSTD_PTHREAD) && defined(_WIN32) + +/** + * Windows minimalist Pthread Wrapper, based on : + * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html + */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +/* mutex */ +#define pthread_mutex_t CRITICAL_SECTION +#define pthread_mutex_init(a,b) InitializeCriticalSection((a)) +#define pthread_mutex_destroy(a) DeleteCriticalSection((a)) +#define pthread_mutex_lock EnterCriticalSection +#define pthread_mutex_unlock LeaveCriticalSection + +/* pthread_create() and pthread_join() */ +typedef struct { + HANDLE handle; + void* (*start_routine)(void*); + void*varg; +} pthread_t; + +int pthread_create(pthread_t* thread, const void* unused, + void* (*start_routine) (void*), void* arg); + +#define pthread_join(a, b) _pthread_join(&(a), (b)) +int _pthread_join(pthread_t* thread, void** value_ptr); + +/** + * add here more wrappers as required + */ + + +#elif defined(ZSTD_PTHREAD) /* posix assumed ; need a better detection mathod */ +/* === POSIX Systems === */ +# include + +#else /* ZSTD_PTHREAD not defined */ +/* No multithreading support */ + +typedef int pthread_mutex_t; +#define pthread_mutex_init(a,b) +#define pthread_mutex_destroy(a) +#define pthread_mutex_lock(a) +#define pthread_mutex_unlock(a) + +/* do not use pthread_t */ + +#endif /* ZSTD_PTHREAD */ + +#if defined (__cplusplus) +} +#endif + +#endif /* THREADING_H_938743 */ diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 97de6e645..770f59758 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1,6 +1,6 @@ #include /* malloc */ #include /* threadpool */ -#include /* mutex */ +#include "threading.h" /* mutex */ #include "zstd_internal.h" /* MIN, ERROR */ #include "zstdmt_compress.h" From d13243353465b386e333651e1ba63f5f4b30cdce Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Sat, 31 Dec 2016 19:10:13 -0500 Subject: [PATCH 076/227] Switch thread pool test to threading.h --- .travis.yml | 2 +- tests/Makefile | 8 +++++--- tests/pool.c | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 36537cbee..6bf99f1bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ matrix: os: linux sudo: false - - env: Ubu=12.04cont Cmd="make zlibwrapper && make clean && make -C tests test-pool && make -C tests test-symbols && make clean && make -C tests test-zstd-nolegacy && make clean && make cmaketest && make clean && make -C contrib/pzstd googletest pzstd tests check && make -C contrib/pzstd clean" + - env: Ubu=12.04cont Cmd="make zlibwrapper && make clean && make -C tests test-symbols && make clean && make -C tests test-zstd-nolegacy && make clean && make cmaketest && make clean && make -C contrib/pzstd googletest pzstd tests check && make -C contrib/pzstd clean" os: linux sudo: false language: cpp diff --git a/tests/Makefile b/tests/Makefile index 739944de8..6312584a9 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -48,8 +48,10 @@ ZDICT_FILES := $(ZSTDDIR)/dictBuilder/*.c # Define *.exe as extension for Windows systems ifneq (,$(filter Windows%,$(OS))) EXT =.exe +PTHREAD = -DZSTD_PTHREAD else EXT = +PTHREAD = -pthread -DZSTD_PTHREAD endif VOID = /dev/null @@ -158,8 +160,8 @@ else $(CC) $(FLAGS) $^ -o $@$(EXT) -Wl,-rpath=$(ZSTDDIR) $(ZSTDDIR)/libzstd.so endif -pool : pool.c $(ZSTDDIR)/common/pool.c - $(CC) $(FLAGS) -pthread -DZSTD_PTHREAD $^ -o $@$(EXT) +pool : pool.c $(ZSTDDIR)/common/pool.c $(ZSTDDIR)/common/threading.c + $(CC) $(FLAGS) $(PTHREAD) $^ -o $@$(EXT) namespaceTest: if $(CC) namespaceTest.c ../lib/common/xxhash.c -o $@ ; then echo compilation should fail; exit 1 ; fi @@ -225,7 +227,7 @@ zstd-playTests: datagen file $(ZSTD) ZSTD="$(QEMU_SYS) $(ZSTD)" ./playTests.sh $(ZSTDRTTEST) -test: test-zstd test-fullbench test-fuzzer test-zstream test-longmatch test-invalidDictionaries +test: test-zstd test-fullbench test-fuzzer test-zstream test-longmatch test-invalidDictionaries test-pool test32: test-zstd32 test-fullbench32 test-fuzzer32 test-zstream32 diff --git a/tests/pool.c b/tests/pool.c index ce38075d0..27414642d 100644 --- a/tests/pool.c +++ b/tests/pool.c @@ -1,5 +1,5 @@ #include "pool.h" -#include +#include "threading.h" #include #include @@ -31,7 +31,7 @@ int testOrder(size_t numThreads, size_t queueLog) { POOL_ctx *ctx = POOL_create(numThreads, queueLog); ASSERT_TRUE(ctx); data.i = 0; - ASSERT_FALSE(pthread_mutex_init(&data.mutex, NULL)); + pthread_mutex_init(&data.mutex, NULL); { size_t i; for (i = 0; i < 1024; ++i) { @@ -46,7 +46,7 @@ int testOrder(size_t numThreads, size_t queueLog) { ASSERT_EQ(i, data.data[i]); } } - ASSERT_FALSE(pthread_mutex_destroy(&data.mutex)); + pthread_mutex_destroy(&data.mutex); return 0; } From 4204e03e77113ccc22ebf805a8a8da60066b83e8 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Sat, 31 Dec 2016 19:10:29 -0500 Subject: [PATCH 077/227] Add threading.h condition variables --- lib/common/threading.c | 2 +- lib/common/threading.h | 31 ++++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/lib/common/threading.c b/lib/common/threading.c index 1725650c0..abad2c159 100644 --- a/lib/common/threading.c +++ b/lib/common/threading.c @@ -15,7 +15,7 @@ * This file will hold wrapper for systems, which do not support Pthreads */ -#ifdef _WIN32 +#if defined(ZSTD_PTHREAD) && defined(_WIN32) /** * Windows minimalist Pthread Wrapper, based on : diff --git a/lib/common/threading.h b/lib/common/threading.h index a8126eb7b..d5dc8f75a 100644 --- a/lib/common/threading.h +++ b/lib/common/threading.h @@ -24,24 +24,42 @@ extern "C" { * Windows minimalist Pthread Wrapper, based on : * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html */ +#ifdef WINVER +# undef WINVER +#endif +#define WINVER 0x0600 + +#ifdef _WIN32_WINNT +# undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0600 #ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif + #include /* mutex */ #define pthread_mutex_t CRITICAL_SECTION #define pthread_mutex_init(a,b) InitializeCriticalSection((a)) #define pthread_mutex_destroy(a) DeleteCriticalSection((a)) -#define pthread_mutex_lock EnterCriticalSection -#define pthread_mutex_unlock LeaveCriticalSection +#define pthread_mutex_lock(a) EnterCriticalSection((a)) +#define pthread_mutex_unlock(a) LeaveCriticalSection((a)) + +/* condition variable */ +#define pthread_cond_t CONDITION_VARIABLE +#define pthread_cond_init(a, b) InitializeConditionVariable((a)) +#define pthread_cond_destroy(a) /* No delete */ +#define pthread_cond_wait(a, b) SleepConditionVariableCS((a), (b), INFINITE) +#define pthread_cond_signal(a) WakeConditionVariable((a)) +#define pthread_cond_broadcast(a) WakeAllConditionVariable((a)) /* pthread_create() and pthread_join() */ typedef struct { HANDLE handle; void* (*start_routine)(void*); - void*varg; + void* arg; } pthread_t; int pthread_create(pthread_t* thread, const void* unused, @@ -68,6 +86,13 @@ typedef int pthread_mutex_t; #define pthread_mutex_lock(a) #define pthread_mutex_unlock(a) +typedef int pthread_cond_t; +#define pthread_cond_init(a,b) +#define pthread_cond_destroy(a) +#define pthread_cond_wait(a,b) +#define pthread_cond_signal(a) +#define pthread_cond_broadcast(a) + /* do not use pthread_t */ #endif /* ZSTD_PTHREAD */ From bb13387d7d04253713ed50c2e77c96df00a26d75 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Sat, 31 Dec 2016 19:10:47 -0500 Subject: [PATCH 078/227] Fix pool for threading.h --- lib/common/pool.c | 19 +++++++++---------- lib/common/pool.h | 1 + 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index 97ca7ddab..e24691f77 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -51,21 +51,21 @@ static void* POOL_thread(void* opaque) { if (!ctx) { return NULL; } for (;;) { /* Lock the mutex and wait for a non-empty queue or until shutdown */ - if (pthread_mutex_lock(&ctx->queueMutex)) { return NULL; } + pthread_mutex_lock(&ctx->queueMutex); while (ctx->queueHead == ctx->queueTail && !ctx->shutdown) { - if (pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex)) { return NULL; } + pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex); } /* empty => shutting down: so stop */ if (ctx->queueHead == ctx->queueTail) { - if (pthread_mutex_unlock(&ctx->queueMutex)) { return NULL; } + pthread_mutex_unlock(&ctx->queueMutex); return opaque; } /* Pop a job off the queue */ { POOL_job const job = ctx->queue[ctx->queueHead]; ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize; /* Unlock the mutex, signal a pusher, and run the job */ - if (pthread_mutex_unlock(&ctx->queueMutex)) { return NULL; } - if (pthread_cond_signal(&ctx->queuePushCond)) { return NULL; } + pthread_mutex_unlock(&ctx->queueMutex); + pthread_cond_signal(&ctx->queuePushCond); job.function(job.opaque); } } @@ -73,7 +73,6 @@ static void* POOL_thread(void* opaque) { } POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { - int err = 0; POOL_ctx *ctx; /* Check the parameters */ if (!numThreads || !queueSize) { return NULL; } @@ -88,15 +87,15 @@ POOL_ctx *POOL_create(size_t numThreads, size_t queueSize) { ctx->queue = (POOL_job *)malloc(ctx->queueSize * sizeof(POOL_job)); ctx->queueHead = 0; ctx->queueTail = 0; - err |= pthread_mutex_init(&ctx->queueMutex, NULL); - err |= pthread_cond_init(&ctx->queuePushCond, NULL); - err |= pthread_cond_init(&ctx->queuePopCond, NULL); + pthread_mutex_init(&ctx->queueMutex, NULL); + pthread_cond_init(&ctx->queuePushCond, NULL); + pthread_cond_init(&ctx->queuePopCond, NULL); ctx->shutdown = 0; /* Allocate space for the thread handles */ ctx->threads = (pthread_t *)malloc(numThreads * sizeof(pthread_t)); ctx->numThreads = 0; /* Check for errors */ - if (!ctx->threads || !ctx->queue || err) { POOL_free(ctx); return NULL; } + if (!ctx->threads || !ctx->queue) { POOL_free(ctx); return NULL; } /* Initialize the threads */ { size_t i; for (i = 0; i < numThreads; ++i) { diff --git a/lib/common/pool.h b/lib/common/pool.h index f4afc1ee3..c26f543fc 100644 --- a/lib/common/pool.h +++ b/lib/common/pool.h @@ -39,6 +39,7 @@ typedef void (*POOL_add_function)(void *, POOL_function, void *); /*! POOL_add() : Add the job `function(opaque)` to the thread pool. Possibly blocks until there is room in the queue. + Note : The function may be executed asynchronously, so `opaque` must live until the function has been completed. */ void POOL_add(void *ctx, POOL_function function, void *opaque); From 5ca0fd204548da3a0bdf524f5323045f154d4324 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Sat, 31 Dec 2016 22:39:32 -0500 Subject: [PATCH 079/227] Shorten thread pool tests --- tests/pool.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/pool.c b/tests/pool.c index 27414642d..adc5947df 100644 --- a/tests/pool.c +++ b/tests/pool.c @@ -14,7 +14,7 @@ struct data { pthread_mutex_t mutex; - unsigned data[1024]; + unsigned data[16]; size_t i; }; @@ -26,20 +26,20 @@ void fn(void *opaque) { pthread_mutex_unlock(&data->mutex); } -int testOrder(size_t numThreads, size_t queueLog) { +int testOrder(size_t numThreads, size_t queueSize) { struct data data; - POOL_ctx *ctx = POOL_create(numThreads, queueLog); + POOL_ctx *ctx = POOL_create(numThreads, queueSize); ASSERT_TRUE(ctx); data.i = 0; pthread_mutex_init(&data.mutex, NULL); { size_t i; - for (i = 0; i < 1024; ++i) { + for (i = 0; i < 16; ++i) { POOL_add(ctx, &fn, &data); } } POOL_free(ctx); - ASSERT_EQ(1024, data.i); + ASSERT_EQ(16, data.i); { size_t i; for (i = 0; i < data.i; ++i) { @@ -52,19 +52,19 @@ int testOrder(size_t numThreads, size_t queueLog) { int main(int argc, const char **argv) { size_t numThreads; - for (numThreads = 1; numThreads <= 8; ++numThreads) { - size_t queueLog; - for (queueLog = 1; queueLog <= 8; ++queueLog) { - if (testOrder(numThreads, queueLog)) { + for (numThreads = 1; numThreads <= 4; ++numThreads) { + size_t queueSize; + for (queueSize = 1; queueSize <= 2; ++queueSize) { + if (testOrder(numThreads, queueSize)) { printf("FAIL: testOrder\n"); return 1; } } } printf("PASS: testOrder\n"); - (POOL_create(0, 1) || POOL_create(1, 0)) ? printf("FAIL: testInvalid\n") - : printf("PASS: testInvalid\n"); (void)argc; (void)argv; + return (POOL_create(0, 1) || POOL_create(1, 0)) ? printf("FAIL: testInvalid\n"), 1 + : printf("PASS: testInvalid\n"), 0; return 0; } From 2ec635a16236e53014ce9ee69a01cdbf8ca77836 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 1 Jan 2017 17:31:33 +0100 Subject: [PATCH 080/227] use pthread_cond to send signals between threads --- lib/compress/zstdmt_compress.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 770f59758..b9cc81f67 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -115,6 +115,7 @@ typedef struct { size_t cSize; unsigned jobCompleted; pthread_mutex_t* jobCompleted_mutex; + pthread_cond_t* jobCompleted_cond; } ZSTDMT_jobDescription; /* ZSTDMT_compressFrame() : POOL_function type */ @@ -126,7 +127,9 @@ void ZSTDMT_compressFrame(void* jobDescription) job->cSize = ZSTD_compressCCtx(job->cctx, job->dstBuff.start, job->dstBuff.size, job->srcStart, job->srcSize, job->compressionLevel); DEBUGLOG(5, "compressed to %u bytes ", (unsigned)job->cSize); job->jobCompleted = 1; - DEBUGLOG(5, "unlocking mutex jobCompleted_mutex"); + DEBUGLOG(5, "sending jobCompleted signal"); + pthread_mutex_lock(job->jobCompleted_mutex); + pthread_cond_signal(job->jobCompleted_cond); pthread_mutex_unlock(job->jobCompleted_mutex); DEBUGLOG(5, "ZSTDMT_compressFrame completed"); } @@ -188,6 +191,7 @@ struct ZSTDMT_CCtx_s { ZSTDMT_CCtxPool* cctxPool; unsigned nbThreads; pthread_mutex_t jobCompleted_mutex; + pthread_cond_t jobCompleted_cond; ZSTDMT_jobDescription jobs[1]; /* variable size */ }; @@ -201,6 +205,7 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) cctx->buffPool = ZSTDMT_createBufferPool(nbThreads); cctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads); pthread_mutex_init(&cctx->jobCompleted_mutex, NULL); + pthread_cond_init(&cctx->jobCompleted_cond, NULL); return cctx; } @@ -248,6 +253,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, mtctx->jobs[u].frameID = u; mtctx->jobs[u].jobCompleted = 0; mtctx->jobs[u].jobCompleted_mutex = &mtctx->jobCompleted_mutex; + mtctx->jobs[u].jobCompleted_cond = &mtctx->jobCompleted_cond; DEBUGLOG(3, "posting job %u (%u bytes)", u, (U32)frameSize); POOL_add(mtctx->factory, ZSTDMT_compressFrame, &mtctx->jobs[u]); @@ -261,10 +267,14 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, size_t dstPos = 0; for (frameID=0; frameIDjobCompleted_mutex); while (mtctx->jobs[frameID].jobCompleted==0) { - DEBUGLOG(4, "waiting for signal jobCompleted_mutex") - pthread_mutex_lock(&mtctx->jobCompleted_mutex); + DEBUGLOG(4, "waiting for jobCompleted signal for frame %u", frameID); + pthread_cond_wait(&mtctx->jobCompleted_cond, &mtctx->jobCompleted_mutex); } + pthread_mutex_unlock(&mtctx->jobCompleted_mutex); + { size_t const cSize = mtctx->jobs[frameID].cSize; if (ZSTD_isError(cSize)) return cSize; if (dstPos + cSize > dstCapacity) return ERROR(dstSize_tooSmall); From 0ec6a95ba126978567fafc02f839064c60b74b9d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 2 Jan 2017 00:49:42 +0100 Subject: [PATCH 081/227] minor fixes --- lib/compress/zstdmt_compress.c | 5 +++-- lib/zstd.h | 6 +++--- programs/bench.c | 11 +++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index b9cc81f67..294ce86d4 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -123,12 +123,12 @@ void ZSTDMT_compressFrame(void* jobDescription) { DEBUGLOG(5, "Entering ZSTDMT_compressFrame() "); ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; - DEBUGLOG(5, "compressing %u bytes with ZSTD_compressCCtx : ", (unsigned)job->srcSize); + DEBUGLOG(5, "compressing %u bytes from frame %u with ZSTD_compressCCtx : ", (unsigned)job->srcSize, job->jobCompleted); job->cSize = ZSTD_compressCCtx(job->cctx, job->dstBuff.start, job->dstBuff.size, job->srcStart, job->srcSize, job->compressionLevel); DEBUGLOG(5, "compressed to %u bytes ", (unsigned)job->cSize); - job->jobCompleted = 1; DEBUGLOG(5, "sending jobCompleted signal"); pthread_mutex_lock(job->jobCompleted_mutex); + job->jobCompleted = 1; pthread_cond_signal(job->jobCompleted_cond); pthread_mutex_unlock(job->jobCompleted_mutex); DEBUGLOG(5, "ZSTDMT_compressFrame completed"); @@ -215,6 +215,7 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) /* incompleted ! */ ZSTDMT_freeBufferPool(mtctx->buffPool); ZSTDMT_freeCCtxPool(mtctx->cctxPool); pthread_mutex_destroy(&mtctx->jobCompleted_mutex); + pthread_cond_destroy(&mtctx->jobCompleted_cond); free(mtctx); return 0; } diff --git a/lib/zstd.h b/lib/zstd.h index 333feff7d..55cc466d7 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -369,9 +369,9 @@ typedef struct { } ZSTD_compressionParameters; typedef struct { - unsigned contentSizeFlag; /**< 1: content size will be in frame header (if known). */ - unsigned checksumFlag; /**< 1: will generate a 22-bits checksum at end of frame, to be used for error detection by decompressor */ - unsigned noDictIDFlag; /**< 1: no dict ID will be saved into frame header (if dictionary compression) */ + unsigned contentSizeFlag; /**< 1: content size will be in frame header (when known) */ + unsigned checksumFlag; /**< 1: generate a 32-bits checksum at end of frame, for error detection */ + unsigned noDictIDFlag; /**< 1: no dictID will be saved into frame header (if dictionary compression) */ } ZSTD_frameParameters; typedef struct { diff --git a/programs/bench.c b/programs/bench.c index c718e2199..e846e9ef3 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -91,12 +91,12 @@ static clock_t g_time = 0; /* ************************************* * Time ***************************************/ -/* for posix only - proper detection macros to setup */ +/* for posix only - needs proper detection macros to setup */ #include #include typedef unsigned long long clock_us_t; -static clock_us_t BMK_clockMicroSec() +static clock_us_t BMK_clockMicroSec(void) { static clock_t _ticksPerSecond = 0; if (_ticksPerSecond <= 0) _ticksPerSecond = sysconf(_SC_CLK_TCK); @@ -235,7 +235,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, /* Bench */ { U64 fastestC = (U64)(-1LL), fastestD = (U64)(-1LL); U64 const crcOrig = g_decodeOnly ? 0 : XXH64(srcBuffer, srcSize, 0); - UTIL_time_t coolTime; + clock_us_t coolTime = BMK_clockMicroSec(); U64 const maxTime = (g_nbSeconds * TIMELOOP_MICROSEC) + 1; U64 totalCTime=0, totalDTime=0; U32 cCompleted=g_decodeOnly, dCompleted=0; @@ -245,15 +245,14 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, ZSTDMT_CCtx* const mtcctx = ZSTDMT_createCCtx(g_nbThreads); - UTIL_getTime(&coolTime); DISPLAYLEVEL(2, "\r%79s\r", ""); while (!cCompleted || !dCompleted) { /* overheat protection */ - if (UTIL_clockSpanMicro(coolTime, ticksPerSecond) > ACTIVEPERIOD_MICROSEC) { + if (BMK_clockMicroSec() - coolTime > ACTIVEPERIOD_MICROSEC) { DISPLAYLEVEL(2, "\rcooling down ... \r"); UTIL_sleep(COOLPERIOD_SEC); - UTIL_getTime(&coolTime); + coolTime = BMK_clockMicroSec(); } if (!g_decodeOnly) { From f1cb55192c3b61768678a748a60e9b83f98133f3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 2 Jan 2017 01:11:55 +0100 Subject: [PATCH 082/227] fixed linux warnings --- lib/common/threading.h | 4 ++-- lib/compress/zstdmt_compress.c | 18 +++++++++--------- programs/bench.c | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/common/threading.h b/lib/common/threading.h index d5dc8f75a..4572d71d5 100644 --- a/lib/common/threading.h +++ b/lib/common/threading.h @@ -80,13 +80,13 @@ int _pthread_join(pthread_t* thread, void** value_ptr); #else /* ZSTD_PTHREAD not defined */ /* No multithreading support */ -typedef int pthread_mutex_t; +#define pthread_mutex_t int /* #define rather than typedef, as sometimes pthread support is implicit, resulting in duplicated symbols */ #define pthread_mutex_init(a,b) #define pthread_mutex_destroy(a) #define pthread_mutex_lock(a) #define pthread_mutex_unlock(a) -typedef int pthread_cond_t; +#define pthread_cond_t int #define pthread_cond_init(a,b) #define pthread_cond_destroy(a) #define pthread_cond_wait(a,b) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 294ce86d4..dd495c988 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -78,18 +78,18 @@ static void ZSTDMT_freeBufferPool(ZSTDMT_bufferPool* bufPool) static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) { if (pool->nbBuffers) { /* try to use an existing buffer */ - pool->nbBuffers--; - buffer_t const buf = pool->bTable[pool->nbBuffers]; + buffer_t const buf = pool->bTable[--(pool->nbBuffers)]; size_t const availBufferSize = buf.size; if ((availBufferSize >= bSize) & (availBufferSize <= 10*bSize)) /* large enough, but not too much */ return buf; free(buf.start); /* size conditions not respected : create a new buffer */ } /* create new buffer */ - buffer_t buf; - buf.size = bSize; - buf.start = malloc(bSize); - return buf; + { buffer_t buf; + buf.size = bSize; + buf.start = malloc(bSize); + return buf; + } } /* effectively store buffer for later re-use, up to pool capacity */ @@ -121,9 +121,8 @@ typedef struct { /* ZSTDMT_compressFrame() : POOL_function type */ void ZSTDMT_compressFrame(void* jobDescription) { - DEBUGLOG(5, "Entering ZSTDMT_compressFrame() "); ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; - DEBUGLOG(5, "compressing %u bytes from frame %u with ZSTD_compressCCtx : ", (unsigned)job->srcSize, job->jobCompleted); + DEBUGLOG(5, "thread : compressing %u bytes from frame %u with ZSTD_compressCCtx : ", (unsigned)job->srcSize, job->jobCompleted); job->cSize = ZSTD_compressCCtx(job->cctx, job->dstBuff.start, job->dstBuff.size, job->srcStart, job->srcSize, job->compressionLevel); DEBUGLOG(5, "compressed to %u bytes ", (unsigned)job->cSize); DEBUGLOG(5, "sending jobCompleted signal"); @@ -197,8 +196,9 @@ struct ZSTDMT_CCtx_s { ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) { + ZSTDMT_CCtx* cctx; if ((nbThreads < 1) | (nbThreads > ZSTDMT_NBTHREADS_MAX)) return NULL; - ZSTDMT_CCtx* const cctx = (ZSTDMT_CCtx*) calloc(1, sizeof(ZSTDMT_CCtx) + nbThreads*sizeof(ZSTDMT_jobDescription)); + cctx = (ZSTDMT_CCtx*) calloc(1, sizeof(ZSTDMT_CCtx) + nbThreads*sizeof(ZSTDMT_jobDescription)); if (!cctx) return NULL; cctx->nbThreads = nbThreads; cctx->factory = POOL_create(nbThreads, 1); diff --git a/programs/bench.c b/programs/bench.c index e846e9ef3..a3c013a8b 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -101,8 +101,8 @@ static clock_us_t BMK_clockMicroSec(void) static clock_t _ticksPerSecond = 0; if (_ticksPerSecond <= 0) _ticksPerSecond = sysconf(_SC_CLK_TCK); - struct tms junk; clock_t newTicks = (clock_t) times(&junk); (void)junk; - return ((((clock_us_t)newTicks)*(1000000))/_ticksPerSecond); + { struct tms junk; clock_t newTicks = (clock_t) times(&junk); (void)junk; + return ((((clock_us_t)newTicks)*(1000000))/_ticksPerSecond); } } From cdb2763f4a325c629794bde2816aeba1fe09f06a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 2 Jan 2017 01:43:56 +0100 Subject: [PATCH 083/227] new Makefile target zstdmt --- programs/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/programs/Makefile b/programs/Makefile index 6bd0014a6..4e3510e4b 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -130,6 +130,10 @@ gzstd: clean_decomp_o && $(MAKE) zstd; \ fi +zstdmt: CPPFLAGS += -DZSTD_PTHREAD +zstdmt: LDFLAGS += -lpthread +zstdmt: zstd + generate_res: windres/generate_res.bat @@ -164,7 +168,7 @@ ifneq (,$(filter $(shell uname),OpenBSD FreeBSD NetBSD DragonFly SunOS)) MANDIR ?= $(PREFIX)/man/man1 else MANDIR ?= $(PREFIX)/share/man/man1 -endif +endif INSTALL_PROGRAM ?= $(INSTALL) -m 755 INSTALL_SCRIPT ?= $(INSTALL) -m 755 From 747452677d6eca37578ee77307c652f4135909d7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 2 Jan 2017 02:05:45 +0100 Subject: [PATCH 084/227] fixed cmake tests --- build/cmake/lib/CMakeLists.txt | 37 ++++++++--------------------- build/cmake/programs/CMakeLists.txt | 34 ++++++-------------------- programs/zstdcli.c | 12 +++++----- 3 files changed, 23 insertions(+), 60 deletions(-) diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index 41fe2733f..dce39aba1 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -1,30 +1,10 @@ # ################################################################ -# zstd - Makefile -# Copyright (C) Yann Collet 2014-2016 -# All rights reserved. -# -# BSD license -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright notice, this -# list of conditions and the following disclaimer in the documentation and/or -# other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# * Copyright (c) 2014-present, Yann Collet, Facebook, Inc. +# * All rights reserved. +# * +# * This source code is licensed under the BSD-style license found in the +# * LICENSE file in the root directory of this source tree. An additional grant +# * of patent rights can be found in the PATENTS file in the same directory. # # You can contact the author at : # - zstd homepage : http://www.zstd.net/ @@ -58,13 +38,16 @@ MESSAGE("ZSTD VERSION ${LIBVER_MAJOR}.${LIBVER_MINOR}.${LIBVER_RELEASE}") SET(Sources ${LIBRARY_DIR}/common/entropy_common.c + ${LIBRARY_DIR}/common/fse_decompress.c + ${LIBRARY_DIR}/common/threading.c + ${LIBRARY_DIR}/common/pool.c ${LIBRARY_DIR}/common/zstd_common.c ${LIBRARY_DIR}/common/error_private.c ${LIBRARY_DIR}/common/xxhash.c - ${LIBRARY_DIR}/common/fse_decompress.c ${LIBRARY_DIR}/compress/fse_compress.c ${LIBRARY_DIR}/compress/huf_compress.c ${LIBRARY_DIR}/compress/zstd_compress.c + ${LIBRARY_DIR}/compress/zstdmt_compress.c ${LIBRARY_DIR}/decompress/huf_decompress.c ${LIBRARY_DIR}/decompress/zstd_decompress.c ${LIBRARY_DIR}/dictBuilder/divsufsort.c diff --git a/build/cmake/programs/CMakeLists.txt b/build/cmake/programs/CMakeLists.txt index c2931b096..9b3c3acc9 100644 --- a/build/cmake/programs/CMakeLists.txt +++ b/build/cmake/programs/CMakeLists.txt @@ -1,30 +1,10 @@ # ################################################################ -# zstd - Makefile -# Copyright (C) Yann Collet 2014-2016 -# All rights reserved. -# -# BSD license -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright notice, this -# list of conditions and the following disclaimer in the documentation and/or -# other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# * Copyright (c) 2015-present, Yann Collet, Facebook, Inc. +# * All rights reserved. +# * +# * This source code is licensed under the BSD-style license found in the +# * LICENSE file in the root directory of this source tree. An additional grant +# * of patent rights can be found in the PATENTS file in the same directory. # # You can contact the author at : # - zstd homepage : http://www.zstd.net/ @@ -40,7 +20,7 @@ SET(ROOT_DIR ../../..) # Define programs directory, where sources and header files are located SET(LIBRARY_DIR ${ROOT_DIR}/lib) SET(PROGRAMS_DIR ${ROOT_DIR}/programs) -INCLUDE_DIRECTORIES(${PROGRAMS_DIR} ${LIBRARY_DIR} ${LIBRARY_DIR}/common ${LIBRARY_DIR}/dictBuilder) +INCLUDE_DIRECTORIES(${PROGRAMS_DIR} ${LIBRARY_DIR} ${LIBRARY_DIR}/common ${LIBRARY_DIR}/compression ${LIBRARY_DIR}/dictBuilder) IF (ZSTD_LEGACY_SUPPORT) SET(PROGRAMS_LEGACY_DIR ${PROGRAMS_DIR}/legacy) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 03ad1ac7e..0474c96c4 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -422,6 +422,12 @@ int main(int argCount, const char* argv[]) BMK_SetBlockSize(bSize); } break; + + /* nb of threads (hidden option) */ + case 'T': + argument++; + BMK_SetNbThreads(readU32FromChar(&argument)); + break; #endif /* ZSTD_NOBENCH */ /* Dictionary Selection level */ @@ -430,12 +436,6 @@ int main(int argCount, const char* argv[]) dictSelect = readU32FromChar(&argument); break; - /* nb of threads (hidden option) */ - case 'T': - argument++; - BMK_SetNbThreads(readU32FromChar(&argument)); - break; - /* Pause at the end (-p) or set an additional param (-p#) (hidden option) */ case 'p': argument++; #ifndef ZSTD_NOBENCH From 6334b04d6123cd426c1bcc69e0167331c9f098d4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 2 Jan 2017 03:22:18 +0100 Subject: [PATCH 085/227] compile object files, for faster recompilation --- lib/Makefile | 9 +++++---- programs/Makefile | 31 ++++++++++++++----------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index efd3b87fe..34363b7b4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -36,6 +36,8 @@ CPPFLAGS += -I./legacy -DZSTD_LEGACY_SUPPORT=1 ZSTD_FILES+= $(wildcard legacy/*.c) endif +ZSTD_OBJ := $(patsubst %.c,%.o,$(ZSTD_FILES)) + # OS X linker doesn't support -soname, and use different extension # see : https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/DynamicLibraryDesignGuidelines.html ifeq ($(shell uname), Darwin) @@ -60,10 +62,9 @@ default: lib all: lib libzstd.a: ARFLAGS = rcs -libzstd.a: $(ZSTD_FILES) +libzstd.a: $(ZSTD_OBJ) @echo compiling static library - @$(CC) $(FLAGS) -c $^ - @$(AR) $(ARFLAGS) $@ *.o + @$(AR) $(ARFLAGS) $@ $^ $(LIBZSTD): LDFLAGS += -shared -fPIC -fvisibility=hidden $(LIBZSTD): $(ZSTD_FILES) @@ -84,7 +85,7 @@ lib: libzstd.a libzstd clean: @$(RM) core *.o *.a *.gcda *.$(SHARED_EXT) *.$(SHARED_EXT).* libzstd.pc dll/libzstd.dll dll/libzstd.lib - @$(RM) decompress/*.o + @$(RM) common/*.o compress/*.o decompress/*.o dictBuilder/*.o legacy/*.o deprecated/*.o @echo Cleaning library completed #----------------------------------------------------------------------------- diff --git a/programs/Makefile b/programs/Makefile index 4e3510e4b..77d5ab6e4 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -1,5 +1,5 @@ # ########################################################################## -# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. +# Copyright (c) 2015-present, Yann Collet, Facebook, Inc. # All rights reserved. # # This source code is licensed under the BSD-style license found in the @@ -33,7 +33,7 @@ FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c ZSTDCOMP_FILES := $(ZSTDDIR)/compress/*.c -ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/huf_decompress.c +ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/*.c ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) ZDICT_FILES := $(ZSTDDIR)/dictBuilder/*.c ZSTDDECOMP_O = $(ZSTDDIR)/decompress/zstd_decompress.o @@ -47,6 +47,8 @@ CPPFLAGS += -I$(ZSTDDIR)/legacy ZSTDLEGACY_FILES:= $(ZSTDDIR)/legacy/*.c endif +ZSTDLIB_FILES := $(wildcard $(ZSTD_FILES)) $(wildcard $(ZSTDLEGACY_FILES)) $(wildcard $(ZDICT_FILES)) +ZSTDLIB_OBJ := $(patsubst %.c,%.o,$(ZSTDLIB_FILES)) # Define *.exe as extension for Windows systems ifneq (,$(filter Windows%,$(OS))) @@ -72,8 +74,7 @@ all: zstd $(ZSTDDECOMP_O): CFLAGS += $(ALIGN_LOOP) zstd : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) -zstd : $(ZSTDDECOMP_O) $(ZSTD_FILES) $(ZSTDLEGACY_FILES) $(ZDICT_FILES) \ - zstdcli.c fileio.c bench.c datagen.c dibio.c +zstd : $(ZSTDLIB_OBJ) zstdcli.o fileio.o bench.o datagen.o dibio.o ifneq (,$(filter Windows%,$(OS))) windres/generate_res.bat endif @@ -81,8 +82,7 @@ endif zstd32 : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) -zstd32 : $(ZSTDDIR)/decompress/zstd_decompress.c $(ZSTD_FILES) $(ZSTDLEGACY_FILES) $(ZDICT_FILES) \ - zstdcli.c fileio.c bench.c datagen.c dibio.c +zstd32 : $(ZSTDLIB_FILES) zstdcli.c fileio.c bench.c datagen.c dibio.c ifneq (,$(filter Windows%,$(OS))) windres/generate_res.bat endif @@ -104,26 +104,23 @@ zstd-pgo : clean zstd $(RM) $(ZSTDDECOMP_O) $(MAKE) zstd MOREFLAGS=-fprofile-use -zstd-frugal: $(ZSTDDECOMP_O) $(ZSTD_FILES) zstdcli.c fileio.c +zstd-frugal: $(ZSTD_FILES) zstdcli.c fileio.c $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT $^ -o zstd$(EXT) -zstd-small: clean_decomp_o - ZSTD_LEGACY_SUPPORT=0 CFLAGS="-Os -s" $(MAKE) zstd-frugal +zstd-small: + CFLAGS="-Os -s" $(MAKE) zstd-frugal -zstd-decompress-clean: $(ZSTDDECOMP_O) $(ZSTDCOMMON_FILES) $(ZSTDDECOMP_FILES) zstdcli.c fileio.c - $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NOCOMPRESS $^ -o zstd-decompress$(EXT) - -zstd-decompress: clean_decomp_o - ZSTD_LEGACY_SUPPORT=0 $(MAKE) zstd-decompress-clean +zstd-decompress: $(ZSTDCOMMON_FILES) $(ZSTDDECOMP_FILES) zstdcli.c fileio.c + $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NOCOMPRESS $^ -o $@$(EXT) zstd-compress: $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) zstdcli.c fileio.c $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NODECOMPRESS $^ -o $@$(EXT) -gzstd: clean_decomp_o +gzstd: @echo "int main(){}" | $(CC) -o have_zlib -x c - -lz && echo found zlib || echo did not found zlib @if [ -s have_zlib ]; then \ echo building gzstd with .gz decompression support \ - && rm have_zlib$(EXT) \ + && $(RM) have_zlib$(EXT) fileio.o \ && CPPFLAGS=-DZSTD_GZDECOMPRESS LDFLAGS="-lz" $(MAKE) zstd; \ else \ echo "WARNING : no zlib, building gzstd with only .zst files support : NO .gz SUPPORT !!!" \ @@ -132,7 +129,7 @@ gzstd: clean_decomp_o zstdmt: CPPFLAGS += -DZSTD_PTHREAD zstdmt: LDFLAGS += -lpthread -zstdmt: zstd +zstdmt: clean zstd generate_res: windres/generate_res.bat From 96b39f65fa0e11664c64b5b82bbfdd433125d203 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Sat, 31 Dec 2016 21:07:44 -0800 Subject: [PATCH 086/227] Add COVER dictionary builder --- lib/dictBuilder/cover.c | 1041 +++++++++++++++++++++++++++++++++++++++ lib/dictBuilder/zdict.h | 52 ++ 2 files changed, 1093 insertions(+) create mode 100644 lib/dictBuilder/cover.c diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c new file mode 100644 index 000000000..1bad01869 --- /dev/null +++ b/lib/dictBuilder/cover.c @@ -0,0 +1,1041 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/*-************************************* +* Dependencies +***************************************/ +#include /* fprintf */ +#include /* malloc, free, qsort */ +#include /* memset */ +#include /* clock */ +#ifdef ZSTD_PTHREAD +#include "threading.h" +#endif + +#include "mem.h" /* read */ +#include "zstd_internal.h" /* includes zstd.h */ +#ifndef ZDICT_STATIC_LINKING_ONLY +#define ZDICT_STATIC_LINKING_ONLY +#endif +#include "zdict.h" + +/*-************************************* +* Constants +***************************************/ +#define COVER_MAX_SAMPLES_SIZE ((U32)-1) + +/*-************************************* +* Console display +***************************************/ +static int g_displayLevel = 2; +#define DISPLAY(...) \ + { \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } +#define LOCALDISPLAYLEVEL(displayLevel, l, ...) \ + if (displayLevel >= l) { \ + DISPLAY(__VA_ARGS__); \ + } /* 0 : no display; 1: errors; 2: default; 3: details; 4: debug */ +#define DISPLAYLEVEL(l, ...) LOCALDISPLAYLEVEL(g_displayLevel, l, __VA_ARGS__) + +#define LOCALDISPLAYUPDATE(displayLevel, l, ...) \ + if (displayLevel >= l) { \ + if ((clock() - g_time > refreshRate) || (displayLevel >= 4)) { \ + g_time = clock(); \ + DISPLAY(__VA_ARGS__); \ + if (displayLevel >= 4) \ + fflush(stdout); \ + } \ + } +#define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__) +static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; +static clock_t g_time = 0; + +/*-************************************* +* Hash table +*************************************** +* A small specialized hash map for storing activeDmers. +* The map does not resize, so if it becomes full it will loop forever. +* Thus, the map must be large enough to store every value. +* The map implements linear probing and keeps its load less than 0.5. +*/ + +#define MAP_EMPTY_VALUE ((U32)-1) +typedef struct COVER_map_pair_t_s { + U32 key; + U32 value; +} COVER_map_pair_t; + +typedef struct COVER_map_s { + COVER_map_pair_t *data; + U32 sizeLog; + U32 size; + U32 sizeMask; +} COVER_map_t; + +/** + * Clear the map. + */ +static void COVER_map_clear(COVER_map_t *map) { + memset(map->data, MAP_EMPTY_VALUE, map->size * sizeof(COVER_map_pair_t)); +} + +/** + * Initializes a map of the given size. + * Returns 1 on success and 0 on failure. + * The map must be destroyed with COVER_map_destroy(). + * The map is only guaranteed to be large enough to hold size elements. + */ +static int COVER_map_init(COVER_map_t *map, U32 size) { + map->sizeLog = ZSTD_highbit32(size) + 2; + map->size = (U32)1 << map->sizeLog; + map->sizeMask = map->size - 1; + map->data = (COVER_map_pair_t *)malloc(map->size * sizeof(COVER_map_pair_t)); + if (!map->data) { + map->sizeLog = 0; + map->size = 0; + return 0; + } + COVER_map_clear(map); + return 1; +} + +/** + * Internal hash function + */ +static const U32 prime4bytes = 2654435761U; +static U32 COVER_map_hash(COVER_map_t *map, U32 key) { + return (key * prime4bytes) >> (32 - map->sizeLog); +} + +/** + * Helper function that returns the index that a key should be placed into. + */ +static U32 COVER_map_index(COVER_map_t *map, U32 key) { + const U32 hash = COVER_map_hash(map, key); + U32 i; + for (i = hash;; i = (i + 1) & map->sizeMask) { + COVER_map_pair_t *pos = &map->data[i]; + if (pos->value == MAP_EMPTY_VALUE) { + return i; + } + if (pos->key == key) { + return i; + } + } +} + +/** + * Returns the pointer to the value for key. + * If key is not in the map, it is inserted and the value is set to 0. + * The map must not be full. + */ +static U32 *COVER_map_at(COVER_map_t *map, U32 key) { + COVER_map_pair_t *pos = &map->data[COVER_map_index(map, key)]; + if (pos->value == MAP_EMPTY_VALUE) { + pos->key = key; + pos->value = 0; + } + return &pos->value; +} + +/** + * Deletes key from the map if present. + */ +static void COVER_map_remove(COVER_map_t *map, U32 key) { + U32 i = COVER_map_index(map, key); + COVER_map_pair_t *del = &map->data[i]; + U32 shift = 1; + if (del->value == MAP_EMPTY_VALUE) { + return; + } + for (i = (i + 1) & map->sizeMask;; i = (i + 1) & map->sizeMask) { + COVER_map_pair_t *const pos = &map->data[i]; + /* If the position is empty we are done */ + if (pos->value == MAP_EMPTY_VALUE) { + del->value = MAP_EMPTY_VALUE; + return; + } + /* If pos can be moved to del do so */ + if (((i - COVER_map_hash(map, pos->key)) & map->sizeMask) >= shift) { + del->key = pos->key; + del->value = pos->value; + del = pos; + shift = 1; + } else { + ++shift; + } + } +} + +/** + * Destroyes a map that is inited with COVER_map_init(). + */ +static void COVER_map_destroy(COVER_map_t *map) { + if (map->data) { + free(map->data); + } + map->data = NULL; + map->size = 0; +} + +/*-************************************* +* Context +***************************************/ + +typedef struct { + const BYTE *samples; + size_t *offsets; + const size_t *samplesSizes; + size_t nbSamples; + U32 *suffix; + size_t suffixSize; + U32 *freqs; + U32 *dmerAt; + unsigned d; +} COVER_ctx_t; + +/* We need a global context for qsort... */ +static COVER_ctx_t *g_ctx = NULL; + +/*-************************************* +* Helper functions +***************************************/ + +/** + * Returns the sum of the sample sizes. + */ +static size_t COVER_sum(const size_t *samplesSizes, unsigned nbSamples) { + size_t sum = 0; + size_t i; + for (i = 0; i < nbSamples; ++i) { + sum += samplesSizes[i]; + } + return sum; +} + +/** + * Returns -1 if the dmer at lp is less than the dmer at rp. + * Return 0 if the dmers at lp and rp are equal. + * Returns 1 if the dmer at lp is greater than the dmer at rp. + */ +static int COVER_cmp(COVER_ctx_t *ctx, const void *lp, const void *rp) { + const U32 lhs = *(const U32 *)lp; + const U32 rhs = *(const U32 *)rp; + return memcmp(ctx->samples + lhs, ctx->samples + rhs, ctx->d); +} + +/** + * Same as COVER_cmp() except ties are broken by pointer value + * NOTE: g_ctx must be set to call this function. A global is required because + * qsort doesn't take an opaque pointer. + */ +static int COVER_strict_cmp(const void *lp, const void *rp) { + int result = COVER_cmp(g_ctx, lp, rp); + if (result == 0) { + result = lp < rp ? -1 : 1; + } + return result; +} + +/** + * Returns the first pointer in [first, last) whose element does not compare + * less than value. If no such element exists it returns last. + */ +static const size_t *COVER_lower_bound(const size_t *first, const size_t *last, + size_t value) { + size_t count = last - first; + while (count != 0) { + size_t step = count / 2; + const size_t *ptr = first; + ptr += step; + if (*ptr < value) { + first = ++ptr; + count -= step + 1; + } else { + count = step; + } + } + return first; +} + +/** + * Generic groupBy function. + * Groups an array sorted by cmp into groups with equivalent values. + * Calls grp for each group. + */ +static void +COVER_groupBy(const void *data, size_t count, size_t size, COVER_ctx_t *ctx, + int (*cmp)(COVER_ctx_t *, const void *, const void *), + void (*grp)(COVER_ctx_t *, const void *, const void *)) { + const BYTE *ptr = (const BYTE *)data; + size_t num = 0; + while (num < count) { + const BYTE *grpEnd = ptr + size; + ++num; + while (num < count && cmp(ctx, ptr, grpEnd) == 0) { + grpEnd += size; + ++num; + } + grp(ctx, ptr, grpEnd); + ptr = grpEnd; + } +} + +/*-************************************* +* Cover functions +***************************************/ + +/** + * Called on each group of positions with the same dmer. + * Counts the frequency of each dmer and saves it in the suffix array. + * Fills `ctx->dmerAt`. + */ +static void COVER_group(COVER_ctx_t *ctx, const void *group, + const void *groupEnd) { + /* The group consists of all the positions with the same first d bytes. */ + const U32 *grpPtr = (const U32 *)group; + const U32 *grpEnd = (const U32 *)groupEnd; + /* The dmerId is how we will reference this dmer. + * This allows us to map the whole dmer space to a much smaller space, the + * size of the suffix array. + */ + const U32 dmerId = (U32)(grpPtr - ctx->suffix); + /* Count the number of samples this dmer shows up in */ + U32 freq = 0; + /* Details */ + const size_t *curOffsetPtr = ctx->offsets; + const size_t *offsetsEnd = ctx->offsets + ctx->nbSamples; + /* Once *grpPtr >= curSampleEnd this occurrence of the dmer is in a + * different sample than the last. + */ + size_t curSampleEnd = ctx->offsets[0]; + for (; grpPtr != grpEnd; ++grpPtr) { + /* Save the dmerId for this position so we can get back to it. */ + ctx->dmerAt[*grpPtr] = dmerId; + /* Dictionaries only help for the first reference to the dmer. + * After that zstd can reference the match from the previous reference. + * So only count each dmer once for each sample it is in. + */ + if (*grpPtr < curSampleEnd) { + continue; + } + freq += 1; + /* Binary search to find the end of the sample *grpPtr is in. + * In the common case that grpPtr + 1 == grpEnd we can skip the binary + * search because the loop is over. + */ + if (grpPtr + 1 != grpEnd) { + const size_t *sampleEndPtr = + COVER_lower_bound(curOffsetPtr, offsetsEnd, *grpPtr); + curSampleEnd = *sampleEndPtr; + curOffsetPtr = sampleEndPtr + 1; + } + } + /* At this point we are never going to look at this segment of the suffix + * array again. We take advantage of this fact to save memory. + * We store the frequency of the dmer in the first position of the group, + * which is dmerId. + */ + ctx->suffix[dmerId] = freq; +} + +/** + * A segment is a range in the source as well as the score of the segment. + */ +typedef struct { + U32 begin; + U32 end; + double score; +} COVER_segment_t; + +/** + * Selects the best segment in an epoch. + * Segments of are scored according to the function: + * + * Let F(d) be the frequency of dmer d. + * Let L(S) be the length of segment S. + * Let S_i be the dmer at position i of segment S. + * + * F(S_1) + F(S_2) + ... + F(S_{L(S)-d+1}) + * Score(S) = -------------------------------------- + * smoothing + L(S) + * + * We try kStep segment lengths in the range [kMin, kMax]. + * For each segment length we find the best segment according to Score. + * We then take the best segment overall according to Score and return it. + * + * The difference from the paper is that we try multiple segment lengths. + * We want to fit the segment length closer to the length of the useful part. + * Longer segments allow longer matches, so they are worth more than shorter + * ones. However, if the extra length isn't high frequency it hurts us. + * We add the smoothing in to give an advantage to longer segments. + * The larger smoothing is, the more longer matches are favored. + */ +static COVER_segment_t COVER_selectSegment(const COVER_ctx_t *ctx, U32 *freqs, + COVER_map_t *activeDmers, U32 begin, + U32 end, COVER_params_t parameters) { + /* Saves the best segment of any length tried */ + COVER_segment_t globalBestSegment = {0, 0, 0}; + /* For each segment length */ + U32 k; + U32 step = MAX((parameters.kMax - parameters.kMin) / parameters.kStep, 1); + for (k = parameters.kMin; k <= parameters.kMax; k += step) { + /* Save the best segment of this length */ + COVER_segment_t bestSegment = {0, 0, 0}; + COVER_segment_t activeSegment; + const size_t dmersInK = k - ctx->d + 1; + /* Reset the activeDmers in the segment */ + COVER_map_clear(activeDmers); + activeSegment.begin = begin; + activeSegment.end = begin; + activeSegment.score = 0; + /* Slide the active segment through the whole epoch. + * Save the best segment in bestSegment. + */ + while (activeSegment.end < end) { + /* The dmerId for the dmer at the next position */ + U32 newDmer = ctx->dmerAt[activeSegment.end]; + /* The entry in activeDmers for this dmerId */ + U32 *newDmerOcc = COVER_map_at(activeDmers, newDmer); + /* If the dmer isn't already present in the segment add its score. */ + if (*newDmerOcc == 0) { + /* The paper suggest using the L-0.5 norm, but experiments show that it + * doesn't help. + */ + activeSegment.score += freqs[newDmer]; + } + /* Add the dmer to the segment */ + activeSegment.end += 1; + *newDmerOcc += 1; + + /* If the window is now too large, drop the first position */ + if (activeSegment.end - activeSegment.begin == dmersInK + 1) { + U32 delDmer = ctx->dmerAt[activeSegment.begin]; + U32 *delDmerOcc = COVER_map_at(activeDmers, delDmer); + activeSegment.begin += 1; + *delDmerOcc -= 1; + /* If this is the last occurence of the dmer, subtract its score */ + if (*delDmerOcc == 0) { + COVER_map_remove(activeDmers, delDmer); + activeSegment.score -= freqs[delDmer]; + } + } + + /* If this segment is the best so far save it */ + if (activeSegment.score > bestSegment.score) { + bestSegment = activeSegment; + } + } + { + /* Trim off the zero frequency head and tail from the segment. */ + U32 newBegin = bestSegment.end; + U32 newEnd = bestSegment.begin; + U32 pos; + for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) { + U32 freq = freqs[ctx->dmerAt[pos]]; + if (freq != 0) { + newBegin = MIN(newBegin, pos); + newEnd = pos + 1; + } + } + bestSegment.begin = newBegin; + bestSegment.end = newEnd; + /* Calculate the final score normalizing for segment length */ + bestSegment.score /= + (parameters.smoothing + (bestSegment.end - bestSegment.begin)); + } + /* If this segment is the best so far for any length save it */ + if (bestSegment.score > globalBestSegment.score) { + globalBestSegment = bestSegment; + } + } + { + /* Zero out the frequency of each dmer covered by the chosen segment. */ + size_t pos; + for (pos = globalBestSegment.begin; pos != globalBestSegment.end; ++pos) { + freqs[ctx->dmerAt[pos]] = 0; + } + } + return globalBestSegment; +} + +/** + * Check the validity of the parameters. + * If the parameters are valid and any are default, set them to the correct + * values. + * Returns 1 on success, 0 on failure. + */ +static int COVER_defaultParameters(COVER_params_t *parameters) { + /* kMin and d are required parameters */ + if (parameters->d == 0 || parameters->kMin == 0) { + return 0; + } + /* d <= kMin */ + if (parameters->d > parameters->kMin) { + return 0; + } + /* If kMax is set (non-zero) then kMin <= kMax */ + if (parameters->kMax != 0 && parameters->kMax < parameters->kMin) { + return 0; + } + /* If kMax is set, then kStep must be as well */ + if (parameters->kMax != 0 && parameters->kStep == 0) { + return 0; + } + parameters->kMax = MAX(parameters->kMin, parameters->kMax); + parameters->kStep = MAX(1, parameters->kStep); + return 1; +} + +/** + * Clean up a context initialized with `COVER_ctx_init()`. + */ +static void COVER_ctx_destroy(COVER_ctx_t *ctx) { + if (!ctx) { + return; + } + if (ctx->suffix) { + free(ctx->suffix); + ctx->suffix = NULL; + } + if (ctx->freqs) { + free(ctx->freqs); + ctx->freqs = NULL; + } + if (ctx->dmerAt) { + free(ctx->dmerAt); + ctx->dmerAt = NULL; + } + if (ctx->offsets) { + free(ctx->offsets); + ctx->offsets = NULL; + } +} + +/** + * Prepare a context for dictionary building. + * The context is only dependent on the parameter `d` and can used multiple + * times. + * Returns 1 on success or zero on error. + * The context must be destroyed with `COVER_ctx_destroy()`. + */ +static int COVER_ctx_init(COVER_ctx_t *ctx, const void *samplesBuffer, + const size_t *samplesSizes, unsigned nbSamples, + unsigned d) { + const BYTE *const samples = (const BYTE *)samplesBuffer; + const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples); + /* Checks */ + if (totalSamplesSize < d || + totalSamplesSize > (size_t)COVER_MAX_SAMPLES_SIZE) { + return 0; + } + /* Zero the context */ + memset(ctx, 0, sizeof(*ctx)); + DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbSamples, + (U32)totalSamplesSize); + ctx->samples = samples; + ctx->samplesSizes = samplesSizes; + ctx->nbSamples = nbSamples; + /* Partial suffix array */ + ctx->suffixSize = totalSamplesSize - d + 1; + ctx->suffix = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); + /* Maps index to the dmerID */ + ctx->dmerAt = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); + /* The offsets of each file */ + ctx->offsets = (size_t *)malloc((nbSamples + 1) * sizeof(size_t)); + if (!ctx->suffix || !ctx->dmerAt || !ctx->offsets) { + COVER_ctx_destroy(ctx); + return 0; + } + ctx->freqs = NULL; + ctx->d = d; + + /* Fill offsets from the samlesSizes */ + { + U32 i; + ctx->offsets[0] = 0; + for (i = 1; i <= nbSamples; ++i) { + ctx->offsets[i] = ctx->offsets[i - 1] + samplesSizes[i - 1]; + } + } + DISPLAYLEVEL(2, "Constructing partial suffix array\n"); + { + /* suffix is a partial suffix array. + * It only sorts suffixes by their first parameters.d bytes. + * The sort is stable, so each dmer group is sorted by position in input. + */ + U32 i; + for (i = 0; i < ctx->suffixSize; ++i) { + ctx->suffix[i] = i; + } + /* qsort doesn't take an opaque pointer, so pass as a global */ + g_ctx = ctx; + qsort(ctx->suffix, ctx->suffixSize, sizeof(U32), &COVER_strict_cmp); + } + DISPLAYLEVEL(2, "Computing frequencies\n"); + /* For each dmer group (group of positions with the same first d bytes): + * 1. For each position we set dmerAt[position] = dmerID. The dmerID is + * (groupBeginPtr - suffix). This allows us to go from position to + * dmerID so we can look up values in freq. + * 2. We calculate how many samples the dmer occurs in and save it in + * freqs[dmerId]. + */ + COVER_groupBy(ctx->suffix, ctx->suffixSize, sizeof(U32), ctx, &COVER_cmp, + &COVER_group); + ctx->freqs = ctx->suffix; + ctx->suffix = NULL; + return 1; +} + +/** + * Given the prepared context build the dictionary. + */ +static size_t COVER_buildDictionary(const COVER_ctx_t *ctx, U32 *freqs, + COVER_map_t *activeDmers, void *dictBuffer, + size_t dictBufferCapacity, + COVER_params_t parameters) { + BYTE *const dict = (BYTE *)dictBuffer; + size_t tail = dictBufferCapacity; + /* Divide the data up into epochs of equal size. + * We will select at least one segment from each epoch. + */ + const U32 epochs = (U32)(dictBufferCapacity / parameters.kMax); + const U32 epochSize = (U32)(ctx->suffixSize / epochs); + size_t epoch; + DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n", epochs, + epochSize); + for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs) { + const U32 epochBegin = (U32)(epoch * epochSize); + const U32 epochEnd = epochBegin + epochSize; + size_t segmentSize; + COVER_segment_t segment = COVER_selectSegment( + ctx, freqs, activeDmers, epochBegin, epochEnd, parameters); + segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail); + if (segmentSize == 0) { + break; + } + /* We fill the dictionary from the back to allow the best segments to be + * referenced with the smallest offsets. + */ + tail -= segmentSize; + memcpy(dict + tail, ctx->samples + segment.begin, segmentSize); + DISPLAYUPDATE( + 2, "\r%u%% ", + (U32)(((dictBufferCapacity - tail) * 100) / dictBufferCapacity)); + } + DISPLAYLEVEL(2, "\r%79s\r", ""); + return tail; +} + +/** + * Translate from COVER_params_t to ZDICT_params_t required for finalizing the + * dictionary. + */ +static ZDICT_params_t COVER_translateParams(COVER_params_t parameters) { + ZDICT_params_t zdictParams; + memset(&zdictParams, 0, sizeof(zdictParams)); + zdictParams.notificationLevel = 1; + zdictParams.dictID = parameters.dictID; + zdictParams.compressionLevel = parameters.compressionLevel; + return zdictParams; +} + +/** + * Constructs a dictionary using a heuristic based on the following paper: + * + * Liao, Petri, Moffat, Wirth + * Effective Construction of Relative Lempel-Ziv Dictionaries + * Published in WWW 2016. + */ +ZDICTLIB_API size_t COVER_trainFromBuffer( + void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer, + const size_t *samplesSizes, unsigned nbSamples, COVER_params_t parameters) { + BYTE *const dict = (BYTE *)dictBuffer; + COVER_ctx_t ctx; + COVER_map_t activeDmers; + size_t rc; + /* Checks */ + if (!COVER_defaultParameters(¶meters)) { + DISPLAYLEVEL(1, "Cover parameters incorrect\n"); + return ERROR(GENERIC); + } + if (nbSamples == 0) { + DISPLAYLEVEL(1, "Cover must have at least one input file\n"); + return ERROR(GENERIC); + } + if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { + return ERROR(dstSize_tooSmall); + } + /* Initialize global data */ + g_displayLevel = parameters.notificationLevel; + /* Initialize context and activeDmers */ + if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, + parameters.d)) { + DISPLAYLEVEL(1, "Failed to initialize context\n"); + return ERROR(GENERIC); + } + if (!COVER_map_init(&activeDmers, parameters.kMax - parameters.d + 1)) { + DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n"); + COVER_ctx_destroy(&ctx); + return ERROR(GENERIC); + } + + DISPLAYLEVEL(2, "Building dictionary\n"); + { + const size_t tail = + COVER_buildDictionary(&ctx, ctx.freqs, &activeDmers, dictBuffer, + dictBufferCapacity, parameters); + ZDICT_params_t zdictParams = COVER_translateParams(parameters); + DISPLAYLEVEL(2, "Dictionary content size: %u", + (U32)(dictBufferCapacity - tail)); + rc = ZDICT_finalizeDictionary(dict, dictBufferCapacity, dict + tail, + dictBufferCapacity - tail, samplesBuffer, + samplesSizes, nbSamples, zdictParams); + } + if (!ZSTD_isError(rc)) { + DISPLAYLEVEL(2, "Constructed dictionary of size %u\n", (U32)rc); + } + COVER_ctx_destroy(&ctx); + COVER_map_destroy(&activeDmers); + return rc; +} + +/** + * COVER_best_t is used for two purposes: + * 1. Synchronizing threads. + * 2. Saving the best parameters and dictionary. + * + * All of the methods are thread safe if `ZSTD_PTHREAD` is defined. + */ +typedef struct COVER_best_s { +#ifdef ZSTD_PTHREAD + pthread_mutex_t mutex; + pthread_cond_t cond; + size_t liveJobs; +#endif + void *dict; + size_t dictSize; + COVER_params_t parameters; + size_t compressedSize; +} COVER_best_t; + +/** + * Initialize the `COVER_best_t`. + */ +static void COVER_best_init(COVER_best_t *best) { + if (!best) { + return; + } +#ifdef ZSTD_PTHREAD + pthread_mutex_init(&best->mutex, NULL); + pthread_cond_init(&best->cond, NULL); + best->liveJobs = 0; +#endif + best->dict = NULL; + best->dictSize = 0; + best->compressedSize = (size_t)-1; + memset(&best->parameters, 0, sizeof(best->parameters)); +} + +/** + * Wait until liveJobs == 0. + */ +static void COVER_best_wait(COVER_best_t *best) { + if (!best) { + return; + } +#ifdef ZSTD_PTHREAD + pthread_mutex_lock(&best->mutex); + while (best->liveJobs != 0) { + pthread_cond_wait(&best->cond, &best->mutex); + } + pthread_mutex_unlock(&best->mutex); +#endif +} + +/** + * Call COVER_best_wait() and then destroy the COVER_best_t. + */ +static void COVER_best_destroy(COVER_best_t *best) { + if (!best) { + return; + } + COVER_best_wait(best); + if (best->dict) { + free(best->dict); + } +#ifdef ZSTD_PTHREAD + pthread_mutex_destroy(&best->mutex); + pthread_cond_destroy(&best->cond); +#endif +} + +/** + * Called when a thread is about to be launched. + * Increments liveJobs. + */ +static void COVER_best_start(COVER_best_t *best) { + if (!best) { + return; + } +#ifdef ZSTD_PTHREAD + pthread_mutex_lock(&best->mutex); + ++best->liveJobs; + pthread_mutex_unlock(&best->mutex); +#endif +} + +/** + * Called when a thread finishes executing, both on error or success. + * Decrements liveJobs and signals any waiting threads if liveJobs == 0. + * If this dictionary is the best so far save it and its parameters. + */ +static void COVER_best_finish(COVER_best_t *best, size_t compressedSize, + COVER_params_t parameters, void *dict, + size_t dictSize) { + if (!best) { + return; + } + { +#ifdef ZSTD_PTHREAD + size_t liveJobs; + pthread_mutex_lock(&best->mutex); + --best->liveJobs; + liveJobs = best->liveJobs; +#endif + /* If the new dictionary is better */ + if (compressedSize < best->compressedSize) { + /* Allocate space if necessary */ + if (!best->dict || best->dictSize < dictSize) { + if (best->dict) { + free(best->dict); + } + best->dict = malloc(dictSize); + if (!best->dict) { + best->compressedSize = ERROR(GENERIC); + best->dictSize = 0; + return; + } + } + /* Save the dictionary, parameters, and size */ + memcpy(best->dict, dict, dictSize); + best->dictSize = dictSize; + best->parameters = parameters; + best->compressedSize = compressedSize; + } +#ifdef ZSTD_PTHREAD + pthread_mutex_unlock(&best->mutex); + if (liveJobs == 0) { + pthread_cond_broadcast(&best->cond); + } +#endif + } +} + +/** + * Parameters for COVER_tryParameters(). + */ +typedef struct COVER_tryParameters_data_s { + const COVER_ctx_t *ctx; + COVER_best_t *best; + size_t dictBufferCapacity; + COVER_params_t parameters; +} COVER_tryParameters_data_t; + +/** + * Tries a set of parameters and upates the COVER_best_t with the results. + * This function is thread safe if ZSTD_PTHREAD is defined. + * It takes its parameters as an *OWNING* opaque pointer to support threading. + */ +static void COVER_tryParameters(void *opaque) { + /* Save parameters as local variables */ + COVER_tryParameters_data_t *data = (COVER_tryParameters_data_t *)opaque; + const COVER_ctx_t *ctx = data->ctx; + COVER_params_t parameters = data->parameters; + size_t dictBufferCapacity = data->dictBufferCapacity; + size_t totalCompressedSize = ERROR(GENERIC); + /* Allocate space for hash table, dict, and freqs */ + COVER_map_t activeDmers; + BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity); + U32 *freqs = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); + if (!COVER_map_init(&activeDmers, parameters.kMax - parameters.d + 1)) { + DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n"); + goto _cleanup; + } + if (!dict || !freqs) { + DISPLAYLEVEL(1, "Failed to allocate dictionary buffer\n"); + goto _cleanup; + } + /* Copy the frequencies because we need to modify them */ + memcpy(freqs, ctx->freqs, ctx->suffixSize * sizeof(U32)); + /* Build the dictionary */ + { + const size_t tail = COVER_buildDictionary(ctx, freqs, &activeDmers, dict, + dictBufferCapacity, parameters); + ZDICT_params_t zdictParams = COVER_translateParams(parameters); + dictBufferCapacity = ZDICT_finalizeDictionary( + dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, + ctx->samples, ctx->samplesSizes, (unsigned)ctx->nbSamples, zdictParams); + if (ZDICT_isError(dictBufferCapacity)) { + DISPLAYLEVEL(1, "Failed to finalize dictionary\n"); + goto _cleanup; + } + } + /* Check total compressed size */ + { + /* Pointers */ + ZSTD_CCtx *cctx; + ZSTD_CDict *cdict; + void *dst; + /* Local variables */ + size_t dstCapacity; + size_t i; + /* Allocate dst with enough space to compress the maximum sized sample */ + { + size_t maxSampleSize = 0; + for (i = 0; i < ctx->nbSamples; ++i) { + maxSampleSize = MAX(ctx->samplesSizes[i], maxSampleSize); + } + dstCapacity = ZSTD_compressBound(maxSampleSize); + dst = malloc(dstCapacity); + } + /* Create the cctx and cdict */ + cctx = ZSTD_createCCtx(); + cdict = + ZSTD_createCDict(dict, dictBufferCapacity, parameters.compressionLevel); + if (!dst || !cctx || !cdict) { + goto _compressCleanup; + } + /* Compress each sample and sum their sizes (or error) */ + totalCompressedSize = 0; + for (i = 0; i < ctx->nbSamples; ++i) { + const size_t size = ZSTD_compress_usingCDict( + cctx, dst, dstCapacity, ctx->samples + ctx->offsets[i], + ctx->samplesSizes[i], cdict); + if (ZSTD_isError(size)) { + totalCompressedSize = ERROR(GENERIC); + goto _compressCleanup; + } + totalCompressedSize += size; + } + _compressCleanup: + ZSTD_freeCCtx(cctx); + ZSTD_freeCDict(cdict); + if (dst) { + free(dst); + } + } + +_cleanup: + COVER_best_finish(data->best, totalCompressedSize, parameters, dict, + dictBufferCapacity); + free(data); + COVER_map_destroy(&activeDmers); + if (dict) { + free(dict); + } + if (freqs) { + free(freqs); + } +} + +ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, + size_t dictBufferCapacity, + const void *samplesBuffer, + const size_t *samplesSizes, + unsigned nbSamples, + COVER_params_t *parameters) { + /* constants */ + const unsigned dMin = parameters->d == 0 ? 6 : parameters->d; + const unsigned dMax = parameters->d == 0 ? 16 : parameters->d; + const unsigned min = parameters->kMin == 0 ? 32 : parameters->kMin; + const unsigned max = parameters->kMax == 0 ? 1024 : parameters->kMax; + const unsigned kStep = parameters->kStep == 0 ? 8 : parameters->kStep; + const unsigned step = MAX((max - min) / kStep, 1); + /* Local variables */ + unsigned iteration = 1; + const unsigned iterations = + (1 + (dMax - dMin) / 2) * (((1 + kStep) * (2 + kStep)) / 2) * 4; + const int displayLevel = parameters->notificationLevel; + unsigned d; + COVER_best_t best; + COVER_best_init(&best); + /* Turn down display level to clean up display at level 2 and below */ + g_displayLevel = parameters->notificationLevel - 1; + /* Loop through d first because each new value needs a new context */ + LOCALDISPLAYLEVEL(displayLevel, 3, "Trying %u different sets of parameters\n", + iterations); + for (d = dMin; d <= dMax; d += 2) { + unsigned kMin; + /* Initialize the context for this value of d */ + COVER_ctx_t ctx; + LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d); + if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d)) { + LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n"); + COVER_best_destroy(&best); + return ERROR(GENERIC); + } + /* Loop through the rest of the parameters reusing the same context */ + for (kMin = min; kMin <= max; kMin += step) { + unsigned kMax; + LOCALDISPLAYLEVEL(displayLevel, 3, "kMin=%u\n", kMin); + for (kMax = kMin; kMax <= max; kMax += step) { + unsigned smoothing; + LOCALDISPLAYLEVEL(displayLevel, 3, "kMax=%u\n", kMax); + for (smoothing = kMin / 4; smoothing <= kMin * 2; smoothing *= 2) { + /* Prepare the arguments */ + COVER_tryParameters_data_t *data = + (COVER_tryParameters_data_t *)malloc( + sizeof(COVER_tryParameters_data_t)); + LOCALDISPLAYLEVEL(displayLevel, 3, "smoothing=%u\n", smoothing); + if (!data) { + LOCALDISPLAYLEVEL(displayLevel, 1, + "Failed to allocate parameters\n"); + COVER_best_destroy(&best); + COVER_ctx_destroy(&ctx); + return ERROR(GENERIC); + } + data->ctx = &ctx; + data->best = &best; + data->dictBufferCapacity = dictBufferCapacity; + data->parameters = *parameters; + data->parameters.d = d; + data->parameters.kMin = kMin; + data->parameters.kStep = kStep; + data->parameters.kMax = kMax; + data->parameters.smoothing = smoothing; + /* Call the function and pass ownership of data to it */ + COVER_best_start(&best); + COVER_tryParameters(data); + /* Print status */ + LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%% ", + (U32)((iteration * 100) / iterations)); + ++iteration; + } + } + } + COVER_best_wait(&best); + COVER_ctx_destroy(&ctx); + } + LOCALDISPLAYLEVEL(displayLevel, 2, "\r%79s\r", ""); + /* Fill the output buffer and parameters with output of the best parameters */ + { + const size_t dictSize = best.dictSize; + if (ZSTD_isError(best.compressedSize)) { + COVER_best_destroy(&best); + return best.compressedSize; + } + *parameters = best.parameters; + memcpy(dictBuffer, best.dict, dictSize); + COVER_best_destroy(&best); + return dictSize; + } +} diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index 63b8f0722..4a2e79448 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -86,6 +86,58 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_advanced(void* dictBuffer, size_t dict const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_params_t parameters); +/*! COVER_params_t : + For all values 0 means default. + kMin and d are the only required parameters. +*/ +typedef struct { + unsigned d; /* dmer size : constraint: <= kMin : Should probably be in the range [6, 16]. */ + unsigned kMin; /* Minimum segment size : constraint: > 0 */ + unsigned kStep; /* Try kStep segment lengths uniformly distributed in the range [kMin, kMax] : 0 (default) only if kMax == 0 */ + unsigned kMax; /* Maximum segment size : 0 = kMin (default) : constraint : 0 or >= kMin */ + unsigned smoothing; /* Higher smoothing => larger segments are selected. Only useful if kMax > kMin. */ + + unsigned notificationLevel; /* Write to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ + unsigned dictID; /* 0 means auto mode (32-bits random value); other : force dictID value */ + int compressionLevel; /* 0 means default; target a specific zstd compression level */ +} COVER_params_t; + + +/*! COVER_trainFromBuffer() : + Train a dictionary from an array of samples using the COVER algorithm. + Samples must be stored concatenated in a single flat buffer `samplesBuffer`, + supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order. + The resulting dictionary will be saved into `dictBuffer`. + @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) + or an error code, which can be tested with ZDICT_isError(). + Tips : In general, a reasonable dictionary has a size of ~ 100 KB. + It's obviously possible to target smaller or larger ones, just by specifying different `dictBufferCapacity`. + In general, it's recommended to provide a few thousands samples, but this can vary a lot. + It's recommended that total size of all samples be about ~x100 times the target size of dictionary. +*/ +ZDICTLIB_API size_t COVER_trainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, + COVER_params_t parameters); + +/*! COVER_optimizeTrainFromBuffer() : + The same requirements as above hold for all the parameters except `parameters`. + This function tries many parameter combinations and picks the best parameters. + `*parameters` is filled with the best parameters found, and the dictionary + constructed with those parameters is stored in `dictBuffer`. + + All of the {d, kMin, kStep, kMax} are optional, and smoothing is ignored. + If d is non-zero then we don't check multiple values of d, otherwise we check d = {6, 8, 10, 12, 14, 16}. + If kStep is non-zero then it is used, otherwise we pick 8. + If kMin and kMax are non-zero, then they limit the search space for kMin and kMax, + otherwise we check kMin and kMax values in the range [32, 1024]. + + @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) + or an error code, which can be tested with ZDICT_isError(). + On success `*parameters` contains the parameters selected. +*/ +ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, + const void* samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, + COVER_params_t *parameters); /*! ZDICT_finalizeDictionary() : From 9103ed6c8bf01531602d09efe6a0fa21c5012b54 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Sat, 31 Dec 2016 21:08:12 -0800 Subject: [PATCH 087/227] Add cover.c to build files --- build/VS2005/fuzzer/fuzzer.vcproj | 4 ++++ build/VS2005/zstd/zstd.vcproj | 4 ++++ build/VS2005/zstdlib/zstdlib.vcproj | 4 ++++ build/VS2008/fuzzer/fuzzer.vcproj | 4 ++++ build/VS2008/zstd/zstd.vcproj | 4 ++++ build/VS2008/zstdlib/zstdlib.vcproj | 4 ++++ build/VS2010/fuzzer/fuzzer.vcxproj | 1 + build/VS2010/libzstd-dll/libzstd-dll.vcxproj | 1 + build/VS2010/libzstd/libzstd.vcxproj | 1 + build/VS2010/zstd/zstd.vcxproj | 1 + build/cmake/lib/CMakeLists.txt | 1 + 11 files changed, 29 insertions(+) diff --git a/build/VS2005/fuzzer/fuzzer.vcproj b/build/VS2005/fuzzer/fuzzer.vcproj index b1ac81365..d6ec14d16 100644 --- a/build/VS2005/fuzzer/fuzzer.vcproj +++ b/build/VS2005/fuzzer/fuzzer.vcproj @@ -331,6 +331,10 @@ RelativePath="..\..\..\programs\datagen.c" > + + diff --git a/build/VS2005/zstd/zstd.vcproj b/build/VS2005/zstd/zstd.vcproj index 9f49e3cb6..5ef7a98f8 100644 --- a/build/VS2005/zstd/zstd.vcproj +++ b/build/VS2005/zstd/zstd.vcproj @@ -343,6 +343,10 @@ RelativePath="..\..\..\programs\dibio.c" > + + diff --git a/build/VS2005/zstdlib/zstdlib.vcproj b/build/VS2005/zstdlib/zstdlib.vcproj index d95212b33..828cc8285 100644 --- a/build/VS2005/zstdlib/zstdlib.vcproj +++ b/build/VS2005/zstdlib/zstdlib.vcproj @@ -327,6 +327,10 @@ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > + + diff --git a/build/VS2008/fuzzer/fuzzer.vcproj b/build/VS2008/fuzzer/fuzzer.vcproj index 311b79909..f6912b8d9 100644 --- a/build/VS2008/fuzzer/fuzzer.vcproj +++ b/build/VS2008/fuzzer/fuzzer.vcproj @@ -332,6 +332,10 @@ RelativePath="..\..\..\programs\datagen.c" > + + diff --git a/build/VS2008/zstd/zstd.vcproj b/build/VS2008/zstd/zstd.vcproj index ad64f8696..3bd5ed536 100644 --- a/build/VS2008/zstd/zstd.vcproj +++ b/build/VS2008/zstd/zstd.vcproj @@ -344,6 +344,10 @@ RelativePath="..\..\..\programs\dibio.c" > + + diff --git a/build/VS2008/zstdlib/zstdlib.vcproj b/build/VS2008/zstdlib/zstdlib.vcproj index b1c103e32..69b742d19 100644 --- a/build/VS2008/zstdlib/zstdlib.vcproj +++ b/build/VS2008/zstdlib/zstdlib.vcproj @@ -328,6 +328,10 @@ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > + + diff --git a/build/VS2010/fuzzer/fuzzer.vcxproj b/build/VS2010/fuzzer/fuzzer.vcxproj index 7227c7ec0..623a9ca43 100644 --- a/build/VS2010/fuzzer/fuzzer.vcxproj +++ b/build/VS2010/fuzzer/fuzzer.vcxproj @@ -165,6 +165,7 @@ + diff --git a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj index f1ea5c822..f0feecb26 100644 --- a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj +++ b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj @@ -32,6 +32,7 @@ + diff --git a/build/VS2010/libzstd/libzstd.vcxproj b/build/VS2010/libzstd/libzstd.vcxproj index 228e83da4..c8d21dd4a 100644 --- a/build/VS2010/libzstd/libzstd.vcxproj +++ b/build/VS2010/libzstd/libzstd.vcxproj @@ -32,6 +32,7 @@ + diff --git a/build/VS2010/zstd/zstd.vcxproj b/build/VS2010/zstd/zstd.vcxproj index 5b5852689..2b30966ab 100644 --- a/build/VS2010/zstd/zstd.vcxproj +++ b/build/VS2010/zstd/zstd.vcxproj @@ -29,6 +29,7 @@ + diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index 41fe2733f..34a639cdb 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -67,6 +67,7 @@ SET(Sources ${LIBRARY_DIR}/compress/zstd_compress.c ${LIBRARY_DIR}/decompress/huf_decompress.c ${LIBRARY_DIR}/decompress/zstd_decompress.c + ${LIBRARY_DIR}/dictBuilder/cover.c ${LIBRARY_DIR}/dictBuilder/divsufsort.c ${LIBRARY_DIR}/dictBuilder/zdict.c ${LIBRARY_DIR}/deprecated/zbuff_common.c From df8415c50275cd0a4f2ab2e57fc2d6a928de2995 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Sat, 31 Dec 2016 21:08:24 -0800 Subject: [PATCH 088/227] Add COVER to the zstd cli --- programs/dibio.c | 61 ++++++++++++++++++++++++++++++++++++++----- programs/dibio.h | 4 +-- programs/zstdcli.c | 64 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 114 insertions(+), 15 deletions(-) diff --git a/programs/dibio.c b/programs/dibio.c index b95bab34e..ba15d2106 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -42,6 +42,7 @@ #define SAMPLESIZE_MAX (128 KB) #define MEMMULT 11 /* rough estimation : memory cost to analyze 1 byte of sample */ +#define COVER_MEMMULT 9 /* rough estimation : memory cost to analyze 1 byte of sample */ static const size_t maxMemory = (sizeof(size_t) == 4) ? (2 GB - 64 MB) : ((size_t)(512 MB) << sizeof(size_t)); #define NOISELENGTH 32 @@ -118,10 +119,36 @@ static unsigned DiB_loadFiles(void* buffer, size_t* bufferSizePtr, fileSizes[n] = fileSize; fclose(f); } } + DISPLAYLEVEL(2, "\r%79s\r", ""); *bufferSizePtr = pos; return n; } +#define DiB_rotl32(x,r) ((x << r) | (x >> (32 - r))) +static U32 DiB_rand(U32* src) +{ + static const U32 prime1 = 2654435761U; + static const U32 prime2 = 2246822519U; + U32 rand32 = *src; + rand32 *= prime1; + rand32 ^= prime2; + rand32 = DiB_rotl32(rand32, 13); + *src = rand32; + return rand32 >> 5; +} + +static void DiB_shuffle(const char** fileNamesTable, unsigned nbFiles) { + /* Initialize the pseudorandom number generator */ + U32 seed = 0xFD2FB528; + unsigned i; + for (i = nbFiles - 1; i > 0; --i) { + unsigned const j = DiB_rand(&seed) % (i + 1); + const char* tmp = fileNamesTable[j]; + fileNamesTable[j] = fileNamesTable[i]; + fileNamesTable[i] = tmp; + } +} + /*-******************************************************** * Dictionary training functions @@ -202,7 +229,8 @@ size_t ZDICT_trainFromBuffer_unsafe(void* dictBuffer, size_t dictBufferCapacity, int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, const char** fileNamesTable, unsigned nbFiles, - ZDICT_params_t params) + ZDICT_params_t *params, COVER_params_t *coverParams, + int optimizeCover) { void* const dictBuffer = malloc(maxDictSize); size_t* const fileSizes = (size_t*)malloc(nbFiles * sizeof(size_t)); @@ -213,8 +241,10 @@ int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, int result = 0; /* Checks */ + if (params) g_displayLevel = params->notificationLevel; + else if (coverParams) g_displayLevel = coverParams->notificationLevel; + else EXM_THROW(13, "Neither dictionary algorith selected"); /* should not happen */ if ((!fileSizes) || (!srcBuffer) || (!dictBuffer)) EXM_THROW(12, "not enough memory for DiB_trainFiles"); /* should not happen */ - g_displayLevel = params.notificationLevel; if (g_tooLargeSamples) { DISPLAYLEVEL(2, "! Warning : some samples are very large \n"); DISPLAYLEVEL(2, "! Note that dictionary is only useful for small files or beginning of large files. \n"); @@ -233,12 +263,31 @@ int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, DISPLAYLEVEL(1, "Not enough memory; training on %u MB only...\n", (unsigned)(benchedSize >> 20)); /* Load input buffer */ + DISPLAYLEVEL(3, "Shuffling input files\n"); + DiB_shuffle(fileNamesTable, nbFiles); nbFiles = DiB_loadFiles(srcBuffer, &benchedSize, fileSizes, fileNamesTable, nbFiles); - DiB_fillNoise((char*)srcBuffer + benchedSize, NOISELENGTH); /* guard band, for end of buffer condition */ - { size_t const dictSize = ZDICT_trainFromBuffer_unsafe(dictBuffer, maxDictSize, - srcBuffer, fileSizes, nbFiles, - params); + { + size_t dictSize; + if (params) { + DiB_fillNoise((char*)srcBuffer + benchedSize, NOISELENGTH); /* guard band, for end of buffer condition */ + dictSize = ZDICT_trainFromBuffer_unsafe(dictBuffer, maxDictSize, + srcBuffer, fileSizes, nbFiles, + *params); + } else if (optimizeCover) { + dictSize = COVER_optimizeTrainFromBuffer( + dictBuffer, maxDictSize, srcBuffer, fileSizes, nbFiles, + coverParams); + if (!ZDICT_isError(dictSize)) { + DISPLAYLEVEL(2, "smoothing=%d\nkMin=%d\nkStep=%d\nkMax=%d\nd=%d\n", + coverParams->smoothing, coverParams->kMin, + coverParams->kStep, coverParams->kMax, coverParams->d); + } + } else { + dictSize = COVER_trainFromBuffer(dictBuffer, maxDictSize, + srcBuffer, fileSizes, nbFiles, + *coverParams); + } if (ZDICT_isError(dictSize)) { DISPLAYLEVEL(1, "dictionary training failed : %s \n", ZDICT_getErrorName(dictSize)); /* should not happen */ result = 1; diff --git a/programs/dibio.h b/programs/dibio.h index 6780d8698..e61d0042c 100644 --- a/programs/dibio.h +++ b/programs/dibio.h @@ -32,7 +32,7 @@ */ int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, const char** fileNamesTable, unsigned nbFiles, - ZDICT_params_t parameters); - + ZDICT_params_t *params, COVER_params_t *coverParams, + int optimizeCover); #endif diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 978ffcfe0..f4d33d3f8 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -127,6 +127,8 @@ static int usage_advanced(const char* programName) DISPLAY( "\n"); DISPLAY( "Dictionary builder :\n"); DISPLAY( "--train ## : create a dictionary from a training set of files \n"); + DISPLAY( "--cover=k=#,d=# : use the cover algorithm with parameters k and d \n"); + DISPLAY( "--optimize-cover[=steps=#,k=#,d=#] : optimize cover parameters with optional parameters\n"); DISPLAY( " -o file : `file` is dictionary name (default: %s) \n", g_defaultDictName); DISPLAY( "--maxdict ## : limit dictionary to specified size (default : %u) \n", g_defaultMaxDictSize); DISPLAY( " -s# : dictionary selectivity level (default: %u)\n", g_defaultSelectivityLevel); @@ -192,6 +194,29 @@ static unsigned longCommandWArg(const char** stringPtr, const char* longCommand) } +#ifndef ZSTD_NODICT +/** + * parseCoverParameters() : + * reads cover parameters from *stringPtr (e.g. "--cover=smoothing=100,kmin=48,kstep=4,kmax=64,d=8") into *params + * @return 1 means that cover parameters were correct + * @return 0 in case of malformed parameters + */ +static unsigned parseCoverParameters(const char* stringPtr, COVER_params_t *params) +{ + memset(params, 0, sizeof(*params)); + for (; ;) { + if (longCommandWArg(&stringPtr, "smoothing=")) { params->smoothing = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "k=") || longCommandWArg(&stringPtr, "kMin=") || longCommandWArg(&stringPtr, "kmin=")) { params->kMin = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "kStep=") || longCommandWArg(&stringPtr, "kstep=")) { params->kStep = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "kMax=") || longCommandWArg(&stringPtr, "kmax=")) { params->kMax = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "d=")) { params->d = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + return 0; + } + if (stringPtr[0] != 0) return 0; + DISPLAYLEVEL(4, "smoothing=%d\nkMin=%d\nkStep=%d\nkMax=%d\nd=%d\n", params->smoothing, params->kMin, params->kStep, params->kMax, params->d); + return 1; +} +#endif /** parseCompressionParameters() : * reads compression parameters from *stringPtr (e.g. "--zstd=wlog=23,clog=23,hlog=22,slog=6,slen=3,tlen=48,strat=6") into *params * @return 1 means that compression parameters were correct @@ -254,6 +279,10 @@ int main(int argCount, const char* argv[]) char* fileNamesBuf = NULL; unsigned fileNamesNb; #endif +#ifndef ZSTD_NODICT + COVER_params_t coverParams; + int cover = 0; +#endif /* init */ (void)recursive; (void)cLevelLast; /* not used when ZSTD_NOBENCH set */ @@ -318,6 +347,20 @@ int main(int argCount, const char* argv[]) if (!strcmp(argument, "--rm")) { FIO_setRemoveSrcFile(1); continue; } /* long commands with arguments */ +#ifndef ZSTD_NODICT + if (longCommandWArg(&argument, "--cover=")) { + cover=1; if (!parseCoverParameters(argument, &coverParams)) CLEAN_RETURN(badusage(programName)); + continue; + } + if (longCommandWArg(&argument, "--optimize-cover")) { + cover=2; + /* Allow optional arguments following an = */ + if (*argument == 0) { memset(&coverParams, 0, sizeof(coverParams)); } + else if (*argument++ != '=') { CLEAN_RETURN(badusage(programName)); } + else if (!parseCoverParameters(argument, &coverParams)) { CLEAN_RETURN(badusage(programName)); } + continue; + } +#endif if (longCommandWArg(&argument, "--memlimit=")) { memLimit = readU32FromChar(&argument); continue; } if (longCommandWArg(&argument, "--memory=")) { memLimit = readU32FromChar(&argument); continue; } if (longCommandWArg(&argument, "--memlimit-decompress=")) { memLimit = readU32FromChar(&argument); continue; } @@ -520,13 +563,20 @@ int main(int argCount, const char* argv[]) /* Check if dictionary builder is selected */ if (operation==zom_train) { #ifndef ZSTD_NODICT - ZDICT_params_t dictParams; - memset(&dictParams, 0, sizeof(dictParams)); - dictParams.compressionLevel = dictCLevel; - dictParams.selectivityLevel = dictSelect; - dictParams.notificationLevel = displayLevel; - dictParams.dictID = dictID; - DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, dictParams); + if (cover) { + coverParams.compressionLevel = dictCLevel; + coverParams.notificationLevel = displayLevel; + coverParams.dictID = dictID; + DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, NULL, &coverParams, cover - 1); + } else { + ZDICT_params_t dictParams; + memset(&dictParams, 0, sizeof(dictParams)); + dictParams.compressionLevel = dictCLevel; + dictParams.selectivityLevel = dictSelect; + dictParams.notificationLevel = displayLevel; + dictParams.dictID = dictID; + DiB_trainFromFiles(outFileName, maxDictSize, filenameTable, filenameIdx, &dictParams, NULL, 0); + } #endif goto _end; } From cbb3ce376b3ecbe2a1a45d8ddcdeb0d6f2d7deb8 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Sun, 1 Jan 2017 01:59:51 -0500 Subject: [PATCH 089/227] Add cover cli to playtests --- tests/playTests.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index dfc90c338..a94446e7e 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -255,6 +255,27 @@ rm -rf dirTestDict rm tmp* +$ECHO "\n**** cover dictionary tests **** " + +TESTFILE=../programs/zstdcli.c +./datagen > tmpDict +$ECHO "- Create first dictionary" +$ZSTD --train --cover=k=46,d=8 *.c ../programs/*.c -o tmpDict +cp $TESTFILE tmp +$ZSTD -f tmp -D tmpDict +$ZSTD -d tmp.zst -D tmpDict -fo result +$DIFF $TESTFILE result +$ECHO "- Create second (different) dictionary" +$ZSTD --train --cover=kmin=46,kstep=2,kmax=64,d=6,smoothing=23 *.c ../programs/*.c ../programs/*.h -o tmpDictC +$ZSTD -d tmp.zst -D tmpDictC -fo result && die "wrong dictionary not detected!" +$ECHO "- Create dictionary with short dictID" +$ZSTD --train --cover=k=46,d=8 *.c ../programs/*.c --dictID 1 -o tmpDict1 +cmp tmpDict tmpDict1 && die "dictionaries should have different ID !" +$ECHO "- Create dictionary with size limit" +$ZSTD --train --optimize-cover=kstep=2,d=8 *.c ../programs/*.c -o tmpDict2 --maxdict 4K +rm tmp* + + $ECHO "\n**** integrity tests **** " $ECHO "test one file (tmp1.zst) " From 85667997811104a5a5cfa5c37ff530931a454e9e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 3 Jan 2017 00:25:01 +0100 Subject: [PATCH 090/227] separated ppc and ppc64 tests, for more regular timing --- .travis.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6bf99f1bf..b0489bd63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -92,7 +92,17 @@ matrix: - gcc-aarch64-linux-gnu - libc6-dev-arm64-cross - - env: Ubu=14.04 Cmd='make ppctest && make clean && make ppc64test' + - env: Ubu=14.04 Cmd='make ppctest' + dist: trusty + sudo: required + addons: + apt: + packages: + - qemu-system-ppc + - qemu-user-static + - gcc-powerpc-linux-gnu + + - env: Ubu=14.04 Cmd='make ppc64test' dist: trusty sudo: required addons: @@ -101,7 +111,6 @@ matrix: - qemu-system-ppc - qemu-user-static - gcc-powerpc-linux-gnu - - libc6-dev-armel-cross - env: Ubu=14.04 Cmd='make -C lib all && CFLAGS="-O1 -g" make -C zlibWrapper valgrindTest && make -C tests valgrindTest' os: linux @@ -114,7 +123,7 @@ matrix: - env: Ubu=14.04 Cmd="make gpptest && make clean && make gnu90test && make clean && make c99test && make clean && make gnu99test && make clean - && make clangtest && make clean && make -C contrib/pzstd googletest32 + && make clangtest && make clean && make -C contrib/pzstd googletest32 && make -C contrib/pzstd all32 && make -C contrib/pzstd check && make -C contrib/pzstd clean" os: linux dist: trusty From 3a1fefcf006484bda63d8948146d13ef2b7d2428 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 2 Jan 2017 12:40:43 -0800 Subject: [PATCH 091/227] Simplify COVER parameters --- lib/dictBuilder/cover.c | 340 +++++++++++++++++++--------------------- lib/dictBuilder/zdict.h | 15 +- programs/dibio.c | 4 +- programs/zstdcli.c | 8 +- tests/playTests.sh | 4 +- 5 files changed, 172 insertions(+), 199 deletions(-) diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c index 1bad01869..c8bee4e26 100644 --- a/lib/dictBuilder/cover.c +++ b/lib/dictBuilder/cover.c @@ -361,137 +361,103 @@ typedef struct { * Segments of are scored according to the function: * * Let F(d) be the frequency of dmer d. - * Let L(S) be the length of segment S. - * Let S_i be the dmer at position i of segment S. + * Let S_i be the dmer at position i of segment S which has length k. * - * F(S_1) + F(S_2) + ... + F(S_{L(S)-d+1}) - * Score(S) = -------------------------------------- - * smoothing + L(S) + * Score(S) = F(S_1) + F(S_2) + ... + F(S_{k-d+1}) * - * We try kStep segment lengths in the range [kMin, kMax]. - * For each segment length we find the best segment according to Score. - * We then take the best segment overall according to Score and return it. - * - * The difference from the paper is that we try multiple segment lengths. - * We want to fit the segment length closer to the length of the useful part. - * Longer segments allow longer matches, so they are worth more than shorter - * ones. However, if the extra length isn't high frequency it hurts us. - * We add the smoothing in to give an advantage to longer segments. - * The larger smoothing is, the more longer matches are favored. + * Once the dmer d is in the dictionay we set F(d) = 0. */ static COVER_segment_t COVER_selectSegment(const COVER_ctx_t *ctx, U32 *freqs, COVER_map_t *activeDmers, U32 begin, U32 end, COVER_params_t parameters) { - /* Saves the best segment of any length tried */ - COVER_segment_t globalBestSegment = {0, 0, 0}; - /* For each segment length */ - U32 k; - U32 step = MAX((parameters.kMax - parameters.kMin) / parameters.kStep, 1); - for (k = parameters.kMin; k <= parameters.kMax; k += step) { - /* Save the best segment of this length */ - COVER_segment_t bestSegment = {0, 0, 0}; - COVER_segment_t activeSegment; - const size_t dmersInK = k - ctx->d + 1; - /* Reset the activeDmers in the segment */ - COVER_map_clear(activeDmers); - activeSegment.begin = begin; - activeSegment.end = begin; - activeSegment.score = 0; - /* Slide the active segment through the whole epoch. - * Save the best segment in bestSegment. - */ - while (activeSegment.end < end) { - /* The dmerId for the dmer at the next position */ - U32 newDmer = ctx->dmerAt[activeSegment.end]; - /* The entry in activeDmers for this dmerId */ - U32 *newDmerOcc = COVER_map_at(activeDmers, newDmer); - /* If the dmer isn't already present in the segment add its score. */ - if (*newDmerOcc == 0) { - /* The paper suggest using the L-0.5 norm, but experiments show that it - * doesn't help. - */ - activeSegment.score += freqs[newDmer]; - } - /* Add the dmer to the segment */ - activeSegment.end += 1; - *newDmerOcc += 1; + /* Constants */ + const U32 k = parameters.k; + const U32 d = parameters.d; + const U32 dmersInK = k - d + 1; + /* Try each segment (activeSegment) and save the best (bestSegment) */ + COVER_segment_t bestSegment = {0, 0, 0}; + COVER_segment_t activeSegment; + /* Reset the activeDmers in the segment */ + COVER_map_clear(activeDmers); + /* The activeSegment starts at the beginning of the epoch. */ + activeSegment.begin = begin; + activeSegment.end = begin; + activeSegment.score = 0; + /* Slide the activeSegment through the whole epoch. + * Save the best segment in bestSegment. + */ + while (activeSegment.end < end) { + /* The dmerId for the dmer at the next position */ + U32 newDmer = ctx->dmerAt[activeSegment.end]; + /* The entry in activeDmers for this dmerId */ + U32 *newDmerOcc = COVER_map_at(activeDmers, newDmer); + /* If the dmer isn't already present in the segment add its score. */ + if (*newDmerOcc == 0) { + /* The paper suggest using the L-0.5 norm, but experiments show that it + * doesn't help. + */ + activeSegment.score += freqs[newDmer]; + } + /* Add the dmer to the segment */ + activeSegment.end += 1; + *newDmerOcc += 1; - /* If the window is now too large, drop the first position */ - if (activeSegment.end - activeSegment.begin == dmersInK + 1) { - U32 delDmer = ctx->dmerAt[activeSegment.begin]; - U32 *delDmerOcc = COVER_map_at(activeDmers, delDmer); - activeSegment.begin += 1; - *delDmerOcc -= 1; - /* If this is the last occurence of the dmer, subtract its score */ - if (*delDmerOcc == 0) { - COVER_map_remove(activeDmers, delDmer); - activeSegment.score -= freqs[delDmer]; - } - } - - /* If this segment is the best so far save it */ - if (activeSegment.score > bestSegment.score) { - bestSegment = activeSegment; + /* If the window is now too large, drop the first position */ + if (activeSegment.end - activeSegment.begin == dmersInK + 1) { + U32 delDmer = ctx->dmerAt[activeSegment.begin]; + U32 *delDmerOcc = COVER_map_at(activeDmers, delDmer); + activeSegment.begin += 1; + *delDmerOcc -= 1; + /* If this is the last occurence of the dmer, subtract its score */ + if (*delDmerOcc == 0) { + COVER_map_remove(activeDmers, delDmer); + activeSegment.score -= freqs[delDmer]; } } - { - /* Trim off the zero frequency head and tail from the segment. */ - U32 newBegin = bestSegment.end; - U32 newEnd = bestSegment.begin; - U32 pos; - for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) { - U32 freq = freqs[ctx->dmerAt[pos]]; - if (freq != 0) { - newBegin = MIN(newBegin, pos); - newEnd = pos + 1; - } - } - bestSegment.begin = newBegin; - bestSegment.end = newEnd; - /* Calculate the final score normalizing for segment length */ - bestSegment.score /= - (parameters.smoothing + (bestSegment.end - bestSegment.begin)); - } - /* If this segment is the best so far for any length save it */ - if (bestSegment.score > globalBestSegment.score) { - globalBestSegment = bestSegment; + + /* If this segment is the best so far save it */ + if (activeSegment.score > bestSegment.score) { + bestSegment = activeSegment; } } + { + /* Trim off the zero frequency head and tail from the segment. */ + U32 newBegin = bestSegment.end; + U32 newEnd = bestSegment.begin; + U32 pos; + for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) { + U32 freq = freqs[ctx->dmerAt[pos]]; + if (freq != 0) { + newBegin = MIN(newBegin, pos); + newEnd = pos + 1; + } + } + bestSegment.begin = newBegin; + bestSegment.end = newEnd; + } { /* Zero out the frequency of each dmer covered by the chosen segment. */ - size_t pos; - for (pos = globalBestSegment.begin; pos != globalBestSegment.end; ++pos) { + U32 pos; + for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) { freqs[ctx->dmerAt[pos]] = 0; } } - return globalBestSegment; + return bestSegment; } /** * Check the validity of the parameters. - * If the parameters are valid and any are default, set them to the correct - * values. - * Returns 1 on success, 0 on failure. + * Returns non-zero if the parameters are valid and 0 otherwise. */ -static int COVER_defaultParameters(COVER_params_t *parameters) { - /* kMin and d are required parameters */ - if (parameters->d == 0 || parameters->kMin == 0) { +static int COVER_checkParameters(COVER_params_t parameters) { + /* k and d are required parameters */ + if (parameters.d == 0 || parameters.k == 0) { return 0; } - /* d <= kMin */ - if (parameters->d > parameters->kMin) { + /* d <= k */ + if (parameters.d > parameters.k) { return 0; } - /* If kMax is set (non-zero) then kMin <= kMax */ - if (parameters->kMax != 0 && parameters->kMax < parameters->kMin) { - return 0; - } - /* If kMax is set, then kStep must be as well */ - if (parameters->kMax != 0 && parameters->kStep == 0) { - return 0; - } - parameters->kMax = MAX(parameters->kMin, parameters->kMax); - parameters->kStep = MAX(1, parameters->kStep); return 1; } @@ -607,17 +573,22 @@ static size_t COVER_buildDictionary(const COVER_ctx_t *ctx, U32 *freqs, /* Divide the data up into epochs of equal size. * We will select at least one segment from each epoch. */ - const U32 epochs = (U32)(dictBufferCapacity / parameters.kMax); + const U32 epochs = (U32)(dictBufferCapacity / parameters.k); const U32 epochSize = (U32)(ctx->suffixSize / epochs); size_t epoch; DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n", epochs, epochSize); + /* Loop through the epochs until there are no more segments or the dictionary + * is full. + */ for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs) { const U32 epochBegin = (U32)(epoch * epochSize); const U32 epochEnd = epochBegin + epochSize; size_t segmentSize; + /* Select a segment */ COVER_segment_t segment = COVER_selectSegment( ctx, freqs, activeDmers, epochBegin, epochEnd, parameters); + /* Trim the segment if necessary and if it is empty then we are done */ segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail); if (segmentSize == 0) { break; @@ -661,9 +632,8 @@ ZDICTLIB_API size_t COVER_trainFromBuffer( BYTE *const dict = (BYTE *)dictBuffer; COVER_ctx_t ctx; COVER_map_t activeDmers; - size_t rc; /* Checks */ - if (!COVER_defaultParameters(¶meters)) { + if (!COVER_checkParameters(parameters)) { DISPLAYLEVEL(1, "Cover parameters incorrect\n"); return ERROR(GENERIC); } @@ -672,6 +642,8 @@ ZDICTLIB_API size_t COVER_trainFromBuffer( return ERROR(GENERIC); } if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { + DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n", + ZDICT_DICTSIZE_MIN); return ERROR(dstSize_tooSmall); } /* Initialize global data */ @@ -682,7 +654,7 @@ ZDICTLIB_API size_t COVER_trainFromBuffer( DISPLAYLEVEL(1, "Failed to initialize context\n"); return ERROR(GENERIC); } - if (!COVER_map_init(&activeDmers, parameters.kMax - parameters.d + 1)) { + if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) { DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n"); COVER_ctx_destroy(&ctx); return ERROR(GENERIC); @@ -694,18 +666,17 @@ ZDICTLIB_API size_t COVER_trainFromBuffer( COVER_buildDictionary(&ctx, ctx.freqs, &activeDmers, dictBuffer, dictBufferCapacity, parameters); ZDICT_params_t zdictParams = COVER_translateParams(parameters); - DISPLAYLEVEL(2, "Dictionary content size: %u", - (U32)(dictBufferCapacity - tail)); - rc = ZDICT_finalizeDictionary(dict, dictBufferCapacity, dict + tail, - dictBufferCapacity - tail, samplesBuffer, - samplesSizes, nbSamples, zdictParams); + const size_t dictionarySize = ZDICT_finalizeDictionary( + dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, + samplesBuffer, samplesSizes, nbSamples, zdictParams); + if (!ZSTD_isError(dictionarySize)) { + DISPLAYLEVEL(2, "Constructed dictionary of size %u\n", + (U32)dictionarySize); + } + COVER_ctx_destroy(&ctx); + COVER_map_destroy(&activeDmers); + return dictionarySize; } - if (!ZSTD_isError(rc)) { - DISPLAYLEVEL(2, "Constructed dictionary of size %u\n", (U32)rc); - } - COVER_ctx_destroy(&ctx); - COVER_map_destroy(&activeDmers); - return rc; } /** @@ -713,7 +684,8 @@ ZDICTLIB_API size_t COVER_trainFromBuffer( * 1. Synchronizing threads. * 2. Saving the best parameters and dictionary. * - * All of the methods are thread safe if `ZSTD_PTHREAD` is defined. + * All of the methods except COVER_best_init() are thread safe if zstd is + * compiled with multithreaded support. */ typedef struct COVER_best_s { #ifdef ZSTD_PTHREAD @@ -852,26 +824,26 @@ typedef struct COVER_tryParameters_data_s { /** * Tries a set of parameters and upates the COVER_best_t with the results. - * This function is thread safe if ZSTD_PTHREAD is defined. + * This function is thread safe if zstd is compiled with multithreaded support. * It takes its parameters as an *OWNING* opaque pointer to support threading. */ static void COVER_tryParameters(void *opaque) { /* Save parameters as local variables */ - COVER_tryParameters_data_t *data = (COVER_tryParameters_data_t *)opaque; - const COVER_ctx_t *ctx = data->ctx; - COVER_params_t parameters = data->parameters; + COVER_tryParameters_data_t *const data = (COVER_tryParameters_data_t *)opaque; + const COVER_ctx_t *const ctx = data->ctx; + const COVER_params_t parameters = data->parameters; size_t dictBufferCapacity = data->dictBufferCapacity; size_t totalCompressedSize = ERROR(GENERIC); /* Allocate space for hash table, dict, and freqs */ COVER_map_t activeDmers; BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity); U32 *freqs = (U32 *)malloc(ctx->suffixSize * sizeof(U32)); - if (!COVER_map_init(&activeDmers, parameters.kMax - parameters.d + 1)) { + if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) { DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n"); goto _cleanup; } if (!dict || !freqs) { - DISPLAYLEVEL(1, "Failed to allocate dictionary buffer\n"); + DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n"); goto _cleanup; } /* Copy the frequencies because we need to modify them */ @@ -880,7 +852,7 @@ static void COVER_tryParameters(void *opaque) { { const size_t tail = COVER_buildDictionary(ctx, freqs, &activeDmers, dict, dictBufferCapacity, parameters); - ZDICT_params_t zdictParams = COVER_translateParams(parameters); + const ZDICT_params_t zdictParams = COVER_translateParams(parameters); dictBufferCapacity = ZDICT_finalizeDictionary( dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail, ctx->samples, ctx->samplesSizes, (unsigned)ctx->nbSamples, zdictParams); @@ -954,27 +926,42 @@ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, unsigned nbSamples, COVER_params_t *parameters) { /* constants */ - const unsigned dMin = parameters->d == 0 ? 6 : parameters->d; - const unsigned dMax = parameters->d == 0 ? 16 : parameters->d; - const unsigned min = parameters->kMin == 0 ? 32 : parameters->kMin; - const unsigned max = parameters->kMax == 0 ? 1024 : parameters->kMax; - const unsigned kStep = parameters->kStep == 0 ? 8 : parameters->kStep; - const unsigned step = MAX((max - min) / kStep, 1); + const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d; + const unsigned kMaxD = parameters->d == 0 ? 16 : parameters->d; + const unsigned kMinK = parameters->k == 0 ? kMaxD : parameters->k; + const unsigned kMaxK = parameters->k == 0 ? 2048 : parameters->k; + const unsigned kSteps = parameters->steps == 0 ? 256 : parameters->steps; + const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1); + const unsigned kIterations = + (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize); /* Local variables */ - unsigned iteration = 1; - const unsigned iterations = - (1 + (dMax - dMin) / 2) * (((1 + kStep) * (2 + kStep)) / 2) * 4; const int displayLevel = parameters->notificationLevel; + unsigned iteration = 1; unsigned d; + unsigned k; COVER_best_t best; + /* Checks */ + if (kMinK < kMaxD || kMaxK < kMinK) { + LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n"); + return ERROR(GENERIC); + } + if (nbSamples == 0) { + DISPLAYLEVEL(1, "Cover must have at least one input file\n"); + return ERROR(GENERIC); + } + if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) { + DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n", + ZDICT_DICTSIZE_MIN); + return ERROR(dstSize_tooSmall); + } + /* Initialization */ COVER_best_init(&best); - /* Turn down display level to clean up display at level 2 and below */ + /* Turn down global display level to clean up display at level 2 and below */ g_displayLevel = parameters->notificationLevel - 1; /* Loop through d first because each new value needs a new context */ - LOCALDISPLAYLEVEL(displayLevel, 3, "Trying %u different sets of parameters\n", - iterations); - for (d = dMin; d <= dMax; d += 2) { - unsigned kMin; + LOCALDISPLAYLEVEL(displayLevel, 2, "Trying %u different sets of parameters\n", + kIterations); + for (d = kMinD; d <= kMaxD; d += 2) { /* Initialize the context for this value of d */ COVER_ctx_t ctx; LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d); @@ -983,44 +970,37 @@ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, COVER_best_destroy(&best); return ERROR(GENERIC); } - /* Loop through the rest of the parameters reusing the same context */ - for (kMin = min; kMin <= max; kMin += step) { - unsigned kMax; - LOCALDISPLAYLEVEL(displayLevel, 3, "kMin=%u\n", kMin); - for (kMax = kMin; kMax <= max; kMax += step) { - unsigned smoothing; - LOCALDISPLAYLEVEL(displayLevel, 3, "kMax=%u\n", kMax); - for (smoothing = kMin / 4; smoothing <= kMin * 2; smoothing *= 2) { - /* Prepare the arguments */ - COVER_tryParameters_data_t *data = - (COVER_tryParameters_data_t *)malloc( - sizeof(COVER_tryParameters_data_t)); - LOCALDISPLAYLEVEL(displayLevel, 3, "smoothing=%u\n", smoothing); - if (!data) { - LOCALDISPLAYLEVEL(displayLevel, 1, - "Failed to allocate parameters\n"); - COVER_best_destroy(&best); - COVER_ctx_destroy(&ctx); - return ERROR(GENERIC); - } - data->ctx = &ctx; - data->best = &best; - data->dictBufferCapacity = dictBufferCapacity; - data->parameters = *parameters; - data->parameters.d = d; - data->parameters.kMin = kMin; - data->parameters.kStep = kStep; - data->parameters.kMax = kMax; - data->parameters.smoothing = smoothing; - /* Call the function and pass ownership of data to it */ - COVER_best_start(&best); - COVER_tryParameters(data); - /* Print status */ - LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%% ", - (U32)((iteration * 100) / iterations)); - ++iteration; - } + /* Loop through k reusing the same context */ + for (k = kMinK; k <= kMaxK; k += kStepSize) { + /* Prepare the arguments */ + COVER_tryParameters_data_t *data = (COVER_tryParameters_data_t *)malloc( + sizeof(COVER_tryParameters_data_t)); + LOCALDISPLAYLEVEL(displayLevel, 3, "k=%u\n", k); + if (!data) { + LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n"); + COVER_best_destroy(&best); + COVER_ctx_destroy(&ctx); + return ERROR(GENERIC); } + data->ctx = &ctx; + data->best = &best; + data->dictBufferCapacity = dictBufferCapacity; + data->parameters = *parameters; + data->parameters.k = k; + data->parameters.d = d; + data->parameters.steps = kSteps; + /* Check the parameters */ + if (!COVER_checkParameters(data->parameters)) { + DISPLAYLEVEL(1, "Cover parameters incorrect\n"); + continue; + } + /* Call the function and pass ownership of data to it */ + COVER_best_start(&best); + COVER_tryParameters(data); + /* Print status */ + LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%% ", + (U32)((iteration * 100) / kIterations)); + ++iteration; } COVER_best_wait(&best); COVER_ctx_destroy(&ctx); diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index 4a2e79448..b99990c37 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -91,11 +91,9 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_advanced(void* dictBuffer, size_t dict kMin and d are the only required parameters. */ typedef struct { - unsigned d; /* dmer size : constraint: <= kMin : Should probably be in the range [6, 16]. */ - unsigned kMin; /* Minimum segment size : constraint: > 0 */ - unsigned kStep; /* Try kStep segment lengths uniformly distributed in the range [kMin, kMax] : 0 (default) only if kMax == 0 */ - unsigned kMax; /* Maximum segment size : 0 = kMin (default) : constraint : 0 or >= kMin */ - unsigned smoothing; /* Higher smoothing => larger segments are selected. Only useful if kMax > kMin. */ + unsigned k; /* Segment size : constraint: 0 < k : Reasonable range [16, 2048+] */ + unsigned d; /* dmer size : constraint: 0 < d <= k : Reasonable range [6, 16] */ + unsigned steps; /* Number of steps : Only used for optimization : 0 means default (256) : Higher means more parameters checked */ unsigned notificationLevel; /* Write to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ unsigned dictID; /* 0 means auto mode (32-bits random value); other : force dictID value */ @@ -125,11 +123,10 @@ ZDICTLIB_API size_t COVER_trainFromBuffer(void* dictBuffer, size_t dictBufferCap `*parameters` is filled with the best parameters found, and the dictionary constructed with those parameters is stored in `dictBuffer`. - All of the {d, kMin, kStep, kMax} are optional, and smoothing is ignored. + All of the parameters d, k, steps are optional. If d is non-zero then we don't check multiple values of d, otherwise we check d = {6, 8, 10, 12, 14, 16}. - If kStep is non-zero then it is used, otherwise we pick 8. - If kMin and kMax are non-zero, then they limit the search space for kMin and kMax, - otherwise we check kMin and kMax values in the range [32, 1024]. + if steps is zero it defaults to its default value. + If k is non-zero then we don't check multiple values of k, otherwise we check steps values in [16, 2048]. @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) or an error code, which can be tested with ZDICT_isError(). diff --git a/programs/dibio.c b/programs/dibio.c index ba15d2106..33c0250c5 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -279,9 +279,7 @@ int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, dictBuffer, maxDictSize, srcBuffer, fileSizes, nbFiles, coverParams); if (!ZDICT_isError(dictSize)) { - DISPLAYLEVEL(2, "smoothing=%d\nkMin=%d\nkStep=%d\nkMax=%d\nd=%d\n", - coverParams->smoothing, coverParams->kMin, - coverParams->kStep, coverParams->kMax, coverParams->d); + DISPLAYLEVEL(2, "k=%u\nd=%u\nsteps=%u\n", coverParams->k, coverParams->d, coverParams->steps); } } else { dictSize = COVER_trainFromBuffer(dictBuffer, maxDictSize, diff --git a/programs/zstdcli.c b/programs/zstdcli.c index f4d33d3f8..54c66a320 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -205,15 +205,13 @@ static unsigned parseCoverParameters(const char* stringPtr, COVER_params_t *para { memset(params, 0, sizeof(*params)); for (; ;) { - if (longCommandWArg(&stringPtr, "smoothing=")) { params->smoothing = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "k=") || longCommandWArg(&stringPtr, "kMin=") || longCommandWArg(&stringPtr, "kmin=")) { params->kMin = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "kStep=") || longCommandWArg(&stringPtr, "kstep=")) { params->kStep = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } - if (longCommandWArg(&stringPtr, "kMax=") || longCommandWArg(&stringPtr, "kmax=")) { params->kMax = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "k=")) { params->k = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } if (longCommandWArg(&stringPtr, "d=")) { params->d = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "steps=")) { params->steps = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } return 0; } if (stringPtr[0] != 0) return 0; - DISPLAYLEVEL(4, "smoothing=%d\nkMin=%d\nkStep=%d\nkMax=%d\nd=%d\n", params->smoothing, params->kMin, params->kStep, params->kMax, params->d); + DISPLAYLEVEL(4, "k=%u\nd=%u\nsteps=%u\n", params->k, params->d, params->steps); return 1; } #endif diff --git a/tests/playTests.sh b/tests/playTests.sh index a94446e7e..5bb882aa4 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -266,13 +266,13 @@ $ZSTD -f tmp -D tmpDict $ZSTD -d tmp.zst -D tmpDict -fo result $DIFF $TESTFILE result $ECHO "- Create second (different) dictionary" -$ZSTD --train --cover=kmin=46,kstep=2,kmax=64,d=6,smoothing=23 *.c ../programs/*.c ../programs/*.h -o tmpDictC +$ZSTD --train --cover=k=56,d=8 *.c ../programs/*.c ../programs/*.h -o tmpDictC $ZSTD -d tmp.zst -D tmpDictC -fo result && die "wrong dictionary not detected!" $ECHO "- Create dictionary with short dictID" $ZSTD --train --cover=k=46,d=8 *.c ../programs/*.c --dictID 1 -o tmpDict1 cmp tmpDict tmpDict1 && die "dictionaries should have different ID !" $ECHO "- Create dictionary with size limit" -$ZSTD --train --optimize-cover=kstep=2,d=8 *.c ../programs/*.c -o tmpDict2 --maxdict 4K +$ZSTD --train --optimize-cover=steps=8 *.c ../programs/*.c -o tmpDict2 --maxdict 4K rm tmp* From a8b4fe04819241ec15567f1e6913cbf73daf4720 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 2 Jan 2017 18:45:19 -0800 Subject: [PATCH 092/227] Add COVER dictionary builder to fuzzer unit tests --- tests/fuzzer.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 86d4c6beb..37fa8dced 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -28,6 +28,7 @@ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressContinue, ZSTD_compressBlock */ #include "zstd.h" /* ZSTD_VERSION_STRING */ #include "zstd_errors.h" /* ZSTD_getErrorCode */ +#define ZDICT_STATIC_LINKING_ONLY #include "zdict.h" /* ZDICT_trainFromBuffer */ #include "datagen.h" /* RDG_genBuffer */ #include "mem.h" @@ -317,6 +318,61 @@ static int basicUnitTests(U32 seed, double compressibility) free(samplesSizes); } + /* COVER dictionary builder tests */ + { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + ZSTD_DCtx* const dctx = ZSTD_createDCtx(); + 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)); + COVER_params_t params; + U32 dictID; + + if (dictBuffer==NULL || samplesSizes==NULL) { + free(dictBuffer); + free(samplesSizes); + goto _output_error; + } + + DISPLAYLEVEL(4, "test%3i : COVER_trainFromBuffer : ", testNb++); + { U32 u; for (u=0; u Date: Mon, 9 Jan 2017 19:47:09 +0100 Subject: [PATCH 093/227] minor man page update --- programs/zstd.1 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/programs/zstd.1 b/programs/zstd.1 index 9b10b1873..db79be594 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -69,6 +69,8 @@ from standard input if it is a terminal. .PP Unless .B \-\-stdout +or +.B \-o is specified, .I files are written to a new file whose name is derived from the source @@ -159,7 +161,8 @@ No files are created or removed. # compression level [1-19] (default:3) .TP .BR \--ultra - unlocks high compression levels 20+ (maximum 22), using a lot more memory + unlocks high compression levels 20+ (maximum 22), using a lot more memory. +Note that decompression will also require more memory when using these levels. .TP .B \-D file use `file` as Dictionary to compress or decompress FILE(s) @@ -293,7 +296,7 @@ There are 8 strategies numbered from 0 to 7, from faster to stronger: .PD Specify the maximum number of bits for a match distance. .IP "" -The higher number of bits increases the chance to find a match what usually improves compression ratio. +The higher number of bits increases the chance to find a match what usually improves compression ratio. It also increases memory requirements for compressor and decompressor. .IP "" The minimum \fIwlog\fR is 10 (1 KiB) and the maximum is 25 (32 MiB) for 32-bit compilation and 27 (128 MiB) for 64-bit compilation. @@ -319,7 +322,7 @@ The minimum \fIhlog\fR is 6 (64 B) and the maximum is 25 (32 MiB) for 32-bit com .PD Specify the maximum number of bits for a hash chain or a binary tree. .IP "" -The higher number of bits increases the chance to find a match what usually improves compression ratio. +The higher number of bits increases the chance to find a match what usually improves compression ratio. It also slows down compression speed and increases memory requirements for compression. This option is ignored for the ZSTD_fast strategy. .IP "" From c220d4c74daf105b8425b3c7bfee4910f7286b8e Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 9 Jan 2017 16:49:04 -0800 Subject: [PATCH 094/227] Use COVER_MEMMULT when training with COVER. --- programs/dibio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programs/dibio.c b/programs/dibio.c index 33c0250c5..5ef202c8a 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -235,7 +235,8 @@ int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, void* const dictBuffer = malloc(maxDictSize); size_t* const fileSizes = (size_t*)malloc(nbFiles * sizeof(size_t)); unsigned long long const totalSizeToLoad = DiB_getTotalCappedFileSize(fileNamesTable, nbFiles); - size_t const maxMem = DiB_findMaxMem(totalSizeToLoad * MEMMULT) / MEMMULT; + size_t const memMult = params ? MEMMULT : COVER_MEMMULT; + size_t const maxMem = DiB_findMaxMem(totalSizeToLoad * memMult) / memMult; size_t benchedSize = (size_t) MIN ((unsigned long long)maxMem, totalSizeToLoad); void* const srcBuffer = malloc(benchedSize+NOISELENGTH); int result = 0; From 555e2816371617a8868afb61c9eccbb47909054e Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 9 Jan 2017 16:50:00 -0800 Subject: [PATCH 095/227] Handle large input size in 32-bit mode correctly --- lib/dictBuilder/cover.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c index c8bee4e26..089f077c1 100644 --- a/lib/dictBuilder/cover.c +++ b/lib/dictBuilder/cover.c @@ -28,7 +28,7 @@ /*-************************************* * Constants ***************************************/ -#define COVER_MAX_SAMPLES_SIZE ((U32)-1) +#define COVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((U32)-1) : ((U32)1 GB)) /*-************************************* * Console display @@ -500,7 +500,9 @@ static int COVER_ctx_init(COVER_ctx_t *ctx, const void *samplesBuffer, const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples); /* Checks */ if (totalSamplesSize < d || - totalSamplesSize > (size_t)COVER_MAX_SAMPLES_SIZE) { + totalSamplesSize >= (size_t)COVER_MAX_SAMPLES_SIZE) { + DISPLAYLEVEL(1, "Total samples size is too large, maximum size is %u MB\n", + (COVER_MAX_SAMPLES_SIZE >> 20)); return 0; } /* Zero the context */ @@ -518,6 +520,7 @@ static int COVER_ctx_init(COVER_ctx_t *ctx, const void *samplesBuffer, /* The offsets of each file */ ctx->offsets = (size_t *)malloc((nbSamples + 1) * sizeof(size_t)); if (!ctx->suffix || !ctx->dmerAt || !ctx->offsets) { + DISPLAYLEVEL(1, "Failed to allocate scratch buffers\n"); COVER_ctx_destroy(ctx); return 0; } @@ -651,7 +654,6 @@ ZDICTLIB_API size_t COVER_trainFromBuffer( /* Initialize context and activeDmers */ if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, parameters.d)) { - DISPLAYLEVEL(1, "Failed to initialize context\n"); return ERROR(GENERIC); } if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) { From 8d984699dbfd3a5fb3ad74e0ebac93085c3eb90d Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 9 Jan 2017 17:00:12 -0800 Subject: [PATCH 096/227] Document memory requirements for COVER algorithm --- lib/dictBuilder/zdict.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index b99990c37..1b3bcb5b7 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -108,6 +108,7 @@ typedef struct { The resulting dictionary will be saved into `dictBuffer`. @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) or an error code, which can be tested with ZDICT_isError(). + Note : COVER_trainFromBuffer() requires about 9 bytes of memory for each input byte. Tips : In general, a reasonable dictionary has a size of ~ 100 KB. It's obviously possible to target smaller or larger ones, just by specifying different `dictBufferCapacity`. In general, it's recommended to provide a few thousands samples, but this can vary a lot. @@ -131,6 +132,7 @@ ZDICTLIB_API size_t COVER_trainFromBuffer(void* dictBuffer, size_t dictBufferCap @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) or an error code, which can be tested with ZDICT_isError(). On success `*parameters` contains the parameters selected. + Note : COVER_optimizeTrainFromBuffer() requires about 9 bytes of memory for each input byte. */ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, From 47557ba2b259828960598e1040af3f1d71222cd3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 11 Jan 2017 15:35:56 +0100 Subject: [PATCH 097/227] fixed ZSTDMT_createCCtxPool() when inner CCtx creation fails --- lib/compress/zstdmt_compress.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index dd495c988..9657bdc62 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -74,7 +74,7 @@ static void ZSTDMT_freeBufferPool(ZSTDMT_bufferPool* bufPool) free(bufPool); } -/* note : invocation only from main thread ! */ +/* assumption : invocation from main thread only ! */ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) { if (pool->nbBuffers) { /* try to use an existing buffer */ @@ -92,7 +92,7 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) } } -/* effectively store buffer for later re-use, up to pool capacity */ +/* store buffer for later re-use, up to pool capacity */ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf) { if (pool->nbBuffers < pool->totalBuffers) { @@ -122,15 +122,12 @@ typedef struct { void ZSTDMT_compressFrame(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; - DEBUGLOG(5, "thread : compressing %u bytes from frame %u with ZSTD_compressCCtx : ", (unsigned)job->srcSize, job->jobCompleted); job->cSize = ZSTD_compressCCtx(job->cctx, job->dstBuff.start, job->dstBuff.size, job->srcStart, job->srcSize, job->compressionLevel); - DEBUGLOG(5, "compressed to %u bytes ", (unsigned)job->cSize); - DEBUGLOG(5, "sending jobCompleted signal"); + DEBUGLOG(5, "frame %u : compressed %u bytes into %u bytes ", (unsigned)job->frameID, (unsigned)job->srcSize, (unsigned)job->cSize); pthread_mutex_lock(job->jobCompleted_mutex); job->jobCompleted = 1; pthread_cond_signal(job->jobCompleted_cond); pthread_mutex_unlock(job->jobCompleted_mutex); - DEBUGLOG(5, "ZSTDMT_compressFrame completed"); } @@ -142,16 +139,22 @@ typedef struct { ZSTD_CCtx* cctx[1]; /* variable size */ } ZSTDMT_CCtxPool; -/* note : CCtxPool invocation only from main thread */ +/* assumption : CCtxPool invocation only from main thread */ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads) { ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) calloc(1, sizeof(ZSTDMT_CCtxPool) + nbThreads*sizeof(ZSTD_CCtx*)); if (!cctxPool) return NULL; - { unsigned u; - for (u=0; ucctx[u] = ZSTD_createCCtx(); /* check for NULL result ! */ - } + { unsigned threadNb; + for (threadNb=0; threadNbcctx[threadNb] = ZSTD_createCCtx(); + if (cctxPool->cctx[threadNb]==NULL) { /* failed cctx allocation : abort cctxPool creation */ + unsigned u; + for (u=0; ucctx[u]); + free(cctxPool); + return NULL; + } } } cctxPool->totalCCtx = cctxPool->availCCtx = nbThreads; return cctxPool; } From 8ce1cc2bec798fb959dcc403b462eebf3a985119 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 11 Jan 2017 15:44:26 +0100 Subject: [PATCH 098/227] improved ZSTD_createCCtxPool() cancellation use ZSTD_freeCCtxPool() to release the partially created pool. avoids to duplicate logic. Also : identified a new difficult corner case : when freeing the Pool, all CCtx should be previously released back to the pool. Otherwise, it means some CCtx are still in use. There is currently no clear policy on what to do in such a case. Note : it's supposed to never happen. Since pool creation/usage is static, it has no external user, which limits risks. --- lib/compress/zstdmt_compress.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 9657bdc62..5c7b654ab 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -141,6 +141,15 @@ typedef struct { /* assumption : CCtxPool invocation only from main thread */ +/* note : all CCtx borrowed from the pool should be released back to the pool _before_ freeing the pool */ +static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool) +{ + unsigned u; + for (u=0; uavailCCtx; u++) /* note : availCCtx is supposed == totalCCtx; otherwise, some CCtx are still in use */ + ZSTD_freeCCtx(pool->cctx[u]); + free(pool); +} + static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads) { ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) calloc(1, sizeof(ZSTDMT_CCtxPool) + nbThreads*sizeof(ZSTD_CCtx*)); @@ -149,10 +158,8 @@ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads) for (threadNb=0; threadNbcctx[threadNb] = ZSTD_createCCtx(); if (cctxPool->cctx[threadNb]==NULL) { /* failed cctx allocation : abort cctxPool creation */ - unsigned u; - for (u=0; ucctx[u]); - free(cctxPool); + cctxPool->totalCCtx = cctxPool->availCCtx = threadNb; + ZSTDMT_freeCCtxPool(cctxPool); return NULL; } } } cctxPool->totalCCtx = cctxPool->availCCtx = nbThreads; @@ -165,7 +172,7 @@ static ZSTD_CCtx* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* pool) pool->availCCtx--; return pool->cctx[pool->availCCtx]; } - /* should not be possible, since totalCCtx==nbThreads */ + /* note : should not be possible, since totalCCtx==nbThreads */ return ZSTD_createCCtx(); } @@ -174,18 +181,10 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) if (pool->availCCtx < pool->totalCCtx) pool->cctx[pool->availCCtx++] = cctx; else - /* should not be possible, since totalCCtx==nbThreads */ + /* note : should not be possible, since totalCCtx==nbThreads */ ZSTD_freeCCtx(cctx); } -static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool) -{ - unsigned u; - for (u=0; utotalCCtx; u++) - ZSTD_freeCCtx(pool->cctx[u]); - free(pool); -} - struct ZSTDMT_CCtx_s { POOL_ctx* factory; From 085179bb78248796775193433c0596969511da55 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 11 Jan 2017 15:58:05 +0100 Subject: [PATCH 099/227] fixed ZSTDMT_createCCtx() : checked inner objects are properly created --- lib/compress/zstdmt_compress.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 5c7b654ab..5fbf32b93 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -206,12 +206,16 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) cctx->factory = POOL_create(nbThreads, 1); cctx->buffPool = ZSTDMT_createBufferPool(nbThreads); cctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads); - pthread_mutex_init(&cctx->jobCompleted_mutex, NULL); + if (!cctx->factory | !cctx->buffPool | !cctx->cctxPool) { /* one object was not created */ + ZSTDMT_freeCCtx(cctx); + return NULL; + } + pthread_mutex_init(&cctx->jobCompleted_mutex, NULL); /* Todo : check init function return */ pthread_cond_init(&cctx->jobCompleted_cond, NULL); return cctx; } -size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) /* incompleted ! */ +size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) { POOL_free(mtctx->factory); ZSTDMT_freeBufferPool(mtctx->buffPool); From 04cbc364996dc6688436500b327003b63537631c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 11 Jan 2017 16:08:08 +0100 Subject: [PATCH 100/227] minor refactor (release CCtx 1st) and comment clarification --- lib/compress/zstdmt_compress.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 5fbf32b93..8471b7509 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -277,21 +277,21 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, pthread_mutex_lock(&mtctx->jobCompleted_mutex); while (mtctx->jobs[frameID].jobCompleted==0) { - DEBUGLOG(4, "waiting for jobCompleted signal for frame %u", frameID); + DEBUGLOG(4, "waiting for jobCompleted signal from frame %u", frameID); pthread_cond_wait(&mtctx->jobCompleted_cond, &mtctx->jobCompleted_mutex); } pthread_mutex_unlock(&mtctx->jobCompleted_mutex); + ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[frameID].cctx); { size_t const cSize = mtctx->jobs[frameID].cSize; if (ZSTD_isError(cSize)) return cSize; if (dstPos + cSize > dstCapacity) return ERROR(dstSize_tooSmall); - if (frameID) { + if (frameID) { /* note : frame 0 is already written directly into dst */ memcpy((char*)dst + dstPos, mtctx->jobs[frameID].dstBuff.start, cSize); ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[frameID].dstBuff); } dstPos += cSize ; } - ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[frameID].cctx); } DEBUGLOG(3, "compressed size : %u ", (U32)dstPos); return dstPos; From 5eb749e734120c3b50c4e434b45728ac7cdcc451 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 11 Jan 2017 18:21:25 +0100 Subject: [PATCH 101/227] ZSTDMT_compress() creates a single frame The new strategy involves cutting frame at block level. The result is a single frame, preserving ZSTD_getDecompressedSize() As a consequence, bench can now make a full round-trip, since the result is compatible with ZSTD_decompress(). This strategy will not make it possible to decode the frame with multiple threads since the exact cut between independent blocks is not known. MT decoding needs further discussions. --- Makefile | 2 +- lib/compress/zstd_compress.c | 6 ++++-- lib/compress/zstdmt_compress.c | 31 +++++++++++++++++++++++++------ lib/zstd.h | 6 +++--- programs/Makefile | 2 +- programs/bench.c | 17 +++++++++-------- 6 files changed, 43 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index 19b12d0ef..0a3634c39 100644 --- a/Makefile +++ b/Makefile @@ -88,7 +88,7 @@ travis-install: $(MAKE) install PREFIX=~/install_test_dir gpptest: clean - $(MAKE) -C programs all CC=g++ CFLAGS="-O3 -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror" + CC=g++ $(MAKE) -C programs all CFLAGS="-O3 -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror" gcc5test: clean gcc-5 -v diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 7626b33a6..c4dbb6ced 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2408,12 +2408,14 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, cctx->nextSrc = ip + srcSize; - { size_t const cSize = frame ? + if (srcSize) { + size_t const cSize = frame ? ZSTD_compress_generic (cctx, dst, dstCapacity, src, srcSize, lastFrameChunk) : ZSTD_compressBlock_internal (cctx, dst, dstCapacity, src, srcSize); if (ZSTD_isError(cSize)) return cSize; return cSize + fhSize; - } + } else + return fhSize; } diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 8471b7509..ae986468b 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -28,8 +28,8 @@ if (g_debugLevel>=MUTEX_WAIT_TIME_DLEVEL) { \ unsigned long long afterTime = GetCurrentClockTimeMicroseconds(); \ unsigned long long elapsedTime = (afterTime-beforeTime); \ if (elapsedTime > 1000) { /* or whatever threshold you like; I'm using 1 millisecond here */ \ - DEBUGLOG(MUTEX_WAIT_TIME_DLEVEL, "Thread %li took %llu microseconds to acquire mutex %s \n", \ - (long int) pthread_self(), elapsedTime, #mutex); \ + DEBUGLOG(MUTEX_WAIT_TIME_DLEVEL, "Thread took %llu microseconds to acquire mutex %s \n", \ + elapsedTime, #mutex); \ } \ } else pthread_mutex_lock(mutex); @@ -112,6 +112,7 @@ typedef struct { buffer_t dstBuff; int compressionLevel; unsigned frameID; + unsigned long long fullFrameSize; size_t cSize; unsigned jobCompleted; pthread_mutex_t* jobCompleted_mutex; @@ -122,9 +123,26 @@ typedef struct { void ZSTDMT_compressFrame(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; - job->cSize = ZSTD_compressCCtx(job->cctx, job->dstBuff.start, job->dstBuff.size, job->srcStart, job->srcSize, job->compressionLevel); + buffer_t dstBuff = job->dstBuff; + ZSTD_parameters const params = ZSTD_getParams(job->compressionLevel, job->fullFrameSize, 0); + size_t hSize = ZSTD_compressBegin_advanced(job->cctx, NULL, 0, params, job->fullFrameSize); + if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } + hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, 0); /* flush frame header */ + if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } + if ((job->frameID & 1) == 0) { /* preserve frame header when it is first beginning of frame */ + dstBuff.start = (char*)dstBuff.start + hSize; + dstBuff.size -= hSize; + } else + hSize = 0; + + job->cSize = (job->frameID>=2) ? /* last chunk signal */ + ZSTD_compressEnd(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize) : + ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize); + if (!ZSTD_isError(job->cSize)) job->cSize += hSize; DEBUGLOG(5, "frame %u : compressed %u bytes into %u bytes ", (unsigned)job->frameID, (unsigned)job->srcSize, (unsigned)job->cSize); - pthread_mutex_lock(job->jobCompleted_mutex); + +_endJob: + PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); job->jobCompleted = 1; pthread_cond_signal(job->jobCompleted_cond); pthread_mutex_unlock(job->jobCompleted_mutex); @@ -254,10 +272,11 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, mtctx->jobs[u].srcStart = srcStart + frameStartPos; mtctx->jobs[u].srcSize = frameSize; + mtctx->jobs[u].fullFrameSize = srcSize; mtctx->jobs[u].compressionLevel = compressionLevel; mtctx->jobs[u].dstBuff = dstBuffer; mtctx->jobs[u].cctx = cctx; - mtctx->jobs[u].frameID = u; + mtctx->jobs[u].frameID = (u>0) | ((u==nbFrames-1)<<1); mtctx->jobs[u].jobCompleted = 0; mtctx->jobs[u].jobCompleted_mutex = &mtctx->jobCompleted_mutex; mtctx->jobs[u].jobCompleted_cond = &mtctx->jobCompleted_cond; @@ -275,7 +294,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, for (frameID=0; frameIDjobCompleted_mutex); + PTHREAD_MUTEX_LOCK(&mtctx->jobCompleted_mutex); while (mtctx->jobs[frameID].jobCompleted==0) { DEBUGLOG(4, "waiting for jobCompleted signal from frame %u", frameID); pthread_cond_wait(&mtctx->jobCompleted_cond, &mtctx->jobCompleted_mutex); diff --git a/lib/zstd.h b/lib/zstd.h index 55cc466d7..198f45eac 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -561,10 +561,10 @@ ZSTDLIB_API size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds); In which case, it will "discard" the relevant memory section from its history. Finish a frame with ZSTD_compressEnd(), which will write the last block(s) and optional checksum. - It's possible to use a NULL,0 src content, in which case, it will write a final empty block to end the frame, - Without last block mark, frames will be considered unfinished (broken) by decoders. + It's possible to use srcSize==0, in which case, it will write a final empty block to end the frame. + Without last block mark, frames will be considered unfinished (corrupted) by decoders. - You can then reuse `ZSTD_CCtx` (ZSTD_compressBegin()) to compress some new frame. + `ZSTD_CCtx` object can be re-used (ZSTD_compressBegin()) to compress some new frame. */ /*===== Buffer-less streaming compression functions =====*/ diff --git a/programs/Makefile b/programs/Makefile index 77d5ab6e4..ff95ddc62 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -129,7 +129,7 @@ gzstd: zstdmt: CPPFLAGS += -DZSTD_PTHREAD zstdmt: LDFLAGS += -lpthread -zstdmt: clean zstd +zstdmt: zstd generate_res: windres/generate_res.bat diff --git a/programs/bench.c b/programs/bench.c index a3c013a8b..40e1d4aba 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -321,7 +321,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, memcpy(compressedBuffer, srcBuffer, loadedCompressedSize); } -#if 1 +#if 0 /* disable decompression test */ dCompleted=1; (void)totalDTime; (void)fastestD; (void)crcOrig; /* unused when decompression disabled */ #else @@ -330,13 +330,14 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, UTIL_sleepMilli(1); /* give processor time to other processes */ UTIL_waitForNextTick(ticksPerSecond); - UTIL_getTime(&clockStart); if (!dCompleted) { U64 clockLoop = g_nbSeconds ? TIMELOOP_MICROSEC : 1; U32 nbLoops = 0; + clock_us_t clockStart; ZSTD_DDict* const ddict = ZSTD_createDDict(dictBuffer, dictBufferSize); if (!ddict) EXM_THROW(2, "ZSTD_createDDict() allocation failure"); + clockStart = BMK_clockMicroSec(); do { U32 blockNb; for (blockNb=0; blockNb= maxTime); } } From 107bcbbbc23c73e8d48f066adc8959af690c0b12 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 12 Jan 2017 01:25:46 +0100 Subject: [PATCH 102/227] zstdmt : changed internal naming from frame to chunk Since the result of mt compression is a single frame, changed naming, which implied the concatenation of multiple frames. minor : ensures that content size is written in header --- lib/compress/zstdmt_compress.c | 261 ++++++++++++++++++++++----------- lib/compress/zstdmt_compress.h | 16 +- 2 files changed, 191 insertions(+), 86 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index ae986468b..6fe37a6f4 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1,7 +1,8 @@ #include /* malloc */ +#include /* memcpy */ #include /* threadpool */ #include "threading.h" /* mutex */ -#include "zstd_internal.h" /* MIN, ERROR */ +#include "zstd_internal.h" /* MIN, ERROR, ZSTD_* */ #include "zstdmt_compress.h" #if 0 @@ -43,7 +44,7 @@ if (g_debugLevel>=MUTEX_WAIT_TIME_DLEVEL) { \ #define ZSTDMT_NBTHREADS_MAX 128 -/* === Buffer Pool === */ +/* ===== Buffer Pool ===== */ typedef struct buffer_s { void* start; @@ -82,13 +83,12 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) size_t const availBufferSize = buf.size; if ((availBufferSize >= bSize) & (availBufferSize <= 10*bSize)) /* large enough, but not too much */ return buf; - free(buf.start); /* size conditions not respected : create a new buffer */ + free(buf.start); /* size conditions not respected : scratch this buffer and create a new one */ } /* create new buffer */ - { buffer_t buf; - buf.size = bSize; - buf.start = malloc(bSize); - return buf; + { void* const start = malloc(bSize); + if (start==NULL) bSize = 0; + return (buffer_t) { start, bSize }; /* note : start can be NULL if malloc fails ! */ } } @@ -104,52 +104,7 @@ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf) } - -typedef struct { - ZSTD_CCtx* cctx; - const void* srcStart; - size_t srcSize; - buffer_t dstBuff; - int compressionLevel; - unsigned frameID; - unsigned long long fullFrameSize; - size_t cSize; - unsigned jobCompleted; - pthread_mutex_t* jobCompleted_mutex; - pthread_cond_t* jobCompleted_cond; -} ZSTDMT_jobDescription; - -/* ZSTDMT_compressFrame() : POOL_function type */ -void ZSTDMT_compressFrame(void* jobDescription) -{ - ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; - buffer_t dstBuff = job->dstBuff; - ZSTD_parameters const params = ZSTD_getParams(job->compressionLevel, job->fullFrameSize, 0); - size_t hSize = ZSTD_compressBegin_advanced(job->cctx, NULL, 0, params, job->fullFrameSize); - if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } - hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, 0); /* flush frame header */ - if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } - if ((job->frameID & 1) == 0) { /* preserve frame header when it is first beginning of frame */ - dstBuff.start = (char*)dstBuff.start + hSize; - dstBuff.size -= hSize; - } else - hSize = 0; - - job->cSize = (job->frameID>=2) ? /* last chunk signal */ - ZSTD_compressEnd(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize) : - ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize); - if (!ZSTD_isError(job->cSize)) job->cSize += hSize; - DEBUGLOG(5, "frame %u : compressed %u bytes into %u bytes ", (unsigned)job->frameID, (unsigned)job->srcSize, (unsigned)job->cSize); - -_endJob: - PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); - job->jobCompleted = 1; - pthread_cond_signal(job->jobCompleted_cond); - pthread_mutex_unlock(job->jobCompleted_mutex); -} - - -/* === CCtx Pool === */ +/* ===== CCtx Pool ===== */ typedef struct { unsigned totalCCtx; @@ -191,11 +146,12 @@ static ZSTD_CCtx* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* pool) return pool->cctx[pool->availCCtx]; } /* note : should not be possible, since totalCCtx==nbThreads */ - return ZSTD_createCCtx(); + return ZSTD_createCCtx(); /* note : can be NULL is creation fails ! */ } static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) { + if (cctx==NULL) return; /* release on NULL */ if (pool->availCCtx < pool->totalCCtx) pool->cctx[pool->availCCtx++] = cctx; else @@ -204,6 +160,55 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) } +/* ===== Thread worker ===== */ + +typedef struct { + ZSTD_CCtx* cctx; + const void* srcStart; + size_t srcSize; + buffer_t dstBuff; + size_t cSize; + size_t dstFlushed; + unsigned long long fullFrameSize; + unsigned firstChunk; + unsigned lastChunk; + unsigned jobCompleted; + pthread_mutex_t* jobCompleted_mutex; + pthread_cond_t* jobCompleted_cond; + ZSTD_parameters params; +} ZSTDMT_jobDescription; + +/* ZSTDMT_compressChunk() : POOL_function type */ +void ZSTDMT_compressChunk(void* jobDescription) +{ + ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; + buffer_t dstBuff = job->dstBuff; + size_t hSize = ZSTD_compressBegin_advanced(job->cctx, NULL, 0, job->params, job->fullFrameSize); + if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } + hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, 0); /* flush frame header */ + if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } + if (job->firstChunk) { /* preserve frame header when it is first chunk - otherwise, overwrite */ + dstBuff.start = (char*)dstBuff.start + hSize; + dstBuff.size -= hSize; + } else + hSize = 0; + + job->cSize = (job->lastChunk) ? /* last chunk signal */ + ZSTD_compressEnd(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize) : + ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize); + if (!ZSTD_isError(job->cSize)) job->cSize += hSize; + DEBUGLOG(5, "chunk %u : compressed %u bytes into %u bytes ", (unsigned)job->lastChunk, (unsigned)job->srcSize, (unsigned)job->cSize); + +_endJob: + PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); + job->jobCompleted = 1; + pthread_cond_signal(job->jobCompleted_cond); + pthread_mutex_unlock(job->jobCompleted_mutex); +} + + +/* ===== Multi-threaded compression ===== */ + struct ZSTDMT_CCtx_s { POOL_ctx* factory; ZSTDMT_bufferPool* buffPool; @@ -250,64 +255,66 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, const void* src, size_t srcSize, int compressionLevel) { - ZSTD_parameters const params = ZSTD_getParams(compressionLevel, srcSize, 0); - size_t const frameSizeTarget = (size_t)1 << (params.cParams.windowLog + 2); - unsigned const nbFramesMax = (unsigned)(srcSize / frameSizeTarget) + (srcSize < frameSizeTarget) /* min 1 */; - unsigned const nbFrames = MIN(nbFramesMax, mtctx->nbThreads); - size_t const avgFrameSize = (srcSize + (nbFrames-1)) / nbFrames; + ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize, 0); + size_t const chunkTargetSize = (size_t)1 << (params.cParams.windowLog + 2); + unsigned const nbChunksMax = (unsigned)(srcSize / chunkTargetSize) + (srcSize < chunkTargetSize) /* min 1 */; + unsigned const nbChunks = MIN(nbChunksMax, mtctx->nbThreads); + size_t const proposedChunkSize = (srcSize + (nbChunks-1)) / nbChunks; + size_t const avgChunkSize = ((proposedChunkSize & 0x1FFFF) < 0xFFFF) ? proposedChunkSize + 0xFFFF : proposedChunkSize; /* avoid too small last block */ size_t remainingSrcSize = srcSize; const char* const srcStart = (const char*)src; size_t frameStartPos = 0; - - DEBUGLOG(2, "windowLog : %u => frameSizeTarget : %u ", params.cParams.windowLog, (U32)frameSizeTarget); - DEBUGLOG(2, "nbFrames : %u (size : %u bytes) ", nbFrames, (U32)avgFrameSize); + DEBUGLOG(3, "windowLog : %2u => chunkTargetSize : %u bytes ", params.cParams.windowLog, (U32)chunkTargetSize); + DEBUGLOG(2, "nbChunks : %2u (chunkSize : %u bytes) ", nbChunks, (U32)avgChunkSize); + params.fParams.contentSizeFlag = 1; { unsigned u; - for (u=0; ubuffPool, dstBufferCapacity) : (buffer_t){ dst, dstCapacity }; - ZSTD_CCtx* cctx = ZSTDMT_getCCtx(mtctx->cctxPool); + ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(mtctx->cctxPool); /* should check for NULL ! */ mtctx->jobs[u].srcStart = srcStart + frameStartPos; - mtctx->jobs[u].srcSize = frameSize; + mtctx->jobs[u].srcSize = chunkSize; mtctx->jobs[u].fullFrameSize = srcSize; - mtctx->jobs[u].compressionLevel = compressionLevel; + mtctx->jobs[u].params = params; mtctx->jobs[u].dstBuff = dstBuffer; mtctx->jobs[u].cctx = cctx; - mtctx->jobs[u].frameID = (u>0) | ((u==nbFrames-1)<<1); + mtctx->jobs[u].firstChunk = (u==0); + mtctx->jobs[u].lastChunk = (u==nbChunks-1); mtctx->jobs[u].jobCompleted = 0; mtctx->jobs[u].jobCompleted_mutex = &mtctx->jobCompleted_mutex; mtctx->jobs[u].jobCompleted_cond = &mtctx->jobCompleted_cond; - DEBUGLOG(3, "posting job %u (%u bytes)", u, (U32)frameSize); - POOL_add(mtctx->factory, ZSTDMT_compressFrame, &mtctx->jobs[u]); + DEBUGLOG(3, "posting job %u (%u bytes)", u, (U32)chunkSize); + POOL_add(mtctx->factory, ZSTDMT_compressChunk, &mtctx->jobs[u]); - frameStartPos += frameSize; - remainingSrcSize -= frameSize; + frameStartPos += chunkSize; + remainingSrcSize -= chunkSize; } } - /* note : since nbFrames <= nbThreads, all jobs should be running immediately in parallel */ + /* note : since nbChunks <= nbThreads, all jobs should be running immediately in parallel */ - { unsigned frameID; + { unsigned chunkID; size_t dstPos = 0; - for (frameID=0; frameIDjobCompleted_mutex); - while (mtctx->jobs[frameID].jobCompleted==0) { - DEBUGLOG(4, "waiting for jobCompleted signal from frame %u", frameID); + while (mtctx->jobs[chunkID].jobCompleted==0) { + DEBUGLOG(4, "waiting for jobCompleted signal from chunk %u", chunkID); pthread_cond_wait(&mtctx->jobCompleted_cond, &mtctx->jobCompleted_mutex); } pthread_mutex_unlock(&mtctx->jobCompleted_mutex); - ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[frameID].cctx); - { size_t const cSize = mtctx->jobs[frameID].cSize; + ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[chunkID].cctx); + { size_t const cSize = mtctx->jobs[chunkID].cSize; if (ZSTD_isError(cSize)) return cSize; if (dstPos + cSize > dstCapacity) return ERROR(dstSize_tooSmall); - if (frameID) { /* note : frame 0 is already written directly into dst */ - memcpy((char*)dst + dstPos, mtctx->jobs[frameID].dstBuff.start, cSize); - ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[frameID].dstBuff); + if (chunkID) { /* note : chunk 0 is already written directly into dst */ + memcpy((char*)dst + dstPos, mtctx->jobs[chunkID].dstBuff.start, cSize); + ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[chunkID].dstBuff); } dstPos += cSize ; } @@ -317,3 +324,89 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, } } + + +/* ====================================== */ +/* ======= Streaming API ======= */ +/* ====================================== */ + +#if 0 + +size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { + zcs->params = ZSTD_getParams(compressionLevel, 0, 0); + zcs->targetSectionSize = 1 << (zcs->params.cParams.windowLog + 2); + zcs->inBuffSize = 5 * (1 << zcs->params.cParams.windowLog); + zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); /* check for NULL ! */ + zcs->inBuff.current = 0; + zcs->doneJobID = 0; + zcs->nextJobID = 0; + return 0; +} + +typedef struct { + buffer_t buffer; + unsigned current; +} inBuff_t; + + +size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input) +{ + /* fill input buffer */ + { size_t const toLoad = MIN(input->size - input->pos, zcs->inBuffSize - zcs->inBuff.current); + memcpy((char*)zcs->inBuff.buffer.start + zcs->inBuff.current, input->src, toLoad); + input->pos += toLoad; + } + + if (zcs->inBuff.current == zcs->inBuffSize) { /* filled enough : let's compress */ + size_t const dstBufferCapacity = ZSTD_compressBound(zcs->targetSectionSize); + buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->targetSectionSize); /* should check for NULL */ + ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); /* should check for NULL */ + unsigned const jobID = zcs->nextJobID & zcs->jobIDmask; + + zcs->jobs[jobID].srcStart = zcs->inBuff.start; + zcs->jobs[jobID].srcSize = zcs->targetSectionSize; + zcs->jobs[jobID].fullFrameSize = 0; + zcs->jobs[jobID].compressionLevel = zcs->compressionLevel; + zcs->jobs[jobID].dstBuff = dstBuffer; + zcs->jobs[jobID].cctx = cctx; + zcs->jobs[jobID].frameID = (jobID>0); + zcs->jobs[jobID].jobCompleted = 0; + zcs->jobs[jobID].dstFlushed = 0; + zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex; + zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond; + + /* get a new buffer for next input - save remaining into it */ + zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); /* check for NULL ! */ + zcs->inBuff.current = zcs->inBuffSize - zcs->targetSectionSize; + memcpy(zcs->inBuff.buffer.start, (char*)zcs->jobs[jobID].srcStart + zcs->targetSectionSize, zcs->inBuff.current); + + DEBUGLOG(3, "posting job %u (%u bytes)", jobID, (U32)zcs->jobs[jobID].srcSize); + POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); + zcs->nextJobID++; + } + + /* check if there is any data available to flush */ + { unsigned const jobID = zcs->doneJobID & zcs->jobIDmask; + ZSTDMT_jobDescription job = zcs->jobs[jobID]; + if (job.jobCompleted) { /* job completed : output can be flushed */ + size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); + ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[jobID].cctx = NULL; /* release cctx for future task */ + free(job.srcStart); zcs->jobs[jobID].srcStart = NULL; /* note : need a buff_t for release */ + memcpy((char*)output->dst + output->pos, job.dstBuff.start + job.dstFlushed, toWrite); + output->pos += toWrite; + job.dstFlushed += toWrite; + if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => next one */ + ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); + zcs->doneJobID++; + } else + zcs->jobs[jobID].dstFlushed = job.dstFlushed; + } } + + /* recommended next input size : fill current input buffer */ + return zcs->inBuffSize - zcs->inBuff.current; +} + +size_t ZSTDMT_flushStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); +size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); + +#endif diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 73ee379b8..ca5d6b601 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -1,12 +1,24 @@ +/* === Dependencies === */ #include /* size_t */ +#include "zstd.h" /* ZSTD_inBuffer, ZSTD_outBuffer */ + + +/* === Simple one-pass functions === */ typedef struct ZSTDMT_CCtx_s ZSTDMT_CCtx; - -ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads); +ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbThreads); size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* cctx); size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel); + + +/* === Streaming functions === */ + +size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel); +size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); +size_t ZSTDMT_flushStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); +size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); From b05c4828eaf67fce7d2be9ef70a53591351b9ec8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 12 Jan 2017 02:01:28 +0100 Subject: [PATCH 103/227] zstdmt : correctly check for cctx and buffer allocation Result from getBuffer and getCCtx could be NULL when allocation fails. Now correctly checks : job creation stop and last job reports an allocation error. releaseBuffer and releaseCCtx are now also compatible with NULL input. Identified a new potential issue : when early job fails, later jobs are not collected for resource retrieval. --- lib/compress/zstd_compress.c | 4 ++-- lib/compress/zstdmt_compress.c | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c4dbb6ced..d4800dce1 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2627,9 +2627,9 @@ size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t di } -size_t ZSTD_compressBegin(ZSTD_CCtx* zc, int compressionLevel) +size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel) { - return ZSTD_compressBegin_usingDict(zc, NULL, 0, compressionLevel); + return ZSTD_compressBegin_usingDict(cctx, NULL, 0, compressionLevel); } diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 6fe37a6f4..7e8bb9f3e 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -95,6 +95,7 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) /* store buffer for later re-use, up to pool capacity */ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* pool, buffer_t buf) { + if (buf.start == NULL) return; /* release on NULL */ if (pool->nbBuffers < pool->totalBuffers) { pool->bTable[pool->nbBuffers++] = buf; /* store for later re-use */ return; @@ -187,10 +188,10 @@ void ZSTDMT_compressChunk(void* jobDescription) if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, 0); /* flush frame header */ if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } - if (job->firstChunk) { /* preserve frame header when it is first chunk - otherwise, overwrite */ + if (job->firstChunk) { /* preserve frame header when it is first chunk */ dstBuff.start = (char*)dstBuff.start + hSize; dstBuff.size -= hSize; - } else + } else /* otherwise, overwrite */ hSize = 0; job->cSize = (job->lastChunk) ? /* last chunk signal */ @@ -258,7 +259,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize, 0); size_t const chunkTargetSize = (size_t)1 << (params.cParams.windowLog + 2); unsigned const nbChunksMax = (unsigned)(srcSize / chunkTargetSize) + (srcSize < chunkTargetSize) /* min 1 */; - unsigned const nbChunks = MIN(nbChunksMax, mtctx->nbThreads); + unsigned nbChunks = MIN(nbChunksMax, mtctx->nbThreads); size_t const proposedChunkSize = (srcSize + (nbChunks-1)) / nbChunks; size_t const avgChunkSize = ((proposedChunkSize & 0x1FFFF) < 0xFFFF) ? proposedChunkSize + 0xFFFF : proposedChunkSize; /* avoid too small last block */ size_t remainingSrcSize = srcSize; @@ -274,7 +275,14 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, size_t const chunkSize = MIN(remainingSrcSize, avgChunkSize); size_t const dstBufferCapacity = u ? ZSTD_compressBound(chunkSize) : dstCapacity; buffer_t const dstBuffer = u ? ZSTDMT_getBuffer(mtctx->buffPool, dstBufferCapacity) : (buffer_t){ dst, dstCapacity }; - ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(mtctx->cctxPool); /* should check for NULL ! */ + ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(mtctx->cctxPool); + + if ((cctx==NULL) || (dstBuffer.start==NULL)) { + mtctx->jobs[u].cSize = ERROR(memory_allocation); /* job result */ + mtctx->jobs[u].jobCompleted = 1; + nbChunks = u+1; + break; /* let's wait for previous jobs to complete, but don't start new ones */ + } mtctx->jobs[u].srcStart = srcStart + frameStartPos; mtctx->jobs[u].srcSize = chunkSize; @@ -310,8 +318,8 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[chunkID].cctx); { size_t const cSize = mtctx->jobs[chunkID].cSize; - if (ZSTD_isError(cSize)) return cSize; - if (dstPos + cSize > dstCapacity) return ERROR(dstSize_tooSmall); + if (ZSTD_isError(cSize)) return cSize; /* leaving here : later ressources won't be released */ + if (dstPos + cSize > dstCapacity) return ERROR(dstSize_tooSmall); /* leaving here : later ressources won't be released */ if (chunkID) { /* note : chunk 0 is already written directly into dst */ memcpy((char*)dst + dstPos, mtctx->jobs[chunkID].dstBuff.start, cSize); ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[chunkID].dstBuff); From 834ab50fa32fd7ee521028cf275e3c85194d8d7f Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Wed, 11 Jan 2017 17:31:06 -0800 Subject: [PATCH 104/227] Fixed decompress_usingDict not propagating corrupted dictionary error --- lib/decompress/zstd_decompress.c | 2 +- tests/fuzzer.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 02f3bf455..30198f036 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1444,7 +1444,7 @@ size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx, #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT==1) if (ZSTD_isLegacy(src, srcSize)) return ZSTD_decompressLegacy(dst, dstCapacity, src, srcSize, dict, dictSize); #endif - ZSTD_decompressBegin_usingDict(dctx, dict, dictSize); + CHECK_F(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize)); ZSTD_checkContinuity(dctx, dst); return ZSTD_decompressFrame(dctx, dst, dstCapacity, src, srcSize); } diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 37fa8dced..19b199ab9 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -312,6 +312,14 @@ static int basicUnitTests(U32 seed, double compressibility) if (r != CNBuffSize) goto _output_error); DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(4, "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; + } + ZSTD_freeCCtx(cctx); ZSTD_freeDCtx(dctx); free(dictBuffer); From ad9f6bd1238a9eb9a49bceb2df1b31d44942fcda Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 12 Jan 2017 03:06:35 +0100 Subject: [PATCH 105/227] zstdmt : fix : resources properly collected even when early fail In previous version, main function would return early when detecting a job error. Late threads resources were therefore not collected back into pools. New version just register the error, but continue the collecting process. All buffers and context should be released back to pool before leaving main function. --- lib/compress/zstdmt_compress.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 7e8bb9f3e..24f5e5b8b 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -305,7 +305,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, /* note : since nbChunks <= nbThreads, all jobs should be running immediately in parallel */ { unsigned chunkID; - size_t dstPos = 0; + size_t error = 0, dstPos = 0; for (chunkID=0; chunkIDcctxPool, mtctx->jobs[chunkID].cctx); { size_t const cSize = mtctx->jobs[chunkID].cSize; - if (ZSTD_isError(cSize)) return cSize; /* leaving here : later ressources won't be released */ - if (dstPos + cSize > dstCapacity) return ERROR(dstSize_tooSmall); /* leaving here : later ressources won't be released */ + if (ZSTD_isError(cSize)) error = cSize; + if ((!error) && (dstPos + cSize > dstCapacity)) error = ERROR(dstSize_tooSmall); if (chunkID) { /* note : chunk 0 is already written directly into dst */ - memcpy((char*)dst + dstPos, mtctx->jobs[chunkID].dstBuff.start, cSize); + if (!error) memcpy((char*)dst + dstPos, mtctx->jobs[chunkID].dstBuff.start, cSize); ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[chunkID].dstBuff); } dstPos += cSize ; } } - DEBUGLOG(3, "compressed size : %u ", (U32)dstPos); - return dstPos; + if (!error) DEBUGLOG(3, "compressed size : %u ", (U32)dstPos); + return error ? error : dstPos; } } From 5b726dbe4dcbaafca2dcf5b6ed89023b287061fa Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 12 Jan 2017 17:46:46 +0100 Subject: [PATCH 106/227] fix gcc-arm warning "suggest braces around empty body" --- lib/compress/zstdmt_compress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 24f5e5b8b..6f467f6a5 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -36,7 +36,7 @@ if (g_debugLevel>=MUTEX_WAIT_TIME_DLEVEL) { \ #else -# define DEBUGLOG(l, ...) /* disabled */ +# define DEBUGLOG(l, ...) {} /* disabled */ # define PTHREAD_MUTEX_LOCK(m) pthread_mutex_lock(m) #endif From c44c4d52235f65562cf6a77d40362913289306f3 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 12 Jan 2017 09:38:29 -0800 Subject: [PATCH 107/227] Fix missing 'OK' logging on fuzzer testcase --- tests/fuzzer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 19b199ab9..00cfb0574 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -319,6 +319,7 @@ static int basicUnitTests(U32 seed, double compressibility) "\x37\xa4\x30\xec\x11\x22\x33\x44", 8); if (ZSTD_getErrorCode(ret) != ZSTD_error_dictionary_corrupted) goto _output_error; } + DISPLAYLEVEL(4, "OK \n"); ZSTD_freeCCtx(cctx); ZSTD_freeDCtx(dctx); From 7d6f478d157642ea91b8d4d19c2267e039dcbc17 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Sat, 14 Jan 2017 17:44:54 -0800 Subject: [PATCH 108/227] Set dictionary ID in ZSTD_initCStream_usingCDict() When porting python-zstandard to use ZSTD_initCStream_usingCDict() so compression dictionaries could be reused, an automated test failed due to compressed content changing. I tracked this down to ZSTD_initCStream_usingCDict() not setting the dictID field of the ZSTD_CCtx attached to the ZSTD_CStream instance. I'm not 100% convinced this is the correct or full solution, as I'm still seeing one automated test failing with this change. --- lib/compress/zstd_compress.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 7626b33a6..4408644c6 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2970,6 +2970,7 @@ size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict) ZSTD_parameters const params = ZSTD_getParamsFromCDict(cdict); size_t const initError = ZSTD_initCStream_advanced(zcs, NULL, 0, params, 0); zcs->cdict = cdict; + zcs->cctx->dictID = params.fParams.noDictIDFlag ? 0 : cdict->refContext->dictID; return initError; } From 33fce03045bccbdac39a53a2aab1be0ce9d70ced Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 16 Jan 2017 19:46:22 -0800 Subject: [PATCH 109/227] added test checking dictID when using ZSTD_initCStream_usingCDict() It shows that dictID is not properly added into frame header --- Makefile | 2 +- tests/Makefile | 8 ++--- tests/zstreamtest.c | 72 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 65 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 19b12d0ef..0a3634c39 100644 --- a/Makefile +++ b/Makefile @@ -88,7 +88,7 @@ travis-install: $(MAKE) install PREFIX=~/install_test_dir gpptest: clean - $(MAKE) -C programs all CC=g++ CFLAGS="-O3 -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror" + CC=g++ $(MAKE) -C programs all CFLAGS="-O3 -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror" gcc5test: clean gcc-5 -v diff --git a/tests/Makefile b/tests/Makefile index c080fe34a..82c7af419 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -18,10 +18,6 @@ # zstreamtest32: Same as zstreamtest, but forced to compile in 32-bits mode # ########################################################################## -DESTDIR?= -PREFIX ?= /usr/local -BINDIR = $(PREFIX)/bin -MANDIR = $(PREFIX)/share/man/man1 ZSTDDIR = ../lib PRGDIR = ../programs PYTHON ?= python3 @@ -123,10 +119,10 @@ zbufftest-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c zbufftest.c $(MAKE) -C $(ZSTDDIR) libzstd $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@$(EXT) -zstreamtest : $(ZSTD_FILES) $(PRGDIR)/datagen.c zstreamtest.c +zstreamtest : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c zstreamtest.c $(CC) $(FLAGS) $^ -o $@$(EXT) -zstreamtest32 : $(ZSTD_FILES) $(PRGDIR)/datagen.c zstreamtest.c +zstreamtest32 : $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c zstreamtest.c $(CC) -m32 $(FLAGS) $^ -o $@$(EXT) zstreamtest-dll : LDFLAGS+= -L$(ZSTDDIR) -lzstd diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index ce6193085..4024e5eda 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -26,9 +26,10 @@ #include /* clock_t, clock() */ #include /* strcmp */ #include "mem.h" -#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel, ZSTD_customMem */ +#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel, ZSTD_customMem, ZSTD_getDictID_fromFrame */ #include "zstd.h" /* ZSTD_compressBound */ #include "zstd_errors.h" /* ZSTD_error_srcSize_wrong */ +#include "zdict.h" /* ZDICT_trainFromBuffer */ #include "datagen.h" /* RDG_genBuffer */ #define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ #include "xxhash.h" /* XXH64_* */ @@ -44,8 +45,7 @@ static const U32 nbTestsDefault = 10000; #define COMPRESSIBLE_NOISE_LENGTH (10 MB) #define FUZ_COMPRESSIBILITY_DEFAULT 50 -static const U32 prime1 = 2654435761U; -static const U32 prime2 = 2246822519U; +static const U32 prime32 = 2654435761U; /*-************************************ @@ -81,8 +81,9 @@ static clock_t FUZ_GetClockSpan(clock_t clockStart) #define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r))) unsigned int FUZ_rand(unsigned int* seedPtr) { + static const U32 prime2 = 2246822519U; U32 rand32 = *seedPtr; - rand32 *= prime1; + rand32 *= prime32; rand32 += prime2; rand32 = FUZ_rotl32(rand32, 13); *seedPtr = rand32; @@ -107,6 +108,41 @@ static void freeFunction(void* opaque, void* address) * Basic Unit tests ======================================================*/ +typedef struct { + void* start; + size_t size; + size_t filled; +} buffer_t; + +static const buffer_t g_nullBuffer = { NULL, 0 , 0 }; + +static buffer_t FUZ_createDictionary(const void* src, size_t srcSize, size_t blockSize, size_t requestedDictSize) +{ + buffer_t dict = { NULL, 0, 0 }; + size_t const nbBlocks = (srcSize + (blockSize-1)) / blockSize; + size_t* const blockSizes = (size_t*) malloc(nbBlocks * sizeof(size_t)); + if (!blockSizes) return dict; + dict.start = malloc(requestedDictSize); + if (!dict.start) { free(blockSizes); return dict; } + { size_t nb; + for (nb=0; nb= 4 MB); + dictionary = FUZ_createDictionary(CNBuffer, 4 MB, 4 KB, 40 KB); + if (!dictionary.start) { + DISPLAY("Error creating dictionary, aborting \n"); + goto _output_error; + } + dictID = ZDICT_getDictID(dictionary.start, dictionary.filled); + /* generate skippable frame */ MEM_writeLE32(compressedBuffer, ZSTD_MAGIC_SKIPPABLE_START); MEM_writeLE32(((char*)compressedBuffer)+4, (U32)skippableFrameSize); @@ -260,8 +307,6 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo { size_t const r = ZSTD_endStream(zc, &outBuff); if (r != 0) goto _output_error; } /* error, or some data not flushed */ { unsigned long long origSize = ZSTD_getDecompressedSize(outBuff.dst, outBuff.pos); - DISPLAY("outBuff.pos : %u \n", (U32)outBuff.pos); - DISPLAY("origSize = %u \n", (U32)origSize); if ((size_t)origSize != CNBufferSize) goto _output_error; } /* exact original size must be present */ DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/COMPRESSIBLE_NOISE_LENGTH*100); @@ -320,7 +365,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo /* CDict scenario */ DISPLAYLEVEL(4, "test%3i : digested dictionary : ", testNb++); - { ZSTD_CDict* const cdict = ZSTD_createCDict(CNBuffer, 128 KB, 1); + { ZSTD_CDict* const cdict = ZSTD_createCDict(dictionary.start, dictionary.filled, 1); size_t const initError = ZSTD_initCStream_usingCDict(zc, cdict); if (ZSTD_isError(initError)) goto _output_error; cSize = 0; @@ -346,9 +391,15 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo DISPLAYLEVEL(4, "OK (%u bytes) \n", (U32)s); } + DISPLAYLEVEL(4, "test%3i : check Dictionary ID : ", testNb++); + { unsigned const dID = ZSTD_getDictID_fromFrame(compressedBuffer, cSize); + if (dID != dictID) goto _output_error; + DISPLAYLEVEL(4, "OK (%u) \n", dID); + } + /* DDict scenario */ DISPLAYLEVEL(4, "test%3i : decompress %u bytes with digested dictionary : ", testNb++, (U32)CNBufferSize); - { ZSTD_DDict* const ddict = ZSTD_createDDict(CNBuffer, 128 KB); + { ZSTD_DDict* const ddict = ZSTD_createDDict(dictionary.start, dictionary.filled); size_t const initError = ZSTD_initDStream_usingDDict(zd, ddict); if (ZSTD_isError(initError)) goto _output_error; inBuff.src = compressedBuffer; @@ -388,6 +439,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo _end: + FUZ_freeDictionary(dictionary); ZSTD_freeCStream(zc); ZSTD_freeDStream(zd); free(CNBuffer); @@ -492,7 +544,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres if (nbTests >= testNb) { DISPLAYUPDATE(2, "\r%6u/%6u ", testNb, nbTests); } else { DISPLAYUPDATE(2, "\r%6u ", testNb); } FUZ_rand(&coreSeed); - lseed = coreSeed ^ prime1; + lseed = coreSeed ^ prime32; /* states full reset (deliberately not synchronized) */ /* some issues can only happen when reusing states */ From d72f4b6b7a7c06f61233942b2cef1f5eded9e246 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 17 Jan 2017 12:40:06 +0100 Subject: [PATCH 110/227] added "Makefile is validated" --- lib/Makefile | 6 ++++-- lib/zstd.h | 2 +- programs/Makefile | 2 ++ tests/Makefile | 2 ++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index efd3b87fe..01b4183c8 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,11 +1,13 @@ -# ################################################################ +# ########################################################################## # Copyright (c) 2016-present, Yann Collet, Facebook, Inc. # All rights reserved. # +# This Makefile is validated for Linux, macOS, *BSD, Hurd, Solaris, MSYS2 targets +# # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. An additional grant # of patent rights can be found in the PATENTS file in the same directory. -# ################################################################ +# ########################################################################## # Version numbers LIBVER_MAJOR_SCRIPT:=`sed -n '/define ZSTD_VERSION_MAJOR/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < ./zstd.h` diff --git a/lib/zstd.h b/lib/zstd.h index 333feff7d..c31fea98d 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -370,7 +370,7 @@ typedef struct { typedef struct { unsigned contentSizeFlag; /**< 1: content size will be in frame header (if known). */ - unsigned checksumFlag; /**< 1: will generate a 22-bits checksum at end of frame, to be used for error detection by decompressor */ + unsigned checksumFlag; /**< 1: will generate a 32-bits checksum at end of frame, to be used for error detection by decompressor */ unsigned noDictIDFlag; /**< 1: no dict ID will be saved into frame header (if dictionary compression) */ } ZSTD_frameParameters; diff --git a/programs/Makefile b/programs/Makefile index 15ae01096..a4c149a07 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -2,6 +2,8 @@ # Copyright (c) 2016-present, Yann Collet, Facebook, Inc. # All rights reserved. # +# This Makefile is validated for Linux, macOS, *BSD, Hurd, Solaris, MSYS2 targets +# # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. An additional grant # of patent rights can be found in the PATENTS file in the same directory. diff --git a/tests/Makefile b/tests/Makefile index c080fe34a..eadb1a673 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -2,6 +2,8 @@ # Copyright (c) 2016-present, Yann Collet, Facebook, Inc. # All rights reserved. # +# This Makefile is validated for Linux, macOS, *BSD, Hurd, Solaris, MSYS2 targets +# # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. An additional grant # of patent rights can be found in the PATENTS file in the same directory. From 5b114d3c01a26491176ea234f3cfac3ac293d4fc Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 17 Jan 2017 13:02:06 +0100 Subject: [PATCH 111/227] zlibWrapper: added get_crc_table --- zlibWrapper/zstd_zlibwrapper.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zlibWrapper/zstd_zlibwrapper.c b/zlibWrapper/zstd_zlibwrapper.c index 238510523..d5666b703 100644 --- a/zlibWrapper/zstd_zlibwrapper.c +++ b/zlibWrapper/zstd_zlibwrapper.c @@ -1042,3 +1042,8 @@ ZEXTERN uLong ZEXPORT z_crc32 OF((uLong crc, const Bytef *buf, uInt len)) { return crc32(crc, buf, len); } + +ZEXTERN const z_crc_t FAR * ZEXPORT z_get_crc_table OF((void)) +{ + return get_crc_table(); +} From 8e44bd83f153f9db4d2898b968b59fb1043a7727 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 17 Jan 2017 13:15:25 -0800 Subject: [PATCH 112/227] updated NEWS --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 1b132ca94..46bdb25a2 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,9 @@ v1.1.3 cli : new : advanced commands for detailed parameters, by Przemyslaw Skibinski cli : fix zstdless on Mac OS-X, by Andrew Janke +dictBuilder : improved dictionary generation quality, thanks to Nick Terrell API : fix : all symbols properly exposed in libzstd, by Nick Terrell +API : fix : ZSTD_initCStream_usingCDict() properly writes dictID into frame header, by Gregory Szorc (#511) API : new : ZSTD_create?Dict_byReference(), requested by Bartosz Taudul API : new : ZDICT_finalizeDictionary() From 57d423c5df337646020f5872ef4e7cc78d0c5c99 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Tue, 17 Jan 2017 11:04:08 -0800 Subject: [PATCH 113/227] Don't create dict in streaming apis if dictSize == 0 --- lib/compress/zstd_compress.c | 2 +- lib/decompress/zstd_decompress.c | 2 +- lib/zstd.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 4408644c6..df53db470 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2951,7 +2951,7 @@ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, if (zcs->outBuff == NULL) return ERROR(memory_allocation); } - if (dict) { + if (dict && dictSize >= 8) { ZSTD_freeCDict(zcs->cdictLocal); zcs->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize, 0, params, zcs->customMem); if (zcs->cdictLocal == NULL) return ERROR(memory_allocation); diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 30198f036..c53f3c3d1 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1936,7 +1936,7 @@ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t di zds->stage = zdss_loadHeader; zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; ZSTD_freeDDict(zds->ddictLocal); - if (dict) { + if (dict && dictSize >= 8) { zds->ddictLocal = ZSTD_createDDict(dict, dictSize); if (zds->ddictLocal == NULL) return ERROR(memory_allocation); } else zds->ddictLocal = NULL; diff --git a/lib/zstd.h b/lib/zstd.h index 333feff7d..52afc26bd 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -510,7 +510,7 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); /*===== Advanced Streaming compression functions =====*/ ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem); ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct */ -ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); +ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /* note: a dict will not be used if dict == NULL or dictSize < 8 */ ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /**< note : cdict will just be referenced, and must outlive compression session */ @@ -521,7 +521,7 @@ ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs); /*===== Advanced Streaming decompression functions =====*/ typedef enum { ZSTDdsp_maxWindowSize } ZSTD_DStreamParameter_e; ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem); -ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); +ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /* note: a dict will not be used if dict == NULL or dictSize < 8 */ ZSTDLIB_API size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue); ZSTDLIB_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); /**< note : ddict will just be referenced, and must outlive decompression session */ ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); /**< re-use decompression parameters from previous init; saves dictionary loading */ From a73c4129329d6dc4c81987af987f3574569bbc0f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 17 Jan 2017 15:31:16 -0800 Subject: [PATCH 114/227] completed ZSTDMT streaming compression Provides the baseline compression API : size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel); size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); size_t ZSTDMT_flushStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); Not tested yet --- lib/compress/zstdmt_compress.c | 148 ++++++++++++++++++++++++++------- 1 file changed, 119 insertions(+), 29 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 6f467f6a5..fb9183f9e 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -2,10 +2,11 @@ #include /* memcpy */ #include /* threadpool */ #include "threading.h" /* mutex */ -#include "zstd_internal.h" /* MIN, ERROR, ZSTD_* */ +#include "zstd_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */ #include "zstdmt_compress.h" #if 0 + # include # include # include @@ -163,8 +164,14 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) /* ===== Thread worker ===== */ +typedef struct { + buffer_t buffer; + size_t filled; +} inBuff_t; + typedef struct { ZSTD_CCtx* cctx; + buffer_t src; const void* srcStart; size_t srcSize; buffer_t dstBuff; @@ -208,25 +215,41 @@ _endJob: } +/* ------------------------------------------ */ /* ===== Multi-threaded compression ===== */ +/* ------------------------------------------ */ struct ZSTDMT_CCtx_s { POOL_ctx* factory; ZSTDMT_bufferPool* buffPool; ZSTDMT_CCtxPool* cctxPool; - unsigned nbThreads; pthread_mutex_t jobCompleted_mutex; pthread_cond_t jobCompleted_cond; - ZSTDMT_jobDescription jobs[1]; /* variable size */ + size_t targetSectionSize; + size_t inBuffSize; + inBuff_t inBuff; + ZSTD_parameters params; + unsigned nbThreads; + unsigned jobIDMask; + unsigned doneJobID; + unsigned nextJobID; + unsigned frameEnded; + ZSTDMT_jobDescription jobs[1]; /* variable size (must lies at the end) */ }; ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) { ZSTDMT_CCtx* cctx; + U32 const minNbJobs = nbThreads + 1; + U32 const nbJobsLog2 = ZSTD_highbit32(minNbJobs) + 1; + U32 const nbJobs = 1 << nbJobsLog2; + DEBUGLOG(4, "nbThreads : %u ; minNbJobs : %u ; nbJobsLog2 : %u ; nbJobs : %u \n", + nbThreads, minNbJobs, nbJobsLog2, nbJobs); if ((nbThreads < 1) | (nbThreads > ZSTDMT_NBTHREADS_MAX)) return NULL; - cctx = (ZSTDMT_CCtx*) calloc(1, sizeof(ZSTDMT_CCtx) + nbThreads*sizeof(ZSTDMT_jobDescription)); + cctx = (ZSTDMT_CCtx*) calloc(1, sizeof(ZSTDMT_CCtx) + nbJobs*sizeof(ZSTDMT_jobDescription)); if (!cctx) return NULL; cctx->nbThreads = nbThreads; + cctx->jobIDMask = nbJobs - 1; cctx->factory = POOL_create(nbThreads, 1); cctx->buffPool = ZSTDMT_createBufferPool(nbThreads); cctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads); @@ -338,46 +361,46 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, /* ======= Streaming API ======= */ /* ====================================== */ -#if 0 +#if 1 size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { zcs->params = ZSTD_getParams(compressionLevel, 0, 0); - zcs->targetSectionSize = 1 << (zcs->params.cParams.windowLog + 2); + zcs->targetSectionSize = (size_t)1 << (zcs->params.cParams.windowLog + 2); zcs->inBuffSize = 5 * (1 << zcs->params.cParams.windowLog); zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); /* check for NULL ! */ - zcs->inBuff.current = 0; + zcs->inBuff.filled = 0; zcs->doneJobID = 0; zcs->nextJobID = 0; + zcs->frameEnded = 0; return 0; } -typedef struct { - buffer_t buffer; - unsigned current; -} inBuff_t; - size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { + if (zcs->frameEnded) return ERROR(stage_wrong); + /* fill input buffer */ - { size_t const toLoad = MIN(input->size - input->pos, zcs->inBuffSize - zcs->inBuff.current); - memcpy((char*)zcs->inBuff.buffer.start + zcs->inBuff.current, input->src, toLoad); + { size_t const toLoad = MIN(input->size - input->pos, zcs->inBuffSize - zcs->inBuff.filled); + memcpy((char*)zcs->inBuff.buffer.start + zcs->inBuff.filled, input->src, toLoad); input->pos += toLoad; } - if (zcs->inBuff.current == zcs->inBuffSize) { /* filled enough : let's compress */ + if (zcs->inBuff.filled == zcs->inBuffSize) { /* filled enough : let's compress */ size_t const dstBufferCapacity = ZSTD_compressBound(zcs->targetSectionSize); - buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->targetSectionSize); /* should check for NULL */ + buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); /* should check for NULL */ ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); /* should check for NULL */ - unsigned const jobID = zcs->nextJobID & zcs->jobIDmask; + unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; - zcs->jobs[jobID].srcStart = zcs->inBuff.start; + zcs->jobs[jobID].src = zcs->inBuff.buffer; + zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = zcs->targetSectionSize; zcs->jobs[jobID].fullFrameSize = 0; - zcs->jobs[jobID].compressionLevel = zcs->compressionLevel; + zcs->jobs[jobID].params = zcs->params; zcs->jobs[jobID].dstBuff = dstBuffer; zcs->jobs[jobID].cctx = cctx; - zcs->jobs[jobID].frameID = (jobID>0); + zcs->jobs[jobID].firstChunk = (jobID==0); + zcs->jobs[jobID].lastChunk = 0; zcs->jobs[jobID].jobCompleted = 0; zcs->jobs[jobID].dstFlushed = 0; zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex; @@ -385,22 +408,22 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu /* get a new buffer for next input - save remaining into it */ zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); /* check for NULL ! */ - zcs->inBuff.current = zcs->inBuffSize - zcs->targetSectionSize; - memcpy(zcs->inBuff.buffer.start, (char*)zcs->jobs[jobID].srcStart + zcs->targetSectionSize, zcs->inBuff.current); + zcs->inBuff.filled = (U32)(zcs->inBuffSize - zcs->targetSectionSize); + memcpy(zcs->inBuff.buffer.start, (const char*)zcs->jobs[jobID].srcStart + zcs->targetSectionSize, zcs->inBuff.filled); - DEBUGLOG(3, "posting job %u (%u bytes)", jobID, (U32)zcs->jobs[jobID].srcSize); + DEBUGLOG(3, "posting job %u (%u bytes)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize); POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); zcs->nextJobID++; } /* check if there is any data available to flush */ - { unsigned const jobID = zcs->doneJobID & zcs->jobIDmask; + { unsigned const jobID = zcs->doneJobID & zcs->jobIDMask; ZSTDMT_jobDescription job = zcs->jobs[jobID]; if (job.jobCompleted) { /* job completed : output can be flushed */ size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[jobID].cctx = NULL; /* release cctx for future task */ - free(job.srcStart); zcs->jobs[jobID].srcStart = NULL; /* note : need a buff_t for release */ - memcpy((char*)output->dst + output->pos, job.dstBuff.start + job.dstFlushed, toWrite); + ZSTDMT_releaseBuffer(zcs->buffPool, job.src); zcs->jobs[jobID].srcStart = NULL; zcs->jobs[jobID].src = (buffer_t) { NULL, 0 }; + memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); output->pos += toWrite; job.dstFlushed += toWrite; if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => next one */ @@ -411,10 +434,77 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu } } /* recommended next input size : fill current input buffer */ - return zcs->inBuffSize - zcs->inBuff.current; + return zcs->inBuffSize - zcs->inBuff.filled; +} + +static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned endFrame) +{ + size_t const srcSize = zcs->inBuff.filled; + + if ((srcSize > 0) || (endFrame && !zcs->frameEnded)) { + size_t const dstBufferCapacity = ZSTD_compressBound(srcSize); + buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); /* should check for NULL */ + ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); /* should check for NULL */ + unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; + zcs->jobs[jobID].src = zcs->inBuff.buffer; + zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; + zcs->jobs[jobID].srcSize = srcSize; + zcs->jobs[jobID].fullFrameSize = 0; + zcs->jobs[jobID].params = zcs->params; + zcs->jobs[jobID].dstBuff = dstBuffer; + zcs->jobs[jobID].cctx = cctx; + zcs->jobs[jobID].firstChunk = (jobID==0); + zcs->jobs[jobID].lastChunk = endFrame; + zcs->jobs[jobID].jobCompleted = 0; + zcs->jobs[jobID].dstFlushed = 0; + zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex; + zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond; + + /* get a new buffer for next input */ + if (!endFrame) { + zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); /* check for NULL ! */ + zcs->inBuff.filled = 0; + } else { + zcs->frameEnded = 1; + } + + DEBUGLOG(3, "posting job %u (%u bytes)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize); + POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); + zcs->nextJobID++; + } + + /* check if there is any data available to flush */ + { unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; + ZSTDMT_jobDescription job = zcs->jobs[wJobID]; + if (job.jobCompleted) { /* job completed : output can be flushed */ + size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); + ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[wJobID].cctx = NULL; /* release cctx for future task */ + ZSTDMT_releaseBuffer(zcs->buffPool, job.src); zcs->jobs[wJobID].srcStart = NULL; zcs->jobs[wJobID].src = (buffer_t) { NULL, 0 }; + memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); + output->pos += toWrite; + job.dstFlushed += toWrite; + if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => next one */ + ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); zcs->jobs[wJobID].dstBuff = (buffer_t) { NULL, 0 }; + zcs->doneJobID++; + } else { + zcs->jobs[wJobID].dstFlushed = job.dstFlushed; + } } + /* return value : how many bytes left in buffer ; fake it to 1 if unknown but >0 */ + if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed); + return (zcs->doneJobID < zcs->nextJobID); + } +} + + +size_t ZSTDMT_flushStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output) +{ + return ZSTDMT_flushStream_internal(zcs, output, 0); +} + +size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output) +{ + return ZSTDMT_flushStream_internal(zcs, output, 1); } -size_t ZSTDMT_flushStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); -size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); #endif From d0a1d45582181efd76350922a240f14d7893c985 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 17 Jan 2017 16:15:18 -0800 Subject: [PATCH 115/227] ZSTDMT_{flush,end}Stream() now block on next job completion when nothing to flush The main issue was to avoid a caller to continually loop on {flush,end}Stream() when there was nothing ready to be flushed but still some compression work ongoing in a worker thread. The continuous loop would have resulted in wasted energy. The new version makes call to {flush,end}Stream blocking when there is nothing ready to be flushed. Of course, if all worker threads have exhausted job, it will return zero (all flush completed). Note : There are still some remaining issues to report error codes and properly collect back resources into pools when an error is triggered. --- lib/compress/zstdmt_compress.c | 42 ++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index fb9183f9e..57cc107f7 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -388,10 +388,15 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu if (zcs->inBuff.filled == zcs->inBuffSize) { /* filled enough : let's compress */ size_t const dstBufferCapacity = ZSTD_compressBound(zcs->targetSectionSize); - buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); /* should check for NULL */ - ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); /* should check for NULL */ + buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); + ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; + if ((cctx==NULL) || (dstBuffer.start==NULL)) { + zcs->jobs[jobID].cSize = ERROR(memory_allocation); /* job result : how to collect that error ? */ + zcs->jobs[jobID].jobCompleted = 1; + } + zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = zcs->targetSectionSize; @@ -426,17 +431,18 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); output->pos += toWrite; job.dstFlushed += toWrite; - if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => next one */ - ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); + if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => go to next one */ + ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); zcs->jobs[jobID].dstBuff = (buffer_t) { NULL, 0 }; zcs->doneJobID++; - } else - zcs->jobs[jobID].dstFlushed = job.dstFlushed; - } } + } else { + zcs->jobs[jobID].dstFlushed = job.dstFlushed; /* save flush level into zcs for later retrieval */ + } } } /* recommended next input size : fill current input buffer */ return zcs->inBuffSize - zcs->inBuff.filled; } + static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned endFrame) { size_t const srcSize = zcs->inBuff.filled; @@ -469,14 +475,20 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp } DEBUGLOG(3, "posting job %u (%u bytes)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize); - POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); + POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); /* this call is blocking when thread worker pool is exhausted */ zcs->nextJobID++; } /* check if there is any data available to flush */ { unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; - ZSTDMT_jobDescription job = zcs->jobs[wJobID]; - if (job.jobCompleted) { /* job completed : output can be flushed */ + PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); + while (zcs->jobs[wJobID].jobCompleted==0) { + DEBUGLOG(4, "waiting for jobCompleted signal from chunk %u", zcs->doneJobID); /* we want to block when waiting for data to flush */ + pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); + } + pthread_mutex_unlock(&zcs->jobCompleted_mutex); + { /* job completed : output can be flushed */ + ZSTDMT_jobDescription job = zcs->jobs[wJobID]; size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[wJobID].cctx = NULL; /* release cctx for future task */ ZSTDMT_releaseBuffer(zcs->buffPool, job.src); zcs->jobs[wJobID].srcStart = NULL; zcs->jobs[wJobID].src = (buffer_t) { NULL, 0 }; @@ -488,11 +500,11 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp zcs->doneJobID++; } else { zcs->jobs[wJobID].dstFlushed = job.dstFlushed; - } } - /* return value : how many bytes left in buffer ; fake it to 1 if unknown but >0 */ - if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed); - return (zcs->doneJobID < zcs->nextJobID); - } + } + /* return value : how many bytes left in buffer ; fake it to 1 if unknown but >0 */ + if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed); + return (zcs->doneJobID < zcs->nextJobID); + } } } From 0d6b8f65a9f5864bc75e5bc5a9a7c7abe0c5d197 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 17 Jan 2017 17:46:33 -0800 Subject: [PATCH 116/227] ZSTDMT_free() scrubs potentially unfinished jobs to release their resources In some complex scenarios (free() without finishing compression), it is possible that some resources are still into jobs and not collected back into pools. In which case, previous version of free() would miss them. This would be equivalent to a leak. New version ensures that it even foes after such resource. It requires job consumers to properly mark resources as released, by replacing entries by NULL after releasing back to the pool. Obviously, it's not recommended to free() zstdmt context mid-term, still that's now a supported scenario. The same methodology is also used to ensure proper resource collection after an error is detected. Still to do : - detect compression errors (not just allocation ones) - properly manage resource when init() is called without finishing previous compression. --- lib/compress/zstdmt_compress.c | 90 +++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 57cc107f7..c864ef219 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -52,6 +52,8 @@ typedef struct buffer_s { size_t size; } buffer_t; +static const buffer_t g_nullBuffer = (buffer_t) { NULL, 0 }; + typedef struct ZSTDMT_bufferPool_s { unsigned totalBuffers;; unsigned nbBuffers; @@ -262,10 +264,27 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) return cctx; } +/* ZSTDMT_releaseAllJobResources() : + * Ensure all workers are killed first. */ +static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx) +{ + unsigned jobID; + for (jobID=0; jobID <= mtctx->jobIDMask; jobID++) { + ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[jobID].dstBuff); + mtctx->jobs[jobID].dstBuff = g_nullBuffer; + ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[jobID].src); + mtctx->jobs[jobID].src = g_nullBuffer; + ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[jobID].cctx); + mtctx->jobs[jobID].cctx = NULL; + } +} + size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) { + if (mtctx==NULL) return 0; /* compatible with free on NULL */ POOL_free(mtctx->factory); - ZSTDMT_freeBufferPool(mtctx->buffPool); + ZSTDMT_releaseAllJobResources(mtctx); /* kill workers first */ + ZSTDMT_freeBufferPool(mtctx->buffPool); /* release job resources first */ ZSTDMT_freeCCtxPool(mtctx->cctxPool); pthread_mutex_destroy(&mtctx->jobCompleted_mutex); pthread_cond_destroy(&mtctx->jobCompleted_cond); @@ -340,12 +359,15 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, pthread_mutex_unlock(&mtctx->jobCompleted_mutex); ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[chunkID].cctx); + mtctx->jobs[chunkID].cctx = NULL; + mtctx->jobs[chunkID].srcStart = NULL; { size_t const cSize = mtctx->jobs[chunkID].cSize; if (ZSTD_isError(cSize)) error = cSize; if ((!error) && (dstPos + cSize > dstCapacity)) error = ERROR(dstSize_tooSmall); if (chunkID) { /* note : chunk 0 is already written directly into dst */ if (!error) memcpy((char*)dst + dstPos, mtctx->jobs[chunkID].dstBuff.start, cSize); ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->jobs[chunkID].dstBuff); + mtctx->jobs[chunkID].dstBuff = g_nullBuffer; } dstPos += cSize ; } @@ -363,6 +385,19 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, #if 1 +static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* zcs) { + while (zcs->doneJobID < zcs->nextJobID) { + unsigned const jobID = zcs->doneJobID & zcs->jobIDMask; + PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); + while (zcs->jobs[jobID].jobCompleted==0) { + DEBUGLOG(4, "waiting for jobCompleted signal from chunk %u", zcs->doneJobID); /* we want to block when waiting for data to flush */ + pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); + } + pthread_mutex_unlock(&zcs->jobCompleted_mutex); + zcs->doneJobID++; + } +} + size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { zcs->params = ZSTD_getParams(compressionLevel, 0, 0); zcs->targetSectionSize = (size_t)1 << (zcs->params.cParams.windowLog + 2); @@ -393,8 +428,12 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; if ((cctx==NULL) || (dstBuffer.start==NULL)) { - zcs->jobs[jobID].cSize = ERROR(memory_allocation); /* job result : how to collect that error ? */ + zcs->jobs[jobID].cSize = ERROR(memory_allocation); zcs->jobs[jobID].jobCompleted = 1; + zcs->nextJobID++; + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + return ERROR(memory_allocation); } zcs->jobs[jobID].src = zcs->inBuff.buffer; @@ -412,7 +451,15 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond; /* get a new buffer for next input - save remaining into it */ - zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); /* check for NULL ! */ + zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); + if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */ + zcs->jobs[jobID].cSize = ERROR(memory_allocation); + zcs->jobs[jobID].jobCompleted = 1; + zcs->nextJobID++; + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + return ERROR(memory_allocation); + } zcs->inBuff.filled = (U32)(zcs->inBuffSize - zcs->targetSectionSize); memcpy(zcs->inBuff.buffer.start, (const char*)zcs->jobs[jobID].srcStart + zcs->targetSectionSize, zcs->inBuff.filled); @@ -426,13 +473,16 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu ZSTDMT_jobDescription job = zcs->jobs[jobID]; if (job.jobCompleted) { /* job completed : output can be flushed */ size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); - ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[jobID].cctx = NULL; /* release cctx for future task */ - ZSTDMT_releaseBuffer(zcs->buffPool, job.src); zcs->jobs[jobID].srcStart = NULL; zcs->jobs[jobID].src = (buffer_t) { NULL, 0 }; + ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); + zcs->jobs[jobID].cctx = NULL; + ZSTDMT_releaseBuffer(zcs->buffPool, job.src); + zcs->jobs[jobID].srcStart = NULL; zcs->jobs[jobID].src = g_nullBuffer; memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); output->pos += toWrite; job.dstFlushed += toWrite; if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => go to next one */ - ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); zcs->jobs[jobID].dstBuff = (buffer_t) { NULL, 0 }; + ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); + zcs->jobs[jobID].dstBuff = g_nullBuffer; zcs->doneJobID++; } else { zcs->jobs[jobID].dstFlushed = job.dstFlushed; /* save flush level into zcs for later retrieval */ @@ -449,9 +499,19 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp if ((srcSize > 0) || (endFrame && !zcs->frameEnded)) { size_t const dstBufferCapacity = ZSTD_compressBound(srcSize); - buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); /* should check for NULL */ - ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); /* should check for NULL */ + buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); + ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; + + if ((cctx==NULL) || (dstBuffer.start==NULL)) { + zcs->jobs[jobID].cSize = ERROR(memory_allocation); + zcs->jobs[jobID].jobCompleted = 1; + zcs->nextJobID++; + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + return ERROR(memory_allocation); + } + zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; @@ -468,8 +528,16 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp /* get a new buffer for next input */ if (!endFrame) { - zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); /* check for NULL ! */ + zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); zcs->inBuff.filled = 0; + if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */ + zcs->jobs[jobID].cSize = ERROR(memory_allocation); + zcs->jobs[jobID].jobCompleted = 1; + zcs->nextJobID++; + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + return ERROR(memory_allocation); + } } else { zcs->frameEnded = 1; } @@ -491,12 +559,12 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp ZSTDMT_jobDescription job = zcs->jobs[wJobID]; size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[wJobID].cctx = NULL; /* release cctx for future task */ - ZSTDMT_releaseBuffer(zcs->buffPool, job.src); zcs->jobs[wJobID].srcStart = NULL; zcs->jobs[wJobID].src = (buffer_t) { NULL, 0 }; + ZSTDMT_releaseBuffer(zcs->buffPool, job.src); zcs->jobs[wJobID].srcStart = NULL; zcs->jobs[wJobID].src = g_nullBuffer; memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); output->pos += toWrite; job.dstFlushed += toWrite; if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => next one */ - ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); zcs->jobs[wJobID].dstBuff = (buffer_t) { NULL, 0 }; + ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); zcs->jobs[wJobID].dstBuff = g_nullBuffer; zcs->doneJobID++; } else { zcs->jobs[wJobID].dstFlushed = job.dstFlushed; From 5edab91bbb9b4daf9290c6c159bca277dadf7eec Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 18 Jan 2017 10:39:39 +0100 Subject: [PATCH 117/227] get_crc_table only with ZLIB_VERNUM >= 0x1270 --- zlibWrapper/zstd_zlibwrapper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zlibWrapper/zstd_zlibwrapper.c b/zlibWrapper/zstd_zlibwrapper.c index d5666b703..2b55bfabc 100644 --- a/zlibWrapper/zstd_zlibwrapper.c +++ b/zlibWrapper/zstd_zlibwrapper.c @@ -1043,7 +1043,9 @@ ZEXTERN uLong ZEXPORT z_crc32 OF((uLong crc, const Bytef *buf, uInt len)) return crc32(crc, buf, len); } +#if ZLIB_VERNUM >= 0x1270 ZEXTERN const z_crc_t FAR * ZEXPORT z_get_crc_table OF((void)) { return get_crc_table(); } +#endif From 69f7990fc59ac4b37cf59ec2acf88b9012d22042 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 18 Jan 2017 12:01:50 +0100 Subject: [PATCH 118/227] gzguts.h updated to zlib 1.2.11 --- zlibWrapper/gzguts.h | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/zlibWrapper/gzguts.h b/zlibWrapper/gzguts.h index 40536de4f..84651b88d 100644 --- a/zlibWrapper/gzguts.h +++ b/zlibWrapper/gzguts.h @@ -3,7 +3,7 @@ * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ /* gzguts.h -- zlib internal header definitions for gz* operations - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler * For conditions of distribution and use, see http://www.zlib.net/zlib_license.html */ @@ -30,6 +30,10 @@ # include # include #endif + +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +#endif #include #ifdef _WIN32 @@ -40,6 +44,10 @@ # include #endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define WIDECHAR +#endif + #ifdef WINAPI_FAMILY # define open _open # define read _read @@ -100,18 +108,19 @@ # endif #endif -/* unlike snprintf (which is required in C99, yet still not supported by - Microsoft more than a decade later!), _snprintf does not guarantee null - termination of the result -- however this is only used in gzlib.c where +/* unlike snprintf (which is required in C99), _snprintf does not guarantee + null termination of the result -- however this is only used in gzlib.c where the result is assured to fit in the space provided */ -#ifdef _MSC_VER +#if defined(_MSC_VER) && _MSC_VER < 1900 # define snprintf _snprintf #endif #ifndef local # define local static #endif -/* compile with -Dlocal if your debugger can't find static symbols */ +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ /* gz* functions always use library allocation functions */ #ifndef STDC @@ -175,7 +184,7 @@ typedef struct { char *path; /* path or fd for error messages */ unsigned size; /* buffer size, zero if not allocated yet */ unsigned want; /* requested buffer size, default is GZBUFSIZE */ - unsigned char *in; /* input buffer */ + unsigned char *in; /* input buffer (double-sized when writing) */ unsigned char *out; /* output buffer (double-sized when reading) */ int direct; /* 0 if processing gzip, 1 if transparent */ /* just for reading */ From 7f82aad187e6d5e505f0a0ffc25d9c90eec57461 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 18 Jan 2017 12:08:08 +0100 Subject: [PATCH 119/227] gzlib.c updated to zlib 1.2.11 --- zlibWrapper/gzlib.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/zlibWrapper/gzlib.c b/zlibWrapper/gzlib.c index 932319af6..2caf54e54 100644 --- a/zlibWrapper/gzlib.c +++ b/zlibWrapper/gzlib.c @@ -1,14 +1,14 @@ /* gzlib.c contains minimal changes required to be compiled with zlibWrapper: - * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ + * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ /* gzlib.c -- zlib functions common to reading and writing gzip files - * Copyright (C) 2004, 2010, 2011, 2012, 2013 Mark Adler - * For conditions of distribution and use, see http://www.zlib.net/zlib_license.html + * Copyright (C) 2004-2017 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" -#if defined(_WIN32) && !defined(__BORLANDC__) +#if defined(_WIN32) && !defined(__BORLANDC__) && !defined(__MINGW32__) # define LSEEK _lseeki64 #else #if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 @@ -97,7 +97,7 @@ local gzFile gz_open(path, fd, mode) const char *mode; { gz_statep state; - size_t len; + z_size_t len; int oflag; #ifdef O_CLOEXEC int cloexec = 0; @@ -191,10 +191,10 @@ local gzFile gz_open(path, fd, mode) } /* save the path name for error messages */ -#ifdef _WIN32 +#ifdef WIDECHAR if (fd == -2) { len = wcstombs(NULL, path, 0); - if (len == (size_t)-1) + if (len == (z_size_t)-1) len = 0; } else @@ -205,7 +205,7 @@ local gzFile gz_open(path, fd, mode) free(state.state); return NULL; } -#ifdef _WIN32 +#ifdef WIDECHAR if (fd == -2) if (len) wcstombs(state.state->path, path, len + 1); @@ -214,7 +214,7 @@ local gzFile gz_open(path, fd, mode) else #endif #if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(state.state->path, len + 1, "%s", (const char *)path); + (void)snprintf(state.state->path, len + 1, "%s", (const char *)path); #else strcpy(state.state->path, path); #endif @@ -242,7 +242,7 @@ local gzFile gz_open(path, fd, mode) /* open the file with the appropriate flags (or just use fd) */ state.state->fd = fd > -1 ? fd : ( -#ifdef _WIN32 +#ifdef WIDECHAR fd == -2 ? _wopen(path, oflag, 0666) : #endif open((const char *)path, oflag, 0666)); @@ -251,8 +251,10 @@ local gzFile gz_open(path, fd, mode) free(state.state); return NULL; } - if (state.state->mode == GZ_APPEND) + if (state.state->mode == GZ_APPEND) { + LSEEK(state.state->fd, 0, SEEK_END); /* so gzoffset() is correct */ state.state->mode = GZ_WRITE; /* simplify later checks */ + } /* save the current position for rewinding (only if reading) */ if (state.state->mode == GZ_READ) { @@ -294,7 +296,7 @@ gzFile ZEXPORT gzdopen(fd, mode) if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) return NULL; #if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(path, 7 + 3 * sizeof(int), "", fd); /* for debugging */ + (void)snprintf(path, 7 + 3 * sizeof(int), "", fd); #else sprintf(path, "", fd); /* for debugging */ #endif @@ -304,7 +306,7 @@ gzFile ZEXPORT gzdopen(fd, mode) } /* -- see zlib.h -- */ -#ifdef _WIN32 +#ifdef WIDECHAR gzFile ZEXPORT gzopen_w(path, mode) const wchar_t *path; const char *mode; @@ -332,6 +334,8 @@ int ZEXPORT gzbuffer(file, size) return -1; /* check and set requested size */ + if ((size << 1) < size) + return -1; /* need to be able to double it */ if (size < 2) size = 2; /* need two bytes to check magic header */ state.state->want = size; @@ -569,8 +573,8 @@ void ZEXPORT gzclearerr(file) gz_error(state, Z_OK, NULL); } -/* Create an error message in allocated memory and set state->err and - state->msg accordingly. Free any previous error message already there. Do +/* Create an error message in allocated memory and set state.state->err and + state.state->msg accordingly. Free any previous error message already there. Do not try to free or allocate space if the error is Z_MEM_ERROR (out of memory). Simply save the error message as a static string. If there is an allocation failure constructing the error message, then convert the error to @@ -587,7 +591,7 @@ void ZLIB_INTERNAL gz_error(state, err, msg) state.state->msg = NULL; } - /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */ + /* if fatal, set state.state->x.have to 0 so that the gzgetc() macro fails */ if (err != Z_OK && err != Z_BUF_ERROR) state.state->x.have = 0; @@ -607,14 +611,13 @@ void ZLIB_INTERNAL gz_error(state, err, msg) return; } #if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(state.state->msg, strlen(state.state->path) + strlen(msg) + 3, - "%s%s%s", state.state->path, ": ", msg); + (void)snprintf(state.state->msg, strlen(state.state->path) + strlen(msg) + 3, + "%s%s%s", state.state->path, ": ", msg); #else strcpy(state.state->msg, state.state->path); strcat(state.state->msg, ": "); strcat(state.state->msg, msg); #endif - return; } #ifndef INT_MAX From 5735fd74ee0d566a3714d591b1059177f4c5b18b Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 18 Jan 2017 12:14:01 +0100 Subject: [PATCH 120/227] gzread.c updated to zlib 1.2.11 --- zlibWrapper/gzread.c | 184 ++++++++++++++++++++++++++++--------------- 1 file changed, 121 insertions(+), 63 deletions(-) diff --git a/zlibWrapper/gzread.c b/zlibWrapper/gzread.c index f251e2fe4..004fe6af9 100644 --- a/zlibWrapper/gzread.c +++ b/zlibWrapper/gzread.c @@ -1,9 +1,9 @@ /* gzread.c contains minimal changes required to be compiled with zlibWrapper: - * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ - -/* gzread.c -- zlib functions for reading gzip files - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler - * For conditions of distribution and use, see http://www.zlib.net/zlib_license.html + * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ + + /* gzread.c -- zlib functions for reading gzip files + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" @@ -15,9 +15,10 @@ local int gz_look OF((gz_statep)); local int gz_decomp OF((gz_statep)); local int gz_fetch OF((gz_statep)); local int gz_skip OF((gz_statep, z_off64_t)); +local z_size_t gz_read OF((gz_statep, voidp, z_size_t)); /* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from - state->fd, and update state->eof, state->err, and state->msg as appropriate. + state.state->fd, and update state.state->eof, state.state->err, and state.state->msg as appropriate. This function needs to loop on read(), since read() is not guaranteed to read the number of bytes requested, depending on the type of descriptor. */ local int gz_load(state, buf, len, have) @@ -27,13 +28,17 @@ local int gz_load(state, buf, len, have) unsigned *have; { int ret; + unsigned get, max = ((unsigned)-1 >> 2) + 1; *have = 0; do { - ret = (int)read(state.state->fd, buf + *have, len - *have); + get = len - *have; + if (get > max) + get = max; + ret = read(state.state->fd, buf + *have, get); if (ret <= 0) break; - *have += ret; + *have += (unsigned)ret; } while (*have < len); if (ret < 0) { gz_error(state, Z_ERRNO, zstrerror()); @@ -77,8 +82,8 @@ local int gz_avail(state) return 0; } -/* Look for gzip header, set up for inflate or copy. state->x.have must be 0. - If this is the first time in, allocate required memory. state->how will be +/* Look for gzip header, set up for inflate or copy. state.state->x.have must be 0. + If this is the first time in, allocate required memory. state.state->how will be left unchanged if there is no more input data available, will be set to COPY if there is no gzip header and direct copying will be performed, or it will be set to GZIP for decompression. If direct copying, then leftover input @@ -97,10 +102,8 @@ local int gz_look(state) state.state->in = (unsigned char *)malloc(state.state->want); state.state->out = (unsigned char *)malloc(state.state->want << 1); if (state.state->in == NULL || state.state->out == NULL) { - if (state.state->out != NULL) - free(state.state->out); - if (state.state->in != NULL) - free(state.state->in); + free(state.state->out); + free(state.state->in); gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } @@ -136,7 +139,6 @@ local int gz_look(state) file -- for here we assume that if a gzip file is being written, then the header will be written in a single operation, so that reading a single byte is sufficient indication that it is not a gzip file) */ - //printf("strm->next_in[0]=%d strm->next_in[1]=%d\n", strm->next_in[0], strm->next_in[1]); if (strm->avail_in > 1 && ((strm->next_in[0] == 31 && strm->next_in[1] == 139) /* gz header */ || (strm->next_in[0] == 40 && strm->next_in[1] == 181))) { /* zstd header */ @@ -170,9 +172,9 @@ local int gz_look(state) } /* Decompress from input to the provided next_out and avail_out in the state. - On return, state->x.have and state->x.next point to the just decompressed - data. If the gzip stream completes, state->how is reset to LOOK to look for - the next gzip stream or raw data, once state->x.have is depleted. Returns 0 + On return, state.state->x.have and state.state->x.next point to the just decompressed + data. If the gzip stream completes, state.state->how is reset to LOOK to look for + the next gzip stream or raw data, once state.state->x.have is depleted. Returns 0 on success, -1 on failure. */ local int gz_decomp(state) gz_statep state; @@ -222,11 +224,11 @@ local int gz_decomp(state) return 0; } -/* Fetch data and put it in the output buffer. Assumes state->x.have is 0. +/* Fetch data and put it in the output buffer. Assumes state.state->x.have is 0. Data is either copied from the input file or decompressed from the input - file depending on state->how. If state->how is LOOK, then a gzip header is + file depending on state.state->how. If state.state->how is LOOK, then a gzip header is looked for to determine whether to copy or decompress. Returns -1 on error, - otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the + otherwise 0. gz_fetch() will leave state.state->how as COPY or GZIP unless the end of the input file has been reached and all data has been processed. */ local int gz_fetch(state) gz_statep state; @@ -289,33 +291,17 @@ local int gz_skip(state, len) return 0; } -/* -- see zlib.h -- */ -int ZEXPORT gzread(file, buf, len) - gzFile file; - voidp buf; - unsigned len; -{ - unsigned got, n; +/* Read len bytes into buf from file, or less than len up to the end of the + input. Return the number of bytes read. If zero is returned, either the + end of file was reached, or there was an error. state.state->err must be + consulted in that case to determine which. */ +local z_size_t gz_read(state, buf, len) gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state.state->strm); - - /* check that we're reading and that there's no (serious) error */ - if (state.state->mode != GZ_READ || - (state.state->err != Z_OK && state.state->err != Z_BUF_ERROR)) - return -1; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids the flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); - return -1; - } + voidp buf; + z_size_t len; +{ + z_size_t got; + unsigned n; /* if len is zero, avoid unnecessary operations */ if (len == 0) @@ -325,32 +311,38 @@ int ZEXPORT gzread(file, buf, len) if (state.state->seek) { state.state->seek = 0; if (gz_skip(state, state.state->skip) == -1) - return -1; + return 0; } /* get len bytes to buf, or less than len if at the end */ got = 0; do { + /* set n to the maximum amount of len that fits in an unsigned int */ + n = -1; + if (n > len) + n = len; + /* first just try copying data from the output buffer */ if (state.state->x.have) { - n = state.state->x.have > len ? len : state.state->x.have; + if (state.state->x.have < n) + n = state.state->x.have; memcpy(buf, state.state->x.next, n); state.state->x.next += n; state.state->x.have -= n; } /* output buffer empty -- return if we're at the end of the input */ - else if (state.state->eof && strm->avail_in == 0) { + else if (state.state->eof && state.state->strm.avail_in == 0) { state.state->past = 1; /* tried to read past end */ break; } /* need output data -- for small len or new stream load up our output buffer */ - else if (state.state->how == LOOK || len < (state.state->size << 1)) { + else if (state.state->how == LOOK || n < (state.state->size << 1)) { /* get more output, looking for header if required */ if (gz_fetch(state) == -1) - return -1; + return 0; continue; /* no progress yet -- go back to copy above */ /* the copy above assures that we will leave with space in the output buffer, allowing at least one gzungetc() to succeed */ @@ -358,16 +350,16 @@ int ZEXPORT gzread(file, buf, len) /* large len -- read directly into user buffer */ else if (state.state->how == COPY) { /* read directly */ - if (gz_load(state, (unsigned char *)buf, len, &n) == -1) - return -1; + if (gz_load(state, (unsigned char *)buf, n, &n) == -1) + return 0; } /* large len -- decompress directly into user buffer */ - else { /* state->how == GZIP */ - strm->avail_out = len; - strm->next_out = (unsigned char *)buf; + else { /* state.state->how == GZIP */ + state.state->strm.avail_out = n; + state.state->strm.next_out = (unsigned char *)buf; if (gz_decomp(state) == -1) - return -1; + return 0; n = state.state->x.have; state.state->x.have = 0; } @@ -379,8 +371,75 @@ int ZEXPORT gzread(file, buf, len) state.state->x.pos += n; } while (len); - /* return number of bytes read into user buffer (will fit in int) */ - return (int)got; + /* return number of bytes read into user buffer */ + return got; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzread(file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state.state->mode != GZ_READ || + (state.state->err != Z_OK && state.state->err != Z_BUF_ERROR)) + return -1; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids a flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in an int"); + return -1; + } + + /* read len or fewer bytes to buf */ + len = gz_read(state, buf, len); + + /* check for an error */ + if (len == 0 && state.state->err != Z_OK && state.state->err != Z_BUF_ERROR) + return -1; + + /* return the number of bytes read (this is assured to fit in an int) */ + return (int)len; +} + +/* -- see zlib.h -- */ +z_size_t ZEXPORT gzfread(buf, size, nitems, file) + voidp buf; + z_size_t size; + z_size_t nitems; + gzFile file; +{ + z_size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state.state->mode != GZ_READ || + (state.state->err != Z_OK && state.state->err != Z_BUF_ERROR)) + return 0; + + /* compute bytes to read -- error on overflow */ + len = nitems * size; + if (size && len / size != nitems) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); + return 0; + } + + /* read len or fewer bytes to buf, return the number of full items read */ + return len ? gz_read(state, buf, len) / size : 0; } /* -- see zlib.h -- */ @@ -401,7 +460,6 @@ ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); #endif - int ZEXPORT gzgetc(file) gzFile file; { @@ -426,8 +484,8 @@ int ZEXPORT gzgetc(file) return *(state.state->x.next)++; } - /* nothing there -- try gzread() */ - ret = gzread(file, buf, 1); + /* nothing there -- try gz_read() */ + ret = gz_read(state, buf, 1); return ret < 1 ? -1 : buf[0]; } From 3805a0090467a86b877114f51a461b93f5b14c5f Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 18 Jan 2017 12:47:32 +0100 Subject: [PATCH 121/227] gzwrite.c updated to zlib 1.2.11 --- zlibWrapper/gzwrite.c | 340 ++++++++++++++++++++++++++---------------- 1 file changed, 214 insertions(+), 126 deletions(-) diff --git a/zlibWrapper/gzwrite.c b/zlibWrapper/gzwrite.c index 6f3c9658b..c17d194a2 100644 --- a/zlibWrapper/gzwrite.c +++ b/zlibWrapper/gzwrite.c @@ -1,9 +1,9 @@ /* gzwrite.c contains minimal changes required to be compiled with zlibWrapper: - * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ - -/* gzwrite.c -- zlib functions for writing gzip files - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler - * For conditions of distribution and use, see http://www.zlib.net/zlib_license.html + * - gz_statep was converted to union to work with -Wstrict-aliasing=1 */ + + /* gzwrite.c -- zlib functions for writing gzip files + * Copyright (C) 2004-2017 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" @@ -12,17 +12,19 @@ local int gz_init OF((gz_statep)); local int gz_comp OF((gz_statep, int)); local int gz_zero OF((gz_statep, z_off64_t)); +local z_size_t gz_write OF((gz_statep, voidpc, z_size_t)); /* Initialize state for writing a gzip file. Mark initialization by setting - state->size to non-zero. Return -1 on failure or 0 on success. */ + state.state->size to non-zero. Return -1 on a memory allocation failure, or 0 on + success. */ local int gz_init(state) gz_statep state; { int ret; z_streamp strm = &(state.state->strm); - /* allocate input buffer */ - state.state->in = (unsigned char *)malloc(state.state->want); + /* allocate input buffer (double size for gzprintf) */ + state.state->in = (unsigned char *)malloc(state.state->want << 1); if (state.state->in == NULL) { gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; @@ -50,6 +52,7 @@ local int gz_init(state) gz_error(state, Z_MEM_ERROR, "out of memory"); return -1; } + strm->next_in = NULL; } /* mark state as initialized */ @@ -65,17 +68,17 @@ local int gz_init(state) } /* Compress whatever is at avail_in and next_in and write to the output file. - Return -1 if there is an error writing to the output file, otherwise 0. - flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, - then the deflate() state is reset to start a new gzip stream. If gz->direct - is true, then simply write to the output file without compressing, and - ignore flush. */ + Return -1 if there is an error writing to the output file or if gz_init() + fails to allocate memory, otherwise 0. flush is assumed to be a valid + deflate() flush value. If flush is Z_FINISH, then the deflate() state is + reset to start a new gzip stream. If gz->direct is true, then simply write + to the output file without compressing, and ignore flush. */ local int gz_comp(state, flush) gz_statep state; int flush; { - int ret, got; - unsigned have; + int ret, writ; + unsigned have, put, max = ((unsigned)-1 >> 2) + 1; z_streamp strm = &(state.state->strm); /* allocate memory if this is the first time through */ @@ -84,12 +87,16 @@ local int gz_comp(state, flush) /* write directly if requested */ if (state.state->direct) { - got = (int)write(state.state->fd, strm->next_in, strm->avail_in); - if (got < 0 || (unsigned)got != strm->avail_in) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; + while (strm->avail_in) { + put = strm->avail_in > max ? max : strm->avail_in; + writ = write(state.state->fd, strm->next_in, put); + if (writ < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + strm->avail_in -= (unsigned)writ; + strm->next_in += writ; } - strm->avail_in = 0; return 0; } @@ -100,17 +107,21 @@ local int gz_comp(state, flush) doing Z_FINISH then don't write until we get to Z_STREAM_END */ if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && (flush != Z_FINISH || ret == Z_STREAM_END))) { - have = (unsigned)(strm->next_out - state.state->x.next); - if (have && ((got = (int)write(state.state->fd, state.state->x.next, have)) < 0 || - (unsigned)got != have)) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; + while (strm->next_out > state.state->x.next) { + put = strm->next_out - state.state->x.next > (int)max ? max : + (unsigned)(strm->next_out - state.state->x.next); + writ = write(state.state->fd, state.state->x.next, put); + if (writ < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + state.state->x.next += writ; } if (strm->avail_out == 0) { strm->avail_out = state.state->size; strm->next_out = state.state->out; + state.state->x.next = state.state->out; } - state.state->x.next = strm->next_out; } /* compress */ @@ -132,7 +143,8 @@ local int gz_comp(state, flush) return 0; } -/* Compress len zeros to output. Return -1 on error, 0 on success. */ +/* Compress len zeros to output. Return -1 on a write error or memory + allocation failure by gz_comp(), or 0 on success. */ local int gz_zero(state, len) gz_statep state; z_off64_t len; @@ -164,32 +176,14 @@ local int gz_zero(state, len) return 0; } -/* -- see zlib.h -- */ -int ZEXPORT gzwrite(file, buf, len) - gzFile file; - voidpc buf; - unsigned len; -{ - unsigned put = len; +/* Write len bytes from buf to file. Return the number of bytes written. If + the returned value is less than len, then there was an error. */ +local z_size_t gz_write(state, buf, len) gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return 0; - state = (gz_statep)file; - strm = &(state.state->strm); - - /* check that we're writing and that there's no error */ - if (state.state->mode != GZ_WRITE || state.state->err != Z_OK) - return 0; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids the flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); - return 0; - } + voidpc buf; + z_size_t len; +{ + z_size_t put = len; /* if len is zero, avoid unnecessary operations */ if (len == 0) @@ -212,14 +206,15 @@ int ZEXPORT gzwrite(file, buf, len) do { unsigned have, copy; - if (strm->avail_in == 0) - strm->next_in = state.state->in; - have = (unsigned)((strm->next_in + strm->avail_in) - state.state->in); + if (state.state->strm.avail_in == 0) + state.state->strm.next_in = state.state->in; + have = (unsigned)((state.state->strm.next_in + state.state->strm.avail_in) - + state.state->in); copy = state.state->size - have; if (copy > len) copy = len; memcpy(state.state->in + have, buf, copy); - strm->avail_in += copy; + state.state->strm.avail_in += copy; state.state->x.pos += copy; buf = (const char *)buf + copy; len -= copy; @@ -229,19 +224,83 @@ int ZEXPORT gzwrite(file, buf, len) } else { /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + if (state.state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1) return 0; /* directly compress user buffer to file */ - strm->avail_in = len; - strm->next_in = (z_const Bytef *)buf; - state.state->x.pos += len; - if (gz_comp(state, Z_NO_FLUSH) == -1) - return 0; + state.state->strm.next_in = (z_const Bytef *)buf; + do { + unsigned n = (unsigned)-1; + if (n > len) + n = len; + state.state->strm.avail_in = n; + state.state->x.pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + len -= n; + } while (len); } - /* input was all buffered or compressed (put will fit in int) */ - return (int)put; + /* input was all buffered or compressed */ + return put; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzwrite(file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state.state->mode != GZ_WRITE || state.state->err != Z_OK) + return 0; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids a flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); + return 0; + } + + /* write len bytes from buf (the return value will fit in an int) */ + return (int)gz_write(state, buf, len); +} + +/* -- see zlib.h -- */ +z_size_t ZEXPORT gzfwrite(buf, size, nitems, file) + voidpc buf; + z_size_t size; + z_size_t nitems; + gzFile file; +{ + z_size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state.state->mode != GZ_WRITE || state.state->err != Z_OK) + return 0; + + /* compute bytes to read -- error on overflow */ + len = nitems * size; + if (size && len / size != nitems) { + gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t"); + return 0; + } + + /* write len bytes to buf, return the number of full items written */ + return len ? gz_write(state, buf, len) / size : 0; } /* -- see zlib.h -- */ @@ -271,7 +330,7 @@ int ZEXPORT gzputc(file, c) return -1; } - /* try writing to input buffer for speed (state->size == 0 if buffer not + /* try writing to input buffer for speed (state.state->size == 0 if buffer not initialized) */ if (state.state->size) { if (strm->avail_in == 0) @@ -287,7 +346,7 @@ int ZEXPORT gzputc(file, c) /* no room in buffer or not initialized, use gz_write() */ buf[0] = (unsigned char)c; - if (gzwrite(file, buf, 1) != 1) + if (gz_write(state, buf, 1) != 1) return -1; return c & 0xff; } @@ -298,11 +357,21 @@ int ZEXPORT gzputs(file, str) const char *str; { int ret; - unsigned len; + z_size_t len; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state.state->mode != GZ_WRITE || state.state->err != Z_OK) + return -1; /* write string */ - len = (unsigned)strlen(str); - ret = gzwrite(file, str, len); + len = strlen(str); + ret = gz_write(state, str, len); return ret == 0 && len != 0 ? -1 : ret; } @@ -312,63 +381,73 @@ int ZEXPORT gzputs(file, str) /* -- see zlib.h -- */ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) { - int size, len; + int len; + unsigned left; + char *next; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) - return -1; + return Z_STREAM_ERROR; state = (gz_statep)file; strm = &(state.state->strm); /* check that we're writing and that there's no error */ if (state.state->mode != GZ_WRITE || state.state->err != Z_OK) - return 0; + return Z_STREAM_ERROR; /* make sure we have some buffer space */ if (state.state->size == 0 && gz_init(state) == -1) - return 0; + return state.state->err; /* check for seek request */ if (state.state->seek) { state.state->seek = 0; if (gz_zero(state, state.state->skip) == -1) - return 0; + return state.state->err; } - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* do the printf() into the input buffer, put length in len */ - size = (int)(state.state->size); - state.state->in[size - 1] = 0; + /* do the printf() into the input buffer, put length in len -- the input + buffer is double-sized just for this function, so there is guaranteed to + be state.state->size bytes available after the current contents */ + if (strm->avail_in == 0) + strm->next_in = state.state->in; + next = (char *)(state.state->in + (strm->next_in - state.state->in) + strm->avail_in); + next[state.state->size - 1] = 0; #ifdef NO_vsnprintf # ifdef HAS_vsprintf_void - (void)vsprintf((char *)(state.state->in), format, va); - for (len = 0; len < size; len++) - if (state.state->in[len] == 0) break; + (void)vsprintf(next, format, va); + for (len = 0; len < state.state->size; len++) + if (next[len] == 0) break; # else - len = vsprintf((char *)(state.state->in), format, va); + len = vsprintf(next, format, va); # endif #else # ifdef HAS_vsnprintf_void - (void)vsnprintf((char *)(state.state->in), size, format, va); - len = strlen((char *)(state.state->in)); + (void)vsnprintf(next, state.state->size, format, va); + len = strlen(next); # else - len = vsnprintf((char *)(state.state->in), size, format, va); + len = vsnprintf(next, state.state->size, format, va); # endif #endif /* check that printf() results fit in buffer */ - if (len <= 0 || len >= (int)size || state.state->in[size - 1] != 0) + if (len == 0 || (unsigned)len >= state.state->size || next[state.state->size - 1] != 0) return 0; - /* update buffer and position, defer compression until needed */ - strm->avail_in = (unsigned)len; - strm->next_in = state.state->in; + /* update buffer and position, compress first half if past that */ + strm->avail_in += (unsigned)len; state.state->x.pos += len; + if (strm->avail_in >= state.state->size) { + left = strm->avail_in - state.state->size; + strm->avail_in = state.state->size; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return state.state->err; + memcpy(state.state->in, state.state->in + state.state->size, left); + strm->next_in = state.state->in; + strm->avail_in = left; + } return len; } @@ -393,73 +472,82 @@ int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; { - int size, len; + unsigned len, left; + char *next; gz_statep state; z_streamp strm; /* get internal structure */ if (file == NULL) - return -1; + return Z_STREAM_ERROR; state = (gz_statep)file; strm = &(state.state->strm); /* check that can really pass pointer in ints */ if (sizeof(int) != sizeof(void *)) - return 0; + return Z_STREAM_ERROR; /* check that we're writing and that there's no error */ if (state.state->mode != GZ_WRITE || state.state->err != Z_OK) - return 0; + return Z_STREAM_ERROR; /* make sure we have some buffer space */ if (state.state->size == 0 && gz_init(state) == -1) - return 0; + return state.state->error; /* check for seek request */ if (state.state->seek) { state.state->seek = 0; if (gz_zero(state, state.state->skip) == -1) - return 0; + return state.state->error; } - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* do the printf() into the input buffer, put length in len */ - size = (int)(state.state->size); - state.state->in[size - 1] = 0; + /* do the printf() into the input buffer, put length in len -- the input + buffer is double-sized just for this function, so there is guaranteed to + be state.state->size bytes available after the current contents */ + if (strm->avail_in == 0) + strm->next_in = state.state->in; + next = (char *)(strm->next_in + strm->avail_in); + next[state.state->size - 1] = 0; #ifdef NO_snprintf # ifdef HAS_sprintf_void - sprintf((char *)(state.state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20); for (len = 0; len < size; len++) - if (state.state->in[len] == 0) break; + if (next[len] == 0) + break; # else - len = sprintf((char *)(state.state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, + a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #else # ifdef HAS_snprintf_void - snprintf((char *)(state.state->in), size, format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); - len = strlen((char *)(state.state->in)); + snprintf(next, state.state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(next); # else - len = snprintf((char *)(state.state->in), size, format, a1, a2, a3, a4, a5, a6, - a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, - a19, a20); + len = snprintf(next, state.state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); # endif #endif /* check that printf() results fit in buffer */ - if (len <= 0 || len >= (int)size || state.state->in[size - 1] != 0) + if (len == 0 || len >= state.state->size || next[state.state->size - 1] != 0) return 0; - /* update buffer and position, defer compression until needed */ - strm->avail_in = (unsigned)len; - strm->next_in = state.state->in; + /* update buffer and position, compress first half if past that */ + strm->avail_in += len; state.state->x.pos += len; - return len; + if (strm->avail_in >= state.state->size) { + left = strm->avail_in - state.state->size; + strm->avail_in = state.state->size; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return state.state->err; + memcpy(state.state->in, state.state->in + state.state->size, left); + strm->next_in = state.state->in; + strm->avail_in = left; + } + return (int)len; } #endif @@ -473,7 +561,7 @@ int ZEXPORT gzflush(file, flush) /* get internal structure */ if (file == NULL) - return -1; + return Z_STREAM_ERROR; state = (gz_statep)file; /* check that we're writing and that there's no error */ @@ -488,11 +576,11 @@ int ZEXPORT gzflush(file, flush) if (state.state->seek) { state.state->seek = 0; if (gz_zero(state, state.state->skip) == -1) - return -1; + return state.state->err; } /* compress remaining data with requested flush */ - gz_comp(state, flush); + (void)gz_comp(state, flush); return state.state->err; } @@ -523,13 +611,13 @@ int ZEXPORT gzsetparams(file, level, strategy) if (state.state->seek) { state.state->seek = 0; if (gz_zero(state, state.state->skip) == -1) - return -1; + return state.state->err; } /* change compression parameters for subsequent input */ if (state.state->size) { /* flush previous input with previous parameters before changing */ - if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) + if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1) return state.state->err; deflateParams(strm, level, strategy); } From c9512db301674a8fdd6528ddcb9067f129d2c7aa Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 18 Jan 2017 12:51:44 +0100 Subject: [PATCH 122/227] gzcompatibility.h updated to zlib 1.2.11 --- zlibWrapper/gzcompatibility.h | 22 ++++++++++++++++++++++ zlibWrapper/zstd_zlibwrapper.c | 14 ++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/zlibWrapper/gzcompatibility.h b/zlibWrapper/gzcompatibility.h index a4f275e11..e2ec1addb 100644 --- a/zlibWrapper/gzcompatibility.h +++ b/zlibWrapper/gzcompatibility.h @@ -43,3 +43,25 @@ ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, const char *mode)); #endif #endif + + +#if ZLIB_VERNUM < 0x12B0 +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +#endif diff --git a/zlibWrapper/zstd_zlibwrapper.c b/zlibWrapper/zstd_zlibwrapper.c index 2b55bfabc..a22270276 100644 --- a/zlibWrapper/zstd_zlibwrapper.c +++ b/zlibWrapper/zstd_zlibwrapper.c @@ -1043,6 +1043,20 @@ ZEXTERN uLong ZEXPORT z_crc32 OF((uLong crc, const Bytef *buf, uInt len)) return crc32(crc, buf, len); } + +#if ZLIB_VERNUM >= 0x12B0 +ZEXTERN uLong ZEXPORT z_adler32_z OF((uLong adler, const Bytef *buf, z_size_t len)) +{ + return adler32_z(adler, buf, len); +} + +ZEXTERN uLong ZEXPORT z_crc32_z OF((uLong crc, const Bytef *buf, z_size_t len)) +{ + return crc32_z(crc, buf, len); +} +#endif + + #if ZLIB_VERNUM >= 0x1270 ZEXTERN const z_crc_t FAR * ZEXPORT z_get_crc_table OF((void)) { From c3a04deda241ed62da47e422dcc664307569d462 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 18 Jan 2017 14:36:10 +0100 Subject: [PATCH 123/227] fixed clang warnings in gzread.c and gzwrite.c --- zlibWrapper/gzread.c | 8 ++++---- zlibWrapper/gzwrite.c | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/zlibWrapper/gzread.c b/zlibWrapper/gzread.c index 004fe6af9..81f4fb082 100644 --- a/zlibWrapper/gzread.c +++ b/zlibWrapper/gzread.c @@ -27,7 +27,7 @@ local int gz_load(state, buf, len, have) unsigned len; unsigned *have; { - int ret; + ssize_t ret; unsigned get, max = ((unsigned)-1 >> 2) + 1; *have = 0; @@ -320,7 +320,7 @@ local z_size_t gz_read(state, buf, len) /* set n to the maximum amount of len that fits in an unsigned int */ n = -1; if (n > len) - n = len; + n = (unsigned)len; /* first just try copying data from the output buffer */ if (state.state->x.have) { @@ -401,7 +401,7 @@ int ZEXPORT gzread(file, buf, len) } /* read len or fewer bytes to buf */ - len = gz_read(state, buf, len); + len = (unsigned)gz_read(state, buf, len); /* check for an error */ if (len == 0 && state.state->err != Z_OK && state.state->err != Z_BUF_ERROR) @@ -485,7 +485,7 @@ int ZEXPORT gzgetc(file) } /* nothing there -- try gz_read() */ - ret = gz_read(state, buf, 1); + ret = (unsigned)gz_read(state, buf, 1); return ret < 1 ? -1 : buf[0]; } diff --git a/zlibWrapper/gzwrite.c b/zlibWrapper/gzwrite.c index c17d194a2..af00e23c4 100644 --- a/zlibWrapper/gzwrite.c +++ b/zlibWrapper/gzwrite.c @@ -89,7 +89,7 @@ local int gz_comp(state, flush) if (state.state->direct) { while (strm->avail_in) { put = strm->avail_in > max ? max : strm->avail_in; - writ = write(state.state->fd, strm->next_in, put); + writ = (int)write(state.state->fd, strm->next_in, put); if (writ < 0) { gz_error(state, Z_ERRNO, zstrerror()); return -1; @@ -110,7 +110,7 @@ local int gz_comp(state, flush) while (strm->next_out > state.state->x.next) { put = strm->next_out - state.state->x.next > (int)max ? max : (unsigned)(strm->next_out - state.state->x.next); - writ = write(state.state->fd, state.state->x.next, put); + writ = (int)write(state.state->fd, state.state->x.next, put); if (writ < 0) { gz_error(state, Z_ERRNO, zstrerror()); return -1; @@ -204,7 +204,7 @@ local z_size_t gz_write(state, buf, len) if (len < state.state->size) { /* copy to input buffer, compress when full */ do { - unsigned have, copy; + z_size_t have, copy; if (state.state->strm.avail_in == 0) state.state->strm.next_in = state.state->in; @@ -230,10 +230,10 @@ local z_size_t gz_write(state, buf, len) /* directly compress user buffer to file */ state.state->strm.next_in = (z_const Bytef *)buf; do { - unsigned n = (unsigned)-1; + z_size_t n = (unsigned)-1; if (n > len) n = len; - state.state->strm.avail_in = n; + state.state->strm.avail_in = (z_uInt)n; state.state->x.pos += n; if (gz_comp(state, Z_NO_FLUSH) == -1) return 0; @@ -371,7 +371,7 @@ int ZEXPORT gzputs(file, str) /* write string */ len = strlen(str); - ret = gz_write(state, str, len); + ret = (int)gz_write(state, str, len); return ret == 0 && len != 0 ? -1 : ret; } From 957a6d596b493f2b0b6b05edcda8097c01f70062 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 18 Jan 2017 19:04:00 +0100 Subject: [PATCH 124/227] updated link to copyright notice --- zlibWrapper/gzlib.c | 2 +- zlibWrapper/gzread.c | 2 +- zlibWrapper/gzwrite.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zlibWrapper/gzlib.c b/zlibWrapper/gzlib.c index 2caf54e54..aa94206a8 100644 --- a/zlibWrapper/gzlib.c +++ b/zlibWrapper/gzlib.c @@ -3,7 +3,7 @@ /* gzlib.c -- zlib functions common to reading and writing gzip files * Copyright (C) 2004-2017 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h + * For conditions of distribution and use, see http://www.zlib.net/zlib_license.html */ #include "gzguts.h" diff --git a/zlibWrapper/gzread.c b/zlibWrapper/gzread.c index 81f4fb082..d37aaa1d4 100644 --- a/zlibWrapper/gzread.c +++ b/zlibWrapper/gzread.c @@ -3,7 +3,7 @@ /* gzread.c -- zlib functions for reading gzip files * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h + * For conditions of distribution and use, see http://www.zlib.net/zlib_license.html */ #include "gzguts.h" diff --git a/zlibWrapper/gzwrite.c b/zlibWrapper/gzwrite.c index af00e23c4..bcda4774a 100644 --- a/zlibWrapper/gzwrite.c +++ b/zlibWrapper/gzwrite.c @@ -3,7 +3,7 @@ /* gzwrite.c -- zlib functions for writing gzip files * Copyright (C) 2004-2017 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h + * For conditions of distribution and use, see http://www.zlib.net/zlib_license.html */ #include "gzguts.h" From a6db7a7b9b3c60d24f4645cfb71a2aaa0d77a072 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 18 Jan 2017 11:57:34 -0800 Subject: [PATCH 125/227] fixed cmaketest (buffer_t){NULL,0} is not considered a constant. {NULL,0} is. --- lib/compress/zstdmt_compress.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index c864ef219..329dc78fd 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -52,7 +52,7 @@ typedef struct buffer_s { size_t size; } buffer_t; -static const buffer_t g_nullBuffer = (buffer_t) { NULL, 0 }; +static const buffer_t g_nullBuffer = { NULL, 0 }; typedef struct ZSTDMT_bufferPool_s { unsigned totalBuffers;; @@ -277,6 +277,8 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx) ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[jobID].cctx); mtctx->jobs[jobID].cctx = NULL; } + ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->inBuff.buffer); + mtctx->inBuff.buffer = g_nullBuffer; } size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) @@ -402,7 +404,8 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { zcs->params = ZSTD_getParams(compressionLevel, 0, 0); zcs->targetSectionSize = (size_t)1 << (zcs->params.cParams.windowLog + 2); zcs->inBuffSize = 5 * (1 << zcs->params.cParams.windowLog); - zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); /* check for NULL ! */ + zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); + if (zcs->inBuff.buffer.start == NULL) return ERROR(memory_allocation); zcs->inBuff.filled = 0; zcs->doneJobID = 0; zcs->nextJobID = 0; @@ -413,7 +416,7 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { - if (zcs->frameEnded) return ERROR(stage_wrong); + if (zcs->frameEnded) return ERROR(stage_wrong); /* current frame being ended. Finish it and restart a new one */ /* fill input buffer */ { size_t const toLoad = MIN(input->size - input->pos, zcs->inBuffSize - zcs->inBuff.filled); From 563ef8acf440033fbc60df75df06cef31982ae73 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 18 Jan 2017 12:12:10 -0800 Subject: [PATCH 126/227] CCtxPool starts empty, as suggested by @terrelln Also : make zstdmt now a target from root --- .gitignore | 1 + Makefile | 5 +++++ lib/compress/zstdmt_compress.c | 22 +++++++--------------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 796a696d3..dd7a74519 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ # Executables zstd +zstdmt *.exe *.out *.app diff --git a/Makefile b/Makefile index 0a3634c39..8ffc9ae9d 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,11 @@ zstd: @$(MAKE) -C $(PRGDIR) $@ cp $(PRGDIR)/zstd$(EXT) . +.PHONY: zstdmt +zstdmt: + @$(MAKE) -C $(PRGDIR) $@ + cp $(PRGDIR)/zstd$(EXT) ./zstdmt$(EXT) + .PHONY: zlibwrapper zlibwrapper: $(MAKE) -C $(ZWRAPDIR) test diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 329dc78fd..f880e8525 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -122,8 +122,8 @@ typedef struct { static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool) { unsigned u; - for (u=0; uavailCCtx; u++) /* note : availCCtx is supposed == totalCCtx; otherwise, some CCtx are still in use */ - ZSTD_freeCCtx(pool->cctx[u]); + for (u=0; utotalCCtx; u++) + ZSTD_freeCCtx(pool->cctx[u]); /* note : compatible with free on NULL */ free(pool); } @@ -131,15 +131,8 @@ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads) { ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) calloc(1, sizeof(ZSTDMT_CCtxPool) + nbThreads*sizeof(ZSTD_CCtx*)); if (!cctxPool) return NULL; - { unsigned threadNb; - for (threadNb=0; threadNbcctx[threadNb] = ZSTD_createCCtx(); - if (cctxPool->cctx[threadNb]==NULL) { /* failed cctx allocation : abort cctxPool creation */ - cctxPool->totalCCtx = cctxPool->availCCtx = threadNb; - ZSTDMT_freeCCtxPool(cctxPool); - return NULL; - } } } - cctxPool->totalCCtx = cctxPool->availCCtx = nbThreads; + cctxPool->totalCCtx = nbThreads; + cctxPool->availCCtx = 0; return cctxPool; } @@ -149,17 +142,16 @@ static ZSTD_CCtx* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* pool) pool->availCCtx--; return pool->cctx[pool->availCCtx]; } - /* note : should not be possible, since totalCCtx==nbThreads */ - return ZSTD_createCCtx(); /* note : can be NULL is creation fails ! */ + return ZSTD_createCCtx(); /* note : can be NULL, when creation fails ! */ } static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) { - if (cctx==NULL) return; /* release on NULL */ + if (cctx==NULL) return; /* compatibility with release on NULL */ if (pool->availCCtx < pool->totalCCtx) pool->cctx[pool->availCCtx++] = cctx; else - /* note : should not be possible, since totalCCtx==nbThreads */ + /* pool overflow : should not happen, since totalCCtx==nbThreads */ ZSTD_freeCCtx(cctx); } From 0b5370ae38b85268411e2a22512df72b17cca245 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Wed, 18 Jan 2017 13:44:43 -0800 Subject: [PATCH 127/227] Prefix notes with /**< --- lib/zstd.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/zstd.h b/lib/zstd.h index 52afc26bd..7625f12fc 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -510,7 +510,7 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); /*===== Advanced Streaming compression functions =====*/ ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem); ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct */ -ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /* note: a dict will not be used if dict == NULL or dictSize < 8 */ +ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< note: a dict will not be used if dict == NULL or dictSize < 8 */ ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /**< note : cdict will just be referenced, and must outlive compression session */ @@ -521,7 +521,7 @@ ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs); /*===== Advanced Streaming decompression functions =====*/ typedef enum { ZSTDdsp_maxWindowSize } ZSTD_DStreamParameter_e; ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem); -ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /* note: a dict will not be used if dict == NULL or dictSize < 8 */ +ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: a dict will not be used if dict == NULL or dictSize < 8 */ ZSTDLIB_API size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue); ZSTDLIB_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); /**< note : ddict will just be referenced, and must outlive decompression session */ ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); /**< re-use decompression parameters from previous init; saves dictionary loading */ From 4885f591b30348cfef45fffff24a9a7dbb19ea40 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 18 Jan 2017 14:11:37 -0800 Subject: [PATCH 128/227] trap compression errors, collect back resources from workers --- lib/compress/zstdmt_compress.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index f880e8525..3762f5a25 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -423,7 +423,6 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; if ((cctx==NULL) || (dstBuffer.start==NULL)) { - zcs->jobs[jobID].cSize = ERROR(memory_allocation); zcs->jobs[jobID].jobCompleted = 1; zcs->nextJobID++; ZSTDMT_waitForAllJobsCompleted(zcs); @@ -438,7 +437,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu zcs->jobs[jobID].params = zcs->params; zcs->jobs[jobID].dstBuff = dstBuffer; zcs->jobs[jobID].cctx = cctx; - zcs->jobs[jobID].firstChunk = (jobID==0); + zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0); zcs->jobs[jobID].lastChunk = 0; zcs->jobs[jobID].jobCompleted = 0; zcs->jobs[jobID].dstFlushed = 0; @@ -448,7 +447,6 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu /* get a new buffer for next input - save remaining into it */ zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */ - zcs->jobs[jobID].cSize = ERROR(memory_allocation); zcs->jobs[jobID].jobCompleted = 1; zcs->nextJobID++; ZSTDMT_waitForAllJobsCompleted(zcs); @@ -472,6 +470,11 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu zcs->jobs[jobID].cctx = NULL; ZSTDMT_releaseBuffer(zcs->buffPool, job.src); zcs->jobs[jobID].srcStart = NULL; zcs->jobs[jobID].src = g_nullBuffer; + if (ZSTD_isError(job.cSize)) { + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + return job.cSize; + } memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); output->pos += toWrite; job.dstFlushed += toWrite; @@ -499,7 +502,6 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; if ((cctx==NULL) || (dstBuffer.start==NULL)) { - zcs->jobs[jobID].cSize = ERROR(memory_allocation); zcs->jobs[jobID].jobCompleted = 1; zcs->nextJobID++; ZSTDMT_waitForAllJobsCompleted(zcs); @@ -514,7 +516,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp zcs->jobs[jobID].params = zcs->params; zcs->jobs[jobID].dstBuff = dstBuffer; zcs->jobs[jobID].cctx = cctx; - zcs->jobs[jobID].firstChunk = (jobID==0); + zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0); zcs->jobs[jobID].lastChunk = endFrame; zcs->jobs[jobID].jobCompleted = 0; zcs->jobs[jobID].dstFlushed = 0; @@ -526,7 +528,6 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); zcs->inBuff.filled = 0; if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */ - zcs->jobs[jobID].cSize = ERROR(memory_allocation); zcs->jobs[jobID].jobCompleted = 1; zcs->nextJobID++; ZSTDMT_waitForAllJobsCompleted(zcs); @@ -543,6 +544,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp } /* check if there is any data available to flush */ + if (zcs->doneJobID == zcs->nextJobID) return 0; /* all flushed ! */ { unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); while (zcs->jobs[wJobID].jobCompleted==0) { @@ -555,6 +557,11 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[wJobID].cctx = NULL; /* release cctx for future task */ ZSTDMT_releaseBuffer(zcs->buffPool, job.src); zcs->jobs[wJobID].srcStart = NULL; zcs->jobs[wJobID].src = g_nullBuffer; + if (ZSTD_isError(job.cSize)) { + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + return job.cSize; + } memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); output->pos += toWrite; job.dstFlushed += toWrite; From 3a01c46b266996e7256faf731dd6813b9f47f3a7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 18 Jan 2017 15:18:17 -0800 Subject: [PATCH 129/227] ZSTDMT_initCStream() supports restart from invalid state ZSTDMT_initCStream() will correcly scrub for resources when it detects that previous compression was not properly finished. --- lib/compress/zstdmt_compress.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 3762f5a25..c417e8aad 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -228,6 +228,7 @@ struct ZSTDMT_CCtx_s { unsigned doneJobID; unsigned nextJobID; unsigned frameEnded; + unsigned allJobsCompleted; ZSTDMT_jobDescription jobs[1]; /* variable size (must lies at the end) */ }; @@ -244,6 +245,7 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) if (!cctx) return NULL; cctx->nbThreads = nbThreads; cctx->jobIDMask = nbJobs - 1; + cctx->allJobsCompleted = 1; cctx->factory = POOL_create(nbThreads, 1); cctx->buffPool = ZSTDMT_createBufferPool(nbThreads); cctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads); @@ -277,8 +279,8 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) { if (mtctx==NULL) return 0; /* compatible with free on NULL */ POOL_free(mtctx->factory); - ZSTDMT_releaseAllJobResources(mtctx); /* kill workers first */ - ZSTDMT_freeBufferPool(mtctx->buffPool); /* release job resources first */ + if (!mtctx->allJobsCompleted) ZSTDMT_releaseAllJobResources(mtctx); /* stop workers first */ + ZSTDMT_freeBufferPool(mtctx->buffPool); /* release job resources into pools first */ ZSTDMT_freeCCtxPool(mtctx->cctxPool); pthread_mutex_destroy(&mtctx->jobCompleted_mutex); pthread_cond_destroy(&mtctx->jobCompleted_cond); @@ -393,6 +395,11 @@ static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* zcs) { } size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { + if (zcs->allJobsCompleted == 0) { /* previous job not correctly finished */ + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + zcs->allJobsCompleted = 1; + } zcs->params = ZSTD_getParams(compressionLevel, 0, 0); zcs->targetSectionSize = (size_t)1 << (zcs->params.cParams.windowLog + 2); zcs->inBuffSize = 5 * (1 << zcs->params.cParams.windowLog); @@ -402,13 +409,14 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { zcs->doneJobID = 0; zcs->nextJobID = 0; zcs->frameEnded = 0; + zcs->allJobsCompleted = 0; return 0; } size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { - if (zcs->frameEnded) return ERROR(stage_wrong); /* current frame being ended. Finish it and restart a new one */ + if (zcs->frameEnded) return ERROR(stage_wrong); /* current frame being ended. Only flush is allowed. Restart with init */ /* fill input buffer */ { size_t const toLoad = MIN(input->size - input->pos, zcs->inBuffSize - zcs->inBuff.filled); @@ -573,7 +581,9 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp } /* return value : how many bytes left in buffer ; fake it to 1 if unknown but >0 */ if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed); - return (zcs->doneJobID < zcs->nextJobID); + if (zcs->doneJobID < zcs->nextJobID) return 1; /* still some buffer to flush */ + zcs->allJobsCompleted = zcs->frameEnded; + return 0; } } } From 6073b3e6b86488d1d6963b7dbf3fedf34ac7158e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 18 Jan 2017 15:32:38 -0800 Subject: [PATCH 130/227] ZSTDMT_endStream : nullify input buffer after flush There will be no more input after ZSTDMT_endStream invocation : only flush/end is allowed (to fully collect compressed result). --- lib/compress/zstdmt_compress.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index c417e8aad..d552acee0 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -543,6 +543,8 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp return ERROR(memory_allocation); } } else { + zcs->inBuff.buffer = g_nullBuffer; + zcs->inBuff.filled = 0; zcs->frameEnded = 1; } From 502966ab9c6f92d96beb122217332ca07bfe9a53 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Thu, 19 Jan 2017 12:10:52 +0100 Subject: [PATCH 131/227] zlibWrapper: added the totalInBytes flag - we need it as strm->total_in can be reset by user --- zlibWrapper/.gitignore | 3 +++ zlibWrapper/zstd_zlibwrapper.c | 42 ++++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/zlibWrapper/.gitignore b/zlibWrapper/.gitignore index 8ce15613c..23d2f3a66 100644 --- a/zlibWrapper/.gitignore +++ b/zlibWrapper/.gitignore @@ -20,3 +20,6 @@ zwrapbench *.bat *.zip *.txt + +# Directories +minizip/ \ No newline at end of file diff --git a/zlibWrapper/zstd_zlibwrapper.c b/zlibWrapper/zstd_zlibwrapper.c index a22270276..1960d19e9 100644 --- a/zlibWrapper/zstd_zlibwrapper.c +++ b/zlibWrapper/zstd_zlibwrapper.c @@ -81,7 +81,8 @@ typedef enum { ZWRAP_useInit, ZWRAP_useReset, ZWRAP_streamEnd } ZWRAP_state_t; typedef struct { ZSTD_CStream* zbc; int compressionLevel; - int streamEnd; + int streamEnd; /* a flag to signal the end of a stream */ + unsigned long long totalInBytes; /* we need it as strm->total_in can be reset by user */ ZSTD_customMem customMem; z_stream allocFunc; /* copy of zalloc, zfree, opaque */ ZSTD_inBuffer inBuffer; @@ -189,6 +190,7 @@ ZEXTERN int ZEXPORT z_deflateInit_ OF((z_streamp strm, int level, level = ZWRAP_DEFAULT_CLEVEL; zwc->streamEnd = 0; + zwc->totalInBytes = 0; zwc->compressionLevel = level; strm->state = (struct internal_state*) zwc; /* use state which in not used by user */ strm->total_in = 0; @@ -217,7 +219,10 @@ int ZWRAP_deflateReset_keepDict(z_streamp strm) return deflateReset(strm); { ZWRAP_CCtx* zwc = (ZWRAP_CCtx*) strm->state; - if (zwc) zwc->streamEnd = 0; + if (zwc) { + zwc->streamEnd = 0; + zwc->totalInBytes = 0; + } } strm->total_in = 0; @@ -289,7 +294,7 @@ ZEXTERN int ZEXPORT z_deflate OF((z_streamp strm, int flush)) if (res != Z_OK) return ZWRAPC_finishWithError(zwc, strm, res); if (flush != Z_FINISH) zwc->comprState = ZWRAP_useReset; } else { - if (strm->total_in == 0) { + if (zwc->totalInBytes == 0) { if (zwc->comprState == ZWRAP_useReset) { size_t const errorCode = ZSTD_resetCStream(zwc->zbc, (flush == Z_FINISH) ? strm->avail_in : zwc->pledgedSrcSize); if (ZSTD_isError(errorCode)) { LOG_WRAPPERC("ERROR: ZSTD_resetCStream errorCode=%s\n", ZSTD_getErrorName(errorCode)); return ZWRAPC_finishWithError(zwc, strm, 0); } @@ -317,6 +322,7 @@ ZEXTERN int ZEXPORT z_deflate OF((z_streamp strm, int flush)) strm->total_out += zwc->outBuffer.pos; strm->avail_out -= zwc->outBuffer.pos; strm->total_in += zwc->inBuffer.pos; + zwc->totalInBytes += zwc->inBuffer.pos; strm->next_in += zwc->inBuffer.pos; strm->avail_in -= zwc->inBuffer.pos; } @@ -411,6 +417,7 @@ typedef struct { ZSTD_DStream* zbd; char headerBuf[16]; /* should be equal or bigger than ZSTD_frameHeaderSize_min */ int errorCount; + unsigned long long totalInBytes; /* we need it as strm->total_in can be reset by user */ ZWRAP_state_t decompState; ZSTD_inBuffer inBuffer; ZSTD_outBuffer outBuffer; @@ -511,6 +518,7 @@ ZEXTERN int ZEXPORT z_inflateInit_ OF((z_streamp strm, strcpy(zwd->version, version); zwd->stream_size = stream_size; + zwd->totalInBytes = 0; strm->state = (struct internal_state*) zwd; /* use state which in not used by user */ strm->total_in = 0; strm->total_out = 0; @@ -551,6 +559,7 @@ int ZWRAP_inflateReset_keepDict(z_streamp strm) if (zwd == NULL) return Z_STREAM_ERROR; ZWRAP_initDCtx(zwd); zwd->decompState = ZWRAP_useReset; + zwd->totalInBytes = 0; } strm->total_in = 0; @@ -610,9 +619,9 @@ ZEXTERN int ZEXPORT z_inflateSetDictionary OF((z_streamp strm, if (ZSTD_isError(errorCode)) return ZWRAPD_finishWithError(zwd, strm, 0); zwd->decompState = ZWRAP_useReset; - if (strm->total_in == ZSTD_HEADERSIZE) { + if (zwd->totalInBytes == ZSTD_HEADERSIZE) { zwd->inBuffer.src = zwd->headerBuf; - zwd->inBuffer.size = strm->total_in; + zwd->inBuffer.size = zwd->totalInBytes; zwd->inBuffer.pos = 0; zwd->outBuffer.dst = strm->next_out; zwd->outBuffer.size = 0; @@ -650,8 +659,8 @@ ZEXTERN int ZEXPORT z_inflate OF((z_streamp strm, int flush)) if (zwd == NULL) return Z_STREAM_ERROR; if (zwd->decompState == ZWRAP_streamEnd) return Z_STREAM_END; - if (strm->total_in < ZLIB_HEADERSIZE) { - if (strm->total_in == 0 && strm->avail_in >= ZLIB_HEADERSIZE) { + if (zwd->totalInBytes < ZLIB_HEADERSIZE) { + if (zwd->totalInBytes == 0 && strm->avail_in >= ZLIB_HEADERSIZE) { if (MEM_readLE32(strm->next_in) != ZSTD_MAGICNUMBER) { if (zwd->windowBits) errorCode = inflateInit2_(strm, zwd->windowBits, zwd->version, zwd->stream_size); @@ -668,12 +677,13 @@ ZEXTERN int ZEXPORT z_inflate OF((z_streamp strm, int flush)) return res; } } else { - srcSize = MIN(strm->avail_in, ZLIB_HEADERSIZE - strm->total_in); - memcpy(zwd->headerBuf+strm->total_in, strm->next_in, srcSize); + srcSize = MIN(strm->avail_in, ZLIB_HEADERSIZE - zwd->totalInBytes); + memcpy(zwd->headerBuf+zwd->totalInBytes, strm->next_in, srcSize); strm->total_in += srcSize; + zwd->totalInBytes += srcSize; strm->next_in += srcSize; strm->avail_in -= srcSize; - if (strm->total_in < ZLIB_HEADERSIZE) return Z_OK; + if (zwd->totalInBytes < ZLIB_HEADERSIZE) return Z_OK; if (MEM_readLE32(zwd->headerBuf) != ZSTD_MAGICNUMBER) { z_stream strm2; @@ -725,9 +735,9 @@ ZEXTERN int ZEXPORT z_inflate OF((z_streamp strm, int flush)) zwd->decompState = ZWRAP_useInit; } - if (strm->total_in < ZSTD_HEADERSIZE) + if (zwd->totalInBytes < ZSTD_HEADERSIZE) { - if (strm->total_in == 0 && strm->avail_in >= ZSTD_HEADERSIZE) { + if (zwd->totalInBytes == 0 && strm->avail_in >= ZSTD_HEADERSIZE) { if (zwd->decompState == ZWRAP_useInit) { errorCode = ZSTD_initDStream(zwd->zbd); if (ZSTD_isError(errorCode)) { LOG_WRAPPERD("ERROR: ZSTD_initDStream errorCode=%s\n", ZSTD_getErrorName(errorCode)); goto error; } @@ -736,12 +746,13 @@ ZEXTERN int ZEXPORT z_inflate OF((z_streamp strm, int flush)) if (ZSTD_isError(errorCode)) goto error; } } else { - srcSize = MIN(strm->avail_in, ZSTD_HEADERSIZE - strm->total_in); - memcpy(zwd->headerBuf+strm->total_in, strm->next_in, srcSize); + srcSize = MIN(strm->avail_in, ZSTD_HEADERSIZE - zwd->totalInBytes); + memcpy(zwd->headerBuf+zwd->totalInBytes, strm->next_in, srcSize); strm->total_in += srcSize; + zwd->totalInBytes += srcSize; strm->next_in += srcSize; strm->avail_in -= srcSize; - if (strm->total_in < ZSTD_HEADERSIZE) return Z_OK; + if (zwd->totalInBytes < ZSTD_HEADERSIZE) return Z_OK; if (zwd->decompState == ZWRAP_useInit) { errorCode = ZSTD_initDStream(zwd->zbd); @@ -785,6 +796,7 @@ ZEXTERN int ZEXPORT z_inflate OF((z_streamp strm, int flush)) strm->total_out += zwd->outBuffer.pos; strm->avail_out -= zwd->outBuffer.pos; strm->total_in += zwd->inBuffer.pos; + zwd->totalInBytes += zwd->inBuffer.pos; strm->next_in += zwd->inBuffer.pos; strm->avail_in -= zwd->inBuffer.pos; if (errorCode == 0) { From 37226c1e9f968dd1a0f4bdbcfaf54716aa88697d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Jan 2017 10:18:17 -0800 Subject: [PATCH 132/227] Simplified compressChunk job minor refactoring : compression done in a single call on first chunk Avoid a mutable hSize variable and eventual recombination to cSize at the end --- lib/compress/zstdmt_compress.c | 52 +++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index d552acee0..93220f5c9 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -10,8 +10,16 @@ # include # include # include - static unsigned g_debugLevel = 2; -# define DEBUGLOG(l, ...) if (l<=g_debugLevel) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, " \n"); } + static unsigned g_debugLevel = 3; +# define DEBUGLOGRAW(l, ...) if (l<=g_debugLevel) { fprintf(stderr, __VA_ARGS__); } +# define DEBUGLOG(l, ...) if (l<=g_debugLevel) { fprintf(stderr, __FILE__ ": "); fprintf(stderr, __VA_ARGS__); fprintf(stderr, " \n"); } + +# define DEBUG_PRINTHEX(l,p,n) { \ + unsigned debug_u; \ + for (debug_u=0; debug_u<(n); debug_u++) \ + DEBUGLOGRAW(l, "%02X ", ((const unsigned char*)(p))[debug_u]); \ + DEBUGLOGRAW(l, " \n"); \ +} static unsigned long long GetCurrentClockTimeMicroseconds() { @@ -39,6 +47,7 @@ if (g_debugLevel>=MUTEX_WAIT_TIME_DLEVEL) { \ # define DEBUGLOG(l, ...) {} /* disabled */ # define PTHREAD_MUTEX_LOCK(m) pthread_mutex_lock(m) +# define DEBUG_PRINTHEX(l,p,n) {} #endif @@ -184,22 +193,20 @@ typedef struct { void ZSTDMT_compressChunk(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; - buffer_t dstBuff = job->dstBuff; - size_t hSize = ZSTD_compressBegin_advanced(job->cctx, NULL, 0, job->params, job->fullFrameSize); - if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } - hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, 0); /* flush frame header */ - if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } - if (job->firstChunk) { /* preserve frame header when it is first chunk */ - dstBuff.start = (char*)dstBuff.start + hSize; - dstBuff.size -= hSize; - } else /* otherwise, overwrite */ - hSize = 0; + buffer_t const dstBuff = job->dstBuff; + size_t const initError = ZSTD_compressBegin_advanced(job->cctx, NULL, 0, job->params, job->fullFrameSize); + if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } + if (!job->firstChunk) { + size_t const hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, 0); /* flush frame header */ + if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } + } + DEBUGLOG(3, "Compressing : "); + DEBUG_PRINTHEX(3, job->srcStart, 12); job->cSize = (job->lastChunk) ? /* last chunk signal */ ZSTD_compressEnd(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize) : ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize); - if (!ZSTD_isError(job->cSize)) job->cSize += hSize; - DEBUGLOG(5, "chunk %u : compressed %u bytes into %u bytes ", (unsigned)job->lastChunk, (unsigned)job->srcSize, (unsigned)job->cSize); + DEBUGLOG(3, "compressed %u bytes into %u bytes (first:%u) (last:%u)", (unsigned)job->srcSize, (unsigned)job->cSize, job->firstChunk, job->lastChunk); _endJob: PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); @@ -271,8 +278,10 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx) ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[jobID].cctx); mtctx->jobs[jobID].cctx = NULL; } + memset(mtctx->jobs, 0, (mtctx->jobIDMask+1)*sizeof(ZSTDMT_jobDescription)); ZSTDMT_releaseBuffer(mtctx->buffPool, mtctx->inBuff.buffer); mtctx->inBuff.buffer = g_nullBuffer; + mtctx->allJobsCompleted = 1; } size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) @@ -335,6 +344,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, mtctx->jobs[u].jobCompleted_cond = &mtctx->jobCompleted_cond; DEBUGLOG(3, "posting job %u (%u bytes)", u, (U32)chunkSize); + DEBUG_PRINTHEX(3, mtctx->jobs[u].srcStart, 12); POOL_add(mtctx->factory, ZSTDMT_compressChunk, &mtctx->jobs[u]); frameStartPos += chunkSize; @@ -345,14 +355,14 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, { unsigned chunkID; size_t error = 0, dstPos = 0; for (chunkID=0; chunkIDjobCompleted_mutex); while (mtctx->jobs[chunkID].jobCompleted==0) { DEBUGLOG(4, "waiting for jobCompleted signal from chunk %u", chunkID); pthread_cond_wait(&mtctx->jobCompleted_cond, &mtctx->jobCompleted_mutex); } pthread_mutex_unlock(&mtctx->jobCompleted_mutex); + DEBUGLOG(3, "ready to write chunk %u ", chunkID); ZSTDMT_releaseCCtx(mtctx->cctxPool, mtctx->jobs[chunkID].cctx); mtctx->jobs[chunkID].cctx = NULL; @@ -422,6 +432,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu { size_t const toLoad = MIN(input->size - input->pos, zcs->inBuffSize - zcs->inBuff.filled); memcpy((char*)zcs->inBuff.buffer.start + zcs->inBuff.filled, input->src, toLoad); input->pos += toLoad; + zcs->inBuff.filled += toLoad; } if (zcs->inBuff.filled == zcs->inBuffSize) { /* filled enough : let's compress */ @@ -438,6 +449,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu return ERROR(memory_allocation); } + DEBUGLOG(1, "preparing job %u to compress %u bytes \n", (U32)zcs->nextJobID, (U32)zcs->targetSectionSize); zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = zcs->targetSectionSize; @@ -474,6 +486,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu ZSTDMT_jobDescription job = zcs->jobs[jobID]; if (job.jobCompleted) { /* job completed : output can be flushed */ size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); + DEBUGLOG(1, "trying to flush compressed data from job %u \n", (U32)zcs->doneJobID); ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[jobID].cctx = NULL; ZSTDMT_releaseBuffer(zcs->buffPool, job.src); @@ -489,6 +502,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => go to next one */ ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); zcs->jobs[jobID].dstBuff = g_nullBuffer; + zcs->jobs[jobID].jobCompleted = 0; zcs->doneJobID++; } else { zcs->jobs[jobID].dstFlushed = job.dstFlushed; /* save flush level into zcs for later retrieval */ @@ -503,6 +517,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp { size_t const srcSize = zcs->inBuff.filled; + DEBUGLOG(1, "flushing : %u bytes to compress", (U32)srcSize); if ((srcSize > 0) || (endFrame && !zcs->frameEnded)) { size_t const dstBufferCapacity = ZSTD_compressBound(srcSize); buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); @@ -548,12 +563,13 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp zcs->frameEnded = 1; } - DEBUGLOG(3, "posting job %u (%u bytes)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize); + DEBUGLOG(1, "posting job %u : %u bytes (end:%u)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize, zcs->jobs[jobID].lastChunk); POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); /* this call is blocking when thread worker pool is exhausted */ zcs->nextJobID++; } /* check if there is any data available to flush */ + DEBUGLOG(1, "zcs->doneJobID : %u ; zcs->nextJobID : %u ", zcs->doneJobID, zcs->nextJobID); if (zcs->doneJobID == zcs->nextJobID) return 0; /* all flushed ! */ { unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); @@ -565,6 +581,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp { /* job completed : output can be flushed */ ZSTDMT_jobDescription job = zcs->jobs[wJobID]; size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); + DEBUGLOG(1, "Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[wJobID].cctx = NULL; /* release cctx for future task */ ZSTDMT_releaseBuffer(zcs->buffPool, job.src); zcs->jobs[wJobID].srcStart = NULL; zcs->jobs[wJobID].src = g_nullBuffer; if (ZSTD_isError(job.cSize)) { @@ -577,6 +594,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp job.dstFlushed += toWrite; if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => next one */ ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); zcs->jobs[wJobID].dstBuff = g_nullBuffer; + zcs->jobs[wJobID].jobCompleted = 0; zcs->doneJobID++; } else { zcs->jobs[wJobID].dstFlushed = job.dstFlushed; From 32dfae6f9841871d88eadcd27a970efab71feb28 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Jan 2017 10:32:55 -0800 Subject: [PATCH 133/227] fixed Multi-threaded compression MT compression generates a single frame. Multi-threading operates by breaking the frames into independent sections. But from a decoder perspective, there is no difference : it's just a suite of blocks. Problem is, decoder preserves repCodes from previous block to start decoding next block. This is also valid between sections, since they are no different than changing block. Previous version would incorrectly initialize repcodes to their default value at the beginning of each section. When using them, there was a mismatch between encoder (default values) and decoder (values from previous block). This change ensures that repcodes won't be used at the beginning of a new section. It works by setting them to 0. This only works with regular (single segment) variants : extDict variants will fail ! Fortunately, sections beyond the 1st one belong to this category. To be checked : btopt strategy. This change was only validated from fast to btlazy2 strategies. --- lib/common/zstd_internal.h | 9 +++++++++ lib/compress/zstd_compress.c | 8 ++++++++ lib/compress/zstdmt_compress.c | 11 ++++++----- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 96e057758..4b56ce1a2 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -267,4 +267,13 @@ MEM_STATIC U32 ZSTD_highbit32(U32 val) } +/* hidden functions */ + +/* ZSTD_invalidateRepCodes() : + * ensures next compression will not use repcodes from previous block. + * Note : only works with regular variant; + * do not use with extDict variant ! */ +void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx); + + #endif /* ZSTD_CCOMMON_H_MODULE */ diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d4800dce1..84a4a0216 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -317,6 +317,14 @@ static size_t ZSTD_resetCCtx_advanced (ZSTD_CCtx* zc, } } +/* ZSTD_invalidateRepCodes() : + * ensures next compression will not use repcodes from previous block. + * Note : only works with regular variant; + * do not use with extDict variant ! */ +void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) { + int i; + for (i=0; irep[i] = 0; +} /*! ZSTD_copyCCtx() : * Duplicate an existing context `srcCCtx` into another one `dstCCtx`. diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 93220f5c9..b060b73f4 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -176,10 +176,10 @@ typedef struct { ZSTD_CCtx* cctx; buffer_t src; const void* srcStart; - size_t srcSize; + size_t srcSize; buffer_t dstBuff; - size_t cSize; - size_t dstFlushed; + size_t cSize; + size_t dstFlushed; unsigned long long fullFrameSize; unsigned firstChunk; unsigned lastChunk; @@ -196,9 +196,10 @@ void ZSTDMT_compressChunk(void* jobDescription) buffer_t const dstBuff = job->dstBuff; size_t const initError = ZSTD_compressBegin_advanced(job->cctx, NULL, 0, job->params, job->fullFrameSize); if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } - if (!job->firstChunk) { - size_t const hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, 0); /* flush frame header */ + if (!job->firstChunk) { /* flush frame header */ + size_t const hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, 0); if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } + ZSTD_invalidateRepCodes(job->cctx); } DEBUGLOG(3, "Compressing : "); From 736788f8e82c85197797879142837c130044eb72 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Jan 2017 12:12:50 -0800 Subject: [PATCH 134/227] added streaming fuzzer tests for MT API Also : fixed corner case, where nb of jobs completed becomes > jobQueueSize which is possible when many flushes are issued while there is not enough dst buffer to flush completed ones. --- lib/compress/zstdmt_compress.c | 21 ++- programs/bench.c | 11 ++ tests/Makefile | 6 +- tests/zstreamtest.c | 330 ++++++++++++++++++++++++++++----- 4 files changed, 310 insertions(+), 58 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index b060b73f4..775c52aae 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -243,7 +243,7 @@ struct ZSTDMT_CCtx_s { ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) { ZSTDMT_CCtx* cctx; - U32 const minNbJobs = nbThreads + 1; + U32 const minNbJobs = nbThreads + 2; U32 const nbJobsLog2 = ZSTD_highbit32(minNbJobs) + 1; U32 const nbJobs = 1 << nbJobsLog2; DEBUGLOG(4, "nbThreads : %u ; minNbJobs : %u ; nbJobsLog2 : %u ; nbJobs : %u \n", @@ -436,7 +436,8 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu zcs->inBuff.filled += toLoad; } - if (zcs->inBuff.filled == zcs->inBuffSize) { /* filled enough : let's compress */ + if ( (zcs->inBuff.filled == zcs->inBuffSize) /* filled enough : let's compress */ + && (zcs->nextJobID <= zcs->doneJobID + zcs->jobIDMask) ) { /* avoid overwriting job round buffer */ size_t const dstBufferCapacity = ZSTD_compressBound(zcs->targetSectionSize); buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); @@ -477,8 +478,8 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu zcs->inBuff.filled = (U32)(zcs->inBuffSize - zcs->targetSectionSize); memcpy(zcs->inBuff.buffer.start, (const char*)zcs->jobs[jobID].srcStart + zcs->targetSectionSize, zcs->inBuff.filled); - DEBUGLOG(3, "posting job %u (%u bytes)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize); - POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); + DEBUGLOG(3, "posting job %u (%u bytes) (note : doneJob = %u=>%u)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize, zcs->doneJobID, zcs->doneJobID & zcs->jobIDMask); + POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); /* This call is blocking if all workers are busy */ zcs->nextJobID++; } @@ -487,7 +488,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu ZSTDMT_jobDescription job = zcs->jobs[jobID]; if (job.jobCompleted) { /* job completed : output can be flushed */ size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); - DEBUGLOG(1, "trying to flush compressed data from job %u \n", (U32)zcs->doneJobID); + DEBUGLOG(1, "flush %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[jobID].cctx = NULL; ZSTDMT_releaseBuffer(zcs->buffPool, job.src); @@ -500,6 +501,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); output->pos += toWrite; job.dstFlushed += toWrite; + DEBUGLOG(1, "remaining : %u bytes ", (U32)(job.cSize - job.dstFlushed)); if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => go to next one */ ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); zcs->jobs[jobID].dstBuff = g_nullBuffer; @@ -519,7 +521,8 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp size_t const srcSize = zcs->inBuff.filled; DEBUGLOG(1, "flushing : %u bytes to compress", (U32)srcSize); - if ((srcSize > 0) || (endFrame && !zcs->frameEnded)) { + if ( ((srcSize > 0) || (endFrame && !zcs->frameEnded)) + && (zcs->nextJobID <= zcs->doneJobID + zcs->jobIDMask) ) { size_t const dstBufferCapacity = ZSTD_compressBound(srcSize); buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); @@ -564,7 +567,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp zcs->frameEnded = 1; } - DEBUGLOG(1, "posting job %u : %u bytes (end:%u)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize, zcs->jobs[jobID].lastChunk); + DEBUGLOG(1, "posting job %u : %u bytes (end:%u) (note : doneJob = %u=>%u)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize, zcs->jobs[jobID].lastChunk, zcs->doneJobID, zcs->doneJobID & zcs->jobIDMask); POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); /* this call is blocking when thread worker pool is exhausted */ zcs->nextJobID++; } @@ -575,7 +578,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp { unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); while (zcs->jobs[wJobID].jobCompleted==0) { - DEBUGLOG(4, "waiting for jobCompleted signal from chunk %u", zcs->doneJobID); /* we want to block when waiting for data to flush */ + DEBUGLOG(5, "waiting for jobCompleted signal from job %u", zcs->doneJobID); /* we want to block when waiting for data to flush */ pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); } pthread_mutex_unlock(&zcs->jobCompleted_mutex); @@ -602,7 +605,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp } /* return value : how many bytes left in buffer ; fake it to 1 if unknown but >0 */ if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed); - if (zcs->doneJobID < zcs->nextJobID) return 1; /* still some buffer to flush */ + if ((zcs->doneJobID < zcs->nextJobID) || (zcs->inBuff.filled)) return 1; /* still some buffer to flush */ zcs->allJobsCompleted = zcs->frameEnded; return 0; } } diff --git a/programs/bench.c b/programs/bench.c index 40e1d4aba..5299b4712 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -385,6 +385,17 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, pos = (U32)(u - bacc); bNb = pos / (128 KB); DISPLAY("(block %u, sub %u, pos %u) \n", segNb, bNb, pos); + if (u>5) { + int n; + for (n=-5; n<0; n++) DISPLAY("%02X ", ((const BYTE*)srcBuffer)[u+n]); + DISPLAY(" :%02X: ", ((const BYTE*)srcBuffer)[u]); + for (n=1; n<3; n++) DISPLAY("%02X ", ((const BYTE*)srcBuffer)[u+n]); + DISPLAY(" \n"); + for (n=-5; n<0; n++) DISPLAY("%02X ", ((const BYTE*)resultBuffer)[u+n]); + DISPLAY(" :%02X: ", ((const BYTE*)resultBuffer)[u]); + for (n=1; n<3; n++) DISPLAY("%02X ", ((const BYTE*)resultBuffer)[u+n]); + DISPLAY(" \n"); + } break; } if (u==srcSize-1) { /* should never happen */ diff --git a/tests/Makefile b/tests/Makefile index 6312584a9..2f399242e 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -18,17 +18,13 @@ # zstreamtest32: Same as zstreamtest, but forced to compile in 32-bits mode # ########################################################################## -DESTDIR?= -PREFIX ?= /usr/local -BINDIR = $(PREFIX)/bin -MANDIR = $(PREFIX)/share/man/man1 ZSTDDIR = ../lib PRGDIR = ../programs PYTHON ?= python3 TESTARTEFACT := versionsTest namespaceTest -CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) +CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) CFLAGS ?= -O3 CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index ce6193085..8720ec78a 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -29,6 +29,7 @@ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel, ZSTD_customMem */ #include "zstd.h" /* ZSTD_compressBound */ #include "zstd_errors.h" /* ZSTD_error_srcSize_wrong */ +#include "zstdmt_compress.h" #include "datagen.h" /* RDG_genBuffer */ #define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ #include "xxhash.h" /* XXH64_* */ @@ -137,7 +138,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo cSize = skippableFrameSize + 8; /* Basic compression test */ - DISPLAYLEVEL(4, "test%3i : compress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); + DISPLAYLEVEL(3, "test%3i : compress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); ZSTD_initCStream_usingDict(zc, CNBuffer, 128 KB, 1); outBuff.dst = (char*)(compressedBuffer)+cSize; outBuff.size = compressedBufferSize; @@ -151,16 +152,16 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo { size_t const r = ZSTD_endStream(zc, &outBuff); if (r != 0) goto _output_error; } /* error, or some data not flushed */ cSize += outBuff.pos; - DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/COMPRESSIBLE_NOISE_LENGTH*100); + DISPLAYLEVEL(3, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/COMPRESSIBLE_NOISE_LENGTH*100); - DISPLAYLEVEL(4, "test%3i : check CStream size : ", testNb++); + DISPLAYLEVEL(3, "test%3i : check CStream size : ", testNb++); { size_t const s = ZSTD_sizeof_CStream(zc); if (ZSTD_isError(s)) goto _output_error; - DISPLAYLEVEL(4, "OK (%u bytes) \n", (U32)s); + DISPLAYLEVEL(3, "OK (%u bytes) \n", (U32)s); } /* skippable frame test */ - DISPLAYLEVEL(4, "test%3i : decompress skippable frame : ", testNb++); + DISPLAYLEVEL(3, "test%3i : decompress skippable frame : ", testNb++); ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB); inBuff.src = compressedBuffer; inBuff.size = cSize; @@ -171,11 +172,11 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (r != 0) goto _output_error; } if (outBuff.pos != 0) goto _output_error; /* skippable frame len is 0 */ - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); /* Basic decompression test */ inBuff2 = inBuff; - DISPLAYLEVEL(4, "test%3i : decompress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); + DISPLAYLEVEL(3, "test%3i : decompress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB); { size_t const r = ZSTD_setDStreamParameter(zd, ZSTDdsp_maxWindowSize, 1000000000); /* large limit */ if (ZSTD_isError(r)) goto _output_error; } @@ -183,33 +184,33 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo if (remaining != 0) goto _output_error; } /* should reach end of frame == 0; otherwise, some data left, or an error */ if (outBuff.pos != CNBufferSize) goto _output_error; /* should regenerate the same amount */ if (inBuff.pos != inBuff.size) goto _output_error; /* should have read the entire frame */ - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); /* Re-use without init */ - DISPLAYLEVEL(4, "test%3i : decompress again without init (re-use previous settings): ", testNb++); + DISPLAYLEVEL(3, "test%3i : decompress again without init (re-use previous settings): ", testNb++); outBuff.pos = 0; { size_t const remaining = ZSTD_decompressStream(zd, &outBuff, &inBuff2); if (remaining != 0) goto _output_error; } /* should reach end of frame == 0; otherwise, some data left, or an error */ if (outBuff.pos != CNBufferSize) goto _output_error; /* should regenerate the same amount */ if (inBuff.pos != inBuff.size) goto _output_error; /* should have read the entire frame */ - DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(3, "OK \n"); /* check regenerated data is byte exact */ - DISPLAYLEVEL(4, "test%3i : check decompressed result : ", testNb++); + DISPLAYLEVEL(3, "test%3i : check decompressed result : ", testNb++); { size_t i; for (i=0; i 100 bytes */ - DISPLAYLEVEL(4, "OK (%s)\n", ZSTD_getErrorName(r)); } + DISPLAYLEVEL(3, "OK (%s)\n", ZSTD_getErrorName(r)); } _end: @@ -412,6 +411,7 @@ static size_t findDiff(const void* buf1, const void* buf2, size_t max) for (u=0; u= testNb) { DISPLAYUPDATE(2, "\r%6u/%6u ", testNb, nbTests); } + else { DISPLAYUPDATE(2, "\r%6u ", testNb); } + FUZ_rand(&coreSeed); + lseed = coreSeed ^ prime1; + + /* states full reset (deliberately not synchronized) */ + /* some issues can only happen when reusing states */ + if ((FUZ_rand(&lseed) & 0xFF) == 131) { + U32 const nbThreads = (FUZ_rand(&lseed) % 6) + 1; + ZSTDMT_freeCCtx(zc); + zc = ZSTDMT_createCCtx(nbThreads); + resetAllowed=0; + } + if ((FUZ_rand(&lseed) & 0xFF) == 132) { + ZSTD_freeDStream(zd); + zd = ZSTD_createDStream(); + ZSTD_initDStream_usingDict(zd, NULL, 0); /* ensure at least one init */ + } + + /* 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]; + } + + /* compression init */ + if ((FUZ_rand(&lseed)&1) /* at beginning, to keep same nb of rand */ + && oldTestLog /* at least one test happened */ && resetAllowed) { + maxTestSize = FUZ_randomLength(&lseed, oldTestLog+2); + if (maxTestSize >= srcBufferSize) maxTestSize = srcBufferSize-1; + { int const compressionLevel = (FUZ_rand(&lseed) % 5) + 1; + size_t const resetError = ZSTDMT_initCStream(zc, compressionLevel); + CHECK(ZSTD_isError(resetError), "ZSTD_resetCStream error : %s", ZSTD_getErrorName(resetError)); + } + } else { + U32 const testLog = FUZ_rand(&lseed) % maxSrcLog; + U32 const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (testLog/3))) + 1; + maxTestSize = FUZ_rLogLength(&lseed, testLog); + oldTestLog = testLog; + /* random dictionary selection */ + dictSize = 0; + dict = NULL; + { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? 0 : maxTestSize; + ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize); + params.fParams.checksumFlag = FUZ_rand(&lseed) & 1; + params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1; + { size_t const initError = ZSTDMT_initCStream(zc, cLevel); + CHECK (ZSTD_isError(initError),"ZSTD_initCStream_advanced error : %s", ZSTD_getErrorName(initError)); + } } } + + /* multi-segments compression test */ + XXH64_reset(&xxhState, 0); + { ZSTD_outBuffer outBuff = { cBuffer, cBufferSize, 0 } ; + U32 n; + for (n=0, cSize=0, totalTestSize=0 ; totalTestSize < maxTestSize ; n++) { + /* compress random chunks into randomly sized dst buffers */ + { size_t const randomSrcSize = FUZ_randomLength(&lseed, maxSampleLog); + size_t const srcSize = MIN (maxTestSize-totalTestSize, randomSrcSize); + size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize); + size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); + size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize); + ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 }; + outBuff.size = outBuff.pos + dstBuffSize; + + DISPLAYLEVEL(5, "Sending %u bytes to compress \n", (U32)srcSize); + { size_t const compressionError = ZSTDMT_compressStream(zc, &outBuff, &inBuff); + CHECK (ZSTD_isError(compressionError), "compression error : %s", ZSTD_getErrorName(compressionError)); } + + XXH64_update(&xxhState, srcBuffer+srcStart, inBuff.pos); + memcpy(copyBuffer+totalTestSize, srcBuffer+srcStart, inBuff.pos); + totalTestSize += inBuff.pos; + } + + /* random flush operation, to mess around */ + if ((FUZ_rand(&lseed) & 15) == 0) { + size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); + size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize); + outBuff.size = outBuff.pos + adjustedDstSize; + DISPLAYLEVEL(5, "Flushing into dst buffer of size %u \n", (U32)adjustedDstSize); + { size_t const flushError = ZSTDMT_flushStream(zc, &outBuff); + CHECK (ZSTD_isError(flushError), "flush error : %s", ZSTD_getErrorName(flushError)); + } } } + + /* final frame epilogue */ + { size_t remainingToFlush = (size_t)(-1); + while (remainingToFlush) { + size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); + size_t const adjustedDstSize = MIN(cBufferSize - cSize, randomDstSize); + outBuff.size = outBuff.pos + adjustedDstSize; + DISPLAYLEVEL(5, "Ending into dst buffer of size %u \n", (U32)adjustedDstSize); + remainingToFlush = ZSTDMT_endStream(zc, &outBuff); + CHECK (ZSTD_isError(remainingToFlush), "flush error : %s", ZSTD_getErrorName(remainingToFlush)); + DISPLAYLEVEL(5, "endStream : remainingToFlush : %u \n", (U32)remainingToFlush); + } } + DISPLAYLEVEL(5, "Frame completed \n"); + crcOrig = XXH64_digest(&xxhState); + cSize = outBuff.pos; + } + + /* multi - fragments decompression test */ + if (!dictSize /* don't reset if dictionary : could be different */ && (FUZ_rand(&lseed) & 1)) { + CHECK (ZSTD_isError(ZSTD_resetDStream(zd)), "ZSTD_resetDStream failed"); + } else { + ZSTD_initDStream_usingDict(zd, dict, dictSize); + } + { size_t decompressionResult = 1; + ZSTD_inBuffer inBuff = { cBuffer, cSize, 0 }; + ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 }; + for (totalGenSize = 0 ; decompressionResult ; ) { + size_t const readCSrcSize = FUZ_randomLength(&lseed, maxSampleLog); + size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); + size_t const dstBuffSize = MIN(dstBufferSize - totalGenSize, randomDstSize); + inBuff.size = inBuff.pos + readCSrcSize; + outBuff.size = inBuff.pos + dstBuffSize; + decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff); + CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult)); + } + CHECK (decompressionResult != 0, "frame not fully decoded"); + CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size") + CHECK (inBuff.pos != cSize, "compressed data should be fully read") + { U64 const crcDest = XXH64(dstBuffer, totalTestSize, 0); + if (crcDest!=crcOrig) findDiff(copyBuffer, dstBuffer, totalTestSize); + CHECK (crcDest!=crcOrig, "decompressed data corrupted"); + } } + + /*===== noisy/erroneous src decompression test =====*/ + + /* add some noise */ + { U32 const nbNoiseChunks = (FUZ_rand(&lseed) & 7) + 2; + U32 nn; for (nn=0; nn='0') && (*argument<='9')) { @@ -731,7 +973,7 @@ int main(int argc, const char** argv) } break; - case 'T': + case 'T': /* limit tests by time */ argument++; nbTests=0; g_clockTime=0; while ((*argument>='0') && (*argument<='9')) { @@ -744,7 +986,7 @@ int main(int argc, const char** argv) g_clockTime *= CLOCKS_PER_SEC; break; - case 's': + case 's': /* manually select seed */ argument++; seed=0; seedset=1; @@ -755,7 +997,7 @@ int main(int argc, const char** argv) } break; - case 't': + case 't': /* select starting test number */ argument++; testNb=0; while ((*argument>='0') && (*argument<='9')) { @@ -799,12 +1041,12 @@ int main(int argc, const char** argv) if (testNb==0) { result = basicUnitTests(0, ((double)proba) / 100, customNULL); /* constant seed for predictability */ if (!result) { - DISPLAYLEVEL(4, "Unit tests using customMem :\n") + DISPLAYLEVEL(3, "Unit tests using customMem :\n") result = basicUnitTests(0, ((double)proba) / 100, customMem); /* use custom memory allocation functions */ } } - if (!result) - result = fuzzerTests(seed, nbTests, testNb, ((double)proba) / 100); + if (!result) result = fuzzerTests(seed, nbTests, testNb, ((double)proba) / 100); + if (!result) result = fuzzerTests_MT(seed, nbTests, testNb, ((double)proba) / 100); if (mainPause) { int unused; From f22adae984f25a7ba625cd9ebe1d0cbea2c85861 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Jan 2017 13:46:30 -0800 Subject: [PATCH 135/227] fixed minor warning (unused variable) in fuzzer --- programs/zstdcli.c | 2 +- tests/zstreamtest.c | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 0474c96c4..c9d8100eb 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -561,7 +561,7 @@ int main(int argCount, const char* argv[]) } } #endif - /* No warning message in pipe mode (stdin + stdout) or multi-files mode */ + /* No status message in pipe mode (stdin - stdout) or multi-files mode */ if (!strcmp(filenameTable[0], stdinmark) && outFileName && !strcmp(outFileName,stdoutmark) && (displayLevel==2)) displayLevel=1; if ((filenameIdx>1) & (displayLevel==2)) displayLevel=1; diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 8720ec78a..6cf6c4a09 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -769,13 +769,9 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp /* random dictionary selection */ dictSize = 0; dict = NULL; - { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? 0 : maxTestSize; - ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize); - params.fParams.checksumFlag = FUZ_rand(&lseed) & 1; - params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1; - { size_t const initError = ZSTDMT_initCStream(zc, cLevel); - CHECK (ZSTD_isError(initError),"ZSTD_initCStream_advanced error : %s", ZSTD_getErrorName(initError)); - } } } + { size_t const initError = ZSTDMT_initCStream(zc, cLevel); + CHECK (ZSTD_isError(initError),"ZSTD_initCStream_advanced error : %s", ZSTD_getErrorName(initError)); + } } /* multi-segments compression test */ XXH64_reset(&xxhState, 0); From 0f984d94c4a17345fb0d2ba475c406524d618f56 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Jan 2017 14:05:07 -0800 Subject: [PATCH 136/227] changed MT enabling macro to ZSTD_MULTITHREAD --- lib/common/pool.c | 6 +++--- lib/common/threading.c | 6 +++--- lib/common/threading.h | 8 ++++---- programs/Makefile | 2 +- tests/Makefile | 10 +++++----- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index e24691f77..f40ed39d4 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -11,7 +11,7 @@ #include /* size_t */ #include /* malloc, calloc, free */ -#ifdef ZSTD_PTHREAD +#ifdef ZSTD_MULTITHREAD #include @@ -160,7 +160,7 @@ void POOL_add(void *ctxVoid, POOL_function function, void *opaque) { pthread_cond_signal(&ctx->queuePopCond); } -#else /* ZSTD_PTHREAD not defined */ +#else /* ZSTD_MULTITHREAD not defined */ /* No multi-threading support */ /* We don't need any data, but if it is empty malloc() might return NULL. */ @@ -183,4 +183,4 @@ void POOL_add(void *ctx, POOL_function function, void *opaque) { function(opaque); } -#endif /* ZSTD_PTHREAD */ +#endif /* ZSTD_MULTITHREAD */ diff --git a/lib/common/threading.c b/lib/common/threading.c index abad2c159..38bbab0d4 100644 --- a/lib/common/threading.c +++ b/lib/common/threading.c @@ -12,10 +12,10 @@ */ /** - * This file will hold wrapper for systems, which do not support Pthreads + * This file will hold wrapper for systems, which do not support pthreads */ -#if defined(ZSTD_PTHREAD) && defined(_WIN32) +#if defined(ZSTD_MULTITHREAD) && defined(_WIN32) /** * Windows minimalist Pthread Wrapper, based on : @@ -70,4 +70,4 @@ int _pthread_join(pthread_t * thread, void **value_ptr) } } -#endif +#endif /* ZSTD_MULTITHREAD */ diff --git a/lib/common/threading.h b/lib/common/threading.h index 4572d71d5..74b2ec042 100644 --- a/lib/common/threading.h +++ b/lib/common/threading.h @@ -18,7 +18,7 @@ extern "C" { #endif -#if defined(ZSTD_PTHREAD) && defined(_WIN32) +#if defined(ZSTD_MULTITHREAD) && defined(_WIN32) /** * Windows minimalist Pthread Wrapper, based on : @@ -73,11 +73,11 @@ int _pthread_join(pthread_t* thread, void** value_ptr); */ -#elif defined(ZSTD_PTHREAD) /* posix assumed ; need a better detection mathod */ +#elif defined(ZSTD_MULTITHREAD) /* posix assumed ; need a better detection mathod */ /* === POSIX Systems === */ # include -#else /* ZSTD_PTHREAD not defined */ +#else /* ZSTD_MULTITHREAD not defined */ /* No multithreading support */ #define pthread_mutex_t int /* #define rather than typedef, as sometimes pthread support is implicit, resulting in duplicated symbols */ @@ -95,7 +95,7 @@ int _pthread_join(pthread_t* thread, void** value_ptr); /* do not use pthread_t */ -#endif /* ZSTD_PTHREAD */ +#endif /* ZSTD_MULTITHREAD */ #if defined (__cplusplus) } diff --git a/programs/Makefile b/programs/Makefile index ff95ddc62..02c924f39 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -127,7 +127,7 @@ gzstd: && $(MAKE) zstd; \ fi -zstdmt: CPPFLAGS += -DZSTD_PTHREAD +zstdmt: CPPFLAGS += -DZSTD_MULTITHREAD zstdmt: LDFLAGS += -lpthread zstdmt: zstd diff --git a/tests/Makefile b/tests/Makefile index 2f399242e..6bc1ad2e2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -44,10 +44,10 @@ ZDICT_FILES := $(ZSTDDIR)/dictBuilder/*.c # Define *.exe as extension for Windows systems ifneq (,$(filter Windows%,$(OS))) EXT =.exe -PTHREAD = -DZSTD_PTHREAD +MULTITHREAD = -DZSTD_MULTITHREAD else EXT = -PTHREAD = -pthread -DZSTD_PTHREAD +MULTITHREAD = -pthread -DZSTD_MULTITHREAD endif VOID = /dev/null @@ -122,10 +122,10 @@ zbufftest-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c zbufftest.c $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@$(EXT) zstreamtest : $(ZSTD_FILES) $(PRGDIR)/datagen.c zstreamtest.c - $(CC) $(FLAGS) $^ -o $@$(EXT) + $(CC) $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT) zstreamtest32 : $(ZSTD_FILES) $(PRGDIR)/datagen.c zstreamtest.c - $(CC) -m32 $(FLAGS) $^ -o $@$(EXT) + $(CC) -m32 $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT) zstreamtest-dll : LDFLAGS+= -L$(ZSTDDIR) -lzstd zstreamtest-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c zstreamtest.c @@ -157,7 +157,7 @@ else endif pool : pool.c $(ZSTDDIR)/common/pool.c $(ZSTDDIR)/common/threading.c - $(CC) $(FLAGS) $(PTHREAD) $^ -o $@$(EXT) + $(CC) $(FLAGS) $(MULTITHREAD) $^ -o $@$(EXT) namespaceTest: if $(CC) namespaceTest.c ../lib/common/xxhash.c -o $@ ; then echo compilation should fail; exit 1 ; fi From 19d670ba9d59b987c51dab5d900d45c62b24490a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Jan 2017 15:32:07 -0800 Subject: [PATCH 137/227] Added ZSTDMT_initCStream_advanced() variant Correctly compress with custom params and dictionary Added relevant fuzzer test in zstreamtest Also : new macro ZSTDMT_SECTION_LOGSIZE_MIN, which sets a minimum size for a full job (note : a flush() command can still generate a partial job anytime) --- lib/compress/zstdmt_compress.c | 44 +++++++++++++++++++++++++++------- lib/compress/zstdmt_compress.h | 9 +++++-- tests/Makefile | 4 ++-- tests/zstreamtest.c | 31 ++++++++++++++++-------- 4 files changed, 66 insertions(+), 22 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 775c52aae..dd1fd3452 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1,3 +1,12 @@ + + +/* ====== Tuning parameters ====== */ +#ifndef ZSTDMT_SECTION_LOGSIZE_MIN +#define ZSTDMT_SECTION_LOGSIZE_MIN 20 /*< minimum size for a full compression job (20==2^20==1 MB) */ +#endif + +/* ====== Dependencies ====== */ + #include /* malloc */ #include /* memcpy */ #include /* threadpool */ @@ -180,13 +189,15 @@ typedef struct { buffer_t dstBuff; size_t cSize; size_t dstFlushed; - unsigned long long fullFrameSize; unsigned firstChunk; unsigned lastChunk; unsigned jobCompleted; pthread_mutex_t* jobCompleted_mutex; pthread_cond_t* jobCompleted_cond; ZSTD_parameters params; + const void* dict; + size_t dictSize; + unsigned long long fullFrameSize; } ZSTDMT_jobDescription; /* ZSTDMT_compressChunk() : POOL_function type */ @@ -194,7 +205,7 @@ void ZSTDMT_compressChunk(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; buffer_t const dstBuff = job->dstBuff; - size_t const initError = ZSTD_compressBegin_advanced(job->cctx, NULL, 0, job->params, job->fullFrameSize); + size_t const initError = ZSTD_compressBegin_advanced(job->cctx, job->dict, job->dictSize, job->params, job->fullFrameSize); if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } if (!job->firstChunk) { /* flush frame header */ size_t const hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, 0); @@ -237,6 +248,9 @@ struct ZSTDMT_CCtx_s { unsigned nextJobID; unsigned frameEnded; unsigned allJobsCompleted; + unsigned long long frameContentSize; + const void* dict; + size_t dictSize; ZSTDMT_jobDescription jobs[1]; /* variable size (must lies at the end) */ }; @@ -405,15 +419,20 @@ static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* zcs) { } } -size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { +size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, + ZSTD_parameters params, unsigned long long pledgedSrcSize) { if (zcs->allJobsCompleted == 0) { /* previous job not correctly finished */ ZSTDMT_waitForAllJobsCompleted(zcs); ZSTDMT_releaseAllJobResources(zcs); zcs->allJobsCompleted = 1; } - zcs->params = ZSTD_getParams(compressionLevel, 0, 0); - zcs->targetSectionSize = (size_t)1 << (zcs->params.cParams.windowLog + 2); - zcs->inBuffSize = 5 * (1 << zcs->params.cParams.windowLog); + params.fParams.checksumFlag = 0; /* current limitation : no checksum (to be lifted in a later version) */ + zcs->params = params; + zcs->dict = dict; + zcs->dictSize = dictSize; + zcs->frameContentSize = pledgedSrcSize; + zcs->targetSectionSize = (size_t)1 << MAX(ZSTDMT_SECTION_LOGSIZE_MIN, (zcs->params.cParams.windowLog + 2)); + zcs->inBuffSize = zcs->targetSectionSize + (1 << zcs->params.cParams.windowLog); zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); if (zcs->inBuff.buffer.start == NULL) return ERROR(memory_allocation); zcs->inBuff.filled = 0; @@ -424,6 +443,11 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { return 0; } +size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { + ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, 0); + return ZSTDMT_initCStream_advanced(zcs, NULL, 0, params, 0); +} + size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { @@ -455,8 +479,10 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = zcs->targetSectionSize; - zcs->jobs[jobID].fullFrameSize = 0; zcs->jobs[jobID].params = zcs->params; + zcs->jobs[jobID].dict = zcs->nextJobID == 0 ? zcs->dict : NULL; + zcs->jobs[jobID].dictSize = zcs->nextJobID == 0 ? zcs->dictSize : 0; + zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize; zcs->jobs[jobID].dstBuff = dstBuffer; zcs->jobs[jobID].cctx = cctx; zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0); @@ -539,8 +565,10 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; - zcs->jobs[jobID].fullFrameSize = 0; zcs->jobs[jobID].params = zcs->params; + zcs->jobs[jobID].dict = zcs->nextJobID == 0 ? zcs->dict : NULL; + zcs->jobs[jobID].dictSize = zcs->nextJobID == 0 ? zcs->dictSize : 0; + zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize; zcs->jobs[jobID].dstBuff = dstBuffer; zcs->jobs[jobID].cctx = cctx; zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0); diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index ca5d6b601..d1b01a79e 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -1,6 +1,7 @@ /* === Dependencies === */ #include /* size_t */ +#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters */ #include "zstd.h" /* ZSTD_inBuffer, ZSTD_outBuffer */ @@ -19,6 +20,10 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, /* === Streaming functions === */ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel); +size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, + ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown ; current limitation : no checksum */ + size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); -size_t ZSTDMT_flushStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); -size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); + +size_t ZSTDMT_flushStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ +size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ diff --git a/tests/Makefile b/tests/Makefile index 6bc1ad2e2..bbc8d3de1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -26,8 +26,8 @@ TESTARTEFACT := versionsTest namespaceTest CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) CFLAGS ?= -O3 -CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ - -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef +CFLAGS += -g -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ + -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 6cf6c4a09..1feec450b 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -759,7 +759,7 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp if (maxTestSize >= srcBufferSize) maxTestSize = srcBufferSize-1; { int const compressionLevel = (FUZ_rand(&lseed) % 5) + 1; size_t const resetError = ZSTDMT_initCStream(zc, compressionLevel); - CHECK(ZSTD_isError(resetError), "ZSTD_resetCStream error : %s", ZSTD_getErrorName(resetError)); + CHECK(ZSTD_isError(resetError), "ZSTDMT_initCStream error : %s", ZSTD_getErrorName(resetError)); } } else { U32 const testLog = FUZ_rand(&lseed) % maxSrcLog; @@ -767,11 +767,18 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp maxTestSize = FUZ_rLogLength(&lseed, testLog); oldTestLog = testLog; /* random dictionary selection */ - dictSize = 0; - dict = NULL; - { size_t const initError = ZSTDMT_initCStream(zc, cLevel); - CHECK (ZSTD_isError(initError),"ZSTD_initCStream_advanced error : %s", ZSTD_getErrorName(initError)); - } } + dictSize = ((FUZ_rand(&lseed)&63)==1) ? FUZ_randomLength(&lseed, maxSampleLog) : 0; + { size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize); + dict = srcBuffer + dictStart; + } + { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? 0 : maxTestSize; + ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize); + DISPLAYLEVEL(5, "Init with windowLog = %u \n", params.cParams.windowLog); + params.fParams.checksumFlag = FUZ_rand(&lseed) & 1; + params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1; + { size_t const initError = ZSTDMT_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize); + CHECK (ZSTD_isError(initError),"ZSTDMT_initCStream_advanced error : %s", ZSTD_getErrorName(initError)); + } } } /* multi-segments compression test */ XXH64_reset(&xxhState, 0); @@ -790,6 +797,7 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp DISPLAYLEVEL(5, "Sending %u bytes to compress \n", (U32)srcSize); { size_t const compressionError = ZSTDMT_compressStream(zc, &outBuff, &inBuff); CHECK (ZSTD_isError(compressionError), "compression error : %s", ZSTD_getErrorName(compressionError)); } + DISPLAYLEVEL(5, "%u bytes read by ZSTDMT_compressStream \n", (U32)inBuff.pos); XXH64_update(&xxhState, srcBuffer+srcStart, inBuff.pos); memcpy(copyBuffer+totalTestSize, srcBuffer+srcStart, inBuff.pos); @@ -924,8 +932,9 @@ int main(int argc, const char** argv) int testNb = 0; int proba = FUZ_COMPRESSIBILITY_DEFAULT; int result=0; - U32 mainPause = 0; - const char* programName = argv[0]; + int mainPause = 0; + int mtOnly = 0; + const char* const programName = argv[0]; ZSTD_customMem customMem = { allocFunction, freeFunction, NULL }; ZSTD_customMem customNULL = { NULL, NULL, NULL }; @@ -936,8 +945,10 @@ int main(int argc, const char** argv) /* Parsing commands. Aggregated commands are allowed */ if (argument[0]=='-') { - argument++; + if (!strcmp(argument, "--mt")) { mtOnly=1; continue; } + + argument++; while (*argument!=0) { switch(*argument) { @@ -1041,7 +1052,7 @@ int main(int argc, const char** argv) result = basicUnitTests(0, ((double)proba) / 100, customMem); /* use custom memory allocation functions */ } } - if (!result) result = fuzzerTests(seed, nbTests, testNb, ((double)proba) / 100); + if (!result && !mtOnly) result = fuzzerTests(seed, nbTests, testNb, ((double)proba) / 100); if (!result) result = fuzzerTests_MT(seed, nbTests, testNb, ((double)proba) / 100); if (mainPause) { From 500014af49609327001c0e7eedac432cc1f0426b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Jan 2017 16:59:56 -0800 Subject: [PATCH 138/227] zstd cli can now compress using multi-threading added : command -T# added : ZSTD_resetCStream() (zstdmt_compress) added : FIO_setNbThreads() (fileio) --- lib/compress/zstdmt_compress.c | 7 +++ lib/compress/zstdmt_compress.h | 1 + programs/Makefile | 2 +- programs/bench.c | 30 +++++++++---- programs/bench.h | 6 +-- programs/fileio.c | 80 +++++++++++++++++++++++++--------- programs/fileio.h | 1 + programs/zstdcli.c | 44 +++++++++++-------- 8 files changed, 119 insertions(+), 52 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index dd1fd3452..de5032271 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -443,6 +443,13 @@ size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t di return 0; } +/* ZSTDMT_resetCStream() : + * pledgedSrcSize is optional and can be zero == unknown */ +size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize) +{ + return ZSTDMT_initCStream_advanced(zcs, zcs->dict, zcs->dictSize, zcs->params, pledgedSrcSize); +} + size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, 0); return ZSTDMT_initCStream_advanced(zcs, NULL, 0, params, 0); diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index d1b01a79e..759906db1 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -20,6 +20,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, /* === Streaming functions === */ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel); +size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown ; current limitation : no checksum */ diff --git a/programs/Makefile b/programs/Makefile index 02c924f39..f2a0ff26e 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -22,7 +22,7 @@ else ALIGN_LOOP = endif -CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/dictBuilder +CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress -I$(ZSTDDIR)/dictBuilder CFLAGS ?= -O3 CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef \ diff --git a/programs/bench.c b/programs/bench.c index 5299b4712..74d26ca06 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -9,6 +9,14 @@ +/* ************************************** +* Tuning parameters +****************************************/ +#ifndef BMK_TIMETEST_DEFAULT_S /* default minimum time per test */ +#define BMK_TIMETEST_DEFAULT_S 3 +#endif + + /* ************************************** * Compiler Warnings ****************************************/ @@ -43,7 +51,6 @@ # define ZSTD_GIT_COMMIT_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_GIT_COMMIT) #endif -#define NBSECONDS 3 #define TIMELOOP_MICROSEC 1*1000000ULL /* 1 second */ #define ACTIVEPERIOD_MICROSEC 70*1000000ULL /* 70 seconds */ #define COOLPERIOD_SEC 10 @@ -109,31 +116,36 @@ static clock_us_t BMK_clockMicroSec(void) /* ************************************* * Benchmark Parameters ***************************************/ -static U32 g_nbSeconds = NBSECONDS; -static size_t g_blockSize = 0; static int g_additionalParam = 0; static U32 g_decodeOnly = 0; -static U32 g_nbThreads = 1; void BMK_setNotificationLevel(unsigned level) { g_displayLevel=level; } void BMK_setAdditionalParam(int additionalParam) { g_additionalParam=additionalParam; } -void BMK_SetNbSeconds(unsigned nbSeconds) +static U32 g_nbSeconds = BMK_TIMETEST_DEFAULT_S; +void BMK_setNbSeconds(unsigned nbSeconds) { g_nbSeconds = nbSeconds; - DISPLAYLEVEL(3, "- test >= %u seconds per compression / decompression -\n", g_nbSeconds); + DISPLAYLEVEL(3, "- test >= %u seconds per compression / decompression - \n", g_nbSeconds); } -void BMK_SetBlockSize(size_t blockSize) +static size_t g_blockSize = 0; +void BMK_setBlockSize(size_t blockSize) { g_blockSize = blockSize; - DISPLAYLEVEL(2, "using blocks of size %u KB \n", (U32)(blockSize>>10)); + if (g_blockSize) DISPLAYLEVEL(2, "using blocks of size %u KB \n", (U32)(blockSize>>10)); } void BMK_setDecodeOnlyMode(unsigned decodeFlag) { g_decodeOnly = (decodeFlag>0); } -void BMK_SetNbThreads(unsigned nbThreads) { g_nbThreads = nbThreads; } +static U32 g_nbThreads = 1; +void BMK_setNbThreads(unsigned nbThreads) { +#ifndef ZSTD_MULTITHREAD + if (nbThreads > 1) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n"); +#endif + g_nbThreads = nbThreads; +} /* ******************************************************** diff --git a/programs/bench.h b/programs/bench.h index 87850bcc3..2918c02bf 100644 --- a/programs/bench.h +++ b/programs/bench.h @@ -19,9 +19,9 @@ int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles,const char* dic int cLevel, int cLevelLast, ZSTD_compressionParameters* compressionParams); /* Set Parameters */ -void BMK_SetNbSeconds(unsigned nbLoops); -void BMK_SetBlockSize(size_t blockSize); -void BMK_SetNbThreads(unsigned nbThreads); +void BMK_setNbSeconds(unsigned nbLoops); +void BMK_setBlockSize(size_t blockSize); +void BMK_setNbThreads(unsigned nbThreads); void BMK_setNotificationLevel(unsigned level); void BMK_setAdditionalParam(int additionalParam); void BMK_setDecodeOnlyMode(unsigned decodeFlag); diff --git a/programs/fileio.c b/programs/fileio.c index a112cc049..3864a5fab 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -34,11 +34,14 @@ #include "fileio.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */ #include "zstd.h" -#ifdef ZSTD_GZDECOMPRESS -#include "zlib.h" -#if !defined(z_const) - #define z_const +#ifdef ZSTD_MULTITHREAD +# include "zstdmt_compress.h" #endif +#ifdef ZSTD_GZDECOMPRESS +# include "zlib.h" +# if !defined(z_const) +# define z_const +# endif #endif @@ -103,7 +106,13 @@ static U32 g_removeSrcFile = 0; void FIO_setRemoveSrcFile(unsigned flag) { g_removeSrcFile = (flag>0); } static U32 g_memLimit = 0; void FIO_setMemLimit(unsigned memLimit) { g_memLimit = memLimit; } - +static U32 g_nbThreads = 1; +void FIO_setNbThreads(unsigned nbThreads) { +#ifndef ZSTD_MULTITHREAD + if (nbThreads > 1) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n"); +#endif + g_nbThreads = nbThreads; +} /*-************************************* @@ -226,22 +235,30 @@ static size_t FIO_loadFile(void** bufferPtr, const char* fileName) * Compression ************************************************************************/ typedef struct { + FILE* srcFile; + FILE* dstFile; void* srcBuffer; size_t srcBufferSize; void* dstBuffer; size_t dstBufferSize; +#ifdef ZSTD_MULTITHREAD + ZSTDMT_CCtx* cctx; +#else ZSTD_CStream* cctx; - FILE* dstFile; - FILE* srcFile; +#endif } cRess_t; -static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, +static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, U64 srcSize, ZSTD_compressionParameters* comprParams) { cRess_t ress; memset(&ress, 0, sizeof(ress)); +#ifdef ZSTD_MULTITHREAD + ress.cctx = ZSTDMT_createCCtx(g_nbThreads); +#else ress.cctx = ZSTD_createCStream(); +#endif if (ress.cctx == NULL) EXM_THROW(30, "zstd: allocation error : can't create ZSTD_CStream"); ress.srcBufferSize = ZSTD_CStreamInSize(); ress.srcBuffer = malloc(ress.srcBufferSize); @@ -264,7 +281,11 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, if (comprParams->searchLength) params.cParams.searchLength = comprParams->searchLength; if (comprParams->targetLength) params.cParams.targetLength = comprParams->targetLength; if (comprParams->strategy) params.cParams.strategy = (ZSTD_strategy)(comprParams->strategy - 1); +#ifdef ZSTD_MULTITHREAD + { size_t const errorCode = ZSTDMT_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, srcSize); +#else { size_t const errorCode = ZSTD_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, srcSize); +#endif if (ZSTD_isError(errorCode)) EXM_THROW(33, "Error initializing CStream : %s", ZSTD_getErrorName(errorCode)); } } free(dictBuffer); @@ -277,7 +298,11 @@ static void FIO_freeCResources(cRess_t ress) { free(ress.srcBuffer); free(ress.dstBuffer); +#ifdef ZSTD_MULTITHREAD + ZSTDMT_freeCCtx(ress.cctx); +#else ZSTD_freeCStream(ress.cctx); /* never fails */ +#endif } @@ -296,7 +321,11 @@ static int FIO_compressFilename_internal(cRess_t ress, U64 const fileSize = UTIL_getFileSize(srcFileName); /* init */ +#ifdef ZSTD_MULTITHREAD + { size_t const resetError = ZSTDMT_resetCStream(ress.cctx, fileSize); +#else { size_t const resetError = ZSTD_resetCStream(ress.cctx, fileSize); +#endif if (ZSTD_isError(resetError)) EXM_THROW(21, "Error initializing compression : %s", ZSTD_getErrorName(resetError)); } @@ -311,11 +340,14 @@ static int FIO_compressFilename_internal(cRess_t ress, /* Compress using buffered streaming */ { ZSTD_inBuffer inBuff = { ress.srcBuffer, inSize, 0 }; ZSTD_outBuffer outBuff= { ress.dstBuffer, ress.dstBufferSize, 0 }; - { size_t const result = ZSTD_compressStream(ress.cctx, &outBuff, &inBuff); - if (ZSTD_isError(result)) EXM_THROW(23, "Compression error : %s ", ZSTD_getErrorName(result)); } - if (inBuff.pos != inBuff.size) - /* inBuff should be entirely consumed since buffer sizes are recommended ones */ - EXM_THROW(24, "Compression error : input block not fully consumed"); + while (inBuff.pos != inBuff.size) { /* note : is there any possibility of endless loop ? for example, if outBuff is not large enough ? */ +#ifdef ZSTD_MULTITHREAD + size_t const result = ZSTDMT_compressStream(ress.cctx, &outBuff, &inBuff); +#else + size_t const result = ZSTD_compressStream(ress.cctx, &outBuff, &inBuff); +#endif + if (ZSTD_isError(result)) EXM_THROW(23, "Compression error : %s ", ZSTD_getErrorName(result)); + } /* Write cBlock */ { size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile); @@ -326,13 +358,19 @@ static int FIO_compressFilename_internal(cRess_t ress, } /* End of Frame */ - { ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 }; - size_t const result = ZSTD_endStream(ress.cctx, &outBuff); - if (result!=0) EXM_THROW(26, "Compression error : cannot create frame end"); - - { size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile); - if (sizeCheck!=outBuff.pos) EXM_THROW(27, "Write error : cannot write frame end into %s", dstFileName); } - compressedfilesize += outBuff.pos; + { size_t result = 1; + while (result!=0) { /* note : is there any possibility of endless loop ? */ + ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 }; +#ifdef ZSTD_MULTITHREAD + result = ZSTDMT_endStream(ress.cctx, &outBuff); +#else + result = ZSTD_endStream(ress.cctx, &outBuff); +#endif + if (ZSTD_isError(result)) EXM_THROW(26, "Compression error during frame end : %s", ZSTD_getErrorName(result)); + { size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile); + if (sizeCheck!=outBuff.pos) EXM_THROW(27, "Write error : cannot write frame end into %s", dstFileName); } + compressedfilesize += outBuff.pos; + } } /* Status */ @@ -632,7 +670,7 @@ unsigned long long FIO_decompressFrame(dRess_t* ress, if (ZSTD_isError(readSizeHint)) EXM_THROW(36, "Decoding error : %s", ZSTD_getErrorName(readSizeHint)); /* Write block */ - storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, outBuff.pos, storedSkips); + storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, outBuff.pos, storedSkips); frameSize += outBuff.pos; DISPLAYUPDATE(2, "\rDecoded : %u MB... ", (U32)((alreadyDecoded+frameSize)>>20) ); diff --git a/programs/fileio.h b/programs/fileio.h index b71658334..9ef449294 100644 --- a/programs/fileio.h +++ b/programs/fileio.h @@ -40,6 +40,7 @@ void FIO_setDictIDFlag(unsigned dictIDFlag); void FIO_setChecksumFlag(unsigned checksumFlag); void FIO_setRemoveSrcFile(unsigned flag); void FIO_setMemLimit(unsigned memLimit); +void FIO_setNbThreads(unsigned nbThreads); /*-************************************* diff --git a/programs/zstdcli.c b/programs/zstdcli.c index c9d8100eb..de25d0f0a 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -110,12 +110,15 @@ static int usage_advanced(const char* programName) DISPLAY( " -q : suppress warnings; specify twice to suppress errors too\n"); DISPLAY( " -c : force write to standard output, even if it is the console\n"); #ifdef UTIL_HAS_CREATEFILELIST - DISPLAY( " -r : operate recursively on directories\n"); + DISPLAY( " -r : operate recursively on directories \n"); #endif #ifndef ZSTD_NOCOMPRESS DISPLAY( "--ultra : enable levels beyond %i, up to %i (requires more memory)\n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel()); DISPLAY( "--no-dictID : don't write dictID into header (dictionary compression)\n"); - DISPLAY( "--[no-]check : integrity check (default:enabled)\n"); + DISPLAY( "--[no-]check : integrity check (default:enabled) \n"); +#ifdef ZSTD_MULTITHREAD + DISPLAY( " -T# : use # threads for compression (default:1) \n"); +#endif #endif #ifndef ZSTD_NODECOMPRESS DISPLAY( "--test : test compressed file integrity \n"); @@ -233,7 +236,10 @@ int main(int argCount, const char* argv[]) nextArgumentIsDictID=0, nextArgumentsAreFiles=0, ultra=0, - lastCommand = 0; + lastCommand = 0, + nbThreads = 1; + unsigned bench_nbSeconds = 3; /* would be better if this value was synchronized from bench */ + size_t blockSize = 0; zstd_operation_mode operation = zom_compress; ZSTD_compressionParameters compressionParams; int cLevel = ZSTDCLI_CLEVEL_DEFAULT; @@ -396,39 +402,37 @@ int main(int argCount, const char* argv[]) #ifndef ZSTD_NOBENCH /* Benchmark */ - case 'b': operation=zom_bench; argument++; break; + case 'b': + operation=zom_bench; + argument++; + break; /* range bench (benchmark only) */ case 'e': - /* compression Level */ - argument++; - cLevelLast = readU32FromChar(&argument); - break; + /* compression Level */ + argument++; + cLevelLast = readU32FromChar(&argument); + break; /* Modify Nb Iterations (benchmark only) */ case 'i': argument++; - { U32 const iters = readU32FromChar(&argument); - BMK_setNotificationLevel(displayLevel); - BMK_SetNbSeconds(iters); - } + bench_nbSeconds = readU32FromChar(&argument); break; /* cut input into blocks (benchmark only) */ case 'B': argument++; - { size_t const bSize = readU32FromChar(&argument); - BMK_setNotificationLevel(displayLevel); - BMK_SetBlockSize(bSize); - } + blockSize = readU32FromChar(&argument); break; +#endif /* ZSTD_NOBENCH */ + /* nb of threads (hidden option) */ case 'T': argument++; - BMK_SetNbThreads(readU32FromChar(&argument)); + nbThreads = readU32FromChar(&argument); break; -#endif /* ZSTD_NOBENCH */ /* Dictionary Selection level */ case 's': @@ -518,6 +522,9 @@ int main(int argCount, const char* argv[]) if (operation==zom_bench) { #ifndef ZSTD_NOBENCH BMK_setNotificationLevel(displayLevel); + BMK_setBlockSize(blockSize); + BMK_setNbThreads(nbThreads); + BMK_setNbSeconds(bench_nbSeconds); BMK_benchFiles(filenameTable, filenameIdx, dictFileName, cLevel, cLevelLast, &compressionParams); #endif goto _end; @@ -569,6 +576,7 @@ int main(int argCount, const char* argv[]) FIO_setNotificationLevel(displayLevel); if (operation==zom_compress) { #ifndef ZSTD_NOCOMPRESS + FIO_setNbThreads(nbThreads); if ((filenameIdx==1) && outFileName) operationResult = FIO_compressFilename(outFileName, filenameTable[0], dictFileName, cLevel, &compressionParams); else From b459aad5b461c163c53accca48d7c6aafdcff021 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Jan 2017 17:33:37 -0800 Subject: [PATCH 139/227] renamed savedRep into repToConfirm --- lib/compress/zstd_compress.c | 22 +++++++++++----------- lib/compress/zstd_opt.h | 8 ++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 84a4a0216..d984af1fc 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -63,7 +63,7 @@ struct ZSTD_CCtx_s { U32 loadedDictEnd; ZSTD_compressionStage_e stage; U32 rep[ZSTD_REP_NUM]; - U32 savedRep[ZSTD_REP_NUM]; + U32 repToConfirm[ZSTD_REP_NUM]; U32 dictID; ZSTD_parameters params; void* workSpace; @@ -742,7 +742,7 @@ _check_compressibility: if ((size_t)(op-ostart) >= maxCSize) return 0; } /* confirm repcodes */ - { int i; for (i=0; irep[i] = zc->savedRep[i]; } + { int i; for (i=0; irep[i] = zc->repToConfirm[i]; } return op - ostart; } @@ -1011,8 +1011,8 @@ void ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, } } } /* save reps for next block */ - cctx->savedRep[0] = offset_1 ? offset_1 : offsetSaved; - cctx->savedRep[1] = offset_2 ? offset_2 : offsetSaved; + cctx->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved; + cctx->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved; /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -1126,7 +1126,7 @@ static void ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx, } } } /* save reps for next block */ - ctx->savedRep[0] = offset_1; ctx->savedRep[1] = offset_2; + ctx->repToConfirm[0] = offset_1; ctx->repToConfirm[1] = offset_2; /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -1280,8 +1280,8 @@ void ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx, } } } /* save reps for next block */ - cctx->savedRep[0] = offset_1 ? offset_1 : offsetSaved; - cctx->savedRep[1] = offset_2 ? offset_2 : offsetSaved; + cctx->repToConfirm[0] = offset_1 ? offset_1 : offsetSaved; + cctx->repToConfirm[1] = offset_2 ? offset_2 : offsetSaved; /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -1430,7 +1430,7 @@ static void ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, } } } /* save reps for next block */ - ctx->savedRep[0] = offset_1; ctx->savedRep[1] = offset_2; + ctx->repToConfirm[0] = offset_1; ctx->repToConfirm[1] = offset_2; /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -1962,8 +1962,8 @@ _storeSequence: } } /* Save reps for next block */ - ctx->savedRep[0] = offset_1 ? offset_1 : savedOffset; - ctx->savedRep[1] = offset_2 ? offset_2 : savedOffset; + ctx->repToConfirm[0] = offset_1 ? offset_1 : savedOffset; + ctx->repToConfirm[1] = offset_2 ? offset_2 : savedOffset; /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -2157,7 +2157,7 @@ _storeSequence: } } /* Save reps for next block */ - ctx->savedRep[0] = offset_1; ctx->savedRep[1] = offset_2; + ctx->repToConfirm[0] = offset_1; ctx->repToConfirm[1] = offset_2; /* Last Literals */ { size_t const lastLLSize = iend - anchor; diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index f071c4f30..8393e7b4c 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -38,7 +38,7 @@ MEM_STATIC void ZSTD_rescaleFreqs(seqStore_t* ssPtr, const BYTE* src, size_t src ssPtr->cachedLiterals = NULL; ssPtr->cachedPrice = ssPtr->cachedLitLength = 0; - ssPtr->staticPrices = 0; + ssPtr->staticPrices = 0; if (ssPtr->litLengthSum == 0) { if (srcSize <= 1024) ssPtr->staticPrices = 1; @@ -56,7 +56,7 @@ MEM_STATIC void ZSTD_rescaleFreqs(seqStore_t* ssPtr, const BYTE* src, size_t src for (u=0; u<=MaxLit; u++) { ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u]>>ZSTD_FREQ_DIV); - ssPtr->litSum += ssPtr->litFreq[u]; + ssPtr->litSum += ssPtr->litFreq[u]; } for (u=0; u<=MaxLL; u++) ssPtr->litLengthFreq[u] = 1; @@ -634,7 +634,7 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ } } /* for (cur=0; cur < last_pos; ) */ /* Save reps for next block */ - { int i; for (i=0; isavedRep[i] = rep[i]; } + { int i; for (i=0; irepToConfirm[i] = rep[i]; } /* Last Literals */ { size_t const lastLLSize = iend - anchor; @@ -907,7 +907,7 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ } } /* for (cur=0; cur < last_pos; ) */ /* Save reps for next block */ - { int i; for (i=0; isavedRep[i] = rep[i]; } + { int i; for (i=0; irepToConfirm[i] = rep[i]; } /* Last Literals */ { size_t lastLLSize = iend - anchor; From 458c8a94b4330b300027217936d3e3f7343a9483 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Jan 2017 17:44:15 -0800 Subject: [PATCH 140/227] minor refactoring : cleaner MT integration within bench --- programs/bench.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 74d26ca06..7dc98653a 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -180,6 +180,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, size_t const maxCompressedSize = ZSTD_compressBound(srcSize) + (maxNbBlocks * 1024); /* add some room for safety */ void* const compressedBuffer = malloc(maxCompressedSize); void* resultBuffer = malloc(srcSize); + ZSTDMT_CCtx* const mtctx = ZSTDMT_createCCtx(g_nbThreads); ZSTD_CCtx* const ctx = ZSTD_createCCtx(); ZSTD_DCtx* const dctx = ZSTD_createDCtx(); size_t const loadedCompressedSize = srcSize; @@ -255,8 +256,6 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, const char* const marks[NB_MARKS] = { " |", " /", " =", "\\" }; U32 markNb = 0; - ZSTDMT_CCtx* const mtcctx = ZSTDMT_createCCtx(g_nbThreads); - DISPLAYLEVEL(2, "\r%79s\r", ""); while (!cCompleted || !dCompleted) { @@ -300,15 +299,17 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, cdict); - } else if (1) { - rSize = ZSTDMT_compressCCtx(mtcctx, + } else { +#ifdef ZSTD_MULTITHREAD /* note : limitation : MT single-pass does not support compression with dictionary */ + rSize = ZSTDMT_compressCCtx(mtctx, blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, cLevel); - } else { +#else rSize = ZSTD_compress_advanced (ctx, blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, NULL, 0, zparams); +#endif } if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_compress_usingCDict() failed : %s", ZSTD_getErrorName(rSize)); blockTable[blockNb].cSize = rSize; @@ -433,6 +434,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, free(blockTable); free(compressedBuffer); free(resultBuffer); + ZSTDMT_freeCCtx(mtctx); ZSTD_freeCCtx(ctx); ZSTD_freeDCtx(dctx); return 0; From 5fba09fa414c355e7c831a5725857779f23cc022 Mon Sep 17 00:00:00 2001 From: cyan4973 Date: Fri, 20 Jan 2017 12:23:30 -0800 Subject: [PATCH 141/227] updated util's time for Windows compatibility Correctly measures time on Posix systems when running with Multi-threading Todo : check Windows measurement under multi-threading --- lib/common/threading.c | 6 +++++ lib/compress/zstdmt_compress.c | 26 ++++++++++++++------ programs/bench.c | 43 ++++++++++++---------------------- programs/util.h | 22 +++++++++++------ 4 files changed, 55 insertions(+), 42 deletions(-) diff --git a/lib/common/threading.c b/lib/common/threading.c index 38bbab0d4..b56e594b2 100644 --- a/lib/common/threading.c +++ b/lib/common/threading.c @@ -15,6 +15,12 @@ * This file will hold wrapper for systems, which do not support pthreads */ +/* ====== Compiler specifics ====== */ +#if defined(_MSC_VER) +# pragma warning(disable : 4206) /* disable: C4206: translation unit is empty (when ZSTD_MULTITHREAD is not defined) */ +#endif + + #if defined(ZSTD_MULTITHREAD) && defined(_WIN32) /** diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index de5032271..3674281a4 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -2,11 +2,17 @@ /* ====== Tuning parameters ====== */ #ifndef ZSTDMT_SECTION_LOGSIZE_MIN -#define ZSTDMT_SECTION_LOGSIZE_MIN 20 /*< minimum size for a full compression job (20==2^20==1 MB) */ +# define ZSTDMT_SECTION_LOGSIZE_MIN 20 /*< minimum size for a full compression job (20==2^20==1 MB) */ #endif -/* ====== Dependencies ====== */ +/* ====== Compiler specifics ====== */ +#if defined(_MSC_VER) +# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */ +#endif + + +/* ====== Dependencies ====== */ #include /* malloc */ #include /* memcpy */ #include /* threadpool */ @@ -14,6 +20,8 @@ #include "zstd_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */ #include "zstdmt_compress.h" + +/* ====== Debug ====== */ #if 0 # include @@ -73,7 +81,7 @@ typedef struct buffer_s { static const buffer_t g_nullBuffer = { NULL, 0 }; typedef struct ZSTDMT_bufferPool_s { - unsigned totalBuffers;; + unsigned totalBuffers; unsigned nbBuffers; buffer_t bTable[1]; /* variable size */ } ZSTDMT_bufferPool; @@ -107,10 +115,13 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) free(buf.start); /* size conditions not respected : scratch this buffer and create a new one */ } /* create new buffer */ - { void* const start = malloc(bSize); + { buffer_t buffer; + void* const start = malloc(bSize); if (start==NULL) bSize = 0; - return (buffer_t) { start, bSize }; /* note : start can be NULL if malloc fails ! */ - } + buffer.start = start; /* note : start can be NULL if malloc fails ! */ + buffer.size = bSize; + return buffer; + } } /* store buffer for later re-use, up to pool capacity */ @@ -336,7 +347,8 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, for (u=0; ubuffPool, dstBufferCapacity) : (buffer_t){ dst, dstCapacity }; + buffer_t const dstAsBuffer = { dst, dstCapacity }; + buffer_t const dstBuffer = u ? ZSTDMT_getBuffer(mtctx->buffPool, dstBufferCapacity) : dstAsBuffer; ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(mtctx->cctxPool); if ((cctx==NULL) || (dstBuffer.start==NULL)) { diff --git a/programs/bench.c b/programs/bench.c index 7dc98653a..2286ead92 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -95,23 +95,6 @@ static clock_t g_time = 0; exit(error); \ } -/* ************************************* -* Time -***************************************/ -/* for posix only - needs proper detection macros to setup */ -#include -#include - -typedef unsigned long long clock_us_t; -static clock_us_t BMK_clockMicroSec(void) -{ - static clock_t _ticksPerSecond = 0; - if (_ticksPerSecond <= 0) _ticksPerSecond = sysconf(_SC_CLK_TCK); - - { struct tms junk; clock_t newTicks = (clock_t) times(&junk); (void)junk; - return ((((clock_us_t)newTicks)*(1000000))/_ticksPerSecond); } -} - /* ************************************* * Benchmark Parameters @@ -248,7 +231,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, /* Bench */ { U64 fastestC = (U64)(-1LL), fastestD = (U64)(-1LL); U64 const crcOrig = g_decodeOnly ? 0 : XXH64(srcBuffer, srcSize, 0); - clock_us_t coolTime = BMK_clockMicroSec(); + UTIL_time_t coolTime, coolTick; U64 const maxTime = (g_nbSeconds * TIMELOOP_MICROSEC) + 1; U64 totalCTime=0, totalDTime=0; U32 cCompleted=g_decodeOnly, dCompleted=0; @@ -256,25 +239,28 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, const char* const marks[NB_MARKS] = { " |", " /", " =", "\\" }; U32 markNb = 0; + UTIL_initTimer(&coolTick); + UTIL_getTime(&coolTime); DISPLAYLEVEL(2, "\r%79s\r", ""); while (!cCompleted || !dCompleted) { /* overheat protection */ - if (BMK_clockMicroSec() - coolTime > ACTIVEPERIOD_MICROSEC) { + if (UTIL_clockSpanMicro(coolTime, coolTick) > ACTIVEPERIOD_MICROSEC) { DISPLAYLEVEL(2, "\rcooling down ... \r"); UTIL_sleep(COOLPERIOD_SEC); - coolTime = BMK_clockMicroSec(); + UTIL_getTime(&coolTime); } if (!g_decodeOnly) { - clock_us_t clockStart; + UTIL_time_t clockTick, clockStart; /* Compression */ DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->\r", marks[markNb], displayName, (U32)srcSize); if (!cCompleted) memset(compressedBuffer, 0xE5, maxCompressedSize); /* warm up and erase result buffer */ UTIL_sleepMilli(1); /* give processor time to other processes */ UTIL_waitForNextTick(ticksPerSecond); - clockStart = BMK_clockMicroSec(); + UTIL_initTimer(&clockTick); + UTIL_getTime(&clockStart); if (!cCompleted) { /* still some time to do compression tests */ ZSTD_parameters zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize); @@ -315,9 +301,9 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, blockTable[blockNb].cSize = rSize; } nbLoops++; - } while (BMK_clockMicroSec() - clockStart < clockLoop); + } while (UTIL_clockSpanMicro(clockStart, clockTick) < clockLoop); ZSTD_freeCDict(cdict); - { clock_us_t const clockSpanMicro = BMK_clockMicroSec() - clockStart; + { U64 const clockSpanMicro = UTIL_clockSpanMicro(clockStart, clockTick); if (clockSpanMicro < fastestC*nbLoops) fastestC = clockSpanMicro / nbLoops; totalCTime += clockSpanMicro; cCompleted = (totalCTime >= maxTime); @@ -347,10 +333,11 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, if (!dCompleted) { U64 clockLoop = g_nbSeconds ? TIMELOOP_MICROSEC : 1; U32 nbLoops = 0; - clock_us_t clockStart; + UTIL_time_t clockStart, clockTick; ZSTD_DDict* const ddict = ZSTD_createDDict(dictBuffer, dictBufferSize); if (!ddict) EXM_THROW(2, "ZSTD_createDDict() allocation failure"); - clockStart = BMK_clockMicroSec(); + UTIL_initTimer(&clockTick); + UTIL_getTime(&clockStart); do { U32 blockNb; for (blockNb=0; blockNb= maxTime); diff --git a/programs/util.h b/programs/util.h index aaa4b7c1e..651027bae 100644 --- a/programs/util.h +++ b/programs/util.h @@ -95,18 +95,26 @@ extern "C" { /*-**************************************** * Time functions ******************************************/ -#if !defined(_WIN32) - typedef clock_t UTIL_time_t; - UTIL_STATIC void UTIL_initTimer(UTIL_time_t* ticksPerSecond) { *ticksPerSecond=0; } - UTIL_STATIC void UTIL_getTime(UTIL_time_t* x) { *x = clock(); } - UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { (void)ticksPerSecond; return 1000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; } - UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { (void)ticksPerSecond; return 1000000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; } -#else +#if (PLATFORM_POSIX_VERSION >= 1) +#include +#include /* times */ + typedef U64 UTIL_time_t; + UTIL_STATIC void UTIL_initTimer(UTIL_time_t* ticksPerSecond) { *ticksPerSecond=sysconf(_SC_CLK_TCK); } + UTIL_STATIC void UTIL_getTime(UTIL_time_t* x) { struct tms junk; clock_t newTicks = (clock_t) times(&junk); (void)junk; *x = (UTIL_time_t)newTicks; } + UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000ULL * (clockEnd - clockStart) / ticksPerSecond; } + UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000000ULL * (clockEnd - clockStart) / ticksPerSecond; } +#elif defined(_WIN32) /* Windows */ typedef LARGE_INTEGER UTIL_time_t; UTIL_STATIC void UTIL_initTimer(UTIL_time_t* ticksPerSecond) { if (!QueryPerformanceFrequency(ticksPerSecond)) fprintf(stderr, "ERROR: QueryPerformance not present\n"); } UTIL_STATIC void UTIL_getTime(UTIL_time_t* x) { QueryPerformanceCounter(x); } UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000ULL*(clockEnd.QuadPart - clockStart.QuadPart)/ticksPerSecond.QuadPart; } UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000000ULL*(clockEnd.QuadPart - clockStart.QuadPart)/ticksPerSecond.QuadPart; } +#else /* relies on standard C (note : clock_t measurements can be wrong when using multi-threading) */ + typedef clock_t UTIL_time_t; + UTIL_STATIC void UTIL_initTimer(UTIL_time_t* ticksPerSecond) { *ticksPerSecond=0; } + UTIL_STATIC void UTIL_getTime(UTIL_time_t* x) { *x = clock(); } + UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { (void)ticksPerSecond; return 1000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; } + UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { (void)ticksPerSecond; return 1000000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; } #endif From 2e3b659ae1c051531c3e90a4e9d795b5701f1826 Mon Sep 17 00:00:00 2001 From: cyan4973 Date: Fri, 20 Jan 2017 14:00:41 -0800 Subject: [PATCH 142/227] fixed minor warnings (Visual, conversion, doxygen) --- lib/common/pool.c | 12 ++++++++++-- lib/compress/zstdmt_compress.c | 12 ++++++++++-- lib/compress/zstdmt_compress.h | 8 ++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index f40ed39d4..693217f24 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -7,13 +7,21 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include "pool.h" + +/* ====== Dependencies ======= */ #include /* size_t */ #include /* malloc, calloc, free */ +#include "pool.h" + +/* ====== Compiler specifics ====== */ +#if defined(_MSC_VER) +# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */ +#endif + #ifdef ZSTD_MULTITHREAD -#include +#include /* pthread adaptation */ /* A job is a function and an opaque argument */ typedef struct POOL_job_s { diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 3674281a4..48717de20 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1,8 +1,16 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ /* ====== Tuning parameters ====== */ #ifndef ZSTDMT_SECTION_LOGSIZE_MIN -# define ZSTDMT_SECTION_LOGSIZE_MIN 20 /*< minimum size for a full compression job (20==2^20==1 MB) */ +# define ZSTDMT_SECTION_LOGSIZE_MIN 20 /* minimum size for a full compression job (20==2^20==1 MB) */ #endif @@ -444,7 +452,7 @@ size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t di zcs->dictSize = dictSize; zcs->frameContentSize = pledgedSrcSize; zcs->targetSectionSize = (size_t)1 << MAX(ZSTDMT_SECTION_LOGSIZE_MIN, (zcs->params.cParams.windowLog + 2)); - zcs->inBuffSize = zcs->targetSectionSize + (1 << zcs->params.cParams.windowLog); + zcs->inBuffSize = zcs->targetSectionSize + ((size_t)1 << zcs->params.cParams.windowLog); zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); if (zcs->inBuff.buffer.start == NULL) return ERROR(memory_allocation); zcs->inBuff.filled = 0; diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 759906db1..7d336db06 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -1,3 +1,11 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ /* === Dependencies === */ #include /* size_t */ From 326575c3a3023e15964947583ecd655bd8a775f4 Mon Sep 17 00:00:00 2001 From: cyan4973 Date: Fri, 20 Jan 2017 14:49:44 -0800 Subject: [PATCH 143/227] fixed VS2010 project --- build/VS2010/zstd/zstd.vcxproj | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/build/VS2010/zstd/zstd.vcxproj b/build/VS2010/zstd/zstd.vcxproj index 5b5852689..9886af0bb 100644 --- a/build/VS2010/zstd/zstd.vcxproj +++ b/build/VS2010/zstd/zstd.vcxproj @@ -21,11 +21,14 @@ + + + @@ -45,7 +48,10 @@ + + + @@ -66,6 +72,7 @@ + @@ -137,7 +144,7 @@ false - $(IncludePath);$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\dictBuilder;$(UniversalCRT_IncludePath); + $(IncludePath);$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\compress;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\dictBuilder;$(UniversalCRT_IncludePath); false $(LibraryPath); @@ -206,6 +213,7 @@ false false MultiThreaded + /DZSTD_MULTITHREAD %(AdditionalOptions) Console @@ -218,4 +226,4 @@ - + \ No newline at end of file From f0ffa237da8180d7aff82f41d43da12d881e62ea Mon Sep 17 00:00:00 2001 From: cyan4973 Date: Fri, 20 Jan 2017 15:24:06 -0800 Subject: [PATCH 144/227] fixed VS2008 project --- build/VS2008/zstd/zstd.vcproj | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/build/VS2008/zstd/zstd.vcproj b/build/VS2008/zstd/zstd.vcproj index ad64f8696..f5b3f5587 100644 --- a/build/VS2008/zstd/zstd.vcproj +++ b/build/VS2008/zstd/zstd.vcproj @@ -376,6 +376,14 @@ RelativePath="..\..\..\lib\decompress\huf_decompress.c" > + + + + @@ -428,6 +436,10 @@ RelativePath="..\..\..\programs\zstdcli.c" > + + - - @@ -470,6 +478,14 @@ RelativePath="..\..\..\lib\common\mem.h" > + + + + @@ -486,6 +502,10 @@ RelativePath="..\..\..\lib\zstd.h" > + + @@ -530,6 +550,10 @@ RelativePath="..\..\..\lib\legacy\zstd_v07.h" > + + From 900f39e7091f39f5d8ca50d7f9fdc25e68f4e0e9 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 20 Jan 2017 16:36:29 -0800 Subject: [PATCH 145/227] skip zstdmt at root directory --- .gitignore | 1 + Makefile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 796a696d3..dd7a74519 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ # Executables zstd +zstdmt *.exe *.out *.app diff --git a/Makefile b/Makefile index 0a3634c39..8569ee660 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ clean: @$(MAKE) -C $(TESTDIR) $@ > $(VOID) @$(MAKE) -C $(ZWRAPDIR) $@ > $(VOID) @$(MAKE) -C examples/ $@ > $(VOID) - @$(RM) zstd$(EXT) tmp* + @$(RM) zstd$(EXT) zstdmt$(EXT) tmp* @echo Cleaning completed From 317604e0addb03ea3aa202324d2625f2754497bd Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 20 Jan 2017 17:18:41 -0800 Subject: [PATCH 146/227] fixed : compilation of zstreamtest in dll mode --- lib/compress/zstdmt_compress.h | 20 ++++++++++---------- tests/.gitignore | 1 + 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 7d336db06..7e9d07035 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -10,16 +10,16 @@ /* === Dependencies === */ #include /* size_t */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters */ -#include "zstd.h" /* ZSTD_inBuffer, ZSTD_outBuffer */ +#include "zstd.h" /* ZSTD_inBuffer, ZSTD_outBuffer, ZSTDLIB_API */ /* === Simple one-pass functions === */ typedef struct ZSTDMT_CCtx_s ZSTDMT_CCtx; -ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbThreads); -size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* cctx); +ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbThreads); +ZSTDLIB_API size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* cctx); -size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, +ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel); @@ -27,12 +27,12 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, /* === Streaming functions === */ -size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel); -size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ -size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, +ZSTDLIB_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel); +ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ +ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown ; current limitation : no checksum */ -size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); +ZSTDLIB_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); -size_t ZSTDMT_flushStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ -size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ +ZSTDLIB_API size_t ZSTDMT_flushStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ +ZSTDLIB_API size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ diff --git a/tests/.gitignore b/tests/.gitignore index 5041404dd..53520238f 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -6,6 +6,7 @@ fuzzer32 fuzzer-dll zbufftest zbufftest32 +zbufftest-dll zstreamtest zstreamtest32 zstreamtest-dll From f8804d1014da44e02eaa9e83883d0eb12d6308f4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 20 Jan 2017 17:23:19 -0800 Subject: [PATCH 147/227] convert tabs to space joys of using multiple editors from multiple environments ... --- programs/bench.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 2286ead92..1ca40d6b9 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -239,28 +239,28 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, const char* const marks[NB_MARKS] = { " |", " /", " =", "\\" }; U32 markNb = 0; - UTIL_initTimer(&coolTick); - UTIL_getTime(&coolTime); + UTIL_initTimer(&coolTick); + UTIL_getTime(&coolTime); DISPLAYLEVEL(2, "\r%79s\r", ""); while (!cCompleted || !dCompleted) { /* overheat protection */ - if (UTIL_clockSpanMicro(coolTime, coolTick) > ACTIVEPERIOD_MICROSEC) { + if (UTIL_clockSpanMicro(coolTime, coolTick) > ACTIVEPERIOD_MICROSEC) { DISPLAYLEVEL(2, "\rcooling down ... \r"); UTIL_sleep(COOLPERIOD_SEC); UTIL_getTime(&coolTime); } if (!g_decodeOnly) { - UTIL_time_t clockTick, clockStart; + UTIL_time_t clockTick, clockStart; /* Compression */ DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->\r", marks[markNb], displayName, (U32)srcSize); if (!cCompleted) memset(compressedBuffer, 0xE5, maxCompressedSize); /* warm up and erase result buffer */ UTIL_sleepMilli(1); /* give processor time to other processes */ UTIL_waitForNextTick(ticksPerSecond); - UTIL_initTimer(&clockTick); - UTIL_getTime(&clockStart); + UTIL_initTimer(&clockTick); + UTIL_getTime(&clockStart); if (!cCompleted) { /* still some time to do compression tests */ ZSTD_parameters zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize); @@ -301,9 +301,9 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, blockTable[blockNb].cSize = rSize; } nbLoops++; - } while (UTIL_clockSpanMicro(clockStart, clockTick) < clockLoop); + } while (UTIL_clockSpanMicro(clockStart, clockTick) < clockLoop); ZSTD_freeCDict(cdict); - { U64 const clockSpanMicro = UTIL_clockSpanMicro(clockStart, clockTick); + { U64 const clockSpanMicro = UTIL_clockSpanMicro(clockStart, clockTick); if (clockSpanMicro < fastestC*nbLoops) fastestC = clockSpanMicro / nbLoops; totalCTime += clockSpanMicro; cCompleted = (totalCTime >= maxTime); @@ -336,8 +336,8 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, UTIL_time_t clockStart, clockTick; ZSTD_DDict* const ddict = ZSTD_createDDict(dictBuffer, dictBufferSize); if (!ddict) EXM_THROW(2, "ZSTD_createDDict() allocation failure"); - UTIL_initTimer(&clockTick); - UTIL_getTime(&clockStart); + UTIL_initTimer(&clockTick); + UTIL_getTime(&clockStart); do { U32 blockNb; for (blockNb=0; blockNb Date: Sat, 21 Jan 2017 21:56:36 -0800 Subject: [PATCH 148/227] minor : tab to spaces --- lib/compress/zstdmt_compress.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 48717de20..1ddb53877 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -124,12 +124,12 @@ static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* pool, size_t bSize) } /* create new buffer */ { buffer_t buffer; - void* const start = malloc(bSize); + void* const start = malloc(bSize); if (start==NULL) bSize = 0; - buffer.start = start; /* note : start can be NULL if malloc fails ! */ - buffer.size = bSize; - return buffer; - } + buffer.start = start; /* note : start can be NULL if malloc fails ! */ + buffer.size = bSize; + return buffer; + } } /* store buffer for later re-use, up to pool capacity */ @@ -355,7 +355,7 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, for (u=0; ubuffPool, dstBufferCapacity) : dstAsBuffer; ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(mtctx->cctxPool); From 0cf74fa95751d2ce7e61f394243d5f5e19329927 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 21 Jan 2017 22:06:49 -0800 Subject: [PATCH 149/227] optimized pool allocation by 1 slot --- lib/compress/zstdmt_compress.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 1ddb53877..8de54d4a6 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -13,6 +13,8 @@ # define ZSTDMT_SECTION_LOGSIZE_MIN 20 /* minimum size for a full compression job (20==2^20==1 MB) */ #endif +#define ZSTDMT_NBTHREADS_MAX 128 + /* ====== Compiler specifics ====== */ #if defined(_MSC_VER) @@ -77,8 +79,6 @@ if (g_debugLevel>=MUTEX_WAIT_TIME_DLEVEL) { \ #endif -#define ZSTDMT_NBTHREADS_MAX 128 - /* ===== Buffer Pool ===== */ typedef struct buffer_s { @@ -97,9 +97,10 @@ typedef struct ZSTDMT_bufferPool_s { static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbThreads) { unsigned const maxNbBuffers = 2*nbThreads + 2; - ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)calloc(1, sizeof(ZSTDMT_bufferPool) + maxNbBuffers * sizeof(buffer_t)); + ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)calloc(1, sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t)); if (bufPool==NULL) return NULL; bufPool->totalBuffers = maxNbBuffers; + bufPool->nbBuffers = 0; return bufPool; } @@ -164,9 +165,11 @@ static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool) free(pool); } +/* ZSTDMT_createCCtxPool() : + * implies nbThreads >= 1 , checked by caller ZSTDMT_createCCtx() */ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads) { - ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) calloc(1, sizeof(ZSTDMT_CCtxPool) + nbThreads*sizeof(ZSTD_CCtx*)); + ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) calloc(1, sizeof(ZSTDMT_CCtxPool) + (nbThreads-1)*sizeof(ZSTD_CCtx*)); if (!cctxPool) return NULL; cctxPool->totalCCtx = nbThreads; cctxPool->availCCtx = 0; From 9d6f7637ecb95372386aa836ca0f57cded446da4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 21 Jan 2017 22:14:08 -0800 Subject: [PATCH 150/227] protected (mutex) read to jobCompleted, as suggested by @terrelln --- lib/compress/zstdmt_compress.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 8de54d4a6..4e09a2082 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -541,8 +541,12 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu /* check if there is any data available to flush */ { unsigned const jobID = zcs->doneJobID & zcs->jobIDMask; - ZSTDMT_jobDescription job = zcs->jobs[jobID]; - if (job.jobCompleted) { /* job completed : output can be flushed */ + unsigned jobCompleted; + pthread_mutex_lock(&zcs->jobCompleted_mutex); + jobCompleted = zcs->jobs[jobID].jobCompleted; + pthread_mutex_unlock(&zcs->jobCompleted_mutex); + if (jobCompleted) { + ZSTDMT_jobDescription const job = zcs->jobs[jobID]; size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); DEBUGLOG(1, "flush %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); @@ -556,15 +560,13 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu } memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); output->pos += toWrite; - job.dstFlushed += toWrite; + zcs->jobs[jobID].dstFlushed += toWrite; DEBUGLOG(1, "remaining : %u bytes ", (U32)(job.cSize - job.dstFlushed)); - if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => go to next one */ + if (zcs->jobs[jobID].dstFlushed == job.cSize) { /* output buffer fully flushed => go to next one */ ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); zcs->jobs[jobID].dstBuff = g_nullBuffer; zcs->jobs[jobID].jobCompleted = 0; zcs->doneJobID++; - } else { - zcs->jobs[jobID].dstFlushed = job.dstFlushed; /* save flush level into zcs for later retrieval */ } } } /* recommended next input size : fill current input buffer */ From bd6bc2261237526ab133e5f1ab7262fa91745f69 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 22 Jan 2017 15:54:14 -0800 Subject: [PATCH 151/227] playtest.sh : changed sdiff into $DIFF --- tests/playTests.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index 5bb882aa4..35731f9cf 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -135,14 +135,14 @@ $ZSTD -c world.tmp > world.zstd cat hello.zstd world.zstd > helloworld.zstd $ZSTD -dc helloworld.zstd > result.tmp cat result.tmp -sdiff helloworld.tmp result.tmp +$DIFF helloworld.tmp result.tmp $ECHO "frame concatenation without checksum" $ZSTD -c hello.tmp > hello.zstd --no-check $ZSTD -c world.tmp > world.zstd --no-check cat hello.zstd world.zstd > helloworld.zstd $ZSTD -dc helloworld.zstd > result.tmp cat result.tmp -sdiff helloworld.tmp result.tmp +$DIFF helloworld.tmp result.tmp rm ./*.tmp ./*.zstd $ECHO "frame concatenation tests completed" From c5933487227fcd9262d84efd2a0a4e5432b0c41c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 22 Jan 2017 16:40:06 -0800 Subject: [PATCH 152/227] ZSTDMT_initCStream_usingDict() can outlive dict Like ZSTD_initCStream_usingDict(), ZSTDMT_initCStream_usingDict() now keep a copy of dict internally. This way, dict can be released : it does not longer have to outlive all future compression sessions. --- lib/compress/zstd_compress.c | 3 +- lib/compress/zstdmt_compress.c | 51 ++++++++++++++++++++++++---------- lib/compress/zstdmt_compress.h | 4 +-- lib/zstd.h | 1 + 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e8a375116..3c69a1ae0 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2603,7 +2603,6 @@ static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* zc, const void* dict, si } } - /*! ZSTD_compressBegin_internal() : * @return : 0, or an error code */ static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, @@ -2825,7 +2824,7 @@ static ZSTD_parameters ZSTD_getParamsFromCDict(const ZSTD_CDict* cdict) { return ZSTD_getParamsFromCCtx(cdict->refContext); } -size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, U64 pledgedSrcSize) +size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, unsigned long long pledgedSrcSize) { if (cdict->dictContentSize) CHECK_F(ZSTD_copyCCtx(cctx, cdict->refContext, pledgedSrcSize)) else CHECK_F(ZSTD_compressBegin_advanced(cctx, NULL, 0, cdict->refContext->params, pledgedSrcSize)); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 4e09a2082..283f76841 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -217,6 +217,7 @@ typedef struct { pthread_mutex_t* jobCompleted_mutex; pthread_cond_t* jobCompleted_cond; ZSTD_parameters params; + ZSTD_CDict* cdict; const void* dict; size_t dictSize; unsigned long long fullFrameSize; @@ -227,8 +228,13 @@ void ZSTDMT_compressChunk(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; buffer_t const dstBuff = job->dstBuff; - size_t const initError = ZSTD_compressBegin_advanced(job->cctx, job->dict, job->dictSize, job->params, job->fullFrameSize); - if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } + if (job->cdict) { + size_t const initError = ZSTD_compressBegin_usingCDict(job->cctx, job->cdict, job->fullFrameSize); + if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } + } else { + size_t const initError = ZSTD_compressBegin_advanced(job->cctx, job->dict, job->dictSize, job->params, job->fullFrameSize); + if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } + } if (!job->firstChunk) { /* flush frame header */ size_t const hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, 0); if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } @@ -271,8 +277,7 @@ struct ZSTDMT_CCtx_s { unsigned frameEnded; unsigned allJobsCompleted; unsigned long long frameContentSize; - const void* dict; - size_t dictSize; + ZSTD_CDict* cdict; ZSTDMT_jobDescription jobs[1]; /* variable size (must lies at the end) */ }; @@ -324,6 +329,7 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx) size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) { if (mtctx==NULL) return 0; /* compatible with free on NULL */ + ZSTD_freeCDict(mtctx->cdict); POOL_free(mtctx->factory); if (!mtctx->allJobsCompleted) ZSTDMT_releaseAllJobResources(mtctx); /* stop workers first */ ZSTDMT_freeBufferPool(mtctx->buffPool); /* release job resources into pools first */ @@ -442,8 +448,12 @@ static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* zcs) { } } -size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, - ZSTD_parameters params, unsigned long long pledgedSrcSize) { + +static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, + const void* dict, size_t dictSize, unsigned updateDict, + ZSTD_parameters params, unsigned long long pledgedSrcSize) +{ + ZSTD_customMem const cmem = { NULL, NULL, NULL }; if (zcs->allJobsCompleted == 0) { /* previous job not correctly finished */ ZSTDMT_waitForAllJobsCompleted(zcs); ZSTDMT_releaseAllJobResources(zcs); @@ -451,8 +461,12 @@ size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t di } params.fParams.checksumFlag = 0; /* current limitation : no checksum (to be lifted in a later version) */ zcs->params = params; - zcs->dict = dict; - zcs->dictSize = dictSize; + if (updateDict) { + ZSTD_freeCDict(zcs->cdict); zcs->cdict = NULL; + if (dict && dictSize) { + zcs->cdict = ZSTD_createCDict_advanced(dict, dictSize, 0, params, cmem); + if (zcs->cdict == NULL) return ERROR(memory_allocation); + } } zcs->frameContentSize = pledgedSrcSize; zcs->targetSectionSize = (size_t)1 << MAX(ZSTDMT_SECTION_LOGSIZE_MIN, (zcs->params.cParams.windowLog + 2)); zcs->inBuffSize = zcs->targetSectionSize + ((size_t)1 << zcs->params.cParams.windowLog); @@ -466,16 +480,23 @@ size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t di return 0; } +size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, + const void* dict, size_t dictSize, + ZSTD_parameters params, unsigned long long pledgedSrcSize) +{ + return ZSTDMT_initCStream_internal(zcs, dict, dictSize, 1, params, pledgedSrcSize); +} + /* ZSTDMT_resetCStream() : * pledgedSrcSize is optional and can be zero == unknown */ size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize) { - return ZSTDMT_initCStream_advanced(zcs, zcs->dict, zcs->dictSize, zcs->params, pledgedSrcSize); + return ZSTDMT_initCStream_internal(zcs, NULL, 0, 0, zcs->params, pledgedSrcSize); } size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, 0); - return ZSTDMT_initCStream_advanced(zcs, NULL, 0, params, 0); + return ZSTDMT_initCStream_internal(zcs, NULL, 0, 1, params, 0); } @@ -510,8 +531,9 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = zcs->targetSectionSize; zcs->jobs[jobID].params = zcs->params; - zcs->jobs[jobID].dict = zcs->nextJobID == 0 ? zcs->dict : NULL; - zcs->jobs[jobID].dictSize = zcs->nextJobID == 0 ? zcs->dictSize : 0; + zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL; + zcs->jobs[jobID].dict = NULL; + zcs->jobs[jobID].dictSize = 0; zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize; zcs->jobs[jobID].dstBuff = dstBuffer; zcs->jobs[jobID].cctx = cctx; @@ -598,8 +620,9 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; zcs->jobs[jobID].params = zcs->params; - zcs->jobs[jobID].dict = zcs->nextJobID == 0 ? zcs->dict : NULL; - zcs->jobs[jobID].dictSize = zcs->nextJobID == 0 ? zcs->dictSize : 0; + zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL; + zcs->jobs[jobID].dict = NULL; + zcs->jobs[jobID].dictSize = 0; zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize; zcs->jobs[jobID].dstBuff = dstBuffer; zcs->jobs[jobID].cctx = cctx; diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 7e9d07035..bdc4caabc 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -29,8 +29,8 @@ ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, ZSTDLIB_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel); ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ -ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, - ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown ; current limitation : no checksum */ +ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, /**< dict can be released after init */ + ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< params current limitation : no checksum ; pledgedSrcSize is optional and can be zero == unknown */ ZSTDLIB_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); diff --git a/lib/zstd.h b/lib/zstd.h index a0d5c7856..52d65206c 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -572,6 +572,7 @@ ZSTDLIB_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel); ZSTDLIB_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel); ZSTDLIB_API size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); +ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, unsigned long long pledgedSrcSize); ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); From 1a2547f6540c8f208fe116072cd935960acfe62a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 22 Jan 2017 23:49:52 -0800 Subject: [PATCH 153/227] ZSTDMT_compressStream() becomes blocking when required to ensure forward progresses In some (rare) cases, job list could be blocked by a first job still being processed, while all following ones are completed, waiting to be flushed. In such case, the current job-table implementation is unable to accept new job. As a consequence, a call to ZSTDMT_compressStream() can be useless (nothing read, nothing flushed), with the risk to trigger a busy-wait on the caller side (needlessly loop over ZSTDMT_compressStream() ). In such a case, ZSTDMT_compressStream() will block until the first job is completed and ready to flush. It ensures some forward progress by guaranteeing it will flush at least a part of the completed job. Energy-wasting busy-wait is avoided. --- lib/compress/zstdmt_compress.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 283f76841..176f940c8 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -565,6 +565,10 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu { unsigned const jobID = zcs->doneJobID & zcs->jobIDMask; unsigned jobCompleted; pthread_mutex_lock(&zcs->jobCompleted_mutex); + while (zcs->jobs[jobID].jobCompleted == 0 && zcs->inBuff.filled == zcs->inBuffSize) { + /* when no new job could be started, block until there is something to flush, ensuring forward progress */ + pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); + } jobCompleted = zcs->jobs[jobID].jobCompleted; pthread_mutex_unlock(&zcs->jobCompleted_mutex); if (jobCompleted) { From 84581ff8d7cb7bcd18091c6bfada1764baa3c206 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 23 Jan 2017 00:56:54 -0800 Subject: [PATCH 154/227] ZSTDMT_compressCCtx : fallback to single-thread mode when nbChunks==1 --- lib/compress/zstdmt_compress.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 176f940c8..18ed74417 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -360,6 +360,15 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, DEBUGLOG(2, "nbChunks : %2u (chunkSize : %u bytes) ", nbChunks, (U32)avgChunkSize); params.fParams.contentSizeFlag = 1; + if (nbChunks==1) { /* fallback to single-thread mode */ + size_t result; + ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(mtctx->cctxPool); + if (!cctx) return ERROR(memory_allocation); + result = ZSTD_compressCCtx(mtctx->cctxPool->cctx[0], dst, dstCapacity, src, srcSize, compressionLevel); + ZSTDMT_releaseCCtx(mtctx->cctxPool, cctx); + return result; + } + { unsigned u; for (u=0; udoneJobID < zcs->nextJobID) { unsigned const jobID = zcs->doneJobID & zcs->jobIDMask; @@ -708,6 +715,3 @@ size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output) { return ZSTDMT_flushStream_internal(zcs, output, 1); } - - -#endif From 1cbf251e43c116d06d7e15ca5f77e84fea440be1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 23 Jan 2017 01:43:58 -0800 Subject: [PATCH 155/227] ZSTDMT streaming : fall back to (regular) single thread mode when nbThreads==1 --- lib/compress/zstdmt_compress.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 18ed74417..135d274f8 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -172,7 +172,10 @@ static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(unsigned nbThreads) ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) calloc(1, sizeof(ZSTDMT_CCtxPool) + (nbThreads-1)*sizeof(ZSTD_CCtx*)); if (!cctxPool) return NULL; cctxPool->totalCCtx = nbThreads; - cctxPool->availCCtx = 0; + cctxPool->availCCtx = 1; /* at least one cctx for single-thread mode */ + cctxPool->cctx[0] = ZSTD_createCCtx(); + if (!cctxPool->cctx[0]) { ZSTDMT_freeCCtxPool(cctxPool); return NULL; } + DEBUGLOG(1, "cctxPool created, with %u threads", nbThreads); return cctxPool; } @@ -278,6 +281,7 @@ struct ZSTDMT_CCtx_s { unsigned allJobsCompleted; unsigned long long frameContentSize; ZSTD_CDict* cdict; + ZSTD_CStream* cstream; ZSTDMT_jobDescription jobs[1]; /* variable size (must lies at the end) */ }; @@ -287,7 +291,7 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) U32 const minNbJobs = nbThreads + 2; U32 const nbJobsLog2 = ZSTD_highbit32(minNbJobs) + 1; U32 const nbJobs = 1 << nbJobsLog2; - DEBUGLOG(4, "nbThreads : %u ; minNbJobs : %u ; nbJobsLog2 : %u ; nbJobs : %u \n", + DEBUGLOG(5, "nbThreads : %u ; minNbJobs : %u ; nbJobsLog2 : %u ; nbJobs : %u \n", nbThreads, minNbJobs, nbJobsLog2, nbJobs); if ((nbThreads < 1) | (nbThreads > ZSTDMT_NBTHREADS_MAX)) return NULL; cctx = (ZSTDMT_CCtx*) calloc(1, sizeof(ZSTDMT_CCtx) + nbJobs*sizeof(ZSTDMT_jobDescription)); @@ -302,8 +306,14 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) ZSTDMT_freeCCtx(cctx); return NULL; } + if (nbThreads==1) { + cctx->cstream = ZSTD_createCStream(); + if (!cctx->cstream) { + ZSTDMT_freeCCtx(cctx); return NULL; + } } pthread_mutex_init(&cctx->jobCompleted_mutex, NULL); /* Todo : check init function return */ pthread_cond_init(&cctx->jobCompleted_cond, NULL); + DEBUGLOG(4, "mt_cctx created, for %u threads \n", nbThreads); return cctx; } @@ -329,11 +339,12 @@ static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx) size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) { if (mtctx==NULL) return 0; /* compatible with free on NULL */ - ZSTD_freeCDict(mtctx->cdict); POOL_free(mtctx->factory); if (!mtctx->allJobsCompleted) ZSTDMT_releaseAllJobResources(mtctx); /* stop workers first */ ZSTDMT_freeBufferPool(mtctx->buffPool); /* release job resources into pools first */ ZSTDMT_freeCCtxPool(mtctx->cctxPool); + ZSTD_freeCDict(mtctx->cdict); + ZSTD_freeCStream(mtctx->cstream); pthread_mutex_destroy(&mtctx->jobCompleted_mutex); pthread_cond_destroy(&mtctx->jobCompleted_cond); free(mtctx); @@ -361,12 +372,8 @@ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, params.fParams.contentSizeFlag = 1; if (nbChunks==1) { /* fallback to single-thread mode */ - size_t result; - ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(mtctx->cctxPool); - if (!cctx) return ERROR(memory_allocation); - result = ZSTD_compressCCtx(mtctx->cctxPool->cctx[0], dst, dstCapacity, src, srcSize, compressionLevel); - ZSTDMT_releaseCCtx(mtctx->cctxPool, cctx); - return result; + ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0]; + return ZSTD_compressCCtx(cctx, dst, dstCapacity, src, srcSize, compressionLevel); } { unsigned u; @@ -461,6 +468,7 @@ static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, ZSTD_parameters params, unsigned long long pledgedSrcSize) { ZSTD_customMem const cmem = { NULL, NULL, NULL }; + if (zcs->nbThreads==1) return ZSTD_initCStream_advanced(zcs->cstream, dict, dictSize, params, pledgedSrcSize); if (zcs->allJobsCompleted == 0) { /* previous job not correctly finished */ ZSTDMT_waitForAllJobsCompleted(zcs); ZSTDMT_releaseAllJobResources(zcs); @@ -498,6 +506,7 @@ size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, * pledgedSrcSize is optional and can be zero == unknown */ size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize) { + if (zcs->nbThreads==1) return ZSTD_resetCStream(zcs->cstream, pledgedSrcSize); return ZSTDMT_initCStream_internal(zcs, NULL, 0, 0, zcs->params, pledgedSrcSize); } @@ -510,6 +519,7 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { if (zcs->frameEnded) return ERROR(stage_wrong); /* current frame being ended. Only flush is allowed. Restart with init */ + if (zcs->nbThreads==1) return ZSTD_compressStream(zcs->cstream, output, input); /* fill input buffer */ { size_t const toLoad = MIN(input->size - input->pos, zcs->inBuffSize - zcs->inBuff.filled); @@ -708,10 +718,12 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp size_t ZSTDMT_flushStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output) { + if (zcs->nbThreads==1) return ZSTD_flushStream(zcs->cstream, output); return ZSTDMT_flushStream_internal(zcs, output, 0); } size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output) { + if (zcs->nbThreads==1) return ZSTD_endStream(zcs->cstream, output); return ZSTDMT_flushStream_internal(zcs, output, 1); } From 94364bf87a320fed426b2312adbb74e403dc62e5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 23 Jan 2017 11:43:51 -0800 Subject: [PATCH 156/227] refactor ZSTDMT streaming flush code now shared by both ZSTDMT_compressStream() and ZSTDMT_flushStream() --- lib/compress/zstdmt_compress.c | 73 +++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 135d274f8..9e7754b88 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -244,8 +244,8 @@ void ZSTDMT_compressChunk(void* jobDescription) ZSTD_invalidateRepCodes(job->cctx); } - DEBUGLOG(3, "Compressing : "); - DEBUG_PRINTHEX(3, job->srcStart, 12); + DEBUGLOG(4, "Compressing : "); + DEBUG_PRINTHEX(4, job->srcStart, 12); job->cSize = (job->lastChunk) ? /* last chunk signal */ ZSTD_compressEnd(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize) : ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize); @@ -516,6 +516,54 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { } +/* ZSTDMT_flushNextJob() : + * output : will be updated with amount of data flushed . + * blockToFlush : the function will block and wait if there is no data available to flush . + * @return : amount of data remaining within internal buffer, 1 if unknown but > 0, 0 if no more */ +static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush) +{ + unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; + if (zcs->doneJobID == zcs->nextJobID) return 0; /* all flushed ! */ + PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); + while (zcs->jobs[wJobID].jobCompleted==0) { + DEBUGLOG(5, "waiting for jobCompleted signal from job %u", zcs->doneJobID); /* block when nothing available to flush */ + if (!blockToFlush) { pthread_mutex_unlock(&zcs->jobCompleted_mutex); return 0; } /* nothing ready to be flushed => skip */ + pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); + } + pthread_mutex_unlock(&zcs->jobCompleted_mutex); + /* compression job completed : output can be flushed */ + { ZSTDMT_jobDescription job = zcs->jobs[wJobID]; + size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); + DEBUGLOG(4, "Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); + ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); + zcs->jobs[wJobID].cctx = NULL; + ZSTDMT_releaseBuffer(zcs->buffPool, job.src); + zcs->jobs[wJobID].srcStart = NULL; + zcs->jobs[wJobID].src = g_nullBuffer; + if (ZSTD_isError(job.cSize)) { + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + return job.cSize; + } + memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); + output->pos += toWrite; + job.dstFlushed += toWrite; + if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => move to next one */ + ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); + zcs->jobs[wJobID].dstBuff = g_nullBuffer; + zcs->jobs[wJobID].jobCompleted = 0; + zcs->doneJobID++; + } else { + zcs->jobs[wJobID].dstFlushed = job.dstFlushed; + } + /* return value : how many bytes left in buffer ; fake it to 1 if unknown but >0 */ + if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed); + if (zcs->doneJobID < zcs->nextJobID) return 1; /* still some buffer to flush */ + zcs->allJobsCompleted = zcs->frameEnded; /* frame completed and entirely flushed */ + return 0; /* everything flushed */ +} } + + size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { if (zcs->frameEnded) return ERROR(stage_wrong); /* current frame being ended. Only flush is allowed. Restart with init */ @@ -579,6 +627,8 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu } /* check if there is any data available to flush */ + ZSTDMT_flushNextJob(zcs, output, (zcs->inBuff.filled == zcs->inBuffSize)); /* we'll block if it wasn't possible to create new job due to saturation */ +#if 0 { unsigned const jobID = zcs->doneJobID & zcs->jobIDMask; unsigned jobCompleted; pthread_mutex_lock(&zcs->jobCompleted_mutex); @@ -611,7 +661,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu zcs->jobs[jobID].jobCompleted = 0; zcs->doneJobID++; } } } - +#endif /* recommended next input size : fill current input buffer */ return zcs->inBuffSize - zcs->inBuff.filled; } @@ -671,25 +721,27 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp zcs->frameEnded = 1; } - DEBUGLOG(1, "posting job %u : %u bytes (end:%u) (note : doneJob = %u=>%u)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize, zcs->jobs[jobID].lastChunk, zcs->doneJobID, zcs->doneJobID & zcs->jobIDMask); + DEBUGLOG(3, "posting job %u : %u bytes (end:%u) (note : doneJob = %u=>%u)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize, zcs->jobs[jobID].lastChunk, zcs->doneJobID, zcs->doneJobID & zcs->jobIDMask); POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); /* this call is blocking when thread worker pool is exhausted */ zcs->nextJobID++; } /* check if there is any data available to flush */ - DEBUGLOG(1, "zcs->doneJobID : %u ; zcs->nextJobID : %u ", zcs->doneJobID, zcs->nextJobID); - if (zcs->doneJobID == zcs->nextJobID) return 0; /* all flushed ! */ + DEBUGLOG(5, "zcs->doneJobID : %u ; zcs->nextJobID : %u ", zcs->doneJobID, zcs->nextJobID); + return ZSTDMT_flushNextJob(zcs, output, 1); + +#if 0 { unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); while (zcs->jobs[wJobID].jobCompleted==0) { - DEBUGLOG(5, "waiting for jobCompleted signal from job %u", zcs->doneJobID); /* we want to block when waiting for data to flush */ + DEBUGLOG(5, "waiting for jobCompleted signal from job %u", zcs->doneJobID); /* block when nothing available to flush */ pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); } pthread_mutex_unlock(&zcs->jobCompleted_mutex); - { /* job completed : output can be flushed */ - ZSTDMT_jobDescription job = zcs->jobs[wJobID]; + /* compression job completed : output can be flushed */ + { ZSTDMT_jobDescription job = zcs->jobs[wJobID]; size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); - DEBUGLOG(1, "Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); + DEBUGLOG(4, "Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[wJobID].cctx = NULL; /* release cctx for future task */ ZSTDMT_releaseBuffer(zcs->buffPool, job.src); zcs->jobs[wJobID].srcStart = NULL; zcs->jobs[wJobID].src = g_nullBuffer; if (ZSTD_isError(job.cSize)) { @@ -713,6 +765,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp zcs->allJobsCompleted = zcs->frameEnded; return 0; } } +#endif } From 96f152f708012bf4a083a72f014ec8a9e64e492a Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 24 Jan 2017 13:18:50 +0100 Subject: [PATCH 157/227] improved ZSTD_compressBlock_opt_extDict_generic --- lib/compress/zstd_opt.h | 4 ++-- zlibWrapper/.gitignore | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index f071c4f30..3bbf2eb7d 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -825,7 +825,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, match_num = ZSTD_BtGetAllMatches_selectMLS_extDict(ctx, inr, iend, maxSearches, mls, matches, minMatch); - if (match_num > 0 && matches[match_num-1].len > sufficient_len) { + if (match_num > 0 && (matches[match_num-1].len > sufficient_len || cur + matches[match_num-1].len >= ZSTD_OPT_NUM)) { best_mlen = matches[match_num-1].len; best_off = matches[match_num-1].off; last_pos = cur + 1; @@ -835,7 +835,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, /* set prices using matches at position = cur */ for (u = 0; u < match_num; u++) { mlen = (u>0) ? matches[u-1].len+1 : best_mlen; - best_mlen = (cur + matches[u].len < ZSTD_OPT_NUM) ? matches[u].len : ZSTD_OPT_NUM - cur; + best_mlen = matches[u].len; while (mlen <= best_mlen) { if (opt[cur].mlen == 1) { diff --git a/zlibWrapper/.gitignore b/zlibWrapper/.gitignore index 23d2f3a66..6167ca4da 100644 --- a/zlibWrapper/.gitignore +++ b/zlibWrapper/.gitignore @@ -22,4 +22,4 @@ zwrapbench *.txt # Directories -minizip/ \ No newline at end of file +minizip/ From 6ad02e7762d790d7d45a9249cb508219aa062336 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 24 Jan 2017 15:01:46 +0100 Subject: [PATCH 158/227] JOB_NUMBER -eq 9 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6bf99f1bf..35b25c099 100644 --- a/.travis.yml +++ b/.travis.yml @@ -159,4 +159,4 @@ matrix: script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') # - if [ $JOB_NUMBER -eq 9 ] || [ $JOB_NUMBER -eq 10 ]; then sh -c "$Cmd"; fi - - sh -c "$Cmd" + - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "master" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -eq 9 ]; then sh -c "$Cmd"; fi From 74d2cfdee2fb1b67b1c119420e75811832ff4c53 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 24 Jan 2017 17:42:28 +0100 Subject: [PATCH 159/227] .travis.yml: test jobs 12-15 --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 35b25c099..9f13272c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,6 @@ matrix: # Container-based Ubuntu 12.04 LTS Server Edition 64 bit (doesn't support 32-bit includes) - - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install" - os: linux - sudo: false - - env: Ubu=12.04cont Cmd="make zlibwrapper && make clean && make -C tests test-symbols && make clean && make -C tests test-zstd-nolegacy && make clean && make cmaketest && make clean && make -C contrib/pzstd googletest pzstd tests check && make -C contrib/pzstd clean" os: linux sudo: false @@ -156,7 +152,12 @@ matrix: - gcc-6 - gcc-6-multilib + # Container-based Ubuntu 12.04 LTS Server Edition 64 bit (doesn't support 32-bit includes) + - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install" + os: linux + sudo: false + script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') # - if [ $JOB_NUMBER -eq 9 ] || [ $JOB_NUMBER -eq 10 ]; then sh -c "$Cmd"; fi - - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "master" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -eq 9 ]; then sh -c "$Cmd"; fi + - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "master" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi From 3488a4a473f0631a2ea493bbf8f0fa2637019e4d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 24 Jan 2017 11:48:40 -0800 Subject: [PATCH 160/227] ZSTDMT now supports frame checksum --- lib/compress/zstdmt_compress.c | 144 +++++++++++---------------------- lib/compress/zstdmt_compress.h | 6 +- 2 files changed, 52 insertions(+), 98 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 9e7754b88..0b91ad4ea 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -29,6 +29,8 @@ #include "threading.h" /* mutex */ #include "zstd_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */ #include "zstdmt_compress.h" +#define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ +#include "xxhash.h" /* ====== Debug ====== */ @@ -217,6 +219,7 @@ typedef struct { unsigned firstChunk; unsigned lastChunk; unsigned jobCompleted; + unsigned jobScanned; pthread_mutex_t* jobCompleted_mutex; pthread_cond_t* jobCompleted_cond; ZSTD_parameters params; @@ -254,6 +257,7 @@ void ZSTDMT_compressChunk(void* jobDescription) _endJob: PTHREAD_MUTEX_LOCK(job->jobCompleted_mutex); job->jobCompleted = 1; + job->jobScanned = 0; pthread_cond_signal(job->jobCompleted_cond); pthread_mutex_unlock(job->jobCompleted_mutex); } @@ -273,6 +277,7 @@ struct ZSTDMT_CCtx_s { size_t inBuffSize; inBuff_t inBuff; ZSTD_parameters params; + XXH64_state_t xxhState; unsigned nbThreads; unsigned jobIDMask; unsigned doneJobID; @@ -474,7 +479,6 @@ static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, ZSTDMT_releaseAllJobResources(zcs); zcs->allJobsCompleted = 1; } - params.fParams.checksumFlag = 0; /* current limitation : no checksum (to be lifted in a later version) */ zcs->params = params; if (updateDict) { ZSTD_freeCDict(zcs->cdict); zcs->cdict = NULL; @@ -492,6 +496,7 @@ static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, zcs->nextJobID = 0; zcs->frameEnded = 0; zcs->allJobsCompleted = 0; + if (params.fParams.checksumFlag) XXH64_reset(&zcs->xxhState, 0); return 0; } @@ -518,7 +523,7 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { /* ZSTDMT_flushNextJob() : * output : will be updated with amount of data flushed . - * blockToFlush : the function will block and wait if there is no data available to flush . + * blockToFlush : if >0, the function will block and wait if there is no data available to flush . * @return : amount of data remaining within internal buffer, 1 if unknown but > 0, 0 if no more */ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush) { @@ -526,28 +531,43 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi if (zcs->doneJobID == zcs->nextJobID) return 0; /* all flushed ! */ PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); while (zcs->jobs[wJobID].jobCompleted==0) { - DEBUGLOG(5, "waiting for jobCompleted signal from job %u", zcs->doneJobID); /* block when nothing available to flush */ - if (!blockToFlush) { pthread_mutex_unlock(&zcs->jobCompleted_mutex); return 0; } /* nothing ready to be flushed => skip */ - pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); + DEBUGLOG(5, "waiting for jobCompleted signal from job %u", zcs->doneJobID); + if (!blockToFlush) { pthread_mutex_unlock(&zcs->jobCompleted_mutex); return 0; } /* nothing ready to be flushed => skip */ + pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); /* block when nothing available to flush */ } pthread_mutex_unlock(&zcs->jobCompleted_mutex); /* compression job completed : output can be flushed */ { ZSTDMT_jobDescription job = zcs->jobs[wJobID]; - size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); - DEBUGLOG(4, "Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); - ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); - zcs->jobs[wJobID].cctx = NULL; - ZSTDMT_releaseBuffer(zcs->buffPool, job.src); - zcs->jobs[wJobID].srcStart = NULL; - zcs->jobs[wJobID].src = g_nullBuffer; - if (ZSTD_isError(job.cSize)) { - ZSTDMT_waitForAllJobsCompleted(zcs); - ZSTDMT_releaseAllJobResources(zcs); - return job.cSize; + if (!job.jobScanned) { + if (ZSTD_isError(job.cSize)) { + DEBUGLOG(5, "compression error detected "); + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + return job.cSize; + } + ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); + zcs->jobs[wJobID].cctx = NULL; + DEBUGLOG(5, "zcs->params.fParams.checksumFlag : %u ", zcs->params.fParams.checksumFlag); + if (zcs->params.fParams.checksumFlag) { + XXH64_update(&zcs->xxhState, job.srcStart, job.srcSize); + if (zcs->frameEnded && (zcs->doneJobID+1 == zcs->nextJobID)) { /* write checksum at end of last section */ + U32 const checksum = (U32)XXH64_digest(&zcs->xxhState); + DEBUGLOG(4, "writing checksum : %08X \n", checksum); + MEM_writeLE32((char*)job.dstBuff.start + job.cSize, checksum); + job.cSize += 4; + zcs->jobs[wJobID].cSize += 4; + } } + ZSTDMT_releaseBuffer(zcs->buffPool, job.src); + zcs->jobs[wJobID].srcStart = NULL; + zcs->jobs[wJobID].src = g_nullBuffer; + zcs->jobs[wJobID].jobScanned = 1; + } + { size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); + DEBUGLOG(4, "Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); + memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); + output->pos += toWrite; + job.dstFlushed += toWrite; } - memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); - output->pos += toWrite; - job.dstFlushed += toWrite; if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => move to next one */ ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); zcs->jobs[wJobID].dstBuff = g_nullBuffer; @@ -583,7 +603,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; - if ((cctx==NULL) || (dstBuffer.start==NULL)) { + if ((cctx==NULL) || (dstBuffer.start==NULL)) { /* cannot get resources for next job */ zcs->jobs[jobID].jobCompleted = 1; zcs->nextJobID++; ZSTDMT_waitForAllJobsCompleted(zcs); @@ -591,11 +611,12 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu return ERROR(memory_allocation); } - DEBUGLOG(1, "preparing job %u to compress %u bytes \n", (U32)zcs->nextJobID, (U32)zcs->targetSectionSize); + DEBUGLOG(4, "preparing job %u to compress %u bytes \n", (U32)zcs->nextJobID, (U32)zcs->targetSectionSize); zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = zcs->targetSectionSize; zcs->jobs[jobID].params = zcs->params; + if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0; /* do not calculate checksum within sections, just keep it in header for first section */ zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL; zcs->jobs[jobID].dict = NULL; zcs->jobs[jobID].dictSize = 0; @@ -626,44 +647,11 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu zcs->nextJobID++; } - /* check if there is any data available to flush */ + /* check for data to flush */ ZSTDMT_flushNextJob(zcs, output, (zcs->inBuff.filled == zcs->inBuffSize)); /* we'll block if it wasn't possible to create new job due to saturation */ -#if 0 - { unsigned const jobID = zcs->doneJobID & zcs->jobIDMask; - unsigned jobCompleted; - pthread_mutex_lock(&zcs->jobCompleted_mutex); - while (zcs->jobs[jobID].jobCompleted == 0 && zcs->inBuff.filled == zcs->inBuffSize) { - /* when no new job could be started, block until there is something to flush, ensuring forward progress */ - pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); - } - jobCompleted = zcs->jobs[jobID].jobCompleted; - pthread_mutex_unlock(&zcs->jobCompleted_mutex); - if (jobCompleted) { - ZSTDMT_jobDescription const job = zcs->jobs[jobID]; - size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); - DEBUGLOG(1, "flush %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); - ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); - zcs->jobs[jobID].cctx = NULL; - ZSTDMT_releaseBuffer(zcs->buffPool, job.src); - zcs->jobs[jobID].srcStart = NULL; zcs->jobs[jobID].src = g_nullBuffer; - if (ZSTD_isError(job.cSize)) { - ZSTDMT_waitForAllJobsCompleted(zcs); - ZSTDMT_releaseAllJobResources(zcs); - return job.cSize; - } - memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); - output->pos += toWrite; - zcs->jobs[jobID].dstFlushed += toWrite; - DEBUGLOG(1, "remaining : %u bytes ", (U32)(job.cSize - job.dstFlushed)); - if (zcs->jobs[jobID].dstFlushed == job.cSize) { /* output buffer fully flushed => go to next one */ - ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); - zcs->jobs[jobID].dstBuff = g_nullBuffer; - zcs->jobs[jobID].jobCompleted = 0; - zcs->doneJobID++; - } } } -#endif + /* recommended next input size : fill current input buffer */ - return zcs->inBuffSize - zcs->inBuff.filled; + return zcs->inBuffSize - zcs->inBuff.filled; /* note : could be zero when input buffer is fully filled and no more availability to create new job */ } @@ -671,7 +659,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp { size_t const srcSize = zcs->inBuff.filled; - DEBUGLOG(1, "flushing : %u bytes to compress", (U32)srcSize); + DEBUGLOG(4, "flushing : %u bytes left to compress", (U32)srcSize); if ( ((srcSize > 0) || (endFrame && !zcs->frameEnded)) && (zcs->nextJobID <= zcs->doneJobID + zcs->jobIDMask) ) { size_t const dstBufferCapacity = ZSTD_compressBound(srcSize); @@ -691,6 +679,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; zcs->jobs[jobID].params = zcs->params; + if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0; /* do not calculate checksum within sections, just keep it in header for first section */ zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL; zcs->jobs[jobID].dict = NULL; zcs->jobs[jobID].dictSize = 0; @@ -719,6 +708,8 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp zcs->inBuff.buffer = g_nullBuffer; zcs->inBuff.filled = 0; zcs->frameEnded = 1; + if (zcs->nextJobID == 0) + zcs->params.fParams.checksumFlag = 0; /* single chunk : checksum is calculated directly within worker thread */ } DEBUGLOG(3, "posting job %u : %u bytes (end:%u) (note : doneJob = %u=>%u)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize, zcs->jobs[jobID].lastChunk, zcs->doneJobID, zcs->doneJobID & zcs->jobIDMask); @@ -729,43 +720,6 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp /* check if there is any data available to flush */ DEBUGLOG(5, "zcs->doneJobID : %u ; zcs->nextJobID : %u ", zcs->doneJobID, zcs->nextJobID); return ZSTDMT_flushNextJob(zcs, output, 1); - -#if 0 - { unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; - PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); - while (zcs->jobs[wJobID].jobCompleted==0) { - DEBUGLOG(5, "waiting for jobCompleted signal from job %u", zcs->doneJobID); /* block when nothing available to flush */ - pthread_cond_wait(&zcs->jobCompleted_cond, &zcs->jobCompleted_mutex); - } - pthread_mutex_unlock(&zcs->jobCompleted_mutex); - /* compression job completed : output can be flushed */ - { ZSTDMT_jobDescription job = zcs->jobs[wJobID]; - size_t const toWrite = MIN(job.cSize - job.dstFlushed, output->size - output->pos); - DEBUGLOG(4, "Flushing %u bytes from job %u ", (U32)toWrite, zcs->doneJobID); - ZSTDMT_releaseCCtx(zcs->cctxPool, job.cctx); zcs->jobs[wJobID].cctx = NULL; /* release cctx for future task */ - ZSTDMT_releaseBuffer(zcs->buffPool, job.src); zcs->jobs[wJobID].srcStart = NULL; zcs->jobs[wJobID].src = g_nullBuffer; - if (ZSTD_isError(job.cSize)) { - ZSTDMT_waitForAllJobsCompleted(zcs); - ZSTDMT_releaseAllJobResources(zcs); - return job.cSize; - } - memcpy((char*)output->dst + output->pos, (const char*)job.dstBuff.start + job.dstFlushed, toWrite); - output->pos += toWrite; - job.dstFlushed += toWrite; - if (job.dstFlushed == job.cSize) { /* output buffer fully flushed => next one */ - ZSTDMT_releaseBuffer(zcs->buffPool, job.dstBuff); zcs->jobs[wJobID].dstBuff = g_nullBuffer; - zcs->jobs[wJobID].jobCompleted = 0; - zcs->doneJobID++; - } else { - zcs->jobs[wJobID].dstFlushed = job.dstFlushed; - } - /* return value : how many bytes left in buffer ; fake it to 1 if unknown but >0 */ - if (job.cSize > job.dstFlushed) return (job.cSize - job.dstFlushed); - if ((zcs->doneJobID < zcs->nextJobID) || (zcs->inBuff.filled)) return 1; /* still some buffer to flush */ - zcs->allJobsCompleted = zcs->frameEnded; - return 0; - } } -#endif } diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index bdc4caabc..84d25f738 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -28,9 +28,9 @@ ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, /* === Streaming functions === */ ZSTDLIB_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel); -ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ -ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, /**< dict can be released after init */ - ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< params current limitation : no checksum ; pledgedSrcSize is optional and can be zero == unknown */ +ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ +ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, /**< dict can be released after init, a local copy is preserved within zcs */ + ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ ZSTDLIB_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); From 512cbe8c10b59b957ecb107b119af95720b6d470 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 24 Jan 2017 17:02:26 -0800 Subject: [PATCH 161/227] zstdmt cli and API allow selection of section sizes By default, section sizes are 4x window size. This new setting allow manual selection of section sizes. The larger they are, the (slightly) better the compression ratio, but also the higher the memory allocation cost, and eventually the lesser the nb of possible threads, since each section is compressed by a single thread. It also introduces a prototype to set generic parameters, ZSTDMT_setMTCtxParameter() The idea is that it's possible to add enums to extend the list of parameters that can be set this way. This is more long-term oriented than a fixed-size struct. Consider it as a test. --- lib/compress/zstdmt_compress.c | 25 +++++++++++++++++----- lib/compress/zstdmt_compress.h | 38 +++++++++++++++++++++++++++------- programs/fileio.c | 14 ++++++++++++- programs/fileio.h | 1 + programs/zstdcli.c | 2 ++ 5 files changed, 67 insertions(+), 13 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 0b91ad4ea..1baccf0fc 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -9,10 +9,6 @@ /* ====== Tuning parameters ====== */ -#ifndef ZSTDMT_SECTION_LOGSIZE_MIN -# define ZSTDMT_SECTION_LOGSIZE_MIN 20 /* minimum size for a full compression job (20==2^20==1 MB) */ -#endif - #define ZSTDMT_NBTHREADS_MAX 128 @@ -285,6 +281,7 @@ struct ZSTDMT_CCtx_s { unsigned frameEnded; unsigned allJobsCompleted; unsigned long long frameContentSize; + size_t sectionSize; ZSTD_CDict* cdict; ZSTD_CStream* cstream; ZSTDMT_jobDescription jobs[1]; /* variable size (must lies at the end) */ @@ -304,6 +301,7 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) cctx->nbThreads = nbThreads; cctx->jobIDMask = nbJobs - 1; cctx->allJobsCompleted = 1; + cctx->sectionSize = 0; cctx->factory = POOL_create(nbThreads, 1); cctx->buffPool = ZSTDMT_createBufferPool(nbThreads); cctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads); @@ -356,6 +354,22 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) return 0; } +unsigned ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, unsigned value) +{ + switch(parameter) + { + case ZSTDMT_p_sectionSize : + mtctx->sectionSize = value; + return 0; + default : + return ERROR(compressionParameter_unsupported); + } +} + + +/* ------------------------------------------ */ +/* ===== Multi-threaded compression ===== */ +/* ------------------------------------------ */ size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, void* dst, size_t dstCapacity, @@ -487,7 +501,8 @@ static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, if (zcs->cdict == NULL) return ERROR(memory_allocation); } } zcs->frameContentSize = pledgedSrcSize; - zcs->targetSectionSize = (size_t)1 << MAX(ZSTDMT_SECTION_LOGSIZE_MIN, (zcs->params.cParams.windowLog + 2)); + zcs->targetSectionSize = zcs->sectionSize ? zcs->sectionSize : (size_t)1 << (zcs->params.cParams.windowLog + 2); + zcs->targetSectionSize = MAX(ZSTDMT_SECTION_SIZE_MIN, zcs->targetSectionSize); zcs->inBuffSize = zcs->targetSectionSize + ((size_t)1 << zcs->params.cParams.windowLog); zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); if (zcs->inBuff.buffer.start == NULL) return ERROR(memory_allocation); diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 84d25f738..c00782e9d 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -7,6 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ + +/* Note : All prototypes defined in this file shall be considered experimental. + * There is no guarantee of API continuity (yet) on any of these prototypes */ + /* === Dependencies === */ #include /* size_t */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters */ @@ -27,12 +31,32 @@ ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* cctx, /* === Streaming functions === */ -ZSTDLIB_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel); -ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ -ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* zcs, const void* dict, size_t dictSize, /**< dict can be released after init, a local copy is preserved within zcs */ - ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ +ZSTDLIB_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel); +ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ -ZSTDLIB_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); +ZSTDLIB_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input); -ZSTDLIB_API size_t ZSTDMT_flushStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ -ZSTDLIB_API size_t ZSTDMT_endStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ +ZSTDLIB_API size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ +ZSTDLIB_API size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */ + + +/* === Advanced functions and parameters === */ + +#ifndef ZSTDMT_SECTION_SIZE_MIN +# define ZSTDMT_SECTION_SIZE_MIN (1U << 20) /* 1 MB - Minimum size of each compression job */ +#endif + +ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, const void* dict, size_t dictSize, /**< dict can be released after init, a local copy is preserved within zcs */ + ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ + +/* ZSDTMT_parameter : + * List of parameters that can be set using ZSTDMT_setMTCtxParameter() */ +typedef enum { ZSTDMT_p_sectionSize /* size of input "section". Each section is compressed in parallel. 0 means default, which is dynamically determined within compression functions */ + } ZSDTMT_parameter; + +/* ZSTDMT_setMTCtxParameter() : + * allow setting individual parameters, one at a time, among a list of enums defined in ZSTDMT_parameter. + * The function must be called typically after ZSTD_createCCtx(). + * Parameters not explicitly reset by ZSTDMT_init*() remain the same in consecutive compression sessions. + * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ +ZSTDLIB_API unsigned ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, unsigned value); diff --git a/programs/fileio.c b/programs/fileio.c index 3864a5fab..86db12acb 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -113,6 +113,16 @@ void FIO_setNbThreads(unsigned nbThreads) { #endif g_nbThreads = nbThreads; } +static U32 g_blockSize = 0; +void FIO_setBlockSize(unsigned blockSize) { + if (blockSize && g_nbThreads==1) + DISPLAYLEVEL(2, "Setting block size is useless in single-thread mode \n"); +#ifdef ZSTD_MULTITHREAD + if (blockSize-1 < ZSTDMT_SECTION_SIZE_MIN-1) /* intentional underflow */ + DISPLAYLEVEL(2, "Note : minimum block size is %u KB \n", (ZSTDMT_SECTION_SIZE_MIN>>10)); +#endif + g_blockSize = blockSize; +} /*-************************************* @@ -283,10 +293,12 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, if (comprParams->strategy) params.cParams.strategy = (ZSTD_strategy)(comprParams->strategy - 1); #ifdef ZSTD_MULTITHREAD { size_t const errorCode = ZSTDMT_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, srcSize); + if (ZSTD_isError(errorCode)) EXM_THROW(33, "Error initializing CStream : %s", ZSTD_getErrorName(errorCode)); + ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_sectionSize, g_blockSize); #else { size_t const errorCode = ZSTD_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, srcSize); -#endif if (ZSTD_isError(errorCode)) EXM_THROW(33, "Error initializing CStream : %s", ZSTD_getErrorName(errorCode)); +#endif } } free(dictBuffer); } diff --git a/programs/fileio.h b/programs/fileio.h index 9ef449294..19f09c33a 100644 --- a/programs/fileio.h +++ b/programs/fileio.h @@ -41,6 +41,7 @@ void FIO_setChecksumFlag(unsigned checksumFlag); void FIO_setRemoveSrcFile(unsigned flag); void FIO_setMemLimit(unsigned memLimit); void FIO_setNbThreads(unsigned nbThreads); +void FIO_setBlockSize(unsigned blockSize); /*-************************************* diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 785ecedee..549dad01a 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -118,6 +118,7 @@ static int usage_advanced(const char* programName) DISPLAY( "--[no-]check : integrity check (default:enabled) \n"); #ifdef ZSTD_MULTITHREAD DISPLAY( " -T# : use # threads for compression (default:1) \n"); + DISPLAY( " -B# : select size of independent sections (default:0==automatic) \n"); #endif #endif #ifndef ZSTD_NODECOMPRESS @@ -625,6 +626,7 @@ int main(int argCount, const char* argv[]) if (operation==zom_compress) { #ifndef ZSTD_NOCOMPRESS FIO_setNbThreads(nbThreads); + FIO_setBlockSize((U32)blockSize); if ((filenameIdx==1) && outFileName) operationResult = FIO_compressFilename(outFileName, filenameTable[0], dictFileName, cLevel, &compressionParams); else From f14a669054dc5bc88ed6ecae31c1304d6d10e75f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 24 Jan 2017 17:41:49 -0800 Subject: [PATCH 162/227] refactor job creation code shared accross ZSTDMT_{compress,flush,end}Stream(), for easier maintenance --- lib/compress/zstdmt_compress.c | 167 +++++++++++++-------------------- 1 file changed, 65 insertions(+), 102 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 1baccf0fc..e57908078 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -536,10 +536,71 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { } +static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsigned endFrame) +{ + size_t const dstBufferCapacity = ZSTD_compressBound(srcSize); + buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); + ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); + unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; + + if ((cctx==NULL) || (dstBuffer.start==NULL)) { + zcs->jobs[jobID].jobCompleted = 1; + zcs->nextJobID++; + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + return ERROR(memory_allocation); + } + + DEBUGLOG(4, "preparing job %u to compress %u bytes \n", zcs->nextJobID, (U32)srcSize); + zcs->jobs[jobID].src = zcs->inBuff.buffer; + zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; + zcs->jobs[jobID].srcSize = srcSize; + zcs->jobs[jobID].params = zcs->params; + if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0; /* do not calculate checksum within sections, just keep it in header for first section */ + zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL; + zcs->jobs[jobID].dict = NULL; + zcs->jobs[jobID].dictSize = 0; + zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize; + zcs->jobs[jobID].dstBuff = dstBuffer; + zcs->jobs[jobID].cctx = cctx; + zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0); + zcs->jobs[jobID].lastChunk = endFrame; + zcs->jobs[jobID].jobCompleted = 0; + zcs->jobs[jobID].dstFlushed = 0; + zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex; + zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond; + + /* get a new buffer for next input */ + if (!endFrame) { + zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); + if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */ + zcs->jobs[jobID].jobCompleted = 1; + zcs->nextJobID++; + ZSTDMT_waitForAllJobsCompleted(zcs); + ZSTDMT_releaseAllJobResources(zcs); + return ERROR(memory_allocation); + } + zcs->inBuff.filled -= srcSize; + memcpy(zcs->inBuff.buffer.start, (const char*)zcs->jobs[jobID].srcStart + srcSize, zcs->inBuff.filled); + } else { + zcs->inBuff.buffer = g_nullBuffer; + zcs->inBuff.filled = 0; + zcs->frameEnded = 1; + if (zcs->nextJobID == 0) + zcs->params.fParams.checksumFlag = 0; /* single chunk : checksum is calculated directly within worker thread */ + } + + DEBUGLOG(3, "posting job %u : %u bytes (end:%u) (note : doneJob = %u=>%u)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize, zcs->jobs[jobID].lastChunk, zcs->doneJobID, zcs->doneJobID & zcs->jobIDMask); + POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); /* this call is blocking when thread worker pool is exhausted */ + zcs->nextJobID++; + return 0; +} + + /* ZSTDMT_flushNextJob() : * output : will be updated with amount of data flushed . * blockToFlush : if >0, the function will block and wait if there is no data available to flush . - * @return : amount of data remaining within internal buffer, 1 if unknown but > 0, 0 if no more */ + * @return : amount of data remaining within internal buffer, 1 if unknown but > 0, 0 if no more, or an error code */ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush) { unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; @@ -613,57 +674,11 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu if ( (zcs->inBuff.filled == zcs->inBuffSize) /* filled enough : let's compress */ && (zcs->nextJobID <= zcs->doneJobID + zcs->jobIDMask) ) { /* avoid overwriting job round buffer */ - size_t const dstBufferCapacity = ZSTD_compressBound(zcs->targetSectionSize); - buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); - ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); - unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; - - if ((cctx==NULL) || (dstBuffer.start==NULL)) { /* cannot get resources for next job */ - zcs->jobs[jobID].jobCompleted = 1; - zcs->nextJobID++; - ZSTDMT_waitForAllJobsCompleted(zcs); - ZSTDMT_releaseAllJobResources(zcs); - return ERROR(memory_allocation); - } - - DEBUGLOG(4, "preparing job %u to compress %u bytes \n", (U32)zcs->nextJobID, (U32)zcs->targetSectionSize); - zcs->jobs[jobID].src = zcs->inBuff.buffer; - zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; - zcs->jobs[jobID].srcSize = zcs->targetSectionSize; - zcs->jobs[jobID].params = zcs->params; - if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0; /* do not calculate checksum within sections, just keep it in header for first section */ - zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL; - zcs->jobs[jobID].dict = NULL; - zcs->jobs[jobID].dictSize = 0; - zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize; - zcs->jobs[jobID].dstBuff = dstBuffer; - zcs->jobs[jobID].cctx = cctx; - zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0); - zcs->jobs[jobID].lastChunk = 0; - zcs->jobs[jobID].jobCompleted = 0; - zcs->jobs[jobID].dstFlushed = 0; - zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex; - zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond; - - /* get a new buffer for next input - save remaining into it */ - zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); - if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */ - zcs->jobs[jobID].jobCompleted = 1; - zcs->nextJobID++; - ZSTDMT_waitForAllJobsCompleted(zcs); - ZSTDMT_releaseAllJobResources(zcs); - return ERROR(memory_allocation); - } - zcs->inBuff.filled = (U32)(zcs->inBuffSize - zcs->targetSectionSize); - memcpy(zcs->inBuff.buffer.start, (const char*)zcs->jobs[jobID].srcStart + zcs->targetSectionSize, zcs->inBuff.filled); - - DEBUGLOG(3, "posting job %u (%u bytes) (note : doneJob = %u=>%u)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize, zcs->doneJobID, zcs->doneJobID & zcs->jobIDMask); - POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); /* This call is blocking if all workers are busy */ - zcs->nextJobID++; + CHECK_F( ZSTDMT_createCompressionJob(zcs, zcs->targetSectionSize, 0) ); } /* check for data to flush */ - ZSTDMT_flushNextJob(zcs, output, (zcs->inBuff.filled == zcs->inBuffSize)); /* we'll block if it wasn't possible to create new job due to saturation */ + CHECK_F( ZSTDMT_flushNextJob(zcs, output, (zcs->inBuff.filled == zcs->inBuffSize)) ); /* block if it wasn't possible to create new job due to saturation */ /* recommended next input size : fill current input buffer */ return zcs->inBuffSize - zcs->inBuff.filled; /* note : could be zero when input buffer is fully filled and no more availability to create new job */ @@ -677,59 +692,7 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp DEBUGLOG(4, "flushing : %u bytes left to compress", (U32)srcSize); if ( ((srcSize > 0) || (endFrame && !zcs->frameEnded)) && (zcs->nextJobID <= zcs->doneJobID + zcs->jobIDMask) ) { - size_t const dstBufferCapacity = ZSTD_compressBound(srcSize); - buffer_t const dstBuffer = ZSTDMT_getBuffer(zcs->buffPool, dstBufferCapacity); - ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(zcs->cctxPool); - unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; - - if ((cctx==NULL) || (dstBuffer.start==NULL)) { - zcs->jobs[jobID].jobCompleted = 1; - zcs->nextJobID++; - ZSTDMT_waitForAllJobsCompleted(zcs); - ZSTDMT_releaseAllJobResources(zcs); - return ERROR(memory_allocation); - } - - zcs->jobs[jobID].src = zcs->inBuff.buffer; - zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; - zcs->jobs[jobID].srcSize = srcSize; - zcs->jobs[jobID].params = zcs->params; - if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0; /* do not calculate checksum within sections, just keep it in header for first section */ - zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL; - zcs->jobs[jobID].dict = NULL; - zcs->jobs[jobID].dictSize = 0; - zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize; - zcs->jobs[jobID].dstBuff = dstBuffer; - zcs->jobs[jobID].cctx = cctx; - zcs->jobs[jobID].firstChunk = (zcs->nextJobID==0); - zcs->jobs[jobID].lastChunk = endFrame; - zcs->jobs[jobID].jobCompleted = 0; - zcs->jobs[jobID].dstFlushed = 0; - zcs->jobs[jobID].jobCompleted_mutex = &zcs->jobCompleted_mutex; - zcs->jobs[jobID].jobCompleted_cond = &zcs->jobCompleted_cond; - - /* get a new buffer for next input */ - if (!endFrame) { - zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); - zcs->inBuff.filled = 0; - if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */ - zcs->jobs[jobID].jobCompleted = 1; - zcs->nextJobID++; - ZSTDMT_waitForAllJobsCompleted(zcs); - ZSTDMT_releaseAllJobResources(zcs); - return ERROR(memory_allocation); - } - } else { - zcs->inBuff.buffer = g_nullBuffer; - zcs->inBuff.filled = 0; - zcs->frameEnded = 1; - if (zcs->nextJobID == 0) - zcs->params.fParams.checksumFlag = 0; /* single chunk : checksum is calculated directly within worker thread */ - } - - DEBUGLOG(3, "posting job %u : %u bytes (end:%u) (note : doneJob = %u=>%u)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize, zcs->jobs[jobID].lastChunk, zcs->doneJobID, zcs->doneJobID & zcs->jobIDMask); - POOL_add(zcs->factory, ZSTDMT_compressChunk, &zcs->jobs[jobID]); /* this call is blocking when thread worker pool is exhausted */ - zcs->nextJobID++; + CHECK_F( ZSTDMT_createCompressionJob(zcs, srcSize, endFrame) ); } /* check if there is any data available to flush */ From dc8dae596a5ddc52d8fe98491855119999a8b8e2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 24 Jan 2017 22:32:12 -0800 Subject: [PATCH 163/227] overlapped section, for improved compression Sections 2+ read a bit of data from previous section in order to improve compression ratio. This also costs some CPU, to reference read data. Read data is currently fixed to window>>3 size --- lib/compress/zstdmt_compress.c | 43 +++++++++++++++++++++------------- lib/compress/zstdmt_compress.h | 2 +- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index e57908078..99b2e68fb 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -209,6 +209,7 @@ typedef struct { buffer_t src; const void* srcStart; size_t srcSize; + size_t dictSize; buffer_t dstBuff; size_t cSize; size_t dstFlushed; @@ -220,8 +221,6 @@ typedef struct { pthread_cond_t* jobCompleted_cond; ZSTD_parameters params; ZSTD_CDict* cdict; - const void* dict; - size_t dictSize; unsigned long long fullFrameSize; } ZSTDMT_jobDescription; @@ -229,16 +228,18 @@ typedef struct { void ZSTDMT_compressChunk(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; + const void* const src = (const char*)job->srcStart + job->dictSize; buffer_t const dstBuff = job->dstBuff; + DEBUGLOG(3, "job (first:%u) (last:%u) : dictSize %u, srcSize %u", job->firstChunk, job->lastChunk, (U32)job->dictSize, (U32)job->srcSize); if (job->cdict) { size_t const initError = ZSTD_compressBegin_usingCDict(job->cctx, job->cdict, job->fullFrameSize); if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { - size_t const initError = ZSTD_compressBegin_advanced(job->cctx, job->dict, job->dictSize, job->params, job->fullFrameSize); + size_t const initError = ZSTD_compressBegin_advanced(job->cctx, job->srcStart, job->dictSize, job->params, job->fullFrameSize); if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } if (!job->firstChunk) { /* flush frame header */ - size_t const hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, 0); + size_t const hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, src, 0); if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } ZSTD_invalidateRepCodes(job->cctx); } @@ -246,8 +247,8 @@ void ZSTDMT_compressChunk(void* jobDescription) DEBUGLOG(4, "Compressing : "); DEBUG_PRINTHEX(4, job->srcStart, 12); job->cSize = (job->lastChunk) ? /* last chunk signal */ - ZSTD_compressEnd(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize) : - ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, job->srcStart, job->srcSize); + ZSTD_compressEnd (job->cctx, dstBuff.start, dstBuff.size, src, job->srcSize) : + ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, src, job->srcSize); DEBUGLOG(3, "compressed %u bytes into %u bytes (first:%u) (last:%u)", (unsigned)job->srcSize, (unsigned)job->cSize, job->firstChunk, job->lastChunk); _endJob: @@ -271,6 +272,8 @@ struct ZSTDMT_CCtx_s { pthread_cond_t jobCompleted_cond; size_t targetSectionSize; size_t inBuffSize; + size_t dictSize; + size_t targetDictSize; inBuff_t inBuff; ZSTD_parameters params; XXH64_state_t xxhState; @@ -354,7 +357,7 @@ size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx) return 0; } -unsigned ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, unsigned value) +size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, unsigned value) { switch(parameter) { @@ -503,10 +506,14 @@ static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, zcs->frameContentSize = pledgedSrcSize; zcs->targetSectionSize = zcs->sectionSize ? zcs->sectionSize : (size_t)1 << (zcs->params.cParams.windowLog + 2); zcs->targetSectionSize = MAX(ZSTDMT_SECTION_SIZE_MIN, zcs->targetSectionSize); - zcs->inBuffSize = zcs->targetSectionSize + ((size_t)1 << zcs->params.cParams.windowLog); + //zcs->targetDictSize = ((size_t)1 << zcs->params.cParams.windowLog); /* full window size, for test */ + zcs->targetDictSize = ((size_t)1 << zcs->params.cParams.windowLog) >> 3; /* fixed currently */ + //zcs->targetDictSize = 0; + zcs->inBuffSize = zcs->targetSectionSize + ((size_t)1 << zcs->params.cParams.windowLog) /* margin */ + zcs->targetDictSize; zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); if (zcs->inBuff.buffer.start == NULL) return ERROR(memory_allocation); zcs->inBuff.filled = 0; + zcs->dictSize = 0; zcs->doneJobID = 0; zcs->nextJobID = 0; zcs->frameEnded = 0; @@ -551,15 +558,14 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi return ERROR(memory_allocation); } - DEBUGLOG(4, "preparing job %u to compress %u bytes \n", zcs->nextJobID, (U32)srcSize); + DEBUGLOG(4, "preparing job %u to compress %u bytes with %u preload ", zcs->nextJobID, (U32)srcSize, (U32)zcs->dictSize); zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; + zcs->jobs[jobID].dictSize = zcs->dictSize; /* note : zcs->inBuff.filled is presumed >= srcSize + dictSize */ zcs->jobs[jobID].params = zcs->params; if (zcs->nextJobID) zcs->jobs[jobID].params.fParams.checksumFlag = 0; /* do not calculate checksum within sections, just keep it in header for first section */ zcs->jobs[jobID].cdict = zcs->nextJobID==0 ? zcs->cdict : NULL; - zcs->jobs[jobID].dict = NULL; - zcs->jobs[jobID].dictSize = 0; zcs->jobs[jobID].fullFrameSize = zcs->frameContentSize; zcs->jobs[jobID].dstBuff = dstBuffer; zcs->jobs[jobID].cctx = cctx; @@ -572,6 +578,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi /* get a new buffer for next input */ if (!endFrame) { + size_t const newDictSize = MIN(srcSize + zcs->dictSize, zcs->targetDictSize); zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); if (zcs->inBuff.buffer.start == NULL) { /* not enough memory to allocate next input buffer */ zcs->jobs[jobID].jobCompleted = 1; @@ -580,8 +587,12 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi ZSTDMT_releaseAllJobResources(zcs); return ERROR(memory_allocation); } - zcs->inBuff.filled -= srcSize; - memcpy(zcs->inBuff.buffer.start, (const char*)zcs->jobs[jobID].srcStart + srcSize, zcs->inBuff.filled); + DEBUGLOG(5, "inBuff filled to %u", (U32)zcs->inBuff.filled); + zcs->inBuff.filled -= srcSize + zcs->dictSize - newDictSize; + DEBUGLOG(5, "new job : filled to %u, with %u dict and %u src", (U32)zcs->inBuff.filled, (U32)newDictSize, (U32)(zcs->inBuff.filled - newDictSize)); + memmove(zcs->inBuff.buffer.start, (const char*)zcs->jobs[jobID].srcStart + zcs->dictSize + srcSize - newDictSize, zcs->inBuff.filled); + DEBUGLOG(5, "new inBuff pre-filled"); + zcs->dictSize = newDictSize; } else { zcs->inBuff.buffer = g_nullBuffer; zcs->inBuff.filled = 0; @@ -625,7 +636,7 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi zcs->jobs[wJobID].cctx = NULL; DEBUGLOG(5, "zcs->params.fParams.checksumFlag : %u ", zcs->params.fParams.checksumFlag); if (zcs->params.fParams.checksumFlag) { - XXH64_update(&zcs->xxhState, job.srcStart, job.srcSize); + XXH64_update(&zcs->xxhState, (const char*)job.srcStart + job.dictSize, job.srcSize); if (zcs->frameEnded && (zcs->doneJobID+1 == zcs->nextJobID)) { /* write checksum at end of last section */ U32 const checksum = (U32)XXH64_digest(&zcs->xxhState); DEBUGLOG(4, "writing checksum : %08X \n", checksum); @@ -689,10 +700,10 @@ static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* outp { size_t const srcSize = zcs->inBuff.filled; - DEBUGLOG(4, "flushing : %u bytes left to compress", (U32)srcSize); + if (srcSize) DEBUGLOG(1, "flushing : %u bytes left to compress", (U32)srcSize); if ( ((srcSize > 0) || (endFrame && !zcs->frameEnded)) && (zcs->nextJobID <= zcs->doneJobID + zcs->jobIDMask) ) { - CHECK_F( ZSTDMT_createCompressionJob(zcs, srcSize, endFrame) ); + CHECK_F( ZSTDMT_createCompressionJob(zcs, srcSize - zcs->dictSize, endFrame) ); } /* check if there is any data available to flush */ diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index c00782e9d..1288c1ed0 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -59,4 +59,4 @@ typedef enum { ZSTDMT_p_sectionSize /* size of input "section". Each section * The function must be called typically after ZSTD_createCCtx(). * Parameters not explicitly reset by ZSTDMT_init*() remain the same in consecutive compression sessions. * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ -ZSTDLIB_API unsigned ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, unsigned value); +ZSTDLIB_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, unsigned value); From 3bb010a667d87dd220d25d2fea99fea8edb54c79 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 25 Jan 2017 11:19:35 +0100 Subject: [PATCH 164/227] .travis.yml: optimized order of short tests --- .travis.yml | 39 +++++++++++++++++++++------------------ tests/Makefile | 4 ++-- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9f13272c1..dba6ae9aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,7 +76,6 @@ matrix: - gcc-arm-linux-gnueabi - libc6-dev-armel-cross - # Ubuntu 14.04 LTS Server Edition 64 bit - env: Ubu=14.04 Cmd="make aarch64test" dist: trusty sudo: required @@ -108,6 +107,23 @@ matrix: packages: - valgrind + + + # other feature branches => short tests + - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install" + os: linux + sudo: false + + - env: Ubu=14.04 Cmd="make -C tests test32" + os: linux + dist: trusty + sudo: required + addons: + apt: + packages: + - libc6-dev-i386 + - gcc-multilib + - env: Ubu=14.04 Cmd="make gpptest && make clean && make gnu90test && make clean && make c99test && make clean && make gnu99test && make clean && make clangtest && make clean && make -C contrib/pzstd googletest32 @@ -127,16 +143,6 @@ matrix: - g++-4.8 - g++-4.8-multilib - - env: Ubu=14.04 Cmd="make -C tests test32" - os: linux - dist: trusty - sudo: required - addons: - apt: - packages: - - libc6-dev-i386 - - gcc-multilib - - env: Ubu=14.04 Cmd="make gcc5test && make clean && make gcc6test && make clean && make -C tests dll" os: linux dist: trusty @@ -152,12 +158,9 @@ matrix: - gcc-6 - gcc-6-multilib - # Container-based Ubuntu 12.04 LTS Server Edition 64 bit (doesn't support 32-bit includes) - - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install" - os: linux - sudo: false - script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') - # - if [ $JOB_NUMBER -eq 9 ] || [ $JOB_NUMBER -eq 10 ]; then sh -c "$Cmd"; fi - - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "master" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi + # dev => normal tests; other feature branches => short tests (number > 11) + - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi + # master => long tests, as this is the final step towards a Release + - if [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$TRAVIS_BRANCH" = "master" ]; then FUZZERTEST=-T10mn sh -c "$Cmd"; fi diff --git a/tests/Makefile b/tests/Makefile index 15fdc77f9..a4282a450 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -52,8 +52,8 @@ endif VOID = /dev/null ZSTREAM_TESTTIME = -T2mn -FUZZERTEST= -T5mn -ZSTDRTTEST= --test-large-data +FUZZERTEST ?= -T5mn +ZSTDRTTEST = --test-large-data .PHONY: default all all32 dll clean test test32 test-all namespaceTest versionsTest From c4874aab4c64a75ff531c96162c9d1e5be83cc33 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 25 Jan 2017 11:57:28 +0100 Subject: [PATCH 165/227] .travis.yml: different tests for "master" branch --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index dba6ae9aa..ffe759c4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -161,6 +161,6 @@ matrix: script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') # dev => normal tests; other feature branches => short tests (number > 11) - - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi + - if [ "$TRAVIS_BRANCH" != "master" ] && [ "$TRAVIS_BRANCH" = "dev" ] || [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi # master => long tests, as this is the final step towards a Release - - if [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$TRAVIS_BRANCH" = "master" ]; then FUZZERTEST=-T10mn sh -c "$Cmd"; fi + - if [ "$TRAVIS_BRANCH" = "master" ]; then FUZZERTEST=-T10mn sh -c "$Cmd"; fi From 64fa2dbc5ece4054f88b2f2c761336ab37c0f64f Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 25 Jan 2017 13:02:33 +0100 Subject: [PATCH 166/227] Fixed https://github.com/facebook/zstd/issues/232 --- .travis.yml | 2 +- programs/fileio.c | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ffe759c4a..c5a387791 100644 --- a/.travis.yml +++ b/.travis.yml @@ -161,6 +161,6 @@ matrix: script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') # dev => normal tests; other feature branches => short tests (number > 11) - - if [ "$TRAVIS_BRANCH" != "master" ] && [ "$TRAVIS_BRANCH" = "dev" ] || [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi + - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ $JOB_NUMBER -gt 11 ] || [ "$TRAVIS_BRANCH" = "dev" ] && [ "$TRAVIS_BRANCH" != "master" ]; then sh -c "$Cmd"; fi # master => long tests, as this is the final step towards a Release - if [ "$TRAVIS_BRANCH" = "master" ]; then FUZZERTEST=-T10mn sh -c "$Cmd"; fi diff --git a/programs/fileio.c b/programs/fileio.c index a112cc049..9c3c2b7af 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -361,6 +361,11 @@ static int FIO_compressFilename_srcFile(cRess_t ress, DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName); return 1; } + if (!UTIL_doesFileExists(srcFileName)) { + DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", srcFileName); + return 1; + } + ress.srcFile = FIO_openSrcFile(srcFileName); if (!ress.srcFile) return 1; /* srcFile could not be opened */ @@ -719,6 +724,10 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* dstFileName, const ch DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName); return 1; } + if (!UTIL_doesFileExists(srcFileName)) { + DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", srcFileName); + return 1; + } srcFile = FIO_openSrcFile(srcFileName); if (srcFile==0) return 1; From 5022a18d5161f76d709771ff67168b501607578e Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 25 Jan 2017 13:11:26 +0100 Subject: [PATCH 167/227] improved #232 fix --- programs/fileio.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 9c3c2b7af..430cc07eb 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -138,6 +138,10 @@ static FILE* FIO_openSrcFile(const char* srcFileName) f = stdin; SET_BINARY_MODE(stdin); } else { + if (!UTIL_doesFileExists(srcFileName)) { + DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", srcFileName); + return NULL; + } f = fopen(srcFileName, "rb"); if ( f==NULL ) DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno)); } @@ -361,10 +365,6 @@ static int FIO_compressFilename_srcFile(cRess_t ress, DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName); return 1; } - if (!UTIL_doesFileExists(srcFileName)) { - DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", srcFileName); - return 1; - } ress.srcFile = FIO_openSrcFile(srcFileName); if (!ress.srcFile) return 1; /* srcFile could not be opened */ @@ -724,10 +724,6 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* dstFileName, const ch DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName); return 1; } - if (!UTIL_doesFileExists(srcFileName)) { - DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", srcFileName); - return 1; - } srcFile = FIO_openSrcFile(srcFileName); if (srcFile==0) return 1; From 943cff9c37e999c77ccd11cf5dec29a6f010fe3d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 25 Jan 2017 12:31:07 -0800 Subject: [PATCH 168/227] fixed zstdmt cli freeze issue with large nb of threads fileio.c was continually pushing more content without giving a chance to flush compressed one. It would block the job queue when input data was accumulated too fast (requiring to define many threads). Fixed : fileio flushes whatever it can after each input attempt. --- lib/compress/zstdmt_compress.c | 10 +++++++--- programs/fileio.c | 15 +++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 99b2e68fb..04e0adfcd 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -233,6 +233,7 @@ void ZSTDMT_compressChunk(void* jobDescription) DEBUGLOG(3, "job (first:%u) (last:%u) : dictSize %u, srcSize %u", job->firstChunk, job->lastChunk, (U32)job->dictSize, (U32)job->srcSize); if (job->cdict) { size_t const initError = ZSTD_compressBegin_usingCDict(job->cctx, job->cdict, job->fullFrameSize); + if (job->cdict) DEBUGLOG(3, "using CDict "); if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { size_t const initError = ZSTD_compressBegin_advanced(job->cctx, job->srcStart, job->dictSize, job->params, job->fullFrameSize); @@ -296,6 +297,7 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) U32 const minNbJobs = nbThreads + 2; U32 const nbJobsLog2 = ZSTD_highbit32(minNbJobs) + 1; U32 const nbJobs = 1 << nbJobsLog2; + //nbThreads = 1; /* for tests */ DEBUGLOG(5, "nbThreads : %u ; minNbJobs : %u ; nbJobsLog2 : %u ; nbJobs : %u \n", nbThreads, minNbJobs, nbJobsLog2, nbJobs); if ((nbThreads < 1) | (nbThreads > ZSTDMT_NBTHREADS_MAX)) return NULL; @@ -490,6 +492,7 @@ static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, ZSTD_parameters params, unsigned long long pledgedSrcSize) { ZSTD_customMem const cmem = { NULL, NULL, NULL }; + DEBUGLOG(3, "Started new compression, with windowLog : %u", params.cParams.windowLog); if (zcs->nbThreads==1) return ZSTD_initCStream_advanced(zcs->cstream, dict, dictSize, params, pledgedSrcSize); if (zcs->allJobsCompleted == 0) { /* previous job not correctly finished */ ZSTDMT_waitForAllJobsCompleted(zcs); @@ -596,6 +599,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi } else { zcs->inBuff.buffer = g_nullBuffer; zcs->inBuff.filled = 0; + zcs->dictSize = 0; zcs->frameEnded = 1; if (zcs->nextJobID == 0) zcs->params.fParams.checksumFlag = 0; /* single chunk : checksum is calculated directly within worker thread */ @@ -698,12 +702,12 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned endFrame) { - size_t const srcSize = zcs->inBuff.filled; + size_t const srcSize = zcs->inBuff.filled - zcs->dictSize; - if (srcSize) DEBUGLOG(1, "flushing : %u bytes left to compress", (U32)srcSize); + if (srcSize) DEBUGLOG(4, "flushing : %u bytes left to compress", (U32)srcSize); if ( ((srcSize > 0) || (endFrame && !zcs->frameEnded)) && (zcs->nextJobID <= zcs->doneJobID + zcs->jobIDMask) ) { - CHECK_F( ZSTDMT_createCompressionJob(zcs, srcSize - zcs->dictSize, endFrame) ); + CHECK_F( ZSTDMT_createCompressionJob(zcs, srcSize, endFrame) ); } /* check if there is any data available to flush */ diff --git a/programs/fileio.c b/programs/fileio.c index 86db12acb..db2bb55d6 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -349,23 +349,22 @@ static int FIO_compressFilename_internal(cRess_t ress, readsize += inSize; DISPLAYUPDATE(2, "\rRead : %u MB ", (U32)(readsize>>20)); - /* Compress using buffered streaming */ { ZSTD_inBuffer inBuff = { ress.srcBuffer, inSize, 0 }; - ZSTD_outBuffer outBuff= { ress.dstBuffer, ress.dstBufferSize, 0 }; while (inBuff.pos != inBuff.size) { /* note : is there any possibility of endless loop ? for example, if outBuff is not large enough ? */ + ZSTD_outBuffer outBuff= { ress.dstBuffer, ress.dstBufferSize, 0 }; #ifdef ZSTD_MULTITHREAD size_t const result = ZSTDMT_compressStream(ress.cctx, &outBuff, &inBuff); #else size_t const result = ZSTD_compressStream(ress.cctx, &outBuff, &inBuff); #endif if (ZSTD_isError(result)) EXM_THROW(23, "Compression error : %s ", ZSTD_getErrorName(result)); - } - /* Write cBlock */ - { size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile); - if (sizeCheck!=outBuff.pos) EXM_THROW(25, "Write error : cannot write compressed block into %s", dstFileName); } - compressedfilesize += outBuff.pos; - } + /* Write compressed stream */ + if (outBuff.pos) { + size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile); + if (sizeCheck!=outBuff.pos) EXM_THROW(25, "Write error : cannot write compressed block into %s", dstFileName); + compressedfilesize += outBuff.pos; + } } } DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%% ", (U32)(readsize>>20), (double)compressedfilesize/readsize*100); } From bb0027405afb197aff767d1a3d9a759e88d105ed Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 25 Jan 2017 16:25:38 -0800 Subject: [PATCH 169/227] fixed zstdmt corruption issue when enabling overlapped sections see Asana board for detailed explanation on why and how to fix it --- lib/compress/zstd_compress.c | 38 +++++++++++++++++++++++--------- lib/compress/zstdmt_compress.c | 1 + lib/decompress/zstd_decompress.c | 2 +- lib/zstd.h | 8 ++++++- programs/fileio.c | 2 +- tests/zstreamtest.c | 13 ++++++----- 6 files changed, 46 insertions(+), 18 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 3c69a1ae0..95c7e1a7a 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -60,7 +60,8 @@ struct ZSTD_CCtx_s { U32 nextToUpdate; /* index from which to continue dictionary update */ U32 nextToUpdate3; /* index from which to continue dictionary update */ U32 hashLog3; /* dispatch table : larger == faster, more memory */ - U32 loadedDictEnd; + U32 loadedDictEnd; /* index of end of dictionary */ + U32 forceWindow; /* force back-references to respect limit of 1<customMem), &customMem, sizeof(customMem)); + cctx->customMem = customMem; return cctx; } @@ -118,6 +119,15 @@ size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx) return sizeof(*cctx) + cctx->workSpaceSize; } +size_t ZSTD_setCCtxParameter(ZSTD_CCtx* cctx, ZSTD_CCtxParameter param, unsigned value) +{ + switch(param) + { + case ZSTD_p_forceWindow : cctx->forceWindow = value; return 0; + default: return ERROR(parameter_unknown); + } +} + const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) /* hidden interface */ { return &(ctx->seqStore); @@ -748,6 +758,13 @@ _check_compressibility: } +#if 0 /* for debug */ +# define STORESEQ_DEBUG +#include /* fprintf */ +U32 g_startDebug = 0; +const BYTE* g_start = NULL; +#endif + /*! ZSTD_storeSeq() : Store a sequence (literal length, literals, offset code and match length code) into seqStore_t. `offsetCode` : distance to match, or 0 == repCode. @@ -755,13 +772,14 @@ _check_compressibility: */ MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const void* literals, U32 offsetCode, size_t matchCode) { -#if 0 /* for debug */ - static const BYTE* g_start = NULL; - const U32 pos = (U32)((const BYTE*)literals - g_start); - if (g_start==NULL) g_start = (const BYTE*)literals; - //if ((pos > 1) && (pos < 50000)) - printf("Cpos %6u :%5u literals & match %3u bytes at distance %6u \n", - pos, (U32)litLength, (U32)matchCode+MINMATCH, (U32)offsetCode); +#ifdef STORESEQ_DEBUG + if (g_startDebug) { + const U32 pos = (U32)((const BYTE*)literals - g_start); + if (g_start==NULL) g_start = (const BYTE*)literals; + if ((pos > 1895000) && (pos < 1895300)) + fprintf(stderr, "Cpos %6u :%5u literals & match %3u bytes at distance %6u \n", + pos, (U32)litLength, (U32)matchCode+MINMATCH, (U32)offsetCode); + } #endif /* copy Literals */ ZSTD_wildcopy(seqStorePtr->lit, literals, litLength); @@ -2305,7 +2323,7 @@ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, else cctx->nextToUpdate -= correction; } - if ((U32)(ip+blockSize - cctx->base) > cctx->loadedDictEnd + maxDist) { + if ((U32)(ip+blockSize - cctx->base) > (cctx->forceWindow ? 0 : cctx->loadedDictEnd) + maxDist) { /* enforce maxDist */ U32 const newLowLimit = (U32)(ip+blockSize - cctx->base) - maxDist; if (cctx->lowLimit < newLowLimit) cctx->lowLimit = newLowLimit; diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 04e0adfcd..988e133d6 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -238,6 +238,7 @@ void ZSTDMT_compressChunk(void* jobDescription) } else { size_t const initError = ZSTD_compressBegin_advanced(job->cctx, job->srcStart, job->dictSize, job->params, job->fullFrameSize); if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } + ZSTD_setCCtxParameter(job->cctx, ZSTD_p_forceWindow, 1); } if (!job->firstChunk) { /* flush frame header */ size_t const hSize = ZSTD_compressContinue(job->cctx, dstBuff.start, dstBuff.size, src, 0); diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index c53f3c3d1..9c04503d2 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1973,7 +1973,7 @@ size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, switch(paramType) { default : return ERROR(parameter_unknown); - case ZSTDdsp_maxWindowSize : zds->maxWindowSize = paramValue ? paramValue : (U32)(-1); break; + case DStream_p_maxWindowSize : zds->maxWindowSize = paramValue ? paramValue : (U32)(-1); break; } return 0; } diff --git a/lib/zstd.h b/lib/zstd.h index 52d65206c..7a0aa3304 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -401,6 +401,12 @@ ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem); * Gives the amount of memory used by a given ZSTD_CCtx */ ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx); +/*! ZSTD_setCCtxParameter() : + * Set advanced parameters, selected through enum ZSTD_CCtxParameter + * @result : 0, or an error code (which can be tested with ZSTD_isError()) */ +typedef enum { ZSTD_p_forceWindow } ZSTD_CCtxParameter; +size_t ZSTD_setCCtxParameter(ZSTD_CCtx* cctx, ZSTD_CCtxParameter param, unsigned value); + /*! ZSTD_createCDict_byReference() : * Create a digested dictionary for compression * Dictionary content is simply referenced, and therefore stays in dictBuffer. @@ -519,7 +525,7 @@ ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs); /*===== Advanced Streaming decompression functions =====*/ -typedef enum { ZSTDdsp_maxWindowSize } ZSTD_DStreamParameter_e; +typedef enum { DStream_p_maxWindowSize } ZSTD_DStreamParameter_e; ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem); ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: a dict will not be used if dict == NULL or dictSize < 8 */ ZSTDLIB_API size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue); diff --git a/programs/fileio.c b/programs/fileio.c index db2bb55d6..f18e418af 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -530,7 +530,7 @@ static dRess_t FIO_createDResources(const char* dictFileName) /* Allocation */ ress.dctx = ZSTD_createDStream(); if (ress.dctx==NULL) EXM_THROW(60, "Can't create ZSTD_DStream"); - ZSTD_setDStreamParameter(ress.dctx, ZSTDdsp_maxWindowSize, g_memLimit); + ZSTD_setDStreamParameter(ress.dctx, DStream_p_maxWindowSize, g_memLimit); ress.srcBufferSize = ZSTD_DStreamInSize(); ress.srcBuffer = malloc(ress.srcBufferSize); ress.dstBufferSize = ZSTD_DStreamOutSize(); diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 9efba323c..2cb6d65e0 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -225,7 +225,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo inBuff2 = inBuff; DISPLAYLEVEL(3, "test%3i : decompress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB); - { size_t const r = ZSTD_setDStreamParameter(zd, ZSTDdsp_maxWindowSize, 1000000000); /* large limit */ + { size_t const r = ZSTD_setDStreamParameter(zd, DStream_p_maxWindowSize, 1000000000); /* large limit */ if (ZSTD_isError(r)) goto _output_error; } { size_t const remaining = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (remaining != 0) goto _output_error; } /* should reach end of frame == 0; otherwise, some data left, or an error */ @@ -426,7 +426,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo /* Memory restriction */ DISPLAYLEVEL(3, "test%3i : maxWindowSize < frame requirement : ", testNb++); ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB); - { size_t const r = ZSTD_setDStreamParameter(zd, ZSTDdsp_maxWindowSize, 1000); /* too small limit */ + { size_t const r = ZSTD_setDStreamParameter(zd, DStream_p_maxWindowSize, 1000); /* too small limit */ if (ZSTD_isError(r)) goto _output_error; } inBuff.src = compressedBuffer; inBuff.size = cSize; @@ -466,6 +466,10 @@ static size_t findDiff(const void* buf1, const void* buf2, size_t max) if (b1[u] != b2[u]) break; } DISPLAY("Error at position %u / %u \n", (U32)u, (U32)max); + DISPLAY(" %02X %02X %02X :%02X: %02X %02X %02X %02X %02X \n", + b1[u-3], b1[u-2], b1[u-1], b1[u-0], b1[u+1], b1[u+2], b1[u+3], b1[u+4], b1[u+5]); + DISPLAY(" %02X %02X %02X :%02X: %02X %02X %02X %02X %02X \n", + b2[u-3], b2[u-2], b2[u-1], b2[u-0], b2[u+1], b2[u+2], b2[u+3], b2[u+4], b2[u+5]); return u; } @@ -902,9 +906,8 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff); CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult)); } - CHECK (decompressionResult != 0, "frame not fully decoded"); - CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size") - CHECK (inBuff.pos != cSize, "compressed data should be fully read") + CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size (%u != %u)", (U32)outBuff.pos, (U32)totalTestSize); + CHECK (inBuff.pos != cSize, "compressed data should be fully read (%u != %u)", (U32)inBuff.pos, (U32)cSize); { U64 const crcDest = XXH64(dstBuffer, totalTestSize, 0); if (crcDest!=crcOrig) findDiff(copyBuffer, dstBuffer, totalTestSize); CHECK (crcDest!=crcOrig, "decompressed data corrupted"); From 06e7697f964e486b062b92e0656749dc54b73d47 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 25 Jan 2017 16:39:03 -0800 Subject: [PATCH 170/227] added test of new parameter ZSTD_p_forceWindow --- lib/compress/zstd_compress.c | 6 +++--- lib/compress/zstdmt_compress.h | 5 +++-- lib/zstd.h | 4 +++- tests/fuzzer.c | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 95c7e1a7a..b6cf37646 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -123,7 +123,7 @@ size_t ZSTD_setCCtxParameter(ZSTD_CCtx* cctx, ZSTD_CCtxParameter param, unsigned { switch(param) { - case ZSTD_p_forceWindow : cctx->forceWindow = value; return 0; + case ZSTD_p_forceWindow : cctx->forceWindow = value>0; cctx->loadedDictEnd = 0; return 0; default: return ERROR(parameter_unknown); } } @@ -2323,7 +2323,7 @@ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, else cctx->nextToUpdate -= correction; } - if ((U32)(ip+blockSize - cctx->base) > (cctx->forceWindow ? 0 : cctx->loadedDictEnd) + maxDist) { + if ((U32)(ip+blockSize - cctx->base) > cctx->loadedDictEnd + maxDist) { /* enforce maxDist */ U32 const newLowLimit = (U32)(ip+blockSize - cctx->base) - maxDist; if (cctx->lowLimit < newLowLimit) cctx->lowLimit = newLowLimit; @@ -2477,7 +2477,7 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_CCtx* zc, const void* src, size_t zc->dictBase = zc->base; zc->base += ip - zc->nextSrc; zc->nextToUpdate = zc->dictLimit; - zc->loadedDictEnd = (U32)(iend - zc->base); + zc->loadedDictEnd = zc->forceWindow ? 0 : (U32)(iend - zc->base); zc->nextSrc = iend; if (srcSize <= HASH_READ_SIZE) return 0; diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 1288c1ed0..4757e3e06 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -51,8 +51,9 @@ ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, const void* d /* ZSDTMT_parameter : * List of parameters that can be set using ZSTDMT_setMTCtxParameter() */ -typedef enum { ZSTDMT_p_sectionSize /* size of input "section". Each section is compressed in parallel. 0 means default, which is dynamically determined within compression functions */ - } ZSDTMT_parameter; +typedef enum { + ZSTDMT_p_sectionSize /* size of input "section". Each section is compressed in parallel. 0 means default, which is dynamically determined within compression functions */ +} ZSDTMT_parameter; /* ZSTDMT_setMTCtxParameter() : * allow setting individual parameters, one at a time, among a list of enums defined in ZSTDMT_parameter. diff --git a/lib/zstd.h b/lib/zstd.h index 7a0aa3304..8325710b7 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -404,7 +404,9 @@ ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx); /*! ZSTD_setCCtxParameter() : * Set advanced parameters, selected through enum ZSTD_CCtxParameter * @result : 0, or an error code (which can be tested with ZSTD_isError()) */ -typedef enum { ZSTD_p_forceWindow } ZSTD_CCtxParameter; +typedef enum { + ZSTD_p_forceWindow /* Force back-references to remain < windowSize, even when referencing Dictionary content (default:0)*/ +} ZSTD_CCtxParameter; size_t ZSTD_setCCtxParameter(ZSTD_CCtx* cctx, ZSTD_CCtxParameter param, unsigned value); /*! ZSTD_createCDict_byReference() : diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 00cfb0574..60546c07a 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -755,6 +755,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD CHECK (ZSTD_isError(errorCode), "ZSTD_copyCCtx error : %s", ZSTD_getErrorName(errorCode)); } } XXH64_reset(&xxhState, 0); + ZSTD_setCCtxParameter(ctx, ZSTD_p_forceWindow, FUZ_rand(&lseed) & 1); { U32 const nbChunks = (FUZ_rand(&lseed) & 127) + 2; U32 n; for (totalTestSize=0, cSize=0, n=0 ; n Date: Wed, 25 Jan 2017 16:41:52 -0800 Subject: [PATCH 171/227] Updated format specification to be easier to understand --- doc/zstd_compression_format.md | 136 +++++++++++++++++++++------------ 1 file changed, 88 insertions(+), 48 deletions(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index b48b39104..03a970c16 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -57,7 +57,6 @@ Whenever it does not support a parameter defined in the compressed stream, it must produce a non-ambiguous error code and associated error message explaining which parameter is unsupported. - Overall conventions ----------- In this document: @@ -267,7 +266,7 @@ The `Window_Descriptor` byte is optional. It is absent when `Single_Segment_flag In this case, the maximum back-reference distance is the content size itself, which can be any value from 1 to 2^64-1 bytes (16 EB). -| Bit numbers | 7-3 | 0-2 | +| Bit numbers | 7-3 | 2-0 | | ----------- | ---------- | ---------- | | Field name | `Exponent` | `Mantissa` | @@ -381,7 +380,7 @@ There are 4 block types : This value cannot be used with current version of this specification. Block sizes must respect a few rules : -- In compressed mode, compressed size if always strictly `< decompressed size`. +- In compressed mode, `compressed size` is always strictly `< decompressed size`. - Block decompressed size is always <= maximum back-reference distance . - Block decompressed size is always <= 128 KB @@ -478,7 +477,7 @@ For values spanning several bytes, convention is little-endian. __`Size_Format` for `Raw_Literals_Block` and `RLE_Literals_Block`__ : -- Value x0 : `Regenerated_Size` uses 5 bits (0-31). +- Value X0 : `Size_Format` uses 1 bit, `Regenerated_Size` uses 5 bits (0-31). `Literals_Section_Header` has 1 byte. `Regenerated_Size = Header[0]>>3` - Value 01 : `Regenerated_Size` uses 12 bits (0-4095). @@ -507,7 +506,8 @@ __`Size_Format` for `Compressed_Literals_Block` and `Repeat_Stats_Literals_Block `Literals_Section_Header` has 5 bytes. Both `Compressed_Size` and `Regenerated_Size` fields follow little-endian convention. - +Note: `Compressed_Size` __includes__ the size of the Huffman Tree description if it +is present. #### `Huffman_Tree_Description` @@ -550,23 +550,24 @@ Let's presume the following Huffman tree must be described : | `Number_of_Bits` | 1 | 2 | 3 | 0 | 4 | 4 | The tree depth is 4, since its smallest element uses 4 bits. -Value `5` will not be listed, nor will values above `5`. +Value `5` will not be listed as it can be determined from the values for 0-4, +nor will values above `5` as they are all 0. Values from `0` to `4` will be listed using `Weight` instead of `Number_of_Bits`. Weight formula is : ``` Weight = Number_of_Bits ? (Max_Number_of_Bits + 1 - Number_of_Bits) : 0 ``` -It gives the following serie of weights : +It gives the following series of weights : -| `Weight` | 4 | 3 | 2 | 0 | 1 | -| -------- | --- | --- | --- | --- | --- | | literal | 0 | 1 | 2 | 3 | 4 | +| -------- | --- | --- | --- | --- | --- | +| `Weight` | 4 | 3 | 2 | 0 | 1 | The decoder will do the inverse operation : having collected weights of literals from `0` to `4`, it knows the last literal, `5`, is present with a non-zero weight. -The weight of `5` can be deducted by joining to the nearest power of 2. -Sum of `2^(Weight-1)` (excluding 0) is : +The weight of `5` can be determined by advancing to the next power of 2. +The sum of `2^(Weight-1)` (excluding 0's) is : `8 + 4 + 2 + 0 + 1 = 15`. Nearest power of 2 is 16. Therefore, `Max_Number_of_Bits = 4` and `Weight[5] = 1`. @@ -574,23 +575,38 @@ Therefore, `Max_Number_of_Bits = 4` and `Weight[5] = 1`. ##### Huffman Tree header This is a single byte value (0-255), -which tells how to decode the list of weights. +which describes how to decode the list of weights. - if `headerByte` >= 128 : this is a direct representation, where each `Weight` is written directly as a 4 bits field (0-15). + They are encoded forward, 2 weights to a byte with the first weight taking + the top 4 bits and the second taking the bottom two (e.g. + `Weight[0] = (Byte[0] >> 4), Weight[1] = (Byte[0] & 0xf)`, etc.). The full representation occupies `((Number_of_Symbols+1)/2)` bytes, meaning it uses a last full byte even if `Number_of_Symbols` is odd. `Number_of_Symbols = headerByte - 127`. Note that maximum `Number_of_Symbols` is 255-127 = 128. - A larger serie must necessarily use FSE compression. + A larger series must necessarily use FSE compression. - if `headerByte` < 128 : - the serie of weights is compressed by FSE. - The length of the FSE-compressed serie is equal to `headerByte` (0-127). + the series of weights is compressed by FSE. + The length of the FSE-compressed series is equal to `headerByte` (0-127). ##### Finite State Entropy (FSE) compression of Huffman weights -The serie of weights is compressed using FSE compression. +FSE decoding uses three operations: `Init_State`, `Decode_Symbol`, and `Update_State`. +`Init_State` reads in the initial state value from a bitstream, +`Decode_Symbol` outputs a symbol based on the current state, +and `Update_State` goes to a new state based on the current state and some number of consumed bits. + +FSE streams must be read in reverse from the order they're encoded in, +so bitstreams start at a certain offset and works backwards towards their base. + +For more on how FSE bitstreams work, see [Finite State Entropy]. + +[Finite State Entropy]:https://github.com/Cyan4973/FiniteStateEntropy/ + +The series of Huffman weights is compressed using FSE compression. It's a single bitstream with 2 interleaved states, sharing a single distribution table. @@ -598,22 +614,27 @@ To decode an FSE bitstream, it is necessary to know its compressed size. Compressed size is provided by `headerByte`. It's also necessary to know its _maximum possible_ decompressed size, which is `255`, since literal values span from `0` to `255`, -and last symbol value is not represented. +and last symbol's weight is not represented. An FSE bitstream starts by a header, describing probabilities distribution. It will create a Decoding Table. -Table must be pre-allocated, which requires to support a maximum accuracy. +The table must be pre-allocated, so a maximum accuracy must be fixed. For a list of Huffman weights, maximum accuracy is 7 bits. -FSE header is [described in relevant chapter](#fse-distribution-table--condensed-format), -and so is [FSE bitstream](#bitstream). +The FSE header format is [described in a relevant chapter](#fse-distribution-table--condensed-format), +as well as the [FSE bitstream](#bitstream). The main difference is that Huffman header compression uses 2 states, which share the same FSE distribution table. -Bitstream contains only FSE symbols (no interleaved "raw bitfields"). -The number of symbols to decode is discovered -by tracking bitStream overflow condition. -When both states have overflowed the bitstream, end is reached. +The first state (`State1`) encodes the even indexed symbols, +and the second (`State2`) encodes the odd indexes. +State1 is initialized first, and then State2, and they take turns decoding +a single symbol and updating their state. +The number of symbols to decode is determined +by tracking bitStream overflow condition: +If updating state after decoding a symbol would require more bits than +remain in the stream, it is assumed the extra bits are 0. Then, +the symbols for each of the final states are decoded and the process is complete. ##### Conversion from weights to Huffman prefix codes @@ -687,9 +708,20 @@ Consequently, a last byte of `0` is not possible. And the final-bit-flag itself is not part of the useful bitstream. Hence, the last byte contains between 0 and 7 useful bits. +For example, if the literal sequence "0145" was encoded using the prefix codes above, +it would be encoded as: +``` +00000001 01110000 +``` + +|Symbol | 5 | 4 | 1 | 0 | Padding | +|--------|------|------|----|---|---------| +|Encoding|`0000`|`0001`|`01`|`1`| `10000` | + Starting from the end, it's possible to read the bitstream in a little-endian fashion, -keeping track of already used bits. +keeping track of already used bits. Since the bitstream is encoded in reverse +order, by starting at the end the symbols can be read in forward order. Reading the last `Max_Number_of_Bits` bits, it's then possible to compare extracted value to decoding table, @@ -700,7 +732,6 @@ If a bitstream is not entirely and exactly consumed, hence reaching exactly its beginning position with _all_ bits consumed, the decoding process is considered faulty. - ### `Sequences_Section` A compressed block is a succession of _sequences_ . @@ -712,7 +743,7 @@ The offset gives the position to copy from, which can be within a previous block. When all _sequences_ are decoded, -if there is any literal left in the _literal section_, +if there is are any literals left in the _literal section_, these bytes are added at the end of the block. The `Sequences_Section` regroup all symbols required to decode commands. @@ -810,7 +841,7 @@ They define lengths from 0 to 131071 bytes. When `Compression_Mode` is `Predefined_Mode`, a predefined distribution is used for FSE compression. -Below is its definition. It uses an accuracy of 6 bits (64 states). +Its definition is below. It uses an accuracy of 6 bits (64 states). ``` short literalsLength_defaultDistribution[36] = { 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, @@ -835,12 +866,12 @@ They define lengths from 3 to 131074 bytes. | `Match_Length_Code` | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | | ------------------- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | -| `Baseline` | 67 | 83 | 99 | 131 | 258 | 514 | 1026 | 2050 | +| `Baseline` | 67 | 83 | 99 | 131 | 259 | 515 | 1027 | 2051 | | `Number_of_Bits` | 4 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | | `Match_Length_Code` | 48 | 49 | 50 | 51 | 52 | | ------------------- | ---- | ---- | ---- | ---- | ---- | -| `Baseline` | 4098 | 8194 |16486 |32770 |65538 | +| `Baseline` | 4099 | 8195 |16387 |32771 |65539 | | `Number_of_Bits` | 12 | 13 | 14 | 15 | 16 | ##### Default distribution for match length codes @@ -848,7 +879,7 @@ They define lengths from 3 to 131074 bytes. When `Compression_Mode` is defined as `Predefined_Mode`, a predefined distribution is used for FSE compression. -Below is its definition. It uses an accuracy of 6 bits (64 states). +Its definition is below. It uses an accuracy of 6 bits (64 states). ``` short matchLengths_defaultDistribution[53] = { 1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, @@ -908,7 +939,7 @@ When present, they are in this order : - Match Lengths The content to decode depends on their respective encoding mode : -- `Predefined_Mode` : no content. Use predefined distribution table. +- `Predefined_Mode` : no content. Use the predefined distribution table. - `RLE_Mode` : 1 byte. This is the only code to use across the whole compressed block. - `FSE_Compressed_Mode` : A distribution table is present. - `Repeat_Mode` : no content. Re-use distribution from previous compressed block. @@ -936,12 +967,12 @@ It depends on : __example__ : Presuming an `Accuracy_Log` of 8, and presuming 100 probabilities points have already been distributed, - the decoder may read any value from `0` to `255 - 100 + 1 == 156` (included). + the decoder may read any value from `0` to `255 - 100 + 1 == 156` (inclusive). Therefore, it must read `log2sup(156) == 8` bits. - Value decoded : small values use 1 less bit : __example__ : - Presuming values from 0 to 156 (included) are possible, + Presuming values from 0 to 156 (inclusive) are possible, 255-156 = 99 values are remaining in an 8-bits field. They are used this way : first 99 values (hence from 0 to 98) use only 7 bits, @@ -967,7 +998,7 @@ For the purpose of calculating cumulated distribution, it counts as one. [next paragraph]:#fse-decoding--from-normalized-distribution-to-decoding-tables -When a symbol has a probability of `zero`, +When a symbol has a __probability__ of `zero`, it is followed by a 2-bits repeat flag. This repeat flag tells how many probabilities of zeroes follow the current one. It provides a number ranging from 0 to 3. @@ -1012,6 +1043,9 @@ position &= tableSize-1; A position is skipped if already occupied, typically by a "less than 1" probability symbol. +`position` does not reset between symbols, it simply iterates through +each position in the table, switching to the next symbol when enough +states have been allocated to the current one. The result is a list of state values. Each state will decode the current symbol. @@ -1043,7 +1077,7 @@ Numbering starts from higher states using less bits. | `Baseline` | 32 | 64 | 96 | 0 | 16 | | range | 32-63 | 64-95 | 96-127 | 0-15 | 16-31 | -Next state is determined from current state +The next state is determined from current state by reading the required `Number_of_Bits`, and adding the specified `Baseline`. @@ -1093,15 +1127,16 @@ and then for `Literals_Length`. It starts by inserting the number of literals defined by `Literals_Length`, then continue by copying `Match_Length` bytes from `currentPos - Offset`. -The next operation is to update states. -Using rules pre-calculated in the decoding tables, +If it is not the last sequence in the block, +the next operation is to update states. +Using the rules pre-calculated in the decoding tables, `Literals_Length_State` is updated, followed by `Match_Length_State`, and then `Offset_State`. This operation will be repeated `Number_of_Sequences` times. At the end, the bitstream shall be entirely consumed, -otherwise bitstream is considered corrupted. +otherwise the bitstream is considered corrupted. [Symbol Decoding]:#the-codes-for-literals-lengths-match-lengths-and-offsets @@ -1111,13 +1146,13 @@ As seen in [Offset Codes], the first 3 values define a repeated offset and we wi They are sorted in recency order, with `Repeated_Offset1` meaning "most recent one". There is an exception though, when current sequence's literals length is `0`. -In which case, repeated offsets are "pushed by one", +In this case, repeated offsets are shifted by one, so `Repeated_Offset1` becomes `Repeated_Offset2`, `Repeated_Offset2` becomes `Repeated_Offset3`, and `Repeated_Offset3` becomes `Repeated_Offset1 - 1_byte`. -On first block, offset history is populated by the following values : 1, 4 and 8 (in order). +In the first block, the offset history is populated with the following values : 1, 4 and 8 (in order). -Then each block receives its start value from previous compressed block. +Then each block gets its starting offset history from the ending values of the most recent compressed block. Note that non-compressed blocks are skipped, they do not contribute to offset history. @@ -1125,11 +1160,12 @@ they do not contribute to offset history. ###### Offset updates rules -New offset take the lead in offset history, -up to its previous place if it was already present. +The newest offset takes the lead in offset history, +shifting others back (up to its previous place if it was already present). -It means that when `Repeated_Offset1` (most recent) is used, history is unmodified. +This means that when `Repeated_Offset1` (most recent) is used, history is unmodified. When `Repeated_Offset2` is used, it's swapped with `Repeated_Offset1`. +If any other offset is used, it becomes `Repeated_Offset1` and the rest are shift back by one. Dictionary format @@ -1137,6 +1173,9 @@ Dictionary format `zstd` is compatible with "raw content" dictionaries, free of any format restriction, except that they must be at least 8 bytes. +These dictionaries function as if they were just the `Content` block of a formatted +dictionary. + But dictionaries created by `zstd --train` follow a format, described here. __Pre-requisites__ : a dictionary has a size, @@ -1160,16 +1199,17 @@ _Reserved ranges :_ - low range : 1 - 32767 - high range : >= (2^31) -__`Entropy_Tables`__ : following the same format as a [compressed blocks]. +__`Entropy_Tables`__ : following the same format as the tables in [compressed blocks]. They are stored in following order : Huffman tables for literals, FSE table for offsets, FSE table for match lengths, and FSE table for literals lengths. - It's finally followed by 3 offset values, populating recent offsets, + It's finally followed by 3 offset values, populating recent offsets (instead of using `{1,4,8}`), stored in order, 4-bytes little-endian each, for a total of 12 bytes. Each recent offset must have a value < dictionary size. __`Content`__ : The rest of the dictionary is its content. - The content act as a "past" in front of data to compress or decompress. + The content act as a "past" in front of data to compress or decompress, + so it can be referenced in sequence commands. [compressed blocks]: #the-format-of-compressed_block From 8dafb1acf5efb4e8099e16690b3588b1b9488097 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 25 Jan 2017 17:01:13 -0800 Subject: [PATCH 172/227] CLI : automatically set overlap size to max (windowSize) for max compression level --- lib/compress/zstdmt_compress.c | 10 ++++++---- lib/compress/zstdmt_compress.h | 3 ++- programs/fileio.c | 6 +++++- programs/fileio.h | 3 ++- programs/zstdcli.c | 1 + tests/zstreamtest.c | 4 ++-- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 988e133d6..5f0bf2ab1 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -285,6 +285,7 @@ struct ZSTDMT_CCtx_s { unsigned nextJobID; unsigned frameEnded; unsigned allJobsCompleted; + unsigned overlapWrLog; unsigned long long frameContentSize; size_t sectionSize; ZSTD_CDict* cdict; @@ -298,7 +299,6 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) U32 const minNbJobs = nbThreads + 2; U32 const nbJobsLog2 = ZSTD_highbit32(minNbJobs) + 1; U32 const nbJobs = 1 << nbJobsLog2; - //nbThreads = 1; /* for tests */ DEBUGLOG(5, "nbThreads : %u ; minNbJobs : %u ; nbJobsLog2 : %u ; nbJobs : %u \n", nbThreads, minNbJobs, nbJobsLog2, nbJobs); if ((nbThreads < 1) | (nbThreads > ZSTDMT_NBTHREADS_MAX)) return NULL; @@ -308,6 +308,7 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) cctx->jobIDMask = nbJobs - 1; cctx->allJobsCompleted = 1; cctx->sectionSize = 0; + cctx->overlapWrLog = 3; cctx->factory = POOL_create(nbThreads, 1); cctx->buffPool = ZSTDMT_createBufferPool(nbThreads); cctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads); @@ -367,6 +368,9 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, case ZSTDMT_p_sectionSize : mtctx->sectionSize = value; return 0; + case ZSTDMT_p_overlapSectionRLog : + mtctx->overlapWrLog = value; + return 0; default : return ERROR(compressionParameter_unsupported); } @@ -510,9 +514,7 @@ static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, zcs->frameContentSize = pledgedSrcSize; zcs->targetSectionSize = zcs->sectionSize ? zcs->sectionSize : (size_t)1 << (zcs->params.cParams.windowLog + 2); zcs->targetSectionSize = MAX(ZSTDMT_SECTION_SIZE_MIN, zcs->targetSectionSize); - //zcs->targetDictSize = ((size_t)1 << zcs->params.cParams.windowLog); /* full window size, for test */ - zcs->targetDictSize = ((size_t)1 << zcs->params.cParams.windowLog) >> 3; /* fixed currently */ - //zcs->targetDictSize = 0; + zcs->targetDictSize = zcs->overlapWrLog < 10 ? (size_t)1 << (zcs->params.cParams.windowLog - zcs->overlapWrLog) : 0; zcs->inBuffSize = zcs->targetSectionSize + ((size_t)1 << zcs->params.cParams.windowLog) /* margin */ + zcs->targetDictSize; zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); if (zcs->inBuff.buffer.start == NULL) return ERROR(memory_allocation); diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 4757e3e06..92de52d65 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -52,7 +52,8 @@ ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, const void* d /* ZSDTMT_parameter : * List of parameters that can be set using ZSTDMT_setMTCtxParameter() */ typedef enum { - ZSTDMT_p_sectionSize /* size of input "section". Each section is compressed in parallel. 0 means default, which is dynamically determined within compression functions */ + ZSTDMT_p_sectionSize, /* size of input "section". Each section is compressed in parallel. 0 means default, which is dynamically determined within compression functions */ + ZSTDMT_p_overlapSectionRLog /* reverse log of overlapped section; 0 == use a complete window, 3(default) == use 1/8th of window, values >=10 means no overlap */ } ZSDTMT_parameter; /* ZSTDMT_setMTCtxParameter() : diff --git a/programs/fileio.c b/programs/fileio.c index f18e418af..ac7dffb31 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ + /* ************************************* * Compiler Options ***************************************/ @@ -266,10 +267,13 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, #ifdef ZSTD_MULTITHREAD ress.cctx = ZSTDMT_createCCtx(g_nbThreads); + if (ress.cctx == NULL) EXM_THROW(30, "zstd: allocation error : can't create ZSTD_CStream"); + if (cLevel==ZSTD_maxCLevel()) + ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_overlapSectionRLog, 0); /* use complete window for overlap */ #else ress.cctx = ZSTD_createCStream(); -#endif if (ress.cctx == NULL) EXM_THROW(30, "zstd: allocation error : can't create ZSTD_CStream"); +#endif ress.srcBufferSize = ZSTD_CStreamInSize(); ress.srcBuffer = malloc(ress.srcBufferSize); ress.dstBufferSize = ZSTD_CStreamOutSize(); diff --git a/programs/fileio.h b/programs/fileio.h index 19f09c33a..11178bcca 100644 --- a/programs/fileio.h +++ b/programs/fileio.h @@ -12,12 +12,13 @@ #define FILEIO_H_23981798732 #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressionParameters */ -#include "zstd.h" /* ZSTD_compressionParameters */ +#include "zstd.h" /* ZSTD_* */ #if defined (__cplusplus) extern "C" { #endif + /* ************************************* * Special i/o constants **************************************/ diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 549dad01a..64f2c919c 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -20,6 +20,7 @@ #endif + /*-************************************ * Dependencies **************************************/ diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 2cb6d65e0..bef8734c7 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -992,8 +992,8 @@ int main(int argc, const char** argv) int mainPause = 0; int mtOnly = 0; const char* const programName = argv[0]; - ZSTD_customMem customMem = { allocFunction, freeFunction, NULL }; - ZSTD_customMem customNULL = { NULL, NULL, NULL }; + ZSTD_customMem const customMem = { allocFunction, freeFunction, NULL }; + ZSTD_customMem const customNULL = { NULL, NULL, NULL }; /* Check command line */ for(argNb=1; argNb Date: Thu, 26 Jan 2017 09:16:56 -0800 Subject: [PATCH 173/227] fixed clang documentation warning --- lib/zstd.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/zstd.h b/lib/zstd.h index 8325710b7..5c80bbac5 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -401,12 +401,12 @@ ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem); * Gives the amount of memory used by a given ZSTD_CCtx */ ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx); -/*! ZSTD_setCCtxParameter() : - * Set advanced parameters, selected through enum ZSTD_CCtxParameter - * @result : 0, or an error code (which can be tested with ZSTD_isError()) */ typedef enum { ZSTD_p_forceWindow /* Force back-references to remain < windowSize, even when referencing Dictionary content (default:0)*/ } ZSTD_CCtxParameter; +/*! ZSTD_setCCtxParameter() : + * Set advanced parameters, selected through enum ZSTD_CCtxParameter + * @result : 0, or an error code (which can be tested with ZSTD_isError()) */ size_t ZSTD_setCCtxParameter(ZSTD_CCtx* cctx, ZSTD_CCtxParameter param, unsigned value); /*! ZSTD_createCDict_byReference() : From 81c96702260fe5068622ea87a0f29e5d626d22a7 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 26 Jan 2017 11:15:34 -0800 Subject: [PATCH 174/227] Fixed commented issues --- doc/zstd_compression_format.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index 03a970c16..eb5c1ca7e 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -380,9 +380,9 @@ There are 4 block types : This value cannot be used with current version of this specification. Block sizes must respect a few rules : -- In compressed mode, `compressed size` is always strictly `< decompressed size`. -- Block decompressed size is always <= maximum back-reference distance . -- Block decompressed size is always <= 128 KB +- In compressed mode, compressed size is always strictly less than decompressed size. +- Block decompressed size is always <= maximum back-reference distance. +- Block decompressed size is always <= 128 KB. __`Block_Content`__ @@ -580,7 +580,7 @@ which describes how to decode the list of weights. - if `headerByte` >= 128 : this is a direct representation, where each `Weight` is written directly as a 4 bits field (0-15). They are encoded forward, 2 weights to a byte with the first weight taking - the top 4 bits and the second taking the bottom two (e.g. + the top four bits and the second taking the bottom four (e.g. `Weight[0] = (Byte[0] >> 4), Weight[1] = (Byte[0] & 0xf)`, etc.). The full representation occupies `((Number_of_Symbols+1)/2)` bytes, meaning it uses a last full byte even if `Number_of_Symbols` is odd. From ef33d005329aca32183888c8334b85fced9e5caf Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 26 Jan 2017 12:24:21 -0800 Subject: [PATCH 175/227] fixed : ZSTD_setCCtxParameter() properly exposed in DLL --- lib/zstd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zstd.h b/lib/zstd.h index 5c80bbac5..f5cbf4b48 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -407,7 +407,7 @@ typedef enum { /*! ZSTD_setCCtxParameter() : * Set advanced parameters, selected through enum ZSTD_CCtxParameter * @result : 0, or an error code (which can be tested with ZSTD_isError()) */ -size_t ZSTD_setCCtxParameter(ZSTD_CCtx* cctx, ZSTD_CCtxParameter param, unsigned value); +ZSTDLIB_API size_t ZSTD_setCCtxParameter(ZSTD_CCtx* cctx, ZSTD_CCtxParameter param, unsigned value); /*! ZSTD_createCDict_byReference() : * Create a digested dictionary for compression From 83c387eb8eedac6f9b6aa1030759492228ef1b08 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 26 Jan 2017 15:25:32 -0800 Subject: [PATCH 176/227] Fix zstdmt_compress.h include --- programs/bench.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 1ca40d6b9..dcb23b1f2 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -40,6 +40,7 @@ #include "zstd.h" #include "datagen.h" /* RDG_genBuffer */ #include "xxhash.h" +#include "zstdmt_compress.h" /* ************************************* @@ -148,8 +149,6 @@ typedef struct { #define MIN(a,b) ((a)<(b) ? (a) : (b)) #define MAX(a,b) ((a)>(b) ? (a) : (b)) -#include "compress/zstdmt_compress.h" - static int BMK_benchMem(const void* srcBuffer, size_t srcSize, const char* displayName, int cLevel, const size_t* fileSizes, U32 nbFiles, From e628eaf87a92f275897171d830057766af322c5f Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 26 Jan 2017 15:29:10 -0800 Subject: [PATCH 177/227] Fix pool.c threading.h import --- lib/common/pool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index 693217f24..e439fe1b0 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -21,7 +21,7 @@ #ifdef ZSTD_MULTITHREAD -#include /* pthread adaptation */ +#include "threading.h" /* pthread adaptation */ /* A job is a function and an opaque argument */ typedef struct POOL_job_s { From 9aa1aa13c119eeb37f568e76f81c4705e6cb2dfb Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 26 Jan 2017 16:07:59 -0800 Subject: [PATCH 178/227] Fix Visual Studios project --- build/VS2005/zstd/zstd.vcproj | 16 ++++++++++++---- build/VS2008/zstd/zstd.vcproj | 8 ++++---- build/VS2010/zstd/zstd.vcxproj | 8 ++++---- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/build/VS2005/zstd/zstd.vcproj b/build/VS2005/zstd/zstd.vcproj index 5ef7a98f8..58f254bc8 100644 --- a/build/VS2005/zstd/zstd.vcproj +++ b/build/VS2005/zstd/zstd.vcproj @@ -43,7 +43,7 @@ + + @@ -533,6 +537,10 @@ RelativePath="..\..\..\lib\legacy\zstd_v07.h" > + + diff --git a/build/VS2008/zstd/zstd.vcproj b/build/VS2008/zstd/zstd.vcproj index 0beb59dd7..2dfaf3937 100644 --- a/build/VS2008/zstd/zstd.vcproj +++ b/build/VS2008/zstd/zstd.vcproj @@ -44,7 +44,7 @@ true - $(IncludePath);$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\dictBuilder;$(UniversalCRT_IncludePath); + $(IncludePath);$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\compress;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\dictBuilder;$(UniversalCRT_IncludePath); false $(LibraryPath) true - $(IncludePath);$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\dictBuilder;$(UniversalCRT_IncludePath); + $(IncludePath);$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\compress;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\dictBuilder;$(UniversalCRT_IncludePath); false $(LibraryPath); false - $(IncludePath);$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\dictBuilder;$(UniversalCRT_IncludePath); + $(IncludePath);$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\compress;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\dictBuilder;$(UniversalCRT_IncludePath); false $(LibraryPath) @@ -227,4 +227,4 @@ - \ No newline at end of file + From d86153d9034ff5950c8a803569a91a5b7515998e Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 26 Jan 2017 16:58:25 -0800 Subject: [PATCH 179/227] Edits as per comments, and change wildcard 'X' to '?' --- doc/zstd_compression_format.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index eb5c1ca7e..afa1737d3 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -116,7 +116,7 @@ Skippable frames defined in this specification are compatible with [LZ4] ones. __`Magic_Number`__ 4 Bytes, little-endian format. -Value : 0x184D2A5X, which means any value from 0x184D2A50 to 0x184D2A5F. +Value : 0x184D2A5?, which means any value from 0x184D2A50 to 0x184D2A5F. All 16 values are valid to identify a skippable frame. __`Frame_Size`__ @@ -477,13 +477,16 @@ For values spanning several bytes, convention is little-endian. __`Size_Format` for `Raw_Literals_Block` and `RLE_Literals_Block`__ : -- Value X0 : `Size_Format` uses 1 bit, `Regenerated_Size` uses 5 bits (0-31). +- Value ?0 : `Size_Format` uses 1 bit. + `Regenerated_Size` uses 5 bits (0-31). `Literals_Section_Header` has 1 byte. `Regenerated_Size = Header[0]>>3` -- Value 01 : `Regenerated_Size` uses 12 bits (0-4095). +- Value 01 : `Size_Format` uses 2 bits. + `Regenerated_Size` uses 12 bits (0-4095). `Literals_Section_Header` has 2 bytes. `Regenerated_Size = (Header[0]>>4) + (Header[1]<<4)` -- Value 11 : `Regenerated_Size` uses 20 bits (0-1048575). +- Value 11 : `Size_Format` uses 2 bits. + `Regenerated_Size` uses 20 bits (0-1048575). `Literals_Section_Header` has 3 bytes. `Regenerated_Size = (Header[0]>>4) + (Header[1]<<4) + (Header[2]<<12)` @@ -580,7 +583,8 @@ which describes how to decode the list of weights. - if `headerByte` >= 128 : this is a direct representation, where each `Weight` is written directly as a 4 bits field (0-15). They are encoded forward, 2 weights to a byte with the first weight taking - the top four bits and the second taking the bottom four (e.g. + the top four bits and the second taking the bottom four (e.g. the following + operations could be used to read the weights: `Weight[0] = (Byte[0] >> 4), Weight[1] = (Byte[0] & 0xf)`, etc.). The full representation occupies `((Number_of_Symbols+1)/2)` bytes, meaning it uses a last full byte even if `Number_of_Symbols` is odd. From 122b6aa657c8da6fe5e5820bc02a353d80db0765 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 27 Jan 2017 03:22:37 -0800 Subject: [PATCH 180/227] updated NEWS --- NEWS | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 46bdb25a2..6e8249426 100644 --- a/NEWS +++ b/NEWS @@ -1,11 +1,15 @@ v1.1.3 +cli : new : experimental target `make zstdmt`, with multi-threading support cli : new : advanced commands for detailed parameters, by Przemyslaw Skibinski cli : fix zstdless on Mac OS-X, by Andrew Janke +cli : fix #232 "compress non-files" dictBuilder : improved dictionary generation quality, thanks to Nick Terrell -API : fix : all symbols properly exposed in libzstd, by Nick Terrell -API : fix : ZSTD_initCStream_usingCDict() properly writes dictID into frame header, by Gregory Szorc (#511) +API : new : lib/compress/ZSTDMT_compress.h multithreading API (experimental) API : new : ZSTD_create?Dict_byReference(), requested by Bartosz Taudul API : new : ZDICT_finalizeDictionary() +API : fix : ZSTD_initCStream_usingCDict() properly writes dictID into frame header, by Gregory Szorc (#511) +API : fix : all symbols properly exposed in libzstd, by Nick Terrell +build : support for Solaris target, by Przemyslaw Skibinski v1.1.2 API : streaming : decompression : changed : automatic implicit reset when chain-decoding new frames without init From 1f1a336241fd34606283f73646928a931bd5d547 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 27 Jan 2017 10:27:29 -0800 Subject: [PATCH 181/227] Fix cmake build --- build/cmake/lib/CMakeLists.txt | 1 + build/cmake/programs/CMakeLists.txt | 2 +- build/cmake/tests/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index db752784b..da9c58fd4 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -66,6 +66,7 @@ SET(Headers ${LIBRARY_DIR}/common/huf.h ${LIBRARY_DIR}/common/mem.h ${LIBRARY_DIR}/common/zstd_internal.h + ${LIBRARY_DIR}/compress/zstdmt_compress.h ${LIBRARY_DIR}/dictBuilder/zdict.h ${LIBRARY_DIR}/deprecated/zbuff.h) diff --git a/build/cmake/programs/CMakeLists.txt b/build/cmake/programs/CMakeLists.txt index 9b3c3acc9..cb3dc6e89 100644 --- a/build/cmake/programs/CMakeLists.txt +++ b/build/cmake/programs/CMakeLists.txt @@ -20,7 +20,7 @@ SET(ROOT_DIR ../../..) # Define programs directory, where sources and header files are located SET(LIBRARY_DIR ${ROOT_DIR}/lib) SET(PROGRAMS_DIR ${ROOT_DIR}/programs) -INCLUDE_DIRECTORIES(${PROGRAMS_DIR} ${LIBRARY_DIR} ${LIBRARY_DIR}/common ${LIBRARY_DIR}/compression ${LIBRARY_DIR}/dictBuilder) +INCLUDE_DIRECTORIES(${PROGRAMS_DIR} ${LIBRARY_DIR} ${LIBRARY_DIR}/common ${LIBRARY_DIR}/compress ${LIBRARY_DIR}/dictBuilder) IF (ZSTD_LEGACY_SUPPORT) SET(PROGRAMS_LEGACY_DIR ${PROGRAMS_DIR}/legacy) diff --git a/build/cmake/tests/CMakeLists.txt b/build/cmake/tests/CMakeLists.txt index 7f9c38e1a..53a699449 100644 --- a/build/cmake/tests/CMakeLists.txt +++ b/build/cmake/tests/CMakeLists.txt @@ -41,7 +41,7 @@ SET(ROOT_DIR ../../..) SET(LIBRARY_DIR ${ROOT_DIR}/lib) SET(PROGRAMS_DIR ${ROOT_DIR}/programs) SET(TESTS_DIR ${ROOT_DIR}/tests) -INCLUDE_DIRECTORIES(${TESTS_DIR} ${PROGRAMS_DIR} ${LIBRARY_DIR} ${LIBRARY_DIR}/common ${LIBRARY_DIR}/dictBuilder) +INCLUDE_DIRECTORIES(${TESTS_DIR} ${PROGRAMS_DIR} ${LIBRARY_DIR} ${LIBRARY_DIR}/common ${LIBRARY_DIR}/compress ${LIBRARY_DIR}/dictBuilder) ADD_EXECUTABLE(fullbench ${PROGRAMS_DIR}/datagen.c ${TESTS_DIR}/fullbench.c) TARGET_LINK_LIBRARIES(fullbench libzstd_static) From 29157320fb5dca130e13572066273acf75d5f5f0 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 24 Jan 2017 13:18:50 +0100 Subject: [PATCH 182/227] improved ZSTD_compressBlock_opt_extDict_generic --- lib/compress/zstd_opt.h | 4 ++-- zlibWrapper/.gitignore | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index 8393e7b4c..8862bbd6b 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -825,7 +825,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, match_num = ZSTD_BtGetAllMatches_selectMLS_extDict(ctx, inr, iend, maxSearches, mls, matches, minMatch); - if (match_num > 0 && matches[match_num-1].len > sufficient_len) { + if (match_num > 0 && (matches[match_num-1].len > sufficient_len || cur + matches[match_num-1].len >= ZSTD_OPT_NUM)) { best_mlen = matches[match_num-1].len; best_off = matches[match_num-1].off; last_pos = cur + 1; @@ -835,7 +835,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, /* set prices using matches at position = cur */ for (u = 0; u < match_num; u++) { mlen = (u>0) ? matches[u-1].len+1 : best_mlen; - best_mlen = (cur + matches[u].len < ZSTD_OPT_NUM) ? matches[u].len : ZSTD_OPT_NUM - cur; + best_mlen = matches[u].len; while (mlen <= best_mlen) { if (opt[cur].mlen == 1) { diff --git a/zlibWrapper/.gitignore b/zlibWrapper/.gitignore index 23d2f3a66..6167ca4da 100644 --- a/zlibWrapper/.gitignore +++ b/zlibWrapper/.gitignore @@ -22,4 +22,4 @@ zwrapbench *.txt # Directories -minizip/ \ No newline at end of file +minizip/ From a1cc1796690482e6d982a8faa99f1ebb6960e88d Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 24 Jan 2017 15:01:46 +0100 Subject: [PATCH 183/227] JOB_NUMBER -eq 9 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b0489bd63..e8b05a4e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -168,4 +168,4 @@ matrix: script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') # - if [ $JOB_NUMBER -eq 9 ] || [ $JOB_NUMBER -eq 10 ]; then sh -c "$Cmd"; fi - - sh -c "$Cmd" + - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "master" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -eq 9 ]; then sh -c "$Cmd"; fi From 89f74fc0a08f8d88e5fb2281339454d3f9fcdd35 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Tue, 24 Jan 2017 17:42:28 +0100 Subject: [PATCH 184/227] .travis.yml: test jobs 12-15 --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index e8b05a4e4..7120284ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,6 @@ matrix: # Container-based Ubuntu 12.04 LTS Server Edition 64 bit (doesn't support 32-bit includes) - - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install" - os: linux - sudo: false - - env: Ubu=12.04cont Cmd="make zlibwrapper && make clean && make -C tests test-symbols && make clean && make -C tests test-zstd-nolegacy && make clean && make cmaketest && make clean && make -C contrib/pzstd googletest pzstd tests check && make -C contrib/pzstd clean" os: linux sudo: false @@ -165,7 +161,12 @@ matrix: - gcc-6 - gcc-6-multilib + # Container-based Ubuntu 12.04 LTS Server Edition 64 bit (doesn't support 32-bit includes) + - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install" + os: linux + sudo: false + script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') # - if [ $JOB_NUMBER -eq 9 ] || [ $JOB_NUMBER -eq 10 ]; then sh -c "$Cmd"; fi - - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "master" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -eq 9 ]; then sh -c "$Cmd"; fi + - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "master" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi From e1ccaa79574a7cb3e71cebecab0ae5fb2aa697eb Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 25 Jan 2017 11:19:35 +0100 Subject: [PATCH 185/227] .travis.yml: optimized order of short tests --- .travis.yml | 39 +++++++++++++++++++++------------------ tests/Makefile | 4 ++-- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7120284ea..ef39ae7f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,7 +76,6 @@ matrix: - gcc-arm-linux-gnueabi - libc6-dev-armel-cross - # Ubuntu 14.04 LTS Server Edition 64 bit - env: Ubu=14.04 Cmd="make aarch64test" dist: trusty sudo: required @@ -117,6 +116,23 @@ matrix: packages: - valgrind + + + # other feature branches => short tests + - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install" + os: linux + sudo: false + + - env: Ubu=14.04 Cmd="make -C tests test32" + os: linux + dist: trusty + sudo: required + addons: + apt: + packages: + - libc6-dev-i386 + - gcc-multilib + - env: Ubu=14.04 Cmd="make gpptest && make clean && make gnu90test && make clean && make c99test && make clean && make gnu99test && make clean && make clangtest && make clean && make -C contrib/pzstd googletest32 @@ -136,16 +152,6 @@ matrix: - g++-4.8 - g++-4.8-multilib - - env: Ubu=14.04 Cmd="make -C tests test32" - os: linux - dist: trusty - sudo: required - addons: - apt: - packages: - - libc6-dev-i386 - - gcc-multilib - - env: Ubu=14.04 Cmd="make gcc5test && make clean && make gcc6test && make clean && make -C tests dll" os: linux dist: trusty @@ -161,12 +167,9 @@ matrix: - gcc-6 - gcc-6-multilib - # Container-based Ubuntu 12.04 LTS Server Edition 64 bit (doesn't support 32-bit includes) - - env: Ubu=12.04cont Cmd="make test && make clean && make travis-install" - os: linux - sudo: false - script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') - # - if [ $JOB_NUMBER -eq 9 ] || [ $JOB_NUMBER -eq 10 ]; then sh -c "$Cmd"; fi - - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "master" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi + # dev => normal tests; other feature branches => short tests (number > 11) + - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi + # master => long tests, as this is the final step towards a Release + - if [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$TRAVIS_BRANCH" = "master" ]; then FUZZERTEST=-T10mn sh -c "$Cmd"; fi diff --git a/tests/Makefile b/tests/Makefile index 937f3b41e..f49d23018 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -54,8 +54,8 @@ endif VOID = /dev/null ZSTREAM_TESTTIME = -T2mn -FUZZERTEST= -T5mn -ZSTDRTTEST= --test-large-data +FUZZERTEST ?= -T5mn +ZSTDRTTEST = --test-large-data .PHONY: default all all32 dll clean test test32 test-all namespaceTest versionsTest From 4b66ddea7e70cee2d71c09b151b13cc973bd2db6 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 25 Jan 2017 11:57:28 +0100 Subject: [PATCH 186/227] .travis.yml: different tests for "master" branch --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ef39ae7f1..bbd775001 100644 --- a/.travis.yml +++ b/.travis.yml @@ -170,6 +170,6 @@ matrix: script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') # dev => normal tests; other feature branches => short tests (number > 11) - - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ "$TRAVIS_BRANCH" = "dev" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi + - if [ "$TRAVIS_BRANCH" != "master" ] && [ "$TRAVIS_BRANCH" = "dev" ] || [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi # master => long tests, as this is the final step towards a Release - - if [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$TRAVIS_BRANCH" = "master" ]; then FUZZERTEST=-T10mn sh -c "$Cmd"; fi + - if [ "$TRAVIS_BRANCH" = "master" ]; then FUZZERTEST=-T10mn sh -c "$Cmd"; fi From 92a4dbf2e456525d7f834cf97c8f16702ec0592e Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 25 Jan 2017 13:02:33 +0100 Subject: [PATCH 187/227] Fixed https://github.com/facebook/zstd/issues/232 --- .travis.yml | 2 +- programs/fileio.c | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bbd775001..ba9f9965d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -170,6 +170,6 @@ matrix: script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') # dev => normal tests; other feature branches => short tests (number > 11) - - if [ "$TRAVIS_BRANCH" != "master" ] && [ "$TRAVIS_BRANCH" = "dev" ] || [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ $JOB_NUMBER -gt 11 ]; then sh -c "$Cmd"; fi + - if [ "$TRAVIS_PULL_REQUEST" = "true" ] || [ $JOB_NUMBER -gt 11 ] || [ "$TRAVIS_BRANCH" = "dev" ] && [ "$TRAVIS_BRANCH" != "master" ]; then sh -c "$Cmd"; fi # master => long tests, as this is the final step towards a Release - if [ "$TRAVIS_BRANCH" = "master" ]; then FUZZERTEST=-T10mn sh -c "$Cmd"; fi diff --git a/programs/fileio.c b/programs/fileio.c index ac7dffb31..596c922f4 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -414,6 +414,11 @@ static int FIO_compressFilename_srcFile(cRess_t ress, DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName); return 1; } + if (!UTIL_doesFileExists(srcFileName)) { + DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", srcFileName); + return 1; + } + ress.srcFile = FIO_openSrcFile(srcFileName); if (!ress.srcFile) return 1; /* srcFile could not be opened */ @@ -772,6 +777,10 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* dstFileName, const ch DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName); return 1; } + if (!UTIL_doesFileExists(srcFileName)) { + DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", srcFileName); + return 1; + } srcFile = FIO_openSrcFile(srcFileName); if (srcFile==0) return 1; From eb2d23a90c888476dfed26b73b640a8f3e2cf075 Mon Sep 17 00:00:00 2001 From: Przemyslaw Skibinski Date: Wed, 25 Jan 2017 13:11:26 +0100 Subject: [PATCH 188/227] improved #232 fix --- programs/fileio.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 596c922f4..86da00131 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -158,6 +158,10 @@ static FILE* FIO_openSrcFile(const char* srcFileName) f = stdin; SET_BINARY_MODE(stdin); } else { + if (!UTIL_doesFileExists(srcFileName)) { + DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", srcFileName); + return NULL; + } f = fopen(srcFileName, "rb"); if ( f==NULL ) DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno)); } @@ -414,10 +418,6 @@ static int FIO_compressFilename_srcFile(cRess_t ress, DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName); return 1; } - if (!UTIL_doesFileExists(srcFileName)) { - DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", srcFileName); - return 1; - } ress.srcFile = FIO_openSrcFile(srcFileName); if (!ress.srcFile) return 1; /* srcFile could not be opened */ @@ -777,10 +777,6 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* dstFileName, const ch DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName); return 1; } - if (!UTIL_doesFileExists(srcFileName)) { - DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", srcFileName); - return 1; - } srcFile = FIO_openSrcFile(srcFileName); if (srcFile==0) return 1; From 9c018cc1407d2e18da377744b3b6f26dc6effc8f Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 26 Jan 2017 15:56:34 -0800 Subject: [PATCH 189/227] Add BUCK files for Nuclide support --- .buckconfig | 9 ++ .buckversion | 1 + .gitignore | 2 + contrib/pzstd/BUCK | 72 +++++++++++++ contrib/pzstd/test/BUCK | 37 +++++++ contrib/pzstd/utils/BUCK | 75 ++++++++++++++ contrib/pzstd/utils/test/BUCK | 35 +++++++ lib/BUCK | 186 ++++++++++++++++++++++++++++++++++ lib/common/pool.c | 2 +- programs/BUCK | 63 ++++++++++++ programs/bench.c | 3 +- zlibWrapper/BUCK | 22 ++++ 12 files changed, 504 insertions(+), 3 deletions(-) create mode 100644 .buckconfig create mode 100644 .buckversion create mode 100644 contrib/pzstd/BUCK create mode 100644 contrib/pzstd/test/BUCK create mode 100644 contrib/pzstd/utils/BUCK create mode 100644 contrib/pzstd/utils/test/BUCK create mode 100644 lib/BUCK create mode 100644 programs/BUCK create mode 100644 zlibWrapper/BUCK diff --git a/.buckconfig b/.buckconfig new file mode 100644 index 000000000..b2b9c036f --- /dev/null +++ b/.buckconfig @@ -0,0 +1,9 @@ +[cxx] + cppflags = -DXXH_NAMESPACE=ZSTD_ -DZSTD_LEGACY_SUPPORT=1 + cflags = -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef -Wpointer-arith + cxxppflags = -DXXH_NAMESPACE=ZSTD_ -DZSTD_LEGACY_SUPPORT=1 + cxxflags = -std=c++11 -Wno-format-security -Wno-deprecated-declarations + gtest_dep = //contrib/pzstd:gtest + +[httpserver] + port = 0 diff --git a/.buckversion b/.buckversion new file mode 100644 index 000000000..892fad966 --- /dev/null +++ b/.buckversion @@ -0,0 +1 @@ +c8dec2e8da52d483f6dd7c6cd2ad694e8e6fed2b diff --git a/.gitignore b/.gitignore index dd7a74519..e02119883 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,5 @@ googletest/ # Directories bin/ +.buckd/ +buck-out/ diff --git a/contrib/pzstd/BUCK b/contrib/pzstd/BUCK new file mode 100644 index 000000000..d04eeedd8 --- /dev/null +++ b/contrib/pzstd/BUCK @@ -0,0 +1,72 @@ +cxx_library( + name='libpzstd', + visibility=['PUBLIC'], + header_namespace='', + exported_headers=[ + 'ErrorHolder.h', + 'Logging.h', + 'Pzstd.h', + ], + headers=[ + 'SkippableFrame.h', + ], + srcs=[ + 'Pzstd.cpp', + 'SkippableFrame.cpp', + ], + deps=[ + ':options', + '//contrib/pzstd/utils:utils', + '//lib:mem', + '//lib:zstd', + ], +) + +cxx_library( + name='options', + visibility=['PUBLIC'], + header_namespace='', + exported_headers=['Options.h'], + srcs=['Options.cpp'], + deps=[ + '//contrib/pzstd/utils:scope_guard', + '//lib:zstd', + '//programs:util', + ], +) + +cxx_binary( + name='pzstd', + visibility=['PUBLIC'], + srcs=['main.cpp'], + deps=[ + ':libpzstd', + ':options', + ], +) + +# Must run "make googletest" first +cxx_library( + name='gtest', + srcs=glob([ + 'googletest/googletest/src/gtest-all.cc', + 'googletest/googlemock/src/gmock-all.cc', + 'googletest/googlemock/src/gmock_main.cc', + ]), + header_namespace='', + exported_headers=subdir_glob([ + ('googletest/googletest/include', '**/*.h'), + ('googletest/googlemock/include', '**/*.h'), + ]), + headers=subdir_glob([ + ('googletest/googletest', 'src/*.cc'), + ('googletest/googletest', 'src/*.h'), + ('googletest/googlemock', 'src/*.cc'), + ('googletest/googlemock', 'src/*.h'), + ]), + platform_linker_flags=[ + ('android', []), + ('', ['-lpthread']), + ], + visibility=['PUBLIC'], +) diff --git a/contrib/pzstd/test/BUCK b/contrib/pzstd/test/BUCK new file mode 100644 index 000000000..6d3fdd3c2 --- /dev/null +++ b/contrib/pzstd/test/BUCK @@ -0,0 +1,37 @@ +cxx_test( + name='options_test', + srcs=['OptionsTest.cpp'], + deps=['//contrib/pzstd:options'], +) + +cxx_test( + name='pzstd_test', + srcs=['PzstdTest.cpp'], + deps=[ + ':round_trip', + '//contrib/pzstd:libpzstd', + '//contrib/pzstd/utils:scope_guard', + '//programs:datagen', + ], +) + +cxx_binary( + name='round_trip_test', + srcs=['RoundTripTest.cpp'], + deps=[ + ':round_trip', + '//contrib/pzstd/utils:scope_guard', + '//programs:datagen', + ] +) + +cxx_library( + name='round_trip', + header_namespace='test', + exported_headers=['RoundTrip.h'], + deps=[ + '//contrib/pzstd:libpzstd', + '//contrib/pzstd:options', + '//contrib/pzstd/utils:scope_guard', + ] +) diff --git a/contrib/pzstd/utils/BUCK b/contrib/pzstd/utils/BUCK new file mode 100644 index 000000000..e757f4120 --- /dev/null +++ b/contrib/pzstd/utils/BUCK @@ -0,0 +1,75 @@ +cxx_library( + name='buffer', + visibility=['PUBLIC'], + header_namespace='utils', + exported_headers=['Buffer.h'], + deps=[':range'], +) + +cxx_library( + name='file_system', + visibility=['PUBLIC'], + header_namespace='utils', + exported_headers=['FileSystem.h'], + deps=[':range'], +) + +cxx_library( + name='likely', + visibility=['PUBLIC'], + header_namespace='utils', + exported_headers=['Likely.h'], +) + +cxx_library( + name='range', + visibility=['PUBLIC'], + header_namespace='utils', + exported_headers=['Range.h'], + deps=[':likely'], +) + +cxx_library( + name='resource_pool', + visibility=['PUBLIC'], + header_namespace='utils', + exported_headers=['ResourcePool.h'], +) + +cxx_library( + name='scope_guard', + visibility=['PUBLIC'], + header_namespace='utils', + exported_headers=['ScopeGuard.h'], +) + +cxx_library( + name='thread_pool', + visibility=['PUBLIC'], + header_namespace='utils', + exported_headers=['ThreadPool.h'], + deps=[':work_queue'], +) + +cxx_library( + name='work_queue', + visibility=['PUBLIC'], + header_namespace='utils', + exported_headers=['WorkQueue.h'], + deps=[':buffer'], +) + +cxx_library( + name='utils', + visibility=['PUBLIC'], + deps=[ + ':buffer', + ':file_system', + ':likely', + ':range', + ':resource_pool', + ':scope_guard', + ':thread_pool', + ':work_queue', + ], +) diff --git a/contrib/pzstd/utils/test/BUCK b/contrib/pzstd/utils/test/BUCK new file mode 100644 index 000000000..a5113cab6 --- /dev/null +++ b/contrib/pzstd/utils/test/BUCK @@ -0,0 +1,35 @@ +cxx_test( + name='buffer_test', + srcs=['BufferTest.cpp'], + deps=['//contrib/pzstd/utils:buffer'], +) + +cxx_test( + name='range_test', + srcs=['RangeTest.cpp'], + deps=['//contrib/pzstd/utils:range'], +) + +cxx_test( + name='resource_pool_test', + srcs=['ResourcePoolTest.cpp'], + deps=['//contrib/pzstd/utils:resource_pool'], +) + +cxx_test( + name='scope_guard_test', + srcs=['ScopeGuardTest.cpp'], + deps=['//contrib/pzstd/utils:scope_guard'], +) + +cxx_test( + name='thread_pool_test', + srcs=['ThreadPoolTest.cpp'], + deps=['//contrib/pzstd/utils:thread_pool'], +) + +cxx_test( + name='work_queue_test', + srcs=['RangeTest.cpp'], + deps=['//contrib/pzstd/utils:work_queue'], +) diff --git a/lib/BUCK b/lib/BUCK new file mode 100644 index 000000000..6812c1b1e --- /dev/null +++ b/lib/BUCK @@ -0,0 +1,186 @@ +cxx_library( + name='zstd', + header_namespace='', + visibility=['PUBLIC'], + deps=[ + ':common', + ':compress', + ':decompress', + ':deprecated', + ], +) + +cxx_library( + name='compress', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('compress', 'zstdmt_compress.h'), + ]), + headers=subdir_glob([ + ('compress', 'zstd_opt.h'), + ]), + srcs=[ + 'compress/zstd_compress.c', + 'compress/zstdmt_compress.c', + ], + deps=[':common'], +) + +cxx_library( + name='decompress', + header_namespace='', + visibility=['PUBLIC'], + srcs=['decompress/zstd_decompress.c'], + deps=[ + ':common', + ':legacy', + ], +) + +cxx_library( + name='deprecated', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('decprecated', '*.h'), + ]), + srcs=glob(['deprecated/*.c']), + deps=[':common'], +) + +cxx_library( + name='legacy', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('legacy', '*.h'), + ]), + srcs=glob(['legacy/*.c']), + deps=[':common'], +) + +cxx_library( + name='zdict', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('dictBuilder', 'zdict.h'), + ]), + headers=subdir_glob([ + ('dictBuilder', 'divsufsort.h'), + ]), + srcs=glob(['dictBuilder/*.c']), + deps=[':common'], +) + +cxx_library( + name='bitstream', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('common', 'bitstream.h'), + ]), +) + +cxx_library( + name='entropy', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('common', 'fse.h'), + ('common', 'huf.h'), + ]), + srcs=[ + 'common/entropy_common.c', + 'common/fse_decompress.c', + 'compress/fse_compress.c', + 'compress/huf_compress.c', + 'decompress/huf_decompress.c', + ], + deps=[ + ':bitstream', + ':errors', + ':mem', + ], +) + +cxx_library( + name='errors', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('common', 'error_private.h'), + ('common', 'zstd_errors.h'), + ]), + srcs=['common/error_private.c'], +) + +cxx_library( + name='mem', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('common', 'mem.h'), + ]), +) + +cxx_library( + name='pool', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('common', 'pool.h'), + ]), + srcs=['common/pool.c'], + deps=[':threading'], +) + +cxx_library( + name='threading', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('common', 'threading.h'), + ]), + srcs=['common/threading.c'], +) + +cxx_library( + name='xxhash', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('common', 'xxhash.h'), + ]), + srcs=['common/xxhash.c'], +) + +cxx_library( + name='zstd_common', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('', 'zstd.h'), + ('common', 'zstd_internal.h'), + ]), + srcs=['common/zstd_common.c'], + deps=[ + ':errors', + ':mem', + ], +) + +cxx_library( + name='common', + deps=[ + ':bitstream', + ':entropy', + ':errors', + ':mem', + ':pool', + ':threading', + ':xxhash', + ':zstd_common', + ] +) diff --git a/lib/common/pool.c b/lib/common/pool.c index 693217f24..e439fe1b0 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -21,7 +21,7 @@ #ifdef ZSTD_MULTITHREAD -#include /* pthread adaptation */ +#include "threading.h" /* pthread adaptation */ /* A job is a function and an opaque argument */ typedef struct POOL_job_s { diff --git a/programs/BUCK b/programs/BUCK new file mode 100644 index 000000000..069403042 --- /dev/null +++ b/programs/BUCK @@ -0,0 +1,63 @@ +cxx_binary( + name='zstd', + headers=glob(['*.h'], excludes=['datagen.h', 'platform.h', 'util.h']), + srcs=glob(['*.c'], excludes=['datagen.c']), + deps=[ + ':datagen', + ':util', + '//lib:zstd', + '//lib:zdict', + '//lib:mem', + '//lib:xxhash', + ], +) + +cxx_binary( + name='zstdmt', + headers=glob(['*.h'], excludes=['datagen.h', 'platform.h', 'util.h']), + srcs=glob(['*.c'], excludes=['datagen.c']), + deps=[ + ':datagen', + ':util', + '//lib:zstd', + '//lib:zdict', + '//lib:mem', + '//lib:xxhash', + ], + preprocessor_flags=['-DZSTD_MULTITHREAD'], + linker_flags=['-lpthread'], +) + +cxx_binary( + name='gzstd', + headers=glob(['*.h'], excludes=['datagen.h', 'platform.h', 'util.h']), + srcs=glob(['*.c'], excludes=['datagen.c']), + deps=[ + ':datagen', + ':util', + '//lib:zstd', + '//lib:zdict', + '//lib:mem', + '//lib:xxhash', + ], + preprocessor_flags=['-DZSTD_GZDECOMPRESS'], + linker_flags=['-lz'], +) + +cxx_library( + name='datagen', + visibility=['PUBLIC'], + header_namespace='', + exported_headers=['datagen.h'], + srcs=['datagen.c'], + deps=['//lib:mem'], +) + + +cxx_library( + name='util', + visibility=['PUBLIC'], + header_namespace='', + exported_headers=['util.h', 'platform.h'], + deps=['//lib:mem'], +) diff --git a/programs/bench.c b/programs/bench.c index 1ca40d6b9..dcb23b1f2 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -40,6 +40,7 @@ #include "zstd.h" #include "datagen.h" /* RDG_genBuffer */ #include "xxhash.h" +#include "zstdmt_compress.h" /* ************************************* @@ -148,8 +149,6 @@ typedef struct { #define MIN(a,b) ((a)<(b) ? (a) : (b)) #define MAX(a,b) ((a)>(b) ? (a) : (b)) -#include "compress/zstdmt_compress.h" - static int BMK_benchMem(const void* srcBuffer, size_t srcSize, const char* displayName, int cLevel, const size_t* fileSizes, U32 nbFiles, diff --git a/zlibWrapper/BUCK b/zlibWrapper/BUCK new file mode 100644 index 000000000..a3b74ac3f --- /dev/null +++ b/zlibWrapper/BUCK @@ -0,0 +1,22 @@ +cxx_library( + name='zlib_wrapper', + visibility=['PUBLIC'], + exported_linker_flags=['-lz'], + header_namespace='', + exported_headers=['zstd_zlibwrapper.h'], + headers=[ + 'gzcompatibility.h', + 'gzguts.h', + ], + srcs=glob(['*.c']), + deps=[ + '//lib:zstd', + '//lib:zstd_common', + ] +) + +cxx_binary( + name='minigzip', + srcs=['examples/minigzip.c'], + deps=[':zlib_wrapper'], +) From 58f499c41e49875359e555a8bcba31709a88a523 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 26 Jan 2017 20:47:59 -0800 Subject: [PATCH 190/227] Clean imports and shorten tests --- contrib/pzstd/Logging.h | 4 +- contrib/pzstd/Options.cpp | 2 +- contrib/pzstd/main.cpp | 5 --- contrib/pzstd/test/PzstdTest.cpp | 64 +++++++++++++++----------------- 4 files changed, 32 insertions(+), 43 deletions(-) diff --git a/contrib/pzstd/Logging.h b/contrib/pzstd/Logging.h index 76c982ab2..6d5582a00 100644 --- a/contrib/pzstd/Logging.h +++ b/contrib/pzstd/Logging.h @@ -37,8 +37,8 @@ class Logger { return level <= level_; } - template - void operator()(int level, const char *fmt, Args... args) { + template + void operator()(int level, String fmt, Args... args) { if (level > level_) { return; } diff --git a/contrib/pzstd/Options.cpp b/contrib/pzstd/Options.cpp index 0b1403354..a0d969393 100644 --- a/contrib/pzstd/Options.cpp +++ b/contrib/pzstd/Options.cpp @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ #include "Options.h" +#include "util.h" #include "utils/ScopeGuard.h" #include @@ -15,7 +16,6 @@ #include #include #include -#include #include #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || \ diff --git a/contrib/pzstd/main.cpp b/contrib/pzstd/main.cpp index 279cbfb5e..7d8dbfbcf 100644 --- a/contrib/pzstd/main.cpp +++ b/contrib/pzstd/main.cpp @@ -9,11 +9,6 @@ #include "ErrorHolder.h" #include "Options.h" #include "Pzstd.h" -#include "utils/FileSystem.h" -#include "utils/Range.h" -#include "utils/ScopeGuard.h" -#include "utils/ThreadPool.h" -#include "utils/WorkQueue.h" using namespace pzstd; diff --git a/contrib/pzstd/test/PzstdTest.cpp b/contrib/pzstd/test/PzstdTest.cpp index c85f73a39..cadfa83f7 100644 --- a/contrib/pzstd/test/PzstdTest.cpp +++ b/contrib/pzstd/test/PzstdTest.cpp @@ -41,23 +41,20 @@ TEST(Pzstd, SmallSizes) { std::fclose(fd); ASSERT_EQ(written, len); } - for (unsigned headers = 0; headers <= 1; ++headers) { - for (unsigned numThreads = 1; numThreads <= 2; ++numThreads) { - for (unsigned level = 1; level <= 4; level *= 4) { - auto errorGuard = makeScopeGuard([&] { - std::fprintf(stderr, "pzstd headers: %u\n", headers); - std::fprintf(stderr, "# threads: %u\n", numThreads); - std::fprintf(stderr, "compression level: %u\n", level); - }); - Options options; - options.overwrite = true; - options.inputFiles = {inputFile}; - options.numThreads = numThreads; - options.compressionLevel = level; - options.verbosity = 1; - ASSERT_TRUE(roundTrip(options)); - errorGuard.dismiss(); - } + for (unsigned numThreads = 1; numThreads <= 2; ++numThreads) { + for (unsigned level = 1; level <= 4; level *= 4) { + auto errorGuard = makeScopeGuard([&] { + std::fprintf(stderr, "# threads: %u\n", numThreads); + std::fprintf(stderr, "compression level: %u\n", level); + }); + Options options; + options.overwrite = true; + options.inputFiles = {inputFile}; + options.numThreads = numThreads; + options.compressionLevel = level; + options.verbosity = 1; + ASSERT_TRUE(roundTrip(options)); + errorGuard.dismiss(); } } } @@ -79,29 +76,26 @@ TEST(Pzstd, LargeSizes) { std::fclose(fd); ASSERT_EQ(written, len); } - for (unsigned headers = 0; headers <= 1; ++headers) { - for (unsigned numThreads = 1; numThreads <= 16; numThreads *= 4) { - for (unsigned level = 1; level <= 4; level *= 2) { - auto errorGuard = makeScopeGuard([&] { - std::fprintf(stderr, "pzstd headers: %u\n", headers); - std::fprintf(stderr, "# threads: %u\n", numThreads); - std::fprintf(stderr, "compression level: %u\n", level); - }); - Options options; - options.overwrite = true; - options.inputFiles = {inputFile}; - options.numThreads = std::min(numThreads, options.numThreads); - options.compressionLevel = level; - options.verbosity = 1; - ASSERT_TRUE(roundTrip(options)); - errorGuard.dismiss(); - } + for (unsigned numThreads = 1; numThreads <= 16; numThreads *= 4) { + for (unsigned level = 1; level <= 4; level *= 4) { + auto errorGuard = makeScopeGuard([&] { + std::fprintf(stderr, "# threads: %u\n", numThreads); + std::fprintf(stderr, "compression level: %u\n", level); + }); + Options options; + options.overwrite = true; + options.inputFiles = {inputFile}; + options.numThreads = std::min(numThreads, options.numThreads); + options.compressionLevel = level; + options.verbosity = 1; + ASSERT_TRUE(roundTrip(options)); + errorGuard.dismiss(); } } } } -TEST(Pzstd, ExtremelyLargeSize) { +TEST(Pzstd, DISABLED_ExtremelyLargeSize) { unsigned seed = std::random_device{}(); std::fprintf(stderr, "Pzstd.ExtremelyLargeSize seed: %u\n", seed); std::mt19937 gen(seed); From cafdd31a38a2cce43b19816a3a0f6218482b447d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 27 Jan 2017 10:44:03 -0800 Subject: [PATCH 191/227] fixed MSAN warnings in legacy decoders In some extraordinary circumstances, *Length field can be generated from reading a partially uninitialized memory segment. Data is correctly identified as corrupted later on, but the read taints some later pointer arithmetic operation. --- lib/legacy/zstd_v04.c | 20 +++++++------------- lib/legacy/zstd_v05.c | 4 ++-- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c index bd011319c..e9509070d 100644 --- a/lib/legacy/zstd_v04.c +++ b/lib/legacy/zstd_v04.c @@ -3016,12 +3016,11 @@ static void ZSTD_decodeSequence(seq_t* seq, seqState_t* seqState) { U32 add = *dumps++; if (add < 255) litLength += add; - else - { + else { litLength = MEM_readLE32(dumps) & 0xFFFFFF; /* no pb : dumps is always followed by seq tables > 1 byte */ dumps += 3; } - if (dumps >= de) dumps = de-1; /* late correction, to avoid read overflow (data is now corrupted anyway) */ + if (dumps >= de) { dumps = de-1; litLength = MaxLL+255; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ } /* Offset */ @@ -3043,16 +3042,14 @@ static void ZSTD_decodeSequence(seq_t* seq, seqState_t* seqState) /* MatchLength */ matchLength = FSE_decodeSymbol(&(seqState->stateML), &(seqState->DStream)); - if (matchLength == MaxML) - { + if (matchLength == MaxML) { U32 add = *dumps++; if (add < 255) matchLength += add; - else - { + else { matchLength = MEM_readLE32(dumps) & 0xFFFFFF; /* no pb : dumps is always followed by seq tables > 1 byte */ dumps += 3; } - if (dumps >= de) dumps = de-1; /* late correction, to avoid read overflow (data is now corrupted anyway) */ + if (dumps >= de) { dumps = de-1; matchLength = MaxML+255; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ } matchLength += MINMATCH; @@ -3116,8 +3113,7 @@ static size_t ZSTD_execSequence(BYTE* op, /* Requirement: op <= oend_8 */ /* match within prefix */ - if (sequence.offset < 8) - { + if (sequence.offset < 8) { /* close range match, overlap */ const int sub2 = dec64table[sequence.offset]; op[0] = match[0]; @@ -3127,9 +3123,7 @@ static size_t ZSTD_execSequence(BYTE* op, match += dec32table[sequence.offset]; ZSTD_copy4(op+4, match); match -= sub2; - } - else - { + } else { ZSTD_copy8(op, match); } op += 8; match += 8; diff --git a/lib/legacy/zstd_v05.c b/lib/legacy/zstd_v05.c index 3dd740e5f..43943d81a 100644 --- a/lib/legacy/zstd_v05.c +++ b/lib/legacy/zstd_v05.c @@ -3230,7 +3230,7 @@ static void ZSTDv05_decodeSequence(seq_t* seq, seqState_t* seqState) if (litLength&1) litLength>>=1, dumps += 3; else litLength = (U16)(litLength)>>1, dumps += 2; } - if (dumps >= de) dumps = de-1; /* late correction, to avoid read overflow (data is now corrupted anyway) */ + if (dumps >= de) { dumps = de-1; litLength = MaxLL+255; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ } /* Offset */ @@ -3263,7 +3263,7 @@ static void ZSTDv05_decodeSequence(seq_t* seq, seqState_t* seqState) if (matchLength&1) matchLength>>=1, dumps += 3; else matchLength = (U16)(matchLength)>>1, dumps += 2; } - if (dumps >= de) dumps = de-1; /* late correction, to avoid read overflow (data is now corrupted anyway) */ + if (dumps >= de) { dumps = de-1; matchLength = MaxML+255; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ } matchLength += MINMATCH; From bbfcc243090d8bbfe64488551df9917c4ac18bd5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 27 Jan 2017 11:27:34 -0800 Subject: [PATCH 192/227] updated NEWS --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 6e8249426..f404f6e37 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ API : new : ZDICT_finalizeDictionary() API : fix : ZSTD_initCStream_usingCDict() properly writes dictID into frame header, by Gregory Szorc (#511) API : fix : all symbols properly exposed in libzstd, by Nick Terrell build : support for Solaris target, by Przemyslaw Skibinski +doc : clarified specification, by Andrew Purcell v1.1.2 API : streaming : decompression : changed : automatic implicit reset when chain-decoding new frames without init From 2fe9126591ea7f436710fe0dd3a79bd473195c40 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 27 Jan 2017 11:56:02 -0800 Subject: [PATCH 193/227] Add multithread support to COVER --- lib/dictBuilder/cover.c | 38 ++++++++++++++++++-------------------- lib/dictBuilder/zdict.h | 5 +++-- programs/zstdcli.c | 1 + 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c index 089f077c1..c5b606db6 100644 --- a/lib/dictBuilder/cover.c +++ b/lib/dictBuilder/cover.c @@ -14,11 +14,10 @@ #include /* malloc, free, qsort */ #include /* memset */ #include /* clock */ -#ifdef ZSTD_PTHREAD -#include "threading.h" -#endif -#include "mem.h" /* read */ +#include "mem.h" /* read */ +#include "pool.h" +#include "threading.h" #include "zstd_internal.h" /* includes zstd.h */ #ifndef ZDICT_STATIC_LINKING_ONLY #define ZDICT_STATIC_LINKING_ONLY @@ -690,11 +689,9 @@ ZDICTLIB_API size_t COVER_trainFromBuffer( * compiled with multithreaded support. */ typedef struct COVER_best_s { -#ifdef ZSTD_PTHREAD pthread_mutex_t mutex; pthread_cond_t cond; size_t liveJobs; -#endif void *dict; size_t dictSize; COVER_params_t parameters; @@ -708,11 +705,9 @@ static void COVER_best_init(COVER_best_t *best) { if (!best) { return; } -#ifdef ZSTD_PTHREAD pthread_mutex_init(&best->mutex, NULL); pthread_cond_init(&best->cond, NULL); best->liveJobs = 0; -#endif best->dict = NULL; best->dictSize = 0; best->compressedSize = (size_t)-1; @@ -726,13 +721,11 @@ static void COVER_best_wait(COVER_best_t *best) { if (!best) { return; } -#ifdef ZSTD_PTHREAD pthread_mutex_lock(&best->mutex); while (best->liveJobs != 0) { pthread_cond_wait(&best->cond, &best->mutex); } pthread_mutex_unlock(&best->mutex); -#endif } /** @@ -746,10 +739,8 @@ static void COVER_best_destroy(COVER_best_t *best) { if (best->dict) { free(best->dict); } -#ifdef ZSTD_PTHREAD pthread_mutex_destroy(&best->mutex); pthread_cond_destroy(&best->cond); -#endif } /** @@ -760,11 +751,9 @@ static void COVER_best_start(COVER_best_t *best) { if (!best) { return; } -#ifdef ZSTD_PTHREAD pthread_mutex_lock(&best->mutex); ++best->liveJobs; pthread_mutex_unlock(&best->mutex); -#endif } /** @@ -779,12 +768,10 @@ static void COVER_best_finish(COVER_best_t *best, size_t compressedSize, return; } { -#ifdef ZSTD_PTHREAD size_t liveJobs; pthread_mutex_lock(&best->mutex); --best->liveJobs; liveJobs = best->liveJobs; -#endif /* If the new dictionary is better */ if (compressedSize < best->compressedSize) { /* Allocate space if necessary */ @@ -805,12 +792,10 @@ static void COVER_best_finish(COVER_best_t *best, size_t compressedSize, best->parameters = parameters; best->compressedSize = compressedSize; } -#ifdef ZSTD_PTHREAD pthread_mutex_unlock(&best->mutex); if (liveJobs == 0) { pthread_cond_broadcast(&best->cond); } -#endif } } @@ -928,11 +913,12 @@ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, unsigned nbSamples, COVER_params_t *parameters) { /* constants */ + const unsigned nbThreads = parameters->nbThreads; const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d; const unsigned kMaxD = parameters->d == 0 ? 16 : parameters->d; const unsigned kMinK = parameters->k == 0 ? kMaxD : parameters->k; const unsigned kMaxK = parameters->k == 0 ? 2048 : parameters->k; - const unsigned kSteps = parameters->steps == 0 ? 256 : parameters->steps; + const unsigned kSteps = parameters->steps == 0 ? 32 : parameters->steps; const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1); const unsigned kIterations = (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize); @@ -942,6 +928,7 @@ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, unsigned d; unsigned k; COVER_best_t best; + POOL_ctx *pool = NULL; /* Checks */ if (kMinK < kMaxD || kMaxK < kMinK) { LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n"); @@ -956,6 +943,12 @@ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, ZDICT_DICTSIZE_MIN); return ERROR(dstSize_tooSmall); } + if (nbThreads > 1) { + pool = POOL_create(nbThreads, 1); + if (!pool) { + return ERROR(memory_allocation); + } + } /* Initialization */ COVER_best_init(&best); /* Turn down global display level to clean up display at level 2 and below */ @@ -998,7 +991,11 @@ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, } /* Call the function and pass ownership of data to it */ COVER_best_start(&best); - COVER_tryParameters(data); + if (pool) { + POOL_add(pool, &COVER_tryParameters, data); + } else { + COVER_tryParameters(data); + } /* Print status */ LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%% ", (U32)((iteration * 100) / kIterations)); @@ -1018,6 +1015,7 @@ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, *parameters = best.parameters; memcpy(dictBuffer, best.dict, dictSize); COVER_best_destroy(&best); + POOL_free(pool); return dictSize; } } diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index 1b3bcb5b7..aba538c48 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -93,8 +93,9 @@ ZDICTLIB_API size_t ZDICT_trainFromBuffer_advanced(void* dictBuffer, size_t dict typedef struct { unsigned k; /* Segment size : constraint: 0 < k : Reasonable range [16, 2048+] */ unsigned d; /* dmer size : constraint: 0 < d <= k : Reasonable range [6, 16] */ - unsigned steps; /* Number of steps : Only used for optimization : 0 means default (256) : Higher means more parameters checked */ + unsigned steps; /* Number of steps : Only used for optimization : 0 means default (32) : Higher means more parameters checked */ + unsigned nbThreads; /* Number of threads : constraint: 0 < nbThreads : 1 means single-threaded : Only used for optimization : Ignored if ZSTD_MULTITHREAD is not defined */ unsigned notificationLevel; /* Write to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ unsigned dictID; /* 0 means auto mode (32-bits random value); other : force dictID value */ int compressionLevel; /* 0 means default; target a specific zstd compression level */ @@ -132,7 +133,7 @@ ZDICTLIB_API size_t COVER_trainFromBuffer(void* dictBuffer, size_t dictBufferCap @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) or an error code, which can be tested with ZDICT_isError(). On success `*parameters` contains the parameters selected. - Note : COVER_optimizeTrainFromBuffer() requires about 9 bytes of memory for each input byte. + Note : COVER_optimizeTrainFromBuffer() requires about 4 bytes of memory for each input byte and additionally another 4 bytes of memory for each byte of memory for each tread. */ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 64f2c919c..8b8a16b2b 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -577,6 +577,7 @@ int main(int argCount, const char* argv[]) if (operation==zom_train) { #ifndef ZSTD_NODICT if (cover) { + coverParams.nbThreads = nbThreads; coverParams.compressionLevel = dictCLevel; coverParams.notificationLevel = displayLevel; coverParams.dictID = dictID; From 20bed4210c704c82885d1c324fdbaa893ce3cf72 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 27 Jan 2017 12:16:16 -0800 Subject: [PATCH 194/227] changed format specification version number --- doc/zstd_compression_format.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index afa1737d3..df983284f 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -16,7 +16,7 @@ Distribution of this document is unlimited. ### Version -0.2.2 (14/09/16) +0.2.3 (27/01/17) Introduction @@ -1402,6 +1402,7 @@ to crosscheck that an implementation implements the decoding table generation al Version changes --------------- +- 0.2.3 : clarified several details, by Sean Purcell - 0.2.2 : added predefined codes, by Johannes Rudolph - 0.2.1 : clarify field names, by Przemyslaw Skibinski - 0.2.0 : numerous format adjustments for zstd v0.8 From 89599104128ca28f368a48a6f43a12b80a4a24b2 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 27 Jan 2017 12:29:27 -0800 Subject: [PATCH 195/227] Add threading and pool to build configs --- build/VS2005/zstdlib/zstdlib.vcproj | 24 ++++++++++++++++++++ build/VS2008/zstdlib/zstdlib.vcproj | 24 ++++++++++++++++++++ build/VS2010/libzstd-dll/libzstd-dll.vcxproj | 6 +++++ build/VS2010/libzstd/libzstd.vcxproj | 6 +++++ build/cmake/lib/CMakeLists.txt | 2 ++ 5 files changed, 62 insertions(+) diff --git a/build/VS2005/zstdlib/zstdlib.vcproj b/build/VS2005/zstdlib/zstdlib.vcproj index 828cc8285..f4c9950ff 100644 --- a/build/VS2005/zstdlib/zstdlib.vcproj +++ b/build/VS2005/zstdlib/zstdlib.vcproj @@ -335,6 +335,14 @@ RelativePath="..\..\..\lib\dictBuilder\divsufsort.c" > + + + + @@ -383,6 +391,10 @@ RelativePath="..\..\..\lib\common\zstd_common.c" > + + @@ -433,6 +445,14 @@ RelativePath="..\..\..\lib\dictBuilder\divsufsort.h" > + + + + @@ -493,6 +513,10 @@ RelativePath="..\..\..\lib\compress\zstd_opt.h" > + + diff --git a/build/VS2008/zstdlib/zstdlib.vcproj b/build/VS2008/zstdlib/zstdlib.vcproj index 69b742d19..cba0ff908 100644 --- a/build/VS2008/zstdlib/zstdlib.vcproj +++ b/build/VS2008/zstdlib/zstdlib.vcproj @@ -336,6 +336,14 @@ RelativePath="..\..\..\lib\dictBuilder\divsufsort.c" > + + + + @@ -372,6 +380,10 @@ RelativePath="..\..\..\lib\common\zstd_common.c" > + + @@ -422,6 +434,14 @@ RelativePath="..\..\..\lib\dictBuilder\divsufsort.h" > + + + + @@ -478,6 +498,10 @@ RelativePath="..\..\..\lib\compress\zstd_opt.h" > + + diff --git a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj index f0feecb26..145890517 100644 --- a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj +++ b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj @@ -19,6 +19,8 @@ + + @@ -27,6 +29,7 @@ + @@ -44,6 +47,8 @@ + + @@ -63,6 +68,7 @@ + diff --git a/build/VS2010/libzstd/libzstd.vcxproj b/build/VS2010/libzstd/libzstd.vcxproj index c8d21dd4a..22d5d4e14 100644 --- a/build/VS2010/libzstd/libzstd.vcxproj +++ b/build/VS2010/libzstd/libzstd.vcxproj @@ -19,6 +19,8 @@ + + @@ -27,6 +29,7 @@ + @@ -44,6 +47,8 @@ + + @@ -63,6 +68,7 @@ + {8BFD8150-94D5-4BF9-8A50-7BD9929A0850} diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index db752784b..08ae3e9ad 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -59,6 +59,8 @@ SET(Sources SET(Headers ${LIBRARY_DIR}/zstd.h + ${LIBRARY_DIR}/common/pool.h + ${LIBRARY_DIR}/common/threading.h ${LIBRARY_DIR}/common/bitstream.h ${LIBRARY_DIR}/common/error_private.h ${LIBRARY_DIR}/common/zstd_errors.h From 5cf84a05e734dbd7872865bcbc67d70c596aa541 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 27 Jan 2017 13:26:44 -0800 Subject: [PATCH 196/227] Revert unnecessary change to Logging.h --- contrib/pzstd/Logging.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/pzstd/Logging.h b/contrib/pzstd/Logging.h index 6d5582a00..76c982ab2 100644 --- a/contrib/pzstd/Logging.h +++ b/contrib/pzstd/Logging.h @@ -37,8 +37,8 @@ class Logger { return level <= level_; } - template - void operator()(int level, String fmt, Args... args) { + template + void operator()(int level, const char *fmt, Args... args) { if (level > level_) { return; } From 5d9b894e46c25eb4b861dbb251588a27f6d87e86 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 27 Jan 2017 13:30:18 -0800 Subject: [PATCH 197/227] Fixed status display for zstdmt There is a large buffering effect when using zstdmt in MT mode. Consequently, data is read first, pushed to workers, and only later will the compressed result come out. That means there is no longer immediate correlation between amount of data read, and amount of data written. This patch disables the displaying of % compression when multi-threading is enabled. It adds the displaying of total size when it can be determined (it usually can be determined for files, but not for stdin) so the user has a sense of "how far from the end" the compression compressed is. There is no modification to decompression side, since decompression is only single-threaded for now. --- programs/fileio.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 86da00131..353fbd54e 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -78,14 +78,14 @@ * Macros ***************************************/ #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) -#define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } +#define DISPLAYLEVEL(l, ...) { if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } } static U32 g_displayLevel = 2; /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + progression; 4 : + information */ void FIO_setNotificationLevel(unsigned level) { g_displayLevel=level; } -#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ +#define DISPLAYUPDATE(l, ...) { if (g_displayLevel>=l) { \ if ((clock() - g_time > refreshRate) || (g_displayLevel>=4)) \ { g_time = clock(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stdout); } } + if (g_displayLevel>=4) fflush(stdout); } } } static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; static clock_t g_time = 0; @@ -373,7 +373,13 @@ static int FIO_compressFilename_internal(cRess_t ress, if (sizeCheck!=outBuff.pos) EXM_THROW(25, "Write error : cannot write compressed block into %s", dstFileName); compressedfilesize += outBuff.pos; } } } - DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%% ", (U32)(readsize>>20), (double)compressedfilesize/readsize*100); +#ifdef ZSTD_MULTITHREAD + if (!fileSize) DISPLAYUPDATE(2, "\rRead : %u MB", (U32)(readsize>>20)) + else DISPLAYUPDATE(2, "\rRead : %u / %u MB", (U32)(readsize>>20), (U32)(fileSize>>20)); +#else + if (!fileSize) DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%", (U32)(readsize>>20), (double)compressedfilesize/readsize*100) + else DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%", (U32)(readsize>>20), (U32)(fileSize>>20), (double)compressedfilesize/readsize*100); +#endif } /* End of Frame */ From 6570167f8b21da01b68fc373ee898cf8005e4204 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 27 Jan 2017 13:56:41 -0800 Subject: [PATCH 198/227] Fix VS fuzzer build configs --- build/VS2005/fuzzer/fuzzer.vcproj | 24 ++++++++++++++++++++++++ build/VS2008/fuzzer/fuzzer.vcproj | 24 ++++++++++++++++++++++++ build/VS2010/fuzzer/fuzzer.vcxproj | 6 ++++++ 3 files changed, 54 insertions(+) diff --git a/build/VS2005/fuzzer/fuzzer.vcproj b/build/VS2005/fuzzer/fuzzer.vcproj index d6ec14d16..c64c5037a 100644 --- a/build/VS2005/fuzzer/fuzzer.vcproj +++ b/build/VS2005/fuzzer/fuzzer.vcproj @@ -343,10 +343,22 @@ RelativePath="..\..\..\lib\common\entropy_common.c" > + + + + + + @@ -393,6 +405,14 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + + + + @@ -449,6 +469,10 @@ RelativePath="..\..\..\lib\common\zstd_internal.h" > + + diff --git a/build/VS2008/fuzzer/fuzzer.vcproj b/build/VS2008/fuzzer/fuzzer.vcproj index f6912b8d9..72540d243 100644 --- a/build/VS2008/fuzzer/fuzzer.vcproj +++ b/build/VS2008/fuzzer/fuzzer.vcproj @@ -340,6 +340,14 @@ RelativePath="..\..\..\lib\dictBuilder\divsufsort.c" > + + + + @@ -380,6 +388,10 @@ RelativePath="..\..\..\lib\common\zstd_common.c" > + + @@ -402,6 +414,14 @@ RelativePath="..\..\..\lib\dictBuilder\divsufsort.h" > + + + + @@ -450,6 +470,10 @@ RelativePath="..\..\..\lib\common\zstd_internal.h" > + + diff --git a/build/VS2010/fuzzer/fuzzer.vcxproj b/build/VS2010/fuzzer/fuzzer.vcxproj index 623a9ca43..07ae508be 100644 --- a/build/VS2010/fuzzer/fuzzer.vcxproj +++ b/build/VS2010/fuzzer/fuzzer.vcxproj @@ -155,6 +155,8 @@ + + @@ -163,6 +165,7 @@ + @@ -172,6 +175,8 @@ + + @@ -179,6 +184,7 @@ + From d98bf492247b99c067b1dfec2199c3b5db80b09e Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 27 Jan 2017 15:42:36 -0800 Subject: [PATCH 199/227] Fix segfault in zstreamtest MT It was reading beyond the end of the input buffer because no errors were detected. Once that was fixed, it wasn't making forward progress because no errors were detected and it was waiting for input. --- tests/zstreamtest.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index bef8734c7..e451535c4 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -933,10 +933,13 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp size_t const randomCSrcSize = FUZ_randomLength(&lseed, maxSampleLog); size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); size_t const adjustedDstSize = MIN(dstBufferSize - outBuff.pos, randomDstSize); + size_t const adjustedCSrcSize = MIN(cSize - inBuff.pos, randomCSrcSize); outBuff.size = outBuff.pos + adjustedDstSize; - inBuff.size = inBuff.pos + randomCSrcSize; + inBuff.size = inBuff.pos + adjustedCSrcSize; { size_t const decompressError = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (ZSTD_isError(decompressError)) break; /* error correctly detected */ + /* No forward progress possible */ + if (outBuff.pos < outBuff.size && inBuff.pos == cSize) break; } } } } DISPLAY("\r%u fuzzer tests completed \n", testNb); From f6d4a786fc188dfd8498d437879f5f0bd5bc4182 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 27 Jan 2017 15:55:30 -0800 Subject: [PATCH 200/227] reduced zstdmt latency when using small custom section sizes with high compression levels Previous version was requiring a fairly large initial amount of input data before starting to create compression jobs. This new version starts the process much sooner. --- lib/compress/zstdmt_compress.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 5f0bf2ab1..ca9bf6a26 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -273,6 +273,7 @@ struct ZSTDMT_CCtx_s { pthread_mutex_t jobCompleted_mutex; pthread_cond_t jobCompleted_cond; size_t targetSectionSize; + size_t marginSize; size_t inBuffSize; size_t dictSize; size_t targetDictSize; @@ -514,8 +515,9 @@ static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, zcs->frameContentSize = pledgedSrcSize; zcs->targetSectionSize = zcs->sectionSize ? zcs->sectionSize : (size_t)1 << (zcs->params.cParams.windowLog + 2); zcs->targetSectionSize = MAX(ZSTDMT_SECTION_SIZE_MIN, zcs->targetSectionSize); + zcs->marginSize = zcs->targetSectionSize >> 2; zcs->targetDictSize = zcs->overlapWrLog < 10 ? (size_t)1 << (zcs->params.cParams.windowLog - zcs->overlapWrLog) : 0; - zcs->inBuffSize = zcs->targetSectionSize + ((size_t)1 << zcs->params.cParams.windowLog) /* margin */ + zcs->targetDictSize; + zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize + zcs->marginSize; zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); if (zcs->inBuff.buffer.start == NULL) return ERROR(memory_allocation); zcs->inBuff.filled = 0; @@ -680,6 +682,7 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input) { + size_t const newJobThreshold = zcs->dictSize + zcs->targetSectionSize + zcs->marginSize; if (zcs->frameEnded) return ERROR(stage_wrong); /* current frame being ended. Only flush is allowed. Restart with init */ if (zcs->nbThreads==1) return ZSTD_compressStream(zcs->cstream, output, input); @@ -690,7 +693,7 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu zcs->inBuff.filled += toLoad; } - if ( (zcs->inBuff.filled == zcs->inBuffSize) /* filled enough : let's compress */ + if ( (zcs->inBuff.filled >= newJobThreshold) /* filled enough : let's compress */ && (zcs->nextJobID <= zcs->doneJobID + zcs->jobIDMask) ) { /* avoid overwriting job round buffer */ CHECK_F( ZSTDMT_createCompressionJob(zcs, zcs->targetSectionSize, 0) ); } From b42dd27ef5ad325943174b43334c3c63d1e94a35 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 27 Jan 2017 16:00:19 -0800 Subject: [PATCH 201/227] Add include guards and extern C --- lib/common/pool.h | 10 ++++++++++ lib/compress/zstdmt_compress.c | 2 +- lib/compress/zstdmt_compress.h | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/common/pool.h b/lib/common/pool.h index c26f543fc..50cb25b12 100644 --- a/lib/common/pool.h +++ b/lib/common/pool.h @@ -9,6 +9,11 @@ #ifndef POOL_H #define POOL_H +#if defined (__cplusplus) +extern "C" { +#endif + + #include /* size_t */ typedef struct POOL_ctx_s POOL_ctx; @@ -43,4 +48,9 @@ typedef void (*POOL_add_function)(void *, POOL_function, void *); */ void POOL_add(void *ctx, POOL_function function, void *opaque); + +#if defined (__cplusplus) +} +#endif + #endif diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 5f0bf2ab1..7423d9db7 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -21,7 +21,7 @@ /* ====== Dependencies ====== */ #include /* malloc */ #include /* memcpy */ -#include /* threadpool */ +#include "pool.h" /* threadpool */ #include "threading.h" /* mutex */ #include "zstd_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */ #include "zstdmt_compress.h" diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 92de52d65..3a26b93d7 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -7,6 +7,13 @@ * of patent rights can be found in the PATENTS file in the same directory. */ + #ifndef ZSTDMT_COMPRESS_H + #define ZSTDMT_COMPRESS_H + + #if defined (__cplusplus) + extern "C" { + #endif + /* Note : All prototypes defined in this file shall be considered experimental. * There is no guarantee of API continuity (yet) on any of these prototypes */ @@ -62,3 +69,10 @@ typedef enum { * Parameters not explicitly reset by ZSTDMT_init*() remain the same in consecutive compression sessions. * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ ZSTDLIB_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, unsigned value); + + +#if defined (__cplusplus) +} +#endif + +#endif /* ZSTDMT_COMPRESS_H */ From 64bf8ffce64ea2f52beba03f6531a1c3001763c3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 27 Jan 2017 17:25:07 -0800 Subject: [PATCH 202/227] report @terrelln patch to ST fuzzer tests --- tests/zstreamtest.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index e451535c4..0fdb1ee12 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -691,10 +691,13 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres size_t const randomCSrcSize = FUZ_randomLength(&lseed, maxSampleLog); size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); size_t const adjustedDstSize = MIN(dstBufferSize - outBuff.pos, randomDstSize); + size_t const adjustedCSrcSize = MIN(cSize - inBuff.pos, randomCSrcSize); outBuff.size = outBuff.pos + adjustedDstSize; - inBuff.size = inBuff.pos + randomCSrcSize; + inBuff.size = inBuff.pos + adjustedCSrcSize; { size_t const decompressError = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (ZSTD_isError(decompressError)) break; /* error correctly detected */ + /* No forward progress possible */ + if (outBuff.pos < outBuff.size && inBuff.pos == cSize) break; } } } } DISPLAY("\r%u fuzzer tests completed \n", testNb); From 90db3afdee20459eb65e00f58879c6802b92acb9 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 27 Jan 2017 17:32:16 -0800 Subject: [PATCH 203/227] Fix typos in VS2010 build config --- build/VS2010/fuzzer/fuzzer.vcxproj | 4 ++-- build/VS2010/libzstd-dll/libzstd-dll.vcxproj | 8 ++++---- build/VS2010/libzstd/libzstd.vcxproj | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build/VS2010/fuzzer/fuzzer.vcxproj b/build/VS2010/fuzzer/fuzzer.vcxproj index 07ae508be..e30511a78 100644 --- a/build/VS2010/fuzzer/fuzzer.vcxproj +++ b/build/VS2010/fuzzer/fuzzer.vcxproj @@ -175,8 +175,8 @@ - - + + diff --git a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj index 145890517..f78598fb4 100644 --- a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj +++ b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj @@ -19,8 +19,8 @@ - - + + @@ -46,7 +46,7 @@ - + @@ -68,7 +68,7 @@ - + diff --git a/build/VS2010/libzstd/libzstd.vcxproj b/build/VS2010/libzstd/libzstd.vcxproj index 22d5d4e14..727795514 100644 --- a/build/VS2010/libzstd/libzstd.vcxproj +++ b/build/VS2010/libzstd/libzstd.vcxproj @@ -46,9 +46,9 @@ - - - + + + From 43474313f83eb2840687d307b0e7cb45a5874729 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 27 Jan 2017 18:43:05 -0800 Subject: [PATCH 204/227] Fix documentation about memory usage --- lib/dictBuilder/zdict.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index aba538c48..4d0a62a2c 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -133,7 +133,7 @@ ZDICTLIB_API size_t COVER_trainFromBuffer(void* dictBuffer, size_t dictBufferCap @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) or an error code, which can be tested with ZDICT_isError(). On success `*parameters` contains the parameters selected. - Note : COVER_optimizeTrainFromBuffer() requires about 4 bytes of memory for each input byte and additionally another 4 bytes of memory for each byte of memory for each tread. + Note : COVER_optimizeTrainFromBuffer() requires about 8 bytes of memory for each input byte and additionally another 5 bytes of memory for each byte of memory for each thread. */ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer, const size_t *samplesSizes, unsigned nbSamples, From b5fd15ccb2137c625e2ec8e37f67f6ebabff8afb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 30 Jan 2017 10:45:58 -0800 Subject: [PATCH 205/227] fixed : legacy decoders v04 and v05 --- lib/legacy/zstd_v04.c | 16 ++++++++-------- lib/legacy/zstd_v05.c | 6 ++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c index e9509070d..723242c6c 100644 --- a/lib/legacy/zstd_v04.c +++ b/lib/legacy/zstd_v04.c @@ -3012,20 +3012,19 @@ static void ZSTD_decodeSequence(seq_t* seq, seqState_t* seqState) /* Literal length */ litLength = FSE_decodeSymbol(&(seqState->stateLL), &(seqState->DStream)); prevOffset = litLength ? seq->offset : seqState->prevOffset; - if (litLength == MaxLL) - { + if (litLength == MaxLL) { U32 add = *dumps++; if (add < 255) litLength += add; else { - litLength = MEM_readLE32(dumps) & 0xFFFFFF; /* no pb : dumps is always followed by seq tables > 1 byte */ + litLength = dumps[0] + (dumps[1]<<8) + (dumps[2]<<16); dumps += 3; } - if (dumps >= de) { dumps = de-1; litLength = MaxLL+255; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ + if (dumps > de) { litLength = MaxLL+255; } /* late correction, to avoid using uninitialized memory */ + if (dumps >= de) { dumps = de-1; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ } /* Offset */ - { - static const U32 offsetPrefix[MaxOff+1] = { + { static const U32 offsetPrefix[MaxOff+1] = { 1 /*fake*/, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, /*fake*/ 1, 1, 1, 1, 1 }; @@ -3046,10 +3045,11 @@ static void ZSTD_decodeSequence(seq_t* seq, seqState_t* seqState) U32 add = *dumps++; if (add < 255) matchLength += add; else { - matchLength = MEM_readLE32(dumps) & 0xFFFFFF; /* no pb : dumps is always followed by seq tables > 1 byte */ + matchLength = dumps[0] + (dumps[1]<<8) + (dumps[2]<<16); dumps += 3; } - if (dumps >= de) { dumps = de-1; matchLength = MaxML+255; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ + if (dumps > de) { matchLength = MaxML+255; } /* late correction, to avoid using uninitialized memory */ + if (dumps >= de) { dumps = de-1; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ } matchLength += MINMATCH; diff --git a/lib/legacy/zstd_v05.c b/lib/legacy/zstd_v05.c index 43943d81a..f13592420 100644 --- a/lib/legacy/zstd_v05.c +++ b/lib/legacy/zstd_v05.c @@ -3230,7 +3230,8 @@ static void ZSTDv05_decodeSequence(seq_t* seq, seqState_t* seqState) if (litLength&1) litLength>>=1, dumps += 3; else litLength = (U16)(litLength)>>1, dumps += 2; } - if (dumps >= de) { dumps = de-1; litLength = MaxLL+255; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ + if (dumps > de) { litLength = MaxLL+255; } /* late correction, to avoid using uninitialized memory */ + if (dumps >= de) { dumps = de-1; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ } /* Offset */ @@ -3263,7 +3264,8 @@ static void ZSTDv05_decodeSequence(seq_t* seq, seqState_t* seqState) if (matchLength&1) matchLength>>=1, dumps += 3; else matchLength = (U16)(matchLength)>>1, dumps += 2; } - if (dumps >= de) { dumps = de-1; matchLength = MaxML+255; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ + if (dumps > de) { matchLength = MaxML+255; } /* late correction, to avoid using uninitialized memory */ + if (dumps >= de) { dumps = de-1; } /* late correction, to avoid read overflow (data is now corrupted anyway) */ } matchLength += MINMATCH; From 88df1aed6119d2e98ae683c3c348c1a0f5504bb3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 30 Jan 2017 11:00:00 -0800 Subject: [PATCH 206/227] changed advanced parameter overlapLog Follows a positive logic (increasing value => increasing overlap) which is easier to use --- lib/compress/zstdmt_compress.c | 10 +++++----- lib/compress/zstdmt_compress.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index ca9bf6a26..07c7c1b38 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -286,7 +286,7 @@ struct ZSTDMT_CCtx_s { unsigned nextJobID; unsigned frameEnded; unsigned allJobsCompleted; - unsigned overlapWrLog; + unsigned overlapRLog; unsigned long long frameContentSize; size_t sectionSize; ZSTD_CDict* cdict; @@ -309,7 +309,7 @@ ZSTDMT_CCtx *ZSTDMT_createCCtx(unsigned nbThreads) cctx->jobIDMask = nbJobs - 1; cctx->allJobsCompleted = 1; cctx->sectionSize = 0; - cctx->overlapWrLog = 3; + cctx->overlapRLog = 3; cctx->factory = POOL_create(nbThreads, 1); cctx->buffPool = ZSTDMT_createBufferPool(nbThreads); cctx->cctxPool = ZSTDMT_createCCtxPool(nbThreads); @@ -369,8 +369,8 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, case ZSTDMT_p_sectionSize : mtctx->sectionSize = value; return 0; - case ZSTDMT_p_overlapSectionRLog : - mtctx->overlapWrLog = value; + case ZSTDMT_p_overlapSectionLog : + mtctx->overlapRLog = (value >= 9) ? 0 : 9 - value; return 0; default : return ERROR(compressionParameter_unsupported); @@ -516,7 +516,7 @@ static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, zcs->targetSectionSize = zcs->sectionSize ? zcs->sectionSize : (size_t)1 << (zcs->params.cParams.windowLog + 2); zcs->targetSectionSize = MAX(ZSTDMT_SECTION_SIZE_MIN, zcs->targetSectionSize); zcs->marginSize = zcs->targetSectionSize >> 2; - zcs->targetDictSize = zcs->overlapWrLog < 10 ? (size_t)1 << (zcs->params.cParams.windowLog - zcs->overlapWrLog) : 0; + zcs->targetDictSize = (size_t)1 << (zcs->params.cParams.windowLog - zcs->overlapRLog); zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize + zcs->marginSize; zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); if (zcs->inBuff.buffer.start == NULL) return ERROR(memory_allocation); diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 92de52d65..acd03b37e 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -53,7 +53,7 @@ ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx, const void* d * List of parameters that can be set using ZSTDMT_setMTCtxParameter() */ typedef enum { ZSTDMT_p_sectionSize, /* size of input "section". Each section is compressed in parallel. 0 means default, which is dynamically determined within compression functions */ - ZSTDMT_p_overlapSectionRLog /* reverse log of overlapped section; 0 == use a complete window, 3(default) == use 1/8th of window, values >=10 means no overlap */ + ZSTDMT_p_overlapSectionLog /* Log of overlapped section; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window */ } ZSDTMT_parameter; /* ZSTDMT_setMTCtxParameter() : From 6be2337c2697f7437bb3fc802b6be4296206305f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 30 Jan 2017 11:17:26 -0800 Subject: [PATCH 207/227] added command --block-size= for Multi-threading only. alias : -B# --- programs/fileio.c | 14 +++++++++++--- programs/fileio.h | 1 + programs/zstdcli.c | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 353fbd54e..abaa15e53 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -124,6 +124,13 @@ void FIO_setBlockSize(unsigned blockSize) { #endif g_blockSize = blockSize; } +static const U32 g_overlapLogNotSet = 9999; +static U32 g_overlapLog = g_overlapLogNotSet; +void FIO_setOverlapLog(unsigned overlapLog){ + if (overlapLog && g_nbThreads==1) + DISPLAYLEVEL(2, "Setting overlapLog is useless in single-thread mode \n"); + g_overlapLog = overlapLog; +} /*-************************************* @@ -272,8 +279,10 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, #ifdef ZSTD_MULTITHREAD ress.cctx = ZSTDMT_createCCtx(g_nbThreads); if (ress.cctx == NULL) EXM_THROW(30, "zstd: allocation error : can't create ZSTD_CStream"); - if (cLevel==ZSTD_maxCLevel()) - ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_overlapSectionRLog, 0); /* use complete window for overlap */ + if ((cLevel==ZSTD_maxCLevel()) && (g_overlapLog==g_overlapLogNotSet)) + ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_overlapSectionLog, 0); /* use complete window for overlap */ + if (g_overlapLog != g_overlapLogNotSet) + ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_overlapSectionLog, g_overlapLog); #else ress.cctx = ZSTD_createCStream(); if (ress.cctx == NULL) EXM_THROW(30, "zstd: allocation error : can't create ZSTD_CStream"); @@ -355,7 +364,6 @@ static int FIO_compressFilename_internal(cRess_t ress, size_t const inSize = fread(ress.srcBuffer, (size_t)1, ress.srcBufferSize, srcFile); if (inSize==0) break; readsize += inSize; - DISPLAYUPDATE(2, "\rRead : %u MB ", (U32)(readsize>>20)); { ZSTD_inBuffer inBuff = { ress.srcBuffer, inSize, 0 }; while (inBuff.pos != inBuff.size) { /* note : is there any possibility of endless loop ? for example, if outBuff is not large enough ? */ diff --git a/programs/fileio.h b/programs/fileio.h index 11178bcca..daff0312e 100644 --- a/programs/fileio.h +++ b/programs/fileio.h @@ -43,6 +43,7 @@ void FIO_setRemoveSrcFile(unsigned flag); void FIO_setMemLimit(unsigned memLimit); void FIO_setNbThreads(unsigned nbThreads); void FIO_setBlockSize(unsigned blockSize); +void FIO_setOverlapLog(unsigned overlapLog); /*-************************************* diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 64f2c919c..c6c8cd2bd 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -370,6 +370,7 @@ int main(int argCount, const char* argv[]) if (longCommandWArg(&argument, "--memlimit=")) { memLimit = readU32FromChar(&argument); continue; } if (longCommandWArg(&argument, "--memory=")) { memLimit = readU32FromChar(&argument); continue; } if (longCommandWArg(&argument, "--memlimit-decompress=")) { memLimit = readU32FromChar(&argument); continue; } + if (longCommandWArg(&argument, "--block-size=")) { blockSize = readU32FromChar(&argument); continue; } if (longCommandWArg(&argument, "--zstd=")) { if (!parseCompressionParameters(argument, &compressionParams)) CLEAN_RETURN(badusage(programName)); continue; } /* fall-through, will trigger bad_usage() later on */ } From cd23dd24af69b3f9b500502c1e10c3047e75ac4b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 30 Jan 2017 12:46:35 -0800 Subject: [PATCH 208/227] zstreamtest uses random overlapLog for fuzzing --- tests/zstreamtest.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index bef8734c7..c053172e9 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -834,9 +834,10 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp DISPLAYLEVEL(5, "Init with windowLog = %u \n", params.cParams.windowLog); params.fParams.checksumFlag = FUZ_rand(&lseed) & 1; params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1; - { size_t const initError = ZSTDMT_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize); - CHECK (ZSTD_isError(initError),"ZSTDMT_initCStream_advanced error : %s", ZSTD_getErrorName(initError)); - } } } + { size_t const initError = ZSTDMT_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize); + CHECK (ZSTD_isError(initError),"ZSTDMT_initCStream_advanced error : %s", ZSTD_getErrorName(initError)); } + ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_overlapSectionLog, FUZ_rand(&lseed) % 12); + } } /* multi-segments compression test */ XXH64_reset(&xxhState, 0); From 92c98a5b216e97fdb257e13e5cbdd06256d9f5d0 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 30 Jan 2017 12:50:31 -0800 Subject: [PATCH 209/227] zstreamtest uses random section sizes for fuzzing --- tests/zstreamtest.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index c053172e9..aeaf02b3f 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -837,6 +837,7 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp { size_t const initError = ZSTDMT_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize); CHECK (ZSTD_isError(initError),"ZSTDMT_initCStream_advanced error : %s", ZSTD_getErrorName(initError)); } ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_overlapSectionLog, FUZ_rand(&lseed) % 12); + ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_sectionSize, FUZ_rand(&lseed) % (2*maxTestSize+1)); } } /* multi-segments compression test */ From 6ccd37c8d43cf3bad135c8bbbcd3a6513196b0d2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 30 Jan 2017 13:07:24 -0800 Subject: [PATCH 210/227] cli : added advanced parameter overlapLog as a hidden (undocumented) parameter for now --- programs/zstdcli.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index c6c8cd2bd..c8cdafb56 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -186,7 +186,7 @@ static unsigned readU32FromChar(const char** stringPtr) } /** longCommandWArg() : - * check is *stringPtr is the same as longCommand. + * 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. */ @@ -220,6 +220,10 @@ static unsigned parseCoverParameters(const char* stringPtr, COVER_params_t *para return 1; } #endif + + +static const U32 g_overlapLogDefault = 9999; +static U32 g_overlapLog = g_overlapLogDefault; /** parseCompressionParameters() : * reads compression parameters from *stringPtr (e.g. "--zstd=wlog=23,clog=23,hlog=22,slog=6,slen=3,tlen=48,strat=6") into *params * @return 1 means that compression parameters were correct @@ -235,6 +239,7 @@ static unsigned parseCompressionParameters(const char* stringPtr, ZSTD_compressi if (longCommandWArg(&stringPtr, "searchLength=") || longCommandWArg(&stringPtr, "slen=")) { params->searchLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } if (longCommandWArg(&stringPtr, "targetLength=") || longCommandWArg(&stringPtr, "tlen=")) { params->targetLength = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } if (longCommandWArg(&stringPtr, "strategy=") || longCommandWArg(&stringPtr, "strat=")) { params->strategy = (ZSTD_strategy)(1 + readU32FromChar(&stringPtr)); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } + if (longCommandWArg(&stringPtr, "overlapLog=") || longCommandWArg(&stringPtr, "ovlog=")) { g_overlapLog = readU32FromChar(&stringPtr); if (stringPtr[0]==',') { stringPtr++; continue; } else break; } return 0; } @@ -629,6 +634,7 @@ int main(int argCount, const char* argv[]) #ifndef ZSTD_NOCOMPRESS FIO_setNbThreads(nbThreads); FIO_setBlockSize((U32)blockSize); + if (g_overlapLog!=g_overlapLogDefault) FIO_setOverlapLog(g_overlapLog); if ((filenameIdx==1) && outFileName) operationResult = FIO_compressFilename(outFileName, filenameTable[0], dictFileName, cLevel, &compressionParams); else From 3672d06d067fda6d87d99c670d49e2b52a083c07 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 30 Jan 2017 13:35:45 -0800 Subject: [PATCH 211/227] zstdmt : section size is set to be a minimum of overlapSize the minimum size condition size is applied transparently (no warning, no error) like previous minimum section size condition (1 KB) which still applies. --- lib/compress/zstdmt_compress.c | 7 ++++++- programs/fileio.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 07c7c1b38..d122d8295 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -370,6 +370,7 @@ size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSDTMT_parameter parameter, mtctx->sectionSize = value; return 0; case ZSTDMT_p_overlapSectionLog : + DEBUGLOG(4, "ZSTDMT_p_overlapSectionLog : %u", value); mtctx->overlapRLog = (value >= 9) ? 0 : 9 - value; return 0; default : @@ -513,10 +514,14 @@ static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, if (zcs->cdict == NULL) return ERROR(memory_allocation); } } zcs->frameContentSize = pledgedSrcSize; + zcs->targetDictSize = (size_t)1 << (zcs->params.cParams.windowLog - zcs->overlapRLog); + DEBUGLOG(4, "overlapRLog : %u ", zcs->overlapRLog); + DEBUGLOG(3, "overlap Size : %u KB", (U32)(zcs->targetDictSize>>10)); zcs->targetSectionSize = zcs->sectionSize ? zcs->sectionSize : (size_t)1 << (zcs->params.cParams.windowLog + 2); zcs->targetSectionSize = MAX(ZSTDMT_SECTION_SIZE_MIN, zcs->targetSectionSize); + zcs->targetSectionSize = MAX(zcs->targetDictSize, zcs->targetSectionSize); + DEBUGLOG(3, "Section Size : %u KB", (U32)(zcs->targetSectionSize>>10)); zcs->marginSize = zcs->targetSectionSize >> 2; - zcs->targetDictSize = (size_t)1 << (zcs->params.cParams.windowLog - zcs->overlapRLog); zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize + zcs->marginSize; zcs->inBuff.buffer = ZSTDMT_getBuffer(zcs->buffPool, zcs->inBuffSize); if (zcs->inBuff.buffer.start == NULL) return ERROR(memory_allocation); diff --git a/programs/fileio.c b/programs/fileio.c index abaa15e53..568b4f115 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -280,7 +280,7 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, ress.cctx = ZSTDMT_createCCtx(g_nbThreads); if (ress.cctx == NULL) EXM_THROW(30, "zstd: allocation error : can't create ZSTD_CStream"); if ((cLevel==ZSTD_maxCLevel()) && (g_overlapLog==g_overlapLogNotSet)) - ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_overlapSectionLog, 0); /* use complete window for overlap */ + ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_overlapSectionLog, 9); /* use complete window for overlap */ if (g_overlapLog != g_overlapLogNotSet) ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_overlapSectionLog, g_overlapLog); #else From 8d8513fb64ca936b26da4153217ee125930d6958 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 30 Jan 2017 14:37:08 -0800 Subject: [PATCH 212/227] fixed C constant restrictions --- programs/fileio.c | 8 ++++---- programs/zstdcli.c | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 568b4f115..960c6e3d9 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -124,8 +124,8 @@ void FIO_setBlockSize(unsigned blockSize) { #endif g_blockSize = blockSize; } -static const U32 g_overlapLogNotSet = 9999; -static U32 g_overlapLog = g_overlapLogNotSet; +#define FIO_OVERLAP_LOG_NOTSET 9999 +static U32 g_overlapLog = FIO_OVERLAP_LOG_NOTSET; void FIO_setOverlapLog(unsigned overlapLog){ if (overlapLog && g_nbThreads==1) DISPLAYLEVEL(2, "Setting overlapLog is useless in single-thread mode \n"); @@ -279,9 +279,9 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, #ifdef ZSTD_MULTITHREAD ress.cctx = ZSTDMT_createCCtx(g_nbThreads); if (ress.cctx == NULL) EXM_THROW(30, "zstd: allocation error : can't create ZSTD_CStream"); - if ((cLevel==ZSTD_maxCLevel()) && (g_overlapLog==g_overlapLogNotSet)) + if ((cLevel==ZSTD_maxCLevel()) && (g_overlapLog==FIO_OVERLAP_LOG_NOTSET)) ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_overlapSectionLog, 9); /* use complete window for overlap */ - if (g_overlapLog != g_overlapLogNotSet) + if (g_overlapLog != FIO_OVERLAP_LOG_NOTSET) ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_overlapSectionLog, g_overlapLog); #else ress.cctx = ZSTD_createCStream(); diff --git a/programs/zstdcli.c b/programs/zstdcli.c index c8cdafb56..c59bf0c3a 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -63,6 +63,8 @@ static const char* g_defaultDictName = "dictionary"; static const unsigned g_defaultMaxDictSize = 110 KB; static const int g_defaultDictCLevel = 3; static const unsigned g_defaultSelectivityLevel = 9; +#define OVERLAP_LOG_DEFAULT 9999 +static U32 g_overlapLog = OVERLAP_LOG_DEFAULT; /*-************************************ @@ -222,8 +224,6 @@ static unsigned parseCoverParameters(const char* stringPtr, COVER_params_t *para #endif -static const U32 g_overlapLogDefault = 9999; -static U32 g_overlapLog = g_overlapLogDefault; /** parseCompressionParameters() : * reads compression parameters from *stringPtr (e.g. "--zstd=wlog=23,clog=23,hlog=22,slog=6,slen=3,tlen=48,strat=6") into *params * @return 1 means that compression parameters were correct @@ -634,7 +634,7 @@ int main(int argCount, const char* argv[]) #ifndef ZSTD_NOCOMPRESS FIO_setNbThreads(nbThreads); FIO_setBlockSize((U32)blockSize); - if (g_overlapLog!=g_overlapLogDefault) FIO_setOverlapLog(g_overlapLog); + if (g_overlapLog!=OVERLAP_LOG_DEFAULT) FIO_setOverlapLog(g_overlapLog); if ((filenameIdx==1) && outFileName) operationResult = FIO_compressFilename(outFileName, filenameTable[0], dictFileName, cLevel, &compressionParams); else From b2e1b3d670f7e73f3b73b02c3734002feb24235f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 30 Jan 2017 14:54:46 -0800 Subject: [PATCH 213/227] fixed overlapLog==0 => no overlap --- lib/compress/zstdmt_compress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index d122d8295..6e63e8801 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -514,7 +514,7 @@ static size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs, if (zcs->cdict == NULL) return ERROR(memory_allocation); } } zcs->frameContentSize = pledgedSrcSize; - zcs->targetDictSize = (size_t)1 << (zcs->params.cParams.windowLog - zcs->overlapRLog); + zcs->targetDictSize = (zcs->overlapRLog>=9) ? 0 : (size_t)1 << (zcs->params.cParams.windowLog - zcs->overlapRLog); DEBUGLOG(4, "overlapRLog : %u ", zcs->overlapRLog); DEBUGLOG(3, "overlap Size : %u KB", (U32)(zcs->targetDictSize>>10)); zcs->targetSectionSize = zcs->sectionSize ? zcs->sectionSize : (size_t)1 << (zcs->params.cParams.windowLog + 2); From c13cd3aa62a2c1a5c2addb8d960ee86e2a5c6727 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 2 Feb 2017 13:50:07 -0800 Subject: [PATCH 214/227] updated dictionary compression paragraph --- README.md | 30 ++++++++++++++++++++---------- doc/images/dict-cr.png | Bin 0 -> 23323 bytes doc/images/dict-cs.png | Bin 0 -> 25052 bytes doc/images/dict-ds.png | Bin 0 -> 27053 bytes 4 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 doc/images/dict-cr.png create mode 100644 doc/images/dict-cs.png create mode 100644 doc/images/dict-ds.png diff --git a/README.md b/README.md index 694bfd53f..b5e16dff7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ targeting real-time compression scenarios at zlib-level and better compression ratios. It is provided as an open-source BSD-licensed **C** library, -and a command line utility producing and decoding `.zst` compressed files. +and a command line utility producing and decoding `.zst` and `.gz` files. For other programming languages, you can consult a list of known ports on [Zstandard homepage](http://www.zstd.net/#other-languages). @@ -47,18 +47,27 @@ For a larger picture including very slow modes, [click on this link](doc/images/ ### The case for Small Data compression -Previous charts provide results applicable to typical file and stream scenarios (several MB). Small data comes with different perspectives. The smaller the amount of data to compress, the more difficult it is to achieve any significant compression. +Previous charts provide results applicable to typical file and stream scenarios (several MB). Small data comes with different perspectives. -This problem is common to many compression algorithms. The reason is, compression algorithms learn from past data how to compress future data. But at the beginning of a new file, there is no "past" to build upon. +The smaller the amount of data to compress, the more difficult it is to compress. This problem is common to all compression algorithms, and reason is, compression algorithms learn from past data how to compress future data. But at the beginning of a new data set, there is no "past" to build upon. -To solve this situation, Zstd offers a __training mode__, which can be used to tune the algorithm for a selected type of data, by providing it with a few samples. The result of the training is stored in a file called "dictionary", which can be loaded before compression and decompression. Using this dictionary, the compression ratio achievable on small data improves dramatically: +To solve this situation, Zstd offers a __training mode__, which can be used to tune the algorithm for a selected type of data. +Training Zstandard is achieved by provide it with a few samples (one file per sample). The result of this training is stored in a file called "dictionary", which must be loaded before compression and decompression. +Using this dictionary, the compression ratio achievable on small data improves dramatically. -![Compressing Small Data](doc/images/smallData.png "Compressing Small Data") +The following example uses the `github-users` [sample set](https://www.dropbox.com/s/mnktkomhkjbf1i2/github_users.tar.zst?dl=0), created from [github public API](https://developer.github.com/v3/users/#get-all-users). +It consists of roughly 10K records weighting about 1KB each. -These compression gains are achieved while simultaneously providing faster compression and decompression speeds. +Compression Ratio | Compression Speed | Decompression Speed +------------------|-------------------|-------------------- +![Compression Ratio](doc/images/dict-cr.png "Compression Ratio") | ![Compression Speed](doc/images/dict-cs.png "Compression Speed") | ![Decompression Speed](doc/images/dict-ds.png "Decompression Speed") -Dictionary works if there is some correlation in a family of small data (there is no _universal dictionary_). -Hence, deploying one dictionary per type of data will provide the greatest benefits. Dictionary gains are mostly effective in the first few KB. Then, the compression algorithm will rely more and more on previously decoded content to compress the rest of the file. + +These compression gains are achieved while simultaneously providing _faster_ compression and decompression speeds. + +Training works if there is some correlation in a family of small data samples. The more data-specific a dictionary is, the more efficient it is (there is no _universal dictionary_). +Hence, deploying one dictionary per type of data will provide the greatest benefits. +Dictionary gains are mostly effective in the first few KB. Then, the compression algorithm will gradually use previously decoded content to better compress the rest of the file. #### Dictionary compression How To : @@ -68,11 +77,12 @@ Hence, deploying one dictionary per type of data will provide the greatest benef 2) Compress with dictionary -`zstd FILE -D dictionaryName` +`zstd -D dictionaryName FILE` 3) Decompress with dictionary -`zstd --decompress FILE.zst -D dictionaryName` +`zstd -D dictionaryName --decompress FILE.zst` + ### Build diff --git a/doc/images/dict-cr.png b/doc/images/dict-cr.png new file mode 100644 index 0000000000000000000000000000000000000000..f555a46c7b99b512966ca81ad8283d7f164ae160 GIT binary patch literal 23323 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 literal 0 HcmV?d00001 diff --git a/doc/images/dict-cs.png b/doc/images/dict-cs.png new file mode 100644 index 0000000000000000000000000000000000000000..ccc02b0d17134efca5b4df2e932e901b474eaea2 GIT binary patch literal 25052 zcmeAS@N?(olHy`uVBq!ia0y~yU<_nnU|Ph%#=yXkKjVD{0|NtNage(c!@6@aFBupZ zI14-?iy0Wig+Q1weg35&1_lQPPZ!6K3dXl{Ip>HR{rT@cYfRn~J6AeT<5k(*LHQ=@jym;n)WTQKe3t2;Y4-+Q9YIe~$5f&&+m_jJ8b&8#IJ$M32* zFsV2&d2$4}Og`{XPe_4L$iuZkX;zoO_sJloLQEnTRSI@=gG>q%T+p#h>BwJ@mI*Bk zt|pu^@01vY6dFTVG#0JoD6|KeuA=B5=*!f;8|)rY#;y!4r#t?`)rtjSt5_hun@}K3cufOk5@a|V&|XYQu6-Z-c40svm!SpwXTWW?Dp#VdVN`| zk_qSLT3g@VxzPSMk5-5L!%t67r#?8)cxiLGf3wU(?@ft^*&_E;Y+Mt&JM7fy(@RrN zPb+${u-)tSw%o(vk=>?;-oLT`|Fd7~W4itCnVa`-_UL%gt-ntrc$v?{ySvMO`%P@) zl|I#9_eI(A>y_Z8ZoKF6elUm{ELyY1XI|AS&CupqrrBb1JDd#UE1w8fe!Uu=dVZd* z-K2KevKcXzPp1Yg_mjPE=O)6>xZJhm`MJ49iHBG!f4|)x+J9wLsJB(wn-y*h(&l+n z{O$jSY|Fi^c6piaIL@@K-nPoz zZQh2ef_V@3RKMT*{mT7Sp{qk`E}Hg<_6Zh5xv2Zikr2L++-K>gxA#kssr};U_`tx0 zXL?r#FF*C{?Ceq#duNviyI!y34$M8XnrYz`rmAl@(|_w-u(&_3?pJ2!wKX#<7y3wg zF`esraa_J$r}+7~&`kd|yi$+?M_Q^>y;%J}a-XH#Z*MKF=X^V#)#a`8A6+ zO`9_11b_Vx=XgWkStb*?_4iy5Ki~04`N5iwBtDUaWp8hNx;+1%N#=Qzj0+AQ#N`dt zzyJIFUi|-1Fs$}VI5BBTZ?fG^qyUCU7k49kpm6u^s}6 z=z=5r$%*cAD?c5%&U=62yI%8q0o>&?yfUw@n!3y3ph`dgvdlm^F_!ADua=52JwAP~ zT>9^qqvG)={`~yRDt`aj+1ab@{{8#DU;YNaV`)eK?^mnWhlrdhzrwG5MNM(G-LM&<|?C!Fui~H@O!jJ#-S9dMByv+C0MR)m?=d52Ynasp$6ng$q z%Z~5&s;_os1w?V2*RT7?eZN2XSdV0ryoyWaxjB|uj@PVxDbn>H#iHa1(n(5-%UX6C24?`z!u-T(i)o@wD1 zwpe`*-MST3^CU72zP}I+T%+>;>-u`pf*$_2x3&g%E3(TJIBbnwzvokz&6f+#O`1aa z4s+{%o{m4&sXkAl+fV(i@p+rYw|ZKa@`c_L`FPOHzVYZ`etVq{4?eJQd|zMxH#=Uw z`m)L`yAKDLlXo9rSbZ537H&Uheu*{vxgo>4^3#*7Hv;7!4zhFCRR?nTb!o(SmVD@H z?(_b-v*cya&Y#a_U+vOz3l?Dbqp?AP^<&lZx#cYCr>5yfKQZ6`(>I~8-}c)KsiOyb zpSB451vGWYbGRk++yC1kHP_+BgRItxs&176u`w47c`xp*{+@N{u`P#R)Q+r$IXnI> z;L!;=FqdUf(et_Gr&a_m-V>R0`|@)C>8YoudA`23mQ^`!%gNVS&D-u5o!0f5V>s)@ z{r&qBT6(ydZ?#<1zj3Gb``wL#`ri@-S+y^u&nayC^7eLkrPZ34ok3}5XDtz>V z=hue)J+W^8zpAf?kJim;`O&vWVq=wi{*Cp_-;VcBGVr~yB{MiwzeDBBr3ISoZw{BR z+-hCf_oHE3a!L2D1d}z)F59EG=dJn|y*+Pg{r}(hn=YC#wa7Dd$xkXj(#kFH>ZPpi z^2uTH))$xe)*W(X*Z#l%@7w%jWxp7fyj?HVjtYo3`nZTJuKplxUAAUd!lOmM@BjZ7 z9{VLgF6Q;5R-a!MllM8x8l7ynYgKLeaQA&(_y&d*=a)M+v#sQ2*9!>b@UwEOa$oea z>jk@f%>uWq7d2~EvGvNFv-#{}`pa4B&xs}`H>Eg6SE(zkPAv`c7v=x|I6k96iLY*z zfHTV(W;L!E#ZrbzE^pk|UrrQR!75$c>BzV^?27mN5BE&E#dKF)D|>I3<1DddL$b-b zX0wNe{1W*>%Dv@v=5x2~`m?}4`Y+40{lD+NU$xQ2{y>$(fj?ydvzuOt$g^v6EUt># zB48lZs>*ObpoZ;d-z%d_Zy)>o)|*|kK}+DA)oY#mKU<0g_fOVfQFs0Fdj0;sud$ni zO{bmM+$hMvbcC(A-;uGKMIuz`!MdIW4e}GjOy1Yc=;iov#6#CEIMVZT8eu=`wCv!6ogm5kkQ@9f4v+=^0_4R+f^Y(rXGxg%tvc79~ zZp(Y4v&;2A?T|=*rKypirNVX4vsiA~a=ssl%Z=uiMAc+{Y@DFN9hI^tX_C&~d*UWc zSAKtZ8GF?CJ5$4-CJghg?J27g*o@Vt3iU!t?=u&V~-wYY~51G;2Q| z6`w7(_!8^a!ezD7Gwo{Y61g;AmH)ETTvWBC-I%YbVfPfDX1UTUYopCG#dO>51?FGk zsl2uF_0zfSCbnD3P9N+L`#k$3)3<6?Ni|tFIe{P@tv$cr?S93-xbh5dm43?0CB7l; zHQy$eTPX$B`aFKv#8xIT_fXeC-G$HRmd9=FPG7aD>6gXjeW%Ule_u3z)xCA^%7a|? zDhKxTT>2P!c&e!5e-Gi(J^K!O&fea6>8Z7x95csCw@Z#+FI7H}n|gAtlfml`?81WF z`4M|tuQS})k#Mfc*(!7I&o5<$--Ul1di_xT|Bv=r`K;?h#C7lH)v!Um`PQlZ_Oiut zbLI%Qb{tCOV6;!^ar14KJ9Vdz#iNefr=(`^v|5r#j8Zxt_4gg|(72g9H)7$Dx%M2l){7`D zdVGcNy#0=&2SPa*zdw0yeV|fWt+=M)_k^+u{w6o&=O6zayW3^q+kzA4&0e%x`sk_% z1=jYITvGDC@tbqs^v)K6ugC8Oa?Gf{>aHz(&i=71`G#< z*DMm4BNA95X-UW~H*Q_{x<5MVp>ij~ z@v8OGll_`+TN?JaTPK%PJZNMWS^i@p`x*uoot6jF!dpr%t>WSQ*SdFpfnTqTWsq&l zk=Ln>xf_mFwld^*F0i}~4LkWfyNmM_B4nE5H}UP@6{*`+#TtKn_pg^v=B#+9^TRnw zRb;W&nrDynq*-=ssSt13;P6QFaPc`y_q(V04jz5mzeAf@op;~sP*0Al8xjvQ)ieD1 zvGZ5UYMz*ycRQcUmD<}sTmD5$TTJ#tLbp%2&qL|%K(kYaS=rXv8SE^)t?IUOhTZ`V z?q$2m!kwz_{O_O0euTHX$Yc3mj>76Sd@q+=trV(l+d2Qk8pfC#pvK8+`xuuE9L>cZ z@lzfezu9Yf@b)=yv3uCi_$+(&`)#-Lg4b1;-zjL83QP_Z^51YMpKE{r$H&L3XFq+i z?XprA-`*#ayr=9}uY0n?DM=}YTf;A>=f;PCG~45CamV->lx;hhd_;X%UsQjb$i7ZX zfNhGr{Fd{Hi$c9+*V_v}ZRc>>(Zu9-GveY=6IL-*1FIt$%bc&uhE?2||9xNXT>H?y zC01uj7ry&E2A*9=W&p zrvI-U4_bd=Q?{#KKwoqa&U>~yzo(WiZoBE9F?rr`-dz!|%x2dlB?^}|Pk12173Fd1Y0Bl}yLkT2^t=Dk=EoTo`G|)x zN0_tr?Dew5m!~i&?xkM4WG~_`U^k^#QW-UovNz&8@oqM*YgoYy9%_gl1NFXXGub zU32;M`I29L&kv@n{XD=GZ23`f;!)B0>t^h?-n}8@%-ln}CNpq~@H2L0eEcGs?Zu=b z;KU?yQJ~Cgk=DzTKV6Dfc78J4SHZENzkB)m5?|%*8UOjR)OUWM z?jZQpIFSd`U6v4D&>^)r$qA(9gYbfmUo#EbKz;2V&H$I*Ktn~4njf42E`NO_oI$WLpdb&708(2IJ_%|rM z8g>3?=#M6z;hA+;q|TYd>8aq(vhU9)@0+_yv-`Z#HwG$P*p?f;GJ1QS<18;hU5+w2 z5te=beq~2)&6;ZUbGCWDpN7f=!^WtsTVNZ zHp~i2-nBvafrrT(+b14>Gxk|unwZnp&)|Bcy+v>v=Y^W>zEhsIb~3n{`0RZc5&Pwp zb(mk~KI>~cbNqT3Tus`Vq|G>MzFrLn4Vi8Ibna^T?3Bo`8yo>Hhp#E~U1@)|n|baD zHE2Fp&%P3q`@t6<)rE5exWv9Z$+W%sS#_gzL%Y5NV^_w(3%iRGcIZERe}I{P zO4Zj_J1-k-YhmHx2yo$5F!VHFob~=-jD)l0zIqq_ouMGhW?%mu|xw$L<(vr@Y!lR;>*2nLU zu*%Vi+~o3Z$78;qKOXmo*37jkeX{5Cxyg#oZ6V&r^)D(o2==IVw#cJ>V#eV%-sHQHfq{wwr?%g(vsPOK8vWR`$!O=pHt8k4v(2>Lo|vfobZ+^* zKqVi+-iO_1*ygKC&0OcVexX6F;)05N-;G=vQ9V0-br;NHc;+r>&~=4ToMU~1Qe3x6 z%+8V@UoQJEEqr`zV{ONA!>X^ZR6zsg>uoM({*>ee#rv1ysI@I8yV~0i)EwtN&{fDX zYsJCU;p?Yq1~1dN{QLL)|8FU_s+uq#R=xpHiwQZU2Y_4aa zUu#yC%WuD{<{((QyZ&;XtlRvG&uVV-kDC3Txng6txc;<ySY_-S7 zdO!CD?ESet|GwUNyWeZJ~(?NZV^0O zTr;?j9!tf3KlR~;IIk|@G_oZK|j$i?|9-kR*^pclPc$d8O?!P2a0la0tv(+L>s{Z|a{?b^=ZcCS_}zH(@S z*}=W34lIfbKK%4iS>VBcUE{4>>-&h8#~oVyzAq45d%*Hy#@F&STO$g~62IDBTfib+ zrFw6-bg+0q@uk)8tL|s7-@9zi3xE5+B|opOuUon}xGpJ$+tKdnl;EIcsZ8HFK(@|b zmNU`p{>FlqpH9;cH}l)Ac&8&}k})AlrK#eOyoeLm@haI3KUj`eab$1LyZdD6^temA zue=Dlzm-LpPkg>!?5-88YG?XEgM?WhIgclF$T4I{IKa5++v)XB7R{EQuvL10rG)3AFGuD6m)_*d+*u#nl_6&}=~<1Og7_za)nRLe zW-JIaygzwk-G`SuKmD1*KUe(1ruAl_zOip6o@(1V`QZaQZx)S3(;V8?O056s$0)ip z>CI!K`jgKSU#qSDx>7@ba@VH0UJh%;&K#N~w4g)nXz(2Cz0a(}9>nhYt#Mbb@n>D- z)CRYMHTQ$}&1RbQjx$No*7XY05d+bUX=kNMcPqKiR|+(~f4D@>ZKa+_pnKKqXVqW( zdYXRj+n)7xZ_RVFKD*WBwfFW1x_s`t^N%re-}_{*&$aU0c~9FJTutof6qkw2djI>v zrJDQ4o1%XzI|!bY&tC0*>6x`$#lfHc6EE_gnN)xN=d+pf!W`RIY&fO0ed*oX+b`Ad z2hLxhC30~>X@_TN*VnVN)pEVfdCz_?=``G%%Onzb+od>0?(Fy4hW~%>AN_P7IN$31 zz9n;?f8RPMI#J0%kT*DAWo5V7{fS3hKg+)LR=@MFKRk1vG=ul_)s7Pv`UH8zXLMy~ z_;Ab04tK9m7-hB&cjZJFR z?^TNWVYecTId`9UQQz{YR-L-SpN|p*g}b$kKpm^n8Fio4&VCmtU2eR&m_=g|>x-c2?inWcxeJ^=PGD7a5Ik#K_cr0)p334`?<>>JAD-x- z#}VLC>}cvZ6I8iO%J#c(tET*-skURe(JU4b#;y!Gzn0kDWhY-G6zy8st6b{i3FaOU39kG;#@N8y`>K}Wl!_|dt}>_=?tz{l=5f1muTI! z<)KD}LOkv6OmAuOB$F1jVVuGf?o{=58iXDs*K^GZs74N9|~a)PXy^?u#%wA7nl z&-gKQF}Rv^3nyM(6}qYTx!qX|A5un-Krist0g)Hv4GN~-!h!73j z@jK`4uFzQ~nM$Rvu5h}@Ek6*ig)%l&p&rfa1yqt1(SLv(j+uL$eZ)`}sv@&>k*2|pw!k_;N zY6}lE2ga0~-Bhc$!A8hHF~QYkx&QoiZu76l6!!}Gc04^Z)A;E{clppm%e6P<-ZpzG zWK!}XKz6BJ^wzA^fTz_i)`FXaVJ{;yh{o>-{r=S5QezSGc4qg(U z^?sVeCJCVq0Y+^ltC}AlvYu79^T|G$TYhh)nbB3BM5XVqulv{Y{+VN#-1fk7s)qPW zLAM?WMP>KCDVD|0PDrND5uBla*~8Y!xlSCEf{ksBXY{iK39p!F<@rj)@$>xZ@9#p_ zANH8LyNYYZ$=;s;Z^Cy=1&QAI8;NZR3Ea~5hEfGFV!R4>7tzDXScGkucY4g00 z+i!1e?LPXFcgA07vz!a@oBy@^7Wx+>Q@iWUtf{5PH!}!r@HjB5!*7m7VA#5tl`fgz z-`#DhJU`jbYGqVtVYkY+yOq!9HdXqu{7{c8x#(JSBe8wu`-r>l2OhcCINbU6_V(k& z#=rUkZ}rJq&k+fj{=CfiRZf~xqm0l7wMBP#l?E%H){Wi29v&JE2v0Lw>#Xt9UHI(Oxze~`wm1vdqVM z@O9bSd&J|#kH`Jtfxk9p-TQm*?K0oltJX_wRWixD6H&V^et%qT)&I4zyRYs1E>?Ma z`b)Dj<$JEnaVO76U)GT3HR-2^LM>C6i^+*E*SH;KeD)FBAk@#WU4v!GagVHfdnydmf*_^&iqqvZOzvH zAF$>^a;t11#|-AZf4|*cnty*^){&OhrLElJTryt^j|W{}7n{7tpuEfSDyOiT$NztS zS^a($Y|Fp7X=%}ce$}22ZKun_Tz5p@{Gy~XVQa^slK0ow&p$KE)O%jVBhFTaZhLpx z(kXMQ-|gIJ8L(t^{QiADvrIgD7fcXnvMzYwVEOrs@zm3kpP6S|Q22U__s&<7>Tfwu zpWoP7oc^>xeoNN+&#q4|r(R#P;)~+#mwb(rR*4pUbrI+YF=Jfz{n^>sQyr4x&b|Kq zQde9#{6|yLJwARd&uL5R<*M}m;kTm>JdfW`y}$kX z{;O*ujq6ibl4H#77Zx5^?K!DcA-^}}<<9rgOcwcz1h`&qP4;zJd?BzkYq8mV!(VH{ z*T*efHF5IeSJtb8ME7mh*?D5wDfy#+pNc*Wd;fvk*qP@X*Vo$%I@*4oS+Qz%nPBn9 zYgbC%t@%}QdzO||-S+Jh%HthZhexdrxjQlZf|tvK2O<|Ilx=$b%v$bO)5Yd)@%9Px zg4Trffodiev)M_pX7^+FS)^QkekOVki^d|w*VeP%E4~&8Yi9`(SkSSIVaocudi@vn ztcx_c|GCudqs31p2f?%AFIA3T)V}gzeXIW(?}Jafn?Y4f3aHkwwAQxV`t#kQ&wJ+G ze){6_sg;MEoV*&8q89Wu7F*8S({elF;|0;{^Y$#!=M%ZWG&jK}=N?CZ%jdSL`;8g< z-X|Z_h14iE-xtU&+?lg)cSU;WJbz)ibsZp8S3r(nnboe?Xd}F!;~C=)v-=xA^@&zY zX9~X``^tBlsxXttMV}Q*Cmt{TESsNRdeKz7S1;{S2T1V?mJ58()~(;S+hY6d#lcNS zt-KnPW;OTqHvN3}>Zl_7a^~)mISj5>ir62&bXm90!Y^JXV_&pgW<*S&@Pdx0S9a@v zzFXA1^K|jN9q&nBJBi-G4#g>7vfpo3f`Z zm)_~gWT~DI>QcB{dETqbOFV^F1~2!!bn39ny1>P4CK(qFcOQ7t~P;HuH9ms zXKH%vN;B^7+ba~)V<+fy`Q@LRbx9>{;o{DBVx<=@%>DJ~!0!G{w!t&zvovwO3EuqR zZ{~3)oB5T`ByVoXoXjI>eC4XgI#XVx5-S>b&!^}5dVU!A#y-3_LKw%=|fC#RgV zes3dboYvzr*D7>Z+1pijrf;#=sPTNv{7A|)Ysn(t%SWdRetoZZKT`DTzSQ4`eGi;E z6jURe=p|SG=c88Cd22IJH5R|WuC}HKG?F8AG^st)Dqc~r_rmJ;>-JsOTy*zTnz$gN zwvtrJqU`uW{lET3~c5>%L% zzvp9H0;}BiwnKl}xPv0@?0nF~oy2Xk?4{AF4azeg>}%P*ui!D$JFd$;vYGSNOKM-s zo4(U~-3woZF6?z&pPY1P)Ehhp;{=_R-Nne-uV4>M%mYN zDj#*KUt!$fQF0~F{p8N)^R$;PUAnQEPs+q&uV2NpGc%1qRhj-Ra8>p`cB{FsxVD9p zbk$c`-?EFQ+D@@2msvS2NI5x4HS4BK(lp-#FA6_DJNxS3z5-b&z8TPHT6{b_9bqf3qOdzEj+D# zYHDi47G8}>UBNEScLMchzkhk|?Ys5!=KOH&sMf9t)m|IAY96QHzF9j%mAf)Leha5b z+w&FTj$*_EzDACSuV7C3Okd? zMH>e*+ZMTn2k)NV>VAD`_P*Ih$yP!dCX8Jf3#%X8etfxx-_w1Al*$A(#;%N62Z|=z za6Q}Iy)JU6g?L2TN5KUhVjIpsknddnYInvy=GFTw3jS?wP?zMNTg)VKagH*d9mCo0 z33@%MjY-N5g1Qdzg|`1Qv8W+c0_Wv)$dF=j)w+`|QJ(go}NG3p$>yaQ@11*7ft<((7wv<_hUt19h5Y zN_66?AHC{+{N-rlvuZv+8L`MIEEr0$WO!3)G7ryE=v9u`7I_l~0*_ONFj2Ba%z=Dpn8x!vd z&dTO{cCWiR`@HGd4ZCOdvIv2OQ(Puju%G>YBxgrE%ObZ1rCDzMuLYylMs1z5=l6-! z>uXGQ&GhtC&}9+{bdJ%xFTYNoufm|KyF0lp_DqV(1VP5G6^v%H-m^`Z(ZZqxs+yNP z{>s7Y86&gsVvYZ$pLblD+E_FeaXMVA@n>kesN%qM71aB?b8}PbX)(~!%PG^Qg;`Jd z7Ze$}(mZ*-s;cV7w?BA0B0{-5&W}UPhsl#8Lh#~L?eJ4m zG=rZU=C>E=P;vSAVsZbpZvA}`Qf4_4)_-2F->>)hcz=KGb!pqGFB4X;-xpOMI=}e& zxv80#mz@MHv;2-!(_OuE^UtGQKi_@2v+nHYhSZ4;RxE2eUOeu%U)N_@^P|8t2{cRa z^zHWhVmp{(%WkG_{FQTio3HKHnim%qZnVhRl67^}t?#VdVkdSKK3)>OKF-VH`s3S=-2WI?9V7GMgTTJuD;~ri;74KK>(iQ~CS2MuDVzBKB{tt&R3RJIgfrC1@mZ+wHB{;@y{}4St*F+*q(b?d!_t z_OUf3bMvGbcJ8p47JA&+m@`6cg0SFR)9h(ercXcJz{tERR9t!C-QDG<=a%2ooblY{ zz@KNX;;|~Ag~Y%50u`Cw&FkdvxV^8ox~9bKexSJc{c4xbwu>(}&h$`7X1eAo!guHL za)0qPJHEfXygcft!m1hkt-pm`%1%yFH7b1Mvh(%2-6?NwZ1f7)$}Qry<|E@ezaN`L zV~q2^-qCZPzwMmBG-sxjoHv4(tV&K4sXQ)Qu3;bee$}3JY(5ol?YqvuySw}J1V!g7 zFTVW!en0%=7JH3*fglqw3~Fbh^K|u(0{p5gE;vqI-KPPZh?yhW(A+SF>|h_lolO z_pG!&%{REe@fTmY!Mc6!|4I}Zm;^T%3H^N?|6l9$G~LPae_yzNdbxc5D*L-~9m-+1 z4B4xGzuoSAe_!p@CO)xgVtO$Wv+dvB+PeDOBC#cE{_}K%)qE!W`}>=F&X(d&U*-y<(pSLn*UdcYkX10~zx1FD38Ju>0-r7%P@9soe-eF&R>vc`w zk0n3lTI15ByE_|11@}ImR~@uXa`mh5*wU%9j9c|J#O!C8WCr=#>}}wY;F@6WON8`8($K4CefeTr4DJ&+^9TXV|PS>=MgPVKjH7N35$`}?_7A@8@WkKezD zYxUgSS98Ak&p55MHdIQod+CH}TCOHX_A~Wdcu>vVSa`O^|NEnW_3Q5ETO5fMpS5ad z*}WFFHOrMm0{MFka$j977ti;-TD)7&&Q#myxxnj5@=PKZ{}h;I0$iN0%r&|1d_{5%7pH{Kf)1&sQ>8|>PK$re%f6&DpXH6v zf{t5HXO?A}+`sI0j7LR4i%H}nQ(3#&{f$ktP90s|aP`cK3oqC0OD_>fQ)(0tSkNK1 zLO90!er)MktqU&?9c}{+JD#7rSoTb8d~JDQp85IZT>RlGEElG>@qS&L%%_T3 z9|BxKW&U!$!?tI3*xzye?@?P=@bTO#o4Il}K7yeB*N)G7p6%{tdiCi}UY|d&*n1Af zt_(iwp#BRnF}3A)O|{#Xvv!w(I!W%bQmmiv9_@DS^J5o#ZNS*I;_11iKl3Ctx|JIz zfcj!@6xRRj3!LT6&wub};>k&>mo_9ie^om8V_Rrg7~7;H*_$3SUs?y5*PMCHoA>eQ zhvh;X%#2+tmc~VFPV24uw!FLBeSZDFoez#S>hJ%vX-?U#%uloL|5+wwx+kFE)s>Z3 zAMJ@h)-SKmE?=|Z!PAz0yI(78zFrA_b#{YCbNCU^AjqZl@%H&*d6OM3f~v%up|@&| zPu?0Uav^@|yJT4!uuV(XP zf^*8-TU(3%|NVYicl(`1aWmJ(&z6nn+dtW12B;_bLU#Il{_EoG9dd5-o!fW>_q3X7 zl)SkS_~AM4?(+B3I)&9&g~vP&z0Tp$JW*pAOUa5AD+Knu`0#f7{V=ChkAHeH9d&9@ znw99kGiP7*^D_C02aP@Vw){H8tmKtjFM4;N#~(AD;!WbeJ)7iYS8`LwlFG zXPkJp=x5$xQ*JeTw+#m8`)xj*Skhwu>xJ^x{l(AEd0sA5=WLnk(7Pc_r|0XT6I@Eo zQ_HW*ZD{Ofex{Rq=Iy?(*P=h&OrO7U=lPVsJ8FNIRermfzO?rDw@aJT&!>3q>}PTE zZcq+9ysojc+g{v#e%$A~ZQ0jAlYFM{EejqTSW;pr-sS$RSIX3Dr?sl@nYk(gLEz!w z$Gd*!Rp*u8+?2X0;UJUc^Et&!%HH1MeCapS$aO^WsmuUIIJ1FGA7A& z9eQOg7n@#O(i@o3D`onm$M~Fw$G4x)=Z9;5kND_6O)ru0jw6$zQbMT9$AB)~=xrXG z)6Op0vc)9RSTW$oMpI}%IAufd?Du;6r0;I~x$a}$jSY@*wO>Pj9^|i6xV6^v{pI=V z_kNpor|`IJ(XW@wPp_~0sx5ogUq~Yg)LnjhYAVy`H;4J{L-=m)xcVO2w%9StUqR#S zT%Xua4K($FHxk zDm<+w*1TtXso#9N*t=7|OrHP8MScCZofRLGo<{I*UzT@w*GX^vy&|{!S#|Gi6*+vc zS4kc=8Tfrl<1MTv45pMl*y4&xT zMZdAjx?HlcNjKN!V2ywI*T4V&z5nmBTanRNFYiaQ=*KUY{g3N#etFs>X}l!+`a00w zAhvHDdfPmcebviCG~n`>tKU(7jvUcCZ-?%f6psY z=ZHT;m{il$i7Ub#LKlYGFInb0TPxnNbjqswABv!cji2q;D>E9BF1|M zYqyK}I25yKt$Q#x)Liz>ieI;-#m}!-nb6_j=OXg2-r((r5_#E#Ps#_Ds@W;#4eVbqPGtJU}lqY+9RY#Htr@X_D&Z_XIjXuAdA8kGOk+HB>$~5TW zvX_recl;{9{mj~Ju78*=-!8jfSDsa0IWND>zL@EnOUdy**-y{y|3_{xs{i}-`f-=w z@U?}H^TVtjrc20wD2V&ftpxVgsr#Ax(y#qCxo`aVSj<(xxZoi5$jn0!@Re%;=0 zw`S~L{yji9YD<8VW&S;zrxVtH*Nxv7V|#JQaiFW&v3sG~kEeOFDw zzVo#SlP)U<%y=(yq2xTzGJlr^X;bZ=``+Lbwz*vJ{cic{>kbKuoaGx1d=LKr@SIrg zs^op2d(H0!Xn&~K&iBm!UtgVM6^{`RX1H83^V;>PH3er62|xLGr)k%%VjUs z5qqS*z_xSEjL$oSSs(6vJ}-Fwz4{&Qzus&<9~M}#m*ts#`nfrpo72yS{aaMalLMQcx6k@>Y|nY`>)ta5YV@}iH-6>iZ+vq2z=tcwmrO4{v(EE6 za433Pw|mLF{$npg;sWE|J~Y(ZersBMiCT}wqL~4wljUwd``x`bf9lcyb$0!_C;l%q zGQ9se{`4LBja$N2R*6Tv>ks*%oMOG<#4=Wqi#aczTIiqs{)I95>)dC*?eadYL*xRPx)ZRGZgn@~+A&FEw3rYn$%cqsrly8)AQg=3VvQ zPjTOzyYF#>sJGpuu-)6dSO1k>W>aUV9`W(5b6(d@-Zj;retp}D^EtELD`ttUc4G1q zUeM9Dp!(}!=bV}E6*GiZJ1|XDbP&`n2+#g3+xNL(LAnB?kcJ+pvGh&+OnHwJ_shp& z2cCVtpz?r)u`7dd*Tw98w|D$r!QlHq(X)YNAIKS>S2}#QJ%6CmLg<7cV^>BWV|DlO z#s5EuXwP~dr*%iI@sV1>(}ib__9}ee+Wxcer@zg{E~Z)4_iUIrpG;mEoER0&97 z(O48&HlwbTJ?!4jU-MLb($pK3K$b1KoUt#Ru|LxDfFNjI*D7fC`xB?Ge&=B@;^wq) z0Zr?sU#jsJmY6)b;W3NGqRbM5JJUZLznCs{Va5~|8FdFi(-#TIz4%3sGxsfLIC57- zpb)f|SG~dP{>KL?`}SLNYP?fwYYzg$0fZVR!uH+$um$NJ^p z_XgdD_SJVq{kgNVIQ7<+%uRWBtwir{%Z-*a%b8K~>dML}tK0Dz`);e9`s8sSmnqC; z%bGPla?q`)$@yz|qzdyuU33597Z(;@U9pXD&ph+uBG;{7w(ZOKyw`WO**@-uwaFV~ zgcKOJgF5Kt_bT1(e!nr^`E*+Jle^{jr>f7dF;Y~$6Mt`g{Qj2T8$ml0nG~h%YIamf z**rp^)W<6$hrBiV06E7af-=?&*;-^|C5^Gow^D(Jwh_LHhZ5hK#NY z-`?IHzBmTdH<#gO>iT^zw)(T$5#ik1JBxSwrs#CDcpPv(aLQv#+Syt2lqTmVJ>+!i zIJ)H7+1cK&udO{g8#L~`s-f)dt)E;?H}@wVW{a;`CTMVfd*0nuuNVbOH7lN%m0w${ zo~mcsm#f$)AhGfEnvNsW`E2hU0&sZ8XgfVxiwhX#|~?(+9~&TTw_i!Xn_Umt%oyFOsgd|Nw(b%BfBK3@D2=X&7j z&e$>o&X$=Daqf#86+j~$iyo@AUf5BX{B>vD->R=K|FZuN{?Tm|qQ2m4`L49k=ZErD zCh#*=%677ua%g|#khLlam_I|gHQ?NAbN?-w{j#U0>xUQD?ydeF_VUMjX(ogFhW$O( zt7D>NlI15m7z%7K5^9~fH|M6&)wtSr!6O^%{{A|3e{<^TX<2+o&9sWMEJ%1)T-3)O-4>R>l0U@ArO>`}%cCu;0Yu^S0qXYn(#V zeP^lM`nkL8?JXn3Eb2M?j0*~-|NowuX`K4)%}uTR=jY~5JzRTX)!uXBQ~jsMZeDb$ zJ!iQ!Xgup-yS!fTG9OPb*45z7c1TDFM~$OlQP!m;o~9qah#6FTNH{2eDQQWo^;`)T z%k$06+2(8;@;w(sa6XBhsnGWE`ziJ2>uVx6r|eeVoDW)ZTJ<&SseN~4@~rn0U;bJn z)%978YvBa3tb48|T(Viadrj`=3irBIJ{5fm8jo;WwDH%mNlqryl%hCZWy%CwpTAt= z&nz`ri)raruchA8^_EObW)it5!+dT_#jN);CnehnozQ3O%BZW9mi3+eJ|f_-bCiTf z1Ir=r1|_LY`xgF`+r*bx>Y>2O*pdW1e zY%Vhx+!wLFsG0Siwc(ueBnRl6Yv9HRn?*n8b?`m*T=0YA!kqKL^Dow%7tWAtWBKIM zpnTDW;rp!jHQ&Py^QA*8_os$LdkKd)T3?>UD- zw`$`A&_MA5&Rl+>W;@T6qZ1uK(~-KR6U^=h%FKPP>zHb& z62Jl)8@B3vwwtv>r-`L&3WKZ3>AvRQLjNp&oe@&mJlSEQ5NN=+(B!@`%kdmg(c6{5 zChPUtmO0~$6H}kif{tZ3H{Pnze{!4k@*1PblNmUlOki*|;ob4;s_dNqKFm^+CxL4T zHd&v~cNv5xPjGPI3~&i9*<*J9_LfZLiV6;9&<>Ld&H$IaCXcpd?mN!-FkDE1QJ(R& z(ysFNabGVyWaE|k!S}4Up)+Q&8}G)c=c3ED-8Q+uS@pyp6#-eMFxQfci(HF9J?L9o zv!Cw!{f^tD{yAbEQu9>q?QK&N54U+P_nW&a(A+m%cXF#fxIoi7JKLOp&Hh&B4IDxW zjO`o|m78iKBWDVokIvs4`t8ll#UDb#!hRX(Mr?36)+?H(%J+RrNJBnyqF=D~pGMDWkU1tX~F;o=HvQ zQ!9qfDVjV}5e!-y+_~vb`^9w7w4!i8ZQ+Btp^@_x&iFC0v3ycnRJD*(F91Af>TKXE z9`B}hKQ`K?>F2rYlTwP*4EQ-y{1oz)R{eD>e|P8QYF+R=;UnM6Ke;+9SYkIT)iG`O z$*MfTTrl^}j*VGQetmsi+uItiDOM`r15#7aVs<}v&h6fU?VDb@IvqImYL91h^p=c; z;L%RQ$+ADE>Bp}VbFBLQZf%#LZ+el>^~<0oEwP(p-rH|G&&w3%BJ%7<_4~cc|9zS3 zQIPI5r~HX!!U2Y#e?Fg2J=DT^N?$n7_xru-^;>do23381C90eSilZiJz1?Z98?0|W zQBq4dIwyWt$;uvC>upkO9mV058>%HKc z5;My!NX4mP){_SZn_25WupfP8k@uU2YuU@DPrIbHXJ6Ng+*`G^=>MTD?b-X*uKOl^ z`>9!D0_$n#*|SZv(@I1xm$0_#xbvDKjoYc_Sy)}FnBe+=*dFa&m~(j&j@W$lRCC(Rb5GtsrXZ=Rd0H-=Dv#KE_f!d7{!R#_-&Ij}M;w`s(WG z1uSWbjbAt})cE&@+XV-^Twb#F{+^RR&zl@HtaM|^*q7d-yMCs|qP88G`=pzajIvoW z_C-6-yxgR*h^^x5VdoFm&N}V8e`~=ipGO>wJM?3JeV#0n*?ar!f{tY`;&bSyhj zm1g%}Ut6oK8?|M^-*30|H}v(COg_MA8|6J;yI!#}Oc`JjL_sNvsDKsj1abZt~=GpHN z;VxCpNn$VGndjBSxr!{_eQ$5IcfXwNs?ZM&%zPcS$305V%rM+k^)*X%7N}&e{QT@> za=)#bY4)`#-1>VArkROLo7^vUoSHhRVK>Vstwjeq1k?A|{S^|5-IUU)6TQu+jbC1` z_Scudm$$a(>uZOtnP7ge!nut{QfcYZrJSGBcQG%H=P*C}-G|dip3`NrL!G)5uf+bZ z*P@#&FDduiY>IlN+-KpWP$Cd{oAH@7i^%Htxwp5KX8jXh{C}=>`3<*YpFBWa$_d{m zY8-h5>M*qNN_&A;P6jS^yK2SU)Yhuf!?AZ^=<2Xnudl6*-g_}Je|OHlX4QY7mQ9hn z!g?hx<3CTQ$4?87uMOSssZTH+G;eb!{q3!-q1uX*J~DO5TIJo|mU}J3!6DZ4{@0_7 zi1O#IUemwKQAXA z^Y+ma`&v$;j*QF8dW*mB%f7C2N2I}iS6XU8k^wj85=Vvg#fv05omdnX8Qj$0@qj7w z`ntJ${(if?QS0!T`#X!%-|W73phlzVtaV#Vbp?k{`Hl&RdE)gV0kz*gOlAT3Liyr? zG?Uz0A-!H-?K)I^zHQoMWa@PFV@19o(~-u5{tmO>@7a9ne4C-l1aDBw{Fph5^0}$n z;a=^0vPru)aPb${9c*IVXpwVfu5~!4#*M#wfsXUJxvzHjpD)Ypd$(>+KR@r%5>MgE4+q(&-s}&p++ph& zVwm5xlA$f1<3P=ECmFZrX3H&4OlR>pFvsDZi%9W&{e3?cIrx+w;P~=t_4;XgvAbSq zJoTu5+-uGywh+{s*wM_X_W$Se`TET4d_7CMf3cnYzG6OOPJIN&6JHlYroFG%?Pghh z+`8;dz@Zg)XFW;;RY7_Cex{k~a}|p!2e;*OG_b#4^XR4n(?i7x=7Lv!+lp)VH1EA9 z{=bvG|3UvlX{K8>?Jv)nWPQBJ5#2R z#){3gvr2niie|i@@&AZ1o5q5PFN(}Io!~4`ky^yJtl zO`PV>v{TWc*v>qr*L&&DydJIfoYr!cA#wf3n?>~*`&_thl*fIR<@-`o63b|PxcJ3E z^S(**?Eh_4Z=0<$0W|+)ID5{!gNy%u_#mJs_f(`fs?~g5)&6To>*s6yNKs%6W)cYu z4w`l;^LgdEebG!=D`y<`xAyhhl_J4lU{doiq~gb>8dlUIRjiwJ7sFKKFc00{%7ny!B}J3vud^rGgLv%T7mAl z9<$z;S=`cFdS7+=DbMMAn`3oOcWZ!J;#P9+ynj4?VeH-j*P_bUmEq;`Qgy-4d2;J( zmM;4_PyfBVTYmOk7rVYXTz|9EjMG+) z_ica6)Y`8NPHl{15((7r<=OsqwYA0nnTiKwQWpQ5$Gk$v2;!(nm9yo>en0(OKil3v zvbff!E2Hi2`m^87a&K|S$sH2lV=}pa_#%JcDppsM=6iZ(pZ8wnRQGtC*PeFa(xX$C z7JG-Es}~EOZW$Qel_7OEV$HMa$IjJDE_$h{G75RLGq_%vAaD{v+q&4@DPLb*4ceX;n|W~&E2{~o_B*A< z6+#?aJKo*h9d4F)N8{`))5%h%pMG0u%z7XA*K@;Nl?if<0bLqy4UdoaPjBTGKlSeJ z?xlWnt%~jx9zS_uq4Q^@WhVD8TX)Ww#j?mpp+qSvGEy`7SWjS@iOc5X<9*BF7Z!a&eCnuEq*G&7>q1-3X zBcixz8}pg+9r}r1=Q3r@);q1txSg{lPo+yxQEbu%rFXn*qqYXcE0%3*s{5uMem_?* z)lx;kmT94IU`NWHhDFgOcedr;Uh`#=Sir3JVFjB6ggSH@FLY|$nf2|>&Bw0=AO-5B zo`%adaFN67cMe|Jk{Jv-+U@C+$^Lx4vXO#93XK`U5xYuOG6{GIUX5+84Ajv`OgRNHN@;SGK_7_C!x`Yh>u4wm>NBzkF6dwjOq2n!A(P%oMqpz>d!7~@vH-CSR2&3NS&pWF zjusGbX;6}KX)Oe?6FeG}vN*aEL<{VnfO-k;xN5M-K`xc4Y{N9s{u( z6dAi#C~BxFgH9V%U=q2gpw$CnGdhD7GX?m#fgC;Rfu_+IfL42>Nyq`bT5lL<;+=DH zK*v(n{3xh=yY>2<%xBAdXFqw|Z|`?=p8C8Br@H^Y?=SaBpY;IL{!5=%sW$D-7P*Z) zoMt&wCoiaQOLtrlCRW*P_xjC2d$YX5A1{CZYtUmX>UpTEi|gj5)b2MoHY%%|+CBDP zFERD^6<&3zrN*9L4lwhd*i-rW$-m$4r&oP_wNeLkkepkul&7bs=bDc7`+j+u<=v6^ zJ@4(YS*F=6P0O7FY`^StneX`Z%#t}SPmS;CG4{E<{gjfas911HbNLDIv}Qn$SN-4D z@!E-J(?uV@p7`oU=c}1{mMi-X|NX|{Ql*l`uk6hw-RR6`>GDBbUQ1j1@#3Oiwt==m zvL^fH&73y*mBpR;wv1gFZ9Ink6?aRopH!V5qXg;=O^>gO1f9#-E?2c8rsSe)(Ehr= z5hp=s5Twtq4ZBzOI~O#l0czUsDu1t+KEHO`m9^2~OD{i-t@(KL)BOK`+@GD9sSK*1 zRpAUyW{d_*(yPZ##B~_;20OP6{ncqjn zS?%r1N=0@N-+wXTGW<|I0$zR%~ZKV1p-=k%#4uz$bjbInNu`&F+O zmg_}r@wf`wKM6YC^ySsn)BpT=Za*C~7nEq*e9EzYajtgwx+!;emwW&I_LlYQv7Jkq ze^TFA4E%UwO^7VWE{5Wpk=Ue|`6C00&0;u`)_V)JE zTd&8hcKZ%m#Xmc5muErU%Mbs4zdzl~Z>Iq|Ptdu3-Ak+VPoBkg!dan}2K_tA-bT$d z$($s#QrOSJ@k6`3_M<%y=T*N8w4Vi9MzVBz+^Ra09~U39^G$TV@bux5!<~KdhaK5v zLvrPxeOJ&?pI@^{rt--|uO07qz1I8rdH#Q$`@ipfzrrtQzvg!Se%mm;ItPJ*CG86T zABpc5_)`Je4R_?yisqIN@-B=$hTB&jesr{()xIJ|WDldbY|I3O1spS;vv9M-+~~7- z#Id#4?#Ds?I)UGPC)V%(7sXlH%*LB?VuE7OVmDq%^Sl_H{ih_p=RZ8u+NIdXzDzfI zTZmFq-50ieTLFH?y-%k_r|kdxZSq3rcCIre9fmDtk+zRdNA9cHx$5(?v$LO`F+Lws zdSGWuF=Oh>hM$oJhg;Pne=qZ!yQ)PZ_vhLCI_211B@>U!Rr|!A*_)~hI=*7hXTQ+v z|Nj2gzPF>H=GV*RpWf|$|7yvr&sW3aLzx@Qa&Lunm%rt9UljQ*)pFt5s;%o+m%qKW)Gc;JcDI;r($7QN z8h4pzUUJzgxLBU0C(qzk$7QdhU<-VXa4@bU4tKA*k0jTVVVz8~V&U(tOs&|UWF z-L=2Cln)B~|4B>aUS{m(FQ9C@X#KQ>uejY(S4f9FR$OwT`{LUcPT@zB!+!H^Y?=yM zG<QCOr|6Jgl zzHHT(7Yp^`9^75-FE6`ROhNHh`rOhm9lqLzVj=NO$^EurN0U=}y5-r#cFo<$)0bPI zGAp(H+uPgbY6~=89bo1UvFDiajYGcXL*uU_FOExpIVtwOtvN7lLW7dNUz+ZMAAQM* zSqFYMU$n342@u#Iw3c~Pm`DWkA(sr!NcstXGoSNZN}cwrH< z<1urpmJy{#QJ?s!if2#}Q-?m*ZC+c7N`uh6%Spr9&yUHd?I^^s<)FilN zd;H_iN9)@kB$ae#GUzVwU=}>Gvq4kYr~1W&6}7))E>?c*+!;~9{=mj@?w86nrwh7LnR#q6ztO)3<%+^^eI4WxTz}KWokl4hzY`M@RpAd^qeF^}e&` zOVo!AbG2BOUbXl8lTdo$bE`q`MD@ChPRqF;tG5V#K5NiyxR)_lE?8*>ds|0RNx%P$ z%Le~*S-<%1$y|53Eg*qQ;o?!h<=y{}oREKhw0!^XyFX$xYuh{f{vBmCv3p_laF17- z+v3_^GOm&*3NiTO5coSW0m28BtiX*0iH6D(_gds|h% z;I-VYjJ|5NN1c-e!&Bd{V7;IC=>_ZLm{4Xl&1XT0b%`wO!9v9aGlX^97%oex>51tn z#2F;+|2+5o3F!QwppWmK@As;kT({M*USU^^-5<60Mn~OwwHHNAef>w}SbR*E2p{W{ zU9Bfo|LgMnDXq*4_dJ`G-P7$be@pgtznNcVgdJ$*{roGC!{mAKozqn%OMLxnE%ruS zS{bY^J9V=EjQtXG=hgjsY1rSGQ-8#AWk!>eVb}3LGCvmTMo1jumr?2xyj*dsbf~i>ntNkMi=d{@rKzExlOp~cJk(ikXDqHRQ};$-_VfAmaR&9>J54GW zo*1W8s|)#5=^S`?yzgo<%Zyc^j^jLbiMPq7mXQZKwk6Q$2U_ zbE`!hXgn?AbJO4Mr^|*Lixc$Nc)= zvtPJwpJ|-F>SZ730HjVnd5M14Kp*`?Mv*m&PeAAV7Tsk^zTEKB-(ZGa?XFqhd*^c* z=q|r;$u-Jl^Gh9D{U!FLk-v8TJF+eoZ5}DiD=0k3FYUPe`utscQhrY` zv)emQ`07VycD@ifCX1aP+l8g;)_lKLou5t|c z_^Kbfnw!%azX)*6nEv7Do|nsJKly$C|30CMKT3AxeDSWCStW1hZ~fNfC+I};rPJet zI>e8#t#cFz%is6oQ~j64@|yp>D!3H%`h45wOypJSl;2(cFIc?&OT@?AdwVLk%FimVuc^^(1nemYXZs`c(D&P>}1q zw2)s>D$eoCw8h&4^`|U+&wl|}cQisg@T*}36KmH*~il?m!hGq$(=lxMvkpdZw=VmjM)&juDFwT9XD z1=$e5gapVqZvb3~{RhcRP^#=k{k7DByI{hRlyI535A2Dq$!l#;08z!a$L zAUM*5kKD7~U-B|&HS1;r(4ayGQ=`<>sZ)y=T?p7V5i}~1BfR3$`Tu|F8)`V-ukKj) Rg@J*A!PC{xWt~$(69BClL{$I) literal 0 HcmV?d00001 diff --git a/doc/images/dict-ds.png b/doc/images/dict-ds.png new file mode 100644 index 0000000000000000000000000000000000000000..858cad685509b24a9d00655e73369cacdc8dd5b2 GIT binary patch literal 27053 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 literal 0 HcmV?d00001 From 4dfabc45d925e491c84e37cc804f9a307043401c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 2 Feb 2017 13:50:51 -0800 Subject: [PATCH 215/227] removed unused chart --- doc/images/smallData.png | Bin 36133 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/images/smallData.png diff --git a/doc/images/smallData.png b/doc/images/smallData.png deleted file mode 100644 index 69edf58dfca0f6172f87b043a63c5052e5e3849c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36133 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
-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 P3=9mOu6{1-oD!M<5(H<~ From 3dc85bae666b9837a1634a2a70afd71edf59bc92 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 2 Feb 2017 16:19:09 -0800 Subject: [PATCH 216/227] minor : fixed zstd-frugal fixed a minor unused variable warning when compiling zstd-frugal target --- programs/zstdcli.c | 1 + 1 file changed, 1 insertion(+) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 9df2a4cb2..6ca294fc2 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -576,6 +576,7 @@ int main(int argCount, const char* argv[]) BMK_setNbSeconds(bench_nbSeconds); BMK_benchFiles(filenameTable, filenameIdx, dictFileName, cLevel, cLevelLast, &compressionParams); #endif + (void)bench_nbSeconds; goto _end; } From 030ac243a0f38764b9faf7e68ec0307f0e16d60c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 2 Feb 2017 16:49:34 -0800 Subject: [PATCH 217/227] Changed Makefile to generate zstd with .gz support by default .gz support is detected by a runtime test. --- programs/.gitignore | 1 + programs/Makefile | 37 +++++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/programs/.gitignore b/programs/.gitignore index 24f96cf4b..eeaf051d6 100644 --- a/programs/.gitignore +++ b/programs/.gitignore @@ -8,6 +8,7 @@ zstd-decompress *.o *.ko default.profraw +have_zlib # Executables *.exe diff --git a/programs/Makefile b/programs/Makefile index 4392939dd..fce47b12e 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -41,7 +41,6 @@ ZDICT_FILES := $(ZSTDDIR)/dictBuilder/*.c ZSTDDECOMP_O = $(ZSTDDIR)/decompress/zstd_decompress.o ifeq ($(ZSTD_LEGACY_SUPPORT), 0) -CPPFLAGS += -DZSTD_LEGACY_SUPPORT=0 ZSTDLEGACY_FILES:= else ZSTD_LEGACY_SUPPORT:=1 @@ -66,6 +65,12 @@ else EXT = endif +# zlib detection +HAVE_ZLIB := $(shell echo "int main(){}" | $(CC) -o have_zlib -x c - -lz && echo 1 || echo 0) +ifeq ($(HAVE_ZLIB), 1) +ZLIBCPP = -DZSTD_GZDECOMPRESS +ZLIBLD = -lz +endif .PHONY: default all clean clean_decomp_o install uninstall generate_res @@ -75,13 +80,24 @@ all: zstd $(ZSTDDECOMP_O): CFLAGS += $(ALIGN_LOOP) -zstd : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) -zstd : $(ZSTDLIB_OBJ) zstdcli.o fileio.o bench.o datagen.o dibio.o +zstd-internal : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) +zstd-internal : $(ZSTDLIB_OBJ) zstdcli.o fileio.o bench.o datagen.o dibio.o +ifeq ($(HAVE_ZLIB), 1) + @echo "==> building zstd with .gz decompression support " +else + @echo "==> no zlib, building zstd with .zst support only (no .gz support) " +endif ifneq (,$(filter Windows%,$(OS))) windres/generate_res.bat endif - $(CC) $(FLAGS) $^ $(RES_FILE) -o $@$(EXT) $(LDFLAGS) + $(CC) $(FLAGS) $^ $(RES_FILE) -o zstd$(EXT) $(LDFLAGS) +zstd : CPPFLAGS += $(ZLIBCPP) +zstd : LDFLAGS += $(ZLIBLD) +zstd : zstd-internal + +zstd-nogz : HAVE_ZLIB=0 +zstd-nogz : zstd-internal zstd32 : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) zstd32 : $(ZSTDLIB_FILES) zstdcli.c fileio.c bench.c datagen.c dibio.c @@ -118,17 +134,6 @@ zstd-decompress: $(ZSTDCOMMON_FILES) $(ZSTDDECOMP_FILES) zstdcli.c fileio.c zstd-compress: $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) zstdcli.c fileio.c $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NODECOMPRESS $^ -o $@$(EXT) -gzstd: - @echo "int main(){}" | $(CC) -o have_zlib -x c - -lz && echo found zlib || echo did not found zlib - @if [ -s have_zlib ]; then \ - echo building gzstd with .gz decompression support \ - && $(RM) have_zlib$(EXT) fileio.o \ - && CPPFLAGS=-DZSTD_GZDECOMPRESS LDFLAGS="-lz" $(MAKE) zstd; \ - else \ - echo "WARNING : no zlib, building gzstd with only .zst files support : NO .gz SUPPORT !!!" \ - && $(MAKE) zstd; \ - fi - zstdmt: CPPFLAGS += -DZSTD_MULTITHREAD zstdmt: LDFLAGS += -lpthread zstdmt: zstd @@ -141,7 +146,7 @@ clean: @$(RM) $(ZSTDDIR)/decompress/*.o $(ZSTDDIR)/decompress/zstd_decompress.gcda @$(RM) core *.o tmp* result* *.gcda dictionary *.zst \ zstd$(EXT) zstd32$(EXT) zstd-compress$(EXT) zstd-decompress$(EXT) \ - *.gcda default.profraw + *.gcda default.profraw have_zlib @echo Cleaning completed clean_decomp_o: From c3cba9d8580ca31cd6cb4103fd86919631c146be Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 2 Feb 2017 17:12:50 -0800 Subject: [PATCH 218/227] fixed silent conversion warnings in GZDECOMPRESS path --- programs/fileio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 960c6e3d9..5b9c91bc4 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -743,8 +743,8 @@ static unsigned long long FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, co if (inflateInit2(&strm, 15 /* maxWindowLogSize */ + 16 /* gzip only */) != Z_OK) return 0; /* see http://www.zlib.net/manual.html */ strm.next_out = ress->dstBuffer; - strm.avail_out = ress->dstBufferSize; - strm.avail_in = ress->srcBufferLoaded; + strm.avail_out = (uInt)ress->dstBufferSize; + strm.avail_in = (uInt)ress->srcBufferLoaded; strm.next_in = (z_const unsigned char*)ress->srcBuffer; for ( ; ; ) { @@ -753,7 +753,7 @@ static unsigned long long FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, co ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile); if (ress->srcBufferLoaded == 0) break; strm.next_in = (z_const unsigned char*)ress->srcBuffer; - strm.avail_in = ress->srcBufferLoaded; + strm.avail_in = (uInt)ress->srcBufferLoaded; } ret = inflate(&strm, Z_NO_FLUSH); if (ret != Z_OK && ret != Z_STREAM_END) { DISPLAY("zstd: %s: inflate error %d \n", srcFileName, ret); return 0; } @@ -762,7 +762,7 @@ static unsigned long long FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, co if (fwrite(ress->dstBuffer, 1, decompBytes, ress->dstFile) != decompBytes) EXM_THROW(31, "Write error : cannot write to output file"); outFileSize += decompBytes; strm.next_out = ress->dstBuffer; - strm.avail_out = ress->dstBufferSize; + strm.avail_out = (uInt)ress->dstBufferSize; } } if (ret == Z_STREAM_END) break; From c2a4632789bcf8af3e69d95d358ccb8dd99c6e1d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 2 Feb 2017 20:54:14 -0800 Subject: [PATCH 219/227] release builds use less debug symbols and warnings release build are triggered through either `make`, or their specific target `make zstd-release` and `make lib-release`. --- Makefile | 7 ++++++- lib/Makefile | 13 ++++++++----- programs/Makefile | 17 ++++++++++------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 14d1510ab..d86db7cb3 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ EXT = endif .PHONY: default -default: lib zstd +default: lib zstd-release .PHONY: all all: allmost @@ -50,6 +50,11 @@ zstd: @$(MAKE) -C $(PRGDIR) $@ cp $(PRGDIR)/zstd$(EXT) . +.PHONY: zstd-release +zstd-release: + @$(MAKE) -C $(PRGDIR) + cp $(PRGDIR)/zstd$(EXT) . + .PHONY: zstdmt zstdmt: @$(MAKE) -C $(PRGDIR) $@ diff --git a/lib/Makefile b/lib/Makefile index c4a5ecb91..bef69543f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -22,10 +22,10 @@ VERSION?= $(LIBVER) CPPFLAGS+= -I. -I./common -DXXH_NAMESPACE=ZSTD_ CFLAGS ?= -O3 -CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ - -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef \ - -Wpointer-arith -CFLAGS += $(MOREFLAGS) +DEBUGFLAGS = -g -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ + -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ + -Wstrict-prototypes -Wundef -Wpointer-arith +CFLAGS += $(DEBUGFLAGS) $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) @@ -59,7 +59,7 @@ LIBZSTD = libzstd.$(SHARED_EXT_VER) .PHONY: default all clean install uninstall -default: lib +default: lib-release all: lib @@ -85,6 +85,9 @@ libzstd : $(LIBZSTD) lib: libzstd.a libzstd +lib-release: DEBUGFLAGS := +lib-release: lib + clean: @$(RM) core *.o *.a *.gcda *.$(SHARED_EXT) *.$(SHARED_EXT).* libzstd.pc dll/libzstd.dll dll/libzstd.lib @$(RM) common/*.o compress/*.o decompress/*.o dictBuilder/*.o legacy/*.o deprecated/*.o diff --git a/programs/Makefile b/programs/Makefile index fce47b12e..ce90bd457 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -26,10 +26,10 @@ endif CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress -I$(ZSTDDIR)/dictBuilder CFLAGS ?= -O3 -CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ - -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef \ - -Wpointer-arith -CFLAGS += $(MOREFLAGS) +DEBUGFLAGS = -g -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ + -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ + -Wstrict-prototypes -Wundef -Wpointer-arith +CFLAGS += $(DEBUGFLAGS) $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) @@ -74,7 +74,7 @@ endif .PHONY: default all clean clean_decomp_o install uninstall generate_res -default: zstd +default: zstd-release all: zstd @@ -92,12 +92,15 @@ ifneq (,$(filter Windows%,$(OS))) endif $(CC) $(FLAGS) $^ $(RES_FILE) -o zstd$(EXT) $(LDFLAGS) +zstd-nogz : HAVE_ZLIB=0 +zstd-nogz : zstd-internal + zstd : CPPFLAGS += $(ZLIBCPP) zstd : LDFLAGS += $(ZLIBLD) zstd : zstd-internal -zstd-nogz : HAVE_ZLIB=0 -zstd-nogz : zstd-internal +zstd-release: DEBUGFLAGS := +zstd-release: zstd zstd32 : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) zstd32 : $(ZSTDLIB_FILES) zstdcli.c fileio.c bench.c datagen.c dibio.c From b02ac8d613bdaf7baecc8548b3a56e28586d70e4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 3 Feb 2017 08:43:06 -0800 Subject: [PATCH 220/227] fixed pointer conversion warnings (C++) in gz module --- programs/fileio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 5b9c91bc4..a9e1574a6 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -742,7 +742,7 @@ static unsigned long long FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, co strm.avail_in = Z_NULL; if (inflateInit2(&strm, 15 /* maxWindowLogSize */ + 16 /* gzip only */) != Z_OK) return 0; /* see http://www.zlib.net/manual.html */ - strm.next_out = ress->dstBuffer; + strm.next_out = (Bytef*)ress->dstBuffer; strm.avail_out = (uInt)ress->dstBufferSize; strm.avail_in = (uInt)ress->srcBufferLoaded; strm.next_in = (z_const unsigned char*)ress->srcBuffer; @@ -761,7 +761,7 @@ static unsigned long long FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, co if (decompBytes) { if (fwrite(ress->dstBuffer, 1, decompBytes, ress->dstFile) != decompBytes) EXM_THROW(31, "Write error : cannot write to output file"); outFileSize += decompBytes; - strm.next_out = ress->dstBuffer; + strm.next_out = (Bytef*)ress->dstBuffer; strm.avail_out = (uInt)ress->dstBufferSize; } } From 21eb80d4850e4156d4adc3feb7d77b4f6b1c37ab Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 3 Feb 2017 14:25:07 -0800 Subject: [PATCH 221/227] remove zlib detection artefact result of compilation test is sent to /dev/null --- NEWS | 1 + programs/Makefile | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index f404f6e37..072caee59 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,5 @@ v1.1.3 +cli : zstd can decompress .gz files. Feature can be turned off by targeting `make zstd-nogz` or setting `make HAVE_ZLIB=0` cli : new : experimental target `make zstdmt`, with multi-threading support cli : new : advanced commands for detailed parameters, by Przemyslaw Skibinski cli : fix zstdless on Mac OS-X, by Andrew Janke diff --git a/programs/Makefile b/programs/Makefile index ce90bd457..f94bffd44 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -66,7 +66,8 @@ EXT = endif # zlib detection -HAVE_ZLIB := $(shell echo "int main(){}" | $(CC) -o have_zlib -x c - -lz && echo 1 || echo 0) +VOID = /dev/null +HAVE_ZLIB := $(shell echo "int main(){}" | $(CC) -o $(VOID) -x c - -lz && echo 1 || echo 0) ifeq ($(HAVE_ZLIB), 1) ZLIBCPP = -DZSTD_GZDECOMPRESS ZLIBLD = -lz From 762ddeeb9ee6f83fdb460a96a68a7881f5dc1c4b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 3 Feb 2017 14:35:42 -0800 Subject: [PATCH 222/227] fixed zstdmt compilation under Windows minGW/MSYS2, by @inikep --- programs/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/programs/Makefile b/programs/Makefile index f94bffd44..9746ec4ed 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -139,7 +139,9 @@ zstd-compress: $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) zstdcli.c fileio.c $(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NODECOMPRESS $^ -o $@$(EXT) zstdmt: CPPFLAGS += -DZSTD_MULTITHREAD +ifeq (,$(filter Windows%,$(OS))) zstdmt: LDFLAGS += -lpthread +endif zstdmt: zstd generate_res: From b4016ff02fde7b3f53172934b02821f56e1a5671 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 3 Feb 2017 16:42:07 -0800 Subject: [PATCH 223/227] Add cover dictionary training to zstd.1 Tested with `make install && man zstd` and visual inspection. --- programs/zstd.1 | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/programs/zstd.1 b/programs/zstd.1 index db79be594..384f69e3a 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -251,6 +251,30 @@ and weight typically 100x the target dictionary size (for example, 10 MB for a 1 .B \-s# dictionary selectivity level (default: 9) the smaller the value, the denser the dictionary, improving its efficiency but reducing its possible maximum size. +.TP +.B \--cover=k=#,d=# + Use alternate dictionary builder algorithm named cover with parameters \fIk\fR and \fId\fR with \fId\fR <= \fIk\fR. + Selects segments of size \fIk\fR with the highest score to put in the dictionary. + The score of a segment is computed by the sum of the frequencies of all the subsegments of of size \fId\fR. + Generally \fId\fR should be in the range [6, 24]. + Good values for \fIk\fR vary widely based on the input data, but a safe range is [32, 2048]. + Example: \fB--train --cover=k=64,d=8 FILEs\fR. +.TP +.B \--optimize-cover[=steps=#,k=#,d=#] + If \fIsteps\fR is not specified, the default value of 32 is used. + If \fIk\fR is not specified, \fIsteps\fR values in [16, 2048] are checked for each value of \fId\fR. + If \fId\fR is not specified, the values checked are [6, 8, ..., 16]. + + Runs the cover dictionary builder for each parameter set saves the optimal parameters and dictionary. + Prints the optimal parameters and writes the optimal dictionary to the output file. + Supports multithreading if \fBzstd\fR is compiled with threading support. + + The parameter \fIk\fR is more sensitve than \fId\fR, and is faster to optimize over. + Suggested use is to run with a \fIsteps\fR <= 32 with neither \fIk\fR nor \fId\fR set. + Once it completes, use the value of \fId\fR it selects with a higher \fIsteps\fR (in the range [256, 1024]). + \fBzstd --train --optimize-cover FILEs + \fBzstd --train --optimize-cover=d=d,steps=512 FILEs +.TP .SH BENCHMARK .TP From 6f31b76d1d9af3268de2da027a0e88b6cb07e07e Mon Sep 17 00:00:00 2001 From: cyan4973 Date: Sat, 4 Feb 2017 21:39:41 -0800 Subject: [PATCH 224/227] removed gzstd test from travis this target does no longer exist --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ba9f9965d..90306dab1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ matrix: # Standard Ubuntu 12.04 LTS Server Edition 64 bit - - env: Ubu=12.04 Cmd="make -C programs zstd-small zstd-decompress zstd-compress && make -C tests test-gzstd && make -C programs clean && make -C tests versionsTest" + - env: Ubu=12.04 Cmd="make -C programs zstd-small zstd-decompress zstd-compress && make -C programs clean && make -C tests versionsTest" os: linux sudo: required From 613087c02bd6bf1a95c71790844bf9d770e54e0e Mon Sep 17 00:00:00 2001 From: cyan4973 Date: Sat, 4 Feb 2017 23:36:12 -0800 Subject: [PATCH 225/227] Silence zlib detection routine When it fails, $(CC) sends error message into stderr redirected to /dev/null --- programs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/Makefile b/programs/Makefile index 9746ec4ed..599bef694 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -67,7 +67,7 @@ endif # zlib detection VOID = /dev/null -HAVE_ZLIB := $(shell echo "int main(){}" | $(CC) -o $(VOID) -x c - -lz && echo 1 || echo 0) +HAVE_ZLIB := $(shell echo "int main(){}" | $(CC) -o $(VOID) -x c - -lz 2> $(VOID) && echo 1 || echo 0) ifeq ($(HAVE_ZLIB), 1) ZLIBCPP = -DZSTD_GZDECOMPRESS ZLIBLD = -lz From b54e235bf305f4760bcc26200857cfa7a59ba8cd Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 5 Feb 2017 10:22:58 -0800 Subject: [PATCH 226/227] fixed Mac OS-X specific directory in $(RM) list these directories are now removed with -r command --- lib/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Makefile b/lib/Makefile index bef69543f..05dd2bc9a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -89,7 +89,9 @@ lib-release: DEBUGFLAGS := lib-release: lib clean: - @$(RM) core *.o *.a *.gcda *.$(SHARED_EXT) *.$(SHARED_EXT).* libzstd.pc dll/libzstd.dll dll/libzstd.lib + @$(RM) -r *.dSYM # Mac OS-X specific + @$(RM) core *.o *.a *.gcda *.$(SHARED_EXT) *.$(SHARED_EXT).* libzstd.pc + @$(RM) dll/libzstd.dll dll/libzstd.lib @$(RM) common/*.o compress/*.o decompress/*.o dictBuilder/*.o legacy/*.o deprecated/*.o @echo Cleaning library completed From 5962e5865959ea3f2bae9b0bec7c702ae599fd97 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 5 Feb 2017 18:09:35 -0800 Subject: [PATCH 227/227] updated NEWS for v1.1.3 --- NEWS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 072caee59..4f7463056 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ v1.1.3 -cli : zstd can decompress .gz files. Feature can be turned off by targeting `make zstd-nogz` or setting `make HAVE_ZLIB=0` +cli : zstd can decompress .gz files (can be disabled with `make zstd-nogz` or `make HAVE_ZLIB=0`) cli : new : experimental target `make zstdmt`, with multi-threading support +cli : new : improved dictionary builder "cover" (experimental), by Nick Terrell cli : new : advanced commands for detailed parameters, by Przemyslaw Skibinski cli : fix zstdless on Mac OS-X, by Andrew Janke cli : fix #232 "compress non-files"