From b402490546645c8324709f90a281f5f807b7482a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 26 Jul 2016 00:49:47 +0200 Subject: [PATCH 01/46] fixed #260, reported by @amnilsson --- programs/fileio.c | 2 +- programs/fileio.h | 1 - programs/playTests.sh | 3 +++ programs/zstdcli.c | 6 ++++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 855385bef..538438cd9 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -180,7 +180,7 @@ static FILE* FIO_openSrcFile(const char* srcFileName) return f; } - +/* `dstFileName must` be non-NULL */ static FILE* FIO_openDstFile(const char* dstFileName) { FILE* f; diff --git a/programs/fileio.h b/programs/fileio.h index 4a4f3d226..06d977d63 100644 --- a/programs/fileio.h +++ b/programs/fileio.h @@ -31,7 +31,6 @@ extern "C" { /* ************************************* * Special i/o constants **************************************/ -#define nullString "null" #define stdinmark "stdin" #define stdoutmark "stdout" #ifdef _WIN32 diff --git a/programs/playTests.sh b/programs/playTests.sh index b19c2bc52..6939aa817 100755 --- a/programs/playTests.sh +++ b/programs/playTests.sh @@ -206,6 +206,9 @@ $ECHO "test multiple files (*.zst) " $ZSTD -t *.zst $ECHO "test good and bad files (*) " $ZSTD -t * && die "bad files not detected !" +$ECHO "test --rm and --test combined " +$ZSTD -t --rm tmp1.zst +ls -ls tmp1.zst # check file is still present $ECHO "\n**** zstd round-trip tests **** " diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 344d3c88e..30e0d01b7 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -206,6 +206,7 @@ int main(int argCount, const char** argv) int argNb, bench=0, decode=0, + testmode=0, forceStdout=0, main_pause=0, nextEntryIsDictionary=0, @@ -273,7 +274,7 @@ int main(int argCount, const char** argv) if (!strcmp(argument, "--no-dictID")) { FIO_setDictIDFlag(0); continue; } if (!strcmp(argument, "--sparse")) { FIO_setSparseWrite(2); continue; } if (!strcmp(argument, "--no-sparse")) { FIO_setSparseWrite(0); continue; } - if (!strcmp(argument, "--test")) { decode=1; outFileName=nulmark; FIO_overwriteMode(); continue; } + if (!strcmp(argument, "--test")) { testmode=1; decode=1; continue; } if (!strcmp(argument, "--train")) { dictBuild=1; outFileName=g_defaultDictName; continue; } if (!strcmp(argument, "--maxdict")) { nextArgumentIsMaxDict=1; continue; } if (!strcmp(argument, "--dictID")) { nextArgumentIsDictID=1; continue; } @@ -337,7 +338,7 @@ int main(int argCount, const char** argv) case 'C': argument++; FIO_setChecksumFlag(2); break; /* test compressed file */ - case 't': decode=1; outFileName=nulmark; argument++; break; + case 't': testmode=1; decode=1; argument++; break; /* destination file name */ case 'o': nextArgumentIsOutFileName=1; argument++; break; @@ -503,6 +504,7 @@ int main(int argCount, const char** argv) #endif { /* decompression */ #ifndef ZSTD_NODECOMPRESS + if (testmode) { outFileName=nulmark; FIO_setRemoveSrcFile(0); } /* test mode */ if (filenameIdx==1 && outFileName) operationResult = FIO_decompressFilename(outFileName, filenameTable[0], dictFileName); else From 24a3d90bf011699bcac8f2e9d31fdb83adccf004 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 26 Jul 2016 01:26:56 +0200 Subject: [PATCH 02/46] strengthened integrity tests --- NEWS | 4 +++- programs/fileio.c | 2 +- programs/playTests.sh | 5 ++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 64c1d4ff9..7ffa4023f 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,9 @@ v0.8.0 -Fixed : premature end of frame when zero-sized raw block, reported by Eric Biggers +New : updated compresson format Fixed : legacy mode with ZSTD_HEAPMODE=0, by Christopher Bergqvist +Fixed : premature end of frame when zero-sized raw block, reported by Eric Biggers Fixed : checksum correctly checked in single-pass mode +Fixed : combined --test amd --rm, reported by Andreas M. Nilsson Modified : minor compression level adaptations Updated : compression format specification to v0.2.0 changed : zstd.h moved to /lib directory diff --git a/programs/fileio.c b/programs/fileio.c index 538438cd9..3233be2e5 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -744,7 +744,7 @@ static int FIO_decompressDstFile(dRess_t ress, result = FIO_decompressSrcFile(ress, srcFileName); if (fclose(ress.dstFile)) EXM_THROW(38, "Write error : cannot properly close %s", dstFileName); - if (result != 0) if (remove(dstFileName)) EXM_THROW(39, "remove %s error : %s", dstFileName, strerror(errno)); + if (result != 0) if (remove(dstFileName)) result=1; /* don't do anything if remove fails */ return result; } diff --git a/programs/playTests.sh b/programs/playTests.sh index 6939aa817..755060814 100755 --- a/programs/playTests.sh +++ b/programs/playTests.sh @@ -204,8 +204,11 @@ $ZSTD -t tmp1.zst $ZSTD --test tmp1.zst $ECHO "test multiple files (*.zst) " $ZSTD -t *.zst -$ECHO "test good and bad files (*) " +$ECHO "test bad files (*) " $ZSTD -t * && die "bad files not detected !" +$ZSTD -t tmp1 && die "bad file not detected !" +cp tmp1 tmp2.zst +$ZSTD -t tmp2.zst && die "bad file not detected !" $ECHO "test --rm and --test combined " $ZSTD -t --rm tmp1.zst ls -ls tmp1.zst # check file is still present From 7adc2328a32255cec075357355a8920fe7efc3ba Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 26 Jul 2016 15:39:31 +0200 Subject: [PATCH 03/46] fixed --test on zero-length files, reported by @amnilsson --- lib/legacy/zstd_v07.c | 2 +- programs/fileio.c | 5 ++++- programs/playTests.sh | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c index 62213997f..d95fd438e 100644 --- a/lib/legacy/zstd_v07.c +++ b/lib/legacy/zstd_v07.c @@ -4060,7 +4060,7 @@ static seq_t ZSTDv07_decodeSequence(seqState_t* seqState) } -FORCE_INLINE +static size_t ZSTDv07_execSequence(BYTE* op, BYTE* const oend, seq_t sequence, const BYTE** litPtr, const BYTE* const litLimit_w, diff --git a/programs/fileio.c b/programs/fileio.c index 3233be2e5..f8542f787 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -696,7 +696,10 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* srcFileName) /* check magic number -> version */ size_t const toRead = 4; size_t const sizeCheck = fread(ress.srcBuffer, (size_t)1, toRead, srcFile); - if (sizeCheck==0) break; /* no more input */ + if (sizeCheck==0) { + if (filesize==0) { DISPLAY("zstd: %s: unexpected end of file\n", srcFileName); return 1; } /* srcFileName is empty */ + break; /* no more input */ + } if (sizeCheck != toRead) EXM_THROW(31, "zstd: %s read error : cannot read header", srcFileName); { U32 const magic = MEM_readLE32(ress.srcBuffer); #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) diff --git a/programs/playTests.sh b/programs/playTests.sh index 755060814..ef44f7bdf 100755 --- a/programs/playTests.sh +++ b/programs/playTests.sh @@ -209,6 +209,8 @@ $ZSTD -t * && die "bad files not detected !" $ZSTD -t tmp1 && die "bad file not detected !" cp tmp1 tmp2.zst $ZSTD -t tmp2.zst && die "bad file not detected !" +./datagen -g0 > tmp3 +$ZSTD -t tmp3 && die "bad file not detected !" # detects 0-sized files as bad $ECHO "test --rm and --test combined " $ZSTD -t --rm tmp1.zst ls -ls tmp1.zst # check file is still present From a1dd6b97d2ea40d6927478cfe96502f2e74e51a3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 26 Jul 2016 16:44:09 +0200 Subject: [PATCH 04/46] fixed null-length round trip --- programs/fileio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/programs/fileio.c b/programs/fileio.c index f8542f787..ee5efa9e3 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -683,6 +683,7 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* srcFileName) unsigned long long filesize = 0; FILE* const dstFile = ress.dstFile; FILE* srcFile; + unsigned readSomething = 0; if (UTIL_isDirectory(srcFileName)) { DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName); @@ -697,9 +698,10 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* srcFileName) size_t const toRead = 4; size_t const sizeCheck = fread(ress.srcBuffer, (size_t)1, toRead, srcFile); if (sizeCheck==0) { - if (filesize==0) { DISPLAY("zstd: %s: unexpected end of file\n", srcFileName); return 1; } /* srcFileName is empty */ + if (readSomething==0) { DISPLAY("zstd: %s: unexpected end of file\n", srcFileName); return 1; } /* srcFileName is empty */ break; /* no more input */ } + readSomething = 1; if (sizeCheck != toRead) EXM_THROW(31, "zstd: %s read error : cannot read header", srcFileName); { U32 const magic = MEM_readLE32(ress.srcBuffer); #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) From fbd557d5c21536387ea7d3dc9f18033d851f16c8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 26 Jul 2016 17:13:58 +0200 Subject: [PATCH 05/46] multi-files -t doesn't stop after detecting magic number read failure --- programs/fileio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index ee5efa9e3..33a5c4d36 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -698,11 +698,11 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* srcFileName) size_t const toRead = 4; size_t const sizeCheck = fread(ress.srcBuffer, (size_t)1, toRead, srcFile); if (sizeCheck==0) { - if (readSomething==0) { DISPLAY("zstd: %s: unexpected end of file\n", srcFileName); return 1; } /* srcFileName is empty */ + if (readSomething==0) { DISPLAY("zstd: %s: unexpected end of file \n", srcFileName); fclose(srcFile); return 1; } /* srcFileName is empty */ break; /* no more input */ } readSomething = 1; - if (sizeCheck != toRead) EXM_THROW(31, "zstd: %s read error : cannot read header", srcFileName); + if (sizeCheck != toRead) { DISPLAY("zstd: %s: unknown header \n", srcFileName); fclose(srcFile); return 1; } /* srcFileName is empty */ { U32 const magic = MEM_readLE32(ress.srcBuffer); #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1) if (ZSTD_isLegacy(ress.srcBuffer, 4)) { From d50f9db3ea4c189f1b460dda5b4883a5022cc2a8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 26 Jul 2016 21:30:35 +0200 Subject: [PATCH 06/46] Improved speed on clang and gcc -O2, thanks to @ebiggers ! (#263) --- lib/common/mem.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/common/mem.h b/lib/common/mem.h index 4d35f5ef9..fc7b103e6 100644 --- a/lib/common/mem.h +++ b/lib/common/mem.h @@ -54,7 +54,7 @@ extern "C" { # include /* _byteswap_* */ #endif #if defined(__GNUC__) -# define MEM_STATIC static __attribute__((unused)) +# define MEM_STATIC static __inline __attribute__((unused)) #elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # define MEM_STATIC static inline #elif defined(_MSC_VER) @@ -387,4 +387,3 @@ MEM_STATIC U32 MEM_readMINMATCH(const void* memPtr, U32 length) #endif #endif /* MEM_H_MODULE */ - From e4d0265ea918e4b922c9a3963a07a6e85c805ae2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 26 Jul 2016 10:42:19 -0700 Subject: [PATCH 07/46] Replace remaining references to "direct mode" with "single segment mode" --- lib/compress/zstd_compress.c | 8 ++++---- lib/decompress/zstd_decompress.c | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 84e756b88..aac61be6a 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2311,19 +2311,19 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, U32 const dictIDSizeCode = (dictID>0) + (dictID>=256) + (dictID>=65536); /* 0-3 */ U32 const checksumFlag = params.fParams.checksumFlag>0; U32 const windowSize = 1U << params.cParams.windowLog; - U32 const directModeFlag = params.fParams.contentSizeFlag && (windowSize > (pledgedSrcSize-1)); + U32 const singleSegment = params.fParams.contentSizeFlag && (windowSize > (pledgedSrcSize-1)); BYTE const windowLogByte = (BYTE)((params.cParams.windowLog - ZSTD_WINDOWLOG_ABSOLUTEMIN) << 3); U32 const fcsCode = params.fParams.contentSizeFlag ? (pledgedSrcSize>=256) + (pledgedSrcSize>=65536+256) + (pledgedSrcSize>=0xFFFFFFFFU) : /* 0-3 */ 0; - BYTE const frameHeaderDecriptionByte = (BYTE)(dictIDSizeCode + (checksumFlag<<2) + (directModeFlag<<5) + (fcsCode<<6) ); + BYTE const frameHeaderDecriptionByte = (BYTE)(dictIDSizeCode + (checksumFlag<<2) + (singleSegment<<5) + (fcsCode<<6) ); size_t pos; if (dstCapacity < ZSTD_frameHeaderSize_max) return ERROR(dstSize_tooSmall); MEM_writeLE32(dst, ZSTD_MAGICNUMBER); op[4] = frameHeaderDecriptionByte; pos=5; - if (!directModeFlag) op[pos++] = windowLogByte; + if (!singleSegment) op[pos++] = windowLogByte; switch(dictIDSizeCode) { default: /* impossible */ @@ -2335,7 +2335,7 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, switch(fcsCode) { default: /* impossible */ - case 0 : if (directModeFlag) op[pos++] = (BYTE)(pledgedSrcSize); break; + case 0 : if (singleSegment) op[pos++] = (BYTE)(pledgedSrcSize); break; case 1 : MEM_writeLE16(op+pos, (U16)(pledgedSrcSize-256)); pos+=2; break; case 2 : MEM_writeLE32(op+pos, (U32)(pledgedSrcSize)); pos+=4; break; case 3 : MEM_writeLE64(op+pos, (U64)(pledgedSrcSize)); pos+=8; break; diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index cc61627f2..cd55aa8a5 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -204,10 +204,10 @@ static size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize) if (srcSize < ZSTD_frameHeaderSize_min) return ERROR(srcSize_wrong); { BYTE const fhd = ((const BYTE*)src)[4]; U32 const dictID= fhd & 3; - U32 const directMode = (fhd >> 5) & 1; + U32 const singleSegment = (fhd >> 5) & 1; U32 const fcsId = fhd >> 6; - return ZSTD_frameHeaderSize_min + !directMode + ZSTD_did_fieldSize[dictID] + ZSTD_fcs_fieldSize[fcsId] - + (directMode && !ZSTD_fcs_fieldSize[fcsId]); + return ZSTD_frameHeaderSize_min + !singleSegment + ZSTD_did_fieldSize[dictID] + ZSTD_fcs_fieldSize[fcsId] + + (singleSegment && !ZSTD_fcs_fieldSize[fcsId]); } } @@ -241,14 +241,14 @@ size_t ZSTD_getFrameParams(ZSTD_frameParams* fparamsPtr, const void* src, size_t size_t pos = 5; U32 const dictIDSizeCode = fhdByte&3; U32 const checksumFlag = (fhdByte>>2)&1; - U32 const directMode = (fhdByte>>5)&1; + U32 const singleSegment = (fhdByte>>5)&1; U32 const fcsID = fhdByte>>6; U32 const windowSizeMax = 1U << ZSTD_WINDOWLOG_MAX; U32 windowSize = 0; U32 dictID = 0; U64 frameContentSize = 0; if ((fhdByte & 0x08) != 0) return ERROR(frameParameter_unsupported); /* reserved bits, which must be zero */ - if (!directMode) { + if (!singleSegment) { BYTE const wlByte = ip[pos++]; U32 const windowLog = (wlByte >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN; if (windowLog > ZSTD_WINDOWLOG_MAX) return ERROR(frameParameter_unsupported); @@ -267,7 +267,7 @@ size_t ZSTD_getFrameParams(ZSTD_frameParams* fparamsPtr, const void* src, size_t switch(fcsID) { default: /* impossible */ - case 0 : if (directMode) frameContentSize = ip[pos]; break; + case 0 : if (singleSegment) frameContentSize = ip[pos]; break; case 1 : frameContentSize = MEM_readLE16(ip+pos)+256; break; case 2 : frameContentSize = MEM_readLE32(ip+pos); break; case 3 : frameContentSize = MEM_readLE64(ip+pos); break; From aa6c70bf6079cef0b54c4e810b608abec40fce5b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 26 Jul 2016 10:42:19 -0700 Subject: [PATCH 08/46] ZSTD_decompressFrame(): pass up error code from ZSTD_decodeFrameHeader() --- lib/decompress/zstd_decompress.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index cd55aa8a5..a34a36d1c 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -860,9 +860,11 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, /* Frame Header */ { size_t const frameHeaderSize = ZSTD_frameHeaderSize(src, ZSTD_frameHeaderSize_min); + size_t result; if (ZSTD_isError(frameHeaderSize)) return frameHeaderSize; if (srcSize < frameHeaderSize+ZSTD_blockHeaderSize) return ERROR(srcSize_wrong); - if (ZSTD_decodeFrameHeader(dctx, src, frameHeaderSize)) return ERROR(corruption_detected); + result = ZSTD_decodeFrameHeader(dctx, src, frameHeaderSize); + if (ZSTD_isError(result)) return result; ip += frameHeaderSize; remainingSize -= frameHeaderSize; } From 0a55e7a0bbaa387108f864a38cd249c2337448c2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 26 Jul 2016 10:42:20 -0700 Subject: [PATCH 09/46] ZSTD_decompressFrame(): use remainingSize instead of iend - ip Same behavior, but no need to have redundant variables. --- lib/decompress/zstd_decompress.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index a34a36d1c..2940dd68a 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -849,7 +849,6 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, const void* src, size_t srcSize) { const BYTE* ip = (const BYTE*)src; - const BYTE* const iend = ip + srcSize; BYTE* const ostart = (BYTE* const)dst; BYTE* const oend = ostart + dstCapacity; BYTE* op = ostart; @@ -872,7 +871,7 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, while (1) { size_t decodedSize; blockProperties_t blockProperties; - size_t const cBlockSize = ZSTD_getcBlockSize(ip, iend-ip, &blockProperties); + size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); if (ZSTD_isError(cBlockSize)) return cBlockSize; ip += ZSTD_blockHeaderSize; From dd25a27702a10bb1fcf34510f45dac3ca16ea235 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Jul 2016 12:35:29 +0200 Subject: [PATCH 10/46] added tutorial warning messages for dictBuilder --- .gitignore | 1 + NEWS | 1 + lib/dictBuilder/zdict.c | 20 ++++++++++++++------ programs/dibio.c | 9 ++++++++- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index e7c9a5687..0c458153c 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ projects/cmake/ # Test artefacts tmp* +dictionary # tmp files *.swp diff --git a/NEWS b/NEWS index 7ffa4023f..d01a33131 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,6 @@ v0.8.0 New : updated compresson format +Improved : better speed on clang and gcc -O2, thanks to Eric Biggers Fixed : legacy mode with ZSTD_HEAPMODE=0, by Christopher Bergqvist Fixed : premature end of frame when zero-sized raw block, reported by Eric Biggers Fixed : checksum correctly checked in single-pass mode diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index f15185556..75a9b1e33 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -924,7 +924,7 @@ size_t ZDICT_trainFromBuffer_unsafe( const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_params_t params) { - U32 const dictListSize = MAX( MAX(DICTLISTSIZE, nbSamples), (U32)(maxDictSize/16)); + U32 const dictListSize = MAX(MAX(DICTLISTSIZE, nbSamples), (U32)(maxDictSize/16)); dictItem* const dictList = (dictItem*)malloc(dictListSize * sizeof(*dictList)); unsigned selectivity = params.selectivityLevel; size_t const targetDictSize = maxDictSize; @@ -957,17 +957,25 @@ size_t ZDICT_trainFromBuffer_unsafe( DISPLAYLEVEL(3, "\n %u segments found, of total size %u \n", dictList[0].pos, dictContentSize); DISPLAYLEVEL(3, "list %u best segments \n", nb); for (u=1; u<=nb; u++) { - U32 p = dictList[u].pos; - U32 l = dictList[u].length; - U32 d = MIN(40, l); + U32 pos = dictList[u].pos; + U32 length = dictList[u].length; + U32 printedLength = MIN(40, length); DISPLAYLEVEL(3, "%3u:%3u bytes at pos %8u, savings %7u bytes |", - u, l, p, dictList[u].savings); - ZDICT_printHex(3, (const char*)samplesBuffer+p, d); + u, length, pos, dictList[u].savings); + ZDICT_printHex(3, (const char*)samplesBuffer+pos, printedLength); DISPLAYLEVEL(3, "| \n"); } } } /* create dictionary */ { U32 dictContentSize = ZDICT_dictSize(dictList); + U64 const totalSamplesSize = ZDICT_totalSampleSize(samplesSizes, nbSamples); + if (dictContentSize < targetDictSize/2) { + DISPLAYLEVEL(2, "! warning : created dictionary significantly smaller than requested (%u < %u) \n", dictContentSize, (U32)maxDictSize); + 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"); + if (totalSamplesSize < 10 * targetDictSize) + DISPLAYLEVEL(2, "! consider also increasing the number of samples (total size : %u MB)\n", (U32)(totalSamplesSize>>20)); + } /* build dict content */ { U32 u; diff --git a/programs/dibio.c b/programs/dibio.c index a61ea9cc6..cb864ec1d 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -202,9 +202,16 @@ 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) { + DISPLAYLEVEL(2, "! Warning : nb of samples too low for proper processing \n"); + DISPLAYLEVEL(2, "! Please provide one file per sample \n"); + DISPLAYLEVEL(2, "! Avoid concatenating multiple samples into a single file \n"); + DISPLAYLEVEL(2, "! otherwise, dictBuilder will be unable to find the beginning of each sample \n"); + DISPLAYLEVEL(2, "! resulting in distorted statistics \n"); + } /* init */ - g_displayLevel = params.notificationLevel; if (benchedSize < totalSizeToLoad) DISPLAYLEVEL(1, "Not enough memory; training on %u MB only...\n", (unsigned)(benchedSize >> 20)); From f796f7ab4571271396e09b3ebfae6c92eb52258b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Jul 2016 12:53:54 +0200 Subject: [PATCH 11/46] removed fastscan mode --- lib/dictBuilder/zdict.c | 129 ++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 86 deletions(-) diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 75a9b1e33..846e47766 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -489,7 +489,7 @@ static U32 ZDICT_dictSize(const dictItem* dictList) static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, const void* const buffer, size_t bufferSize, /* buffer must end with noisy guard band */ const size_t* fileSizes, unsigned nbFiles, - U32 shiftRatio, unsigned maxDictSize) + U32 shiftRatio) { int* const suffix0 = (int*)malloc((bufferSize+2)*sizeof(*suffix0)); int* const suffix = suffix0+1; @@ -542,16 +542,6 @@ static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, DISPLAYUPDATE(2, "\r%4.2f %% \r", (double)cursor / bufferSize * 100); } } - /* limit dictionary size */ - { U32 const max = dictList->pos; /* convention : nb of useful elts within dictList */ - U32 currentSize = 0; - U32 n; for (n=1; n maxDictSize) break; - } - dictList->pos = n; - } - _cleanup: free(suffix0); free(reverseSuffix); @@ -845,45 +835,6 @@ _cleanup: } -#define DIB_FASTSEGMENTSIZE 64 -/*! ZDICT_fastSampling() (based on an idea proposed by Giuseppe Ottaviano) : - Fill `dictBuffer` with stripes of size DIB_FASTSEGMENTSIZE from `samplesBuffer`, - up to `dictSize`. - Filling starts from the end of `dictBuffer`, down to maximum possible. - if `dictSize` is not a multiply of DIB_FASTSEGMENTSIZE, some bytes at beginning of `dictBuffer` won't be used. - @return : amount of data written into `dictBuffer`, - or an error code -*/ -static size_t ZDICT_fastSampling(void* dictBuffer, size_t dictSize, - const void* samplesBuffer, size_t samplesSize) -{ - char* dstPtr = (char*)dictBuffer + dictSize; - const char* srcPtr = (const char*)samplesBuffer; - size_t const nbSegments = dictSize / DIB_FASTSEGMENTSIZE; - size_t segNb, interSize; - - if (nbSegments <= 2) return ERROR(srcSize_wrong); - if (samplesSize < dictSize) return ERROR(srcSize_wrong); - - /* first and last segments are part of dictionary, in case they contain interesting header/footer */ - dstPtr -= DIB_FASTSEGMENTSIZE; - memcpy(dstPtr, srcPtr, DIB_FASTSEGMENTSIZE); - dstPtr -= DIB_FASTSEGMENTSIZE; - memcpy(dstPtr, srcPtr+samplesSize-DIB_FASTSEGMENTSIZE, DIB_FASTSEGMENTSIZE); - - /* regularly copy a segment */ - interSize = (samplesSize - nbSegments*DIB_FASTSEGMENTSIZE) / (nbSegments-1); - srcPtr += DIB_FASTSEGMENTSIZE; - for (segNb=2; segNb < nbSegments; segNb++) { - srcPtr += interSize; - dstPtr -= DIB_FASTSEGMENTSIZE; - memcpy(dstPtr, srcPtr, DIB_FASTSEGMENTSIZE); - srcPtr += DIB_FASTSEGMENTSIZE; - } - - return nbSegments * DIB_FASTSEGMENTSIZE; -} - 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) @@ -914,7 +865,7 @@ size_t ZDICT_addEntropyTablesFromBuffer_advanced(void* dictBuffer, size_t dictCo } -#define DIB_MINSAMPLESSIZE (DIB_FASTSEGMENTSIZE*3) +#define DIB_MINSAMPLESSIZE 512 /*! ZDICT_trainFromBuffer_unsafe() : * `samplesBuffer` must be followed by noisy guard band. * @return : size of dictionary. @@ -928,53 +879,67 @@ size_t ZDICT_trainFromBuffer_unsafe( dictItem* const dictList = (dictItem*)malloc(dictListSize * sizeof(*dictList)); unsigned selectivity = params.selectivityLevel; size_t const targetDictSize = maxDictSize; - size_t sBuffSize; + size_t const samplesBuffSize = ZDICT_totalSampleSize(samplesSizes, nbSamples); size_t dictSize = 0; /* checks */ if (!dictList) return ERROR(memory_allocation); if (maxDictSize <= g_provision_entropySize + g_min_fast_dictContent) { free(dictList); return ERROR(dstSize_tooSmall); } + if (samplesBuffSize < DIB_MINSAMPLESSIZE) { free(dictList); return 0; } /* not enough source to create dictionary */ /* init */ - { unsigned u; for (u=0, sBuffSize=0; u1) { /* selectivity == 1 => fast mode */ - ZDICT_trainBuffer(dictList, dictListSize, - samplesBuffer, sBuffSize, - samplesSizes, nbSamples, - selectivity, (U32)targetDictSize); + ZDICT_trainBuffer(dictList, dictListSize, + samplesBuffer, samplesBuffSize, + samplesSizes, nbSamples, + selectivity); + + /* display best matches */ + if (g_displayLevel>= 3) { + U32 const nb = 25; + U32 const dictContentSize = ZDICT_dictSize(dictList); + U32 u; + DISPLAYLEVEL(3, "\n %u segments found, of total size %u \n", dictList[0].pos, dictContentSize); + DISPLAYLEVEL(3, "list %u best segments \n", nb); + for (u=1; u<=nb; u++) { + U32 pos = dictList[u].pos; + U32 length = dictList[u].length; + U32 printedLength = MIN(40, length); + DISPLAYLEVEL(3, "%3u:%3u bytes at pos %8u, savings %7u bytes |", + u, length, pos, dictList[u].savings); + ZDICT_printHex(3, (const char*)samplesBuffer+pos, printedLength); + DISPLAYLEVEL(3, "| \n"); + } } - /* display best matches */ - if (g_displayLevel>= 3) { - U32 const nb = 25; - U32 const dictContentSize = ZDICT_dictSize(dictList); - U32 u; - DISPLAYLEVEL(3, "\n %u segments found, of total size %u \n", dictList[0].pos, dictContentSize); - DISPLAYLEVEL(3, "list %u best segments \n", nb); - for (u=1; u<=nb; u++) { - U32 pos = dictList[u].pos; - U32 length = dictList[u].length; - U32 printedLength = MIN(40, length); - DISPLAYLEVEL(3, "%3u:%3u bytes at pos %8u, savings %7u bytes |", - u, length, pos, dictList[u].savings); - ZDICT_printHex(3, (const char*)samplesBuffer+pos, printedLength); - DISPLAYLEVEL(3, "| \n"); - } } } /* create dictionary */ { U32 dictContentSize = ZDICT_dictSize(dictList); - U64 const totalSamplesSize = ZDICT_totalSampleSize(samplesSizes, nbSamples); if (dictContentSize < targetDictSize/2) { DISPLAYLEVEL(2, "! warning : created dictionary significantly smaller than requested (%u < %u) \n", dictContentSize, (U32)maxDictSize); 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"); - if (totalSamplesSize < 10 * targetDictSize) - DISPLAYLEVEL(2, "! consider also increasing the number of samples (total size : %u MB)\n", (U32)(totalSamplesSize>>20)); + if (samplesBuffSize < 10 * targetDictSize) + DISPLAYLEVEL(2, "! consider also increasing the number of samples (total size : %u MB)\n", (U32)(samplesBuffSize>>20)); + } + + if (dictContentSize > targetDictSize*2) { + DISPLAYLEVEL(2, "! warning : calculated dictionary significantly larger than requested (%u > %u) \n", dictContentSize, (U32)maxDictSize); + DISPLAYLEVEL(2, "! consider decreasing selectivity to produce denser dictionary (-s%u) \n", selectivity-1); + DISPLAYLEVEL(2, "! test its efficiency on samples \n"); + } + + /* limit dictionary size */ + { U32 const max = dictList->pos; /* convention : nb of useful elts within dictList */ + U32 currentSize = 0; + U32 n; for (n=1; n targetDictSize) break; + } + dictList->pos = n; } /* build dict content */ @@ -987,14 +952,6 @@ size_t ZDICT_trainFromBuffer_unsafe( memcpy(ptr, (const char*)samplesBuffer+dictList[u].pos, l); } } - /* fast mode dict content */ - if (selectivity==1) { /* note could also be used to complete a dictionary, but not necessarily better */ - DISPLAYLEVEL(3, "\r%70s\r", ""); /* clean display line */ - DISPLAYLEVEL(3, "Adding %u KB with fast sampling \n", (U32)(targetDictSize>>10)); - dictContentSize = (U32)ZDICT_fastSampling(dictBuffer, targetDictSize, - samplesBuffer, sBuffSize); - } - dictSize = ZDICT_addEntropyTablesFromBuffer_advanced(dictBuffer, dictContentSize, maxDictSize, samplesBuffer, samplesSizes, nbSamples, params); From 07626dfa51d70b7dc90adfda468a67dd3d15f5b5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Jul 2016 13:28:46 +0200 Subject: [PATCH 12/46] improved dictbuilder notifications on selectivity --- lib/dictBuilder/zdict.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 846e47766..683184e6e 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -489,14 +489,13 @@ static U32 ZDICT_dictSize(const dictItem* dictList) static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, const void* const buffer, size_t bufferSize, /* buffer must end with noisy guard band */ const size_t* fileSizes, unsigned nbFiles, - U32 shiftRatio) + U32 minRatio) { int* const suffix0 = (int*)malloc((bufferSize+2)*sizeof(*suffix0)); int* const suffix = suffix0+1; U32* reverseSuffix = (U32*)malloc((bufferSize)*sizeof(*reverseSuffix)); BYTE* doneMarks = (BYTE*)malloc((bufferSize+16)*sizeof(*doneMarks)); /* +16 for overflow security */ U32* filePos = (U32*)malloc(nbFiles * sizeof(*filePos)); - U32 minRatio = nbFiles >> shiftRatio; size_t result = 0; /* init */ @@ -877,7 +876,8 @@ size_t ZDICT_trainFromBuffer_unsafe( { U32 const dictListSize = MAX(MAX(DICTLISTSIZE, nbSamples), (U32)(maxDictSize/16)); dictItem* const dictList = (dictItem*)malloc(dictListSize * sizeof(*dictList)); - unsigned selectivity = params.selectivityLevel; + unsigned const selectivity = params.selectivityLevel == 0 ? g_selectivity_default : params.selectivityLevel; + unsigned const minRep = nbSamples >> selectivity; size_t const targetDictSize = maxDictSize; size_t const samplesBuffSize = ZDICT_totalSampleSize(samplesSizes, nbSamples); size_t dictSize = 0; @@ -890,13 +890,12 @@ size_t ZDICT_trainFromBuffer_unsafe( /* init */ ZDICT_initDictItem(dictList); g_displayLevel = params.notificationLevel; - if (selectivity==0) selectivity = g_selectivity_default; /* build dictionary */ ZDICT_trainBuffer(dictList, dictListSize, samplesBuffer, samplesBuffSize, samplesSizes, nbSamples, - selectivity); + minRep); /* display best matches */ if (g_displayLevel>= 3) { @@ -920,16 +919,20 @@ size_t ZDICT_trainFromBuffer_unsafe( { 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, "! 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"); + 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"); + } if (samplesBuffSize < 10 * targetDictSize) - DISPLAYLEVEL(2, "! consider also increasing the number of samples (total size : %u MB)\n", (U32)(samplesBuffSize>>20)); + DISPLAYLEVEL(2, "! consider increasing the number of samples (total size : %u MB)\n", (U32)(samplesBuffSize>>20)); } - if (dictContentSize > targetDictSize*2) { - DISPLAYLEVEL(2, "! warning : calculated dictionary significantly larger than requested (%u > %u) \n", dictContentSize, (U32)maxDictSize); - DISPLAYLEVEL(2, "! consider decreasing selectivity to produce denser dictionary (-s%u) \n", selectivity-1); - DISPLAYLEVEL(2, "! test its efficiency on samples \n"); + if ((dictContentSize > targetDictSize*2) && (nbSamples > 2*MINRATIO) && (selectivity>1)) { + U32 proposedSelectivity = selectivity-1; + while ((nbSamples >> proposedSelectivity) <= MINRATIO) { proposedSelectivity--; } + DISPLAYLEVEL(2, "! note : calculated dictionary significantly larger than requested (%u > %u) \n", dictContentSize, (U32)maxDictSize); + DISPLAYLEVEL(2, "! you may consider decreasing selectivity to produce denser dictionary (-s%u) \n", proposedSelectivity); + DISPLAYLEVEL(2, "! but test its efficiency on samples \n"); } /* limit dictionary size */ @@ -937,9 +940,10 @@ size_t ZDICT_trainFromBuffer_unsafe( U32 currentSize = 0; U32 n; for (n=1; n targetDictSize) break; + if (currentSize > targetDictSize) { currentSize -= dictList[n].length; break; } } dictList->pos = n; + dictContentSize = currentSize; } /* build dict content */ From c154d9d6a2f150864c10604ead59dc218ecdc7d2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Jul 2016 14:37:00 +0200 Subject: [PATCH 13/46] better support for large dictionaries (> 128 KB) --- lib/common/zstd_internal.h | 25 +++++++++++++++++++++++++ lib/compress/zstd_compress.c | 21 --------------------- lib/dictBuilder/zdict.c | 22 ++++++++++++---------- 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 8e50da347..a68a92cc5 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -230,4 +230,29 @@ void* ZSTD_defaultAllocFunction(void* opaque, size_t size); void ZSTD_defaultFreeFunction(void* opaque, void* address); static const ZSTD_customMem defaultCustomMem = { ZSTD_defaultAllocFunction, ZSTD_defaultFreeFunction, NULL }; +/*====== common function ======*/ + +MEM_STATIC U32 ZSTD_highbit32(U32 val) +{ +# if defined(_MSC_VER) /* Visual */ + unsigned long r=0; + _BitScanReverse(&r, val); + return (unsigned)r; +# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */ + return 31 - __builtin_clz(val); +# else /* Software version */ + static const int DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; + U32 v = val; + int r; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + r = DeBruijnClz[(U32)(v * 0x07C4ACDDU) >> 27]; + return r; +# endif +} + + #endif /* ZSTD_CCOMMON_H_MODULE */ diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index aac61be6a..7ba7f8235 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -73,27 +73,6 @@ static const U32 g_searchStrength = 8; /* control skip over incompressible dat ***************************************/ size_t ZSTD_compressBound(size_t srcSize) { return FSE_compressBound(srcSize) + 12; } -static U32 ZSTD_highbit32(U32 val) -{ -# if defined(_MSC_VER) /* Visual */ - unsigned long r=0; - _BitScanReverse(&r, val); - return (unsigned)r; -# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */ - return 31 - __builtin_clz(val); -# else /* Software version */ - static const int DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; - U32 v = val; - int r; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - r = DeBruijnClz[(U32)(v * 0x07C4ACDDU) >> 27]; - return r; -# endif -} /*-************************************* * Sequence storage diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 683184e6e..421fdd436 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -660,7 +660,7 @@ static void ZDICT_insertSortCount(offsetCount_t table[ZSTD_REP_NUM+1], U32 val, } -#define OFFCODE_MAX 18 /* only applicable to first block */ +#define OFFCODE_MAX 30 /* only applicable to first block */ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, unsigned compressionLevel, const void* srcBuffer, const size_t* fileSizes, unsigned nbFiles, @@ -670,6 +670,7 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, HUF_CREATE_STATIC_CTABLE(hufTable, 255); U32 offcodeCount[OFFCODE_MAX+1]; short offcodeNCount[OFFCODE_MAX+1]; + U32 offcodeMax = ZSTD_highbit32((U32)(dictBufferSize + 128 KB)); U32 matchLengthCount[MaxML+1]; short matchLengthNCount[MaxML+1]; U32 litLengthCount[MaxLL+1]; @@ -686,8 +687,9 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, BYTE* dstPtr = (BYTE*)dstBuffer; /* init */ + 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<=OFFCODE_MAX; u++) offcodeCount[u]=1; + for (u=0; u<=offcodeMax; u++) offcodeCount[u]=1; for (u=0; u<=MaxML; u++) matchLengthCount[u]=1; for (u=0; u<=MaxLL; u++) litLengthCount[u]=1; repOffset[1] = repOffset[4] = repOffset[8] = 1; @@ -866,8 +868,8 @@ size_t ZDICT_addEntropyTablesFromBuffer_advanced(void* dictBuffer, size_t dictCo #define DIB_MINSAMPLESSIZE 512 /*! ZDICT_trainFromBuffer_unsafe() : -* `samplesBuffer` must be followed by noisy guard band. -* @return : size of dictionary. +* Warning : `samplesBuffer` must be followed by noisy guard band. +* @return : size of dictionary, or an error code which can be tested with ZDICT_isError() */ size_t ZDICT_trainFromBuffer_unsafe( void* dictBuffer, size_t maxDictSize, @@ -973,23 +975,23 @@ size_t ZDICT_trainFromBuffer_advanced(void* dictBuffer, size_t dictBufferCapacit const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, ZDICT_params_t params) { + size_t result; void* newBuff; - size_t sBuffSize; + size_t const sBuffSize = ZDICT_totalSampleSize(samplesSizes, nbSamples); + if (sBuffSize < DIB_MINSAMPLESSIZE) return 0; /* not enough content => no dictionary */ - { unsigned u; for (u=0, sBuffSize=0; u no dictionary */ newBuff = malloc(sBuffSize + NOISELENGTH); if (!newBuff) return ERROR(memory_allocation); memcpy(newBuff, samplesBuffer, sBuffSize); ZDICT_fillNoise((char*)newBuff + sBuffSize, NOISELENGTH); /* guard band, for end of buffer condition */ - { size_t const result = ZDICT_trainFromBuffer_unsafe( + result = ZDICT_trainFromBuffer_unsafe( dictBuffer, dictBufferCapacity, newBuff, samplesSizes, nbSamples, params); - free(newBuff); - return result; } + free(newBuff); + return result; } From 55a8bea0b5df2e74dd281db5e08e1aed7f1ff5fd Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Jul 2016 14:48:47 +0200 Subject: [PATCH 14/46] fixed dictionary generation --- NEWS | 1 + lib/dictBuilder/zdict.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index d01a33131..1a329d85c 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ New : updated compresson format Improved : better speed on clang and gcc -O2, thanks to Eric Biggers Fixed : legacy mode with ZSTD_HEAPMODE=0, by Christopher Bergqvist Fixed : premature end of frame when zero-sized raw block, reported by Eric Biggers +Fixed : statistics for large dictionaries (> 128 KB), reported by Ilona Papava Fixed : checksum correctly checked in single-pass mode Fixed : combined --test amd --rm, reported by Andreas M. Nilsson Modified : minor compression level adaptations diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 421fdd436..8713b02e9 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -735,8 +735,8 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, } /* note : the result of this phase should be used to better appreciate the impact on statistics */ - total=0; for (u=0; u<=OFFCODE_MAX; u++) total+=offcodeCount[u]; - errorCode = FSE_normalizeCount(offcodeNCount, Offlog, offcodeCount, total, OFFCODE_MAX); + total=0; for (u=0; u<=offcodeMax; u++) total+=offcodeCount[u]; + errorCode = FSE_normalizeCount(offcodeNCount, Offlog, offcodeCount, total, offcodeMax); if (FSE_isError(errorCode)) { eSize = ERROR(GENERIC); DISPLAYLEVEL(1, "FSE_normalizeCount error with offcodeCount"); From 411053488657c93d1230882c2d299135f54ddf6b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Jul 2016 15:09:11 +0200 Subject: [PATCH 15/46] ZSTD_maxCLevel() is promoted to "stable" API (#254, by @FrancescAlted) --- lib/compress/zstd_compress.c | 2 +- lib/dictBuilder/zdict.c | 4 ++-- lib/dictBuilder/zdict.h | 2 +- lib/zstd.h | 6 +++--- programs/paramgrill.c | 10 +++++----- programs/zstdcli.c | 8 ++++---- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 7ba7f8235..a2b61f676 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2767,7 +2767,7 @@ ZSTDLIB_API size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx, #define ZSTD_DEFAULT_CLEVEL 1 #define ZSTD_MAX_CLEVEL 22 -unsigned ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; } +int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; } static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = { { /* "default" */ diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 8713b02e9..d77f7ad9a 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -85,7 +85,7 @@ #define PRIME2 2246822519U #define MINRATIO 4 -static const U32 g_compressionLevel_default = 5; +static const int g_compressionLevel_default = 5; static const U32 g_selectivity_default = 9; static const size_t g_provision_entropySize = 200; static const size_t g_min_fast_dictContent = 192; @@ -841,7 +841,7 @@ size_t ZDICT_addEntropyTablesFromBuffer_advanced(void* dictBuffer, size_t dictCo ZDICT_params_t params) { size_t hSize; - unsigned const compressionLevel = (params.compressionLevel == 0) ? g_compressionLevel_default : params.compressionLevel; + int const compressionLevel = (params.compressionLevel <= 0) ? g_compressionLevel_default : params.compressionLevel; /* dictionary header */ MEM_writeLE32(dictBuffer, ZSTD_DICT_MAGIC); diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index b96b828f7..599e8fb59 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -86,7 +86,7 @@ const char* ZDICT_getErrorName(size_t errorCode); typedef struct { unsigned selectivityLevel; /* 0 means default; larger => bigger selection => larger dictionary */ - unsigned compressionLevel; /* 0 means default; target a specific zstd compression level */ + int compressionLevel; /* 0 means default; target a specific zstd compression level */ 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 */ unsigned reserved[2]; /* space for future parameters */ diff --git a/lib/zstd.h b/lib/zstd.h index e459f6259..e819eda62 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -93,8 +93,10 @@ unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); ZSTDLIB_API size_t ZSTD_decompress( void* dst, size_t dstCapacity, const void* src, size_t compressedSize); + /*====== Helper functions ======*/ -ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size (worst case scenario) */ +ZSTDLIB_API int ZSTD_maxCLevel(void); /*!< maximum compression level available */ +ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case scenario */ ZSTDLIB_API unsigned ZSTD_isError(size_t code); /*!< tells if a `size_t` function result is an error code */ ZSTDLIB_API const char* ZSTD_getErrorName(size_t code); /*!< provides readable string from an error code */ @@ -264,8 +266,6 @@ ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictS * Gives the amount of memory used by a given ZSTD_CCtx */ ZSTDLIB_API size_t ZSTD_sizeofCCtx(const ZSTD_CCtx* cctx); -ZSTDLIB_API unsigned ZSTD_maxCLevel (void); - /*! ZSTD_getParams() : * same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of a `ZSTD_compressionParameters`. * All fields of `ZSTD_frameParameters` are set to default (0) */ diff --git a/programs/paramgrill.c b/programs/paramgrill.c index 04a55c876..9348a40f9 100644 --- a/programs/paramgrill.c +++ b/programs/paramgrill.c @@ -340,7 +340,7 @@ typedef struct { static void BMK_printWinners2(FILE* f, const winnerInfo_t* winners, size_t srcSize) { - unsigned cLevel; + int cLevel; fprintf(f, "\n /* Proposed configurations : */ \n"); fprintf(f, " /* W, C, H, S, L, T, strat */ \n"); @@ -364,7 +364,7 @@ static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters para { BMK_result_t testResult; int better = 0; - unsigned cLevel; + int cLevel; BMK_benchParam(&testResult, srcBuffer, srcSize, ctx, params); @@ -618,9 +618,9 @@ static void BMK_benchMem(void* srcBuffer, size_t srcSize) } /* establish speed objectives (relative to level 1) */ - { unsigned u; - for (u=2; u<=ZSTD_maxCLevel(); u++) - g_cSpeedTarget[u] = (g_cSpeedTarget[u-1] * 25) / 32; + { int i; + for (i=2; i<=ZSTD_maxCLevel(); i++) + g_cSpeedTarget[i] = (g_cSpeedTarget[i-1] * 25) / 32; } /* populate initial solution */ diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 30e0d01b7..466823222 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -90,7 +90,7 @@ static const char* g_defaultDictName = "dictionary"; static const unsigned g_defaultMaxDictSize = 110 KB; -static const unsigned g_defaultDictCLevel = 5; +static const int g_defaultDictCLevel = 5; static const unsigned g_defaultSelectivityLevel = 9; @@ -216,8 +216,8 @@ int main(int argCount, const char** argv) nextArgumentIsMaxDict=0, nextArgumentIsDictID=0, nextArgumentIsFile=0; - unsigned cLevel = ZSTDCLI_CLEVEL_DEFAULT; - unsigned cLevelLast = 1; + int cLevel = ZSTDCLI_CLEVEL_DEFAULT; + int cLevelLast = 1; unsigned recursive = 0; const char** filenameTable = (const char**)malloc(argCount * sizeof(const char*)); /* argCount >= 1 */ unsigned filenameIdx = 0; @@ -227,7 +227,7 @@ int main(int argCount, const char** argv) char* dynNameSpace = NULL; unsigned maxDictSize = g_defaultMaxDictSize; unsigned dictID = 0; - unsigned dictCLevel = g_defaultDictCLevel; + int dictCLevel = g_defaultDictCLevel; unsigned dictSelect = g_defaultSelectivityLevel; #ifdef UTIL_HAS_CREATEFILELIST const char** fileNamesTable = NULL; From 4b9ca0a6b5b4840dabb56c83fe9fce2599935046 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Jul 2016 19:53:19 +0200 Subject: [PATCH 16/46] minor example variation --- examples/simple_compression.c | 34 +++++++++++++++++----------------- lib/Makefile | 3 ++- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/examples/simple_compression.c b/examples/simple_compression.c index 71a40c271..adff81e85 100644 --- a/examples/simple_compression.c +++ b/examples/simple_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,7 +49,7 @@ 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; @@ -58,11 +58,11 @@ static void* malloc_X(size_t size) 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)); @@ -74,9 +74,9 @@ static void* loadFile_X(const char* fileName, size_t* size) } -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,12 +89,12 @@ static void saveFile_X(const char* fileName, const void* buff, size_t buffSize) } -static void compress(const char* fname, const char* oname) +static void compress_orDie(const char* fname, const char* oname) { 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); size_t const cSize = ZSTD_compress(cBuff, cBuffSize, fBuff, fSize, 1); if (ZSTD_isError(cSize)) { @@ -102,7 +102,7 @@ static void compress(const char* fname, const char* oname) 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); @@ -112,11 +112,11 @@ static void compress(const char* fname, const char* oname) } -static const char* createOutFilename(const char* filename) +static const 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"); @@ -135,8 +135,8 @@ int main(int argc, const char** argv) return 1; } - const char* const outFilename = createOutFilename(inFilename); - compress(inFilename, outFilename); + const char* const outFilename = createOutFilename_orDie(inFilename); + compress_orDie(inFilename, outFilename); return 0; } diff --git a/lib/Makefile b/lib/Makefile index 6df2b1a0b..2b5565817 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -48,7 +48,8 @@ INCLUDEDIR=$(PREFIX)/include 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 +CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ + -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(MOREFLAGS) From 731ef16fc1aa2d1ba3b430922ff85bc339528577 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Jul 2016 21:05:12 +0200 Subject: [PATCH 17/46] minor code style refactoring --- lib/compress/zstd_compress.c | 191 +++++++++++++++++------------------ 1 file changed, 94 insertions(+), 97 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index a2b61f676..790cedc4d 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -66,6 +66,8 @@ * Constants ***************************************/ static const U32 g_searchStrength = 8; /* control skip over incompressible data */ +#define HASH_READ_SIZE 8 +typedef enum { ZSTDcs_created=0, ZSTDcs_init, ZSTDcs_ongoing, ZSTDcs_ending } ZSTD_compressionStage_e; /*-************************************* @@ -101,7 +103,7 @@ struct ZSTD_CCtx_s U32 nextToUpdate3; /* index from which to continue dictionary update */ U32 hashLog3; /* dispatch table : larger == faster, more memory */ U32 loadedDictEnd; - U32 stage; /* 0: created; 1: init,dictLoad; 2:started */ + ZSTD_compressionStage_e stage; U32 rep[ZSTD_REP_NUM]; U32 savedRep[ZSTD_REP_NUM]; U32 dictID; @@ -119,9 +121,9 @@ struct ZSTD_CCtx_s U32* chainTable; HUF_CElt* hufTable; U32 flagStaticTables; - FSE_CTable offcodeCTable [FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)]; - FSE_CTable matchlengthCTable [FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)]; - FSE_CTable litlengthCTable [FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)]; + FSE_CTable offcodeCTable [FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)]; + FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)]; + FSE_CTable litlengthCTable [FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)]; }; ZSTD_CCtx* ZSTD_createCCtx(void) @@ -230,16 +232,16 @@ ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, u size_t ZSTD_estimateCCtxSize(ZSTD_compressionParameters cParams) { - const size_t blockSize = MIN(ZSTD_BLOCKSIZE_ABSOLUTEMAX, (size_t)1 << cParams.windowLog); - const U32 divider = (cParams.searchLength==3) ? 3 : 4; - const size_t maxNbSeq = blockSize / divider; - const size_t tokenSpace = blockSize + 11*maxNbSeq; + size_t const blockSize = MIN(ZSTD_BLOCKSIZE_ABSOLUTEMAX, (size_t)1 << cParams.windowLog); + U32 const divider = (cParams.searchLength==3) ? 3 : 4; + size_t const maxNbSeq = blockSize / divider; + size_t const tokenSpace = blockSize + 11*maxNbSeq; - const size_t chainSize = (cParams.strategy == ZSTD_fast) ? 0 : (1 << cParams.chainLog); - const size_t hSize = ((size_t)1) << cParams.hashLog; - const U32 hashLog3 = (cParams.searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, cParams.windowLog); - const size_t h3Size = ((size_t)1) << hashLog3; - const size_t tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); + size_t const chainSize = (cParams.strategy == ZSTD_fast) ? 0 : (1 << cParams.chainLog); + size_t const hSize = ((size_t)1) << cParams.hashLog; + U32 const hashLog3 = (cParams.searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, cParams.windowLog); + size_t const h3Size = ((size_t)1) << hashLog3; + size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); size_t const optSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, params.cParams.windowLog); - const size_t h3Size = ((size_t)1) << hashLog3; - const size_t tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); + size_t const blockSize = MIN(ZSTD_BLOCKSIZE_ABSOLUTEMAX, (size_t)1 << params.cParams.windowLog); + U32 const divider = (params.cParams.searchLength==3) ? 3 : 4; + size_t const maxNbSeq = blockSize / divider; + size_t const tokenSpace = blockSize + 11*maxNbSeq; + size_t const chainSize = (params.cParams.strategy == ZSTD_fast) ? 0 : (1 << params.cParams.chainLog); + size_t const hSize = ((size_t)1) << params.cParams.hashLog; + U32 const hashLog3 = (params.cParams.searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, params.cParams.windowLog); + size_t const h3Size = ((size_t)1) << hashLog3; + size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); /* Check if workSpace is large enough, alloc a new one if needed */ { size_t const optSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<seqStore.offCodeStart = zc->seqStore.mlCodeStart + maxNbSeq; zc->seqStore.litStart = zc->seqStore.offCodeStart + maxNbSeq; - zc->stage = 1; + zc->stage = ZSTDcs_init; zc->dictID = 0; zc->loadedDictEnd = 0; @@ -330,21 +332,21 @@ static size_t ZSTD_resetCCtx_advanced (ZSTD_CCtx* zc, /*! ZSTD_copyCCtx() : * Duplicate an existing context `srcCCtx` into another one `dstCCtx`. -* Only works during stage 1 (i.e. after creation, but before first call to ZSTD_compressContinue()). +* Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()). * @return : 0, or an error code */ size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx) { - if (srcCCtx->stage!=1) return ERROR(stage_wrong); + if (srcCCtx->stage!=ZSTDcs_init) return ERROR(stage_wrong); memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem)); ZSTD_resetCCtx_advanced(dstCCtx, srcCCtx->params, srcCCtx->frameContentSize, 0); dstCCtx->params.fParams.contentSizeFlag = 0; /* content size different from the one set during srcCCtx init */ /* copy tables */ - { const size_t chainSize = (srcCCtx->params.cParams.strategy == ZSTD_fast) ? 0 : (1 << srcCCtx->params.cParams.chainLog); - const size_t hSize = ((size_t)1) << srcCCtx->params.cParams.hashLog; - const size_t h3Size = (size_t)1 << srcCCtx->hashLog3; - const size_t tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); + { size_t const chainSize = (srcCCtx->params.cParams.strategy == ZSTD_fast) ? 0 : (1 << srcCCtx->params.cParams.chainLog); + size_t const hSize = ((size_t)1) << srcCCtx->params.cParams.hashLog; + size_t const h3Size = (size_t)1 << srcCCtx->hashLog3; + size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); memcpy(dstCCtx->workSpace, srcCCtx->workSpace, tableSpace); } @@ -387,13 +389,13 @@ static void ZSTD_reduceTable (U32* const table, U32 const size, U32 const reduce * rescale all indexes to avoid future overflow (indexes are U32) */ static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue) { - { const U32 hSize = 1 << zc->params.cParams.hashLog; + { U32 const hSize = 1 << zc->params.cParams.hashLog; ZSTD_reduceTable(zc->hashTable, hSize, reducerValue); } - { const U32 chainSize = (zc->params.cParams.strategy == ZSTD_fast) ? 0 : (1 << zc->params.cParams.chainLog); + { U32 const chainSize = (zc->params.cParams.strategy == ZSTD_fast) ? 0 : (1 << zc->params.cParams.chainLog); ZSTD_reduceTable(zc->chainTable, chainSize, reducerValue); } - { const U32 h3Size = (zc->hashLog3) ? 1 << zc->hashLog3 : 0; + { U32 const h3Size = (zc->hashLog3) ? 1 << zc->hashLog3 : 0; ZSTD_reduceTable(zc->hashTable3, h3Size, reducerValue); } } @@ -416,7 +418,7 @@ size_t ZSTD_noCompressBlock (void* dst, size_t dstCapacity, const void* src, siz static size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize) { BYTE* const ostart = (BYTE* const)dst; - U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); + U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); if (srcSize + flSize > dstCapacity) return ERROR(dstSize_tooSmall); @@ -441,7 +443,7 @@ static size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void static size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize) { BYTE* const ostart = (BYTE* const)dst; - U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); + U32 const flSize = 1 + (srcSize>31) + (srcSize>4095); (void)dstCapacity; /* dstCapacity already guaranteed to be >=4, hence large enough */ @@ -472,7 +474,7 @@ static size_t ZSTD_compressLiterals (ZSTD_CCtx* zc, { size_t const minGain = ZSTD_minGain(srcSize); size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB); - BYTE* const ostart = (BYTE*)dst; + BYTE* const ostart = (BYTE*)dst; U32 singleStream = srcSize < 256; symbolEncodingType_e hType = set_compressed; size_t cLitSize; @@ -535,7 +537,7 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr, size_t const nbSeq) 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 }; - const BYTE LL_deltaCode = 19; + BYTE const LL_deltaCode = 19; const U16* const llTable = seqStorePtr->litLengthStart; BYTE* const llCodeTable = seqStorePtr->llCodeStart; size_t u; @@ -722,12 +724,12 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* zc, { size_t n; for (n=nbSeq-2 ; n> (64-h)) ; } static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h); } -//static const U64 prime8bytes = 58295818150454627ULL; static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL; static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; } static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); } @@ -940,10 +940,10 @@ static size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls) static void ZSTD_fillHashTable (ZSTD_CCtx* zc, const void* end, const U32 mls) { U32* const hashTable = zc->hashTable; - const U32 hBits = zc->params.cParams.hashLog; + U32 const hBits = zc->params.cParams.hashLog; const BYTE* const base = zc->base; const BYTE* ip = base + zc->nextToUpdate; - const BYTE* const iend = ((const BYTE*)end) - 8; + const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; const size_t fastHashFillStep = 3; while(ip <= iend) { @@ -959,16 +959,16 @@ void ZSTD_compressBlock_fast_generic(ZSTD_CCtx* cctx, const U32 mls) { U32* const hashTable = cctx->hashTable; - const U32 hBits = cctx->params.cParams.hashLog; + U32 const hBits = cctx->params.cParams.hashLog; seqStore_t* seqStorePtr = &(cctx->seqStore); const BYTE* const base = cctx->base; const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = cctx->dictLimit; + const U32 lowestIndex = cctx->dictLimit; const BYTE* const lowest = base + lowestIndex; const BYTE* const iend = istart + srcSize; - const BYTE* const ilimit = iend - 8; + const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=cctx->rep[0], offset_2=cctx->rep[1]; U32 offsetSaved = 0; @@ -1158,7 +1158,7 @@ static void ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx, static void ZSTD_compressBlock_fast_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) { - const U32 mls = ctx->params.cParams.searchLength; + U32 const mls = ctx->params.cParams.searchLength; switch(mls) { default: @@ -1180,12 +1180,12 @@ static void ZSTD_compressBlock_fast_extDict(ZSTD_CCtx* ctx, static void ZSTD_fillDoubleHashTable (ZSTD_CCtx* cctx, const void* end, const U32 mls) { U32* const hashLarge = cctx->hashTable; - const U32 hBitsL = cctx->params.cParams.hashLog; + U32 const hBitsL = cctx->params.cParams.hashLog; U32* const hashSmall = cctx->chainTable; - const U32 hBitsS = cctx->params.cParams.chainLog; + U32 const hBitsS = cctx->params.cParams.chainLog; const BYTE* const base = cctx->base; const BYTE* ip = base + cctx->nextToUpdate; - const BYTE* const iend = ((const BYTE*)end) - 8; + const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE; const size_t fastHashFillStep = 3; while(ip <= iend) { @@ -1213,7 +1213,7 @@ void ZSTD_compressBlock_doubleFast_generic(ZSTD_CCtx* cctx, const U32 lowestIndex = cctx->dictLimit; const BYTE* const lowest = base + lowestIndex; const BYTE* const iend = istart + srcSize; - const BYTE* const ilimit = iend - 8; + const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=cctx->rep[0], offset_2=cctx->rep[1]; U32 offsetSaved = 0; @@ -1322,9 +1322,9 @@ static void ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, const U32 mls) { U32* const hashLong = ctx->hashTable; - const U32 hBitsL = ctx->params.cParams.hashLog; + U32 const hBitsL = ctx->params.cParams.hashLog; U32* const hashSmall = ctx->chainTable; - const U32 hBitsS = ctx->params.cParams.chainLog; + U32 const hBitsS = ctx->params.cParams.chainLog; seqStore_t* seqStorePtr = &(ctx->seqStore); const BYTE* const base = ctx->base; const BYTE* const dictBase = ctx->dictBase; @@ -1435,7 +1435,7 @@ static void ZSTD_compressBlock_doubleFast_extDict_generic(ZSTD_CCtx* ctx, static void ZSTD_compressBlock_doubleFast_extDict(ZSTD_CCtx* ctx, const void* src, size_t srcSize) { - const U32 mls = ctx->params.cParams.searchLength; + U32 const mls = ctx->params.cParams.searchLength; switch(mls) { default: @@ -1460,13 +1460,13 @@ static void ZSTD_compressBlock_doubleFast_extDict(ZSTD_CCtx* ctx, static U32 ZSTD_insertBt1(ZSTD_CCtx* zc, const BYTE* const ip, const U32 mls, const BYTE* const iend, U32 nbCompares, U32 extDict) { - U32* const hashTable = zc->hashTable; - const U32 hashLog = zc->params.cParams.hashLog; - const size_t h = ZSTD_hashPtr(ip, hashLog, mls); - U32* const bt = zc->chainTable; - const U32 btLog = zc->params.cParams.chainLog - 1; - const U32 btMask= (1 << btLog) - 1; - U32 matchIndex = hashTable[h]; + U32* const hashTable = zc->hashTable; + U32 const hashLog = zc->params.cParams.hashLog; + size_t const h = ZSTD_hashPtr(ip, hashLog, mls); + U32* const bt = zc->chainTable; + U32 const btLog = zc->params.cParams.chainLog - 1; + U32 const btMask = (1 << btLog) - 1; + U32 matchIndex = hashTable[h]; size_t commonLengthSmaller=0, commonLengthLarger=0; const BYTE* const base = zc->base; const BYTE* const dictBase = zc->dictBase; @@ -1479,7 +1479,7 @@ static U32 ZSTD_insertBt1(ZSTD_CCtx* zc, const BYTE* const ip, const U32 mls, co U32* smallerPtr = bt + 2*(current&btMask); U32* largerPtr = smallerPtr + 1; U32 dummy32; /* to be nullified at the end */ - const U32 windowLow = zc->lowLimit; + U32 const windowLow = zc->lowLimit; U32 matchEndIdx = current+8; size_t bestLength = 8; #ifdef ZSTD_C_PREDICT @@ -1564,12 +1564,12 @@ static size_t ZSTD_insertBtAndFindBestMatch ( U32 nbCompares, const U32 mls, U32 extDict) { - U32* const hashTable = zc->hashTable; - const U32 hashLog = zc->params.cParams.hashLog; - const size_t h = ZSTD_hashPtr(ip, hashLog, mls); - U32* const bt = zc->chainTable; - const U32 btLog = zc->params.cParams.chainLog - 1; - const U32 btMask= (1 << btLog) - 1; + U32* const hashTable = zc->hashTable; + U32 const hashLog = zc->params.cParams.hashLog; + size_t const h = ZSTD_hashPtr(ip, hashLog, mls); + U32* const bt = zc->chainTable; + U32 const btLog = zc->params.cParams.chainLog - 1; + U32 const btMask = (1 << btLog) - 1; U32 matchIndex = hashTable[h]; size_t commonLengthSmaller=0, commonLengthLarger=0; const BYTE* const base = zc->base; @@ -1715,13 +1715,11 @@ static size_t ZSTD_BtFindBestMatch_selectMLS_extDict ( -/* *********************** +/* ********************************* * Hash Chain -*************************/ - +***********************************/ #define NEXT_IN_CHAIN(d, mask) chainTable[(d) & mask] - /* Update chains up to ip (excluded) Assumption : always within prefix (ie. not within extDict) */ FORCE_INLINE @@ -2230,7 +2228,6 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCa - static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) @@ -2287,15 +2284,15 @@ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, ZSTD_parameters params, U64 pledgedSrcSize, U32 dictID) { BYTE* const op = (BYTE*)dst; - U32 const dictIDSizeCode = (dictID>0) + (dictID>=256) + (dictID>=65536); /* 0-3 */ - U32 const checksumFlag = params.fParams.checksumFlag>0; - U32 const windowSize = 1U << params.cParams.windowLog; - U32 const singleSegment = params.fParams.contentSizeFlag && (windowSize > (pledgedSrcSize-1)); - BYTE const windowLogByte = (BYTE)((params.cParams.windowLog - ZSTD_WINDOWLOG_ABSOLUTEMIN) << 3); - U32 const fcsCode = params.fParams.contentSizeFlag ? + U32 const dictIDSizeCode = (dictID>0) + (dictID>=256) + (dictID>=65536); /* 0-3 */ + U32 const checksumFlag = params.fParams.checksumFlag>0; + U32 const windowSize = 1U << params.cParams.windowLog; + U32 const singleSegment = params.fParams.contentSizeFlag && (windowSize > (pledgedSrcSize-1)); + BYTE const windowLogByte = (BYTE)((params.cParams.windowLog - ZSTD_WINDOWLOG_ABSOLUTEMIN) << 3); + U32 const fcsCode = params.fParams.contentSizeFlag ? (pledgedSrcSize>=256) + (pledgedSrcSize>=65536+256) + (pledgedSrcSize>=0xFFFFFFFFU) : /* 0-3 */ 0; - BYTE const frameHeaderDecriptionByte = (BYTE)(dictIDSizeCode + (checksumFlag<<2) + (singleSegment<<5) + (fcsCode<<6) ); + BYTE const frameHeaderDecriptionByte = (BYTE)(dictIDSizeCode + (checksumFlag<<2) + (singleSegment<<5) + (fcsCode<<6) ); size_t pos; if (dstCapacity < ZSTD_frameHeaderSize_max) return ERROR(dstSize_tooSmall); @@ -2331,13 +2328,13 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* zc, const BYTE* const ip = (const BYTE*) src; size_t fhSize = 0; - if (zc->stage==0) return ERROR(stage_wrong); - if (frame && (zc->stage==1)) { /* copy saved header */ + if (zc->stage==ZSTDcs_created) return ERROR(stage_wrong); + if (frame && (zc->stage==ZSTDcs_init)) { /* copy saved header */ fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, zc->params, zc->frameContentSize, zc->dictID); if (ZSTD_isError(fhSize)) return fhSize; dstCapacity -= fhSize; dst = (char*)dst + fhSize; - zc->stage = 2; + zc->stage = ZSTDcs_ongoing; } /* Check if blocks follow each other */ @@ -2419,7 +2416,7 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_CCtx* zc, const void* src, size_t zc->loadedDictEnd = (U32)(iend - zc->base); zc->nextSrc = iend; - if (srcSize <= 8) return 0; + if (srcSize <= HASH_READ_SIZE) return 0; switch(zc->params.cParams.strategy) { @@ -2434,12 +2431,12 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_CCtx* zc, const void* src, size_t case ZSTD_greedy: case ZSTD_lazy: case ZSTD_lazy2: - ZSTD_insertAndFindFirstIndex (zc, iend-8, zc->params.cParams.searchLength); + ZSTD_insertAndFindFirstIndex (zc, iend-HASH_READ_SIZE, zc->params.cParams.searchLength); break; case ZSTD_btlazy2: case ZSTD_btopt: - ZSTD_updateTree(zc, iend-8, iend, 1 << zc->params.cParams.searchLog, zc->params.cParams.searchLength); + ZSTD_updateTree(zc, iend-HASH_READ_SIZE, iend, 1 << zc->params.cParams.searchLog, zc->params.cParams.searchLength); break; default: @@ -2454,8 +2451,8 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_CCtx* zc, const void* src, size_t /* Dictionary format : Magic == ZSTD_DICT_MAGIC (4 bytes) HUF_writeCTable(256) - FSE_writeNCount(ml) FSE_writeNCount(off) + FSE_writeNCount(ml) FSE_writeNCount(ll) RepOffsets Dictionary content @@ -2578,15 +2575,15 @@ size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) BYTE* op = (BYTE*)dst; size_t fhSize = 0; - if (cctx->stage==0) return ERROR(stage_wrong); /*< not even init ! */ + if (cctx->stage==ZSTDcs_created) return ERROR(stage_wrong); /*< not even init ! */ /* special case : empty frame */ - if (cctx->stage==1) { + if (cctx->stage==ZSTDcs_init) { fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->params, 0, 0); if (ZSTD_isError(fhSize)) return fhSize; dstCapacity -= fhSize; op += fhSize; - cctx->stage = 2; + cctx->stage = ZSTDcs_ongoing; } /* frame epilogue */ @@ -2597,7 +2594,7 @@ size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) MEM_writeLE24(op, (U32)bt_end + (checksum << 2)); } - cctx->stage = 0; /* return to "created but not init" status */ + cctx->stage = ZSTDcs_created; /* return to "created but no init" status */ return ZSTD_blockHeaderSize+fhSize; } From d4180cad9c9c25ab1b48821b238b9b9c4d61445f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 27 Jul 2016 21:21:36 +0200 Subject: [PATCH 18/46] minor code refactoring --- lib/compress/zstd_compress.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 790cedc4d..e7d8c2dad 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2227,7 +2227,6 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCa } - static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) @@ -2237,7 +2236,7 @@ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, const BYTE* ip = (const BYTE*)src; BYTE* const ostart = (BYTE*)dst; BYTE* op = ostart; - const U32 maxDist = 1 << cctx->params.cParams.windowLog; + U32 const maxDist = 1 << cctx->params.cParams.windowLog; ZSTD_stats_t* stats = &cctx->seqStore.stats; ZSTD_statsInit(stats); /* debug only */ @@ -2305,7 +2304,7 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, default: /* impossible */ case 0 : break; case 1 : op[pos] = (BYTE)(dictID); pos++; break; - case 2 : MEM_writeLE16(op+pos, (U16)(dictID)); pos+=2; break; + case 2 : MEM_writeLE16(op+pos, (U16)dictID); pos+=2; break; case 3 : MEM_writeLE32(op+pos, dictID); pos+=4; break; } switch(fcsCode) @@ -2328,8 +2327,9 @@ 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); - if (frame && (zc->stage==ZSTDcs_init)) { /* copy saved header */ + if (zc->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 (ZSTD_isError(fhSize)) return fhSize; dstCapacity -= fhSize; @@ -2340,13 +2340,13 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* zc, /* Check if blocks follow each other */ if (src != zc->nextSrc) { /* not contiguous */ - size_t const delta = zc->nextSrc - ip; + 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 < 8) zc->lowLimit = zc->dictLimit; /* too small extDict */ + if (zc->dictLimit - zc->lowLimit < HASH_READ_SIZE) zc->lowLimit = zc->dictLimit; /* too small extDict */ } /* preemptive overflow correction */ From c991cc18287ec2d9362d1a222e0c6e00d44704c5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 28 Jul 2016 00:55:43 +0200 Subject: [PATCH 19/46] new frame end, 32-bits checksums --- lib/common/zstd_internal.h | 2 +- lib/compress/zbuff_compress.c | 6 +- lib/compress/zstd_compress.c | 62 +++++++++++++------ lib/decompress/zstd_decompress.c | 103 +++++++++++++++++++++---------- programs/fuzzer.c | 4 +- zstd_compression_format.md | 94 +++++++++++++--------------- 6 files changed, 160 insertions(+), 111 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index a68a92cc5..f801959e7 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -88,7 +88,7 @@ static const size_t ZSTD_did_fieldSize[4] = { 0, 1, 2, 4 }; #define ZSTD_BLOCKHEADERSIZE 3 /* C standard doesn't allow `static const` variable to be init using another `static const` variable */ static const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE; -typedef enum { bt_raw, bt_rle, bt_compressed, bt_end } blockType_e; +typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e; #define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */ #define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */ diff --git a/lib/compress/zbuff_compress.c b/lib/compress/zbuff_compress.c index 9b842f64f..31c859092 100644 --- a/lib/compress/zbuff_compress.c +++ b/lib/compress/zbuff_compress.c @@ -95,6 +95,7 @@ struct ZBUFF_CCtx_s { size_t outBuffContentSize; size_t outBuffFlushedSize; ZBUFF_cStage stage; + U32 checksum; ZSTD_customMem customMem; }; /* typedef'd tp ZBUFF_CCtx within "zstd_buffered.h" */ @@ -164,6 +165,7 @@ size_t ZBUFF_compressInit_advanced(ZBUFF_CCtx* zbc, zbc->inBuffTarget = zbc->blockSize; zbc->outBuffContentSize = zbc->outBuffFlushedSize = 0; zbc->stage = ZBUFFcs_load; + zbc->checksum = params.fParams.checksumFlag > 0; return 0; /* ready to go */ } @@ -300,11 +302,11 @@ size_t ZBUFF_compressEnd(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr) op += outSize; if (remainingToFlush) { *dstCapacityPtr = op-ostart; - return remainingToFlush + ZBUFF_endFrameSize; + return remainingToFlush + ZBUFF_endFrameSize + (zbc->checksum * 4); } /* create epilogue */ zbc->stage = ZBUFFcs_final; - zbc->outBuffContentSize = ZSTD_compressEnd(zbc->zc, zbc->outBuff, zbc->outBuffSize); /* epilogue into outBuff */ + zbc->outBuffContentSize = ZSTD_compressEnd(zbc->zc, zbc->outBuff, zbc->outBuffSize); /* epilogue into outBuff */ } /* flush epilogue */ diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e7d8c2dad..dc4cb92fd 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2227,9 +2227,17 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCa } +/*! ZSTD_compress_generic() : +* Compress a chunk of data into one or multiple blocks. +* All blocks will be terminated, all input will be consumed. +* Function will issue an error if there is not enough `dstCapacity` to hold the compressed content. +* Frame is supposed already started (header already produced) +* @return : compressed size, or an error code +*/ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, - const void* src, size_t srcSize) + const void* src, size_t srcSize, + U32 lastFrameChunk) { size_t blockSize = cctx->blockSize; size_t remaining = srcSize; @@ -2244,6 +2252,7 @@ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, XXH64_update(&cctx->xxhState, src, srcSize); while (remaining) { + U32 const lastBlock = lastFrameChunk & (blockSize >= remaining); size_t cSize; ZSTD_statsResetFreqs(stats); /* debug only */ @@ -2261,12 +2270,15 @@ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, if (ZSTD_isError(cSize)) return cSize; if (cSize == 0) { /* block is not compressible */ - cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize); - if (ZSTD_isError(cSize)) return cSize; + U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(blockSize << 3); + if (blockSize + ZSTD_blockHeaderSize > dstCapacity) return ERROR(dstSize_tooSmall); + MEM_writeLE32(op, cBlockHeader24); /* no pb, 4th byte will be overwritten */ + memcpy(op + ZSTD_blockHeaderSize, ip, blockSize); + cSize = ZSTD_blockHeaderSize+blockSize; } else { - U32 const cBlockHeader24 = (U32)bt_compressed + (U32)(cSize << 2); + U32 const cBlockHeader24 = lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3); MEM_writeLE24(op, cBlockHeader24); - cSize += 3; + cSize += ZSTD_blockHeaderSize; } remaining -= blockSize; @@ -2275,6 +2287,7 @@ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, op += cSize; } + if (lastFrameChunk) cctx->stage = ZSTDcs_ending; ZSTD_statsPrint(stats, cctx->params.cParams.searchLength); /* debug only */ return op-ostart; } @@ -2322,7 +2335,7 @@ static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity, static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize, - U32 frame) + U32 frame, U32 lastFrameChunk) { const BYTE* const ip = (const BYTE*) src; size_t fhSize = 0; @@ -2372,7 +2385,7 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* zc, zc->nextSrc = ip + srcSize; { size_t const cSize = frame ? - ZSTD_compress_generic (zc, dst, dstCapacity, src, srcSize) : + ZSTD_compress_generic (zc, dst, dstCapacity, src, srcSize, lastFrameChunk) : ZSTD_compressBlock_internal (zc, dst, dstCapacity, src, srcSize); if (ZSTD_isError(cSize)) return cSize; return cSize + fhSize; @@ -2384,7 +2397,7 @@ size_t ZSTD_compressContinue (ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { - return ZSTD_compressContinue_internal(zc, dst, dstCapacity, src, srcSize, 1); + return ZSTD_compressContinue_internal(zc, dst, dstCapacity, src, srcSize, 1, 0); } @@ -2398,7 +2411,7 @@ size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const size_t const blockSizeMax = ZSTD_getBlockSizeMax(cctx); if (srcSize > blockSizeMax) return ERROR(srcSize_wrong); ZSTD_LOG_BLOCK("%p: ZSTD_compressBlock searchLength=%d\n", cctx->base, cctx->params.cParams.searchLength); - return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0); + return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0, 0); } @@ -2572,13 +2585,14 @@ size_t ZSTD_compressBegin(ZSTD_CCtx* zc, int compressionLevel) * @return : nb of bytes written into dst (or an error code) */ size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) { - BYTE* op = (BYTE*)dst; + BYTE* const ostart = (BYTE*)dst; + BYTE* op = ostart; size_t fhSize = 0; - if (cctx->stage==ZSTDcs_created) return ERROR(stage_wrong); /*< not even init ! */ + if (cctx->stage == ZSTDcs_created) return ERROR(stage_wrong); /*< not even init ! */ /* special case : empty frame */ - if (cctx->stage==ZSTDcs_init) { + if (cctx->stage == ZSTDcs_init) { fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->params, 0, 0); if (ZSTD_isError(fhSize)) return fhSize; dstCapacity -= fhSize; @@ -2586,16 +2600,24 @@ size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) cctx->stage = ZSTDcs_ongoing; } - /* frame epilogue */ - if (dstCapacity < ZSTD_blockHeaderSize) return ERROR(dstSize_tooSmall); - { U32 const checksum = cctx->params.fParams.checksumFlag ? - (U32)(XXH64_digest(&cctx->xxhState) >> 11) : - 0; - MEM_writeLE24(op, (U32)bt_end + (checksum << 2)); + if (cctx->stage != ZSTDcs_ending) { + /* write one last empty block, make it the "last" block */ + U32 const cBlockHeader24 = 1 /* last block */ + (((U32)bt_raw)<<1) + 0; + if (dstCapacity<4) return ERROR(dstSize_tooSmall); + MEM_writeLE32(op, cBlockHeader24); + op += ZSTD_blockHeaderSize; + dstCapacity -= ZSTD_blockHeaderSize; + } + + if (cctx->params.fParams.checksumFlag) { + U32 const checksum = (U32) XXH64_digest(&cctx->xxhState); + if (dstCapacity<4) return ERROR(dstSize_tooSmall); + MEM_writeLE32(op, checksum); + op += 4; } cctx->stage = ZSTDcs_created; /* return to "created but no init" status */ - return ZSTD_blockHeaderSize+fhSize; + return op-ostart; } @@ -2635,7 +2657,7 @@ static size_t ZSTD_compress_internal (ZSTD_CCtx* ctx, if(ZSTD_isError(errorCode)) return errorCode; } /* body (compression) */ - { size_t const oSize = ZSTD_compressContinue (ctx, op, dstCapacity, src, srcSize); + { size_t const oSize = ZSTD_compressContinue_internal(ctx, op, dstCapacity, src, srcSize, 1, 1); if(ZSTD_isError(oSize)) return oSize; op += oSize; dstCapacity -= oSize; } diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 2940dd68a..60f7568d1 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -105,6 +105,7 @@ static void ZSTD_copy4(void* dst, const void* src) { memcpy(dst, src, 4); } ***************************************************************/ typedef enum { ZSTDds_getFrameHeaderSize, ZSTDds_decodeFrameHeader, ZSTDds_decodeBlockHeader, ZSTDds_decompressBlock, + ZSTDds_decompressLastBlock, ZSTDds_checkChecksum, ZSTDds_decodeSkippableHeader, ZSTDds_skipFrame } ZSTD_dStage; struct ZSTD_DCtx_s @@ -131,6 +132,7 @@ struct ZSTD_DCtx_s ZSTD_customMem customMem; size_t litBufSize; size_t litSize; + size_t rleSize; BYTE litBuffer[ZSTD_BLOCKSIZE_ABSOLUTEMAX + WILDCOPY_OVERLENGTH]; BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; }; /* typedef'd to ZSTD_DCtx within "zstd_static.h" */ @@ -318,6 +320,7 @@ static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t sr typedef struct { blockType_e blockType; + U32 lastBlock; U32 origSize; } blockProperties_t; @@ -327,11 +330,12 @@ size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* bp { if (srcSize < ZSTD_blockHeaderSize) return ERROR(srcSize_wrong); { U32 const cBlockHeader = MEM_readLE24(src); - U32 const cSize = cBlockHeader >> 2; - bpPtr->blockType = (blockType_e)(cBlockHeader & 3); + U32 const cSize = cBlockHeader >> 3; + bpPtr->lastBlock = cBlockHeader & 1; + bpPtr->blockType = (blockType_e)((cBlockHeader >> 1) & 3); bpPtr->origSize = cSize; /* only useful for RLE */ - if (bpPtr->blockType == bt_end) return 0; if (bpPtr->blockType == bt_rle) return 1; + if (bpPtr->blockType == bt_reserved) return ERROR(corruption_detected); return cSize; } } @@ -345,6 +349,14 @@ static size_t ZSTD_copyRawBlock(void* dst, size_t dstCapacity, const void* src, } +static size_t ZSTD_setRleBlock(void* dst, size_t dstCapacity, const void* src, size_t srcSize, size_t regenSize) +{ + if (srcSize != 1) return ERROR(srcSize_wrong); + if (regenSize > dstCapacity) return ERROR(dstSize_tooSmall); + memset(dst, *(const BYTE*)src, regenSize); + return regenSize; +} + /*! ZSTD_decodeLiteralsBlock() : @return : nb of bytes read from src (< srcSize ) */ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, @@ -889,29 +901,29 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, case bt_rle : decodedSize = ZSTD_generateNxBytes(op, oend-op, *ip, blockProperties.origSize); break; - case bt_end : - /* end of frame */ - if (remainingSize) return ERROR(srcSize_wrong); - if (dctx->fParams.checksumFlag) { - U64 const h64 = XXH64_digest(&dctx->xxhState); - U32 const h32 = (U32)(h64>>11) & ((1<<22)-1); - U32 const check32 = MEM_readLE24(src) >> 2; - if (check32 != h32) return ERROR(checksum_wrong); - } - decodedSize = 0; - break; + case bt_reserved : default: - return ERROR(GENERIC); /* impossible */ + return ERROR(corruption_detected); } - if (blockProperties.blockType == bt_end) break; /* bt_end */ if (ZSTD_isError(decodedSize)) return decodedSize; if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, op, decodedSize); op += decodedSize; ip += cBlockSize; remainingSize -= cBlockSize; + if (blockProperties.lastBlock) break; } + if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */ + U32 const checkCalc = (U32)XXH64_digest(&dctx->xxhState); + U32 checkRead; + if (remainingSize<4) return ERROR(checksum_wrong); + checkRead = MEM_readLE32(ip); + if (checkRead != checkCalc) return ERROR(checksum_wrong); + remainingSize -= 4; + } + + if (remainingSize) return ERROR(srcSize_wrong); return op-ostart; } @@ -1022,22 +1034,29 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c { blockProperties_t bp; size_t const cBlockSize = ZSTD_getcBlockSize(src, ZSTD_blockHeaderSize, &bp); if (ZSTD_isError(cBlockSize)) return cBlockSize; - if (bp.blockType == bt_end) { + dctx->expected = cBlockSize; + dctx->bType = bp.blockType; + dctx->rleSize = bp.origSize; + if (cBlockSize) { + dctx->stage = bp.lastBlock ? ZSTDds_decompressLastBlock : ZSTDds_decompressBlock; + return 0; + } + /* empty block */ + if (bp.lastBlock) { if (dctx->fParams.checksumFlag) { - U64 const h64 = XXH64_digest(&dctx->xxhState); - U32 const h32 = (U32)(h64>>11) & ((1<<22)-1); - U32 const check32 = MEM_readLE24(src) >> 2; - if (check32 != h32) return ERROR(checksum_wrong); + dctx->expected = 4; + dctx->stage = ZSTDds_checkChecksum; + } else { + dctx->expected = 0; /* end of frame */ + dctx->stage = ZSTDds_getFrameHeaderSize; } - dctx->expected = 0; - dctx->stage = ZSTDds_getFrameHeaderSize; } else { - dctx->expected = cBlockSize; - dctx->bType = bp.blockType; - dctx->stage = ZSTDds_decompressBlock; + dctx->expected = 3; /* go directly to next header */ + dctx->stage = ZSTDds_decodeBlockHeader; } return 0; } + case ZSTDds_decompressLastBlock: case ZSTDds_decompressBlock: { size_t rSize; switch(dctx->bType) @@ -1049,21 +1068,37 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c rSize = ZSTD_copyRawBlock(dst, dstCapacity, src, srcSize); break; case bt_rle : - return ERROR(GENERIC); /* not yet handled */ - break; - case bt_end : /* should never happen (filtered at phase 1) */ - rSize = 0; + rSize = ZSTD_setRleBlock(dst, dstCapacity, src, srcSize, dctx->rleSize); break; + case bt_reserved : /* should never happen */ default: - return ERROR(GENERIC); /* impossible */ + return ERROR(corruption_detected); } - dctx->stage = ZSTDds_decodeBlockHeader; - dctx->expected = ZSTD_blockHeaderSize; - dctx->previousDstEnd = (char*)dst + rSize; if (ZSTD_isError(rSize)) return rSize; if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, dst, rSize); + + if (dctx->stage == ZSTDds_decompressLastBlock) { /* end of frame */ + if (dctx->fParams.checksumFlag) { /* another round for frame checksum */ + dctx->expected = 0; + dctx->stage = ZSTDds_checkChecksum; + } + dctx->expected = 0; /* ends here */ + dctx->stage = ZSTDds_getFrameHeaderSize; + } else { + dctx->stage = ZSTDds_decodeBlockHeader; + dctx->expected = ZSTD_blockHeaderSize; + dctx->previousDstEnd = (char*)dst + rSize; + } return rSize; } + case ZSTDds_checkChecksum: + { U32 const h32 = (U32)XXH64_digest(&dctx->xxhState); + U32 const check32 = MEM_readLE32(src); /* srcSize == 4, guaranteed by dctx->expected */ + if (check32 != h32) return ERROR(checksum_wrong); + dctx->expected = 0; + dctx->stage = ZSTDds_getFrameHeaderSize; + return 0; + } case ZSTDds_decodeSkippableHeader: { memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_min, src, dctx->expected); dctx->expected = MEM_readLE32(dctx->headerBuffer + 4); diff --git a/programs/fuzzer.c b/programs/fuzzer.c index 3778f12b2..33cfbcb2d 100644 --- a/programs/fuzzer.c +++ b/programs/fuzzer.c @@ -145,8 +145,8 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "OK \n"); DISPLAYLEVEL(4, "test%3i : decompress %u bytes : ", testNb++, (U32)CNBuffSize); - CHECKPLUS( r , ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize), - if (r != CNBuffSize) goto _output_error); + { size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize); + if (r != CNBuffSize) goto _output_error; } DISPLAYLEVEL(4, "OK \n"); DISPLAYLEVEL(4, "test%3i : check decompressed result : ", testNb++); diff --git a/zstd_compression_format.md b/zstd_compression_format.md index efbf13cd4..79f9620e7 100644 --- a/zstd_compression_format.md +++ b/zstd_compression_format.md @@ -100,9 +100,9 @@ General Structure of Zstandard Frame format ------------------------------------------- The structure of a single Zstandard frame is following: -| `Magic_Number` | `Frame_Header` |`Data_Block`| [More data blocks] |`End_Marker`| -|:--------------:|:--------------:|:----------:| ------------------ |:----------:| -| 4 bytes | 2-14 bytes | n bytes | | 3 bytes | +| `Magic_Number` | `Frame_Header` |`Data_Block`| [More data blocks] | [`Content_Checksum`] | +|:--------------:|:--------------:|:----------:| ------------------ |:--------------------:| +| 4 bytes | 2-14 bytes | n bytes | | 0-4 bytes | __`Magic_Number`__ @@ -118,27 +118,13 @@ __`Data_Block`__ Detailed in [next chapter](#the-structure-of-data_block). That’s where compressed data is stored. -__`End_Marker`__ +__`Content_Checksum`__ -The flow of blocks ends when the last block header brings an _end signal_. -This last block header may optionally host a `Content_Checksum`. - -##### __`Content_Checksum`__ - -`Content_Checksum` allow to verify that frame content has been regenerated correctly. +An optional 32-bit checksum, only present if `Content_Checksum_flag` is set. The content checksum is the result of [xxh64() hash function](https://www.xxHash.com) digesting the original (decoded) data as input, and a seed of zero. -Bits from 11 to 32 (included) are extracted to form a 22 bits checksum -stored within `End_Marker`. -``` -mask22bits = (1<<22)-1; -contentChecksum = (XXH64(content, size, 0) >> 11) & mask22bits; -``` -`Content_Checksum` is only present when its associated flag -is set in the frame descriptor. -Its usage is optional. - +The low 4 bytes of the checksum are stored in little endian format. The structure of `Frame_Header` @@ -172,23 +158,25 @@ __`Frame_Content_Size_flag`__ This is a 2-bits flag (`= Frame_Header_Descriptor >> 6`), specifying if decompressed data size is provided within the header. -The `Value` can be converted to `Field_Size` that is number of bytes used by `Frame_Content_Size` according to the following table: +The `Flag_Value` can be converted into `Field_Size`, +which is the number of bytes used by `Frame_Content_Size` +according to the following table: -| `Value` | 0 | 1 | 2 | 3 | +|`Flag_Value`| 0 | 1 | 2 | 3 | | ---------- | --- | --- | --- | --- | |`Field_Size`| 0-1 | 2 | 4 | 8 | -The meaning of `Value` equal to `0` depends on `Single_Segment_flag` : -it either means `0` (size not provided) _if_ the `Window_Descriptor` byte is present, -or `1` (frame content size <= 255 bytes) otherwise. +When `Flag_Value` is `0`, `Field_Size` depends on `Single_Segment_flag` : +if `Single_Segment_flag` is set, `Field_Size` is 1. +Otherwise, `Field_Size` is 0 (content size not provided). __`Single_Segment_flag`__ If this flag is set, -data shall be regenerated within a single continuous memory segment. +data must be regenerated within a single continuous memory segment. -In this case, `Window_Descriptor` byte __is not present__, -but `Frame_Content_Size_flag` field necessarily is. +In this case, `Frame_Content_Size` is necessarily present, +but `Window_Descriptor` byte is skipped. As a consequence, the decoder must allocate a memory segment of size equal or bigger than `Frame_Content_Size`. @@ -205,7 +193,7 @@ depending on local limitations. __`Unused_bit`__ The value of this bit should be set to zero. -A decoder compliant with this specification version should not interpret it. +A decoder compliant with this specification version shall not interpret it. It might be used in a future version, to signal a property which is not mandatory to properly decode the frame. @@ -215,13 +203,12 @@ This bit is reserved for some future feature. Its value _must be zero_. A decoder compliant with this specification version must ensure it is not set. This bit may be used in a future revision, -to signal a feature that must be interpreted in order to decode the frame. +to signal a feature that must be interpreted to decode the frame correctly. __`Content_Checksum_flag`__ -If this flag is set, a content checksum will be present within `End_Marker`. -The checksum is a 22 bits value extracted from the XXH64() of data, -and stored within `End_Marker`. See [`Content_Checksum`](#content_checksum) . +If this flag is set, a 32-bits `Content_Checksum` will be present at frame's end. +See `Content_Checksum` paragraph. __`Dictionary_ID_flag`__ @@ -236,10 +223,10 @@ It also specifies the size of this field. ### `Window_Descriptor` Provides guarantees on maximum back-reference distance -that will be present within compressed data. -This information is useful for decoders to allocate enough memory. +that will be used within compressed data. +This information is important for decoders to allocate enough memory. -The `Window_Descriptor` byte is optional. It should be absent if `Single_Segment_flag` is set. +The `Window_Descriptor` byte is optional. It is absent when `Single_Segment_flag` is set. 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). @@ -265,8 +252,8 @@ a decoder can refuse a compressed frame which requests a memory size beyond decoder's authorized range. For improved interoperability, -decoders are recommended to be compatible with window sizes of 8 MB. -Encoders are recommended to not request more than 8 MB. +decoders are recommended to be compatible with window sizes of 8 MB, +and encoders are recommended to not request more than 8 MB. It's merely a recommendation though, decoders are free to support larger or lower limits, depending on local limitations. @@ -313,30 +300,34 @@ When `Field_Size` is 1, 4 or 8 bytes, the value is read directly. When `Field_Size` is 2, _the offset of 256 is added_. It's allowed to represent a small size (for example `18`) using any compatible variant. -In order to preserve decoder from unreasonable memory requirement, -a decoder can refuse a compressed frame -which requests a memory size beyond decoder's authorized range. - The structure of `Data_Block` ----------------------------- The structure of `Data_Block` is following: -| `Block_Type` | `Block_Size` | `Block_Content` | -|:------------:|:------------:|:---------------:| -| 2 bits | 22 bits | n bytes | +| `Last_Block` | `Block_Type` | `Block_Size` | `Block_Content` | +|:------------:|:------------:|:------------:|:---------------:| +| 1 bit | 2 bits | 21 bits | n bytes | + +The block header uses 3-bytes. + +__`Last_Block`__ + +The lowest bit signals if this block is the last one. +Frame ends right after this block. +It may be followed by an optional `Content_Checksum` . __`Block_Type` and `Block_Size`__ -The block header uses 3-bytes, format is __little-endian__. -The 2 highest bits represent the `Block_Type`, -while the remaining 22 bits represent the (compressed) `Block_Size`. +The next 2 bits represent the `Block_Type`, +while the remaining 21 bits represent the `Block_Size`. +Format is __little-endian__. There are 4 block types : | Value | 0 | 1 | 2 | 3 | | ------------ | ----------- | ----------- | ------------------ | --------- | -| `Block_Type` | `Raw_Block` | `RLE_Block` | `Compressed_Block` | `EndMark` | +| `Block_Type` | `Raw_Block` | `RLE_Block` | `Compressed_Block` | `Reserved`| - `Raw_Block` - this is an uncompressed block. `Block_Size` is the number of bytes to read and copy. @@ -348,9 +339,8 @@ There are 4 block types : `Block_Size` is the compressed size. Decompressed size is unknown, but its maximum possible value is guaranteed (see below) -- `EndMark` - this is not a block. It signals the end of the frame. - The rest of the field may be optionally filled by a checksum - (see [`Content_Checksum`](#content_checksum)). +- `Reserved` - this is not a block. + 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`. From 5b56739b639cc3ebe07cbc1e65f7b2602ed167c9 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 28 Jul 2016 01:17:22 +0200 Subject: [PATCH 20/46] created ZSTD_compressContinueThenEnd() --- lib/compress/zstd_compress.c | 17 +++++++++++++++-- lib/zstd.h | 9 +++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index dc4cb92fd..0c24e4b12 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2393,11 +2393,24 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* zc, } -size_t ZSTD_compressContinue (ZSTD_CCtx* zc, +size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { - return ZSTD_compressContinue_internal(zc, dst, dstCapacity, src, srcSize, 1, 0); + return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1, 0); +} + + +size_t ZSTD_compressContinueThenEnd (ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) +{ + size_t endResult; + size_t const cSize = ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1, 1); + if (ZSTD_isError(cSize)) return cSize; + endResult = ZSTD_compressEnd(cctx, (char*)dst + cSize, dstCapacity-cSize); + if (ZSTD_isError(endResult)) return endResult; + return cSize + endResult; } diff --git a/lib/zstd.h b/lib/zstd.h index e819eda62..aa3d9d635 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -319,6 +319,7 @@ ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx) 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); +ZSTDLIB_API size_t ZSTD_compressContinueThenEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); /* A ZSTD_CCtx object is required to track streaming operations. @@ -342,9 +343,9 @@ ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapaci - ZSTD_compressContinue() detects that prior input has been overwritten when `src` buffer overlaps. In which case, it will "discard" the relevant memory section from its history. - - Finish a frame with ZSTD_compressEnd(), which will write the epilogue. - Without epilogue, frames will be considered unfinished (broken) by decoders. + Finish a frame with ZSTD_compressEnd(), which will write the epilogue, + or ZSTD_compressContinueThenEnd(), which will write the last block. + Without last block / epilogue mark, frames will be considered unfinished (broken) by decoders. You can then reuse `ZSTD_CCtx` (ZSTD_compressBegin()) to compress some new frame. */ @@ -407,7 +408,7 @@ ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t ds == Special case : skippable frames == Skippable frames allow the integration of user-defined data into a flow of concatenated frames. - Skippable frames will be ignored (skipped) by a decompressor. The format of skippable frame is following: + Skippable frames will be ignored (skipped) by a decompressor. The format of skippable frames is as follows : a) Skippable frame ID - 4 Bytes, Little endian format, any value from 0x184D2A50 to 0x184D2A5F b) Frame Size - 4 Bytes, Little endian format, unsigned 32-bits c) Frame Content - any content (User Data) of length equal to Frame Size From 19c1002e46c50c2f02b678f374ec6e40b7e64cde Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 28 Jul 2016 01:25:46 +0200 Subject: [PATCH 21/46] applied ZSTD_compressContinueThenEnd() --- lib/compress/zstd_compress.c | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 0c24e4b12..1605e8bf9 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2646,17 +2646,13 @@ static size_t ZSTD_compress_usingPreparedCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* { size_t const errorCode = ZSTD_copyCCtx(cctx, preparedCCtx); if (ZSTD_isError(errorCode)) return errorCode; } - { size_t const cSize = ZSTD_compressContinue(cctx, dst, dstCapacity, src, srcSize); - if (ZSTD_isError(cSize)) return cSize; - - { size_t const endSize = ZSTD_compressEnd(cctx, (char*)dst+cSize, dstCapacity-cSize); - if (ZSTD_isError(endSize)) return endSize; - return cSize + endSize; - } } + { size_t const cSize = ZSTD_compressContinueThenEnd(cctx, dst, dstCapacity, src, srcSize); + return cSize; + } } -static size_t ZSTD_compress_internal (ZSTD_CCtx* ctx, +static size_t ZSTD_compress_internal (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict,size_t dictSize, @@ -2666,21 +2662,12 @@ static size_t ZSTD_compress_internal (ZSTD_CCtx* ctx, BYTE* op = ostart; /* Init */ - { size_t const errorCode = ZSTD_compressBegin_internal(ctx, dict, dictSize, params, srcSize); + { size_t const errorCode = ZSTD_compressBegin_internal(cctx, dict, dictSize, params, srcSize); if(ZSTD_isError(errorCode)) return errorCode; } /* body (compression) */ - { size_t const oSize = ZSTD_compressContinue_internal(ctx, op, dstCapacity, src, srcSize, 1, 1); - if(ZSTD_isError(oSize)) return oSize; - op += oSize; - dstCapacity -= oSize; } - - /* Close frame */ - { size_t const oSize = ZSTD_compressEnd(ctx, op, dstCapacity); - if(ZSTD_isError(oSize)) return oSize; - op += oSize; } - - return (op - ostart); + { size_t const oSize = ZSTD_compressContinueThenEnd(cctx, op, dstCapacity, src, srcSize); + return oSize; } } size_t ZSTD_compress_advanced (ZSTD_CCtx* ctx, From d469a98c0106fe06fac6fa822fc0a8e3abd03f78 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 28 Jul 2016 03:47:45 +0200 Subject: [PATCH 22/46] =?UTF-8?q?Clarified=20API=20comments,=20from=20sugg?= =?UTF-8?q?estions=20by=20=E2=80=8EBryan=20O'Sullivan=E2=80=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/dictBuilder/zdict.h | 68 ++++++++++++++++++++++------------------- lib/zstd.h | 50 ++++++++++++++++-------------- 2 files changed, 63 insertions(+), 55 deletions(-) diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index 599e8fb59..d61b5922c 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -38,43 +38,28 @@ extern "C" { #endif -/*-************************************* -* Public functions -***************************************/ /*! ZDICT_trainFromBuffer() : - Train a dictionary from a memory buffer `samplesBuffer`, - where `nbSamples` samples have been stored concatenated. - Each sample size is provided into an orderly table `samplesSizes`. - Resulting dictionary will be saved into `dictBuffer`. + Train a dictionary from an array of samples. + 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 by ZDICT_isError(). + 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. */ size_t ZDICT_trainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, - const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); - -/*! ZDICT_addEntropyTablesFromBuffer() : - - Given a content-only dictionary (built for example from common strings in - the input), add entropy tables computed from the memory buffer - `samplesBuffer`, where `nbSamples` samples have been stored concatenated. - Each sample size is provided into an orderly table `samplesSizes`. - - The input dictionary is the last `dictContentSize` bytes of `dictBuffer`. The - resulting dictionary with added entropy tables will written back to - `dictBuffer`. - @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`). -*/ -size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity, - const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); -/*-************************************* -* Helper functions -***************************************/ +/*====== Helper functions ======*/ unsigned ZDICT_isError(size_t errorCode); const char* ZDICT_getErrorName(size_t errorCode); + #ifdef ZDICT_STATIC_LINKING_ONLY /* ==================================================================================== @@ -85,7 +70,7 @@ const char* ZDICT_getErrorName(size_t errorCode); * ==================================================================================== */ typedef struct { - unsigned selectivityLevel; /* 0 means default; larger => bigger selection => larger dictionary */ + unsigned selectivityLevel; /* 0 means default; larger => select more => larger dictionary */ int compressionLevel; /* 0 means default; target a specific zstd compression level */ 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 */ @@ -96,13 +81,32 @@ typedef struct { /*! ZDICT_trainFromBuffer_advanced() : Same as ZDICT_trainFromBuffer() with control over more parameters. `parameters` is optional and can be provided with values set to 0 to mean "default". - @return : size of dictionary stored into `dictBuffer` (<= `dictBufferSize`) + @return : size of dictionary stored into `dictBuffer` (<= `dictBufferSize`), or an error code, which can be tested by ZDICT_isError(). - note : ZDICT_trainFromBuffer_advanced() will send notifications into stderr if instructed to, using ZDICT_setNotificationLevel() + 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, - const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, - ZDICT_params_t parameters); + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, + ZDICT_params_t parameters); + + +/*! ZDICT_addEntropyTablesFromBuffer() : + + 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`). +*/ +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 */ diff --git a/lib/zstd.h b/lib/zstd.h index aa3d9d635..9356f56d2 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -80,13 +80,13 @@ ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity, /*! ZSTD_getDecompressedSize() : * @return : decompressed size if known, 0 otherwise. - note 1 : if `0`, follow up with ZSTD_getFrameParams() to know precise failure cause. - note 2 : decompressed size could be wrong or intentionally modified ! - always ensure results fit within application's authorized limits */ + note 1 : decompressed size could be wrong or intentionally modified ! + always ensure results fit within application's authorized limits ! + note 2 : when `0`, if precise failure cause is needed, use ZSTD_getFrameParams() to know more. */ unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); /*! ZSTD_decompress() : - `compressedSize` : must be _exact_ size of compressed input, otherwise decompression will fail. + `compressedSize` : must be the _exact_ size of compressed input, otherwise decompression will fail. `dstCapacity` must be equal or larger than originalSize. @return : the number of bytes decompressed into `dst` (<= `dstCapacity`), or an errorCode if it fails (which can be tested using ZSTD_isError()) */ @@ -107,16 +107,16 @@ ZSTDLIB_API const char* ZSTD_getErrorName(size_t code); /*!< provides readab /** Compression context */ typedef struct ZSTD_CCtx_s ZSTD_CCtx; /*< incomplete type */ ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx(void); -ZSTDLIB_API size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx); /*!< @return : errorCode */ +ZSTDLIB_API size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx); /** ZSTD_compressCCtx() : Same as ZSTD_compress(), requires an allocated ZSTD_CCtx (see ZSTD_createCCtx()) */ ZSTDLIB_API size_t ZSTD_compressCCtx(ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel); /** Decompression context */ -typedef struct ZSTD_DCtx_s ZSTD_DCtx; +typedef struct ZSTD_DCtx_s ZSTD_DCtx; /*< incomplete type */ ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx(void); -ZSTDLIB_API size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx); /*!< @return : errorCode */ +ZSTDLIB_API size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx); /** ZSTD_decompressDCtx() : * Same as ZSTD_decompress(), requires an allocated ZSTD_DCtx (see ZSTD_createDCtx()) */ @@ -127,7 +127,7 @@ ZSTDLIB_API size_t ZSTD_decompressDCtx(ZSTD_DCtx* ctx, void* dst, size_t dstCapa * Simple dictionary API ***************************/ /*! ZSTD_compress_usingDict() : -* Compression using a pre-defined Dictionary content (see dictBuilder). +* Compression using a predefined Dictionary (see dictBuilder/zdict.h). * Note : This function load the dictionary, resulting in a significant startup time. */ ZSTDLIB_API size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, @@ -136,7 +136,7 @@ ZSTDLIB_API size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx, int compressionLevel); /*! ZSTD_decompress_usingDict() : -* Decompression using a pre-defined Dictionary content (see dictBuilder). +* Decompression using a predefined Dictionary (see dictBuilder/zdict.h). * Dictionary must be identical to the one used during compression. * Note : This function load the dictionary, resulting in a significant startup time */ ZSTDLIB_API size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx, @@ -146,7 +146,7 @@ ZSTDLIB_API size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx, /*-************************** -* Advanced Dictionary API +* Fast Dictionary API ****************************/ /*! ZSTD_createCDict() : * Create a digested dictionary, ready to start compression operation without startup delay. @@ -156,7 +156,7 @@ ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int ZSTDLIB_API size_t ZSTD_freeCDict(ZSTD_CDict* CDict); /*! ZSTD_compress_usingCDict() : -* Compression using a pre-digested Dictionary. +* 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, @@ -172,7 +172,7 @@ ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize); ZSTDLIB_API size_t ZSTD_freeDDict(ZSTD_DDict* ddict); /*! ZSTD_decompress_usingDDict() : -* Decompression using a pre-digested Dictionary +* Decompression using a digested Dictionary * Faster startup than ZSTD_decompress_usingDict(), recommended when same dictionary is used multiple times. */ ZSTDLIB_API size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, @@ -312,6 +312,10 @@ ZSTDLIB_API size_t ZSTD_sizeofDCtx(const ZSTD_DCtx* dctx); /* ****************************************************************** * Buffer-less streaming functions (synchronous mode) ********************************************************************/ +/* This is an advanced API, giving full control over buffer management, for users which need direct control over memory. +* But it's also a complex one, with a lot of restrictions (documented below). +* For an easier streaming API, look into common/zbuff.h +* which removes all restrictions by implementing and managing its own internal buffer */ 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); @@ -373,16 +377,16 @@ ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t ds Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it. A ZSTD_DCtx object can be re-used multiple times. - First optional operation is to retrieve frame parameters, using ZSTD_getFrameParams(), which doesn't consume the input. - It can provide the minimum size of rolling buffer required to properly decompress data (`windowSize`), + First optional operation is to retrieve frame parameters, using ZSTD_getFrameParams(). + ZSTD_getFrameParams() fills a ZSTD_frameParams structure, + which can provide the minimum size of rolling buffer required to decompress data (`windowSize`), and optionally the final size of uncompressed content. - (Note : content size is an optional info that may not be present. 0 means : content size unknown) - Frame parameters are extracted from the beginning of compressed frame. - The amount of data to read is variable, from ZSTD_frameHeaderSize_min to ZSTD_frameHeaderSize_max (so if `srcSize` >= ZSTD_frameHeaderSize_max, it will always work) - If `srcSize` is too small for operation to succeed, function will return the minimum size it requires to produce a result. - Result : 0 when successful, it means the ZSTD_frameParams structure has been filled. - >0 : means there is not enough data into `src`. Provides the expected size to successfully decode header. - errorCode, which can be tested using ZSTD_isError() + (Note : content size is an optional info that may not be present. 0 means : content size unknown). + These information are extracted from the beginning of the compressed frame. + Size of data fragment must be large enough to ensure successful decoding, typically ZSTD_frameHeaderSize_max bytes. + @result : 0 : successful decoding, it means the ZSTD_frameParams structure is correctly filled. + >0 : `srcSize` is too small, please provide at least @result bytes on next try. + errorCode, which can be tested using ZSTD_isError(). Start decompression, with ZSTD_decompressBegin() or ZSTD_decompressBegin_usingDict(). Alternatively, you can copy a prepared context, using ZSTD_copyDCtx(). @@ -399,7 +403,7 @@ ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t ds Alternatively, a round buffer of sufficient size is also possible. Sufficient size is determined by frame parameters. ZSTD_decompressContinue() is very sensitive to contiguity, if 2 blocks don't follow each other, make sure that either the compressor breaks contiguity at the same place, - or that previous contiguous segment is large enough to properly handle maximum back-reference. + or that previous contiguous segment is large enough to properly handle maximum back-reference. A frame is fully decoded when ZSTD_nextSrcSizeToDecompress() returns zero. Context can then be reset to start a new decompression. @@ -407,7 +411,7 @@ ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t ds == Special case : skippable frames == - Skippable frames allow the integration of user-defined data into a flow of concatenated frames. + Skippable frames allow integration of user-defined data into a flow of concatenated frames. Skippable frames will be ignored (skipped) by a decompressor. The format of skippable frames is as follows : a) Skippable frame ID - 4 Bytes, Little endian format, any value from 0x184D2A50 to 0x184D2A5F b) Frame Size - 4 Bytes, Little endian format, unsigned 32-bits From e7bf9156d1218533459133a4e685560bea3363fc Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 28 Jul 2016 05:00:57 +0200 Subject: [PATCH 23/46] =?UTF-8?q?Clarified=20API=20comments,=20from=20sugg?= =?UTF-8?q?estions=20by=20=E2=80=8EBryan=20O'Sullivan=E2=80=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/zbuff.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/common/zbuff.h b/lib/common/zbuff.h index 7820db26d..b31c3e631 100644 --- a/lib/common/zbuff.h +++ b/lib/common/zbuff.h @@ -56,6 +56,10 @@ extern "C" { /* ************************************* * Streaming functions ***************************************/ +/* This is the easier "buffered" streaming API, +* using an internal buffer to lift all restrictions on user-provided buffers +* which can be any size, any place, for both input and output. */ + typedef struct ZBUFF_CCtx_s ZBUFF_CCtx; ZSTDLIB_API ZBUFF_CCtx* ZBUFF_createCCtx(void); ZSTDLIB_API size_t ZBUFF_freeCCtx(ZBUFF_CCtx* cctx); @@ -168,11 +172,11 @@ ZSTDLIB_API size_t ZBUFF_recommendedDOutSize(void); * ==================================================================================== */ /*--- Dependency ---*/ -#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters */ +#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters, ZSTD_customMem */ #include "zstd.h" -/*--- External memory ---*/ +/*--- Custom memory allocator ---*/ /*! ZBUFF_createCCtx_advanced() : * Create a ZBUFF compression context using external alloc and free functions */ ZSTDLIB_API ZBUFF_CCtx* ZBUFF_createCCtx_advanced(ZSTD_customMem customMem); @@ -182,7 +186,7 @@ ZSTDLIB_API ZBUFF_CCtx* ZBUFF_createCCtx_advanced(ZSTD_customMem customMem); ZSTDLIB_API ZBUFF_DCtx* ZBUFF_createDCtx_advanced(ZSTD_customMem customMem); -/*--- Advanced Streaming function ---*/ +/*--- Advanced Streaming Initialization ---*/ ZSTDLIB_API size_t ZBUFF_compressInit_advanced(ZBUFF_CCtx* zbc, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); From 62470b4bab17da9b2d498fe9d943e07f759bf5f8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 28 Jul 2016 15:29:08 +0200 Subject: [PATCH 24/46] Changed ZSTD_compressEnd() --- NEWS | 2 +- lib/compress/zbuff_compress.c | 2 +- lib/compress/zstd_compress.c | 56 +++++++++++++++-------------------- lib/zstd.h | 13 ++++---- programs/fullbench.c | 5 +--- programs/fuzzer.c | 10 ++----- 6 files changed, 36 insertions(+), 52 deletions(-) diff --git a/NEWS b/NEWS index 1a329d85c..7221e461c 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ v0.8.0 -New : updated compresson format Improved : better speed on clang and gcc -O2, thanks to Eric Biggers +Changed : modified API : ZSTD_compressEnd() Fixed : legacy mode with ZSTD_HEAPMODE=0, by Christopher Bergqvist Fixed : premature end of frame when zero-sized raw block, reported by Eric Biggers Fixed : statistics for large dictionaries (> 128 KB), reported by Ilona Papava diff --git a/lib/compress/zbuff_compress.c b/lib/compress/zbuff_compress.c index 31c859092..29fd4dbb1 100644 --- a/lib/compress/zbuff_compress.c +++ b/lib/compress/zbuff_compress.c @@ -306,7 +306,7 @@ size_t ZBUFF_compressEnd(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr) } /* create epilogue */ zbc->stage = ZBUFFcs_final; - zbc->outBuffContentSize = ZSTD_compressEnd(zbc->zc, zbc->outBuff, zbc->outBuffSize); /* epilogue into outBuff */ + zbc->outBuffContentSize = ZSTD_compressEnd(zbc->zc, zbc->outBuff, zbc->outBuffSize, NULL, 0); /* epilogue into outBuff */ } /* flush epilogue */ diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 1605e8bf9..b69dffa25 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2287,7 +2287,7 @@ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, op += cSize; } - if (lastFrameChunk) cctx->stage = ZSTDcs_ending; + if (lastFrameChunk && (op>ostart)) cctx->stage = ZSTDcs_ending; ZSTD_statsPrint(stats, cctx->params.cParams.searchLength); /* debug only */ return op-ostart; } @@ -2401,19 +2401,6 @@ size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, } -size_t ZSTD_compressContinueThenEnd (ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize) -{ - size_t endResult; - size_t const cSize = ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1, 1); - if (ZSTD_isError(cSize)) return cSize; - endResult = ZSTD_compressEnd(cctx, (char*)dst + cSize, dstCapacity-cSize); - if (ZSTD_isError(endResult)) return endResult; - return cSize + endResult; -} - - size_t ZSTD_getBlockSizeMax(ZSTD_CCtx* cctx) { return MIN (ZSTD_BLOCKSIZE_ABSOLUTEMAX, 1 << cctx->params.cParams.windowLog); @@ -2593,10 +2580,10 @@ size_t ZSTD_compressBegin(ZSTD_CCtx* zc, int compressionLevel) } -/*! ZSTD_compressEnd() : -* Write frame epilogue. +/*! ZSTD_writeEpilogue() : +* Ends a frame. * @return : nb of bytes written into dst (or an error code) */ -size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) +static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) { BYTE* const ostart = (BYTE*)dst; BYTE* op = ostart; @@ -2634,6 +2621,19 @@ size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity) } +size_t ZSTD_compressEnd (ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) +{ + size_t endResult; + size_t const cSize = ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1, 1); + if (ZSTD_isError(cSize)) return cSize; + endResult = ZSTD_writeEpilogue(cctx, (char*)dst + cSize, dstCapacity-cSize); + if (ZSTD_isError(endResult)) return endResult; + return cSize + endResult; +} + + /*! 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. @@ -2643,12 +2643,10 @@ static size_t ZSTD_compress_usingPreparedCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* 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; - } - { size_t const cSize = ZSTD_compressContinueThenEnd(cctx, dst, dstCapacity, src, srcSize); - return cSize; - } + size_t const errorCode = ZSTD_copyCCtx(cctx, preparedCCtx); + if (ZSTD_isError(errorCode)) return errorCode; + + return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); } @@ -2658,16 +2656,10 @@ static size_t ZSTD_compress_internal (ZSTD_CCtx* cctx, const void* dict,size_t dictSize, ZSTD_parameters params) { - BYTE* const ostart = (BYTE*)dst; - BYTE* op = ostart; + size_t const errorCode = ZSTD_compressBegin_internal(cctx, dict, dictSize, params, srcSize); + if(ZSTD_isError(errorCode)) return errorCode; - /* Init */ - { size_t const errorCode = ZSTD_compressBegin_internal(cctx, dict, dictSize, params, srcSize); - if(ZSTD_isError(errorCode)) return errorCode; } - - /* body (compression) */ - { size_t const oSize = ZSTD_compressContinueThenEnd(cctx, op, dstCapacity, src, srcSize); - return oSize; } + return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); } size_t ZSTD_compress_advanced (ZSTD_CCtx* ctx, diff --git a/lib/zstd.h b/lib/zstd.h index 9356f56d2..e3663e8b7 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -315,15 +315,14 @@ ZSTDLIB_API size_t ZSTD_sizeofDCtx(const ZSTD_DCtx* dctx); /* This is an advanced API, giving full control over buffer management, for users which need direct control over memory. * But it's also a complex one, with a lot of restrictions (documented below). * For an easier streaming API, look into common/zbuff.h -* which removes all restrictions by implementing and managing its own internal buffer */ +* which removes all restrictions by allocating and managing its own internal buffer */ 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); 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); -ZSTDLIB_API size_t ZSTD_compressContinueThenEnd(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); /* A ZSTD_CCtx object is required to track streaming operations. @@ -338,7 +337,7 @@ ZSTDLIB_API size_t ZSTD_compressContinueThenEnd(ZSTD_CCtx* cctx, void* dst, size Then, consume your input using ZSTD_compressContinue(). There are some important considerations to keep in mind when using this advanced function : - ZSTD_compressContinue() has no internal buffer. It uses externally provided buffer only. - - Interface is synchronous : input is consumed entirely and produce 1 (or more) compressed blocks. + - Interface is synchronous : input is consumed entirely and produce 1+ (or more) compressed blocks. - Caller must ensure there is enough space in `dst` to store compressed data under worst case scenario. Worst case evaluation is provided by ZSTD_compressBound(). ZSTD_compressContinue() doesn't guarantee recover after a failed compression. @@ -347,9 +346,9 @@ ZSTDLIB_API size_t ZSTD_compressContinueThenEnd(ZSTD_CCtx* cctx, void* dst, size - ZSTD_compressContinue() detects that prior input has been overwritten when `src` buffer overlaps. In which case, it will "discard" the relevant memory section from its history. - Finish a frame with ZSTD_compressEnd(), which will write the epilogue, - or ZSTD_compressContinueThenEnd(), which will write the last block. - Without last block / epilogue mark, frames will be considered unfinished (broken) by decoders. + 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. You can then reuse `ZSTD_CCtx` (ZSTD_compressBegin()) to compress some new frame. */ diff --git a/programs/fullbench.c b/programs/fullbench.c index 53041db77..f6852f6d8 100644 --- a/programs/fullbench.c +++ b/programs/fullbench.c @@ -173,12 +173,9 @@ static size_t local_ZBUFF_decompress(void* dst, size_t dstCapacity, void* buff2, static ZSTD_CCtx* g_zcc = NULL; size_t local_ZSTD_compressContinue(void* dst, size_t dstCapacity, void* buff2, const void* src, size_t srcSize) { - size_t compressedSize; (void)buff2; ZSTD_compressBegin(g_zcc, 1); - compressedSize = ZSTD_compressContinue(g_zcc, dst, dstCapacity, src, srcSize); - compressedSize += ZSTD_compressEnd(g_zcc, ((char*)dst)+compressedSize, dstCapacity-compressedSize); - return compressedSize; + return ZSTD_compressEnd(g_zcc, dst, dstCapacity, src, srcSize); } size_t local_ZSTD_decompressContinue(void* dst, size_t dstCapacity, void* buff2, const void* src, size_t srcSize) diff --git a/programs/fuzzer.c b/programs/fuzzer.c index 33cfbcb2d..cb31dc431 100644 --- a/programs/fuzzer.c +++ b/programs/fuzzer.c @@ -186,11 +186,9 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "test%3i : compress with flat dictionary : ", testNb++); cSize = 0; - CHECKPLUS(r, ZSTD_compressContinue(ctxOrig, compressedBuffer, ZSTD_compressBound(CNBuffSize), + CHECKPLUS(r, ZSTD_compressEnd(ctxOrig, compressedBuffer, ZSTD_compressBound(CNBuffSize), (const char*)CNBuffer + dictSize, CNBuffSize - dictSize), cSize += r); - CHECKPLUS(r, ZSTD_compressEnd(ctxOrig, (char*)compressedBuffer+cSize, ZSTD_compressBound(CNBuffSize)-cSize), - cSize += r); DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); DISPLAYLEVEL(4, "test%3i : frame built with flat dictionary should be decompressible : ", testNb++); @@ -204,11 +202,9 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "test%3i : compress with duplicated context : ", testNb++); { size_t const cSizeOrig = cSize; cSize = 0; - CHECKPLUS(r, ZSTD_compressContinue(ctxDuplicated, compressedBuffer, ZSTD_compressBound(CNBuffSize), + CHECKPLUS(r, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, ZSTD_compressBound(CNBuffSize), (const char*)CNBuffer + dictSize, CNBuffSize - dictSize), cSize += r); - CHECKPLUS(r, ZSTD_compressEnd(ctxDuplicated, (char*)compressedBuffer+cSize, ZSTD_compressBound(CNBuffSize)-cSize), - cSize += r); if (cSize != cSizeOrig) goto _output_error; /* should be identical ==> same size */ } DISPLAYLEVEL(4, "OK (%u bytes : %.2f%%)\n", (U32)cSize, (double)cSize/CNBuffSize*100); @@ -696,7 +692,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD totalTestSize += segmentSize; } } - { size_t const flushResult = ZSTD_compressEnd(ctx, cBuffer+cSize, cBufferSize-cSize); + { size_t const flushResult = ZSTD_compressEnd(ctx, cBuffer+cSize, cBufferSize-cSize, NULL, 0); CHECK (ZSTD_isError(flushResult), "multi-segments epilogue error : %s", ZSTD_getErrorName(flushResult)); cSize += flushResult; } From 16e73033adfcd0abf2410fd5aa3471cf076d6abc Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 28 Jul 2016 16:32:34 +0200 Subject: [PATCH 25/46] introduced stage zbf_end --- lib/compress/zbuff_compress.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/compress/zbuff_compress.c b/lib/compress/zbuff_compress.c index 29fd4dbb1..25a142203 100644 --- a/lib/compress/zbuff_compress.c +++ b/lib/compress/zbuff_compress.c @@ -134,7 +134,7 @@ size_t ZBUFF_freeCCtx(ZBUFF_CCtx* zbc) } -/* *** Initialization *** */ +/* ====== Initialization ====== */ size_t ZBUFF_compressInit_advanced(ZBUFF_CCtx* zbc, const void* dict, size_t dictSize, @@ -191,14 +191,16 @@ MEM_STATIC size_t ZBUFF_limitCopy(void* dst, size_t dstCapacity, const void* src } -/* *** Compression *** */ +/* ====== Compression ====== */ + +typedef enum { zbf_gather, zbf_flush, zbf_end } ZBUFF_flush_e; static size_t ZBUFF_compressContinue_generic(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr, const void* src, size_t* srcSizePtr, - int flush) + ZBUFF_flush_e flush) { - U32 notDone = 1; + U32 someMoreWork = 1; const char* const istart = (const char*)src; const char* const iend = istart + *srcSizePtr; const char* ip = istart; @@ -206,7 +208,7 @@ static size_t ZBUFF_compressContinue_generic(ZBUFF_CCtx* zbc, char* const oend = ostart + *dstCapacityPtr; char* op = ostart; - while (notDone) { + while (someMoreWork) { switch(zbc->stage) { case ZBUFFcs_init: return ERROR(init_missing); /* call ZBUFF_compressInit() first ! */ @@ -218,7 +220,7 @@ static size_t ZBUFF_compressContinue_generic(ZBUFF_CCtx* zbc, zbc->inBuffPos += loaded; ip += loaded; if ( (zbc->inBuffPos==zbc->inToCompress) || (!flush && (toLoad != loaded)) ) { - notDone = 0; break; /* not enough input to get a full block : stop there, wait for more */ + someMoreWork = 0; break; /* not enough input to get a full block : stop there, wait for more */ } } /* compress current block (note : this stage cannot be stopped in the middle) */ { void* cDst; @@ -247,14 +249,14 @@ static size_t ZBUFF_compressContinue_generic(ZBUFF_CCtx* zbc, size_t const flushed = ZBUFF_limitCopy(op, oend-op, zbc->outBuff + zbc->outBuffFlushedSize, toFlush); op += flushed; zbc->outBuffFlushedSize += flushed; - if (toFlush!=flushed) { notDone = 0; break; } /* dst too small to store flushed data : stop there */ + if (toFlush!=flushed) { someMoreWork = 0; break; } /* dst too small to store flushed data : stop there */ zbc->outBuffContentSize = zbc->outBuffFlushedSize = 0; zbc->stage = ZBUFFcs_load; break; } case ZBUFFcs_final: - notDone = 0; /* do nothing */ + someMoreWork = 0; /* do nothing */ break; default: @@ -274,17 +276,17 @@ size_t ZBUFF_compressContinue(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr, const void* src, size_t* srcSizePtr) { - return ZBUFF_compressContinue_generic(zbc, dst, dstCapacityPtr, src, srcSizePtr, 0); + return ZBUFF_compressContinue_generic(zbc, dst, dstCapacityPtr, src, srcSizePtr, zbf_gather); } -/* *** Finalize *** */ +/* ====== Finalize ====== */ size_t ZBUFF_compressFlush(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr) { size_t srcSize = 0; - ZBUFF_compressContinue_generic(zbc, dst, dstCapacityPtr, &srcSize, &srcSize, 1); /* use a valid src address instead of NULL */ + ZBUFF_compressContinue_generic(zbc, dst, dstCapacityPtr, &srcSize, &srcSize, zbf_flush); /* use a valid src address instead of NULL */ return zbc->outBuffContentSize - zbc->outBuffFlushedSize; } @@ -298,8 +300,10 @@ size_t ZBUFF_compressEnd(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr) if (zbc->stage != ZBUFFcs_final) { /* flush whatever remains */ size_t outSize = *dstCapacityPtr; - size_t const remainingToFlush = ZBUFF_compressFlush(zbc, dst, &outSize); - op += outSize; + size_t srcSize = 0; + size_t const uselessHint = ZBUFF_compressContinue_generic(zbc, dst, &outSize, &srcSize, &srcSize, zbf_end); /* use a valid address instead of NULL */ + size_t const remainingToFlush = zbc->outBuffContentSize - zbc->outBuffFlushedSize; + op += outSize; (void)uselessHint; if (remainingToFlush) { *dstCapacityPtr = op-ostart; return remainingToFlush + ZBUFF_endFrameSize + (zbc->checksum * 4); From 60ba31c5703deee1df6b22024001add9b65bf3d4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 28 Jul 2016 19:55:09 +0200 Subject: [PATCH 26/46] zbuff uses ZSTD_compressEnd() --- lib/common/zbuff.h | 4 +-- lib/compress/zbuff_compress.c | 21 ++++++++++------ lib/decompress/zbuff_decompress.c | 42 +++++++++++++++++-------------- lib/decompress/zstd_decompress.c | 23 +++++++---------- lib/zstd.h | 3 ++- programs/datagencli.c | 8 +++--- programs/fileio.c | 11 ++++---- programs/playTests.sh | 2 +- programs/zbufftest.c | 11 ++++---- programs/zstd.1 | 3 ++- 10 files changed, 66 insertions(+), 62 deletions(-) diff --git a/lib/common/zbuff.h b/lib/common/zbuff.h index b31c3e631..8e6305e64 100644 --- a/lib/common/zbuff.h +++ b/lib/common/zbuff.h @@ -137,8 +137,8 @@ ZSTDLIB_API size_t ZBUFF_decompressContinue(ZBUFF_DCtx* dctx, * The function will report how many bytes were read or written by modifying *srcSizePtr and *dstCapacityPtr. * Note that it may not consume the entire input, in which case it's up to the caller to present remaining input again. * The content of `dst` will be overwritten (up to *dstCapacityPtr) at each function call, so save its content if it matters, or change `dst`. -* @return : a hint to preferred nb of bytes to use as input for next function call (it's only a hint, to help latency), -* or 0 when a frame is completely decoded, +* @return : 0 when a frame is completely decoded and fully flushed, + >0 when decoding is not finished, with value being a suggested next input size (it's just a hint, tends to help latency), * or an error code, which can be tested using ZBUFF_isError(). * * Hint : recommended buffer sizes (not compulsory) : ZBUFF_recommendedDInSize() and ZBUFF_recommendedDOutSize() diff --git a/lib/compress/zbuff_compress.c b/lib/compress/zbuff_compress.c index 25a142203..5d9291857 100644 --- a/lib/compress/zbuff_compress.c +++ b/lib/compress/zbuff_compress.c @@ -46,7 +46,7 @@ static size_t const ZBUFF_endFrameSize = ZSTD_BLOCKHEADERSIZE; -/*_************************************************** +/*-*********************************************************** * Streaming compression * * A ZBUFF_CCtx object is required to track streaming operation. @@ -77,7 +77,7 @@ static size_t const ZBUFF_endFrameSize = ZSTD_BLOCKHEADERSIZE; * Hint : recommended buffer sizes (not compulsory) * input : ZSTD_BLOCKSIZE_MAX (128 KB), internal unit size, it improves latency to use this value. * output : ZSTD_compressBound(ZSTD_BLOCKSIZE_MAX) + ZSTD_blockHeaderSize + ZBUFF_endFrameSize : ensures it's always possible to write/flush/end a full block at best speed. -* **************************************************/ +* ***********************************************************/ typedef enum { ZBUFFcs_init, ZBUFFcs_load, ZBUFFcs_flush, ZBUFFcs_final } ZBUFF_cStage; @@ -96,6 +96,7 @@ struct ZBUFF_CCtx_s { size_t outBuffFlushedSize; ZBUFF_cStage stage; U32 checksum; + U32 frameEnded; ZSTD_customMem customMem; }; /* typedef'd tp ZBUFF_CCtx within "zstd_buffered.h" */ @@ -166,6 +167,7 @@ size_t ZBUFF_compressInit_advanced(ZBUFF_CCtx* zbc, zbc->outBuffContentSize = zbc->outBuffFlushedSize = 0; zbc->stage = ZBUFFcs_load; zbc->checksum = params.fParams.checksumFlag > 0; + zbc->frameEnded = 0; return 0; /* ready to go */ } @@ -198,7 +200,7 @@ typedef enum { zbf_gather, zbf_flush, zbf_end } ZBUFF_flush_e; static size_t ZBUFF_compressContinue_generic(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr, const void* src, size_t* srcSizePtr, - ZBUFF_flush_e flush) + ZBUFF_flush_e const flush) { U32 someMoreWork = 1; const char* const istart = (const char*)src; @@ -231,8 +233,11 @@ static size_t ZBUFF_compressContinue_generic(ZBUFF_CCtx* zbc, cDst = op; /* compress directly into output buffer (avoid flush stage) */ else cDst = zbc->outBuff, oSize = zbc->outBuffSize; - cSize = ZSTD_compressContinue(zbc->zc, cDst, oSize, zbc->inBuff + zbc->inToCompress, iSize); + cSize = (flush == zbf_end) ? + ZSTD_compressEnd(zbc->zc, cDst, oSize, zbc->inBuff + zbc->inToCompress, iSize) : + ZSTD_compressContinue(zbc->zc, cDst, oSize, zbc->inBuff + zbc->inToCompress, iSize); if (ZSTD_isError(cSize)) return cSize; + if (flush == zbf_end) zbc->frameEnded = 1; /* prepare next block */ zbc->inBuffTarget = zbc->inBuffPos + zbc->blockSize; if (zbc->inBuffTarget > zbc->inBuffSize) @@ -266,6 +271,7 @@ static size_t ZBUFF_compressContinue_generic(ZBUFF_CCtx* zbc, *srcSizePtr = ip - istart; *dstCapacityPtr = op - ostart; + if (zbc->frameEnded) return 0; { size_t hintInSize = zbc->inBuffTarget - zbc->inBuffPos; if (hintInSize==0) hintInSize = zbc->blockSize; return hintInSize; @@ -301,16 +307,17 @@ size_t ZBUFF_compressEnd(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr) /* flush whatever remains */ size_t outSize = *dstCapacityPtr; size_t srcSize = 0; - size_t const uselessHint = ZBUFF_compressContinue_generic(zbc, dst, &outSize, &srcSize, &srcSize, zbf_end); /* use a valid address instead of NULL */ + size_t const notEnded = ZBUFF_compressContinue_generic(zbc, dst, &outSize, &srcSize, &srcSize, zbf_end); /* use a valid address instead of NULL */ size_t const remainingToFlush = zbc->outBuffContentSize - zbc->outBuffFlushedSize; - op += outSize; (void)uselessHint; + op += outSize; if (remainingToFlush) { *dstCapacityPtr = op-ostart; return remainingToFlush + ZBUFF_endFrameSize + (zbc->checksum * 4); } /* create epilogue */ zbc->stage = ZBUFFcs_final; - zbc->outBuffContentSize = ZSTD_compressEnd(zbc->zc, zbc->outBuff, zbc->outBuffSize, NULL, 0); /* epilogue into outBuff */ + zbc->outBuffContentSize = !notEnded ? 0 : + ZSTD_compressEnd(zbc->zc, zbc->outBuff, zbc->outBuffSize, NULL, 0); /* write epilogue into outBuff */ } /* flush epilogue */ diff --git a/lib/decompress/zbuff_decompress.c b/lib/decompress/zbuff_decompress.c index 3c9ada6f0..b22bd84a6 100644 --- a/lib/decompress/zbuff_decompress.c +++ b/lib/decompress/zbuff_decompress.c @@ -158,9 +158,9 @@ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, char* const ostart = (char*)dst; char* const oend = ostart + *dstCapacityPtr; char* op = ostart; - U32 notDone = 1; + U32 someMoreWork = 1; - while (notDone) { + while (someMoreWork) { switch(zbd->stage) { case ZBUFFds_init : @@ -168,9 +168,9 @@ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, case ZBUFFds_loadHeader : { size_t const hSize = ZSTD_getFrameParams(&(zbd->fParams), zbd->headerBuffer, zbd->lhSize); - if (hSize != 0) { + if (ZSTD_isError(hSize)) return hSize; + if (hSize != 0) { /* need more input */ size_t const toLoad = hSize - zbd->lhSize; /* if hSize!=0, hSize > zbd->lhSize */ - if (ZSTD_isError(hSize)) return hSize; if (toLoad > (size_t)(iend-ip)) { /* not enough input to load full header */ memcpy(zbd->headerBuffer + zbd->lhSize, ip, iend-ip); zbd->lhSize += iend-ip; @@ -184,7 +184,7 @@ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, /* Consume header */ { size_t const h1Size = ZSTD_nextSrcSizeToDecompress(zbd->zd); /* == ZSTD_frameHeaderSize_min */ size_t const h1Result = ZSTD_decompressContinue(zbd->zd, NULL, 0, zbd->headerBuffer, h1Size); - if (ZSTD_isError(h1Result)) return h1Result; + if (ZSTD_isError(h1Result)) return h1Result; /* should not happen : already checked */ if (h1Size < zbd->lhSize) { /* long header */ size_t const h2Size = ZSTD_nextSrcSizeToDecompress(zbd->zd); size_t const h2Result = ZSTD_decompressContinue(zbd->zd, NULL, 0, zbd->headerBuffer+h1Size, h2Size); @@ -195,6 +195,7 @@ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, /* Frame header instruct buffer sizes */ { size_t const blockSize = MIN(zbd->fParams.windowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX); + size_t const neededOutSize = zbd->fParams.windowSize + blockSize; zbd->blockSize = blockSize; if (zbd->inBuffSize < blockSize) { zbd->customMem.customFree(zbd->customMem.opaque, zbd->inBuff); @@ -202,20 +203,20 @@ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, zbd->inBuff = (char*)zbd->customMem.customAlloc(zbd->customMem.opaque, blockSize); if (zbd->inBuff == NULL) return ERROR(memory_allocation); } - { size_t const neededOutSize = zbd->fParams.windowSize + blockSize; - if (zbd->outBuffSize < neededOutSize) { - zbd->customMem.customFree(zbd->customMem.opaque, zbd->outBuff); - zbd->outBuffSize = neededOutSize; - zbd->outBuff = (char*)zbd->customMem.customAlloc(zbd->customMem.opaque, neededOutSize); - if (zbd->outBuff == NULL) return ERROR(memory_allocation); - } } } + if (zbd->outBuffSize < neededOutSize) { + zbd->customMem.customFree(zbd->customMem.opaque, zbd->outBuff); + zbd->outBuffSize = neededOutSize; + zbd->outBuff = (char*)zbd->customMem.customAlloc(zbd->customMem.opaque, neededOutSize); + if (zbd->outBuff == NULL) return ERROR(memory_allocation); + } } zbd->stage = ZBUFFds_read; + /* pass-through */ case ZBUFFds_read: { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zbd->zd); if (neededInSize==0) { /* end of frame */ zbd->stage = ZBUFFds_init; - notDone = 0; + someMoreWork = 0; break; } if ((size_t)(iend-ip) >= neededInSize) { /* decode directly from src */ @@ -230,8 +231,9 @@ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, zbd->stage = ZBUFFds_flush; break; } - if (ip==iend) { notDone = 0; break; } /* no more input */ + if (ip==iend) { someMoreWork = 0; break; } /* no more input */ zbd->stage = ZBUFFds_load; + /* pass-through */ } case ZBUFFds_load: @@ -242,7 +244,7 @@ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, loadedSize = ZBUFF_limitCopy(zbd->inBuff + zbd->inPos, toLoad, ip, iend-ip); ip += loadedSize; zbd->inPos += loadedSize; - if (loadedSize < toLoad) { notDone = 0; break; } /* not enough input, wait for more */ + if (loadedSize < toLoad) { someMoreWork = 0; break; } /* not enough input, wait for more */ /* decode loaded input */ { const int isSkipFrame = ZSTD_isSkipFrame(zbd->zd); @@ -254,7 +256,7 @@ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, if (!decodedSize && !isSkipFrame) { zbd->stage = ZBUFFds_read; break; } /* this was just a header */ zbd->outEnd = zbd->outStart + decodedSize; zbd->stage = ZBUFFds_flush; - // break; /* ZBUFFds_flush follows */ + /* pass-through */ } } case ZBUFFds_flush: @@ -262,14 +264,14 @@ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, size_t const flushedSize = ZBUFF_limitCopy(op, oend-op, zbd->outBuff + zbd->outStart, toFlushSize); op += flushedSize; zbd->outStart += flushedSize; - if (flushedSize == toFlushSize) { + if (flushedSize == toFlushSize) { /* flush completed */ zbd->stage = ZBUFFds_read; if (zbd->outStart + zbd->blockSize > zbd->outBuffSize) zbd->outStart = zbd->outEnd = 0; break; } /* cannot flush everything */ - notDone = 0; + someMoreWork = 0; break; } default: return ERROR(GENERIC); /* impossible */ @@ -279,13 +281,15 @@ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, *srcSizePtr = ip-istart; *dstCapacityPtr = op-ostart; { size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zbd->zd); + if (!nextSrcSizeHint) return (zbd->outEnd != zbd->outStart); /* return 0 only if fully flushed too */ + if (nextSrcSizeHint > 4) nextSrcSizeHint += ZSTD_blockHeaderSize; + if (zbd->inPos > nextSrcSizeHint) return ERROR(GENERIC); /* should never happen */ nextSrcSizeHint -= zbd->inPos; /* already loaded*/ return nextSrcSizeHint; } } - /* ************************************* * Tool functions ***************************************/ diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 60f7568d1..5aa43790a 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -979,18 +979,12 @@ size_t ZSTD_decompress(void* dst, size_t dstCapacity, const void* src, size_t sr } -/*_****************************** -* Streaming Decompression API -********************************/ -size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx) -{ - return dctx->expected; -} +/*-********************************** +* Streaming Decompression API +************************************/ +size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx) { return dctx->expected; } -int ZSTD_isSkipFrame(ZSTD_DCtx* dctx) -{ - return dctx->stage == ZSTDds_skipFrame; -} +int ZSTD_isSkipFrame(ZSTD_DCtx* dctx) { return dctx->stage == ZSTDds_skipFrame; } /* for zbuff */ /** ZSTD_decompressContinue() : * @return : nb of bytes generated into `dst` (necessarily <= `dstCapacity) @@ -1079,11 +1073,12 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c if (dctx->stage == ZSTDds_decompressLastBlock) { /* end of frame */ if (dctx->fParams.checksumFlag) { /* another round for frame checksum */ - dctx->expected = 0; + dctx->expected = 4; dctx->stage = ZSTDds_checkChecksum; + } else { + dctx->expected = 0; /* ends here */ + dctx->stage = ZSTDds_getFrameHeaderSize; } - dctx->expected = 0; /* ends here */ - dctx->stage = ZSTDds_getFrameHeaderSize; } else { dctx->stage = ZSTDds_decodeBlockHeader; dctx->expected = ZSTD_blockHeaderSize; diff --git a/lib/zstd.h b/lib/zstd.h index e3663e8b7..01ac7d268 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -316,6 +316,7 @@ ZSTDLIB_API size_t ZSTD_sizeofDCtx(const ZSTD_DCtx* dctx); * But it's also a complex one, with a lot of restrictions (documented below). * For an easier streaming API, look into common/zbuff.h * which removes all restrictions by allocating and managing its own internal buffer */ + 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); @@ -360,7 +361,7 @@ typedef struct { unsigned checksumFlag; } ZSTD_frameParams; -ZSTDLIB_API size_t ZSTD_getFrameParams(ZSTD_frameParams* fparamsPtr, const void* src, size_t srcSize); /**< doesn't consume input */ +ZSTDLIB_API size_t ZSTD_getFrameParams(ZSTD_frameParams* fparamsPtr, const void* src, size_t srcSize); /**< doesn't consume input, see details below */ ZSTDLIB_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx); ZSTDLIB_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); diff --git a/programs/datagencli.c b/programs/datagencli.c index d437d5cb3..c4fa7f73b 100644 --- a/programs/datagencli.c +++ b/programs/datagencli.c @@ -39,7 +39,7 @@ #define MB *(1 <<20) #define GB *(1U<<30) -#define SIZE_DEFAULT (64 KB) +#define SIZE_DEFAULT ((64 KB) + 1) #define SEED_DEFAULT 0 #define COMPRESSIBILITY_DEFAULT 50 @@ -72,15 +72,13 @@ static int usage(const char* programName) int main(int argc, const char** argv) { - int argNb; double proba = (double)COMPRESSIBILITY_DEFAULT / 100; double litProba = 0.0; U64 size = SIZE_DEFAULT; U32 seed = SEED_DEFAULT; - const char* programName; + const char* const programName = argv[0]; - /* Check command line */ - programName = argv[0]; + int argNb; for(argNb=1; argNb>20) ); if (toRead == 0) break; /* end of frame */ - if (readSize) EXM_THROW(38, "Decoding error : should consume entire input"); + if (readSize) EXM_THROW(37, "Decoding error : should consume entire input"); /* Fill input buffer */ - if (toRead > ress.srcBufferSize) EXM_THROW(34, "too large block"); + if (toRead > ress.srcBufferSize) EXM_THROW(38, "too large block"); readSize = fread(ress.srcBuffer, 1, toRead, finput); - if (readSize != toRead) - EXM_THROW(35, "Read error"); + if (readSize == 0) EXM_THROW(39, "Read error : premature end"); } FIO_fwriteSparseEnd(foutput, storedSkips); @@ -710,8 +709,8 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* srcFileName) continue; } #endif - if (((magic & 0xFFFFFFF0U) != ZSTD_MAGIC_SKIPPABLE_START) && (magic != ZSTD_MAGICNUMBER)) { - if (g_overwrite) { /* -df : pass-through mode */ + if (((magic & 0xFFFFFFF0U) != ZSTD_MAGIC_SKIPPABLE_START) & (magic != ZSTD_MAGICNUMBER)) { + if ((g_overwrite) && !strcmp (srcFileName, stdinmark)) { /* pass-through mode */ unsigned const result = FIO_passThrough(dstFile, srcFile, ress.srcBuffer, ress.srcBufferSize); if (fclose(srcFile)) EXM_THROW(32, "zstd: %s close error", srcFileName); /* error should never happen */ return result; diff --git a/programs/playTests.sh b/programs/playTests.sh index ef44f7bdf..05eb49fce 100755 --- a/programs/playTests.sh +++ b/programs/playTests.sh @@ -142,8 +142,8 @@ $ECHO "\n**** multiple files tests **** " ./datagen -s1 > tmp1 2> $INTOVOID ./datagen -s2 -g100K > tmp2 2> $INTOVOID ./datagen -s3 -g1M > tmp3 2> $INTOVOID -$ZSTD -f tmp* $ECHO "compress tmp* : " +$ZSTD -f tmp* ls -ls tmp* rm tmp1 tmp2 tmp3 $ECHO "decompress tmp* : " diff --git a/programs/zbufftest.c b/programs/zbufftest.c index 3e36d015f..ce6beb246 100644 --- a/programs/zbufftest.c +++ b/programs/zbufftest.c @@ -424,23 +424,22 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres U32 const enoughDstSize = dstBuffSize >= remainingToFlush; remainingToFlush = ZBUFF_compressEnd(zc, cBuffer+cSize, &dstBuffSize); CHECK (ZBUFF_isError(remainingToFlush), "flush error : %s", ZBUFF_getErrorName(remainingToFlush)); - //DISPLAY("flush %u bytes : still within context : %i \n", (U32)dstBuffSize, (int)remainingToFlush); - CHECK (enoughDstSize && remainingToFlush, "ZBUFF_compressEnd() not fully flushed, but enough space available"); + CHECK (enoughDstSize && remainingToFlush, "ZBUFF_compressEnd() not fully flushed (%u remaining), but enough space available", (U32)remainingToFlush); cSize += dstBuffSize; } } crcOrig = XXH64_digest(&xxhState); /* multi - fragments decompression test */ ZBUFF_decompressInitDictionary(zd, dict, dictSize); - for (totalCSize = 0, totalGenSize = 0 ; totalCSize < cSize ; ) { + errorCode = 1; + for (totalCSize = 0, totalGenSize = 0 ; errorCode ; ) { size_t readCSrcSize = FUZ_randomLength(&lseed, maxSampleLog); size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); size_t dstBuffSize = MIN(dstBufferSize - totalGenSize, randomDstSize); - size_t const decompressError = ZBUFF_decompressContinue(zd, dstBuffer+totalGenSize, &dstBuffSize, cBuffer+totalCSize, &readCSrcSize); - CHECK (ZBUFF_isError(decompressError), "decompression error : %s", ZBUFF_getErrorName(decompressError)); + errorCode = ZBUFF_decompressContinue(zd, dstBuffer+totalGenSize, &dstBuffSize, cBuffer+totalCSize, &readCSrcSize); + CHECK (ZBUFF_isError(errorCode), "decompression error : %s", ZBUFF_getErrorName(errorCode)); totalGenSize += dstBuffSize; totalCSize += readCSrcSize; - errorCode = decompressError; /* needed for != 0 last test */ } CHECK (errorCode != 0, "frame not fully decoded"); CHECK (totalGenSize != totalTestSize, "decompressed data : wrong size") diff --git a/programs/zstd.1 b/programs/zstd.1 index 9b6be9349..d2dfc3c14 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -80,7 +80,8 @@ It also features a very fast decoder, with speed > 500 MB/s per core. verbose mode .TP .BR \-q ", " --quiet - suppress warnings and notifications; specify twice to suppress errors too + suppress warnings, interactivity and notifications. + specify twice to suppress errors too. .TP .BR \-C ", " --check add integrity check computed from uncompressed data From 4c5bbf64f99de06355fdc08b8e6f869813ef83d8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 28 Jul 2016 20:30:25 +0200 Subject: [PATCH 27/46] fixed : frame concatenation without checksum --- lib/decompress/zbuff_decompress.c | 2 +- lib/decompress/zstd_decompress.c | 21 +++++++++++++++++++++ lib/zstd.h | 5 +++++ programs/playTests.sh | 7 +++++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/decompress/zbuff_decompress.c b/lib/decompress/zbuff_decompress.c index b22bd84a6..908120fc7 100644 --- a/lib/decompress/zbuff_decompress.c +++ b/lib/decompress/zbuff_decompress.c @@ -282,7 +282,7 @@ size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd, *dstCapacityPtr = op-ostart; { size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zbd->zd); if (!nextSrcSizeHint) return (zbd->outEnd != zbd->outStart); /* return 0 only if fully flushed too */ - if (nextSrcSizeHint > 4) nextSrcSizeHint += ZSTD_blockHeaderSize; + nextSrcSizeHint += ZSTD_blockHeaderSize * (ZSTD_nextInputType(zbd->zd) == ZSTDnit_block); if (zbd->inPos > nextSrcSizeHint) return ERROR(GENERIC); /* should never happen */ nextSrcSizeHint -= zbd->inPos; /* already loaded*/ return nextSrcSizeHint; diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 5aa43790a..972a8143d 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -984,6 +984,27 @@ size_t ZSTD_decompress(void* dst, size_t dstCapacity, const void* src, size_t sr ************************************/ size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx) { return dctx->expected; } +ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx) { + switch(dctx->stage) + { + default: /* should not happen */ + case ZSTDds_getFrameHeaderSize: + case ZSTDds_decodeFrameHeader: + return ZSTDnit_frameHeader; + case ZSTDds_decodeBlockHeader: + return ZSTDnit_blockHeader; + case ZSTDds_decompressBlock: + return ZSTDnit_block; + case ZSTDds_decompressLastBlock: + return ZSTDnit_lastBlock; + case ZSTDds_checkChecksum: + return ZSTDnit_checksum; + case ZSTDds_decodeSkippableHeader: + case ZSTDds_skipFrame: + return ZSTDnit_skippableFrame; + } +} + int ZSTD_isSkipFrame(ZSTD_DCtx* dctx) { return dctx->stage == ZSTDds_skipFrame; } /* for zbuff */ /** ZSTD_decompressContinue() : diff --git a/lib/zstd.h b/lib/zstd.h index 01ac7d268..46338aeac 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -370,6 +370,9 @@ ZSTDLIB_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx) ZSTDLIB_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx); ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e; +ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); + /* Buffer-less streaming decompression (synchronous mode) @@ -408,6 +411,8 @@ ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t ds A frame is fully decoded when ZSTD_nextSrcSizeToDecompress() returns zero. Context can then be reset to start a new decompression. + Note : it's possible to know if next input to present is a header or a block, using ZSTD_nextInputType(). + But this information is not required to properly decode a frame. == Special case : skippable frames == diff --git a/programs/playTests.sh b/programs/playTests.sh index 05eb49fce..1fc508f9d 100755 --- a/programs/playTests.sh +++ b/programs/playTests.sh @@ -96,6 +96,13 @@ cat hello.zstd world.zstd > helloworld.zstd $ZSTD -dc helloworld.zstd > result.tmp cat result.tmp sdiff 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 rm ./*.tmp ./*.zstd $ECHO "frame concatenation tests completed" From ffa7d0ac1e186bf78db274f205f35b994e378919 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 28 Jul 2016 21:01:17 +0200 Subject: [PATCH 28/46] clarified comment --- lib/common/zbuff.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/common/zbuff.h b/lib/common/zbuff.h index 8e6305e64..f7db57302 100644 --- a/lib/common/zbuff.h +++ b/lib/common/zbuff.h @@ -138,7 +138,8 @@ ZSTDLIB_API size_t ZBUFF_decompressContinue(ZBUFF_DCtx* dctx, * Note that it may not consume the entire input, in which case it's up to the caller to present remaining input again. * The content of `dst` will be overwritten (up to *dstCapacityPtr) at each function call, so save its content if it matters, or change `dst`. * @return : 0 when a frame is completely decoded and fully flushed, - >0 when decoding is not finished, with value being a suggested next input size (it's just a hint, tends to help latency), +* 1 when there is still some data left within internal buffer to flush, +* >1 when more data is expected, with value being a suggested next input size (it's just a hint, which helps latency), * or an error code, which can be tested using ZBUFF_isError(). * * Hint : recommended buffer sizes (not compulsory) : ZBUFF_recommendedDInSize() and ZBUFF_recommendedDOutSize() From 6a82f0f8bf1f4dd7c53a6273aed2d4c054c1d64a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 29 Jul 2016 00:55:45 +0200 Subject: [PATCH 29/46] minor comments --- NEWS | 2 +- lib/common/zbuff.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 7221e461c..d525bc0ff 100644 --- a/NEWS +++ b/NEWS @@ -3,7 +3,7 @@ Improved : better speed on clang and gcc -O2, thanks to Eric Biggers Changed : modified API : ZSTD_compressEnd() Fixed : legacy mode with ZSTD_HEAPMODE=0, by Christopher Bergqvist Fixed : premature end of frame when zero-sized raw block, reported by Eric Biggers -Fixed : statistics for large dictionaries (> 128 KB), reported by Ilona Papava +Fixed : statistics for large dictionaries (> 256 KB), reported by Ilona Papava Fixed : checksum correctly checked in single-pass mode Fixed : combined --test amd --rm, reported by Andreas M. Nilsson Modified : minor compression level adaptations diff --git a/lib/common/zbuff.h b/lib/common/zbuff.h index f7db57302..269dc227c 100644 --- a/lib/common/zbuff.h +++ b/lib/common/zbuff.h @@ -58,7 +58,9 @@ extern "C" { ***************************************/ /* This is the easier "buffered" streaming API, * using an internal buffer to lift all restrictions on user-provided buffers -* which can be any size, any place, for both input and output. */ +* which can be any size, any place, for both input and output. +* ZBUFF and ZSTD are 100% interoperable, +* frames created by one can be decoded by the other one */ typedef struct ZBUFF_CCtx_s ZBUFF_CCtx; ZSTDLIB_API ZBUFF_CCtx* ZBUFF_createCCtx(void); From f0f9b07a94eeb988c255e43c682242c9d7b78e85 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 29 Jul 2016 17:43:13 +0200 Subject: [PATCH 30/46] minor readme update --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b87e35381..f8353ec1d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ you can consult a list of known ports on [Zstandard homepage](http://www.zstd.ne |master | [![Build Status](https://travis-ci.org/Cyan4973/zstd.svg?branch=master)](https://travis-ci.org/Cyan4973/zstd) | |dev | [![Build Status](https://travis-ci.org/Cyan4973/zstd.svg?branch=dev)](https://travis-ci.org/Cyan4973/zstd) | -As a reference, several fast compression algorithms were tested and compared on a Core i7-3930K CPU @ 4.5GHz, using [lzbench], an open-source in-memory benchmark by @inikep compiled with gcc 5.2.1, with the [Silesia compression corpus]. +As a reference, several fast compression algorithms were tested and compared on a Core i7-3930K CPU @ 4.5GHz, using [lzbench], an open-source in-memory benchmark by @inikep compiled with gcc 5.4.0, with the [Silesia compression corpus]. [lzbench]: https://github.com/inikep/lzbench [Silesia compression corpus]: http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia @@ -19,9 +19,9 @@ As a reference, several fast compression algorithms were tested and compared on |Name | Ratio | C.speed | D.speed | |-----------------|-------|--------:|--------:| | | | MB/s | MB/s | -|**zstd 0.7.0 -1**|**2.877**|**325**| **930** | +|**zstd 0.8.0 -1**|**2.877**|**330**| **930** | | [zlib] 1.2.8 -1 | 2.730 | 95 | 360 | -| brotli -0 | 2.708 | 220 | 430 | +| brotli 0.4 -0 | 2.708 | 320 | 375 | | QuickLZ 1.5 | 2.237 | 510 | 605 | | LZO 2.09 | 2.106 | 610 | 870 | | [LZ4] r131 | 2.101 | 620 | 3100 | @@ -77,8 +77,8 @@ Hence, deploying one dictionary per type of data will provide the greater benefi ### Status -Zstd compression format has reached "Final status". It means it is planned to become the official stable zstd format and be tagged `v1.0`. The reason it's not yet tagged `v1.0` is that it currently performs its "validation period", making sure the format holds all its promises and nothing was missed. -Zstd library also offers legacy decoder support. Any data compressed by any version >= `v0.1` (hence including current one) remains decodable now and in the future. +Zstd compression format has reached "Final status". It means it is planned to become the official stable zstd format tagged `v1.0`. The reason it's not yet tagged `v1.0` is that it currently performs its "validation period", making sure the format holds all its promises and nothing was missed. +Zstd library also offers legacy decoder support. Any data compressed by any version >= `v0.1` is decodable now and in the future. The library has been validated using strong [fuzzer tests](https://en.wikipedia.org/wiki/Fuzz_testing), including both [internal tools](programs/fuzzer.c) and [external ones](http://lcamtuf.coredump.cx/afl). It's able to withstand hazard situations, including invalid inputs. As a consequence, Zstandard is considered safe for, and is currently used in, production environments. From 6b615d32cdc090d2648740a8bfb28b378779e137 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 29 Jul 2016 19:40:37 +0200 Subject: [PATCH 31/46] Updated API comments, following suggestions by Bryan O'Sullivan --- lib/zstd.h | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/zstd.h b/lib/zstd.h index 46338aeac..1dded9b48 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -81,7 +81,9 @@ ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity, /*! ZSTD_getDecompressedSize() : * @return : decompressed size if known, 0 otherwise. note 1 : decompressed size could be wrong or intentionally modified ! - always ensure results fit within application's authorized limits ! + Always ensure result fits within application's authorized limits ! + Each application can set its own limit, depending on local limitations. + For extended interoperability, it is recommended to support at least 8 MB. note 2 : when `0`, if precise failure cause is needed, use ZSTD_getFrameParams() to know more. */ unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); @@ -380,26 +382,30 @@ ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it. A ZSTD_DCtx object can be re-used multiple times. - First optional operation is to retrieve frame parameters, using ZSTD_getFrameParams(). - ZSTD_getFrameParams() fills a ZSTD_frameParams structure, - which can provide the minimum size of rolling buffer required to decompress data (`windowSize`), - and optionally the final size of uncompressed content. - (Note : content size is an optional info that may not be present. 0 means : content size unknown). - These information are extracted from the beginning of the compressed frame. - Size of data fragment must be large enough to ensure successful decoding, typically ZSTD_frameHeaderSize_max bytes. - @result : 0 : successful decoding, it means the ZSTD_frameParams structure is correctly filled. - >0 : `srcSize` is too small, please provide at least @result bytes on next try. + First typical operation is to retrieve frame parameters, using ZSTD_getFrameParams(). + It fills a ZSTD_frameParams structure which provide important information to correctly decode the frame, + such as the minimum rolling buffer size to allocate to decompress data (`windowSize`), + and the dictionary ID used. + (Note : content size is optional, it may not be present. 0 means : content size unknown). + Note that these values could be wrong, either because of data malformation, or because an attacker is spoofing deliberate false information. + As a consequence, check that values remain within valid application range, especially `windowSize`, before allocation. + Each application can set its own limit, depending on local restrictions. For extended interoperability, it is recommended to support at least 8 MB. + Frame parameters are extracted from the beginning of the compressed frame. + Data fragment must be large enough to ensure successful decoding, typically `ZSTD_frameHeaderSize_max` bytes. + @result : 0 : successful decoding, the `ZSTD_frameParams` structure is correctly filled. + >0 : `srcSize` is too small, please provide at least @result bytes on next attempt. errorCode, which can be tested using ZSTD_isError(). Start decompression, with ZSTD_decompressBegin() or ZSTD_decompressBegin_usingDict(). Alternatively, you can copy a prepared context, using ZSTD_copyDCtx(). Then use ZSTD_nextSrcSizeToDecompress() and ZSTD_decompressContinue() alternatively. - ZSTD_nextSrcSizeToDecompress() tells how much bytes to provide as 'srcSize' to ZSTD_decompressContinue(). - ZSTD_decompressContinue() requires this exact amount of bytes, or it will fail. + ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' to ZSTD_decompressContinue(). + ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will fail. @result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity). - It can be zero, which is not an error; it just means ZSTD_decompressContinue() has decoded some header. + It can be zero, which is not an error; it just means ZSTD_decompressContinue() has decoded some metadata item. + It can also be an error code, which can be tested with ZSTD_isError(). ZSTD_decompressContinue() needs previous data blocks during decompression, up to `windowSize`. They should preferably be located contiguously, prior to current block. @@ -412,7 +418,7 @@ ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); Context can then be reset to start a new decompression. Note : it's possible to know if next input to present is a header or a block, using ZSTD_nextInputType(). - But this information is not required to properly decode a frame. + This information is not required to properly decode a frame. == Special case : skippable frames == From ed57d8530ab90cb42055744bb8a6f17d9541bb2f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 29 Jul 2016 21:22:17 +0200 Subject: [PATCH 32/46] new seqStore --- lib/common/zstd_internal.h | 25 +++--- lib/compress/zstd_compress.c | 162 ++++++++++++++++------------------- lib/dictBuilder/zdict.c | 22 ++--- 3 files changed, 99 insertions(+), 110 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 61c5354b6..438045b76 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -183,19 +183,22 @@ typedef struct { MEM_STATIC void ZSTD_statsUpdatePrices(ZSTD_stats_t* stats, size_t litLength, const BYTE* literals, size_t offset, size_t matchLength) { (void)stats; (void)litLength; (void)literals; (void)offset; (void)matchLength; } #endif /* #if ZSTD_OPT_DEBUG == 3 */ + +typedef struct seqDef_s { + U32 offset; + U16 litLength; + U16 matchLength; +} seqDef; + + typedef struct { - void* buffer; - U32* offsetStart; - U32* offset; - BYTE* offCodeStart; + seqDef* sequences; BYTE* litStart; BYTE* lit; - U16* litLengthStart; - U16* litLength; - BYTE* llCodeStart; - U16* matchLengthStart; - U16* matchLength; - BYTE* mlCodeStart; + BYTE* llCode; + BYTE* mlCode; + BYTE* ofCode; + U32 nbSeq; U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */ U32 longLengthPos; /* opt */ @@ -223,7 +226,7 @@ typedef struct { } seqStore_t; const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); -void ZSTD_seqToCodes(const seqStore_t* seqStorePtr, size_t const nbSeq); +void ZSTD_seqToCodes(const seqStore_t* seqStorePtr); int ZSTD_isSkipFrame(ZSTD_DCtx* dctx); /* custom memory allocation functions */ diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b69dffa25..49b8b0af5 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -81,10 +81,8 @@ size_t ZSTD_compressBound(size_t srcSize) { return FSE_compressBound(srcSize) + ***************************************/ static void ZSTD_resetSeqStore(seqStore_t* ssPtr) { - ssPtr->offset = ssPtr->offsetStart; ssPtr->lit = ssPtr->litStart; - ssPtr->litLength = ssPtr->litLengthStart; - ssPtr->matchLength = ssPtr->matchLengthStart; + ssPtr->nbSeq = 0; ssPtr->longLengthID = 0; } @@ -266,6 +264,7 @@ static size_t ZSTD_resetCCtx_advanced (ZSTD_CCtx* zc, U32 const hashLog3 = (params.cParams.searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, params.cParams.windowLog); size_t const h3Size = ((size_t)1) << hashLog3; size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32); + void* ptr; /* Check if workSpace is large enough, alloc a new one if needed */ { size_t const optSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<hashTable = (U32*)(zc->workSpace); zc->chainTable = zc->hashTable + hSize; zc->hashTable3 = zc->chainTable + chainSize; - zc->seqStore.buffer = zc->hashTable3 + h3Size; - zc->hufTable = (HUF_CElt*)zc->seqStore.buffer; + ptr = zc->hashTable3 + h3Size; + zc->hufTable = (HUF_CElt*)ptr; zc->flagStaticTables = 0; - zc->seqStore.buffer = ((U32*)(zc->seqStore.buffer)) + 256; /* note : HUF_CElt* is incomplete type, size is simulated using U32 */ + ptr = ((U32*)ptr) + 256; /* note : HUF_CElt* is incomplete type, size is simulated using U32 */ zc->nextToUpdate = 1; zc->nextSrc = NULL; @@ -302,25 +301,23 @@ static size_t ZSTD_resetCCtx_advanced (ZSTD_CCtx* zc, { int i; for (i=0; irep[i] = repStartValue[i]; } if (params.cParams.strategy == ZSTD_btopt) { - zc->seqStore.litFreq = (U32*)(zc->seqStore.buffer); + zc->seqStore.litFreq = (U32*)ptr; zc->seqStore.litLengthFreq = zc->seqStore.litFreq + (1<seqStore.matchLengthFreq = zc->seqStore.litLengthFreq + (MaxLL+1); zc->seqStore.offCodeFreq = zc->seqStore.matchLengthFreq + (MaxML+1); - zc->seqStore.buffer = zc->seqStore.offCodeFreq + (MaxOff+1); - zc->seqStore.matchTable = (ZSTD_match_t*)zc->seqStore.buffer; - zc->seqStore.buffer = zc->seqStore.matchTable + ZSTD_OPT_NUM+1; - zc->seqStore.priceTable = (ZSTD_optimal_t*)zc->seqStore.buffer; - zc->seqStore.buffer = zc->seqStore.priceTable + ZSTD_OPT_NUM+1; + ptr = zc->seqStore.offCodeFreq + (MaxOff+1); + zc->seqStore.matchTable = (ZSTD_match_t*)ptr; + ptr = zc->seqStore.matchTable + ZSTD_OPT_NUM+1; + zc->seqStore.priceTable = (ZSTD_optimal_t*)ptr; + ptr = zc->seqStore.priceTable + ZSTD_OPT_NUM+1; zc->seqStore.litLengthSum = 0; } - zc->seqStore.offsetStart = (U32*)(zc->seqStore.buffer); - zc->seqStore.buffer = zc->seqStore.offsetStart + maxNbSeq; - zc->seqStore.litLengthStart = (U16*)zc->seqStore.buffer; - zc->seqStore.matchLengthStart = zc->seqStore.litLengthStart + maxNbSeq; - zc->seqStore.llCodeStart = (BYTE*) (zc->seqStore.matchLengthStart + maxNbSeq); - zc->seqStore.mlCodeStart = zc->seqStore.llCodeStart + maxNbSeq; - zc->seqStore.offCodeStart = zc->seqStore.mlCodeStart + maxNbSeq; - zc->seqStore.litStart = zc->seqStore.offCodeStart + maxNbSeq; + zc->seqStore.sequences = (seqDef*)ptr; + ptr = zc->seqStore.sequences + maxNbSeq; + zc->seqStore.llCode = (BYTE*) ptr; + zc->seqStore.mlCode = zc->seqStore.llCode + maxNbSeq; + zc->seqStore.ofCode = zc->seqStore.mlCode + maxNbSeq; + zc->seqStore.litStart = zc->seqStore.ofCode + maxNbSeq; zc->stage = ZSTDcs_init; zc->dictID = 0; @@ -525,57 +522,46 @@ static size_t ZSTD_compressLiterals (ZSTD_CCtx* zc, return lhSize+cLitSize; } +static const BYTE g_LL_Code[64] = { 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 17, 17, 18, 18, 19, 19, + 20, 20, 20, 20, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, + 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24 }; -void ZSTD_seqToCodes(const seqStore_t* seqStorePtr, size_t const nbSeq) +static const BYTE g_ML_Code[128] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, + 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 }; + + +void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) { - /* LL codes */ - { static const BYTE LL_Code[64] = { 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 16, 17, 17, 18, 18, 19, 19, - 20, 20, 20, 20, 21, 21, 21, 21, - 22, 22, 22, 22, 22, 22, 22, 22, - 23, 23, 23, 23, 23, 23, 23, 23, - 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24 }; - BYTE const LL_deltaCode = 19; - const U16* const llTable = seqStorePtr->litLengthStart; - BYTE* const llCodeTable = seqStorePtr->llCodeStart; - size_t u; - for (u=0; u63) ? (BYTE)ZSTD_highbit32(ll) + LL_deltaCode : LL_Code[ll]; - } - if (seqStorePtr->longLengthID==1) - llCodeTable[seqStorePtr->longLengthPos] = MaxLL; - } - - /* Offset codes */ - { const U32* const offsetTable = seqStorePtr->offsetStart; - BYTE* const ofCodeTable = seqStorePtr->offCodeStart; - size_t u; - for (u=0; umatchLengthStart; - BYTE* const mlCodeTable = seqStorePtr->mlCodeStart; - size_t u; - for (u=0; u127) ? (BYTE)ZSTD_highbit32(ml) + ML_deltaCode : ML_Code[ml]; - } - if (seqStorePtr->longLengthID==2) - mlCodeTable[seqStorePtr->longLengthPos] = MaxML; + BYTE const LL_deltaCode = 19; + BYTE const ML_deltaCode = 36; + const seqDef* const sequences = seqStorePtr->sequences; + BYTE* const llCodeTable = seqStorePtr->llCode; + BYTE* const ofCodeTable = seqStorePtr->ofCode; + BYTE* const mlCodeTable = seqStorePtr->mlCode; + U32 const nbSeq = seqStorePtr->nbSeq; + U32 u; + for (u=0; u 63) ? (BYTE)ZSTD_highbit32(llv) + LL_deltaCode : g_LL_Code[llv]; + ofCodeTable[u] = (BYTE)ZSTD_highbit32(sequences[u].offset); + mlCodeTable[u] = (mlv>127) ? (BYTE)ZSTD_highbit32(mlv) + ML_deltaCode : g_ML_Code[mlv]; } + if (seqStorePtr->longLengthID==1) + llCodeTable[seqStorePtr->longLengthPos] = MaxLL; + if (seqStorePtr->longLengthID==2) + mlCodeTable[seqStorePtr->longLengthPos] = MaxML; } @@ -590,17 +576,14 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* zc, FSE_CTable* CTable_OffsetBits = zc->offcodeCTable; FSE_CTable* CTable_MatchLength = zc->matchlengthCTable; U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */ - U16* const llTable = seqStorePtr->litLengthStart; - U16* const mlTable = seqStorePtr->matchLengthStart; - const U32* const offsetTable = seqStorePtr->offsetStart; - const U32* const offsetTableEnd = seqStorePtr->offset; - BYTE* const ofCodeTable = seqStorePtr->offCodeStart; - BYTE* const llCodeTable = seqStorePtr->llCodeStart; - BYTE* const mlCodeTable = seqStorePtr->mlCodeStart; + const seqDef* const sequences = seqStorePtr->sequences; + const BYTE* const ofCodeTable = seqStorePtr->ofCode; + const BYTE* const llCodeTable = seqStorePtr->llCode; + const BYTE* const mlCodeTable = seqStorePtr->mlCode; BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstCapacity; BYTE* op = ostart; - size_t const nbSeq = offsetTableEnd - offsetTable; + size_t const nbSeq = seqStorePtr->nbSeq; BYTE* seqHead; /* Compress literals */ @@ -625,7 +608,7 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* zc, #define MAX_SEQ_FOR_STATIC_FSE 1000 /* convert length/distances into codes */ - ZSTD_seqToCodes(seqStorePtr, nbSeq); + ZSTD_seqToCodes(seqStorePtr); /* CTable for Literal Lengths */ { U32 max = MaxLL; @@ -715,11 +698,11 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* zc, FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]); FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]); FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]); - BIT_addBits(&blockStream, llTable[nbSeq-1], LL_bits[llCodeTable[nbSeq-1]]); + BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]); if (MEM_32bits()) BIT_flushBits(&blockStream); - BIT_addBits(&blockStream, mlTable[nbSeq-1], ML_bits[mlCodeTable[nbSeq-1]]); + BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]); if (MEM_32bits()) BIT_flushBits(&blockStream); - BIT_addBits(&blockStream, offsetTable[nbSeq-1], ofCodeTable[nbSeq-1]); + BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]); BIT_flushBits(&blockStream); { size_t n; @@ -737,11 +720,11 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* zc, FSE_encodeSymbol(&blockStream, &stateLitLength, llCode); /* 16 */ /* 33 */ if (MEM_32bits() || (ofBits+mlBits+llBits >= 64-7-(LLFSELog+MLFSELog+OffFSELog))) BIT_flushBits(&blockStream); /* (7)*/ - BIT_addBits(&blockStream, llTable[n], llBits); + BIT_addBits(&blockStream, sequences[n].litLength, llBits); if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream); - BIT_addBits(&blockStream, mlTable[n], mlBits); + BIT_addBits(&blockStream, sequences[n].matchLength, mlBits); if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/ - BIT_addBits(&blockStream, offsetTable[n], ofBits); /* 31 */ + BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ BIT_flushBits(&blockStream); /* (7)*/ } } @@ -782,6 +765,7 @@ MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const v printf("Cpos %6u :%5u literals & match %3u bytes at distance %6u \n", pos, (U32)litLength, (U32)matchCode+MINMATCH, (U32)offsetCode); #endif + U32 const nbSeq = seqStorePtr->nbSeq; ZSTD_statsUpdatePrices(&seqStorePtr->stats, litLength, (const BYTE*)literals, offsetCode, matchCode); /* debug only */ /* copy Literals */ @@ -789,15 +773,17 @@ MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const v seqStorePtr->lit += litLength; /* literal Length */ - if (litLength>0xFFFF) { seqStorePtr->longLengthID = 1; seqStorePtr->longLengthPos = (U32)(seqStorePtr->litLength - seqStorePtr->litLengthStart); } - *seqStorePtr->litLength++ = (U16)litLength; + if (litLength>0xFFFF) { seqStorePtr->longLengthID = 1; seqStorePtr->longLengthPos = nbSeq; } + seqStorePtr->sequences[nbSeq].litLength = (U16)litLength; /* match offset */ - *(seqStorePtr->offset++) = offsetCode + 1; + seqStorePtr->sequences[nbSeq].offset = offsetCode + 1; /* match Length */ - if (matchCode>0xFFFF) { seqStorePtr->longLengthID = 2; seqStorePtr->longLengthPos = (U32)(seqStorePtr->matchLength - seqStorePtr->matchLengthStart); } - *seqStorePtr->matchLength++ = (U16)matchCode; + if (matchCode>0xFFFF) { seqStorePtr->longLengthID = 2; seqStorePtr->longLengthPos = nbSeq; } + seqStorePtr->sequences[nbSeq].matchLength = (U16)matchCode; + + seqStorePtr->nbSeq++; } diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index d77f7ad9a..d16e1efce 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -594,28 +594,28 @@ static void ZDICT_countEStats(EStats_ress_t esr, ZSTD_parameters params, } /* seqStats */ - { size_t const nbSeq = (size_t)(seqStorePtr->offset - seqStorePtr->offsetStart); - ZSTD_seqToCodes(seqStorePtr, nbSeq); + { U32 const nbSeq = seqStorePtr->nbSeq; + ZSTD_seqToCodes(seqStorePtr); - { const BYTE* codePtr = seqStorePtr->offCodeStart; - size_t u; + { const BYTE* codePtr = seqStorePtr->ofCode; + U32 u; for (u=0; umlCodeStart; - size_t u; + { const BYTE* codePtr = seqStorePtr->mlCode; + U32 u; for (u=0; ullCodeStart; - size_t u; + { const BYTE* codePtr = seqStorePtr->llCode; + U32 u; for (u=0; uoffsetStart; - U32 offset1 = offsetPtr[0] - 3; - U32 offset2 = offsetPtr[1] - 3; + { const seqDef* const seq = seqStorePtr->sequences; + U32 offset1 = seq[0].offset - 3; + U32 offset2 = seq[1].offset - 3; if (offset1 >= MAXREPOFFSET) offset1 = 0; if (offset2 >= MAXREPOFFSET) offset2 = 0; repOffsets[offset1] += 3; From c0ce4f1211d72b6ea2247be3a1e82bccf71ea301 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Jul 2016 00:55:13 +0200 Subject: [PATCH 33/46] slightly improved compression speed --- lib/common/zstd_internal.h | 2 +- lib/compress/zstd_compress.c | 27 +++++++++++++-------------- lib/dictBuilder/zdict.c | 2 +- lib/zstd.h | 15 +++++++++------ 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 438045b76..d0391871b 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -192,13 +192,13 @@ typedef struct seqDef_s { typedef struct { + seqDef* sequencesStart; seqDef* sequences; BYTE* litStart; BYTE* lit; BYTE* llCode; BYTE* mlCode; BYTE* ofCode; - U32 nbSeq; U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */ U32 longLengthPos; /* opt */ diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 49b8b0af5..07f23b5ab 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -82,7 +82,7 @@ size_t ZSTD_compressBound(size_t srcSize) { return FSE_compressBound(srcSize) + static void ZSTD_resetSeqStore(seqStore_t* ssPtr) { ssPtr->lit = ssPtr->litStart; - ssPtr->nbSeq = 0; + ssPtr->sequences = ssPtr->sequencesStart; ssPtr->longLengthID = 0; } @@ -312,8 +312,8 @@ static size_t ZSTD_resetCCtx_advanced (ZSTD_CCtx* zc, ptr = zc->seqStore.priceTable + ZSTD_OPT_NUM+1; zc->seqStore.litLengthSum = 0; } - zc->seqStore.sequences = (seqDef*)ptr; - ptr = zc->seqStore.sequences + maxNbSeq; + zc->seqStore.sequencesStart = (seqDef*)ptr; + ptr = zc->seqStore.sequencesStart + maxNbSeq; zc->seqStore.llCode = (BYTE*) ptr; zc->seqStore.mlCode = zc->seqStore.llCode + maxNbSeq; zc->seqStore.ofCode = zc->seqStore.mlCode + maxNbSeq; @@ -545,11 +545,11 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) { BYTE const LL_deltaCode = 19; BYTE const ML_deltaCode = 36; - const seqDef* const sequences = seqStorePtr->sequences; + const seqDef* const sequences = seqStorePtr->sequencesStart; BYTE* const llCodeTable = seqStorePtr->llCode; BYTE* const ofCodeTable = seqStorePtr->ofCode; BYTE* const mlCodeTable = seqStorePtr->mlCode; - U32 const nbSeq = seqStorePtr->nbSeq; + U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); U32 u; for (u=0; uoffcodeCTable; FSE_CTable* CTable_MatchLength = zc->matchlengthCTable; U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */ - const seqDef* const sequences = seqStorePtr->sequences; + const seqDef* const sequences = seqStorePtr->sequencesStart; const BYTE* const ofCodeTable = seqStorePtr->ofCode; const BYTE* const llCodeTable = seqStorePtr->llCode; const BYTE* const mlCodeTable = seqStorePtr->mlCode; BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstCapacity; BYTE* op = ostart; - size_t const nbSeq = seqStorePtr->nbSeq; + size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; BYTE* seqHead; /* Compress literals */ @@ -765,7 +765,6 @@ MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const v printf("Cpos %6u :%5u literals & match %3u bytes at distance %6u \n", pos, (U32)litLength, (U32)matchCode+MINMATCH, (U32)offsetCode); #endif - U32 const nbSeq = seqStorePtr->nbSeq; ZSTD_statsUpdatePrices(&seqStorePtr->stats, litLength, (const BYTE*)literals, offsetCode, matchCode); /* debug only */ /* copy Literals */ @@ -773,17 +772,17 @@ MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const v seqStorePtr->lit += litLength; /* literal Length */ - if (litLength>0xFFFF) { seqStorePtr->longLengthID = 1; seqStorePtr->longLengthPos = nbSeq; } - seqStorePtr->sequences[nbSeq].litLength = (U16)litLength; + if (litLength>0xFFFF) { seqStorePtr->longLengthID = 1; seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); } + seqStorePtr->sequences[0].litLength = (U16)litLength; /* match offset */ - seqStorePtr->sequences[nbSeq].offset = offsetCode + 1; + seqStorePtr->sequences[0].offset = offsetCode + 1; /* match Length */ - if (matchCode>0xFFFF) { seqStorePtr->longLengthID = 2; seqStorePtr->longLengthPos = nbSeq; } - seqStorePtr->sequences[nbSeq].matchLength = (U16)matchCode; + if (matchCode>0xFFFF) { seqStorePtr->longLengthID = 2; seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); } + seqStorePtr->sequences[0].matchLength = (U16)matchCode; - seqStorePtr->nbSeq++; + seqStorePtr->sequences++; } diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index d16e1efce..84272d194 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -594,7 +594,7 @@ static void ZDICT_countEStats(EStats_ress_t esr, ZSTD_parameters params, } /* seqStats */ - { U32 const nbSeq = seqStorePtr->nbSeq; + { U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); ZSTD_seqToCodes(seqStorePtr); { const BYTE* codePtr = seqStorePtr->ofCode; diff --git a/lib/zstd.h b/lib/zstd.h index 1dded9b48..6b9ed463d 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -80,16 +80,19 @@ ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity, /*! ZSTD_getDecompressedSize() : * @return : decompressed size if known, 0 otherwise. - note 1 : decompressed size could be wrong or intentionally modified ! - Always ensure result fits within application's authorized limits ! - Each application can set its own limit, depending on local limitations. - For extended interoperability, it is recommended to support at least 8 MB. - note 2 : when `0`, if precise failure cause is needed, use ZSTD_getFrameParams() to know more. */ +* note 1 : decompressed size could be wrong or intentionally modified ! +* Always ensure result fits within application's authorized limits ! +* Each application can set its own limit, depending on local restrictions. +* For extended interoperability, it is recommended to support at least 8 MB. +* note 2 : when `0`, if precise failure cause is needed, use ZSTD_getFrameParams() to know more. +* note 3 : when `0`, and if no external guarantee about maximum possible decompressed size, +* it's necessary to use "streaming mode" to decompress data. */ unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); /*! ZSTD_decompress() : `compressedSize` : must be the _exact_ size of compressed input, otherwise decompression will fail. - `dstCapacity` must be equal or larger than originalSize. + `dstCapacity` must be equal or larger than originalSize (see ZSTD_getDecompressedSize() ). + If maximum possible content size is unknown, use streaming mode to decompress data. @return : the number of bytes decompressed into `dst` (<= `dstCapacity`), or an errorCode if it fails (which can be tested using ZSTD_isError()) */ ZSTDLIB_API size_t ZSTD_decompress( void* dst, size_t dstCapacity, From 70a9ff4af31b0580abde8f8abbd4c1921b37340b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Jul 2016 01:09:14 +0200 Subject: [PATCH 34/46] fixed too large selectivity level, reported by Ilona Papava --- lib/dictBuilder/zdict.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 84272d194..e1bece3a7 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -879,7 +879,7 @@ size_t ZDICT_trainFromBuffer_unsafe( U32 const dictListSize = MAX(MAX(DICTLISTSIZE, nbSamples), (U32)(maxDictSize/16)); dictItem* const dictList = (dictItem*)malloc(dictListSize * sizeof(*dictList)); unsigned const selectivity = params.selectivityLevel == 0 ? g_selectivity_default : params.selectivityLevel; - unsigned const minRep = nbSamples >> selectivity; + unsigned const minRep = (selectivity > 30) ? 1 : nbSamples >> selectivity; size_t const targetDictSize = maxDictSize; size_t const samplesBuffSize = ZDICT_totalSampleSize(samplesSizes, nbSamples); size_t dictSize = 0; From 3c6b808870672c0d20566ffd62c0725e0f470d23 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Jul 2016 03:20:47 +0200 Subject: [PATCH 35/46] minor decompression speed gains --- lib/compress/zstd_compress.c | 6 +++--- lib/decompress/zstd_decompress.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 07f23b5ab..d43054d86 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -707,12 +707,12 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* zc, { size_t n; for (n=nbSeq-2 ; n24)) BIT_flushBits(&blockStream); BIT_addBits(&blockStream, sequences[n].matchLength, mlBits); if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/ - BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ + BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ BIT_flushBits(&blockStream); /* (7)*/ } } diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index e1ac20049..a73543ced 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -583,7 +583,7 @@ typedef struct { FSE_DState_t stateLL; FSE_DState_t stateOffb; FSE_DState_t stateML; - size_t prevOffset[ZSTD_REP_NUM]; + U32 prevOffset[ZSTD_REP_NUM]; } seqState_t; @@ -629,7 +629,7 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState) if (ofCode <= 1) { if ((llCode == 0) & (offset <= 1)) offset = 1-offset; if (offset) { - size_t const temp = seqState->prevOffset[offset]; + U32 const temp = seqState->prevOffset[offset]; if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; seqState->prevOffset[1] = seqState->prevOffset[0]; seqState->prevOffset[0] = offset = temp; @@ -639,7 +639,7 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState) } else { seqState->prevOffset[2] = seqState->prevOffset[1]; seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset; + seqState->prevOffset[0] = (U32)offset; } seq.offset = offset; } @@ -763,7 +763,7 @@ static size_t ZSTD_decompressSequences( if (nbSeq) { seqState_t seqState; dctx->fseEntropy = 1; - { U32 i; for (i=0; irep[i]; } + memcpy(seqState.prevOffset, dctx->rep, sizeof(seqState.prevOffset)); { size_t const errorCode = BIT_initDStream(&(seqState.DStream), ip, iend-ip); if (ERR_isError(errorCode)) return ERROR(corruption_detected); } FSE_initDState(&(seqState.stateLL), &(seqState.DStream), DTableLL); @@ -781,7 +781,7 @@ static size_t ZSTD_decompressSequences( /* check if reached exact end */ if (nbSeq) return ERROR(corruption_detected); /* save reps for next block */ - { U32 i; for (i=0; irep[i] = (U32)(seqState.prevOffset[i]); } + memcpy(dctx->rep, seqState.prevOffset, sizeof(seqState.prevOffset)); } /* last literal segment */ From 761f8dbbd22e0eb547fceda0d560faab180a72f4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Jul 2016 11:43:53 +0200 Subject: [PATCH 36/46] back to normal table cell copy --- lib/decompress/zstd_decompress.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index a73543ced..a39708356 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -763,7 +763,7 @@ static size_t ZSTD_decompressSequences( if (nbSeq) { seqState_t seqState; dctx->fseEntropy = 1; - memcpy(seqState.prevOffset, dctx->rep, sizeof(seqState.prevOffset)); + { U32 i; for (i=0; irep[i]; } { size_t const errorCode = BIT_initDStream(&(seqState.DStream), ip, iend-ip); if (ERR_isError(errorCode)) return ERROR(corruption_detected); } FSE_initDState(&(seqState.stateLL), &(seqState.DStream), DTableLL); @@ -781,7 +781,7 @@ static size_t ZSTD_decompressSequences( /* check if reached exact end */ if (nbSeq) return ERROR(corruption_detected); /* save reps for next block */ - memcpy(dctx->rep, seqState.prevOffset, sizeof(seqState.prevOffset)); + { U32 i; for (i=0; irep[i] = (U32)(seqState.prevOffset[i]); } } /* last literal segment */ From f714f59c1660bcde67114c7285046cfc4dd1cee8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Jul 2016 12:05:28 +0200 Subject: [PATCH 37/46] fixed visual warning --- lib/decompress/zstd_decompress.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index a39708356..76c1ca196 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -632,7 +632,8 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState) U32 const temp = seqState->prevOffset[offset]; if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = offset = temp; + seqState->prevOffset[0] = temp; + offset = temp; } else { offset = seqState->prevOffset[0]; } From f34035ecfbc44ef8f97ed71f0a004902a100f806 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Jul 2016 13:12:34 +0200 Subject: [PATCH 38/46] correction on offset history (swap when llCode==0) --- zstd_compression_format.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zstd_compression_format.md b/zstd_compression_format.md index 79f9620e7..b4f8b8af4 100644 --- a/zstd_compression_format.md +++ b/zstd_compression_format.md @@ -1081,9 +1081,9 @@ As seen in [Offset Codes], the first 3 values define a repeated offset. They are sorted in recency order, with 1 meaning "most recent one". There is an exception though, when current sequence's literal length is `0`. -In which case, 1 would just make previous match longer. -Therefore, in such case, 1 means in fact 2, and 2 is impossible. -Meaning of 3 is unmodified. +In which case, the first 2 values are swapped, +meaning `2` refers to the most recent offset, +while `1` refers to the second most recent offset, Repeat offsets start with the following values : 1, 4 and 8 (in order). From 3b2bd1d11c0982e6df4f0d8bc9f1cb53ed6db6e3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Jul 2016 13:21:41 +0200 Subject: [PATCH 39/46] zstd_opt uses same tables as zstd_compress --- lib/compress/zstd_compress.c | 36 ++++++++++++++++---------------- lib/compress/zstd_opt.h | 40 ++++-------------------------------- 2 files changed, 22 insertions(+), 54 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d43054d86..56c63601e 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -522,23 +522,23 @@ static size_t ZSTD_compressLiterals (ZSTD_CCtx* zc, return lhSize+cLitSize; } -static const BYTE g_LL_Code[64] = { 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 16, 17, 17, 18, 18, 19, 19, - 20, 20, 20, 20, 21, 21, 21, 21, - 22, 22, 22, 22, 22, 22, 22, 22, - 23, 23, 23, 23, 23, 23, 23, 23, - 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24 }; +static const BYTE LL_Code[64] = { 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 17, 17, 18, 18, 19, 19, + 20, 20, 20, 20, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, + 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24 }; -static const BYTE g_ML_Code[128] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, - 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, - 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 }; +static const BYTE ML_Code[128] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, + 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 }; void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) @@ -554,9 +554,9 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) for (u=0; u 63) ? (BYTE)ZSTD_highbit32(llv) + LL_deltaCode : g_LL_Code[llv]; + llCodeTable[u] = (llv> 63) ? (BYTE)ZSTD_highbit32(llv) + LL_deltaCode : LL_Code[llv]; ofCodeTable[u] = (BYTE)ZSTD_highbit32(sequences[u].offset); - mlCodeTable[u] = (mlv>127) ? (BYTE)ZSTD_highbit32(mlv) + ML_deltaCode : g_ML_Code[mlv]; + mlCodeTable[u] = (mlv>127) ? (BYTE)ZSTD_highbit32(mlv) + ML_deltaCode : ML_Code[mlv]; } if (seqStorePtr->longLengthID==1) llCodeTable[seqStorePtr->longLengthPos] = MaxLL; diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index 9496ed358..3eac1ac87 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -134,15 +134,7 @@ FORCE_INLINE U32 ZSTD_getLiteralPrice(seqStore_t* ssPtr, U32 litLength, const BY } /* literal Length */ - { static const BYTE LL_Code[64] = { 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 16, 17, 17, 18, 18, 19, 19, - 20, 20, 20, 20, 21, 21, 21, 21, - 22, 22, 22, 22, 22, 22, 22, 22, - 23, 23, 23, 23, 23, 23, 23, 23, - 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24 }; - const BYTE LL_deltaCode = 19; + { const BYTE LL_deltaCode = 19; const BYTE llCode = (litLength>63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; price += LL_bits[llCode] + ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[llCode]+1); } @@ -158,15 +150,7 @@ FORCE_INLINE U32 ZSTD_getPrice(seqStore_t* seqStorePtr, U32 litLength, const BYT U32 price = offCode + seqStorePtr->log2offCodeSum - ZSTD_highbit32(seqStorePtr->offCodeFreq[offCode]+1); /* match Length */ - { static const BYTE ML_Code[128] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, - 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, - 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 }; - const BYTE ML_deltaCode = 36; + { const BYTE ML_deltaCode = 36; const BYTE mlCode = (matchLength>127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; price += ML_bits[mlCode] + seqStorePtr->log2matchLengthSum - ZSTD_highbit32(seqStorePtr->matchLengthFreq[mlCode]+1); } @@ -185,15 +169,7 @@ MEM_STATIC void ZSTD_updatePrice(seqStore_t* seqStorePtr, U32 litLength, const B seqStorePtr->litFreq[literals[u]]++; /* literal Length */ - { static const BYTE LL_Code[64] = { 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 16, 17, 17, 18, 18, 19, 19, - 20, 20, 20, 20, 21, 21, 21, 21, - 22, 22, 22, 22, 22, 22, 22, 22, - 23, 23, 23, 23, 23, 23, 23, 23, - 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24 }; - const BYTE LL_deltaCode = 19; + { const BYTE LL_deltaCode = 19; const BYTE llCode = (litLength>63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; seqStorePtr->litLengthFreq[llCode]++; seqStorePtr->litLengthSum++; @@ -206,15 +182,7 @@ MEM_STATIC void ZSTD_updatePrice(seqStore_t* seqStorePtr, U32 litLength, const B } /* match Length */ - { static const BYTE ML_Code[128] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, - 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, - 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 }; - const BYTE ML_deltaCode = 36; + { const BYTE ML_deltaCode = 36; const BYTE mlCode = (matchLength>127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; seqStorePtr->matchLengthFreq[mlCode]++; seqStorePtr->matchLengthSum++; From 66f69e58d29495e73e789e82f54ba26cc8620d48 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 30 Jul 2016 15:32:47 +0200 Subject: [PATCH 40/46] restore decompression speed on fizzle --- lib/decompress/zstd_decompress.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 76c1ca196..e1ac20049 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -583,7 +583,7 @@ typedef struct { FSE_DState_t stateLL; FSE_DState_t stateOffb; FSE_DState_t stateML; - U32 prevOffset[ZSTD_REP_NUM]; + size_t prevOffset[ZSTD_REP_NUM]; } seqState_t; @@ -629,18 +629,17 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState) if (ofCode <= 1) { if ((llCode == 0) & (offset <= 1)) offset = 1-offset; if (offset) { - U32 const temp = seqState->prevOffset[offset]; + size_t const temp = seqState->prevOffset[offset]; if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = temp; - offset = temp; + seqState->prevOffset[0] = offset = temp; } else { offset = seqState->prevOffset[0]; } } else { seqState->prevOffset[2] = seqState->prevOffset[1]; seqState->prevOffset[1] = seqState->prevOffset[0]; - seqState->prevOffset[0] = (U32)offset; + seqState->prevOffset[0] = offset; } seq.offset = offset; } From 235911e13fb92b5c75df380786da73d7db65accd Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 31 Jul 2016 01:32:48 +0200 Subject: [PATCH 41/46] removed "avg" evaluation from bench -q removed "sleeping" notification from bench -q --- programs/bench.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index a463576b7..f4bff8835 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -202,7 +202,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, /* overheat protection */ if (UTIL_clockSpanMicro(coolTime, ticksPerSecond) > ACTIVEPERIOD_MICROSEC) { - DISPLAY("\rcooling down ... \r"); + DISPLAYLEVEL(2, "\rcooling down ... \r"); UTIL_sleep(COOLPERIOD_SEC); UTIL_getTime(&coolTime); } @@ -352,7 +352,7 @@ static void BMK_benchCLevel(void* srcBuffer, size_t benchedSize, const size_t* fileSizes, unsigned nbFiles, const void* dictBuffer, size_t dictBufferSize) { - benchResult_t result, total; + benchResult_t result; int l; const char* pch = strrchr(displayName, '\\'); /* Windows */ @@ -362,7 +362,6 @@ static void BMK_benchCLevel(void* srcBuffer, size_t benchedSize, SET_HIGH_PRIORITY; memset(&result, 0, sizeof(result)); - memset(&total, 0, sizeof(total)); if (g_displayLevel == 1 && !g_additionalParam) DISPLAY("bench %s %s: input %u bytes, %i iterations, %u KB blocks\n", ZSTD_VERSION_STRING, ZSTD_GIT_COMMIT_STRING, (U32)benchedSize, g_nbIterations, (U32)(g_blockSize>>10)); @@ -379,18 +378,7 @@ static void BMK_benchCLevel(void* srcBuffer, size_t benchedSize, DISPLAY("%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s (param=%d)\n", -l, (int)result.cSize, result.ratio, result.cSpeed, result.dSpeed, displayName, g_additionalParam); else DISPLAY("%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s\n", -l, (int)result.cSize, result.ratio, result.cSpeed, result.dSpeed, displayName); - total.cSize += result.cSize; - total.cSpeed += result.cSpeed; - total.dSpeed += result.dSpeed; - total.ratio += result.ratio; } } - if (g_displayLevel == 1 && cLevelLast > cLevel) { - total.cSize /= 1+cLevelLast-cLevel; - total.cSpeed /= 1+cLevelLast-cLevel; - total.dSpeed /= 1+cLevelLast-cLevel; - total.ratio /= 1+cLevelLast-cLevel; - DISPLAY("avg%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s\n", (int)total.cSize, total.ratio, total.cSpeed, total.dSpeed, displayName); - } } From 8cebfd1d26a7e5b9e80aeec44f5bf8a99b04247f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 31 Jul 2016 01:59:23 +0200 Subject: [PATCH 42/46] fix attempt on test-zstd-speed --- lib/common/zstd_internal.h | 2 +- tests/test-zstd-speed.py | 39 ++++++++++++++++++++++---------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index d0391871b..0a1935a98 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -65,7 +65,7 @@ #endif #define ZSTD_OPT_NUM (1<<12) -#define ZSTD_DICT_MAGIC 0xEC30A437 /* v0.7 */ +#define ZSTD_DICT_MAGIC 0xEC30A437 /* v0.7+ */ #define ZSTD_REP_NUM 3 /* number of repcodes */ #define ZSTD_REP_CHECK (ZSTD_REP_NUM-0) /* number of repcodes to check by the optimal parser */ diff --git a/tests/test-zstd-speed.py b/tests/test-zstd-speed.py index 562d0790c..c517097a5 100755 --- a/tests/test-zstd-speed.py +++ b/tests/test-zstd-speed.py @@ -3,10 +3,9 @@ import argparse import os import string +import subprocess import time import traceback -import subprocess -import signal default_repo_url = 'https://github.com/Cyan4973/zstd.git' @@ -25,7 +24,8 @@ def log(text): def execute(command, print_command=True, print_output=False, print_error=True, param_shell=True): if print_command: log("> " + command) - popen = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=param_shell, cwd=execute.cwd) + popen = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + shell=param_shell, cwd=execute.cwd) stdout = popen.communicate()[0] stdout_lines = stdout.splitlines() if print_output: @@ -40,8 +40,8 @@ execute.cwd = None def does_command_exist(command): try: - execute(command, verbose, False, False); - except Exception as e: + execute(command, verbose, False, False) + except Exception: return False return True @@ -59,13 +59,17 @@ def send_email(emails, topic, text, have_mutt, have_mail): log("e-mail cannot be sent (mail or mutt not found)") -def send_email_with_attachments(branch, commit, last_commit, args, text, results_files, logFileName, have_mutt, have_mail): +def send_email_with_attachments(branch, commit, last_commit, args, text, results_files, + logFileName, have_mutt, have_mail): with open(logFileName, "w") as myfile: myfile.writelines(text) myfile.close() - email_topic = '%s:%s Warning for %s:%s last_commit=%s speed<%s ratio<%s' % (email_header, pid, branch, commit, last_commit, args.lowerLimit, args.ratioLimit) + email_topic = '%s:%s Warning for %s:%s last_commit=%s speed<%s ratio<%s' \ + % (email_header, pid, branch, commit, last_commit, + args.lowerLimit, args.ratioLimit) if have_mutt: - execute('mutt -s "' + email_topic + '" ' + args.emails + ' -a ' + results_files + ' < ' + logFileName) + execute('mutt -s "' + email_topic + '" ' + args.emails + ' -a ' + results_files + + ' < ' + logFileName) elif have_mail: execute('mail -s "' + email_topic + '" ' + args.emails + ' < ' + logFileName) else: @@ -98,11 +102,11 @@ def get_last_results(resultsFileName): csize = [] cspeed = [] dspeed = [] - with open(resultsFileName,'r') as f: + with open(resultsFileName, 'r') as f: for line in f: words = line.split() if len(words) == 2: # branch + commit - commit = words[1]; + commit = words[1] csize = [] cspeed = [] dspeed = [] @@ -113,15 +117,18 @@ def get_last_results(resultsFileName): return commit, csize, cspeed, dspeed -def benchmark_and_compare(branch, commit, last_commit, args, executableName, resultsFileName, testFilePath, fileName, last_csize, last_cspeed, last_dspeed): +def benchmark_and_compare(branch, commit, last_commit, args, executableName, resultsFileName, + testFilePath, fileName, last_csize, last_cspeed, last_dspeed): sleepTime = 30 while os.getloadavg()[0] > args.maxLoadAvg: - log("WARNING: bench loadavg=%.2f is higher than %s, sleeping for %s seconds" % (os.getloadavg()[0], args.maxLoadAvg, sleepTime)) + log("WARNING: bench loadavg=%.2f is higher than %s, sleeping for %s seconds" + % (os.getloadavg()[0], args.maxLoadAvg, sleepTime)) time.sleep(sleepTime) start_load = str(os.getloadavg()) - result = execute('programs/%s -qi5b1e%s %s' % (executableName, args.lastCLevel, testFilePath), print_output=True) + result = execute('programs/%s -qi5b1e%s %s' % (executableName, args.lastCLevel, testFilePath), + print_output=True) end_load = str(os.getloadavg()) - linesExpected = args.lastCLevel + 2; + linesExpected = args.lastCLevel + 1 if len(result) != linesExpected: raise RuntimeError("ERROR: number of result lines=%d is different that expected %d\n%s" % (len(result), linesExpected, '\n'.join(result))) with open(resultsFileName, "a") as myfile: @@ -217,8 +224,8 @@ if __name__ == '__main__': exit(1) # check availability of e-mail senders - have_mutt = does_command_exist("mutt -h"); - have_mail = does_command_exist("mail -V"); + have_mutt = does_command_exist("mutt -h") + have_mail = does_command_exist("mail -V") if not have_mutt and not have_mail: log("ERROR: e-mail senders 'mail' or 'mutt' not found") exit(1) From 0d07ec0c0c9dfddcae85d5685d32eff0d74ecbbb Mon Sep 17 00:00:00 2001 From: jrmarino Date: Sat, 30 Jul 2016 19:10:36 -0500 Subject: [PATCH 43/46] Enable build on FreeBSD ports (includes DragonFly BSD) Zstd has been introduced to FreeBSD ports (http://www.freshports.org/archivers/zstd/) which DragonFly BSD also uses. FreeBSD and DragonFly use the install targets (albeit modified in some cases) so they must be added to the associated Makefile filters. --- Makefile | 8 ++++---- lib/Makefile | 4 ++-- programs/Makefile | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 9f5e1ebfa..c7e79832a 100644 --- a/Makefile +++ b/Makefile @@ -70,10 +70,10 @@ clean: @echo Cleaning completed -#------------------------------------------------------------------------ -#make install is validated only for Linux, OSX, kFreeBSD and Hurd targets -#------------------------------------------------------------------------ -ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU)) +#---------------------------------------------------------------------------------- +#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)) HOST_OS = POSIX install: $(MAKE) -C $(ZSTDDIR) $@ diff --git a/lib/Makefile b/lib/Makefile index 76731abc1..6e0b014b3 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -98,8 +98,8 @@ clean: @echo Cleaning library completed #------------------------------------------------------------------------ -#make install is validated only for Linux, OSX, kFreeBSD and Hurd targets -ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU)) +#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)) libzstd.pc: libzstd.pc: libzstd.pc.in diff --git a/programs/Makefile b/programs/Makefile index a55268a01..e9c99fd97 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -154,10 +154,10 @@ clean: @echo Cleaning completed -#--------------------------------------------------------------------------------- -#make install is validated only for Linux, OSX, kFreeBSD, Hurd and OpenBSD targets -#--------------------------------------------------------------------------------- -ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD)) +#---------------------------------------------------------------------------------- +#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)) HOST_OS = POSIX install: zstd @echo Installing binaries From 8f29e8e0e47605e7d78bcca720340539fc44af3e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 31 Jul 2016 02:43:17 +0200 Subject: [PATCH 44/46] updated NEWS --- NEWS | 1 + tests/.gitignore | 1 + 2 files changed, 2 insertions(+) diff --git a/NEWS b/NEWS index d525bc0ff..16a4fb8ff 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,6 @@ v0.8.0 Improved : better speed on clang and gcc -O2, thanks to Eric Biggers +New : Build on FreeBSD and DragonFly, thanks to JrMarino Changed : modified API : ZSTD_compressEnd() Fixed : legacy mode with ZSTD_HEAPMODE=0, by Christopher Bergqvist Fixed : premature end of frame when zero-sized raw block, reported by Eric Biggers diff --git a/tests/.gitignore b/tests/.gitignore index 15c016090..bda081a64 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -5,3 +5,4 @@ versionsTest # Local script startSpeedTest +speedTest.pid From 917fe188f15fcfd4406ff991d4bf8c60a6ccbfc2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 31 Jul 2016 04:01:57 +0200 Subject: [PATCH 45/46] Implemented repOffset "minus 1" on ll==0 --- lib/compress/zstd_opt.h | 28 ++++++++++++++++------------ lib/decompress/zstd_decompress.c | 4 ++-- zstd_compression_format.md | 19 ++++++++----------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/lib/compress/zstd_opt.h b/lib/compress/zstd_opt.h index 3eac1ac87..1946a3ae5 100644 --- a/lib/compress/zstd_opt.h +++ b/lib/compress/zstd_opt.h @@ -453,7 +453,7 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, /* check repCode */ { U32 i; - for (i=0; i last_pos || price < opt[mlen].price) @@ -544,9 +544,9 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, best_mlen = minMatch; { U32 i; - for (i=0; i litlen) { @@ -661,7 +663,8 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ rep[1] = rep[0]; rep[0] = best_off; } - if (litLength == 0 && offset<=1) offset = 1-offset; + if ((litLength == 0) & (offset==0)) offset = rep[1]; /* protection, but should never happen */ + if ((litLength == 0) & (offset<=2)) offset--; } ZSTD_LOG_ENCODE("%d/%d: ENCODE literals=%d mlen=%d off=%d rep[0]=%d rep[1]=%d\n", (int)(ip-base), (int)(iend-base), (int)(litLength), (int)mlen, (int)(offset), (int)rep[0], (int)rep[1]); @@ -746,7 +749,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, /* check repCode */ { U32 i; - for (i=0; i litlen) { @@ -973,8 +976,9 @@ _storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ if (offset != 1) rep[2] = rep[1]; rep[1] = rep[0]; rep[0] = best_off; - } - if (litLength == 0 && offset<=1) offset = 1-offset; + } + if ((litLength==0) & (offset==0)) offset = rep[1]; /* protection, but should never happen */ + if ((litLength==0) & (offset<=2)) offset --; } ZSTD_LOG_ENCODE("%d/%d: ENCODE literals=%d mlen=%d off=%d rep[0]=%d rep[1]=%d\n", (int)(ip-base), (int)(iend-base), (int)(litLength), (int)mlen, (int)(offset), (int)rep[0], (int)rep[1]); diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index e1ac20049..958d63692 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -627,9 +627,9 @@ static seq_t ZSTD_decodeSequence(seqState_t* seqState) } if (ofCode <= 1) { - if ((llCode == 0) & (offset <= 1)) offset = 1-offset; + offset += (llCode==0); if (offset) { - size_t const temp = seqState->prevOffset[offset]; + size_t const temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1]; seqState->prevOffset[1] = seqState->prevOffset[0]; seqState->prevOffset[0] = offset = temp; diff --git a/zstd_compression_format.md b/zstd_compression_format.md index b4f8b8af4..da5c94afd 100644 --- a/zstd_compression_format.md +++ b/zstd_compression_format.md @@ -1081,11 +1081,11 @@ As seen in [Offset Codes], the first 3 values define a repeated offset. They are sorted in recency order, with 1 meaning "most recent one". There is an exception though, when current sequence's literal length is `0`. -In which case, the first 2 values are swapped, -meaning `2` refers to the most recent offset, -while `1` refers to the second most recent offset, +In which case, repcodes are "pushed by one", +so 1 becomes 2, 2 becomes 3, +and 3 becomes "offset_1 - 1_byte". -Repeat offsets start with the following values : 1, 4 and 8 (in order). +On first block, offset history is populated by the following values : 1, 4 and 8 (in order). Then each block receives its start value from previous compressed block. Note that non-compressed blocks are skipped, @@ -1095,14 +1095,11 @@ they do not contribute to offset history. ###### Offset updates rules -When the new offset is a normal one, -offset history is simply translated by one position, -with the new offset taking first spot. +New offset take the lead in offset history, +up to its previous place if it was already present. -- When repeat offset 1 (most recent) is used, history is unmodified. -- When repeat offset 2 is used, it's swapped with offset 1. -- When repeat offset 3 is used, it takes first spot, - pushing the other ones by one position. +It means that when repeat offset 1 (most recent) is used, history is unmodified. +When repeat offset 2 is used, it's swapped with offset 1. Dictionary format From 3ca750372d43da3f90017c8ee6b04f0743ab6782 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 1 Aug 2016 02:26:20 +0200 Subject: [PATCH 46/46] updated doc (#269) --- NEWS | 2 +- images/Cspeed4.png | Bin 47294 -> 35361 bytes lib/dictBuilder/zdict.c | 2 +- lib/zstd.h | 26 +++++++++++++++++--------- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index 16a4fb8ff..56c46fef1 100644 --- a/NEWS +++ b/NEWS @@ -4,7 +4,7 @@ New : Build on FreeBSD and DragonFly, thanks to JrMarino Changed : modified API : ZSTD_compressEnd() Fixed : legacy mode with ZSTD_HEAPMODE=0, by Christopher Bergqvist Fixed : premature end of frame when zero-sized raw block, reported by Eric Biggers -Fixed : statistics for large dictionaries (> 256 KB), reported by Ilona Papava +Fixed : large dictionaries (> 384 KB), reported by Ilona Papava Fixed : checksum correctly checked in single-pass mode Fixed : combined --test amd --rm, reported by Andreas M. Nilsson Modified : minor compression level adaptations diff --git a/images/Cspeed4.png b/images/Cspeed4.png index d5219d7264977bcdc9e3daa5f5193ca6abbe3030..f0ca0ffba9c4413cc2a8a19dd5768e03e236c095 100644 GIT binary patch literal 35361 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 MUHx3vIVCg!02^b&<^TWy literal 47294 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 VXIU8^zXXtDJYD@<);T3K0RWcjfA|0Z diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index e1bece3a7..6c2277be8 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -879,7 +879,7 @@ size_t ZDICT_trainFromBuffer_unsafe( U32 const dictListSize = MAX(MAX(DICTLISTSIZE, nbSamples), (U32)(maxDictSize/16)); dictItem* const dictList = (dictItem*)malloc(dictListSize * sizeof(*dictList)); unsigned const selectivity = params.selectivityLevel == 0 ? g_selectivity_default : params.selectivityLevel; - unsigned const minRep = (selectivity > 30) ? 1 : nbSamples >> selectivity; + unsigned const minRep = (selectivity > 30) ? MINRATIO : nbSamples >> selectivity; size_t const targetDictSize = maxDictSize; size_t const samplesBuffSize = ZDICT_totalSampleSize(samplesSizes, nbSamples); size_t dictSize = 0; diff --git a/lib/zstd.h b/lib/zstd.h index 6b9ed463d..cb33b5588 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -79,20 +79,28 @@ ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity, int compressionLevel); /*! ZSTD_getDecompressedSize() : -* @return : decompressed size if known, 0 otherwise. -* note 1 : decompressed size could be wrong or intentionally modified ! -* Always ensure result fits within application's authorized limits ! -* Each application can set its own limit, depending on local restrictions. -* For extended interoperability, it is recommended to support at least 8 MB. -* note 2 : when `0`, if precise failure cause is needed, use ZSTD_getFrameParams() to know more. -* note 3 : when `0`, and if no external guarantee about maximum possible decompressed size, -* it's necessary to use "streaming mode" to decompress data. */ +* @return : decompressed size as a 64-bits value _if known_, 0 otherwise. +* note 1 : decompressed size can be very large (64-bits value), +* potentially larger than what local system can handle as a single memory segment. +* In which case, it's necessary to use streaming mode to decompress data. +* note 2 : decompressed size is an optional field, that may not be present. +* When `return==0`, consider data to decompress could have any size. +* In which case, it's necessary to use streaming mode to decompress data, +* or rely on application's implied limits. +* (For example, it may know that its own data is necessarily cut into blocks <= 16 KB). +* note 3 : decompressed size could be wrong or intentionally modified ! +* Always ensure result fits within application's authorized limits ! +* Each application can have its own set of conditions. +* If the intention is to decompress public data compressed by zstd command line utility, +* it is recommended to support at least 8 MB for extended compatibility. +* note 4 : when `return==0`, if precise failure cause is needed, use ZSTD_getFrameParams() to know more. */ unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize); /*! ZSTD_decompress() : `compressedSize` : must be the _exact_ size of compressed input, otherwise decompression will fail. `dstCapacity` must be equal or larger than originalSize (see ZSTD_getDecompressedSize() ). - If maximum possible content size is unknown, use streaming mode to decompress data. + If originalSize is unknown, and if there is no implied application-specific limitations, + it's necessary to use streaming mode to decompress data. @return : the number of bytes decompressed into `dst` (<= `dstCapacity`), or an errorCode if it fails (which can be tested using ZSTD_isError()) */ ZSTDLIB_API size_t ZSTD_decompress( void* dst, size_t dstCapacity,