From 2bd85f41994e9695911cfc4c86fbc04fdb35ee82 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Fri, 22 Sep 2017 11:55:42 -0700 Subject: Add Dictionary Support to the Command Line Tool --- programs/lz4cli.c | 27 ++++++++++++++++++ programs/lz4io.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- programs/lz4io.h | 2 ++ 3 files changed, 109 insertions(+), 4 deletions(-) diff --git a/programs/lz4cli.c b/programs/lz4cli.c index ff489c6..857fa65 100644 --- a/programs/lz4cli.c +++ b/programs/lz4cli.c @@ -113,6 +113,7 @@ static int usage(const char* exeName) DISPLAY( " -9 : High compression \n"); DISPLAY( " -d : decompression (default for %s extension)\n", LZ4_EXTENSION); DISPLAY( " -z : force compression \n"); + DISPLAY( " -D FILE: use dictionary in FILE \n"); DISPLAY( " -f : overwrite output without prompting \n"); DISPLAY( " -k : preserve source files(s) (default) \n"); DISPLAY( "--rm : remove source file(s) after successful de/compression \n"); @@ -290,6 +291,7 @@ int main(int argc, const char** argv) operationMode_e mode = om_auto; const char* input_filename = NULL; const char* output_filename= NULL; + const char* dictionary_filename = NULL; char* dynNameSpace = NULL; const char** inFileNames = (const char**) calloc(argc, sizeof(char*)); unsigned ifnIdx=0; @@ -399,6 +401,22 @@ int main(int argc, const char** argv) /* Compression (default) */ case 'z': mode = om_compress; break; + case 'D': + if (argument[1] == '\0') { + /* path is next arg */ + if (i + 1 == argc) { + /* there is no next arg */ + badusage(exeName); + } + dictionary_filename = argv[++i]; + } else { + /* path follows immediately */ + dictionary_filename = argument + 1; + } + /* skip to end of argument so that we jump to parsing next argument */ + argument += strlen(argument) - 1; + break; + /* Use Legacy format (ex : Linux kernel compression) */ case 'l': legacy_format = 1; blockSize = 8 MB; break; @@ -560,6 +578,15 @@ int main(int argc, const char** argv) mode = om_decompress; /* defer to decompress */ } + if (dictionary_filename) { + if (!strcmp(dictionary_filename, stdinmark) && IS_CONSOLE(stdin)) { + DISPLAYLEVEL(1, "refusing to read from a console\n"); + exit(1); + } + + LZ4IO_setDictionaryFilename(dictionary_filename); + } + /* compress or decompress */ if (!input_filename) input_filename = stdinmark; /* Check if input is defined as console; trigger an error in this case */ diff --git a/programs/lz4io.c b/programs/lz4io.c index 06741b4..642e11c 100644 --- a/programs/lz4io.c +++ b/programs/lz4io.c @@ -57,6 +57,7 @@ #include "lz4.h" /* still required for legacy format */ #include "lz4hc.h" /* still required for legacy format */ #include "lz4frame.h" +#include "lz4frame_static.h" /***************************** @@ -110,6 +111,8 @@ static int g_streamChecksum = 1; static int g_blockIndependence = 1; static int g_sparseFileSupport = 1; static int g_contentSizeFlag = 0; +static int g_useDictionary = 0; +static const char* g_dictionaryFilename = NULL; /************************************** @@ -142,6 +145,12 @@ static int g_contentSizeFlag = 0; /* ****************** Parameters ******************** */ /* ************************************************** */ +int LZ4IO_setDictionaryFilename(const char* dictionaryFilename) { + g_dictionaryFilename = dictionaryFilename; + g_useDictionary = dictionaryFilename != NULL; + return g_useDictionary; +} + /* Default setting : overwrite = 1; return : overwrite mode (0/1) */ int LZ4IO_setOverwrite(int yes) { @@ -395,8 +404,53 @@ typedef struct { void* dstBuffer; size_t dstBufferSize; LZ4F_compressionContext_t ctx; + LZ4F_CDict* cdict; } cRess_t; +static void* LZ4IO_createDict(const char* dictionaryFilename, size_t *dictionarySize) { + FILE* dictionaryFile; + size_t blockSize = 64 KB; + size_t dictionaryBufferSize = blockSize; + size_t readSize; + void* dictionaryBuffer; + *dictionarySize = 0; + dictionaryBuffer = malloc(dictionaryBufferSize); + + if (!dictionaryBuffer) EXM_THROW(25, "Allocation error : not enough memory"); + + if (!dictionaryFilename) EXM_THROW(25, "Dictionary error : no filename provided"); + + dictionaryFile = LZ4IO_openSrcFile(g_dictionaryFilename); + if (!dictionaryFile) EXM_THROW(25, "Dictionary error : could not open dictionary file"); + + do { + if (*dictionarySize + blockSize > dictionaryBufferSize) { + dictionaryBufferSize *= 2; + dictionaryBuffer = realloc(dictionaryBuffer, dictionaryBufferSize); + if (!dictionaryBuffer) EXM_THROW(26, "Allocation error : not enough memory"); + } + /* Read next block */ + readSize = fread((char*)dictionaryBuffer + *dictionarySize, (size_t)1, (size_t)blockSize, dictionaryFile); + *dictionarySize += readSize; + } while (readSize>0); + + return dictionaryBuffer; +} + +static LZ4F_CDict* LZ4IO_createCDict(void) { + size_t dictionarySize; + void* dictionaryBuffer; + LZ4F_CDict* cdict; + if (!g_useDictionary) { + return NULL; + } + dictionaryBuffer = LZ4IO_createDict(g_dictionaryFilename, &dictionarySize); + if (!dictionaryBuffer) EXM_THROW(25, "Dictionary error : could not create dictionary"); + cdict = LZ4F_createCDict(dictionaryBuffer, dictionarySize); + free(dictionaryBuffer); + return cdict; +} + static cRess_t LZ4IO_createCResources(void) { const size_t blockSize = (size_t)LZ4IO_GetBlockSize_FromBlockId (g_blockSizeId); @@ -412,6 +466,8 @@ static cRess_t LZ4IO_createCResources(void) ress.dstBuffer = malloc(ress.dstBufferSize); if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(31, "Allocation error : not enough memory"); + ress.cdict = LZ4IO_createCDict(); + return ress; } @@ -419,6 +475,10 @@ static void LZ4IO_freeCResources(cRess_t ress) { free(ress.srcBuffer); free(ress.dstBuffer); + + LZ4F_freeCDict(ress.cdict); + ress.cdict = NULL; + { LZ4F_errorCode_t const errorCode = LZ4F_freeCompressionContext(ress.ctx); if (LZ4F_isError(errorCode)) EXM_THROW(38, "Error : can't free LZ4F context resource : %s", LZ4F_getErrorName(errorCode)); } } @@ -472,7 +532,7 @@ static int LZ4IO_compressFilename_extRess(cRess_t ress, const char* srcFileName, /* single-block file */ if (readSize < blockSize) { /* Compress in single pass */ - size_t const cSize = LZ4F_compressFrame(dstBuffer, dstBufferSize, srcBuffer, readSize, &prefs); + size_t cSize = LZ4F_compressFrame_usingCDict(dstBuffer, dstBufferSize, srcBuffer, readSize, ress.cdict, &prefs); if (LZ4F_isError(cSize)) EXM_THROW(31, "Compression failed : %s", LZ4F_getErrorName(cSize)); compressedfilesize = cSize; DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%% ", @@ -488,7 +548,7 @@ static int LZ4IO_compressFilename_extRess(cRess_t ress, const char* srcFileName, /* multiple-blocks file */ { /* Write Archive Header */ - size_t headerSize = LZ4F_compressBegin(ctx, dstBuffer, dstBufferSize, &prefs); + size_t headerSize = LZ4F_compressBegin_usingCDict(ctx, dstBuffer, dstBufferSize, ress.cdict, &prefs); if (LZ4F_isError(headerSize)) EXM_THROW(33, "File header generation failed : %s", LZ4F_getErrorName(headerSize)); { size_t const sizeCheck = fwrite(dstBuffer, 1, headerSize, dstFile); if (sizeCheck!=headerSize) EXM_THROW(34, "Write error : cannot write header"); } @@ -745,8 +805,21 @@ typedef struct { size_t dstBufferSize; FILE* dstFile; LZ4F_decompressionContext_t dCtx; + void* dictBuffer; + size_t dictBufferSize; } dRess_t; +static void LZ4IO_loadDDict(dRess_t* ress) { + if (!g_useDictionary) { + ress->dictBuffer = NULL; + ress->dictBufferSize = 0; + return; + } + + ress->dictBuffer = LZ4IO_createDict(g_dictionaryFilename, &ress->dictBufferSize); + if (!ress->dictBuffer) EXM_THROW(25, "Dictionary error : could not create dictionary"); +} + static const size_t LZ4IO_dBufferSize = 64 KB; static dRess_t LZ4IO_createDResources(void) { @@ -763,6 +836,8 @@ static dRess_t LZ4IO_createDResources(void) ress.dstBuffer = malloc(ress.dstBufferSize); if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(61, "Allocation error : not enough memory"); + LZ4IO_loadDDict(&ress); + ress.dstFile = NULL; return ress; } @@ -773,6 +848,7 @@ static void LZ4IO_freeDResources(dRess_t ress) if (LZ4F_isError(errorCode)) EXM_THROW(69, "Error : can't free LZ4F context resource : %s", LZ4F_getErrorName(errorCode)); free(ress.srcBuffer); free(ress.dstBuffer); + free(ress.dictBuffer); } @@ -786,7 +862,7 @@ static unsigned long long LZ4IO_decompressLZ4F(dRess_t ress, FILE* srcFile, FILE { size_t inSize = MAGICNUMBER_SIZE; size_t outSize= 0; LZ4IO_writeLE32(ress.srcBuffer, LZ4IO_MAGICNUMBER); - nextToLoad = LZ4F_decompress(ress.dCtx, ress.dstBuffer, &outSize, ress.srcBuffer, &inSize, NULL); + nextToLoad = LZ4F_decompress_usingDict(ress.dCtx, ress.dstBuffer, &outSize, ress.srcBuffer, &inSize, ress.dictBuffer, ress.dictBufferSize, NULL); if (LZ4F_isError(nextToLoad)) EXM_THROW(62, "Header error : %s", LZ4F_getErrorName(nextToLoad)); } @@ -805,7 +881,7 @@ static unsigned long long LZ4IO_decompressLZ4F(dRess_t ress, FILE* srcFile, FILE /* Decode Input (at least partially) */ size_t remaining = readSize - pos; decodedBytes = ress.dstBufferSize; - nextToLoad = LZ4F_decompress(ress.dCtx, ress.dstBuffer, &decodedBytes, (char*)(ress.srcBuffer)+pos, &remaining, NULL); + nextToLoad = LZ4F_decompress_usingDict(ress.dCtx, ress.dstBuffer, &decodedBytes, (char*)(ress.srcBuffer)+pos, &remaining, ress.dictBuffer, ress.dictBufferSize, NULL); if (LZ4F_isError(nextToLoad)) EXM_THROW(66, "Decompression error : %s", LZ4F_getErrorName(nextToLoad)); pos += remaining; diff --git a/programs/lz4io.h b/programs/lz4io.h index 6190f00..b21b8b6 100644 --- a/programs/lz4io.h +++ b/programs/lz4io.h @@ -64,6 +64,8 @@ int LZ4IO_decompressMultipleFilenames(const char** inFileNamesTable, int ifntSiz /* ****************** Parameters ******************** */ /* ************************************************** */ +int LZ4IO_setDictionaryFilename(const char* dictionaryFilename); + /* Default setting : overwrite = 1; return : overwrite mode (0/1) */ int LZ4IO_setOverwrite(int yes); -- cgit v0.12 From 93f8284c175a4047b0d9df3112927bbb3b832b2a Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Fri, 22 Sep 2017 14:50:11 -0700 Subject: Add some tests verifying command line dictionary functionality --- tests/Makefile | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index e870fcf..1a907b7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -129,6 +129,8 @@ ifneq (,$(filter $(shell uname),SunOS)) DIFF:=gdiff endif +DD:=dd + test: test-lz4 test-lz4c test-frametest test-fullbench test-fuzzer @@ -253,6 +255,31 @@ test-lz4-basic: lz4 datagen unlz4 lz4cat $(LZ4) -BX tmp-tlb-hw -c -q | $(LZ4) -tv # test block checksum @$(RM) tmp-tlb* +test-lz4-dict: lz4 datagen + @echo "\n ---- test lz4 compression/decompression with dictionary ----" + ./datagen -g16KB > tmp-dict + ./datagen -g32KB > tmp-dict-sample-32k + < tmp-dict-sample-32k $(LZ4) -D tmp-dict | $(LZ4) -dD tmp-dict | diff - tmp-dict-sample-32k + ./datagen -g128MB > tmp-dict-sample-128m + < tmp-dict-sample-128m $(LZ4) -D tmp-dict | $(LZ4) -dD tmp-dict | diff - tmp-dict-sample-128m + touch tmp-dict-sample-0 + < tmp-dict-sample-0 $(LZ4) -D tmp-dict | $(LZ4) -dD tmp-dict | diff - tmp-dict-sample-0 + + < tmp-dict-sample-32k $(LZ4) -D tmp-dict-sample-0 | $(LZ4) -dD tmp-dict-sample-0 | diff - tmp-dict-sample-32k + < tmp-dict-sample-0 $(LZ4) -D tmp-dict-sample-0 | $(LZ4) -dD tmp-dict-sample-0 | diff - tmp-dict-sample-0 + + @echo "\n ---- test lz4 dictionary loading ----" + ./datagen -g128KB > tmp-dict-data-128KB + set -e; \ + for l in 0 1 4 128 32767 32768 32769 65535 65536 65537 98303 98304 98305 131071 131072 131073; do \ + ./datagen -g$$l > tmp-dict-$$l; \ + $(DD) if=tmp-dict-$$l of=tmp-dict-$$l-tail bs=1 count=65536 skip=$$((l > 65536 ? l - 65536 : 0)); \ + < tmp-dict-$$l $(LZ4) -D stdin tmp-dict-data-128KB | $(LZ4) -dD tmp-dict-$$l-tail | $(DIFF) - tmp-dict-data-128KB; \ + < tmp-dict-$$l-tail $(LZ4) -D stdin tmp-dict-data-128KB | $(LZ4) -dD tmp-dict-$$l | $(DIFF) - tmp-dict-data-128KB; \ + done + + @$(RM) tmp-dict* + test-lz4-hugefile: lz4 datagen @echo "\n ---- test huge files compression/decompression ----" ./datagen -g6GB | $(LZ4) -vB5D | $(LZ4) -qt @@ -292,7 +319,7 @@ test-lz4-opt-parser: lz4 datagen test-lz4: lz4 datagen test-lz4-basic test-lz4-opt-parser test-lz4-multiple \ test-lz4-sparse test-lz4-frame-concatenation test-lz4-testmode \ - test-lz4-contentSize test-lz4-hugefile + test-lz4-contentSize test-lz4-hugefile test-lz4-dict @$(RM) tmp* test-lz4c: lz4c datagen -- cgit v0.12 From 9a16272261f571d54e4642c760d099adb6cc27b1 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 3 Oct 2017 12:50:28 -0400 Subject: Read the Dictionary into a Circular Buffer --- programs/lz4io.c | 71 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/programs/lz4io.c b/programs/lz4io.c index 642e11c..57434f7 100644 --- a/programs/lz4io.c +++ b/programs/lz4io.c @@ -83,6 +83,7 @@ #define LEGACY_BLOCKSIZE (8 MB) #define MIN_STREAM_BUFSIZE (192 KB) #define LZ4IO_BLOCKSIZEID_DEFAULT 7 +#define LZ4_MAX_DICT_SIZE (64 KB) /************************************** @@ -407,34 +408,60 @@ typedef struct { LZ4F_CDict* cdict; } cRess_t; -static void* LZ4IO_createDict(const char* dictionaryFilename, size_t *dictionarySize) { - FILE* dictionaryFile; - size_t blockSize = 64 KB; - size_t dictionaryBufferSize = blockSize; +static void* LZ4IO_createDict(const char* dictFilename, size_t *dictSize) { size_t readSize; - void* dictionaryBuffer; - *dictionarySize = 0; - dictionaryBuffer = malloc(dictionaryBufferSize); + size_t dictEnd = 0; + size_t dictLen = 0; + size_t dictStart; + size_t circularBufSize = LZ4_MAX_DICT_SIZE; + char* circularBuf; + char* dictBuf; + FILE* dictFile; + + if (!dictFilename) EXM_THROW(25, "Dictionary error : no filename provided"); + + circularBuf = (char *) malloc(circularBufSize); + if (!circularBuf) EXM_THROW(25, "Allocation error : not enough memory"); + + dictFile = LZ4IO_openSrcFile(dictFilename); + if (!dictFile) EXM_THROW(25, "Dictionary error : could not open dictionary file"); + + /* opportunistically seek to the part of the file we care about. If this */ + /* fails it's not a problem since we'll just read everything anyways. */ + if (strcmp(dictFilename, stdinmark)) { + UTIL_fseek(dictFile, -LZ4_MAX_DICT_SIZE, SEEK_END); + } - if (!dictionaryBuffer) EXM_THROW(25, "Allocation error : not enough memory"); + do { + readSize = fread(circularBuf + dictEnd, 1, circularBufSize - dictEnd, dictFile); + dictEnd = (dictEnd + readSize) % circularBufSize; + dictLen += readSize; + } while (readSize>0); - if (!dictionaryFilename) EXM_THROW(25, "Dictionary error : no filename provided"); + if (dictLen > LZ4_MAX_DICT_SIZE) { + dictLen = LZ4_MAX_DICT_SIZE; + } - dictionaryFile = LZ4IO_openSrcFile(g_dictionaryFilename); - if (!dictionaryFile) EXM_THROW(25, "Dictionary error : could not open dictionary file"); + *dictSize = dictLen; - do { - if (*dictionarySize + blockSize > dictionaryBufferSize) { - dictionaryBufferSize *= 2; - dictionaryBuffer = realloc(dictionaryBuffer, dictionaryBufferSize); - if (!dictionaryBuffer) EXM_THROW(26, "Allocation error : not enough memory"); - } - /* Read next block */ - readSize = fread((char*)dictionaryBuffer + *dictionarySize, (size_t)1, (size_t)blockSize, dictionaryFile); - *dictionarySize += readSize; - } while (readSize>0); + dictStart = (circularBufSize + dictEnd - dictLen) % circularBufSize; + + if (dictStart == 0) { + /* We're in the simple case where the dict starts at the beginning of our circular buffer. */ + dictBuf = circularBuf; + circularBuf = NULL; + } else { + /* Otherwise, we will alloc a new buffer and copy our dict into that. */ + dictBuf = (char *) malloc(dictLen ? dictLen : 1); + if (!dictBuf) EXM_THROW(25, "Allocation error : not enough memory"); + + memcpy(dictBuf, circularBuf + dictStart, circularBufSize - dictStart); + memcpy(dictBuf + circularBufSize - dictStart, circularBuf, dictLen - (circularBufSize - dictStart)); + } + + free(circularBuf); - return dictionaryBuffer; + return dictBuf; } static LZ4F_CDict* LZ4IO_createCDict(void) { -- cgit v0.12 From 73bcf90e51b9837454b92265d30d4a214be38b02 Mon Sep 17 00:00:00 2001 From: Rei Odaira Date: Fri, 13 Oct 2017 14:53:37 -0500 Subject: Use the optimization level of O2 for the decompression functions on ppc64le with gcc, to avoid harmful unrolling and SIMDization with O3 --- lib/lz4.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/lz4.c b/lib/lz4.c index 19967f2..179408d 100644 --- a/lib/lz4.c +++ b/lib/lz4.c @@ -117,6 +117,28 @@ # endif /* _MSC_VER */ #endif /* LZ4_FORCE_INLINE */ +/* LZ4_FORCE_O2_GCC_PPC64LE and LZ4_FORCE_O2_INLINE_GCC_PPC64LE + * Gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy, + * together with a simple 8-byte copy loop as a fall-back path. + * However, this optimization hurts the decompression speed by >30%, + * because the execution does not go to the optimized loop + * for typical compressible data, and all of the preamble checks + * before going to the fall-back path become useless overhead. + * This optimization happens only with the -O3 flag, and -O2 generates + * a simple 8-byte copy loop. + * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy + * functions are annotated with __attribute__((optimize("O2"))), + * and also LZ4_wildCopy is forcibly inlined, so that the O2 attribute + * of LZ4_wildCopy does not affect the compression speed. + */ +#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) +# define LZ4_FORCE_O2_GCC_PPC64LE __attribute__((optimize("O2"))) +# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE __attribute__((optimize("O2"))) LZ4_FORCE_INLINE +#else +# define LZ4_FORCE_O2_GCC_PPC64LE +# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE static +#endif + #if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) # define expect(expr,value) (__builtin_expect ((expr),(value)) ) #else @@ -253,7 +275,8 @@ static void LZ4_copy8(void* dst, const void* src) } /* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ -static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd) +LZ4_FORCE_O2_INLINE_GCC_PPC64LE +void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd) { BYTE* d = (BYTE*)dstPtr; const BYTE* s = (const BYTE*)srcPtr; @@ -1112,6 +1135,7 @@ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) * Note that it is important for performance that this function really get inlined, * in order to remove useless branches during compilation optimization. */ +LZ4_FORCE_O2_GCC_PPC64LE LZ4_FORCE_INLINE int LZ4_decompress_generic( const char* const src, char* const dst, @@ -1272,16 +1296,19 @@ _output_error: } +LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0); } +LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_safe_partial(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial, targetOutputSize, noDict, (BYTE*)dest, NULL, 0); } +LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_fast(const char* source, char* dest, int originalSize) { return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)(dest - 64 KB), NULL, 64 KB); @@ -1327,6 +1354,7 @@ int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dicti If it's not possible, save the relevant part of decoded data into a safe buffer, and indicate where it stands using LZ4_setStreamDecode() */ +LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) { LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; @@ -1353,6 +1381,7 @@ int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const ch return result; } +LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) { LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; @@ -1387,6 +1416,7 @@ Advanced decoding functions : the dictionary must be explicitly provided within parameters */ +LZ4_FORCE_O2_GCC_PPC64LE LZ4_FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize) { if (dictSize==0) @@ -1399,17 +1429,20 @@ LZ4_FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); } +LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) { return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize); } +LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) { return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize); } /* debug function */ +LZ4_FORCE_O2_GCC_PPC64LE int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); -- cgit v0.12 From 2d827e1b287881fbdef00b9d88060d63e8a66236 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 14 Oct 2017 18:48:00 -0700 Subject: lz4cli : removed extension artefacts It used to be useful for an old Windows variant which is no longer maintained. --- programs/lz4cli.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/programs/lz4cli.c b/programs/lz4cli.c index 857fa65..05cb69f 100644 --- a/programs/lz4cli.c +++ b/programs/lz4cli.c @@ -90,9 +90,6 @@ static unsigned displayLevel = 2; /* 0 : no display ; 1: errors only ; 2 : dow /*-************************************ * Version modifiers ***************************************/ -#define EXTENDED_ARGUMENTS -#define EXTENDED_HELP -#define EXTENDED_FORMAT #define DEFAULT_COMPRESSOR LZ4IO_compressFilename #define DEFAULT_DECOMPRESSOR LZ4IO_decompressFilename int LZ4IO_compressFilename_Legacy(const char* input_filename, const char* output_filename, int compressionlevel); /* hidden function */ @@ -156,7 +153,6 @@ static int usage_advanced(const char* exeName) DISPLAY( " -hc : high compression \n"); DISPLAY( " -y : overwrite output without prompting \n"); } - EXTENDED_HELP; return 0; } @@ -506,9 +502,6 @@ int main(int argc, const char** argv) /* Pause at the end (hidden option) */ case 'p': main_pause=1; break; - /* Specific commands for customized versions */ - EXTENDED_ARGUMENTS; - /* Unrecognised command */ default : badusage(exeName); } @@ -560,8 +553,7 @@ int main(int argc, const char** argv) free((void*)inFileNames); inFileNames = extendedFileList; ifnIdx = fileNamesNb; - } - } + } } #endif } @@ -583,7 +575,6 @@ int main(int argc, const char** argv) DISPLAYLEVEL(1, "refusing to read from a console\n"); exit(1); } - LZ4IO_setDictionaryFilename(dictionary_filename); } @@ -654,7 +645,7 @@ int main(int argc, const char** argv) operationResult = DEFAULT_DECOMPRESSOR(input_filename, output_filename); } else { /* compression is default action */ if (legacy_format) { - DISPLAYLEVEL(3, "! Generating compressed LZ4 using Legacy format (deprecated) ! \n"); + DISPLAYLEVEL(3, "! Generating LZ4 Legacy format (deprecated) ! \n"); LZ4IO_compressFilename_Legacy(input_filename, output_filename, cLevel); } else { if (multiple_inputs) @@ -666,12 +657,13 @@ int main(int argc, const char** argv) _cleanup: if (main_pause) waitEnter(); - if (dynNameSpace) free(dynNameSpace); + free(dynNameSpace); #ifdef UTIL_HAS_CREATEFILELIST - if (extendedFileList) + if (extendedFileList) { UTIL_freeFileList(extendedFileList, fileNamesBuf); - else + inFileNames = NULL; + } #endif - free((void*)inFileNames); + free((void*)inFileNames); return operationResult; } -- cgit v0.12 From dccf8826f1d76efcbdc655e63cc04cdbd1123619 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 14 Oct 2017 23:50:07 -0700 Subject: lz4cli : minor rewrite of lz4c legacy commands for clarity --- programs/lz4cli.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/programs/lz4cli.c b/programs/lz4cli.c index 05cb69f..362ba49 100644 --- a/programs/lz4cli.c +++ b/programs/lz4cli.c @@ -150,7 +150,7 @@ static int usage_advanced(const char* exeName) DISPLAY( "Legacy arguments : \n"); DISPLAY( " -c0 : fast compression \n"); DISPLAY( " -c1 : high compression \n"); - DISPLAY( " -hc : high compression \n"); + DISPLAY( " -c2,-hc: very high compression \n"); DISPLAY( " -y : overwrite output without prompting \n"); } return 0; @@ -205,14 +205,14 @@ static int usage_longhelp(const char* exeName) DISPLAY( " generator | %s | consumer \n", exeName); if (g_lz4c_legacy_commands) { DISPLAY( "\n"); - DISPLAY( "***** Warning *****\n"); + DISPLAY( "***** Warning ***** \n"); DISPLAY( "Legacy arguments take precedence. Therefore : \n"); - DISPLAY( "---------------------------------\n"); - DISPLAY( " %s -hc filename\n", exeName); - DISPLAY( "means 'compress filename in high compression mode'\n"); - DISPLAY( "It is not equivalent to :\n"); - DISPLAY( " %s -h -c filename\n", exeName); - DISPLAY( "which would display help text and exit\n"); + DISPLAY( "--------------------------------- \n"); + DISPLAY( " %s -hc filename \n", exeName); + DISPLAY( "means 'compress filename in high compression mode' \n"); + DISPLAY( "It is not equivalent to : \n"); + DISPLAY( " %s -h -c filename \n", exeName); + DISPLAY( "which displays help text and exits \n"); } return 0; } @@ -368,10 +368,11 @@ int main(int argc, const char** argv) if (g_lz4c_legacy_commands) { /* Legacy commands (-c0, -c1, -hc, -y) */ - if ((argument[0]=='c') && (argument[1]=='0')) { cLevel=0; argument++; continue; } /* -c0 (fast compression) */ - if ((argument[0]=='c') && (argument[1]=='1')) { cLevel=9; argument++; continue; } /* -c1 (high compression) */ - if ((argument[0]=='h') && (argument[1]=='c')) { cLevel=9; argument++; continue; } /* -hc (high compression) */ - if (argument[0]=='y') { LZ4IO_setOverwrite(1); continue; } /* -y (answer 'yes' to overwrite permission) */ + if (!strcmp(argument, "c0")) { cLevel=0; argument++; continue; } /* -c0 (fast compression) */ + if (!strcmp(argument, "c1")) { cLevel=9; argument++; continue; } /* -c1 (high compression) */ + if (!strcmp(argument, "c2")) { cLevel=12; argument++; continue; } /* -c2 (very high compression) */ + if (!strcmp(argument, "hc")) { cLevel=12; argument++; continue; } /* -hc (very high compression) */ + if (!strcmp(argument, "y")) { LZ4IO_setOverwrite(1); continue; } /* -y (answer 'yes' to overwrite permission) */ } if ((*argument>='0') && (*argument<='9')) { -- cgit v0.12 From 74d8688bc8104d897f9c98fc747400e71fbe77bc Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 17 Oct 2017 12:42:27 -0700 Subject: [bench] Use higher resolution timer on POSIX The timer used was only accurate up to 0.01 seconds. This timer is accurate up to 1 ns. It is a monotonic timer that measures the real time difference, not on CPU time. Copied the benchmark code from https://github.com/facebook/zstd/commit/6ab4d5e9041aba962a810ffee191f95897c6208e --- programs/bench.c | 24 +++++------ programs/util.h | 127 +++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 110 insertions(+), 41 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 5c83d59..fac2a87 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -166,7 +166,6 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, void* const compressedBuffer = malloc(maxCompressedSize); void* const resultBuffer = malloc(srcSize); U32 nbBlocks; - UTIL_time_t ticksPerSecond; struct compressionParameters compP; int cfunctionId; @@ -176,7 +175,6 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, /* init */ if (strlen(displayName)>17) displayName += strlen(displayName)-17; /* can only display 17 characters */ - UTIL_initTimer(&ticksPerSecond); /* Init */ if (cLevel < LZ4HC_CLEVEL_MIN) cfunctionId = 0; else cfunctionId = 1; @@ -229,17 +227,17 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, size_t cSize = 0; double ratio = 0.; - UTIL_getTime(&coolTime); + coolTime = UTIL_getTime(); DISPLAYLEVEL(2, "\r%79s\r", ""); while (!cCompleted || !dCompleted) { UTIL_time_t clockStart; U64 clockLoop = g_nbSeconds ? TIMELOOP_MICROSEC : 1; /* overheat protection */ - if (UTIL_clockSpanMicro(coolTime, ticksPerSecond) > ACTIVEPERIOD_MICROSEC) { + if (UTIL_clockSpanMicro(coolTime) > ACTIVEPERIOD_MICROSEC) { DISPLAYLEVEL(2, "\rcooling down ... \r"); UTIL_sleep(COOLPERIOD_SEC); - UTIL_getTime(&coolTime); + coolTime = UTIL_getTime(); } /* Compression */ @@ -247,8 +245,8 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, if (!cCompleted) memset(compressedBuffer, 0xE5, maxCompressedSize); /* warm up and erase result buffer */ UTIL_sleepMilli(1); /* give processor time to other processes */ - UTIL_waitForNextTick(ticksPerSecond); - UTIL_getTime(&clockStart); + UTIL_waitForNextTick(); + clockStart = UTIL_getTime(); if (!cCompleted) { /* still some time to do compression tests */ U32 nbLoops = 0; @@ -260,8 +258,8 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, blockTable[blockNb].cSize = rSize; } nbLoops++; - } while (UTIL_clockSpanMicro(clockStart, ticksPerSecond) < clockLoop); - { U64 const clockSpan = UTIL_clockSpanMicro(clockStart, ticksPerSecond); + } while (UTIL_clockSpanMicro(clockStart) < clockLoop); + { U64 const clockSpan = UTIL_clockSpanMicro(clockStart); if (clockSpan < fastestC*nbLoops) fastestC = clockSpan / nbLoops; totalCTime += clockSpan; cCompleted = totalCTime>maxTime; @@ -282,8 +280,8 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, if (!dCompleted) memset(resultBuffer, 0xD6, srcSize); /* warm result buffer */ UTIL_sleepMilli(1); /* give processor time to other processes */ - UTIL_waitForNextTick(ticksPerSecond); - UTIL_getTime(&clockStart); + UTIL_waitForNextTick(); + clockStart = UTIL_getTime(); if (!dCompleted) { U32 nbLoops = 0; @@ -300,8 +298,8 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, blockTable[blockNb].resSize = regenSize; } nbLoops++; - } while (UTIL_clockSpanMicro(clockStart, ticksPerSecond) < DECOMP_MULT*clockLoop); - { U64 const clockSpan = UTIL_clockSpanMicro(clockStart, ticksPerSecond); + } while (UTIL_clockSpanMicro(clockStart) < DECOMP_MULT*clockLoop); + { U64 const clockSpan = UTIL_clockSpanMicro(clockStart); if (clockSpan < fastestD*nbLoops) fastestD = clockSpan / nbLoops; totalDTime += clockSpan; dCompleted = totalDTime>(DECOMP_MULT*maxTime); diff --git a/programs/util.h b/programs/util.h index 5a69c55..1382cab 100644 --- a/programs/util.h +++ b/programs/util.h @@ -140,45 +140,116 @@ extern "C" { /*-**************************************** * Time functions ******************************************/ -#if (PLATFORM_POSIX_VERSION >= 1) -#include -#include /* times */ - typedef U64 UTIL_time_t; - UTIL_STATIC void UTIL_initTimer(UTIL_time_t* ticksPerSecond) { *ticksPerSecond=sysconf(_SC_CLK_TCK); } - UTIL_STATIC void UTIL_getTime(UTIL_time_t* x) { struct tms junk; clock_t newTicks = (clock_t) times(&junk); (void)junk; *x = (UTIL_time_t)newTicks; } - UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000ULL * (clockEnd - clockStart) / ticksPerSecond; } - UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000000ULL * (clockEnd - clockStart) / ticksPerSecond; } -#elif defined(_WIN32) /* Windows */ - typedef LARGE_INTEGER UTIL_time_t; - UTIL_STATIC void UTIL_initTimer(UTIL_time_t* ticksPerSecond) { if (!QueryPerformanceFrequency(ticksPerSecond)) fprintf(stderr, "ERROR: QueryPerformance not present\n"); } - UTIL_STATIC void UTIL_getTime(UTIL_time_t* x) { QueryPerformanceCounter(x); } - UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000ULL*(clockEnd.QuadPart - clockStart.QuadPart)/ticksPerSecond.QuadPart; } - UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000000ULL*(clockEnd.QuadPart - clockStart.QuadPart)/ticksPerSecond.QuadPart; } +#if defined(_WIN32) /* Windows */ + typedef LARGE_INTEGER UTIL_time_t; + UTIL_STATIC UTIL_time_t UTIL_getTime(void) { UTIL_time_t x; QueryPerformanceCounter(&x); return x; } + UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t clockStart, UTIL_time_t clockEnd) + { + static LARGE_INTEGER ticksPerSecond; + static int init = 0; + if (!init) { + if (!QueryPerformanceFrequency(&ticksPerSecond)) + fprintf(stderr, "ERROR: QueryPerformanceFrequency() failure\n"); + init = 1; + } + return 1000000ULL*(clockEnd.QuadPart - clockStart.QuadPart)/ticksPerSecond.QuadPart; + } + UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t clockStart, UTIL_time_t clockEnd) + { + static LARGE_INTEGER ticksPerSecond; + static int init = 0; + if (!init) { + if (!QueryPerformanceFrequency(&ticksPerSecond)) + fprintf(stderr, "ERROR: QueryPerformanceFrequency() failure\n"); + init = 1; + } + return 1000000000ULL*(clockEnd.QuadPart - clockStart.QuadPart)/ticksPerSecond.QuadPart; + } +#elif defined(__APPLE__) && defined(__MACH__) + #include + typedef U64 UTIL_time_t; + UTIL_STATIC UTIL_time_t UTIL_getTime(void) { return mach_absolute_time(); } + UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t clockStart, UTIL_time_t clockEnd) + { + static mach_timebase_info_data_t rate; + static int init = 0; + if (!init) { + mach_timebase_info(&rate); + init = 1; + } + return (((clockEnd - clockStart) * (U64)rate.numer) / ((U64)rate.denom))/1000ULL; + } + UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t clockStart, UTIL_time_t clockEnd) + { + static mach_timebase_info_data_t rate; + static int init = 0; + if (!init) { + mach_timebase_info(&rate); + init = 1; + } + return ((clockEnd - clockStart) * (U64)rate.numer) / ((U64)rate.denom); + } +#elif (PLATFORM_POSIX_VERSION >= 200112L) + #include + typedef struct timespec UTIL_time_t; + UTIL_STATIC UTIL_time_t UTIL_getTime(void) + { + UTIL_time_t now; + if (clock_gettime(CLOCK_MONOTONIC, &now)) + fprintf(stderr, "ERROR: Failed to get time\n"); /* we could also exit() */ + return now; + } + UTIL_STATIC UTIL_time_t UTIL_getSpanTime(UTIL_time_t begin, UTIL_time_t end) + { + UTIL_time_t diff; + if (end.tv_nsec < begin.tv_nsec) { + diff.tv_sec = (end.tv_sec - 1) - begin.tv_sec; + diff.tv_nsec = (end.tv_nsec + 1000000000ULL) - begin.tv_nsec; + } else { + diff.tv_sec = end.tv_sec - begin.tv_sec; + diff.tv_nsec = end.tv_nsec - begin.tv_nsec; + } + return diff; + } + UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t begin, UTIL_time_t end) + { + UTIL_time_t const diff = UTIL_getSpanTime(begin, end); + U64 micro = 0; + micro += 1000000ULL * diff.tv_sec; + micro += diff.tv_nsec / 1000ULL; + return micro; + } + UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t begin, UTIL_time_t end) + { + UTIL_time_t const diff = UTIL_getSpanTime(begin, end); + U64 nano = 0; + nano += 1000000000ULL * diff.tv_sec; + nano += diff.tv_nsec; + return nano; + } #else /* relies on standard C (note : clock_t measurements can be wrong when using multi-threading) */ - typedef clock_t UTIL_time_t; - UTIL_STATIC void UTIL_initTimer(UTIL_time_t* ticksPerSecond) { *ticksPerSecond=0; } - UTIL_STATIC void UTIL_getTime(UTIL_time_t* x) { *x = clock(); } - UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { (void)ticksPerSecond; return 1000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; } - UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t ticksPerSecond, UTIL_time_t clockStart, UTIL_time_t clockEnd) { (void)ticksPerSecond; return 1000000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; } + typedef clock_t UTIL_time_t; + UTIL_STATIC UTIL_time_t UTIL_getTime(void) { return clock(); } + UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; } + UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; } #endif /* returns time span in microseconds */ -UTIL_STATIC U64 UTIL_clockSpanMicro( UTIL_time_t clockStart, UTIL_time_t ticksPerSecond ) +UTIL_STATIC U64 UTIL_clockSpanMicro(UTIL_time_t clockStart) { - UTIL_time_t clockEnd; - UTIL_getTime(&clockEnd); - return UTIL_getSpanTimeMicro(ticksPerSecond, clockStart, clockEnd); + UTIL_time_t const clockEnd = UTIL_getTime(); + return UTIL_getSpanTimeMicro(clockStart, clockEnd); } -UTIL_STATIC void UTIL_waitForNextTick(UTIL_time_t ticksPerSecond) +UTIL_STATIC void UTIL_waitForNextTick(void) { - UTIL_time_t clockStart, clockEnd; - UTIL_getTime(&clockStart); + UTIL_time_t const clockStart = UTIL_getTime(); + UTIL_time_t clockEnd; do { - UTIL_getTime(&clockEnd); - } while (UTIL_getSpanTimeNano(ticksPerSecond, clockStart, clockEnd) == 0); + clockEnd = UTIL_getTime(); + } while (UTIL_getSpanTimeNano(clockStart, clockEnd) == 0); } -- cgit v0.12 From 63a7f34fee5e2ecac452aee3731b7390a1eb8328 Mon Sep 17 00:00:00 2001 From: mikir Date: Mon, 30 Oct 2017 13:44:24 +0100 Subject: Separated visibility from LZ4LIB_API macro. --- lib/lz4.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/lz4.h b/lib/lz4.h index d284d63..509d942 100644 --- a/lib/lz4.h +++ b/lib/lz4.h @@ -72,20 +72,24 @@ extern "C" { /* * LZ4_DLL_EXPORT : * Enable exporting of functions when building a Windows DLL -* LZ4LIB_API : +* LZ4LIB_VISIBILITY : * Control library symbols visibility. */ +#ifndef LZ4LIB_VISIBILITY +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default"))) +# else +# define LZ4LIB_VISIBILITY +# endif +#endif #if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) -# define LZ4LIB_API __declspec(dllexport) +# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY #elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) -# define LZ4LIB_API __declspec(dllimport) /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ -#elif defined(__GNUC__) && (__GNUC__ >= 4) -# define LZ4LIB_API __attribute__ ((__visibility__ ("default"))) +# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ #else -# define LZ4LIB_API +# define LZ4LIB_API LZ4LIB_VISIBILITY #endif - /*------ Version ------*/ #define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ #define LZ4_VERSION_MINOR 8 /* for new (non-breaking) interface capabilities */ -- cgit v0.12