From a9febe81ae334d5cb5920144d1b9e3a2cd04f6f9 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 1 Aug 2016 13:37:17 +0200 Subject: [PATCH 01/14] changed bench behavior for slow compression levels --- programs/bench.c | 33 ++++++++++++++++++++++----------- programs/zstd.1 | 2 +- programs/zstdcli.c | 2 +- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index f4bff8835..98e79add7 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -190,13 +190,18 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, { U64 fastestC = (U64)(-1LL), fastestD = (U64)(-1LL); U64 const crcOrig = XXH64(srcBuffer, srcSize, 0); UTIL_time_t coolTime; - U32 testNb; + U64 const maxTime = (g_nbIterations * TIMELOOP_MICROSEC) + 100; + U64 totalCTime=0, totalDTime=0; + U32 cCompleted=0, dCompleted=0; +# define NB_MARKS 4 + const char* const marks[NB_MARKS] = { " |", " /", " =", "\\" }; + U32 markNb = 0; size_t cSize = 0; double ratio = 0.; UTIL_getTime(&coolTime); DISPLAYLEVEL(2, "\r%79s\r", ""); - for (testNb = 1; testNb <= (g_nbIterations + !g_nbIterations); testNb++) { + while (!cCompleted | !dCompleted) { UTIL_time_t clockStart; U64 clockLoop = g_nbIterations ? TIMELOOP_MICROSEC : 1; @@ -208,15 +213,14 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, } /* Compression */ - DISPLAYLEVEL(2, "%2i-%-17.17s :%10u ->\r", testNb, displayName, (U32)srcSize); - memset(compressedBuffer, 0xE5, maxCompressedSize); /* warm up and erase result buffer */ + 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); - { //size_t const refSrcSize = (nbBlocks == 1) ? srcSize : 0; - //ZSTD_parameters const zparams = ZSTD_getParams(cLevel, refSrcSize, dictBufferSize); + if (!cCompleted) { /* still some time to do compression tests */ ZSTD_parameters const zparams = ZSTD_getParams(cLevel, blockSize, dictBufferSize); ZSTD_customMem const cmem = { NULL, NULL, NULL }; U32 nbLoops = 0; @@ -237,13 +241,16 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, ZSTD_freeCDict(cdict); { U64 const clockSpan = UTIL_clockSpanMicro(clockStart, ticksPerSecond); if (clockSpan < fastestC*nbLoops) fastestC = clockSpan / nbLoops; + totalCTime += clockSpan; + cCompleted = totalCTime>maxTime; } } cSize = 0; { U32 blockNb; for (blockNb=0; blockNb%10u (%5.3f),%6.1f MB/s\r", - testNb, displayName, (U32)srcSize, (U32)cSize, ratio, + markNb = (markNb+1) % NB_MARKS; + DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.3f),%6.1f MB/s\r", + marks[markNb], displayName, (U32)srcSize, (U32)cSize, ratio, (double)srcSize / fastestC ); (void)fastestD; (void)crcOrig; /* unused when decompression disabled */ @@ -255,7 +262,8 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, UTIL_waitForNextTick(ticksPerSecond); UTIL_getTime(&clockStart); - { U32 nbLoops = 0; + if (!dCompleted) { + U32 nbLoops = 0; ZSTD_DDict* ddict = ZSTD_createDDict(dictBuffer, dictBufferSize); if (!ddict) EXM_THROW(2, "ZSTD_createDDict() allocation failure"); do { @@ -278,10 +286,13 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, ZSTD_freeDDict(ddict); { U64 const clockSpan = UTIL_clockSpanMicro(clockStart, ticksPerSecond); if (clockSpan < fastestD*nbLoops) fastestD = clockSpan / nbLoops; + totalDTime += clockSpan; + dCompleted = totalDTime>maxTime; } } - DISPLAYLEVEL(2, "%2i-%-17.17s :%10u ->%10u (%5.3f),%6.1f MB/s ,%6.1f MB/s\r", - testNb, displayName, (U32)srcSize, (U32)cSize, ratio, + markNb = (markNb+1) % NB_MARKS; + DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.3f),%6.1f MB/s ,%6.1f MB/s\r", + marks[markNb], displayName, (U32)srcSize, (U32)cSize, ratio, (double)srcSize / fastestC, (double)srcSize / fastestD ); diff --git a/programs/zstd.1 b/programs/zstd.1 index d2dfc3c14..0a0ad629d 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -128,7 +128,7 @@ Typical gains range from ~10% (at 64KB) to x5 better (at <1KB). benchmark file(s) using compression level # .TP .B \-i# - iteration loops [1-9](default : 3), benchmark mode only + minimum evaluation time, in seconds (default : 3s), benchmark mode only .TP .B \-B# cut file into independent blocks of size # (default: no block) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 466823222..1e5536134 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -165,7 +165,7 @@ static int usage_advanced(const char* programName) DISPLAY( "Benchmark arguments :\n"); DISPLAY( " -b# : benchmark file(s), using # compression level (default : 1) \n"); DISPLAY( " -e# : test all compression levels from -bX to # (default: 1)\n"); - DISPLAY( " -i# : iteration loops [1-9](default : 3)\n"); + DISPLAY( " -i# : minimum evaluation time in seconds (default : 3s)\n"); DISPLAY( " -B# : cut file into independent blocks of size # (default: no block)\n"); #endif return 0; From 415251c63ba677ce949a708ebeb8c54260915d18 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 1 Aug 2016 14:26:49 +0200 Subject: [PATCH 02/14] minor style change --- programs/util.h | 7 ++----- programs/zstdcli.c | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/programs/util.h b/programs/util.h index 72a40ca8f..72c5275c2 100644 --- a/programs/util.h +++ b/programs/util.h @@ -365,13 +365,10 @@ UTIL_STATIC const char** UTIL_createFileList(const char **inputNames, unsigned i pos += len + 1; nbFiles++; } - } - else - { + } else { nbFiles += UTIL_prepareFileList(inputNames[i], &buf, &pos, &bufend); if (buf == NULL) return NULL; - } - } + } } if (nbFiles == 0) { free(buf); return NULL; } diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 1e5536134..f4fbba53f 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -441,8 +441,8 @@ int main(int argCount, const char** argv) if (recursive) { fileNamesTable = UTIL_createFileList(filenameTable, filenameIdx, &fileNamesBuf, &fileNamesNb); if (fileNamesTable) { - unsigned i; - for (i=0; i Date: Mon, 1 Aug 2016 16:25:58 +0200 Subject: [PATCH 03/14] added : resilience test for ZSTD_getErrorName() --- programs/fuzzer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/programs/fuzzer.c b/programs/fuzzer.c index cb31dc431..be36d74bb 100644 --- a/programs/fuzzer.c +++ b/programs/fuzzer.c @@ -132,6 +132,16 @@ static int basicUnitTests(U32 seed, double compressibility) RDG_genBuffer(CNBuffer, CNBuffSize, compressibility, 0., seed); /* Basic tests */ + DISPLAYLEVEL(4, "test%3i : ZSTD_getErrorName : ", testNb++); + { const char* errorString = ZSTD_getErrorName(0); + DISPLAYLEVEL(4, "OK : %s \n", errorString); + } + + DISPLAYLEVEL(4, "test%3i : ZSTD_getErrorName with wrong value : ", testNb++); + { const char* errorString = ZSTD_getErrorName(499); + DISPLAYLEVEL(4, "OK : %s \n", errorString); + } + DISPLAYLEVEL(4, "test%3i : compress %u bytes : ", testNb++, (U32)CNBuffSize); CHECKPLUS(r, ZSTD_compress(compressedBuffer, ZSTD_compressBound(CNBuffSize), CNBuffer, CNBuffSize, 1), From 373d4f9070df9c7e923b7b1cb7f01048d3bb83bf Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 1 Aug 2016 17:36:11 +0200 Subject: [PATCH 04/14] updated examples (#273) --- examples/dictionary_compression.c | 47 ++++++++++++++++------------- examples/dictionary_decompression.c | 42 ++++++++++++++------------ 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/examples/dictionary_compression.c b/examples/dictionary_compression.c index c4dc1b90d..5a999549b 100644 --- a/examples/dictionary_compression.c +++ b/examples/dictionary_compression.c @@ -31,7 +31,7 @@ #include // presumes zstd library is installed -static off_t fsize_X(const char *filename) +static off_t fsize_orDie(const char *filename) { struct stat st; if (stat(filename, &st) == 0) return st.st_size; @@ -40,7 +40,7 @@ static off_t fsize_X(const char *filename) exit(1); } -static FILE* fopen_X(const char *filename, const char *instruction) +static FILE* fopen_orDie(const char *filename, const char *instruction) { FILE* const inFile = fopen(filename, instruction); if (inFile) return inFile; @@ -49,20 +49,20 @@ static FILE* fopen_X(const char *filename, const char *instruction) exit(2); } -static void* malloc_X(size_t size) +static void* malloc_orDie(size_t size) { void* const buff = malloc(size); if (buff) return buff; /* error */ - perror(NULL); + perror("malloc"); exit(3); } -static void* loadFile_X(const char* fileName, size_t* size) +static void* loadFile_orDie(const char* fileName, size_t* size) { - off_t const buffSize = fsize_X(fileName); - FILE* const inFile = fopen_X(fileName, "rb"); - void* const buffer = malloc_X(buffSize); + off_t const buffSize = fsize_orDie(fileName); + FILE* const inFile = fopen_orDie(fileName, "rb"); + void* const buffer = malloc_orDie(buffSize); size_t const readSize = fread(buffer, 1, buffSize, inFile); if (readSize != (size_t)buffSize) { fprintf(stderr, "fread: %s : %s \n", fileName, strerror(errno)); @@ -73,9 +73,9 @@ static void* loadFile_X(const char* fileName, size_t* size) return buffer; } -static void saveFile_X(const char* fileName, const void* buff, size_t buffSize) +static void saveFile_orDie(const char* fileName, const void* buff, size_t buffSize) { - FILE* const oFile = fopen_X(fileName, "wb"); + FILE* const oFile = fopen_orDie(fileName, "wb"); size_t const wSize = fwrite(buff, 1, buffSize, oFile); if (wSize != (size_t)buffSize) { fprintf(stderr, "fwrite: %s : %s \n", fileName, strerror(errno)); @@ -89,23 +89,27 @@ static void saveFile_X(const char* fileName, const void* buff, size_t buffSize) /* createDict() : `dictFileName` is supposed to have been created using `zstd --train` */ -static const ZSTD_CDict* createDict(const char* dictFileName) +static ZSTD_CDict* createCDict_orDie(const char* dictFileName) { size_t dictSize; printf("loading dictionary %s \n", dictFileName); - void* const dictBuffer = loadFile_X(dictFileName, &dictSize); - const ZSTD_CDict* const ddict = ZSTD_createCDict(dictBuffer, dictSize, 3); + void* const dictBuffer = loadFile_orDie(dictFileName, &dictSize); + ZSTD_CDict* const cdict = ZSTD_createCDict(dictBuffer, dictSize, 3); + if (!cdict) { + fprintf(stderr, "ZSTD_createCDict error \n"); + exit(7); + } free(dictBuffer); - return ddict; + return cdict; } static void compress(const char* fname, const char* oname, const ZSTD_CDict* cdict) { size_t fSize; - void* const fBuff = loadFile_X(fname, &fSize); + void* const fBuff = loadFile_orDie(fname, &fSize); size_t const cBuffSize = ZSTD_compressBound(fSize); - void* const cBuff = malloc_X(cBuffSize); + void* const cBuff = malloc_orDie(cBuffSize); ZSTD_CCtx* const cctx = ZSTD_createCCtx(); size_t const cSize = ZSTD_compress_usingCDict(cctx, cBuff, cBuffSize, fBuff, fSize, cdict); @@ -114,7 +118,7 @@ static void compress(const char* fname, const char* oname, const ZSTD_CDict* cdi exit(7); } - saveFile_X(oname, cBuff, cSize); + saveFile_orDie(oname, cBuff, cSize); /* success */ printf("%25s : %6u -> %7u - %s \n", fname, (unsigned)fSize, (unsigned)cSize, oname); @@ -125,11 +129,11 @@ static void compress(const char* fname, const char* oname, const ZSTD_CDict* cdi } -static char* createOutFilename(const char* filename) +static char* createOutFilename_orDie(const char* filename) { size_t const inL = strlen(filename); size_t const outL = inL + 5; - void* outSpace = malloc_X(outL); + void* outSpace = malloc_orDie(outL); memset(outSpace, 0, outL); strcat(outSpace, filename); strcat(outSpace, ".zst"); @@ -149,15 +153,16 @@ int main(int argc, const char** argv) /* load dictionary only once */ const char* const dictName = argv[argc-1]; - const ZSTD_CDict* const dictPtr = createDict(dictName); + ZSTD_CDict* const dictPtr = createCDict_orDie(dictName); int u; for (u=1; u // presumes zstd library is installed -static off_t fsize_X(const char *filename) +static off_t fsize_orDie(const char *filename) { struct stat st; if (stat(filename, &st) == 0) return st.st_size; /* error */ - printf("stat: %s : %s \n", filename, strerror(errno)); + perror(filename); exit(1); } -static FILE* fopen_X(const char *filename, const char *instruction) +static FILE* fopen_orDie(const char *filename, const char *instruction) { FILE* const inFile = fopen(filename, instruction); if (inFile) return inFile; /* error */ - printf("fopen: %s : %s \n", filename, strerror(errno)); + perror(filename); exit(2); } -static void* malloc_X(size_t size) +static void* malloc_orDie(size_t size) { void* const buff = malloc(size); if (buff) return buff; /* error */ - printf("malloc: %s \n", strerror(errno)); + perror("malloc"); exit(3); } -static void* loadFile_X(const char* fileName, size_t* size) +static void* loadFile_orDie(const char* fileName, size_t* size) { - off_t const buffSize = fsize_X(fileName); - FILE* const inFile = fopen_X(fileName, "rb"); - void* const buffer = malloc_X(buffSize); + off_t const buffSize = fsize_orDie(fileName); + FILE* const inFile = fopen_orDie(fileName, "rb"); + void* const buffer = malloc_orDie(buffSize); size_t const readSize = fread(buffer, 1, buffSize, inFile); if (readSize != (size_t)buffSize) { - printf("fread: %s : %s \n", fileName, strerror(errno)); + fprintf(stderr, "fread: %s : %s \n", fileName, strerror(errno)); exit(4); } fclose(inFile); @@ -75,12 +75,13 @@ static void* loadFile_X(const char* fileName, size_t* size) /* createDict() : `dictFileName` is supposed to have been created using `zstd --train` */ -static const ZSTD_DDict* createDict(const char* dictFileName) +static ZSTD_DDict* createDict_orDie(const char* dictFileName) { size_t dictSize; printf("loading dictionary %s \n", dictFileName); - void* const dictBuffer = loadFile_X(dictFileName, &dictSize); - const ZSTD_DDict* const ddict = ZSTD_createDDict(dictBuffer, dictSize); + void* const dictBuffer = loadFile_orDie(dictFileName, &dictSize); + ZSTD_DDict* const ddict = ZSTD_createDDict(dictBuffer, dictSize); + if (ddict==NULL) { fprintf(stderr, "ZSTD_createDDict error \n"); exit(5); } free(dictBuffer); return ddict; } @@ -89,19 +90,19 @@ static const ZSTD_DDict* createDict(const char* dictFileName) static void decompress(const char* fname, const ZSTD_DDict* ddict) { size_t cSize; - void* const cBuff = loadFile_X(fname, &cSize); + void* const cBuff = loadFile_orDie(fname, &cSize); unsigned long long const rSize = ZSTD_getDecompressedSize(cBuff, cSize); if (rSize==0) { - printf("%s : original size unknown \n", fname); - exit(5); + fprintf(stderr, "%s : original size unknown \n", fname); + exit(6); } - void* const rBuff = malloc_X(rSize); + void* const rBuff = malloc_orDie(rSize); ZSTD_DCtx* const dctx = ZSTD_createDCtx(); size_t const dSize = ZSTD_decompress_usingDDict(dctx, rBuff, rSize, cBuff, cSize, ddict); if (dSize != rSize) { - printf("error decoding %s : %s \n", fname, ZSTD_getErrorName(dSize)); + fprintf(stderr, "error decoding %s : %s \n", fname, ZSTD_getErrorName(dSize)); exit(7); } @@ -127,10 +128,11 @@ int main(int argc, const char** argv) /* load dictionary only once */ const char* const dictName = argv[argc-1]; - const ZSTD_DDict* const dictPtr = createDict(dictName); + ZSTD_DDict* const dictPtr = createDict_orDie(dictName); int u; for (u=1; u Date: Mon, 1 Aug 2016 17:39:06 +0200 Subject: [PATCH 05/14] fixed freeCDict --- examples/dictionary_compression.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/dictionary_compression.c b/examples/dictionary_compression.c index 5a999549b..8c4a81a77 100644 --- a/examples/dictionary_compression.c +++ b/examples/dictionary_compression.c @@ -163,6 +163,6 @@ int main(int argc, const char** argv) free(outFilename); } - free(dictPtr); + ZSTD_freeCDict(dictPtr); printf("All %u files compressed. \n", argc-2); } From f116e87f59b8563bd09663e1ae6ce7891e661b30 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 1 Aug 2016 19:11:54 +0200 Subject: [PATCH 06/14] fixed analyzer warning --- lib/dictBuilder/zdict.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 6c2277be8..effa3f08e 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -683,10 +683,18 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, size_t pos = 0, errorCode; size_t eSize = 0; size_t const totalSrcSize = ZDICT_totalSampleSize(fileSizes, nbFiles); - size_t const averageSampleSize = totalSrcSize / nbFiles; + size_t const averageSampleSize = totalSrcSize / (nbFiles + !nbFiles); BYTE* dstPtr = (BYTE*)dstBuffer; /* init */ + esr.ref = ZSTD_createCCtx(); + esr.zc = ZSTD_createCCtx(); + esr.workPlace = malloc(ZSTD_BLOCKSIZE_ABSOLUTEMAX); + if (!esr.ref || !esr.zc || !esr.workPlace) { + eSize = ERROR(memory_allocation); + DISPLAYLEVEL(1, "Not enough memory"); + goto _cleanup; + } if (offcodeMax>OFFCODE_MAX) { eSize = ERROR(dictionary_wrong); goto _cleanup; } /* too large dictionary */ for (u=0; u<256; u++) countLit[u]=1; /* any character must be described */ for (u=0; u<=offcodeMax; u++) offcodeCount[u]=1; @@ -694,14 +702,6 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, for (u=0; u<=MaxLL; u++) litLengthCount[u]=1; repOffset[1] = repOffset[4] = repOffset[8] = 1; memset(bestRepOffset, 0, sizeof(bestRepOffset)); - esr.ref = ZSTD_createCCtx(); - esr.zc = ZSTD_createCCtx(); - esr.workPlace = malloc(ZSTD_BLOCKSIZE_ABSOLUTEMAX); - if (!esr.ref || !esr.zc || !esr.workPlace) { - eSize = ERROR(memory_allocation); - DISPLAYLEVEL(1, "Not enough memory"); - goto _cleanup; - } if (compressionLevel==0) compressionLevel=g_compressionLevel_default; params = ZSTD_getParams(compressionLevel, averageSampleSize, dictBufferSize); { size_t const beginResult = ZSTD_compressBegin_advanced(esr.ref, dictBuffer, dictBufferSize, params, 0); From de4c04f6c219ea475af46f83c0b17d856d1c4cb5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 2 Aug 2016 11:27:05 +0200 Subject: [PATCH 07/14] Fixed : ZSTD_compress* can compress > 4 GB in a single pass, reported by Nick Terrell --- programs/bench.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/bench.c b/programs/bench.c index 98e79add7..167085a11 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -101,7 +101,7 @@ void BMK_setAdditionalParam(int additionalParam) { g_additionalParam=additionalP void BMK_SetNbIterations(unsigned nbLoops) { g_nbIterations = nbLoops; - DISPLAYLEVEL(2, "- %i iterations -\n", g_nbIterations); + DISPLAYLEVEL(3, "- test >= %i seconds per compression / decompression -\n", g_nbIterations); } void BMK_SetBlockSize(size_t blockSize) From 346efccc356fc6a970730266ddc9e77964b72fda Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 2 Aug 2016 14:26:00 +0200 Subject: [PATCH 08/14] fixed doc typo --- NEWS | 4 +++ lib/compress/zstd_compress.c | 70 ++++++++++++++++++------------------ zstd_compression_format.md | 2 +- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/NEWS b/NEWS index 56c46fef1..5e46e0a7f 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +v0.8.1 +Changed : -i# now selects benchmark time in second +Fixed : ZSTD_compress* can now compress > 4 GB in a single pass, reported by Nick Terrell + v0.8.0 Improved : better speed on clang and gcc -O2, thanks to Eric Biggers New : Build on FreeBSD and DragonFly, thanks to JrMarino diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 56c63601e..e2ca2d797 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2244,6 +2244,21 @@ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, if (dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE) return ERROR(dstSize_tooSmall); /* not enough space to store compressed block */ if (remaining < blockSize) blockSize = remaining; + /* preemptive overflow correction */ + if (cctx->lowLimit > (1<<30)) { + U32 const btplus = (cctx->params.cParams.strategy == ZSTD_btlazy2) | (cctx->params.cParams.strategy == ZSTD_btopt); + U32 const chainMask = (1 << (cctx->params.cParams.chainLog - btplus)) - 1; + U32 const newLowLimit = cctx->lowLimit & chainMask; /* preserve position % chainSize */ + U32 const correction = cctx->lowLimit - newLowLimit; + ZSTD_reduceIndex(cctx, correction); + cctx->base += correction; + cctx->dictBase += correction; + cctx->lowLimit = newLowLimit; + cctx->dictLimit -= correction; + if (cctx->nextToUpdate < correction) cctx->nextToUpdate = 0; + else cctx->nextToUpdate -= correction; + } + if ((U32)(ip+blockSize - cctx->base) > cctx->loadedDictEnd + maxDist) { /* enforce maxDist */ U32 const newLowLimit = (U32)(ip+blockSize - cctx->base) - maxDist; @@ -2317,7 +2332,7 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, } -static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* zc, +static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 frame, U32 lastFrameChunk) @@ -2325,53 +2340,40 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* zc, const BYTE* const ip = (const BYTE*) src; size_t fhSize = 0; - if (zc->stage==ZSTDcs_created) return ERROR(stage_wrong); /* missing init (ZSTD_compressBegin) */ + if (cctx->stage==ZSTDcs_created) return ERROR(stage_wrong); /* missing init (ZSTD_compressBegin) */ - if (frame && (zc->stage==ZSTDcs_init)) { - fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, zc->params, zc->frameContentSize, zc->dictID); + if (frame && (cctx->stage==ZSTDcs_init)) { + fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->params, cctx->frameContentSize, cctx->dictID); if (ZSTD_isError(fhSize)) return fhSize; dstCapacity -= fhSize; dst = (char*)dst + fhSize; - zc->stage = ZSTDcs_ongoing; + cctx->stage = ZSTDcs_ongoing; } /* Check if blocks follow each other */ - if (src != zc->nextSrc) { + if (src != cctx->nextSrc) { /* not contiguous */ - ptrdiff_t const delta = zc->nextSrc - ip; - zc->lowLimit = zc->dictLimit; - zc->dictLimit = (U32)(zc->nextSrc - zc->base); - zc->dictBase = zc->base; - zc->base -= delta; - zc->nextToUpdate = zc->dictLimit; - if (zc->dictLimit - zc->lowLimit < HASH_READ_SIZE) zc->lowLimit = zc->dictLimit; /* too small extDict */ + ptrdiff_t const delta = cctx->nextSrc - ip; + cctx->lowLimit = cctx->dictLimit; + cctx->dictLimit = (U32)(cctx->nextSrc - cctx->base); + cctx->dictBase = cctx->base; + cctx->base -= delta; + cctx->nextToUpdate = cctx->dictLimit; + if (cctx->dictLimit - cctx->lowLimit < HASH_READ_SIZE) cctx->lowLimit = cctx->dictLimit; /* too small extDict */ } - /* preemptive overflow correction */ - if (zc->lowLimit > (1<<30)) { - U32 const btplus = (zc->params.cParams.strategy == ZSTD_btlazy2) | (zc->params.cParams.strategy == ZSTD_btopt); - U32 const chainMask = (1 << (zc->params.cParams.chainLog - btplus)) - 1; - U32 const newLowLimit = zc->lowLimit & chainMask; /* preserve position % chainSize */ - U32 const correction = zc->lowLimit - newLowLimit; - ZSTD_reduceIndex(zc, correction); - zc->base += correction; - zc->dictBase += correction; - zc->lowLimit = newLowLimit; - zc->dictLimit -= correction; - if (zc->nextToUpdate < correction) zc->nextToUpdate = 0; - else zc->nextToUpdate -= correction; + /* if input and dictionary overlap : reduce dictionary (area presumed modified by input) */ + if ((ip+srcSize > cctx->dictBase + cctx->lowLimit) & (ip < cctx->dictBase + cctx->dictLimit)) { + ptrdiff_t const highInputIdx = (ip + srcSize) - cctx->dictBase; + U32 const lowLimitMax = (highInputIdx > (ptrdiff_t)cctx->dictLimit) ? cctx->dictLimit : (U32)highInputIdx; + cctx->lowLimit = lowLimitMax; } - /* if input and dictionary overlap : reduce dictionary (presumed modified by input) */ - if ((ip+srcSize > zc->dictBase + zc->lowLimit) && (ip < zc->dictBase + zc->dictLimit)) { - zc->lowLimit = (U32)(ip + srcSize - zc->dictBase); - if (zc->lowLimit > zc->dictLimit) zc->lowLimit = zc->dictLimit; - } + cctx->nextSrc = ip + srcSize; - zc->nextSrc = ip + srcSize; { size_t const cSize = frame ? - ZSTD_compress_generic (zc, dst, dstCapacity, src, srcSize, lastFrameChunk) : - ZSTD_compressBlock_internal (zc, dst, dstCapacity, src, srcSize); + 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; } diff --git a/zstd_compression_format.md b/zstd_compression_format.md index da5c94afd..f519c558e 100644 --- a/zstd_compression_format.md +++ b/zstd_compression_format.md @@ -503,7 +503,7 @@ Compressed and regenerated size fields follow little-endian convention. #### Huffman Tree description -This section is only present when literals block type is `Compressed` (`0`). +This section is only present when literals block type is `Compressed` (`2`). Prefix coding represents symbols from an a priori known alphabet by bit sequences (codewords), one codeword for each symbol, From bf2bc112bb01a2e371dfb2073acef37c0ccd5295 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 2 Aug 2016 23:48:13 +0200 Subject: [PATCH 09/14] bench : controlled display update when loading lot of files --- lib/dictBuilder/zdict.c | 2 +- programs/bench.c | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index effa3f08e..e30168cbd 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -920,7 +920,7 @@ size_t ZDICT_trainFromBuffer_unsafe( /* create dictionary */ { U32 dictContentSize = ZDICT_dictSize(dictList); if (dictContentSize < targetDictSize/2) { - DISPLAYLEVEL(2, "! warning : created dictionary significantly smaller than requested (%u < %u) \n", dictContentSize, (U32)maxDictSize); + DISPLAYLEVEL(2, "! warning : selected content significantly smaller than requested (%u < %u) \n", dictContentSize, (U32)maxDictSize); if (minRep > MINRATIO) { DISPLAYLEVEL(2, "! consider increasing selectivity to produce larger dictionary (-s%u) \n", selectivity+1); DISPLAYLEVEL(2, "! note : larger dictionaries are not necessarily better, test its efficiency on samples \n"); diff --git a/programs/bench.c b/programs/bench.c index 167085a11..b545cb007 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -30,6 +30,7 @@ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen, ftello64 */ +#include /* clock_t, clock, CLOCKS_PER_SEC */ #include "mem.h" #define ZSTD_STATIC_LINKING_ONLY @@ -69,6 +70,13 @@ static U32 g_compressibilityDefault = 50; #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 */ +#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); } } +static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; +static clock_t g_time = 0; + /* ************************************* * Exceptions @@ -412,7 +420,7 @@ static void BMK_loadFiles(void* buffer, size_t bufferSize, } f = fopen(fileNamesTable[n], "rb"); if (f==NULL) EXM_THROW(10, "impossible to open file %s", fileNamesTable[n]); - DISPLAYLEVEL(2, "Loading %s... \r", fileNamesTable[n]); + DISPLAYUPDATE(2, "Loading %s... \r", fileNamesTable[n]); if (fileSize > bufferSize-pos) fileSize = bufferSize-pos, nbFiles=n; /* buffer too small - stop after this file */ { size_t const readSize = fread(((char*)buffer)+pos, 1, (size_t)fileSize, f); if (readSize != (size_t)fileSize) EXM_THROW(11, "could not read %s", fileNamesTable[n]); From 6a21971f4a8fddb3c3ba8bc550c8c14d3e220e88 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 3 Aug 2016 00:06:24 +0200 Subject: [PATCH 10/14] bench : implemented avgSize --- programs/bench.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programs/bench.c b/programs/bench.c index b545cb007..2364348d6 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -151,6 +151,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, const void* dictBuffer, size_t dictBufferSize, benchResult_t *result) { size_t const blockSize = (g_blockSize>=32 ? g_blockSize : srcSize) + (!srcSize) /* avoid div by 0 */ ; + size_t const avgSize = MIN(g_blockSize, (srcSize / nbFiles)); U32 const maxNbBlocks = (U32) ((srcSize + (blockSize-1)) / blockSize) + nbFiles; blockParam_t* const blockTable = (blockParam_t*) malloc(maxNbBlocks * sizeof(blockParam_t)); size_t const maxCompressedSize = ZSTD_compressBound(srcSize) + (maxNbBlocks * 1024); /* add some room for safety */ @@ -229,7 +230,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, UTIL_getTime(&clockStart); if (!cCompleted) { /* still some time to do compression tests */ - ZSTD_parameters const zparams = ZSTD_getParams(cLevel, blockSize, dictBufferSize); + ZSTD_parameters const zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize); ZSTD_customMem const cmem = { NULL, NULL, NULL }; U32 nbLoops = 0; ZSTD_CDict* cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, zparams, cmem); From 0763905f44367fb773b51b5abc9975768177177c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 3 Aug 2016 01:57:57 +0200 Subject: [PATCH 11/14] ZSTD_compress_usingCDict() correctly provides original size by default in frame header Fixed dictionary examples --- examples/Makefile | 4 ++++ examples/dictionary_compression.c | 1 + examples/dictionary_decompression.c | 1 + lib/compress/zstd_compress.c | 36 +++++++++++------------------ 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 5e3f0e17f..6ba8655b4 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -56,4 +56,8 @@ test: all ./simple_compression tmp @echo starting simple_decompression ./simple_decompression tmp.zst + @echo dictionary compression + ./dictionary_compression tmp README.md + @echo dictionary decompression + ./dictionary_decompression tmp.zst README.md @echo tests completed diff --git a/examples/dictionary_compression.c b/examples/dictionary_compression.c index 8c4a81a77..ef6bee06d 100644 --- a/examples/dictionary_compression.c +++ b/examples/dictionary_compression.c @@ -165,4 +165,5 @@ int main(int argc, const char** argv) ZSTD_freeCDict(dictPtr); printf("All %u files compressed. \n", argc-2); + return 0; } diff --git a/examples/dictionary_decompression.c b/examples/dictionary_decompression.c index f0976569a..54a583fa8 100644 --- a/examples/dictionary_decompression.c +++ b/examples/dictionary_decompression.c @@ -135,4 +135,5 @@ int main(int argc, const char** argv) ZSTD_freeDDict(dictPtr); printf("All %u files correctly decoded (in memory) \n", argc-2); + return 0; } diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e2ca2d797..e4bf8a20d 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2621,22 +2621,6 @@ size_t ZSTD_compressEnd (ZSTD_CCtx* cctx, } -/*! ZSTD_compress_usingPreparedCCtx() : -* Same as ZSTD_compress_usingDict, but using a reference context `preparedCCtx`, where dictionary has been loaded. -* It avoids reloading the dictionary each time. -* `preparedCCtx` must have been properly initialized using ZSTD_compressBegin_usingDict() or ZSTD_compressBegin_advanced(). -* Requires 2 contexts : 1 for reference (preparedCCtx) which will not be modified, and 1 to run the compression operation (cctx) */ -static size_t ZSTD_compress_usingPreparedCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize) -{ - size_t const errorCode = ZSTD_copyCCtx(cctx, preparedCCtx); - if (ZSTD_isError(errorCode)) return errorCode; - - return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); -} - - static size_t ZSTD_compress_internal (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, @@ -2732,9 +2716,7 @@ ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, ZSTD_pa ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionLevel) { ZSTD_customMem const allocator = { NULL, NULL, NULL }; - ZSTD_parameters params; - memset(¶ms, 0, sizeof(params)); - params.cParams = ZSTD_getCParams(compressionLevel, 0, dictSize); + ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, dictSize); params.fParams.contentSizeFlag = 1; return ZSTD_createCDict_advanced(dict, dictSize, params, allocator); } @@ -2749,14 +2731,24 @@ size_t ZSTD_freeCDict(ZSTD_CDict* cdict) return 0; } +/*! ZSTD_compress_usingCDict() : +* Compression using a digested Dictionary. +* Faster startup than ZSTD_compress_usingDict(), recommended when same dictionary is used multiple times. +* Note that compression level is decided during dictionary creation */ ZSTDLIB_API size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const ZSTD_CDict* cdict) { - return ZSTD_compress_usingPreparedCCtx(cctx, cdict->refContext, - dst, dstCapacity, - src, srcSize); + size_t const errorCode = ZSTD_copyCCtx(cctx, cdict->refContext); + if (ZSTD_isError(errorCode)) return errorCode; + + if (cdict->refContext->params.fParams.contentSizeFlag==1) { + cctx->params.fParams.contentSizeFlag = 1; + cctx->frameContentSize = srcSize; + } + + return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); } From ae40b18d55c927fce1bbbdcaec4c1a3572cdb517 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 3 Aug 2016 01:59:21 +0200 Subject: [PATCH 12/14] bumped library number --- lib/zstd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zstd.h b/lib/zstd.h index cb33b5588..7bd8a05c4 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -55,7 +55,7 @@ extern "C" { /*====== Version ======*/ #define ZSTD_VERSION_MAJOR 0 #define ZSTD_VERSION_MINOR 8 -#define ZSTD_VERSION_RELEASE 0 +#define ZSTD_VERSION_RELEASE 1 #define ZSTD_LIB_VERSION ZSTD_VERSION_MAJOR.ZSTD_VERSION_MINOR.ZSTD_VERSION_RELEASE #define ZSTD_QUOTE(str) #str From e0b4a2d40f6a6e099e665fc241cd4cbb16a0920b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 3 Aug 2016 03:36:03 +0200 Subject: [PATCH 13/14] fixed dictionary generation, reported by Bartosz Taudul --- lib/dictBuilder/zdict.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index e30168cbd..a18ab4469 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -332,12 +332,13 @@ static dictItem ZDICT_analyzePos( } while (length >=MINMATCHLENGTH); /* look backward */ - do { - length = ZDICT_count(b + pos, b + suffix[start-1]); - if (length >= LLIMIT) length = LLIMIT-1; - lengthList[length]++; - if (length >=MINMATCHLENGTH) start--; - } while(length >= MINMATCHLENGTH); + 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 280f9a8754f9fd1dbc3d054147476f8ed3dbc721 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 8 Aug 2016 00:44:00 +0200 Subject: [PATCH 14/14] minor comment --- lib/common/zstd_internal.h | 10 ++++++++++ lib/compress/zstd_compress.c | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 0a1935a98..cc54d7722 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -154,6 +154,16 @@ MEM_STATIC void ZSTD_wildcopy(void* dst, const void* src, size_t length) while (op < oend); } +MEM_STATIC void ZSTD_wildcopy_e(void* dst, const void* src, void* dstEnd) /* should be faster for decoding, but strangely, not verified on all platform */ +{ + const BYTE* ip = (const BYTE*)src; + BYTE* op = (BYTE*)dst; + BYTE* const oend = (BYTE*)dstEnd; + do + COPY8(op, ip) + while (op < oend); +} + /*-******************************************* * Private interfaces diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e4bf8a20d..0f79fc85f 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -940,8 +940,8 @@ static void ZSTD_fillHashTable (ZSTD_CCtx* zc, const void* end, const U32 mls) FORCE_INLINE void ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, - const void* src, size_t srcSize, - const U32 mls) + const void* src, size_t srcSize, + const U32 mls) { U32* const hashTable = cctx->hashTable; U32 const hBits = cctx->params.cParams.hashLog; @@ -973,7 +973,7 @@ void ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, const BYTE* match = base + matchIndex; hashTable[h] = current; /* update hash table */ - if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { /* note : by construction, offset_1 <= current */ + if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStorePtr, ip-anchor, anchor, 0, mLength-MINMATCH);