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/Makefile b/Makefile index 45782af9f..d8e740bdb 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/NEWS b/NEWS index 64c1d4ff9..56c46fef1 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,12 @@ v0.8.0 -Fixed : premature end of frame when zero-sized raw block, reported by Eric Biggers +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 +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 Updated : compression format specification to v0.2.0 changed : zstd.h moved to /lib directory 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. 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/images/Cspeed4.png b/images/Cspeed4.png index d5219d726..f0ca0ffba 100644 Binary files a/images/Cspeed4.png and b/images/Cspeed4.png differ diff --git a/lib/Makefile b/lib/Makefile index 6df2b1a0b..1b4cb378f 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) @@ -99,8 +100,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/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 */ - diff --git a/lib/common/zbuff.h b/lib/common/zbuff.h index 7820db26d..269dc227c 100644 --- a/lib/common/zbuff.h +++ b/lib/common/zbuff.h @@ -56,6 +56,12 @@ 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. +* 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); ZSTDLIB_API size_t ZBUFF_freeCCtx(ZBUFF_CCtx* cctx); @@ -133,8 +139,9 @@ 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, +* 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() @@ -168,11 +175,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 +189,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); diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index b12877c3c..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 */ @@ -89,7 +89,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 */ @@ -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* sequencesStart; + 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 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 */ @@ -231,4 +234,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/zbuff_compress.c b/lib/compress/zbuff_compress.c index 9b842f64f..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; @@ -95,6 +95,8 @@ struct ZBUFF_CCtx_s { size_t outBuffContentSize; size_t outBuffFlushedSize; ZBUFF_cStage stage; + U32 checksum; + U32 frameEnded; ZSTD_customMem customMem; }; /* typedef'd tp ZBUFF_CCtx within "zstd_buffered.h" */ @@ -133,7 +135,7 @@ size_t ZBUFF_freeCCtx(ZBUFF_CCtx* zbc) } -/* *** Initialization *** */ +/* ====== Initialization ====== */ size_t ZBUFF_compressInit_advanced(ZBUFF_CCtx* zbc, const void* dict, size_t dictSize, @@ -164,6 +166,8 @@ 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; + zbc->frameEnded = 0; return 0; /* ready to go */ } @@ -189,14 +193,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 const flush) { - U32 notDone = 1; + U32 someMoreWork = 1; const char* const istart = (const char*)src; const char* const iend = istart + *srcSizePtr; const char* ip = istart; @@ -204,7 +210,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 ! */ @@ -216,7 +222,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; @@ -227,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) @@ -245,14 +254,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: @@ -262,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; @@ -272,17 +282,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; } @@ -296,15 +306,18 @@ 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); + size_t srcSize = 0; + 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; 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 = !notEnded ? 0 : + ZSTD_compressEnd(zbc->zc, zbc->outBuff, zbc->outBuffSize, NULL, 0); /* write epilogue into outBuff */ } /* flush epilogue */ diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 84e756b88..56c63601e 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; /*-************************************* @@ -73,37 +75,14 @@ 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 ***************************************/ static void ZSTD_resetSeqStore(seqStore_t* ssPtr) { - ssPtr->offset = ssPtr->offsetStart; ssPtr->lit = ssPtr->litStart; - ssPtr->litLength = ssPtr->litLengthStart; - ssPtr->matchLength = ssPtr->matchLengthStart; + ssPtr->sequences = ssPtr->sequencesStart; ssPtr->longLengthID = 0; } @@ -122,7 +101,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; @@ -140,9 +119,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) @@ -251,16 +230,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); + 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; @@ -321,27 +301,25 @@ 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.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; + zc->seqStore.litStart = zc->seqStore.ofCode + maxNbSeq; - zc->stage = 1; + zc->stage = ZSTDcs_init; zc->dictID = 0; zc->loadedDictEnd = 0; @@ -351,21 +329,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); } @@ -408,13 +386,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); } } @@ -437,7 +415,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); @@ -462,7 +440,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 */ @@ -493,7 +471,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; @@ -544,57 +522,46 @@ static size_t ZSTD_compressLiterals (ZSTD_CCtx* zc, return lhSize+cLitSize; } +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 }; -void ZSTD_seqToCodes(const seqStore_t* seqStorePtr, size_t const nbSeq) +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) { - /* 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 }; - const BYTE 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->sequencesStart; + BYTE* const llCodeTable = seqStorePtr->llCode; + BYTE* const ofCodeTable = seqStorePtr->ofCode; + BYTE* const mlCodeTable = seqStorePtr->mlCode; + U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); + U32 u; + for (u=0; u 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 : ML_Code[mlv]; } + if (seqStorePtr->longLengthID==1) + llCodeTable[seqStorePtr->longLengthPos] = MaxLL; + if (seqStorePtr->longLengthID==2) + mlCodeTable[seqStorePtr->longLengthPos] = MaxML; } @@ -609,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->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 = offsetTableEnd - offsetTable; + size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; BYTE* seqHead; /* Compress literals */ @@ -644,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; @@ -734,21 +698,21 @@ 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; for (n=nbSeq-2 ; n= 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)*/ } } @@ -808,15 +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 = (U32)(seqStorePtr->litLength - seqStorePtr->litLengthStart); } - *seqStorePtr->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->offset++) = offsetCode + 1; + seqStorePtr->sequences[0].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 = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); } + seqStorePtr->sequences[0].matchLength = (U16)matchCode; + + seqStorePtr->sequences++; } @@ -906,10 +872,9 @@ static size_t ZSTD_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* const static size_t ZSTD_count_2segments(const BYTE* ip, const BYTE* match, const BYTE* iEnd, const BYTE* mEnd, const BYTE* iStart) { const BYTE* const vEnd = MIN( ip + (mEnd - match), iEnd); - size_t matchLength = ZSTD_count(ip, match, vEnd); - if (match + matchLength == mEnd) - matchLength += ZSTD_count(ip+matchLength, iStart, iEnd); - return matchLength; + size_t const matchLength = ZSTD_count(ip, match, vEnd); + if (match + matchLength != mEnd) return matchLength; + return matchLength + ZSTD_count(ip+matchLength, iStart, iEnd); } @@ -936,7 +901,6 @@ static const U64 prime7bytes = 58295818150454627ULL; static size_t ZSTD_hash7(U64 u, U32 h) { return (size_t)(((u << (64-56)) * prime7bytes) >> (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); } @@ -961,10 +925,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) { @@ -980,16 +944,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; @@ -1179,7 +1143,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: @@ -1201,12 +1165,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) { @@ -1234,7 +1198,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; @@ -1343,9 +1307,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; @@ -1456,7 +1420,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: @@ -1481,13 +1445,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; @@ -1500,7 +1464,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 @@ -1585,12 +1549,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; @@ -1736,13 +1700,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 @@ -2250,18 +2212,24 @@ 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; 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 */ @@ -2269,6 +2237,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 */ @@ -2286,12 +2255,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; @@ -2300,6 +2272,7 @@ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, op += cSize; } + if (lastFrameChunk && (op>ostart)) cctx->stage = ZSTDcs_ending; ZSTD_statsPrint(stats, cctx->params.cParams.searchLength); /* debug only */ return op-ostart; } @@ -2308,34 +2281,34 @@ 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 directModeFlag = 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) + (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 */ 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) { 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; @@ -2347,30 +2320,31 @@ 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; - 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); /* 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; dst = (char*)dst + fhSize; - zc->stage = 2; + zc->stage = ZSTDcs_ongoing; } /* 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 */ @@ -2396,7 +2370,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; @@ -2404,11 +2378,11 @@ 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); + return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1, 0); } @@ -2422,7 +2396,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); } @@ -2440,7 +2414,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) { @@ -2455,12 +2429,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: @@ -2475,8 +2449,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 @@ -2591,35 +2565,57 @@ 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* op = (BYTE*)dst; + BYTE* const ostart = (BYTE*)dst; + BYTE* op = ostart; 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 */ - 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; } - cctx->stage = 0; /* return to "created but not init" status */ - return ZSTD_blockHeaderSize+fhSize; + 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 op-ostart; +} + + +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; } @@ -2632,44 +2628,23 @@ 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_compressContinue(cctx, dst, dstCapacity, src, srcSize); - if (ZSTD_isError(cSize)) return cSize; + size_t const errorCode = ZSTD_copyCCtx(cctx, preparedCCtx); + if (ZSTD_isError(errorCode)) return errorCode; - { size_t const endSize = ZSTD_compressEnd(cctx, (char*)dst+cSize, dstCapacity-cSize); - if (ZSTD_isError(endSize)) return endSize; - return cSize + endSize; - } } + return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); } -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, 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(ctx, dict, dictSize, params, srcSize); - if(ZSTD_isError(errorCode)) return errorCode; } - - /* body (compression) */ - { size_t const oSize = ZSTD_compressContinue (ctx, op, dstCapacity, src, srcSize); - 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); + return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); } size_t ZSTD_compress_advanced (ZSTD_CCtx* ctx, @@ -2788,7 +2763,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/compress/zstd_opt.h b/lib/compress/zstd_opt.h index b7970bdb7..3a1e9e193 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++; @@ -485,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) @@ -576,9 +544,9 @@ void ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, best_mlen = minMatch; { U32 i; - for (i=0; i litlen) { @@ -693,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]); @@ -778,7 +749,7 @@ void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx* ctx, /* check repCode */ { U32 i; - for (i=0; i litlen) { @@ -1005,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/zbuff_decompress.c b/lib/decompress/zbuff_decompress.c index 3c9ada6f0..908120fc7 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 */ + 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; } } - /* ************************************* * Tool functions ***************************************/ diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index ecffb4c6d..958d63692 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" */ @@ -204,10 +206,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 +243,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 +269,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; @@ -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, @@ -615,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; @@ -849,7 +861,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; @@ -860,9 +871,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; } @@ -870,7 +883,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; @@ -888,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; } @@ -966,18 +979,33 @@ 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; } + +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; -} +int ZSTD_isSkipFrame(ZSTD_DCtx* dctx) { return dctx->stage == ZSTDds_skipFrame; } /* for zbuff */ /** ZSTD_decompressContinue() : * @return : nb of bytes generated into `dst` (necessarily <= `dstCapacity) @@ -1021,22 +1049,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) @@ -1048,21 +1083,38 @@ 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 = 4; + dctx->stage = ZSTDds_checkChecksum; + } else { + 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/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index f15185556..6c2277be8 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; @@ -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, unsigned maxDictSize) + 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 */ @@ -542,16 +541,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); @@ -605,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 = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart); + 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; @@ -671,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, @@ -681,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]; @@ -697,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; @@ -744,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"); @@ -845,51 +836,12 @@ _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) { 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); @@ -914,60 +866,87 @@ 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. +* 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, 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; + unsigned const selectivity = params.selectivityLevel == 0 ? g_selectivity_default : params.selectivityLevel; + unsigned const minRep = (selectivity > 30) ? MINRATIO : nbSamples >> selectivity; 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, + minRep); + + /* 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 p = dictList[u].pos; - U32 l = dictList[u].length; - U32 d = MIN(40, l); - 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); - DISPLAYLEVEL(3, "| \n"); - } } } /* create dictionary */ { U32 dictContentSize = ZDICT_dictSize(dictList); + if (dictContentSize < targetDictSize/2) { + DISPLAYLEVEL(2, "! warning : created dictionary significantly smaller than requested (%u < %u) \n", dictContentSize, (U32)maxDictSize); + 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 increasing the number of samples (total size : %u MB)\n", (U32)(samplesBuffSize>>20)); + } + + 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 */ + { U32 const max = dictList->pos; /* convention : nb of useful elts within dictList */ + U32 currentSize = 0; + U32 n; for (n=1; n targetDictSize) { currentSize -= dictList[n].length; break; } + } + dictList->pos = n; + dictContentSize = currentSize; + } /* build dict content */ { U32 u; @@ -979,14 +958,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); @@ -1004,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; } diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index b96b828f7..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,8 +70,8 @@ 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 */ + 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 */ unsigned reserved[2]; /* space for future parameters */ @@ -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/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/lib/zstd.h b/lib/zstd.h index e459f6259..cb33b5588 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -79,22 +79,37 @@ ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity, int compressionLevel); /*! 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 */ +* @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 _exact_ size of compressed input, otherwise decompression will fail. - `dstCapacity` must be equal or larger than originalSize. + `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 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, 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 */ @@ -105,16 +120,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()) */ @@ -125,7 +140,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, @@ -134,7 +149,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, @@ -144,7 +159,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. @@ -154,7 +169,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, @@ -170,7 +185,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, @@ -264,8 +279,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) */ @@ -312,13 +325,18 @@ 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 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_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. @@ -333,7 +351,7 @@ ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapaci 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. @@ -342,9 +360,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 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. */ @@ -356,7 +374,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); @@ -365,6 +383,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) @@ -372,42 +393,48 @@ 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`), - 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() + 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. 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. + Note : it's possible to know if next input to present is a header or a block, using ZSTD_nextInputType(). + This information is not required to properly decode a frame. == 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 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 c) Frame Content - any content (User Data) of length equal to Frame Size diff --git a/programs/Makefile b/programs/Makefile index 77c62dc54..be6fbf2c2 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -173,10 +173,10 @@ clean: cleano32: @rm -f ../lib/decompress/*.o -#--------------------------------------------------------------------------------- -#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 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); - } } 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)); diff --git a/programs/fileio.c b/programs/fileio.c index 855385bef..b04ee3b04 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; @@ -636,13 +636,12 @@ unsigned long long FIO_decompressFrame(dRess_t ress, DISPLAYUPDATE(2, "\rDecoded : %u MB... ", (U32)(frameSize>>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); @@ -683,6 +682,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); @@ -696,8 +696,12 @@ 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 != toRead) EXM_THROW(31, "zstd: %s read error : cannot read header", srcFileName); + if (sizeCheck==0) { + 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) { 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)) { @@ -705,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; @@ -744,7 +748,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/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/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 3778f12b2..cb31dc431 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++); @@ -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; } 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/playTests.sh b/programs/playTests.sh index b19c2bc52..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" @@ -142,8 +149,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* : " @@ -204,8 +211,16 @@ $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 !" +./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 $ECHO "\n**** zstd round-trip tests **** " 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 diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 344d3c88e..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; @@ -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, @@ -215,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; @@ -226,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; @@ -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 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 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) diff --git a/zstd_compression_format.md b/zstd_compression_format.md index efbf13cd4..da5c94afd 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`. @@ -1091,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, 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, 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, @@ -1105,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