From d1760113eca6244d40603997467a22423e2e1a5b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 28 Feb 2017 00:14:28 -0800 Subject: [PATCH 01/60] Improved speed of ZSTD_decompressStream() When ZSTD_decompressStream() detects that there is enough space in dst to complete decompression in a single pass, delegates to ZSTD_decompress(), for an extra ~5% speed boost --- lib/decompress/zstd_decompress.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 2646c8028..305a9a876 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2100,7 +2100,7 @@ size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx, { /* pass content and size in case legacy frames are encountered */ return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, - ddict->dictContent, ddict->dictSize, + NULL, 0, ddict); } @@ -2301,6 +2301,20 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB break; } } + /* check for single-pass mode opportunity */ + if (zds->fParams.frameContentSize + && (U64)(size_t)(oend-op) >= zds->fParams.frameContentSize) { + size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend-istart); + if (cSize <= (size_t)(iend-istart)) { + size_t const decompressedSize = ZSTD_decompress_usingDDict(zds->dctx, op, oend-op, istart, cSize, zds->ddict); + if (ZSTD_isError(decompressedSize)) return decompressedSize; + ip += cSize; + op += decompressedSize; + zds->stage = zdss_init; + someMoreWork = 0; + break; + } } + /* Consume header */ ZSTD_refDDict(zds->dctx, zds->ddict); { size_t const h1Size = ZSTD_nextSrcSizeToDecompress(zds->dctx); /* == ZSTD_frameHeaderSize_prefix */ From c0b1731bce1460df837425e39fc2f3745b2d3908 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 28 Feb 2017 01:02:46 -0800 Subject: [PATCH 02/60] added test for decompression with NULL dict and NULL DDict previous version of ZSTD_decompressMultiFrame() would fail that test --- tests/fuzzer.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 79516b6cc..6fb69972a 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -108,6 +108,7 @@ static int basicUnitTests(U32 seed, double compressibility) void* const CNBuffer = malloc(CNBuffSize); void* const compressedBuffer = malloc(ZSTD_compressBound(CNBuffSize)); void* const decodedBuffer = malloc(CNBuffSize); + ZSTD_DCtx* dctx = ZSTD_createDCtx(); int testResult = 0; U32 testNb=0; size_t cSize; @@ -155,6 +156,16 @@ static int basicUnitTests(U32 seed, double compressibility) } } DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(4, "test%3i : decompress with null dict : ", testNb++); + { size_t const r = ZSTD_decompress_usingDict(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize, NULL, 0); + if (r != CNBuffSize) goto _output_error; } + DISPLAYLEVEL(4, "OK \n"); + + DISPLAYLEVEL(4, "test%3i : decompress with null DDict : ", testNb++); + { size_t const r = ZSTD_decompress_usingDDict(dctx, decodedBuffer, CNBuffSize, compressedBuffer, cSize, NULL); + if (r != CNBuffSize) goto _output_error; } + DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(4, "test%3i : decompress with 1 missing byte : ", testNb++); { size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize-1); if (!ZSTD_isError(r)) goto _output_error; @@ -210,7 +221,6 @@ static int basicUnitTests(U32 seed, double compressibility) /* Dictionary and CCtx Duplication tests */ { ZSTD_CCtx* const ctxOrig = ZSTD_createCCtx(); ZSTD_CCtx* const ctxDuplicated = ZSTD_createCCtx(); - ZSTD_DCtx* const dctx = ZSTD_createDCtx(); static const size_t dictSize = 551; DISPLAYLEVEL(4, "test%3i : copy context too soon : ", testNb++); @@ -283,12 +293,10 @@ static int basicUnitTests(U32 seed, double compressibility) ZSTD_freeCCtx(ctxOrig); ZSTD_freeCCtx(ctxDuplicated); - ZSTD_freeDCtx(dctx); } /* Dictionary and dictBuilder tests */ { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); - ZSTD_DCtx* const dctx = ZSTD_createDCtx(); size_t dictSize = 16 KB; void* dictBuffer = malloc(dictSize); size_t const totalSampleSize = 1 MB; @@ -370,14 +378,12 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "OK \n"); ZSTD_freeCCtx(cctx); - ZSTD_freeDCtx(dctx); free(dictBuffer); free(samplesSizes); } /* COVER dictionary builder tests */ { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); - ZSTD_DCtx* const dctx = ZSTD_createDCtx(); size_t dictSize = 16 KB; size_t optDictSize = dictSize; void* dictBuffer = malloc(dictSize); @@ -414,7 +420,7 @@ static int basicUnitTests(U32 seed, double compressibility) memset(¶ms, 0, sizeof(params)); params.steps = 4; optDictSize = COVER_optimizeTrainFromBuffer(dictBuffer, optDictSize, - CNBuffer, samplesSizes, nbSamples, + CNBuffer, samplesSizes, nbSamples / 4, ¶ms); if (ZDICT_isError(optDictSize)) goto _output_error; DISPLAYLEVEL(4, "OK, created dictionary of size %u \n", (U32)optDictSize); @@ -425,7 +431,6 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(4, "OK : %u \n", dictID); ZSTD_freeCCtx(cctx); - ZSTD_freeDCtx(dctx); free(dictBuffer); free(samplesSizes); } @@ -445,7 +450,6 @@ static int basicUnitTests(U32 seed, double compressibility) /* block API tests */ { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); - ZSTD_DCtx* const dctx = ZSTD_createDCtx(); static const size_t dictSize = 65 KB; static const size_t blockSize = 100 KB; /* won't cause pb with small dict size */ size_t cSize2; From a33ae6420491e4aa6819041452a108df96977b02 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 28 Feb 2017 01:15:28 -0800 Subject: [PATCH 03/60] fixed decoding skippable frames --- lib/decompress/zstd_decompress.c | 9 ++++----- tests/zstreamtest.c | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 305a9a876..1252b2c37 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1469,8 +1469,7 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) if (ZSTD_isError(headerSize)) return headerSize; /* Frame Header */ - { - size_t const ret = ZSTD_getFrameParams(&fParams, ip, remainingSize); + { size_t const ret = ZSTD_getFrameParams(&fParams, ip, remainingSize); if (ZSTD_isError(ret)) return ret; if (ret > 0) return ERROR(srcSize_wrong); } @@ -1503,7 +1502,7 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) } /*! ZSTD_decompressFrame() : -* `dctx` must be properly initialized */ +* @dctx must be properly initialized */ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void** srcPtr, size_t *srcSizePtr) @@ -1570,7 +1569,7 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, remainingSize -= 4; } - // Allow caller to get size read + /* Allow caller to get size read */ *srcPtr = ip; *srcSizePtr = remainingSize; return op-ostart; @@ -2302,7 +2301,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB } } /* check for single-pass mode opportunity */ - if (zds->fParams.frameContentSize + if (zds->fParams.frameContentSize && zds->fParams.windowSize /* skippable frame if == 0 */ && (U64)(size_t)(oend-op) >= zds->fParams.frameContentSize) { size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend-istart); if (cSize <= (size_t)(iend-istart)) { diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index c22a284c7..54b890266 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -218,7 +218,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo outBuff.pos = 0; { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (r != 0) goto _output_error; } - if (outBuff.pos != 0) goto _output_error; /* skippable frame len is 0 */ + if (outBuff.pos != 0) goto _output_error; /* skippable frame output len is 0 */ DISPLAYLEVEL(3, "OK \n"); /* Basic decompression test */ From 59709d97d9541605c166d02ba055e3814ea634cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20=C5=A0ev=C4=8D=C3=ADk?= Date: Thu, 9 Feb 2017 15:10:24 +0100 Subject: [PATCH 04/60] Support building contrib utils from cmake --- build/cmake/CMakeLists.txt | 4 ++++ build/cmake/contrib/CMakeLists.txt | 16 ++++++++++++++ build/cmake/contrib/pzstd/CMakeLists.txt | 28 ++++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 build/cmake/contrib/CMakeLists.txt create mode 100644 build/cmake/contrib/pzstd/CMakeLists.txt diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index b8f5d18e4..f28f74118 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -11,6 +11,7 @@ PROJECT(zstd) CMAKE_MINIMUM_REQUIRED(VERSION 2.8.7) OPTION(ZSTD_LEGACY_SUPPORT "LEGACY SUPPORT" OFF) +OPTION(ZSTD_BUILD_CONTRIB "BUILD CONTRIB" OFF) IF (ZSTD_LEGACY_SUPPORT) MESSAGE(STATUS "ZSTD_LEGACY_SUPPORT defined!") @@ -23,6 +24,9 @@ ENDIF (ZSTD_LEGACY_SUPPORT) ADD_SUBDIRECTORY(lib) ADD_SUBDIRECTORY(programs) ADD_SUBDIRECTORY(tests) +IF (ZSTD_BUILD_CONTRIB) + ADD_SUBDIRECTORY(contrib) +ENDIF (ZSTD_BUILD_CONTRIB) #----------------------------------------------------------------------------- # Add extra compilation flags diff --git a/build/cmake/contrib/CMakeLists.txt b/build/cmake/contrib/CMakeLists.txt new file mode 100644 index 000000000..68e0881c5 --- /dev/null +++ b/build/cmake/contrib/CMakeLists.txt @@ -0,0 +1,16 @@ +# ################################################################ +# * Copyright (c) 2015-present, Yann Collet, Facebook, Inc. +# * All rights reserved. +# * +# * This source code is licensed under the BSD-style license found in the +# * LICENSE file in the root directory of this source tree. An additional grant +# * of patent rights can be found in the PATENTS file in the same directory. +# +# You can contact the author at : +# - zstd homepage : http://www.zstd.net/ +# ################################################################ + +PROJECT(contrib) + +ADD_SUBDIRECTORY(pzstd) + diff --git a/build/cmake/contrib/pzstd/CMakeLists.txt b/build/cmake/contrib/pzstd/CMakeLists.txt new file mode 100644 index 000000000..6699fcc87 --- /dev/null +++ b/build/cmake/contrib/pzstd/CMakeLists.txt @@ -0,0 +1,28 @@ +# ################################################################ +# * Copyright (c) 2015-present, Yann Collet, Facebook, Inc. +# * All rights reserved. +# * +# * This source code is licensed under the BSD-style license found in the +# * LICENSE file in the root directory of this source tree. An additional grant +# * of patent rights can be found in the PATENTS file in the same directory. +# +# You can contact the author at : +# - zstd homepage : http://www.zstd.net/ +# ################################################################ + +PROJECT(pzstd) + +SET(CMAKE_INCLUDE_CURRENT_DIR TRUE) + +# Define project root directory +SET(ROOT_DIR ../../../..) + +# Define programs directory, where sources and header files are located +SET(LIBRARY_DIR ${ROOT_DIR}/lib) +SET(PROGRAMS_DIR ${ROOT_DIR}/programs) +SET(PZSTD_DIR ${ROOT_DIR}/contrib/pzstd) +INCLUDE_DIRECTORIES(${PROGRAMS_DIR} ${LIBRARY_DIR} ${LIBRARY_DIR}/common ${PZSTD_DIR}) + +ADD_EXECUTABLE(pzstd ${PZSTD_DIR}/main.cpp ${PZSTD_DIR}/Options.cpp ${PZSTD_DIR}/Pzstd.cpp ${PZSTD_DIR}/SkippableFrame.cpp) +TARGET_LINK_LIBRARIES(pzstd libzstd_static pthread) + From bf8a30ce0d0e8f2b45a954fcf396220050f9172d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20=C5=A0ev=C4=8D=C3=ADk?= Date: Thu, 9 Feb 2017 15:11:05 +0100 Subject: [PATCH 05/60] Add zstdmt target in cmake --- build/cmake/CMakeLists.txt | 1 + build/cmake/programs/CMakeLists.txt | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index f28f74118..6b7c28925 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -11,6 +11,7 @@ PROJECT(zstd) CMAKE_MINIMUM_REQUIRED(VERSION 2.8.7) OPTION(ZSTD_LEGACY_SUPPORT "LEGACY SUPPORT" OFF) +OPTION(ZSTD_MULTITHREAD_SUPPORT "MULTITHREADING SUPPORT" ON) OPTION(ZSTD_BUILD_CONTRIB "BUILD CONTRIB" OFF) IF (ZSTD_LEGACY_SUPPORT) diff --git a/build/cmake/programs/CMakeLists.txt b/build/cmake/programs/CMakeLists.txt index cb3dc6e89..c88ee5cc9 100644 --- a/build/cmake/programs/CMakeLists.txt +++ b/build/cmake/programs/CMakeLists.txt @@ -34,9 +34,17 @@ ENDIF (MSVC) ADD_EXECUTABLE(zstd ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/fileio.c ${PROGRAMS_DIR}/bench.c ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/dibio.c ${PlatformDependResources}) TARGET_LINK_LIBRARIES(zstd libzstd_static) - IF (UNIX) ADD_EXECUTABLE(zstd-frugal ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/fileio.c) TARGET_LINK_LIBRARIES(zstd-frugal libzstd_static) SET_TARGET_PROPERTIES(zstd-frugal PROPERTIES COMPILE_DEFINITIONS "ZSTD_NOBENCH;ZSTD_NODICT") ENDIF (UNIX) + +IF (ZSTD_MULTITHREAD_SUPPORT) + ADD_EXECUTABLE(zstdmt ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/fileio.c ${PROGRAMS_DIR}/bench.c ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/dibio.c ${PlatformDependResources}) + SET_TARGET_PROPERTIES(zstdmt PROPERTIES COMPILE_DEFINITIONS "ZSTD_MULTITHREAD") + TARGET_LINK_LIBRARIES(zstdmt libzstd_static) + IF (UNIX) + TARGET_LINK_LIBRARIES(zstdmt pthread) + ENDIF (UNIX) +ENDIF (ZSTD_MULTITHREAD_SUPPORT) From 5a1cc5c22d149135b93c9c48b0c71a8b1f7c0caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20=C5=A0ev=C4=8D=C3=ADk?= Date: Fri, 10 Feb 2017 12:29:55 +0100 Subject: [PATCH 06/60] Improve handling of library symlinks. Previous method was failing to remove the symlinks when make clean was invoked and wasn't portable. --- build/cmake/lib/CMakeLists.txt | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/build/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt index 265f7aeb8..1950d97cd 100644 --- a/build/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -166,23 +166,17 @@ IF (UNIX) SET(SHARED_LIBRARY_SYMLINK1_PATH ${CMAKE_CURRENT_BINARY_DIR}/${SHARED_LIBRARY_SYMLINK1}) SET(SHARED_LIBRARY_SYMLINK2_PATH ${CMAKE_CURRENT_BINARY_DIR}/${SHARED_LIBRARY_SYMLINK2}) - if (EXISTS ${SHARED_LIBRARY_SYMLINK1_PATH}) - FILE(REMOVE ${SHARED_LIBRARY_SYMLINK1_PATH}) - endif (EXISTS ${SHARED_LIBRARY_SYMLINK1_PATH}) - - if (EXISTS ${SHARED_LIBRARY_SYMLINK2_PATH}) - FILE(REMOVE ${SHARED_LIBRARY_SYMLINK2_PATH}) - endif (EXISTS ${SHARED_LIBRARY_SYMLINK2_PATH}) + ADD_CUSTOM_COMMAND(TARGET libzstd_shared POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink ${SHARED_LIBRARY_LINK} ${SHARED_LIBRARY_SYMLINK1} + DEPENDS ${SHARED_LIBRARY_LINK_PATH} + COMMENT "Generating symbolic link ${SHARED_LIBRARY_LINK} -> ${SHARED_LIBRARY_SYMLINK1}") ADD_CUSTOM_COMMAND(TARGET libzstd_shared POST_BUILD - COMMAND ln -s ${SHARED_LIBRARY_LINK} ${SHARED_LIBRARY_SYMLINK1} - DEPENDS ${SHARED_LIBRARY_LINK_PATH} - COMMENT "Generating symbolic link") + COMMAND ${CMAKE_COMMAND} -E create_symlink ${SHARED_LIBRARY_LINK} ${SHARED_LIBRARY_SYMLINK2} + DEPENDS ${SHARED_LIBRARY_LINK_PATH} + COMMENT "Generating symbolic link ${SHARED_LIBRARY_LINK} -> ${SHARED_LIBRARY_SYMLINK2}") - ADD_CUSTOM_COMMAND(TARGET libzstd_shared POST_BUILD - COMMAND ln -s ${SHARED_LIBRARY_LINK} ${SHARED_LIBRARY_SYMLINK2} - DEPENDS ${SHARED_LIBRARY_LINK_PATH} - COMMENT "Generating symbolic link") + SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${SHARED_LIBRARY_SYMLINK1};${SHARED_LIBRARY_SYMLINK2}") INSTALL(FILES ${SHARED_LIBRARY_SYMLINK1_PATH} DESTINATION ${INSTALL_LIBRARY_DIR}) INSTALL(FILES ${SHARED_LIBRARY_SYMLINK2_PATH} DESTINATION ${INSTALL_LIBRARY_DIR}) From eeb080e6015ecc67a99d912d0376d83e6c81a79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20=C5=A0ev=C4=8D=C3=ADk?= Date: Mon, 27 Feb 2017 13:56:04 +0100 Subject: [PATCH 07/60] -Wstrict-prototypes is not supported with C++ --- build/cmake/CMakeModules/AddExtraCompilationFlags.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/cmake/CMakeModules/AddExtraCompilationFlags.cmake b/build/cmake/CMakeModules/AddExtraCompilationFlags.cmake index 2d59fabb0..e480c7ead 100644 --- a/build/cmake/CMakeModules/AddExtraCompilationFlags.cmake +++ b/build/cmake/CMakeModules/AddExtraCompilationFlags.cmake @@ -132,10 +132,8 @@ MACRO(ADD_EXTRA_COMPILATION_FLAGS) endif (ACTIVATE_WARNING_CAST_ALIGN) if (ACTIVATE_WARNING_STRICT_PROTOTYPES) - list(APPEND CMAKE_CXX_FLAGS ${WARNING_STRICT_PROTOTYPES}) list(APPEND CMAKE_C_FLAGS ${WARNING_STRICT_PROTOTYPES}) else () - string(REPLACE ${WARNING_STRICT_PROTOTYPES} "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") string(REPLACE ${WARNING_STRICT_PROTOTYPES} "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") endif (ACTIVATE_WARNING_STRICT_PROTOTYPES) From 4b62f419698d313647f3a111caf2c2b804711a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20=C5=A0ev=C4=8D=C3=ADk?= Date: Mon, 27 Feb 2017 14:44:49 +0100 Subject: [PATCH 08/60] Added compile flags to pzstd Definition NDEBUG from original Makefile -Wno-shadow silences shadowing in initializers --- build/cmake/contrib/pzstd/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/cmake/contrib/pzstd/CMakeLists.txt b/build/cmake/contrib/pzstd/CMakeLists.txt index 6699fcc87..2a3663f31 100644 --- a/build/cmake/contrib/pzstd/CMakeLists.txt +++ b/build/cmake/contrib/pzstd/CMakeLists.txt @@ -25,4 +25,6 @@ INCLUDE_DIRECTORIES(${PROGRAMS_DIR} ${LIBRARY_DIR} ${LIBRARY_DIR}/common ${PZSTD ADD_EXECUTABLE(pzstd ${PZSTD_DIR}/main.cpp ${PZSTD_DIR}/Options.cpp ${PZSTD_DIR}/Pzstd.cpp ${PZSTD_DIR}/SkippableFrame.cpp) TARGET_LINK_LIBRARIES(pzstd libzstd_static pthread) +SET_TARGET_PROPERTIES(pzstd PROPERTIES COMPILE_DEFINITIONS "NDEBUG") +SET_TARGET_PROPERTIES(pzstd PROPERTIES COMPILE_OPTIONS "-Wno-shadow") From 22d79762ef3e5c3760975742af10e8874958c13b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 28 Feb 2017 02:12:42 -0800 Subject: [PATCH 09/60] fixed multi frames --- lib/decompress/zstd_decompress.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 1252b2c37..5aa863afc 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2307,8 +2307,9 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB if (cSize <= (size_t)(iend-istart)) { size_t const decompressedSize = ZSTD_decompress_usingDDict(zds->dctx, op, oend-op, istart, cSize, zds->ddict); if (ZSTD_isError(decompressedSize)) return decompressedSize; - ip += cSize; + ip = istart + cSize; op += decompressedSize; + zds->dctx->expected = 0; zds->stage = zdss_init; someMoreWork = 0; break; From a81d4fee5822ed15e15922334197a1f0bd3e09c1 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Tue, 28 Feb 2017 15:28:29 -0800 Subject: [PATCH 10/60] Check to ensure ddict isn't null before dereference --- lib/decompress/zstd_decompress.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 2646c8028..0504778e4 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1576,6 +1576,9 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, return op-ostart; } +static const void* ZSTD_DDictDictContent(const ZSTD_DDict* ddict); +static size_t ZSTD_DDictDictSize(const ZSTD_DDict* ddict); + static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, @@ -1583,6 +1586,17 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) { void* const dststart = dst; + + if (ddict) { + if (dict) { + /* programmer error, these two cases should be mutually exclusive */ + return ERROR(GENERIC); + } + + dict = ZSTD_DDictDictContent(ddict); + dictSize = ZSTD_DDictDictSize(ddict); + } + while (srcSize >= ZSTD_frameHeaderSize_prefix) { U32 magicNumber; @@ -1938,6 +1952,16 @@ struct ZSTD_DDict_s { ZSTD_customMem cMem; }; /* typedef'd to ZSTD_DDict within "zstd.h" */ +static const void* ZSTD_DDictDictContent(const ZSTD_DDict* ddict) +{ + return ddict->dictContent; +} + +static size_t ZSTD_DDictDictSize(const ZSTD_DDict* ddict) +{ + return ddict->dictSize; +} + static void ZSTD_refDDict(ZSTD_DCtx* dstDCtx, const ZSTD_DDict* ddict) { ZSTD_decompressBegin(dstDCtx); /* init */ @@ -2100,7 +2124,7 @@ size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx, { /* pass content and size in case legacy frames are encountered */ return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, - ddict->dictContent, ddict->dictSize, + NULL, 0, ddict); } From 43764cdb1dbb2c8889eb5861e8f48ea0a4450990 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 28 Feb 2017 17:44:17 -0800 Subject: [PATCH 11/60] updated NEWS for 1.1.4 cmake, performance --- NEWS | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 7ede5f70c..8b7d83c8a 100644 --- a/NEWS +++ b/NEWS @@ -3,9 +3,11 @@ cli : new : can compress in *.gz format, using --format=gzip command, by Przemys cli : new : advanced benchmark command --priority=rt cli : fix : write on sparse-enabled file systems in 32-bits mode, by @ds77 cli : fix : --rm remains silent when input is stdin +speed : improved decompression speed in streaming mode for single shot scenarios (+5%) +memory : DDict (decompression dictionary) memory usage down from 150 KB to 20 KB API : new : ZSTD_findFrameCompressedSize(), ZSTD_getFrameContentSize(), ZSTD_findDecompressedSize(), by Sean Purcell -API : change : ZSTD_compress*() with srcSize==0 create an empty-frame of known size -build:new : meson build system in contrib/meson, by Dima Krasner +build: new: meson build system in contrib/meson, by Dima Krasner +build: improved cmake script, by @Majlen doc : new : educational decoder, by Sean Purcell v1.1.3 From 4bcc69b7616ea7ec39eab75676905a64b2750bed Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 1 Mar 2017 11:33:25 -0800 Subject: [PATCH 12/60] solves warnings when compiling with global XXH_STATIC_LINKING_ONLY XXH_STATIC_LINKING_ONLY protection macro is intended to be triggered just before the include. The main idea is to keep this setting local : user module shall explicitly understand and accept the static linking restriction which becomes transparent when triggering the macro at project level. Global definition also triggers redefinition warnings for user modules which do locally define the macro. This new version compiles lib and cli without warning when the macro is set globally. That's not a scenario to be recommended, since it trades a local effect for a global one, but it was easy enough to provide from zstd side. --- lib/common/xxhash.c | 4 +++- lib/common/zstd_internal.h | 4 ++++ lib/compress/zstd_compress.c | 2 -- lib/compress/zstdmt_compress.c | 2 -- lib/decompress/zstd_decompress.c | 2 -- lib/legacy/zstd_v07.c | 13 ++++++++----- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/common/xxhash.c b/lib/common/xxhash.c index 29e4fa628..eb44222c5 100644 --- a/lib/common/xxhash.c +++ b/lib/common/xxhash.c @@ -104,7 +104,9 @@ static void XXH_free (void* p) { free(p); } #include static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } -#define XXH_STATIC_LINKING_ONLY +#ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY +#endif #include "xxhash.h" diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 4b56ce1a2..5c5b28732 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -49,6 +49,10 @@ #include "error_private.h" #define ZSTD_STATIC_LINKING_ONLY #include "zstd.h" +#ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ +#endif +#include "xxhash.h" /* XXH_reset, update, digest */ /*-************************************* diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index ec758db00..994e606e6 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -13,8 +13,6 @@ ***************************************/ #include /* memset */ #include "mem.h" -#define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ -#include "xxhash.h" /* XXH_reset, update, digest */ #define FSE_STATIC_LINKING_ONLY /* FSE_encodeSymbol */ #include "fse.h" #define HUF_STATIC_LINKING_ONLY diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 483ea157e..45514a81a 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -25,8 +25,6 @@ #include "threading.h" /* mutex */ #include "zstd_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */ #include "zstdmt_compress.h" -#define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ -#include "xxhash.h" /* ====== Debug ====== */ diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 61c7936f8..32b1747c6 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -43,8 +43,6 @@ *********************************************************/ #include /* memcpy, memmove, memset */ #include "mem.h" /* low level memory routines */ -#define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ -#include "xxhash.h" /* XXH64_* */ #define FSE_STATIC_LINKING_ONLY #include "fse.h" #define HUF_STATIC_LINKING_ONLY diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c index e67916b3c..a54ad0ffd 100644 --- a/lib/legacy/zstd_v07.c +++ b/lib/legacy/zstd_v07.c @@ -13,12 +13,14 @@ #include /* memcpy */ #include /* malloc, free, qsort */ -#define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ -#include "xxhash.h" /* XXH64_* */ +#ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ +#endif +#include "xxhash.h" /* XXH64_* */ #include "zstd_v07.h" -#define FSEv07_STATIC_LINKING_ONLY /* FSEv07_MIN_TABLELOG */ -#define HUFv07_STATIC_LINKING_ONLY /* HUFv07_TABLELOG_ABSOLUTEMAX */ +#define FSEv07_STATIC_LINKING_ONLY /* FSEv07_MIN_TABLELOG */ +#define HUFv07_STATIC_LINKING_ONLY /* HUFv07_TABLELOG_ABSOLUTEMAX */ #define ZSTDv07_STATIC_LINKING_ONLY #include "error_private.h" @@ -4536,7 +4538,8 @@ size_t ZBUFFv07_decompressContinue(ZBUFFv07_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 */ + /* break; */ + /* pass-through */ } } case ZBUFFds_flush: From 76f04940898bf062ad4504d95fe741c0c784cb24 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 1 Mar 2017 13:29:29 -0800 Subject: [PATCH 13/60] xxhash can be included twice in any order Previously, followed by : would fail to include the static definitions, because the second include was simply skipped by guard macro. Now it works as intended : the missing static part is included during the second include. --- lib/common/xxhash.h | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/lib/common/xxhash.h b/lib/common/xxhash.h index 2c9b7c61b..9bad1f59f 100644 --- a/lib/common/xxhash.h +++ b/lib/common/xxhash.h @@ -64,16 +64,12 @@ XXH64 13.8 GB/s 1.9 GB/s XXH32 6.8 GB/s 6.0 GB/s */ -#ifndef XXHASH_H_5627135585666179 -#define XXHASH_H_5627135585666179 1 - #if defined (__cplusplus) extern "C" { #endif -#ifndef XXH_NAMESPACE -# define XXH_NAMESPACE ZSTD_ /* Zstandard specific */ -#endif +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 /* **************************** @@ -242,6 +238,11 @@ XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* restrict dst_state, const XXH /* ************************** * Canonical representation ****************************/ +/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. +* The canonical representation uses human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. +*/ typedef struct { unsigned char digest[4]; } XXH32_canonical_t; typedef struct { unsigned char digest[8]; } XXH64_canonical_t; @@ -251,14 +252,9 @@ XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); -/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. -* The canonical representation uses human-readable write convention, aka big-endian (large digits first). -* These functions allow transformation of hash result into and from its canonical format. -* This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. -*/ +#endif /* XXHASH_H_5627135585666179 */ -#ifdef XXH_STATIC_LINKING_ONLY /* ================================================================================================ This section contains definitions which are not guaranteed to remain stable. @@ -266,6 +262,8 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src They shall only be used with static linking. Never use these definitions in association with dynamic linking ! =================================================================================================== */ +#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXH_STATIC_H_3543687687345) +#define XXH_STATIC_H_3543687687345 /* These definitions are only meant to allow allocation of XXH state statically, on stack, or in a struct for example. @@ -299,11 +297,9 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src # include "xxhash.c" /* include xxhash functions as `static`, for inlining */ # endif -#endif /* XXH_STATIC_LINKING_ONLY */ +#endif /* XXH_STATIC_LINKING_ONLY && XXH_STATIC_H_3543687687345 */ #if defined (__cplusplus) } #endif - -#endif /* XXHASH_H_5627135585666179 */ From d44703d145611945d7b92eeb3c93ad9fbe6fd480 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Wed, 1 Mar 2017 14:36:25 -0800 Subject: [PATCH 14/60] Offsets >= 32MB in 32-bits mode --- lib/common/bitstream.h | 3 +++ lib/compress/zstd_compress.c | 39 ++++++++++++++++++++++++++++---- lib/decompress/zstd_decompress.c | 26 +++++++++++++++++---- lib/zstd.h | 2 +- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/lib/common/bitstream.h b/lib/common/bitstream.h index 3a45244f8..d3873002e 100644 --- a/lib/common/bitstream.h +++ b/lib/common/bitstream.h @@ -60,6 +60,9 @@ extern "C" { # include /* support for bextr (experimental) */ #endif +#define STREAM_ACCUMULATOR_MIN_32 25 +#define STREAM_ACCUMULATOR_MIN_64 57 +#define STREAM_ACCUMULATOR_MIN ((U32)(MEM_32bits() ? STREAM_ACCUMULATOR_MIN_32 : STREAM_ACCUMULATOR_MIN_64)) /*-****************************************** * bitStream encoding API (write forward) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index ec758db00..89f6575dc 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -579,9 +579,9 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) } -size_t ZSTD_compressSequences(ZSTD_CCtx* zc, +FORCE_INLINE size_t ZSTD_compressSequences_generic (ZSTD_CCtx* zc, void* dst, size_t dstCapacity, - size_t srcSize) + size_t srcSize, int const longOffsets) { const seqStore_t* seqStorePtr = &(zc->seqStore); U32 count[MaxSeq+1]; @@ -716,7 +716,18 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* zc, if (MEM_32bits()) BIT_flushBits(&blockStream); BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]); if (MEM_32bits()) BIT_flushBits(&blockStream); - BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]); + if (longOffsets) { + U32 const ofBits = ofCodeTable[nbSeq-1]; + int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); + if (extraBits) { + BIT_addBits(&blockStream, sequences[nbSeq-1].offset, extraBits); + BIT_flushBits(&blockStream); + } + BIT_addBits(&blockStream, sequences[nbSeq-1].offset >> extraBits, + ofBits - extraBits); + } else { + BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]); + } BIT_flushBits(&blockStream); { size_t n; @@ -738,7 +749,17 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* zc, if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream); BIT_addBits(&blockStream, sequences[n].matchLength, mlBits); if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/ - BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ + if (longOffsets) { + int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1); + if (extraBits) { + BIT_addBits(&blockStream, sequences[n].offset, extraBits); + BIT_flushBits(&blockStream); /* (7)*/ + } + BIT_addBits(&blockStream, sequences[n].offset >> extraBits, + ofBits - extraBits); /* 31 */ + } else { + BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */ + } BIT_flushBits(&blockStream); /* (7)*/ } } @@ -763,6 +784,16 @@ _check_compressibility: return op - ostart; } +FORCE_INLINE size_t ZSTD_compressSequences (ZSTD_CCtx* zc, + void* dst, size_t dstCapacity, + size_t srcSize) +{ + if (zc->params.cParams.windowLog > STREAM_ACCUMULATOR_MIN) { + return ZSTD_compressSequences_generic(zc, dst, dstCapacity, srcSize, 1); + } else { + return ZSTD_compressSequences_generic(zc, dst, dstCapacity, srcSize, 0); + } +} #if 0 /* for debug */ # define STORESEQ_DEBUG diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 2646c8028..e39bf42bf 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1144,7 +1144,7 @@ static size_t ZSTD_decompressSequences( } -static seq_t ZSTD_decodeSequenceLong(seqState_t* seqState) +FORCE_INLINE seq_t ZSTD_decodeSequenceLong_generic(seqState_t* seqState, int const longOffsets) { seq_t seq; @@ -1179,8 +1179,15 @@ static seq_t ZSTD_decodeSequenceLong(seqState_t* seqState) if (!ofCode) offset = 0; else { - offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ - if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); + if (longOffsets) { + int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN); + offset = OF_base[ofCode] + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); + if (MEM_32bits() || extraBits) BIT_reloadDStream(&seqState->DStream); + if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits); + } else { + offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ + if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); + } } if (ofCode <= 1) { @@ -1224,6 +1231,14 @@ static seq_t ZSTD_decodeSequenceLong(seqState_t* seqState) return seq; } +static seq_t ZSTD_decodeSequenceLong(seqState_t* seqState, unsigned const windowSize) { + if (ZSTD_highbit32(windowSize) > STREAM_ACCUMULATOR_MIN) { + return ZSTD_decodeSequenceLong_generic(seqState, 1); + } else { + return ZSTD_decodeSequenceLong_generic(seqState, 0); + } +} + FORCE_INLINE size_t ZSTD_execSequenceLong(BYTE* op, BYTE* const oend, seq_t sequence, @@ -1321,6 +1336,7 @@ static size_t ZSTD_decompressSequencesLong( const BYTE* const base = (const BYTE*) (dctx->base); const BYTE* const vBase = (const BYTE*) (dctx->vBase); const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd); + unsigned const windowSize = dctx->fParams.windowSize; int nbSeq; /* Build Decoding Tables */ @@ -1350,13 +1366,13 @@ static size_t ZSTD_decompressSequencesLong( /* prepare in advance */ for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && seqNb Date: Wed, 1 Mar 2017 16:49:20 -0800 Subject: [PATCH 15/60] added gzip tests also : made sure zstd --format=gzip -V would fail if gzip compatibility is not supported --- programs/bench.c | 1 + programs/zstdcli.c | 4 +++- tests/playTests.sh | 35 ++++++++++++++++++++++++++++------- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 663b30743..2dd1cfb0f 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -581,6 +581,7 @@ int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, const char* di { double const compressibility = (double)g_compressibilityDefault / 100; + if (cLevel < 1) cLevel = 1; /* minimum compression level */ if (cLevel > ZSTD_maxCLevel()) cLevel = ZSTD_maxCLevel(); if (cLevelLast > ZSTD_maxCLevel()) cLevelLast = ZSTD_maxCLevel(); if (cLevelLast < cLevel) cLevelLast = cLevel; diff --git a/programs/zstdcli.c b/programs/zstdcli.c index a7b4fddc8..050d7a6a2 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -370,10 +370,12 @@ int main(int argCount, const char* argv[]) if (!strcmp(argument, "--keep")) { FIO_setRemoveSrcFile(0); continue; } if (!strcmp(argument, "--rm")) { FIO_setRemoveSrcFile(1); continue; } if (!strcmp(argument, "--priority=rt")) { setRealTimePrio = 1; continue; } +#ifdef ZSTD_GZCOMPRESS if (!strcmp(argument, "--format=gzip")) { suffix = GZ_EXTENSION; FIO_setCompressionType(FIO_gzipCompression); continue; } +#endif /* long commands with arguments */ -#ifndef ZSTD_NODICT +#ifndef ZSTD_NODICT if (longCommandWArg(&argument, "--cover=")) { cover=1; if (!parseCoverParameters(argument, &coverParams)) CLEAN_RETURN(badusage(programName)); continue; diff --git a/tests/playTests.sh b/tests/playTests.sh index 88c0ecdfe..5933b14e6 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -304,11 +304,32 @@ $ECHO "\n**** benchmark mode tests **** " $ECHO "bench one file" ./datagen > tmp1 -$ZSTD -bi1 tmp1 +$ZSTD -bi0 tmp1 $ECHO "bench multiple levels" -$ZSTD -i1b1e3 tmp1 +$ZSTD -i0b0e3 tmp1 $ECHO "with recursive and quiet modes" -$ZSTD -rqi1b1e3 tmp1 +$ZSTD -rqi1b1e2 tmp1 + + +$ECHO "\n**** gzip compatibility tests **** " + +GZIPMODE=1 +$ZSTD --format=gzip -V || GZIPMODE=0 +if [ $GZIPMODE -eq 1 ]; then + GZIPEXE=1 + which gzip || GZIPEXE=0 + if [ $GZIPEXE -eq 1 ]; then + ./datagen > tmp + $ZSTD --format=gzip -f tmp + gzip -t -v tmp.gz + gzip -f tmp + $ZSTD -d -f -v tmp.gz + else + $ECHO "gzip binary not detected" + fi +else + $ECHO "gzip mode not supported" +fi $ECHO "\n**** zstd round-trip tests **** " @@ -317,10 +338,10 @@ roundTripTest roundTripTest -g15K # TableID==3 roundTripTest -g127K # TableID==2 roundTripTest -g255K # TableID==1 -roundTripTest -g513K # TableID==0 -roundTripTest -g512K 6 # greedy, hash chain -roundTripTest -g512K 16 # btlazy2 -roundTripTest -g512K 19 # btopt +roundTripTest -g522K # TableID==0 +roundTripTest -g519K 6 # greedy, hash chain +roundTripTest -g517K 16 # btlazy2 +roundTripTest -g516K 19 # btopt rm tmp* From 27526c7201bc4cf1b5ad66ad746e20671cab3ae1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 1 Mar 2017 17:02:49 -0800 Subject: [PATCH 16/60] make : added target shortest shortest only run fast part of playTests.sh . cc @iburinoc --- Makefile | 6 +++++- tests/Makefile | 3 +++ tests/playTests.sh | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c73393cdd..0f97e6c5b 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,10 @@ zstdmt: zlibwrapper: $(MAKE) -C $(ZWRAPDIR) test +.PHONY: shortest +shortest: + $(MAKE) -C $(TESTDIR) $@ + .PHONY: test test: $(MAKE) -C $(TESTDIR) $@ @@ -173,7 +177,7 @@ ppcinstall: arminstall: APT_PACKAGES="qemu-system-arm qemu-user-static gcc-powerpc-linux-gnu gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross" $(MAKE) apt-install -valgrindinstall: +valgrindinstall: APT_PACKAGES="valgrind" $(MAKE) apt-install libc6install: diff --git a/tests/Makefile b/tests/Makefile index 30b2a04a3..88c58fce4 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -253,6 +253,9 @@ zstd-playTests: datagen file $(ZSTD) ZSTD="$(QEMU_SYS) $(ZSTD)" ./playTests.sh $(ZSTDRTTEST) +shortest: ZSTDRTTEST= +shortest: test-zstd + test: test-zstd test-fullbench test-fuzzer test-zstream test-invalidDictionaries test-legacy test-decodecorpus ifeq ($(QEMU_SYS),) test: test-pool diff --git a/tests/playTests.sh b/tests/playTests.sh index 5933b14e6..653aaf3c8 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -316,8 +316,9 @@ $ECHO "\n**** gzip compatibility tests **** " GZIPMODE=1 $ZSTD --format=gzip -V || GZIPMODE=0 if [ $GZIPMODE -eq 1 ]; then + $ECHO "gzip support detected" GZIPEXE=1 - which gzip || GZIPEXE=0 + gzip -V || GZIPEXE=0 if [ $GZIPEXE -eq 1 ]; then ./datagen > tmp $ZSTD --format=gzip -f tmp From 78208bd8be0a7e19a066214c053f52534d2729f4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 1 Mar 2017 21:02:06 -0800 Subject: [PATCH 17/60] fixed : build zstd cli after libzstd --- programs/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/programs/Makefile b/programs/Makefile index db718d14c..407a4f374 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -24,7 +24,9 @@ else ALIGN_LOOP = endif -CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress -I$(ZSTDDIR)/dictBuilder +CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ + -I$(ZSTDDIR)/dictBuilder \ + -DXXH_NAMESPACE=ZSTD_ # because xxhash.o already compiled with this macro from library CFLAGS ?= -O3 DEBUGFLAGS = -g -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ From 3475b9b43100a2a8b8e08ae2b09f8aafc2af758a Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 2 Mar 2017 12:33:02 -0800 Subject: [PATCH 18/60] Set dictID to 0 for content only dictionaries --- lib/decompress/zstd_decompress.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 61c7936f8..b7c668d39 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1989,6 +1989,7 @@ static void ZSTD_refDDict(ZSTD_DCtx* dstDCtx, const ZSTD_DDict* ddict) static size_t ZSTD_loadEntropy_inDDict(ZSTD_DDict* ddict) { + ddict->dictID = 0; ddict->entropyPresent = 0; if (ddict->dictSize < 8) return 0; { U32 const magic = MEM_readLE32(ddict->dictContent); From a419777eb12392159aefd6c28fd1e3cc8862ce2e Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 1 Mar 2017 17:51:56 -0800 Subject: [PATCH 19/60] Allow compressor to repeat Huffman tables * Compressor saves most recently used Huffman table and reuses it if it produces better results. * I attempted to preserve CPU usage profile. I intentionally left all of the existing heuristics in place. There is only a speed difference on the second block and later. When compressing large enough blocks (say >= 4 KiB) there is no significant difference in compression speed. Dictionary compression of one block is the same speed for blocks with literals <= 1 KiB, and after that the difference is not very significant. * In the synthetic data, with blocks 10 KB or smaller, most blocks can't use repeated tables because the previous block did not contain a symbol that the current block contains. Once blocks are about 12 KB or more, most previous blocks have valid Huffman tables for the current block, and the compression ratio and decompression speed jumped. * In silesia blocks as small as 4KB can frequently reuse the previous Huffman table (85%), but it isn't as profitable, and the previous Huffman table only gets used about 3% of the time. * Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns and `HUF_estimateCompressedSize()` takes ~35 ns. They are decently well optimized, the first versions took 90 ns and 120 ns respectively. `HUF_validateCTable()` could be twice as fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0. However, `U32` has an alignment of 4 instead of 2, so I think that might be undefined behavior. * I've ran `zstreamtest` compiled normally, with UASAN and with MSAN for 4 hours each. The worst case for the speed difference is a bunch of small blocks in the same frame. I modified `bench.c` to compress the input in a single frame but with blocks of the given block size, set by `-B`. Benchmarks on level 1: | Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s | |-----------|------------|-----------|-------|------------------|--------------------| | zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 | | zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 | | zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 | | zstd | 256 | silesia | 2.204 | 93.4 | 415.7 | | zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 | | zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 | | zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 | | zstd | 512 | silesia | 2.358 | 119.8 | 432.6 | | zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 | | zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 | | zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 | | zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 | | zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 | | zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 | | zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 | | zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 | | zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 | | zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 | | zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 | | zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 | --- lib/common/huf.h | 15 +++++ lib/compress/huf_compress.c | 117 +++++++++++++++++++++++++++-------- lib/compress/zstd_compress.c | 45 +++++++++----- 3 files changed, 136 insertions(+), 41 deletions(-) diff --git a/lib/common/huf.h b/lib/common/huf.h index 9427ae8cb..89a226ea0 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -168,6 +168,16 @@ size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSym size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog); size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); +typedef enum { + HUF_repeat_none, /**< Cannot use the previous table */ + HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, 4}X_repeat */ + HUF_repeat_valid /**< Can use the previous table and it is asumed to be valid */ + } HUF_repeat; +/** HUF_compress4X_repeat() : +* Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. +* If it uses hufTable it does not modify hufTable or repeat. +* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. */ +size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat); /**< `workSpace` must be a table of at least 1024 unsigned */ /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. @@ -216,6 +226,11 @@ size_t HUF_decompress4X4_usingDTable(void* dst, size_t maxDstSize, const void* c size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least 1024 unsigned */ size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); +/** HUF_compress1X_repeat() : +* Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. +* If it uses hufTable it does not modify hufTable or repeat. +* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. */ +size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat); /**< `workSpace` must be a table of at least 1024 unsigned */ size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */ diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 7869ccf64..8b914cbc6 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -409,6 +409,25 @@ size_t HUF_buildCTable (HUF_CElt* tree, const U32* count, U32 maxSymbolValue, U3 return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, nodeTable, sizeof(nodeTable)); } +static size_t HUF_estimateCompressedSize(HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) +{ + size_t nbBits = 0; + int s; + for (s = 0; s <= (int)maxSymbolValue; ++s) { + nbBits += CTable[s].nbBits * count[s]; + } + return nbBits >> 3; +} + +static int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) { + int bad = 0; + int s; + for (s = 0; s <= (int)maxSymbolValue; ++s) { + bad |= (count[s] != 0) & (CTable[s].nbBits == 0); + } + return !bad; +} + static void HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable) { BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits); @@ -510,22 +529,37 @@ size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, si } +static size_t HUF_compressCTable_internal( + BYTE* const ostart, BYTE* op, BYTE* const oend, + const void* src, size_t srcSize, + unsigned singleStream, const HUF_CElt* CTable) +{ + size_t const cSize = singleStream ? + HUF_compress1X_usingCTable(op, oend - op, src, srcSize, CTable) : + HUF_compress4X_usingCTable(op, oend - op, src, srcSize, CTable); + if (HUF_isError(cSize)) { return cSize; } + if (cSize==0) { return 0; } /* uncompressible */ + op += cSize; + /* check compressibility */ + if ((size_t)(op-ostart) >= srcSize-1) { return 0; } + return op-ostart; +} + + /* `workSpace` must a table of at least 1024 unsigned */ static size_t HUF_compress_internal ( void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, unsigned singleStream, - void* workSpace, size_t wkspSize) + void* workSpace, size_t wkspSize, HUF_CElt* oldHufTable, HUF_repeat* repeat) { BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstSize; BYTE* op = ostart; - union { - U32 count[HUF_SYMBOLVALUE_MAX+1]; - HUF_CElt CTable[HUF_SYMBOLVALUE_MAX+1]; - } table; /* `count` can overlap with `CTable`; saves 1 KB */ + U32 count[HUF_SYMBOLVALUE_MAX+1]; + HUF_CElt CTable[HUF_SYMBOLVALUE_MAX+1]; /* checks & inits */ if (wkspSize < sizeof(huffNodeTable)) return ERROR(GENERIC); @@ -536,38 +570,51 @@ static size_t HUF_compress_internal ( if (!maxSymbolValue) maxSymbolValue = HUF_SYMBOLVALUE_MAX; if (!huffLog) huffLog = HUF_TABLELOG_DEFAULT; + /* Heuristic : If we don't need to check the validity of the old table use the old table for small inputs */ + if (srcSize <= 1024 && repeat && *repeat == HUF_repeat_valid) { + return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable); + } + /* Scan input and build symbol stats */ - { CHECK_V_F(largest, FSE_count_wksp (table.count, &maxSymbolValue, (const BYTE*)src, srcSize, (U32*)workSpace) ); + { CHECK_V_F(largest, FSE_count_wksp (count, &maxSymbolValue, (const BYTE*)src, srcSize, (U32*)workSpace) ); if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */ if (largest <= (srcSize >> 7)+1) return 0; /* Fast heuristic : not compressible enough */ } + /* Check validity of previous table */ + if (repeat && *repeat == HUF_repeat_check && !HUF_validateCTable(oldHufTable, count, maxSymbolValue)) { + *repeat = HUF_repeat_none; + } + /* Heuristic : use existing table for small inputs */ + if (srcSize <= 1024 && repeat && *repeat != HUF_repeat_none) { + return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable); + } + /* Build Huffman Tree */ huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue); - { CHECK_V_F(maxBits, HUF_buildCTable_wksp (table.CTable, table.count, maxSymbolValue, huffLog, workSpace, wkspSize) ); + { CHECK_V_F(maxBits, HUF_buildCTable_wksp (CTable, count, maxSymbolValue, huffLog, workSpace, wkspSize) ); huffLog = (U32)maxBits; + /* Zero the unused symbols so we can check it for validity */ + memset(CTable + maxSymbolValue + 1, 0, sizeof(CTable) - (maxSymbolValue + 1) * sizeof(HUF_CElt)); } /* Write table description header */ - { CHECK_V_F(hSize, HUF_writeCTable (op, dstSize, table.CTable, maxSymbolValue, huffLog) ); - if (hSize + 12 >= srcSize) return 0; /* not useful to try compression */ + { CHECK_V_F(hSize, HUF_writeCTable (op, dstSize, CTable, maxSymbolValue, huffLog) ); + /* Check if using the previous table will be beneficial */ + if (repeat && *repeat != HUF_repeat_none) { + size_t const oldSize = HUF_estimateCompressedSize(oldHufTable, count, maxSymbolValue); + size_t const newSize = HUF_estimateCompressedSize(CTable, count, maxSymbolValue); + if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) { + return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable); + } + } + /* Use the new table */ + if (hSize + 12ul >= srcSize) { return 0; } op += hSize; + if (repeat) { *repeat = HUF_repeat_none; } + if (oldHufTable) { memcpy(oldHufTable, CTable, sizeof(CTable)); } /* Save the new table */ } - - /* Compress */ - { size_t const cSize = (singleStream) ? - HUF_compress1X_usingCTable(op, oend - op, src, srcSize, table.CTable) : /* single segment */ - HUF_compress4X_usingCTable(op, oend - op, src, srcSize, table.CTable); - if (HUF_isError(cSize)) return cSize; - if (cSize==0) return 0; /* uncompressible */ - op += cSize; - } - - /* check compressibility */ - if ((size_t)(op-ostart) >= srcSize-1) - return 0; - - return op-ostart; + return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, CTable); } @@ -576,7 +623,16 @@ size_t HUF_compress1X_wksp (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize); + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, NULL, NULL); +} + +size_t HUF_compress1X_repeat (void* dst, size_t dstSize, + const void* src, size_t srcSize, + unsigned maxSymbolValue, unsigned huffLog, + void* workSpace, size_t wkspSize, + HUF_CElt* hufTable, HUF_repeat* repeat) +{ + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, hufTable, repeat); } size_t HUF_compress1X (void* dst, size_t dstSize, @@ -592,7 +648,16 @@ size_t HUF_compress4X_wksp (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize); + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, NULL, NULL); +} + +size_t HUF_compress4X_repeat (void* dst, size_t dstSize, + const void* src, size_t srcSize, + unsigned maxSymbolValue, unsigned huffLog, + void* workSpace, size_t wkspSize, + HUF_CElt* hufTable, HUF_repeat* repeat) +{ + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, hufTable, repeat); } size_t HUF_compress2 (void* dst, size_t dstSize, diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index ec758db00..538cb685c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -81,6 +81,7 @@ struct ZSTD_CCtx_s { U32* chainTable; HUF_CElt* hufTable; U32 flagStaticTables; + HUF_repeat flagStaticHufTable; 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)]; @@ -254,8 +255,11 @@ static size_t ZSTD_resetCCtx_advanced (ZSTD_CCtx* zc, ZSTD_compResetPolicy_e const crp) { if (crp == ZSTDcrp_continue) - if (ZSTD_equivalentParams(params, zc->params)) + if (ZSTD_equivalentParams(params, zc->params)) { + zc->flagStaticTables = 0; + zc->flagStaticHufTable = HUF_repeat_none; return ZSTD_continueCCtx(zc, params, frameContentSize); + } { size_t const blockSize = MIN(ZSTD_BLOCKSIZE_ABSOLUTEMAX, (size_t)1 << params.cParams.windowLog); U32 const divider = (params.cParams.searchLength==3) ? 3 : 4; @@ -289,6 +293,7 @@ static size_t ZSTD_resetCCtx_advanced (ZSTD_CCtx* zc, ptr = zc->hashTable3 + h3Size; zc->hufTable = (HUF_CElt*)ptr; zc->flagStaticTables = 0; + zc->flagStaticHufTable = HUF_repeat_none; ptr = ((U32*)ptr) + 256; /* note : HUF_CElt* is incomplete type, size is simulated using U32 */ zc->nextToUpdate = 1; @@ -374,12 +379,15 @@ size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long /* copy entropy tables */ dstCCtx->flagStaticTables = srcCCtx->flagStaticTables; + dstCCtx->flagStaticHufTable = srcCCtx->flagStaticHufTable; if (srcCCtx->flagStaticTables) { - memcpy(dstCCtx->hufTable, srcCCtx->hufTable, 256*4); memcpy(dstCCtx->litlengthCTable, srcCCtx->litlengthCTable, sizeof(dstCCtx->litlengthCTable)); memcpy(dstCCtx->matchlengthCTable, srcCCtx->matchlengthCTable, sizeof(dstCCtx->matchlengthCTable)); memcpy(dstCCtx->offcodeCTable, srcCCtx->offcodeCTable, sizeof(dstCCtx->offcodeCTable)); } + if (srcCCtx->flagStaticHufTable) { + memcpy(dstCCtx->hufTable, srcCCtx->hufTable, 256*4); + } return 0; } @@ -493,24 +501,27 @@ static size_t ZSTD_compressLiterals (ZSTD_CCtx* zc, /* small ? don't even attempt compression (speed opt) */ # define LITERAL_NOENTROPY 63 - { size_t const minLitSize = zc->flagStaticTables ? 6 : LITERAL_NOENTROPY; + { size_t const minLitSize = zc->flagStaticHufTable == HUF_repeat_valid ? 6 : LITERAL_NOENTROPY; if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } if (dstCapacity < lhSize+1) return ERROR(dstSize_tooSmall); /* not enough space for compression */ - if (zc->flagStaticTables && (lhSize==3)) { - hType = set_repeat; - singleStream = 1; - cLitSize = HUF_compress1X_usingCTable(ostart+lhSize, dstCapacity-lhSize, src, srcSize, zc->hufTable); - } else { - cLitSize = singleStream ? HUF_compress1X_wksp(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, zc->tmpCounters, sizeof(zc->tmpCounters)) - : HUF_compress4X_wksp(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, zc->tmpCounters, sizeof(zc->tmpCounters)); + { HUF_repeat repeat = zc->flagStaticHufTable; + if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1; + cLitSize = singleStream ? HUF_compress1X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, zc->tmpCounters, sizeof(zc->tmpCounters), zc->hufTable, &repeat) + : HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, zc->tmpCounters, sizeof(zc->tmpCounters), zc->hufTable, &repeat); + if (repeat != HUF_repeat_none) { hType = set_repeat; } /* reused the existing table */ + else { zc->flagStaticHufTable = HUF_repeat_check; } /* now have a table to reuse */ } - if ((cLitSize==0) | (cLitSize >= srcSize - minGain)) + if ((cLitSize==0) | (cLitSize >= srcSize - minGain)) { + zc->flagStaticHufTable = HUF_repeat_none; return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); - if (cLitSize==1) + } + if (cLitSize==1) { + zc->flagStaticHufTable = HUF_repeat_none; return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); + } /* Build header */ switch(lhSize) @@ -753,9 +764,12 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* zc, /* check compressibility */ _check_compressibility: - { size_t const minGain = ZSTD_minGain(srcSize); - size_t const maxCSize = srcSize - minGain; - if ((size_t)(op-ostart) >= maxCSize) return 0; } + { size_t const minGain = ZSTD_minGain(srcSize); + size_t const maxCSize = srcSize - minGain; + if ((size_t)(op-ostart) >= maxCSize) { + zc->flagStaticHufTable = HUF_repeat_none; + return 0; + } } /* confirm repcodes */ { int i; for (i=0; irep[i] = zc->repToConfirm[i]; } @@ -2606,6 +2620,7 @@ static size_t ZSTD_loadDictEntropyStats(ZSTD_CCtx* cctx, const void* dict, size_ } cctx->flagStaticTables = 1; + cctx->flagStaticHufTable = HUF_repeat_valid; return dictPtr - (const BYTE*)dict; } From 553f67e0c1f62c06a5e9e6efc1016feb8bb7b398 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 2 Mar 2017 15:15:31 -0800 Subject: [PATCH 20/60] Remove 'generic' inline strategy Seems to avoid performance loss for compression. Same strategy tested on decompression side, did not appear to improve speed. --- lib/compress/zstd_compress.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 5d81f8a38..6b8047206 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -576,11 +576,11 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) mlCodeTable[seqStorePtr->longLengthPos] = MaxML; } - -FORCE_INLINE size_t ZSTD_compressSequences_generic (ZSTD_CCtx* zc, +MEM_STATIC size_t ZSTD_compressSequences (ZSTD_CCtx* zc, void* dst, size_t dstCapacity, - size_t srcSize, int const longOffsets) + size_t srcSize) { + const int longOffsets = zc->params.cParams.windowLog > STREAM_ACCUMULATOR_MIN; const seqStore_t* seqStorePtr = &(zc->seqStore); U32 count[MaxSeq+1]; S16 norm[MaxSeq+1]; @@ -782,17 +782,6 @@ _check_compressibility: return op - ostart; } -FORCE_INLINE size_t ZSTD_compressSequences (ZSTD_CCtx* zc, - void* dst, size_t dstCapacity, - size_t srcSize) -{ - if (zc->params.cParams.windowLog > STREAM_ACCUMULATOR_MIN) { - return ZSTD_compressSequences_generic(zc, dst, dstCapacity, srcSize, 1); - } else { - return ZSTD_compressSequences_generic(zc, dst, dstCapacity, srcSize, 0); - } -} - #if 0 /* for debug */ # define STORESEQ_DEBUG #include /* fprintf */ From 976e325b2e4baf69ba1d20013def951f7c7382ab Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 2 Mar 2017 15:54:39 -0800 Subject: [PATCH 21/60] Fix COVER_optimizeTrainFromBuffer() resource leaks Thanks to @nemequ for reporting the resource leaks. --- lib/dictBuilder/cover.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c index 1ced645b5..3a7b9f39f 100644 --- a/lib/dictBuilder/cover.c +++ b/lib/dictBuilder/cover.c @@ -966,6 +966,7 @@ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d)) { LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n"); COVER_best_destroy(&best); + POOL_free(pool); return ERROR(GENERIC); } /* Loop through k reusing the same context */ @@ -978,6 +979,7 @@ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n"); COVER_best_destroy(&best); COVER_ctx_destroy(&ctx); + POOL_free(pool); return ERROR(GENERIC); } data->ctx = &ctx; @@ -990,6 +992,7 @@ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, /* Check the parameters */ if (!COVER_checkParameters(data->parameters)) { DISPLAYLEVEL(1, "Cover parameters incorrect\n"); + free(data); continue; } /* Call the function and pass ownership of data to it */ @@ -1012,8 +1015,10 @@ ZDICTLIB_API size_t COVER_optimizeTrainFromBuffer(void *dictBuffer, { const size_t dictSize = best.dictSize; if (ZSTD_isError(best.compressedSize)) { + const size_t compressedSize = best.compressedSize; COVER_best_destroy(&best); - return best.compressedSize; + POOL_free(pool); + return compressedSize; } *parameters = best.parameters; memcpy(dictBuffer, best.dict, dictSize); From d051cd5b430e60e8ea8edc89ff1f73844378c3ff Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 2 Mar 2017 16:38:07 -0800 Subject: [PATCH 22/60] Use workspace for count and CTable --- lib/common/huf.h | 12 ++++++++---- lib/compress/huf_compress.c | 19 ++++++++++++++----- lib/compress/zstd_compress.c | 2 +- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/common/huf.h b/lib/common/huf.h index 89a226ea0..1315f1190 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -91,7 +91,7 @@ size_t HUF_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize /** HUF_compress4X_wksp() : * Same as HUF_compress2(), but uses externally allocated `workSpace`, which must be a table of >= 1024 unsigned */ -size_t HUF_compress4X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least 1024 unsigned */ +size_t HUF_compress4X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ @@ -133,6 +133,10 @@ typedef U32 HUF_DTable; #define HUF_CREATE_STATIC_DTABLEX4(DTable, maxTableLog) \ HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = { ((U32)(maxTableLog) * 0x01000001) } +/* The workspace must have alignment at least 4 and be at least this large */ +#define HUF_WORKSPACE_SIZE (6 << 10) +#define HUF_WORKSPACE_SIZE_U32 (HUF_WORKSPACE_SIZE / sizeof(U32)) + /* **************************************** * Advanced decompression functions @@ -177,7 +181,7 @@ typedef enum { * Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. * If it uses hufTable it does not modify hufTable or repeat. * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. */ -size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat); /**< `workSpace` must be a table of at least 1024 unsigned */ +size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. @@ -224,13 +228,13 @@ size_t HUF_decompress4X4_usingDTable(void* dst, size_t maxDstSize, const void* c /* single stream variants */ size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog); -size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least 1024 unsigned */ +size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable); /** HUF_compress1X_repeat() : * Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. * If it uses hufTable it does not modify hufTable or repeat. * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. */ -size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat); /**< `workSpace` must be a table of at least 1024 unsigned */ +size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */ diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index 8b914cbc6..d103c3b22 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -558,11 +558,13 @@ static size_t HUF_compress_internal ( BYTE* const oend = ostart + dstSize; BYTE* op = ostart; - U32 count[HUF_SYMBOLVALUE_MAX+1]; - HUF_CElt CTable[HUF_SYMBOLVALUE_MAX+1]; + U32* count; + size_t const countSize = sizeof(U32) * (HUF_SYMBOLVALUE_MAX + 1); + HUF_CElt* CTable; + size_t const CTableSize = sizeof(HUF_CElt) * (HUF_SYMBOLVALUE_MAX + 1); /* checks & inits */ - if (wkspSize < sizeof(huffNodeTable)) return ERROR(GENERIC); + if (wkspSize < sizeof(huffNodeTable) + countSize + CTableSize) return ERROR(GENERIC); if (!srcSize) return 0; /* Uncompressed (note : 1 means rle, so first byte must be correct) */ if (!dstSize) return 0; /* cannot fit within dst budget */ if (srcSize > HUF_BLOCKSIZE_MAX) return ERROR(srcSize_wrong); /* current block size limit */ @@ -570,6 +572,13 @@ static size_t HUF_compress_internal ( if (!maxSymbolValue) maxSymbolValue = HUF_SYMBOLVALUE_MAX; if (!huffLog) huffLog = HUF_TABLELOG_DEFAULT; + count = (U32*)workSpace; + workSpace = (BYTE*)workSpace + countSize; + wkspSize -= countSize; + CTable = (HUF_CElt*)workSpace; + workSpace = (BYTE*)workSpace + CTableSize; + wkspSize -= CTableSize; + /* Heuristic : If we don't need to check the validity of the old table use the old table for small inputs */ if (srcSize <= 1024 && repeat && *repeat == HUF_repeat_valid) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable); @@ -595,7 +604,7 @@ static size_t HUF_compress_internal ( { CHECK_V_F(maxBits, HUF_buildCTable_wksp (CTable, count, maxSymbolValue, huffLog, workSpace, wkspSize) ); huffLog = (U32)maxBits; /* Zero the unused symbols so we can check it for validity */ - memset(CTable + maxSymbolValue + 1, 0, sizeof(CTable) - (maxSymbolValue + 1) * sizeof(HUF_CElt)); + memset(CTable + maxSymbolValue + 1, 0, CTableSize - (maxSymbolValue + 1) * sizeof(HUF_CElt)); } /* Write table description header */ @@ -612,7 +621,7 @@ static size_t HUF_compress_internal ( if (hSize + 12ul >= srcSize) { return 0; } op += hSize; if (repeat) { *repeat = HUF_repeat_none; } - if (oldHufTable) { memcpy(oldHufTable, CTable, sizeof(CTable)); } /* Save the new table */ + if (oldHufTable) { memcpy(oldHufTable, CTable, CTableSize); } /* Save the new table */ } return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, CTable); } diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 538cb685c..a77d674b2 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -85,7 +85,7 @@ struct ZSTD_CCtx_s { 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)]; - unsigned tmpCounters[1024]; + unsigned tmpCounters[HUF_WORKSPACE_SIZE_U32]; }; ZSTD_CCtx* ZSTD_createCCtx(void) From fe5d27062e83f6f55fdee3c80f31f51fbf10f7bb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 2 Mar 2017 17:09:21 -0800 Subject: [PATCH 23/60] disable prefetch-decode for 32-bits target This decoder variant is detrimental to x86 architecture likely due to register pressure. Note that the variant is disabled for all 32-bits targets. It's unclear if it would help for different architectures, such as ARM, MIPS or PowerPC. --- lib/decompress/zstd_decompress.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 4dfdf20fb..482c334ff 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1410,13 +1410,18 @@ static size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, if (srcSize >= ZSTD_BLOCKSIZE_ABSOLUTEMAX) return ERROR(srcSize_wrong); - /* Decode literals sub-block */ + /* Decode literals section */ { size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize); if (ZSTD_isError(litCSize)) return litCSize; ip += litCSize; srcSize -= litCSize; } - if (dctx->fParams.windowSize > (1<<23)) return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize); + if (sizeof(size_t) > 4) /* do not enable prefetching on 32-bits x86, as it's performance detrimental */ + /* likely because of register pressure */ + /* if that's the correct cause, then 32-bits ARM should be affected differently */ + /* it would be good to test this on ARM real hardware, to see if prefetch version improves speed */ + if (dctx->fParams.windowSize > (1<<23)) + return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize); return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize); } From e02409fdc3bf56243c7106d45faa1fd02b1dbaf7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 2 Mar 2017 17:14:57 -0800 Subject: [PATCH 24/60] update NEWS on @iburinoc's 32-bits version improvement --- NEWS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 8b7d83c8a..760724a25 100644 --- a/NEWS +++ b/NEWS @@ -5,7 +5,8 @@ cli : fix : write on sparse-enabled file systems in 32-bits mode, by @ds77 cli : fix : --rm remains silent when input is stdin speed : improved decompression speed in streaming mode for single shot scenarios (+5%) memory : DDict (decompression dictionary) memory usage down from 150 KB to 20 KB -API : new : ZSTD_findFrameCompressedSize(), ZSTD_getFrameContentSize(), ZSTD_findDecompressedSize(), by Sean Purcell +arch : 32-bits variant able to generate and decode very long matches (>32 MB), by Sean Purcell +API : new : ZSTD_findFrameCompressedSize(), ZSTD_getFrameContentSize(), ZSTD_findDecompressedSize() build: new: meson build system in contrib/meson, by Dima Krasner build: improved cmake script, by @Majlen doc : new : educational decoder, by Sean Purcell From 54c4babd8f67d40cbc63c2cd2cd77914ec80d4ee Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 3 Mar 2017 12:30:24 -0800 Subject: [PATCH 25/60] Always check Huffman tables for ZSTD_lazy+ The compressor always reuses the existing Huffman table if the literals size is at most 1 KiB. If the compression strategy is `ZSTD_lazy` or stronger always check to see if reusing the previous table or creating a new table is better. This doesn't yet weigh in decompression speed. I don't want to add any heuristics there until I have real data to work with to ensure that the heuristic works for at least one use case, preferably more. --- lib/common/huf.h | 10 ++++++---- lib/compress/huf_compress.c | 19 ++++++++++--------- lib/compress/zstd_compress.c | 5 +++-- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/common/huf.h b/lib/common/huf.h index 1315f1190..691f2e764 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -180,8 +180,9 @@ typedef enum { /** HUF_compress4X_repeat() : * Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. * If it uses hufTable it does not modify hufTable or repeat. -* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. */ -size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ +* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. +* If preferRepeat then the old table will always be used if valid. */ +size_t HUF_compress4X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ /** HUF_buildCTable_wksp() : * Same as HUF_buildCTable(), but using externally allocated scratch buffer. @@ -233,8 +234,9 @@ size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, si /** HUF_compress1X_repeat() : * Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. * If it uses hufTable it does not modify hufTable or repeat. -* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. */ -size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ +* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. +* If preferRepeat then the old table will always be used if valid. */ +size_t HUF_compress1X_repeat(void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize, HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */ size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */ size_t HUF_decompress1X4 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */ diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index d103c3b22..fe11aafb8 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -552,7 +552,8 @@ static size_t HUF_compress_internal ( const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, unsigned singleStream, - void* workSpace, size_t wkspSize, HUF_CElt* oldHufTable, HUF_repeat* repeat) + void* workSpace, size_t wkspSize, + HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat) { BYTE* const ostart = (BYTE*)dst; BYTE* const oend = ostart + dstSize; @@ -580,7 +581,7 @@ static size_t HUF_compress_internal ( wkspSize -= CTableSize; /* Heuristic : If we don't need to check the validity of the old table use the old table for small inputs */ - if (srcSize <= 1024 && repeat && *repeat == HUF_repeat_valid) { + if (preferRepeat && repeat && *repeat == HUF_repeat_valid) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable); } @@ -595,7 +596,7 @@ static size_t HUF_compress_internal ( *repeat = HUF_repeat_none; } /* Heuristic : use existing table for small inputs */ - if (srcSize <= 1024 && repeat && *repeat != HUF_repeat_none) { + if (preferRepeat && repeat && *repeat != HUF_repeat_none) { return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable); } @@ -632,16 +633,16 @@ size_t HUF_compress1X_wksp (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, NULL, NULL); + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, NULL, NULL, 0); } size_t HUF_compress1X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, - HUF_CElt* hufTable, HUF_repeat* repeat) + HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, hufTable, repeat); + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, hufTable, repeat, preferRepeat); } size_t HUF_compress1X (void* dst, size_t dstSize, @@ -657,16 +658,16 @@ size_t HUF_compress4X_wksp (void* dst, size_t dstSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, NULL, NULL); + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, NULL, NULL, 0); } size_t HUF_compress4X_repeat (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void* workSpace, size_t wkspSize, - HUF_CElt* hufTable, HUF_repeat* repeat) + HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat) { - return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, hufTable, repeat); + return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, hufTable, repeat, preferRepeat); } size_t HUF_compress2 (void* dst, size_t dstSize, diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 432323e21..15f063e18 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -505,9 +505,10 @@ static size_t ZSTD_compressLiterals (ZSTD_CCtx* zc, if (dstCapacity < lhSize+1) return ERROR(dstSize_tooSmall); /* not enough space for compression */ { HUF_repeat repeat = zc->flagStaticHufTable; + int const preferRepeat = zc->params.cParams.strategy < ZSTD_lazy ? srcSize <= 1024 : 0; if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1; - cLitSize = singleStream ? HUF_compress1X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, zc->tmpCounters, sizeof(zc->tmpCounters), zc->hufTable, &repeat) - : HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, zc->tmpCounters, sizeof(zc->tmpCounters), zc->hufTable, &repeat); + cLitSize = singleStream ? HUF_compress1X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, zc->tmpCounters, sizeof(zc->tmpCounters), zc->hufTable, &repeat, preferRepeat) + : HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, zc->tmpCounters, sizeof(zc->tmpCounters), zc->hufTable, &repeat, preferRepeat); if (repeat != HUF_repeat_none) { hType = set_repeat; } /* reused the existing table */ else { zc->flagStaticHufTable = HUF_repeat_check; } /* now have a table to reuse */ } From 38a3428b37a3b21d2a699b8e1db1f10af57588b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Sun, 5 Mar 2017 19:36:56 -0800 Subject: [PATCH 26/60] support -Werror=format-security Fedora now enables this option by default, resulting in the following build failure: Logging.h: In instantiation of 'void pzstd::Logger::operator()(int, const char*, Args ...) Pzstd.cpp:413:48: required from here Logging.h:46:17: error: format not a string literal and no format arguments [-Werror=format-security] std::fprintf(out_, fmt, args...); ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~ --- .buckconfig | 2 +- contrib/pzstd/Pzstd.cpp | 6 +++--- contrib/pzstd/Pzstd.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.buckconfig b/.buckconfig index b2b9c036f..d698b35ba 100644 --- a/.buckconfig +++ b/.buckconfig @@ -2,7 +2,7 @@ cppflags = -DXXH_NAMESPACE=ZSTD_ -DZSTD_LEGACY_SUPPORT=1 cflags = -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef -Wpointer-arith cxxppflags = -DXXH_NAMESPACE=ZSTD_ -DZSTD_LEGACY_SUPPORT=1 - cxxflags = -std=c++11 -Wno-format-security -Wno-deprecated-declarations + cxxflags = -std=c++11 -Wno-deprecated-declarations gtest_dep = //contrib/pzstd:gtest [httpserver] diff --git a/contrib/pzstd/Pzstd.cpp b/contrib/pzstd/Pzstd.cpp index f4cb19d9c..1265b53ef 100644 --- a/contrib/pzstd/Pzstd.cpp +++ b/contrib/pzstd/Pzstd.cpp @@ -410,7 +410,7 @@ std::uint64_t asyncCompressChunks( }); // Pass the output queue to the writer thread. chunks.push(std::move(out)); - state.log(VERBOSE, "Starting a new frame\n"); + state.log(VERBOSE, "%s\n", "Starting a new frame"); // Fill the input queue for the compression job we just started status = readData(*in, ZSTD_CStreamInSize(), step, fd, &bytesRead); } @@ -547,8 +547,8 @@ std::uint64_t asyncDecompressFrames( if (frameSize == 0) { // We hit a non SkippableFrame ==> not compressed by pzstd or corrupted // Pass the rest of the source to this decompression task - state.log(VERBOSE, - "Input not in pzstd format, falling back to serial decompression\n"); + state.log(VERBOSE, "%s\n", + "Input not in pzstd format, falling back to serial decompression"); while (status == FileStatus::Continue && !state.errorHolder.hasError()) { status = readData(*in, chunkSize, chunkSize, fd, &totalBytesRead); } diff --git a/contrib/pzstd/Pzstd.h b/contrib/pzstd/Pzstd.h index dc60dd9b8..1e29a7170 100644 --- a/contrib/pzstd/Pzstd.h +++ b/contrib/pzstd/Pzstd.h @@ -41,7 +41,7 @@ class SharedState { auto parameters = options.determineParameters(); cStreamPool.reset(new ResourcePool{ [this, parameters]() -> ZSTD_CStream* { - this->log(VERBOSE, "Creating new ZSTD_CStream\n"); + this->log(VERBOSE, "%s\n", "Creating new ZSTD_CStream"); auto zcs = ZSTD_createCStream(); if (zcs) { auto err = ZSTD_initCStream_advanced( @@ -59,7 +59,7 @@ class SharedState { } else { dStreamPool.reset(new ResourcePool{ [this]() -> ZSTD_DStream* { - this->log(VERBOSE, "Creating new ZSTD_DStream\n"); + this->log(VERBOSE, "%s\n", "Creating new ZSTD_DStream"); auto zds = ZSTD_createDStream(); if (zds) { auto err = ZSTD_initDStream(zds); From 1f2c95c5f3f6995d0efdca18577da74336e8e586 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 5 Mar 2017 21:07:20 -0800 Subject: [PATCH 27/60] minor code refactor in HUF module --- lib/common/entropy_common.c | 14 ++------------ lib/common/huf.h | 7 ++++--- lib/decompress/huf_decompress.c | 10 +++++----- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/lib/common/entropy_common.c b/lib/common/entropy_common.c index 72bc398da..b37a082fe 100644 --- a/lib/common/entropy_common.c +++ b/lib/common/entropy_common.c @@ -43,25 +43,15 @@ #include "huf.h" -/*-**************************************** -* Version -******************************************/ +/*=== Version ===*/ unsigned FSE_versionNumber(void) { return FSE_VERSION_NUMBER; } -/*-**************************************** -* FSE Error Management -******************************************/ +/*=== Error Management ===*/ unsigned FSE_isError(size_t code) { return ERR_isError(code); } - const char* FSE_getErrorName(size_t code) { return ERR_getErrorName(code); } - -/* ************************************************************** -* HUF Error Management -****************************************************************/ unsigned HUF_isError(size_t code) { return ERR_isError(code); } - const char* HUF_getErrorName(size_t code) { return ERR_getErrorName(code); } diff --git a/lib/common/huf.h b/lib/common/huf.h index 691f2e764..e5572760a 100644 --- a/lib/common/huf.h +++ b/lib/common/huf.h @@ -102,10 +102,11 @@ size_t HUF_compress4X_wksp (void* dst, size_t dstSize, const void* src, size_t s /* *** Constants *** */ -#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ -#define HUF_TABLELOG_MAX 12 /* max configured tableLog (for static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */ +#define HUF_TABLELOG_MAX 12 /* max configured tableLog (for static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */ #define HUF_TABLELOG_DEFAULT 11 /* tableLog by default, when not specified */ -#define HUF_SYMBOLVALUE_MAX 255 +#define HUF_SYMBOLVALUE_MAX 255 + +#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ #if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX) # error "HUF_TABLELOG_MAX is too large !" #endif diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 0f11f5c1b..6e76cc435 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -108,10 +108,10 @@ size_t HUF_readDTableX2 (HUF_DTable* DTable, const void* src, size_t srcSize) memcpy(DTable, &dtd, sizeof(dtd)); } - /* Prepare ranks */ + /* Calculate starting value for each rank */ { U32 n, nextRankStart = 0; for (n=1; n> 1; - U32 i; + U32 u; HUF_DEltX2 D; D.byte = (BYTE)n; D.nbBits = (BYTE)(tableLog + 1 - w); - for (i = rankVal[w]; i < rankVal[w] + length; i++) - dt[i] = D; + for (u = rankVal[w]; u < rankVal[w] + length; u++) + dt[u] = D; rankVal[w] += length; } } From 8b1d004031b2b78b535b6ce369510e1b91f87cc3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 5 Mar 2017 21:17:32 -0800 Subject: [PATCH 28/60] added -Wformat-security flag, as recommended by @pixelb --- Makefile | 6 +++++- NEWS | 1 + lib/Makefile | 2 +- programs/Makefile | 2 +- tests/Makefile | 5 +++-- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 0f97e6c5b..f0492d944 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ EXT = endif .PHONY: default -default: lib zstd-release +default: lib-release zstd-release .PHONY: all all: | allmost examples manual @@ -42,6 +42,10 @@ all32: .PHONY: lib lib: + @$(MAKE) -C $(ZSTDDIR) $@ + +.PHONY: lib-release +lib-release: @$(MAKE) -C $(ZSTDDIR) .PHONY: zstd diff --git a/NEWS b/NEWS index 760724a25..9073a8724 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ arch : 32-bits variant able to generate and decode very long matches (>32 MB), b API : new : ZSTD_findFrameCompressedSize(), ZSTD_getFrameContentSize(), ZSTD_findDecompressedSize() build: new: meson build system in contrib/meson, by Dima Krasner build: improved cmake script, by @Majlen +build: added -Wformat-security flag, as recommended by Padraig Brady doc : new : educational decoder, by Sean Purcell v1.1.3 diff --git a/lib/Makefile b/lib/Makefile index 05dd2bc9a..58f99baf5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -24,7 +24,7 @@ CPPFLAGS+= -I. -I./common -DXXH_NAMESPACE=ZSTD_ CFLAGS ?= -O3 DEBUGFLAGS = -g -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ - -Wstrict-prototypes -Wundef -Wpointer-arith + -Wstrict-prototypes -Wundef -Wpointer-arith -Wformat-security CFLAGS += $(DEBUGFLAGS) $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) diff --git a/programs/Makefile b/programs/Makefile index 407a4f374..5620a25a3 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -30,7 +30,7 @@ CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ CFLAGS ?= -O3 DEBUGFLAGS = -g -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ - -Wstrict-prototypes -Wundef -Wpointer-arith + -Wstrict-prototypes -Wundef -Wpointer-arith -Wformat-security CFLAGS += $(DEBUGFLAGS) $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) diff --git a/tests/Makefile b/tests/Makefile index 88c58fce4..8b19aa3d5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -28,8 +28,9 @@ TESTARTEFACT := versionsTest namespaceTest CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(PRGDIR) CFLAGS ?= -O3 -CFLAGS += -g -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 \ - -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef +CFLAGS += -g -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ + -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ + -Wstrict-prototypes -Wundef -Wformat-security CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) From 3437bf2febb7e11be3550d3658cce31c6e10039e Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Wed, 1 Mar 2017 16:10:26 -0800 Subject: [PATCH 29/60] Add build targets to the Makefile, and update CircleCI tests --- Makefile | 71 ++++++++++++++++++++++++++++++------ circle.yml | 43 +++++++++++----------- lib/compress/zstd_compress.c | 2 +- 3 files changed, 82 insertions(+), 34 deletions(-) diff --git a/Makefile b/Makefile index f0492d944..e10d29267 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,13 @@ allmost: $(MAKE) -C $(TESTDIR) all $(MAKE) -C $(ZWRAPDIR) all +#skip zwrapper, can't build that on alternate architectures without the proper zlib installed +.PHONY: allarch +allarch: + $(MAKE) -C $(ZSTDDIR) all + $(MAKE) -C $(PRGDIR) all + $(MAKE) -C $(TESTDIR) all + .PHONY: all32 all32: $(MAKE) -C $(PRGDIR) zstd32 @@ -94,7 +101,6 @@ clean: @$(RM) zstd$(EXT) zstdmt$(EXT) tmp* @echo Cleaning completed - #------------------------------------------------------------------------------ # make install is validated only for Linux, OSX, Hurd and some BSD targets #------------------------------------------------------------------------------ @@ -113,9 +119,41 @@ uninstall: travis-install: $(MAKE) install PREFIX=~/install_test_dir -gpptest: clean +gppbuild: clean + g++ -v CC=g++ $(MAKE) -C programs all CFLAGS="-O3 -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror" +gcc5build: clean + gcc-5 -v + CC=gcc-5 $(MAKE) all MOREFLAGS="-Werror" + +gcc6build: clean + gcc-6 -v + CC=gcc-6 $(MAKE) all MOREFLAGS="-Werror" + +clangbuild: clean + clang -v + CXX=clang++ CC=clang $(MAKE) all MOREFLAGS="-Werror -Wconversion -Wno-sign-conversion -Wdocumentation" + +m32build: clean + gcc -v + $(MAKE) all32 + +armbuild: clean + CC=arm-linux-gnueabi-gcc CFLAGS="-Werror" $(MAKE) allarch + +aarch64build: clean + CC=aarch64-linux-gnu-gcc CFLAGS="-Werror" $(MAKE) allarch + +ppcbuild: clean + CC=powerpc-linux-gnu-gcc CLAGS="-m32 -Wno-attributes -Werror" $(MAKE) allarch + +ppc64build: clean + CC=powerpc-linux-gnu-gcc CFLAGS="-m64 -Werror" $(MAKE) allarch + +gpptest: clean + CC=g++ $(MAKE) -C $(PRGDIR) all CFLAGS="-O3 -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror" + gcc5test: clean gcc-5 -v $(MAKE) all CC=gcc-5 MOREFLAGS="-Werror" @@ -126,7 +164,7 @@ gcc6test: clean clangtest: clean clang -v - $(MAKE) all CC=clang MOREFLAGS="-Werror -Wconversion -Wno-sign-conversion -Wdocumentation" + $(MAKE) all CXX=clang-++ CC=clang MOREFLAGS="-Werror -Wconversion -Wno-sign-conversion -Wdocumentation" armtest: clean $(MAKE) -C $(TESTDIR) datagen # use native, faster @@ -206,36 +244,45 @@ endif #make tests validated only for MSYS, Linux, OSX, kFreeBSD and Hurd targets #------------------------------------------------------------------------ ifneq (,$(filter $(HOST_OS),MSYS POSIX)) -cmaketest: +cmakebuild: cmake --version $(RM) -r $(BUILDIR)/cmake/build mkdir $(BUILDIR)/cmake/build cd $(BUILDIR)/cmake/build ; cmake -DPREFIX:STRING=~/install_test_dir $(CMAKE_PARAMS) .. ; $(MAKE) install ; $(MAKE) uninstall -c90test: clean +c90build: clean + gcc -v CFLAGS="-std=c90" $(MAKE) allmost # will fail, due to missing support for `long long` -gnu90test: clean +gnu90build: clean + gcc -v CFLAGS="-std=gnu90" $(MAKE) allmost -c99test: clean +c99build: clean + gcc -v CFLAGS="-std=c99" $(MAKE) allmost -gnu99test: clean +gnu99build: clean + gcc -v CFLAGS="-std=gnu99" $(MAKE) allmost -c11test: clean +c11build: clean + gcc -v CFLAGS="-std=c11" $(MAKE) allmost -bmix64test: clean +bmix64build: clean + gcc -v CFLAGS="-O3 -mbmi -Werror" $(MAKE) -C $(TESTDIR) test -bmix32test: clean +bmix32build: clean + gcc -v CFLAGS="-O3 -mbmi -mx32 -Werror" $(MAKE) -C $(TESTDIR) test -bmi32test: clean +bmi32build: clean + gcc -v CFLAGS="-O3 -mbmi -m32 -Werror" $(MAKE) -C $(TESTDIR) test staticAnalyze: clean + gcc -v CPPFLAGS=-g scan-build --status-bugs -v $(MAKE) all endif diff --git a/circle.yml b/circle.yml index 3102633e6..046a48443 100644 --- a/circle.yml +++ b/circle.yml @@ -1,39 +1,40 @@ dependencies: override: + - sudo dpkg --add-architecture i386 - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; sudo apt-get -y -qq update - #- sudo apt-get -y install qemu-system-ppc qemu-user-static gcc-powerpc-linux-gnu valgrind - #- sudo apt-get -y install qemu-system-arm gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross - - sudo apt-get -y install libc6-dev-i386 clang gcc-5 gcc-6 + - sudo apt-get -y install gcc-powerpc-linux-gnu gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross + - sudo apt-get -y install libstdc++-6-dev clang gcc g++ gcc-5 gcc-6 + - sudo apt-get -y install linux-libc-dev:i386 libc6-dev-i386 # use default "parallel: true" for commands in the machine, checkout, dependencies and database build phase post: - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]]; then make cmaketest && make clean; fi - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-invalidDictionaries && make clean; fi + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then cc -v; make all && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu90build && make clean; fi - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]]; then g++ -v; make gpptest && make clean; fi - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-legacy test-decodecorpus && make clean; fi + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c99build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu99build && make clean; fi - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]]; then gcc -v; make gnu90test && make clean; fi - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-symbols && make clean; fi + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c11build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make cmakebuild && make clean; fi - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]]; then gcc -v; make c99test && make clean; fi - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-longmatch && make clean; fi + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make gppbuild && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gcc5build && make clean; fi - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]]; then gcc -v; make gnu99test && make clean; fi - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests dll && make clean; fi + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make gcc6build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make clangbuild && make clean; fi - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]]; then clang -v; make clangtest && make clean; fi - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C programs zstd-small zstd-decompress zstd-compress zstd32 MOREFLAGS="-I/usr/include/x86_64-linux-gnu" && make clean lib && make clean; fi + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make m32build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make armbuild && make clean; fi - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]]; then make travis-install && make clean; fi - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-fullbench && make clean; fi + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make aarch64build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppcbuild && make clean; fi - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]]; then gcc-5 -v; make gcc5test && gcc-6 -v && make gcc6test && make clean; fi - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-zstream && make clean; fi + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make ppc64build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then true && make clean; fi #could add another test here - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]]; then make -C tests test-zstd && make clean; fi - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-fuzzer FUZZERTEST=-T4mn && make clean; fi + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make shortest && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-legacy test-longmatch test-symbols && make clean; fi test: override: diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 15f063e18..308659467 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -247,7 +247,7 @@ static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_parameters params, U64 fra typedef enum { ZSTDcrp_continue, ZSTDcrp_noMemset, ZSTDcrp_fullReset } ZSTD_compResetPolicy_e; /*! ZSTD_resetCCtx_advanced() : - note : @params must be validated */ + note : `params` must be validated */ static size_t ZSTD_resetCCtx_advanced (ZSTD_CCtx* zc, ZSTD_parameters params, U64 frameContentSize, ZSTD_compResetPolicy_e const crp) From 764c2fdfed11ef8c7bbaf6dab7d7be7d8f1d3be1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 6 Mar 2017 17:20:44 -0800 Subject: [PATCH 30/60] updated benchmark table zstd v1.1.3, new station i7-6700K --- README.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index ce27056bc..9ead400f7 100644 --- a/README.md +++ b/README.md @@ -11,23 +11,22 @@ you can consult a list of known ports on [Zstandard homepage](http://www.zstd.ne |master | [![Build Status](https://travis-ci.org/facebook/zstd.svg?branch=master)](https://travis-ci.org/facebook/zstd) | |dev | [![Build Status](https://travis-ci.org/facebook/zstd.svg?branch=dev)](https://travis-ci.org/facebook/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.4.0, with the [Silesia compression corpus]. +As a reference, several fast compression algorithms were tested and compared on a server running Linux Mint Debian Edition (`Linux version 4.8.0-1-amd64`), with a Core i7-6700K CPU @ 4.0GHz, using [lzbench v1.6], an open-source in-memory benchmark by @inikep compiled with GCC 6.3.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 - -| Name | Ratio | C.speed | D.speed | -|-------------------------|-------|--------:|--------:| -| | | MB/s | MB/s | -| **zstd 0.8.2 -1** |**2.877**| **330** | **940** | -| [zlib] 1.2.8 deflate -1 | 2.730 | 95 | 360 | -| 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 | -| Snappy 1.1.3 | 2.091 | 480 | 1600 | -| LZF 3.6 | 2.077 | 375 | 790 | +| Compressor name | Ratio | Compression| Decompress.| +| --------------- | ------| -----------| ---------- | +| memcpy | 1.000 | 13958 MB/s | 13932 MB/s | +| **zstd 1.1.3 -1** | 2.877 | 430 MB/s | 1110 MB/s | +| zlib 1.2.8 -1 | 2.743 | 110 MB/s | 400 MB/s | +| brotli 0.5.2 -0 | 2.708 | 400 MB/s | 430 MB/s | +| quicklz 1.5.0 -1 | 2.238 | 550 MB/s | 710 MB/s | +| lzo1x 2.09 -1 | 2.108 | 650 MB/s | 830 MB/s | +| lz4 1.7.5 | 2.101 | 720 MB/s | 3600 MB/s | +| snappy 1.1.3 | 2.091 | 500 MB/s | 1650 MB/s | +| lzf 3.6 -1 | 2.077 | 400 MB/s | 860 MB/s | [zlib]:http://www.zlib.net/ [LZ4]: http://www.lz4.org/ From eeb9758c39e3df79b9ca5868eb2f7ce096fd22c1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 6 Mar 2017 17:22:47 -0800 Subject: [PATCH 31/60] fix : remove mempcpy line in bench --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9ead400f7..d3c677b81 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ As a reference, several fast compression algorithms were tested and compared on | Compressor name | Ratio | Compression| Decompress.| | --------------- | ------| -----------| ---------- | -| memcpy | 1.000 | 13958 MB/s | 13932 MB/s | | **zstd 1.1.3 -1** | 2.877 | 430 MB/s | 1110 MB/s | | zlib 1.2.8 -1 | 2.743 | 110 MB/s | 400 MB/s | | brotli 0.5.2 -0 | 2.708 | 400 MB/s | 430 MB/s | From 38ab1db3cd7b8b3b7cd8a26dc70e1d6a41a55863 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 6 Mar 2017 17:24:34 -0800 Subject: [PATCH 32/60] fixed lzbench link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d3c677b81..b2aa3d2d5 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ you can consult a list of known ports on [Zstandard homepage](http://www.zstd.ne As a reference, several fast compression algorithms were tested and compared on a server running Linux Mint Debian Edition (`Linux version 4.8.0-1-amd64`), with a Core i7-6700K CPU @ 4.0GHz, using [lzbench v1.6], an open-source in-memory benchmark by @inikep compiled with GCC 6.3.0, with the [Silesia compression corpus]. -[lzbench]: https://github.com/inikep/lzbench +[lzbench v1.6]: https://github.com/inikep/lzbench [Silesia compression corpus]: http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia | Compressor name | Ratio | Compression| Decompress.| From a1a195044fec16f32c7cfc50f8b0f196f1e56644 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Mon, 6 Mar 2017 16:57:04 -0800 Subject: [PATCH 33/60] Use test section --- circle.yml | 77 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/circle.yml b/circle.yml index 046a48443..298569d14 100644 --- a/circle.yml +++ b/circle.yml @@ -6,38 +6,55 @@ dependencies: - sudo apt-get -y install libstdc++-6-dev clang gcc g++ gcc-5 gcc-6 - sudo apt-get -y install linux-libc-dev:i386 libc6-dev-i386 - # use default "parallel: true" for commands in the machine, checkout, dependencies and database build phase - post: - - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then cc -v; make all && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu90build && make clean; fi - - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c99build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu99build && make clean; fi - - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c11build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make cmakebuild && make clean; fi - - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make gppbuild && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gcc5build && make clean; fi - - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make gcc6build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make clangbuild && make clean; fi - - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make m32build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make armbuild && make clean; fi - - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make aarch64build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppcbuild && make clean; fi - - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make ppc64build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then true && make clean; fi #could add another test here - - | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make shortest && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-legacy test-longmatch test-symbols && make clean; fi - test: override: + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then cc -v; make all && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu90build && make clean; fi + : + parallel: true + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c99build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu99build && make clean; fi + : + parallel: true + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c11build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make cmakebuild && make clean; fi + : + parallel: true + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make gppbuild && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gcc5build && make clean; fi + : + parallel: true + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make gcc6build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make clangbuild && make clean; fi + : + parallel: true + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make m32build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make armbuild && make clean; fi + : + parallel: true + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make aarch64build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppcbuild && make clean; fi + : + parallel: true + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make ppc64build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then true && make clean; fi #could add another test here + : + parallel: true + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make shortest && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-legacy test-longmatch test-symbols && make clean; fi + : + parallel: true + + post: - echo Circle CI tests finished # Longer tests From d66450fd7dbefc49928ed1445eda73e5d0ee17b5 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Tue, 7 Mar 2017 11:36:19 -0800 Subject: [PATCH 34/60] Fix travis test broken by Makefile change --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 82b92c2a2..9c1e10e15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ matrix: - export CXX="g++-6" CC="gcc-6" # OS X Mavericks - - env: Cmd="make gnu90test && make clean && make test && make clean && make travis-install" + - env: Cmd="make gnu90build && make clean && make test && make clean && make travis-install" os: osx script: From baa9b114f8fc3bf68ec7c181aef5b668aae6bdb7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 7 Mar 2017 16:24:54 -0800 Subject: [PATCH 35/60] minor text refactor in readme --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b2aa3d2d5..6de5a1079 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,12 @@ you can consult a list of known ports on [Zstandard homepage](http://www.zstd.ne |master | [![Build Status](https://travis-ci.org/facebook/zstd.svg?branch=master)](https://travis-ci.org/facebook/zstd) | |dev | [![Build Status](https://travis-ci.org/facebook/zstd.svg?branch=dev)](https://travis-ci.org/facebook/zstd) | -As a reference, several fast compression algorithms were tested and compared on a server running Linux Mint Debian Edition (`Linux version 4.8.0-1-amd64`), with a Core i7-6700K CPU @ 4.0GHz, using [lzbench v1.6], an open-source in-memory benchmark by @inikep compiled with GCC 6.3.0, with the [Silesia compression corpus]. +As a reference, several fast compression algorithms were tested and compared +on a server running Linux Mint Debian Edition (`Linux version 4.8.0-1-amd64`), +with a Core i7-6700K CPU @ 4.0GHz, +using [lzbench v1.6], an open-source in-memory benchmark by @inikep +compiled with GCC 6.3.0, +on the [Silesia compression corpus]. [lzbench v1.6]: https://github.com/inikep/lzbench [Silesia compression corpus]: http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia From 881abe44f17d6fc8ef8805e221e4ae48f4a7d5f0 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Tue, 7 Mar 2017 16:52:23 -0800 Subject: [PATCH 36/60] Reduce point at which we reduce offsets to protect against UB --- lib/compress/zstd_compress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 308659467..15a9245e4 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2347,7 +2347,7 @@ static size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, if (remaining < blockSize) blockSize = remaining; /* preemptive overflow correction */ - if (cctx->lowLimit > (2U<<30)) { + if (cctx->lowLimit > (3U<<29)) { U32 const cycleMask = (1 << ZSTD_cycleLog(cctx->params.cParams.hashLog, cctx->params.cParams.strategy)) - 1; U32 const current = (U32)(ip - cctx->base); U32 const newCurrent = (current & cycleMask) + (1 << cctx->params.cParams.windowLog); From e06c303475020e7df6400d2be2293533d0415d56 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 8 Mar 2017 13:45:10 -0800 Subject: [PATCH 37/60] Fix ZSTD_sizeof_CStream() --- lib/compress/zstd_compress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 432323e21..85744b753 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3074,7 +3074,7 @@ size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel) size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs) { if (zcs==NULL) return 0; /* support sizeof on NULL */ - return sizeof(zcs) + ZSTD_sizeof_CCtx(zcs->cctx) + ZSTD_sizeof_CDict(zcs->cdictLocal) + zcs->outBuffSize + zcs->inBuffSize; + return sizeof(*zcs) + ZSTD_sizeof_CCtx(zcs->cctx) + ZSTD_sizeof_CDict(zcs->cdictLocal) + zcs->outBuffSize + zcs->inBuffSize; } /*====== Compression ======*/ From 81512e9ebee5db31ac62eba4e6bc04ea34517729 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 8 Mar 2017 13:45:58 -0800 Subject: [PATCH 38/60] Avoid '#define inline /* ... */' Take definition of `FORCE_INLINE` from `zstd_internal.h`. --- lib/decompress/huf_decompress.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index 0f11f5c1b..1d13150c5 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -35,16 +35,19 @@ /* ************************************************************** * Compiler specifics ****************************************************************/ -#if defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -/* inline is defined */ -#elif defined(_MSC_VER) || defined(__GNUC__) -# define inline __inline -#else -# define inline /* disable inline */ -#endif - #ifdef _MSC_VER /* Visual Studio */ +# define FORCE_INLINE static __forceinline # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +#else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif +# else +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif @@ -152,7 +155,7 @@ static BYTE HUF_decodeSymbolX2(BIT_DStream_t* Dstream, const HUF_DEltX2* dt, con if (MEM_64bits()) \ HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) -static inline size_t HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog) +FORCE_INLINE size_t HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX2* const dt, const U32 dtLog) { BYTE* const pStart = p; @@ -559,7 +562,7 @@ static U32 HUF_decodeLastSymbolX4(void* op, BIT_DStream_t* DStream, const HUF_DE if (MEM_64bits()) \ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) -static inline size_t HUF_decodeStreamX4(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, const HUF_DEltX4* const dt, const U32 dtLog) +FORCE_INLINE size_t HUF_decodeStreamX4(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd, const HUF_DEltX4* const dt, const U32 dtLog) { BYTE* const pStart = p; From e65aab8e0fa823d91a76490924d1d7a67b485aa2 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 8 Mar 2017 15:40:13 -0800 Subject: [PATCH 39/60] Remove 'mem.h' dependency from ZSTD_WINDOWLOG_MAX --- lib/zstd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zstd.h b/lib/zstd.h index f462d0997..0b48b7ec6 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -345,7 +345,7 @@ ZSTDLIB_API size_t ZSTD_DStreamOutSize(void); /*!< recommended size for output #define ZSTD_WINDOWLOG_MAX_32 27 #define ZSTD_WINDOWLOG_MAX_64 27 -#define ZSTD_WINDOWLOG_MAX ((U32)(MEM_32bits() ? ZSTD_WINDOWLOG_MAX_32 : ZSTD_WINDOWLOG_MAX_64)) +#define ZSTD_WINDOWLOG_MAX ((unsigned)(sizeof(size_t) == 4 ? ZSTD_WINDOWLOG_MAX_32 : ZSTD_WINDOWLOG_MAX_64)) #define ZSTD_WINDOWLOG_MIN 10 #define ZSTD_HASHLOG_MAX ZSTD_WINDOWLOG_MAX #define ZSTD_HASHLOG_MIN 6 From 201e8c815709f306db8227c1054e99888d135c6f Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Thu, 9 Mar 2017 02:00:35 +0000 Subject: [PATCH 40/60] programs/Makefile: remove zstd-internal target zstd-internal was intended to be a helper target, but it doesn't help at all, what it does in practice is a useless rebuild of zstd every time "make zstd" is invoked. Fixes: 030ac243a0f3 ("Changed Makefile to generate zstd with .gz support by default") --- programs/Makefile | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/programs/Makefile b/programs/Makefile index 5620a25a3..a935c744a 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -83,8 +83,11 @@ all: zstd $(ZSTDDECOMP_O): CFLAGS += $(ALIGN_LOOP) -zstd-internal : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) -zstd-internal : $(ZSTDLIB_OBJ) zstdcli.o fileio.o bench.o datagen.o dibio.o +zstd : CPPFLAGS += $(ZLIBCPP) +zstd : LDFLAGS += $(ZLIBLD) +zstd-nogz : HAVE_ZLIB=0 +zstd zstd-nogz : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) +zstd zstd-nogz : $(ZSTDLIB_OBJ) zstdcli.o fileio.o bench.o datagen.o dibio.o ifeq ($(HAVE_ZLIB), 1) @echo "==> building zstd with .gz decompression support " else @@ -95,13 +98,6 @@ ifneq (,$(filter Windows%,$(OS))) endif $(CC) $(FLAGS) $^ $(RES_FILE) -o zstd$(EXT) $(LDFLAGS) -zstd-nogz : HAVE_ZLIB=0 -zstd-nogz : zstd-internal - -zstd : CPPFLAGS += $(ZLIBCPP) -zstd : LDFLAGS += $(ZLIBLD) -zstd : zstd-internal - zstd-release: DEBUGFLAGS := zstd-release: zstd From daec40db242db71946dbb250b05a88beadb37008 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Tue, 7 Mar 2017 12:08:15 -0800 Subject: [PATCH 41/60] Update .travis.yml and Makefile for medium tests --- .travis.yml | 54 +++++++++++++++++++++++++------------------------- Makefile | 28 ++++++++++++++++++++++---- appveyor.yml | 5 +++++ tests/Makefile | 4 ++-- 4 files changed, 58 insertions(+), 33 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9c1e10e15..a52d57af3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,39 +1,39 @@ +# Medium Tests: Run on all commits/PRs to dev branch + language: c sudo: required dist: trusty matrix: - fast_finish: true include: # Ubuntu 14.04 - - env: Cmd="make libc6install && make -C tests test32" - - env: Cmd='make valgrindinstall arminstall ppcinstall arm-ppc-compilation && make clean lib && CFLAGS="-O1 -g" make -C zlibWrapper valgrindTest && make -C tests valgrindTest' + - env: Cmd='make gcc6install && CC=gcc-6 make clean uasan-test-zstd' + - env: Cmd='make gcc6install libc6install && CC=gcc-6 make clean uasan-test-zstd32' + - env: Cmd='make clang38install && CC=clang-3.8 make clean msan-test-zstd' - - env: Cmd='CC=gcc-6 make gcc6install uasan-test' - - env: Cmd='CC=gcc-6 make gcc6install uasan-test32' - - env: Cmd="make arminstall armtest && make clean && make aarch64test" - - env: Cmd='make ppcinstall ppctest && make clean && make ppc64test' - - env: Cmd='make gpp6install zlibwrapper && make -C tests clean test-zstd-nolegacy && make -C tests versionsTest && make clean && cd contrib/pzstd && make test-pzstd && make test-pzstd32 && make test-pzstd-tsan && make test-pzstd-asan' - install: - - export CXX="g++-6" CC="gcc-6" + - env: Cmd='make gcc6install && CC=gcc-6 make clean uasan-fuzztest' + - env: Cmd='make gcc6install libc6install && CC=gcc-6 CFLAGS=-m32 make clean uasan-fuzztest' + - env: Cmd='make clang38install && CC=clang-3.8 make clean msan-fuzztest' + - env: Cmd='make clang38install && CC=clang-3.8 make clean tsan-test-zstream' - # OS X Mavericks - - env: Cmd="make gnu90build && make clean && make test && make clean && make travis-install" - os: osx + - env: Cmd='make valgrindinstall && make -C tests clean valgrindTest' + + - env: Cmd='make arminstall && make armfuzz' + - env: Cmd='make arminstall && make aarch64fuzz' + - env: Cmd='make ppcinstall && make ppcfuzz' + - env: Cmd='make ppcinstall && make ppc64fuzz' + +git: + depth: 1 + +branches: + only: + - dev + - master script: - JOB_NUMBER=$(echo $TRAVIS_JOB_NUMBER | sed -e 's:[0-9][0-9]*\.\(.*\):\1:') - # cron & master => full tests, as this is the final step towards a Release - # pull requests => normal tests (job numbers 1-3) - # other feature branches => short tests (job numbers 1-2) - echo JOB_NUMBER=$JOB_NUMBER TRAVIS_BRANCH=$TRAVIS_BRANCH TRAVIS_EVENT_TYPE=$TRAVIS_EVENT_TYPE TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST - - if [ "$TRAVIS_EVENT_TYPE" = "cron" ] || [ "$TRAVIS_BRANCH" = "master" ]; then - FUZZERTEST=-T7mn sh -c "$Cmd" || travis_terminate 1; - else - if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ] && [ $JOB_NUMBER -lt 4 ]; then - sh -c "$Cmd" || travis_terminate 1; - else - if [ $JOB_NUMBER -lt 3 ]; then - sh -c "$Cmd" || travis_terminate 1; - fi - fi - fi + - export FUZZERTEST=-T2mn; + export ZSTREAM_TESTTIME=-T2mn; + export DECODECORPUS_TESTTIME=-T1mn; + sh -c "$Cmd" || travis_terminate 1; diff --git a/Makefile b/Makefile index e10d29267..0187348ee 100644 --- a/Makefile +++ b/Makefile @@ -151,6 +151,18 @@ ppcbuild: clean ppc64build: clean CC=powerpc-linux-gnu-gcc CFLAGS="-m64 -Werror" $(MAKE) allarch +armfuzz: clean + CC=arm-linux-gnueabi-gcc QEMU_SYS=qemu-arm-static MOREFLAGS="-static" $(MAKE) -C $(TESTDIR) fuzztest + +aarch64fuzz: clean + CC=aarch64-linux-gnu-gcc QEMU_SYS=qemu-aarch64-static MOREFLAGS="-static" $(MAKE) -C $(TESTDIR) fuzztest + +ppcfuzz: clean + CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc-static MOREFLAGS="-static" $(MAKE) -C $(TESTDIR) fuzztest + +ppc64fuzz: clean + CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc64-static MOREFLAGS="-m64 -static" $(MAKE) -C $(TESTDIR) fuzztest + gpptest: clean CC=g++ $(MAKE) -C $(PRGDIR) all CFLAGS="-O3 -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror" @@ -189,7 +201,7 @@ arm-ppc-compilation: $(MAKE) -C $(PRGDIR) clean zstd CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc64-static ZSTDRTTEST= MOREFLAGS="-m64 -static" usan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=undefined" + $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=undefined" asan: clean $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=address" @@ -197,15 +209,20 @@ asan: clean msan: clean $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=memory -fno-omit-frame-pointer" # datagen.c fails this test for no obvious reason +msan-%: clean + LDFLAGS=-fuse-ld=gold MOREFLAGS="-fno-sanitize-recover=all -fsanitize=memory -fno-omit-frame-pointer" $(MAKE) -C $(TESTDIR) $* + asan32: clean $(MAKE) -C $(TESTDIR) test32 CC=clang MOREFLAGS="-g -fsanitize=address" uasan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=address -fsanitize=undefined" + $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=address,undefined" uasan-%: clean - LDFLAGS=-fuse-ld=gold CFLAGS="-Og -fsanitize=address -fsanitize=undefined" $(MAKE) -C $(TESTDIR) $* + LDFLAGS=-fuse-ld=gold MOREFLAGS="-Og -fno-sanitize-recover=all -fsanitize=address,undefined" $(MAKE) -C $(TESTDIR) $* +tsan-%: clean + LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=thread" $(MAKE) -C $(TESTDIR) $* apt-install: sudo apt-get -yq --no-install-suggests --no-install-recommends --force-yes install $(APT_PACKAGES) @@ -217,7 +234,7 @@ ppcinstall: APT_PACKAGES="qemu-system-ppc qemu-user-static gcc-powerpc-linux-gnu" $(MAKE) apt-install arminstall: - APT_PACKAGES="qemu-system-arm qemu-user-static gcc-powerpc-linux-gnu gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross" $(MAKE) apt-install + APT_PACKAGES="qemu-system-arm qemu-user-static gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross" $(MAKE) apt-install valgrindinstall: APT_PACKAGES="valgrind" $(MAKE) apt-install @@ -231,6 +248,9 @@ gcc6install: apt-add-repo gpp6install: apt-add-repo APT_PACKAGES="libc6-dev-i386 g++-multilib gcc-6 g++-6 g++-6-multilib" $(MAKE) apt-install +clang38install: + APT_PACKAGES="clang-3.8" $(MAKE) apt-install + endif diff --git a/appveyor.yml b/appveyor.yml index 51ff488a4..9507fec6e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -146,6 +146,11 @@ test_script: fuzzer_VS2015_%PLATFORM%_Release.exe %FUZZERTEST% ) +branches: + only: + - dev + - master + artifacts: - path: bin\zstd.exe - path: bin\zstd32.exe diff --git a/tests/Makefile b/tests/Makefile index 8b19aa3d5..39e4d1015 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -75,8 +75,6 @@ all32: fullbench32 fuzzer32 zstreamtest32 zbufftest32 dll: fuzzer-dll zstreamtest-dll zbufftest-dll - - zstd: $(MAKE) -C $(PRGDIR) $@ @@ -257,6 +255,8 @@ zstd-playTests: datagen shortest: ZSTDRTTEST= shortest: test-zstd +fuzztest: test-fuzzer test-zstream test-decodecorpus + test: test-zstd test-fullbench test-fuzzer test-zstream test-invalidDictionaries test-legacy test-decodecorpus ifeq ($(QEMU_SYS),) test: test-pool From 7c8f5d5bc7ee4c16c5de73cb2e1c0e13f2ffca79 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 9 Mar 2017 16:05:10 -0800 Subject: [PATCH 42/60] Make test times overwritable --- tests/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index 39e4d1015..59256f841 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -60,10 +60,10 @@ endif MULTITHREAD = $(MULTITHREAD_CPP) $(MULTITHREAD_LD) VOID = /dev/null -ZSTREAM_TESTTIME = -T2mn +ZSTREAM_TESTTIME ?= -T2mn FUZZERTEST ?= -T5mn ZSTDRTTEST = --test-large-data -DECODECORPUS_TESTTIME = -T30 +DECODECORPUS_TESTTIME ?= -T30 .PHONY: default all all32 dll clean test test32 test-all namespaceTest versionsTest From 2500dcfa5f6f99dc015231d5561c35d9a4016965 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 9 Mar 2017 13:59:26 -0800 Subject: [PATCH 43/60] Add testing description --- TESTING.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 TESTING.md diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 000000000..1fa5fe8c2 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,44 @@ +Testing +======= + +Zstandard CI testing is split up into three sections: +short, medium, and long tests. + +Short Tests +----------- +Short tests run on CircleCI for new commits on every branch and pull request. +They consist of the following tests: +- Compilation on all supported targets (x86, x86_64, ARM, AArch64, PowerPC, and PowerPC64) +- Compilation on various versions of gcc, clang, and g++ +- `tests/playTests.sh` on x86_64, without the tests on long data (CLI tests) +- Small tests (`tests/legacy.c`, `tests/longmatch.c`, `tests/symbols.c`) on x64_64 + +Medium Tests +------------ +Medium tests run on every commit and pull request to `dev` branch, on TravisCI. +They consist of the following tests: +- The following tests run with UBsan and Asan on x86_64 and x86, as well as with + Msan on x86_64 + - `tests/playTests.sh --test-long-data` + - Fuzzer tests: `tests/fuzzer.c`, `tests/zstreamtest.c`, and `tests/decodecorpus.c` +- `tests/zstreamtest.c` under Tsan (streaming mode, including multithreaded mode) +- Valgrind Test (`make -C tests valgrindTest`) (testing CLI and fuzzer under valgrind) +- Fuzzer tests (see above) on ARM, AArch64, PowerPC, and PowerPC64 + +Long Tests +---------- +Long tests run on all commits to `master` branch, +and once a day on the current version of `dev` branch, +on TravisCI. +They consist of the following tests: +- Entire test suite (including fuzzers and some other specialized tests) on: + - x86_64 and x86 with UBsan and Asan + - x86_64 with Msan + - ARM, AArch64, PowerPC, and PowerPC64 +- Streaming mode fuzzer with Tsan (for the `zstdmt` testing) +- ZlibWrapper tests, including under valgrind +- Versions test (ensuring `zstd` can decode files from all previous versions) +- `pzstd` with asan and tsan, as well as in 32-bits mode +- Testing `zstd` with legacy mode off +- Testing `zbuff` (old streaming API) +- Entire test suite and make install on OS X From caf0ee8d20663b9dce7d0ac4cd048ebc542eb473 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 9 Mar 2017 17:28:08 -0800 Subject: [PATCH 44/60] Make signed integer overflow recoverable in UBsan --- Makefile | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 0187348ee..49f29d782 100644 --- a/Makefile +++ b/Makefile @@ -200,12 +200,19 @@ arm-ppc-compilation: $(MAKE) -C $(PRGDIR) clean zstd CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc-static ZSTDRTTEST= MOREFLAGS="-Werror -Wno-attributes -static" $(MAKE) -C $(PRGDIR) clean zstd CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc64-static ZSTDRTTEST= MOREFLAGS="-m64 -static" +# run UBsan with -fsanitize-recover=signed-integer-overflow +# due to a bug in UBsan when doing pointer subtraction +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63303 + usan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=undefined" + $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=signed-integer-overflow -fsanitize=undefined" asan: clean $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=address" +asan-%: clean + LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=address" $(MAKE) -C $(TESTDIR) $* + msan: clean $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=memory -fno-omit-frame-pointer" # datagen.c fails this test for no obvious reason @@ -216,10 +223,10 @@ asan32: clean $(MAKE) -C $(TESTDIR) test32 CC=clang MOREFLAGS="-g -fsanitize=address" uasan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=address,undefined" + $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=signed-integer-overflow -fsanitize=address,undefined" uasan-%: clean - LDFLAGS=-fuse-ld=gold MOREFLAGS="-Og -fno-sanitize-recover=all -fsanitize=address,undefined" $(MAKE) -C $(TESTDIR) $* + LDFLAGS=-fuse-ld=gold MOREFLAGS="-Og -fno-sanitize-recover=all -fsanitize-recover=signed-integer-overflow -fsanitize=address,undefined" $(MAKE) -C $(TESTDIR) $* tsan-%: clean LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=thread" $(MAKE) -C $(TESTDIR) $* From 8fe5c6862c56f937ad5c3c25d387d4a331b42775 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 9 Mar 2017 11:54:34 -0800 Subject: [PATCH 45/60] Fix undefined behaviour in decompressor --- lib/common/mem.h | 18 ++++++++++-------- lib/decompress/zstd_decompress.c | 6 +++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/common/mem.h b/lib/common/mem.h index 7a3f72141..3cacd216a 100644 --- a/lib/common/mem.h +++ b/lib/common/mem.h @@ -48,14 +48,15 @@ MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (size *****************************************************************/ #if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include - typedef uint8_t BYTE; - typedef uint16_t U16; - typedef int16_t S16; - typedef uint32_t U32; - typedef int32_t S32; - typedef uint64_t U64; - typedef int64_t S64; - typedef intptr_t iPtrDiff; + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef int16_t S16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; + typedef int64_t S64; + typedef intptr_t iPtrDiff; + typedef uintptr_t uPtrDiff; #else typedef unsigned char BYTE; typedef unsigned short U16; @@ -65,6 +66,7 @@ MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (size typedef unsigned long long U64; typedef signed long long S64; typedef ptrdiff_t iPtrDiff; + typedef size_t uPtrDiff; #endif diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 482c334ff..516edfcc6 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -1033,7 +1033,7 @@ size_t ZSTD_execSequence(BYTE* op, if (sequence.offset > (size_t)(oLitEnd - base)) { /* offset beyond prefix */ if (sequence.offset > (size_t)(oLitEnd - vBase)) return ERROR(corruption_detected); - match += (dictEnd-base); + match = dictEnd + (match - base); if (match + sequence.matchLength <= dictEnd) { memmove(oLitEnd, match, sequence.matchLength); return sequenceLength; @@ -1216,7 +1216,7 @@ FORCE_INLINE seq_t ZSTD_decodeSequenceLong_generic(seqState_t* seqState, int con { size_t const pos = seqState->pos + seq.litLength; seq.match = seqState->base + pos - seq.offset; /* single memory segment */ - if (seq.offset > pos) seq.match += seqState->gotoDict; /* separate memory segment */ + if (seq.offset > pos) seq.match += (uPtrDiff)seqState->gotoDict; /* separate memory segment */ seqState->pos = pos + seq.matchLength; } @@ -1356,7 +1356,7 @@ static size_t ZSTD_decompressSequencesLong( { U32 i; for (i=0; ientropy.rep[i]; } seqState.base = base; seqState.pos = (size_t)(op-base); - seqState.gotoDict = (iPtrDiff)(dictEnd - base); + seqState.gotoDict = (iPtrDiff)((uPtrDiff)dictEnd - (uPtrDiff)base); /* cast to avoid undefined behaviour */ CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); From 784082f49cfd48c9f77db23ae7df35703b8b68b2 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Fri, 10 Mar 2017 10:34:45 -0800 Subject: [PATCH 46/60] Change gotoDict type to uPtrDiff --- lib/decompress/zstd_decompress.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 516edfcc6..943bdf94e 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -876,7 +876,7 @@ typedef struct { size_t prevOffset[ZSTD_REP_NUM]; const BYTE* base; size_t pos; - iPtrDiff gotoDict; + uPtrDiff gotoDict; } seqState_t; @@ -1216,7 +1216,7 @@ FORCE_INLINE seq_t ZSTD_decodeSequenceLong_generic(seqState_t* seqState, int con { size_t const pos = seqState->pos + seq.litLength; seq.match = seqState->base + pos - seq.offset; /* single memory segment */ - if (seq.offset > pos) seq.match += (uPtrDiff)seqState->gotoDict; /* separate memory segment */ + if (seq.offset > pos) seq.match += seqState->gotoDict; /* separate memory segment */ seqState->pos = pos + seq.matchLength; } @@ -1356,7 +1356,7 @@ static size_t ZSTD_decompressSequencesLong( { U32 i; for (i=0; ientropy.rep[i]; } seqState.base = base; seqState.pos = (size_t)(op-base); - seqState.gotoDict = (iPtrDiff)((uPtrDiff)dictEnd - (uPtrDiff)base); /* cast to avoid undefined behaviour */ + seqState.gotoDict = (uPtrDiff)dictEnd - (uPtrDiff)base; /* cast to avoid undefined behaviour */ CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected); FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); From 334cb34edba6da2232701b298b61a62fc26259a4 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Mon, 13 Mar 2017 14:32:30 -0700 Subject: [PATCH 47/60] ZSTD_LEGACY_SUPPORT defines lowest supported version --- lib/Makefile | 13 +++-- lib/decompress/zstd_decompress.c | 4 +- lib/legacy/zstd_legacy.h | 97 ++++++++++++++++++++++++++++++++ programs/Makefile | 10 ++-- 4 files changed, 113 insertions(+), 11 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 58f99baf5..18b08a11d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -31,12 +31,15 @@ FLAGS = $(CPPFLAGS) $(CFLAGS) ZSTD_FILES := $(wildcard common/*.c compress/*.c decompress/*.c dictBuilder/*.c deprecated/*.c) -ifeq ($(ZSTD_LEGACY_SUPPORT), 0) -CPPFLAGS += -DZSTD_LEGACY_SUPPORT=0 -else -CPPFLAGS += -I./legacy -DZSTD_LEGACY_SUPPORT=1 -ZSTD_FILES+= $(wildcard legacy/*.c) +ZSTD_LEGACY_SUPPORT ?= 1 + +ifneq ($(ZSTD_LEGACY_SUPPORT), 0) +ifeq ($(shell test $(ZSTD_LEGACY_SUPPORT) -lt 8; echo $$?), 0) + ZSTD_FILES += $(shell ls legacy/*.c | grep 'v0[$(ZSTD_LEGACY_SUPPORT)-7]') endif + CPPFLAGS += -I./legacy +endif +CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) ZSTD_OBJ := $(patsubst %.c,%.o,$(ZSTD_FILES)) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 943bdf94e..2aaa4a3df 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -320,7 +320,7 @@ size_t ZSTD_getFrameParams(ZSTD_frameParams* fparamsPtr, const void* src, size_t * - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) */ unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize) { -#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT==1) +#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) if (ZSTD_isLegacy(src, srcSize)) { unsigned long long const ret = ZSTD_getDecompressedSize_legacy(src, srcSize); return ret == 0 ? ZSTD_CONTENTSIZE_UNKNOWN : ret; @@ -1472,7 +1472,7 @@ size_t ZSTD_generateNxBytes(void* dst, size_t dstCapacity, BYTE byte, size_t len * @return : the compressed size of the frame starting at `src` */ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) { -#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT==1) +#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1) if (ZSTD_isLegacy(src, srcSize)) return ZSTD_findFrameCompressedSizeLegacy(src, srcSize); #endif if (srcSize >= ZSTD_skippableHeaderSize && diff --git a/lib/legacy/zstd_legacy.h b/lib/legacy/zstd_legacy.h index 707e76f0a..18e22e651 100644 --- a/lib/legacy/zstd_legacy.h +++ b/lib/legacy/zstd_legacy.h @@ -28,6 +28,31 @@ extern "C" { #include "zstd_v06.h" #include "zstd_v07.h" +#ifndef ZSTD_LEGACY_SUPPORT +# define ZSTD_LEGACY_SUPPORT 8 +#endif + +#if (ZSTD_LEGACY_SUPPORT <= 1) +# include "zstd_v01.h" +#endif +#if (ZSTD_LEGACY_SUPPORT <= 2) +# include "zstd_v02.h" +#endif +#if (ZSTD_LEGACY_SUPPORT <= 3) +# include "zstd_v03.h" +#endif +#if (ZSTD_LEGACY_SUPPORT <= 4) +# include "zstd_v04.h" +#endif +#if (ZSTD_LEGACY_SUPPORT <= 5) +# include "zstd_v05.h" +#endif +#if (ZSTD_LEGACY_SUPPORT <= 6) +# include "zstd_v06.h" +#endif +#if (ZSTD_LEGACY_SUPPORT <= 7) +# include "zstd_v07.h" +#endif /** ZSTD_isLegacy() : @return : > 0 if supported by legacy decoder. 0 otherwise. @@ -40,13 +65,27 @@ MEM_STATIC unsigned ZSTD_isLegacy(const void* src, size_t srcSize) magicNumberLE = MEM_readLE32(src); switch(magicNumberLE) { +#if (ZSTD_LEGACY_SUPPORT <= 1) case ZSTDv01_magicNumberLE:return 1; +#endif +#if (ZSTD_LEGACY_SUPPORT <= 2) case ZSTDv02_magicNumber : return 2; +#endif +#if (ZSTD_LEGACY_SUPPORT <= 3) case ZSTDv03_magicNumber : return 3; +#endif +#if (ZSTD_LEGACY_SUPPORT <= 4) case ZSTDv04_magicNumber : return 4; +#endif +#if (ZSTD_LEGACY_SUPPORT <= 5) case ZSTDv05_MAGICNUMBER : return 5; +#endif +#if (ZSTD_LEGACY_SUPPORT <= 6) case ZSTDv06_MAGICNUMBER : return 6; +#endif +#if (ZSTD_LEGACY_SUPPORT <= 7) case ZSTDv07_MAGICNUMBER : return 7; +#endif default : return 0; } } @@ -56,24 +95,30 @@ MEM_STATIC unsigned long long ZSTD_getDecompressedSize_legacy(const void* src, s { U32 const version = ZSTD_isLegacy(src, srcSize); if (version < 5) return 0; /* no decompressed size in frame header, or not a legacy format */ +#if (ZSTD_LEGACY_SUPPORT <= 5) if (version==5) { ZSTDv05_parameters fParams; size_t const frResult = ZSTDv05_getFrameParams(&fParams, src, srcSize); if (frResult != 0) return 0; return fParams.srcSize; } +#endif +#if (ZSTD_LEGACY_SUPPORT <= 6) if (version==6) { ZSTDv06_frameParams fParams; size_t const frResult = ZSTDv06_getFrameParams(&fParams, src, srcSize); if (frResult != 0) return 0; return fParams.frameContentSize; } +#endif +#if (ZSTD_LEGACY_SUPPORT <= 7) if (version==7) { ZSTDv07_frameParams fParams; size_t const frResult = ZSTDv07_getFrameParams(&fParams, src, srcSize); if (frResult != 0) return 0; return fParams.frameContentSize; } +#endif return 0; /* should not be possible */ } @@ -86,14 +131,23 @@ MEM_STATIC size_t ZSTD_decompressLegacy( U32 const version = ZSTD_isLegacy(src, compressedSize); switch(version) { +#if (ZSTD_LEGACY_SUPPORT <= 1) case 1 : return ZSTDv01_decompress(dst, dstCapacity, src, compressedSize); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 2) case 2 : return ZSTDv02_decompress(dst, dstCapacity, src, compressedSize); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 3) case 3 : return ZSTDv03_decompress(dst, dstCapacity, src, compressedSize); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : return ZSTDv04_decompress(dst, dstCapacity, src, compressedSize); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 5) case 5 : { size_t result; ZSTDv05_DCtx* const zd = ZSTDv05_createDCtx(); @@ -102,6 +156,8 @@ MEM_STATIC size_t ZSTD_decompressLegacy( ZSTDv05_freeDCtx(zd); return result; } +#endif +#if (ZSTD_LEGACY_SUPPORT <= 6) case 6 : { size_t result; ZSTDv06_DCtx* const zd = ZSTDv06_createDCtx(); @@ -110,6 +166,8 @@ MEM_STATIC size_t ZSTD_decompressLegacy( ZSTDv06_freeDCtx(zd); return result; } +#endif +#if (ZSTD_LEGACY_SUPPORT <= 7) case 7 : { size_t result; ZSTDv07_DCtx* const zd = ZSTDv07_createDCtx(); @@ -118,6 +176,7 @@ MEM_STATIC size_t ZSTD_decompressLegacy( ZSTDv07_freeDCtx(zd); return result; } +#endif default : return ERROR(prefix_unknown); } @@ -129,20 +188,34 @@ MEM_STATIC size_t ZSTD_findFrameCompressedSizeLegacy(const void *src, U32 const version = ZSTD_isLegacy(src, compressedSize); switch(version) { +#if (ZSTD_LEGACY_SUPPORT <= 1) case 1 : return ZSTDv01_findFrameCompressedSize(src, compressedSize); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 2) case 2 : return ZSTDv02_findFrameCompressedSize(src, compressedSize); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 3) case 3 : return ZSTDv03_findFrameCompressedSize(src, compressedSize); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : return ZSTDv04_findFrameCompressedSize(src, compressedSize); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 5) case 5 : return ZSTDv05_findFrameCompressedSize(src, compressedSize); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 6) case 6 : return ZSTDv06_findFrameCompressedSize(src, compressedSize); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 7) case 7 : return ZSTDv07_findFrameCompressedSize(src, compressedSize); +#endif default : return ERROR(prefix_unknown); } @@ -157,10 +230,18 @@ MEM_STATIC size_t ZSTD_freeLegacyStreamContext(void* legacyContext, U32 version) case 2 : case 3 : return ERROR(version_unsupported); +#if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : return ZBUFFv04_freeDCtx((ZBUFFv04_DCtx*)legacyContext); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 5) case 5 : return ZBUFFv05_freeDCtx((ZBUFFv05_DCtx*)legacyContext); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 6) case 6 : return ZBUFFv06_freeDCtx((ZBUFFv06_DCtx*)legacyContext); +#endif +#if (ZSTD_LEGACY_SUPPORT <= 7) case 7 : return ZBUFFv07_freeDCtx((ZBUFFv07_DCtx*)legacyContext); +#endif } } @@ -176,6 +257,7 @@ MEM_STATIC size_t ZSTD_initLegacyStream(void** legacyContext, U32 prevVersion, U case 2 : case 3 : return 0; +#if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : { ZBUFFv04_DCtx* dctx = (prevVersion != newVersion) ? ZBUFFv04_createDCtx() : (ZBUFFv04_DCtx*)*legacyContext; @@ -185,6 +267,8 @@ MEM_STATIC size_t ZSTD_initLegacyStream(void** legacyContext, U32 prevVersion, U *legacyContext = dctx; return 0; } +#endif +#if (ZSTD_LEGACY_SUPPORT <= 5) case 5 : { ZBUFFv05_DCtx* dctx = (prevVersion != newVersion) ? ZBUFFv05_createDCtx() : (ZBUFFv05_DCtx*)*legacyContext; @@ -193,6 +277,8 @@ MEM_STATIC size_t ZSTD_initLegacyStream(void** legacyContext, U32 prevVersion, U *legacyContext = dctx; return 0; } +#endif +#if (ZSTD_LEGACY_SUPPORT <= 6) case 6 : { ZBUFFv06_DCtx* dctx = (prevVersion != newVersion) ? ZBUFFv06_createDCtx() : (ZBUFFv06_DCtx*)*legacyContext; @@ -201,6 +287,8 @@ MEM_STATIC size_t ZSTD_initLegacyStream(void** legacyContext, U32 prevVersion, U *legacyContext = dctx; return 0; } +#endif +#if (ZSTD_LEGACY_SUPPORT <= 7) case 7 : { ZBUFFv07_DCtx* dctx = (prevVersion != newVersion) ? ZBUFFv07_createDCtx() : (ZBUFFv07_DCtx*)*legacyContext; @@ -209,6 +297,7 @@ MEM_STATIC size_t ZSTD_initLegacyStream(void** legacyContext, U32 prevVersion, U *legacyContext = dctx; return 0; } +#endif } } @@ -224,6 +313,7 @@ MEM_STATIC size_t ZSTD_decompressLegacyStream(void* legacyContext, U32 version, case 2 : case 3 : return ERROR(version_unsupported); +#if (ZSTD_LEGACY_SUPPORT <= 4) case 4 : { ZBUFFv04_DCtx* dctx = (ZBUFFv04_DCtx*) legacyContext; @@ -236,6 +326,8 @@ MEM_STATIC size_t ZSTD_decompressLegacyStream(void* legacyContext, U32 version, input->pos += readSize; return hintSize; } +#endif +#if (ZSTD_LEGACY_SUPPORT <= 5) case 5 : { ZBUFFv05_DCtx* dctx = (ZBUFFv05_DCtx*) legacyContext; @@ -248,6 +340,8 @@ MEM_STATIC size_t ZSTD_decompressLegacyStream(void* legacyContext, U32 version, input->pos += readSize; return hintSize; } +#endif +#if (ZSTD_LEGACY_SUPPORT <= 6) case 6 : { ZBUFFv06_DCtx* dctx = (ZBUFFv06_DCtx*) legacyContext; @@ -260,6 +354,8 @@ MEM_STATIC size_t ZSTD_decompressLegacyStream(void* legacyContext, U32 version, input->pos += readSize; return hintSize; } +#endif +#if (ZSTD_LEGACY_SUPPORT <= 7) case 7 : { ZBUFFv07_DCtx* dctx = (ZBUFFv07_DCtx*) legacyContext; @@ -272,6 +368,7 @@ MEM_STATIC size_t ZSTD_decompressLegacyStream(void* legacyContext, U32 version, input->pos += readSize; return hintSize; } +#endif } } diff --git a/programs/Makefile b/programs/Makefile index a935c744a..0bf194260 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -42,12 +42,14 @@ ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) ZDICT_FILES := $(ZSTDDIR)/dictBuilder/*.c ZSTDDECOMP_O = $(ZSTDDIR)/decompress/zstd_decompress.o -ifeq ($(ZSTD_LEGACY_SUPPORT), 0) +ZSTD_LEGACY_SUPPORT ?= 1 ZSTDLEGACY_FILES:= +ifneq ($(ZSTD_LEGACY_SUPPORT), 0) +ifeq ($(shell test $(ZSTD_LEGACY_SUPPORT) -lt 8; echo $$?), 0) + ZSTDLEGACY_FILES += $(shell ls $(ZSTDDIR)/legacy/*.c | grep 'v0[$(ZSTD_LEGACY_SUPPORT)-7]') +endif + CPPFLAGS += -I$(ZSTDDIR)/legacy else -ZSTD_LEGACY_SUPPORT:=1 -CPPFLAGS += -I$(ZSTDDIR)/legacy -ZSTDLEGACY_FILES:= $(ZSTDDIR)/legacy/*.c endif ZSTDLIB_FILES := $(wildcard $(ZSTD_FILES)) $(wildcard $(ZSTDLEGACY_FILES)) $(wildcard $(ZDICT_FILES)) From 120df494e9e5df89781307776377a6e1dac16df3 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Mon, 13 Mar 2017 14:44:08 -0700 Subject: [PATCH 48/60] Update builds to not support legacy v01-v03 --- .buckconfig | 4 ++-- build/VS2005/zstd/zstd.vcproj | 8 ++++---- build/VS2005/zstdlib/zstdlib.vcproj | 8 ++++---- build/VS2008/zstd/zstd.vcproj | 8 ++++---- build/VS2008/zstdlib/zstdlib.vcproj | 8 ++++---- build/VS2010/libzstd-dll/libzstd-dll.vcxproj | 8 ++++---- build/VS2010/libzstd/libzstd.vcxproj | 8 ++++---- build/VS2010/zstd/zstd.vcxproj | 8 ++++---- build/cmake/CMakeLists.txt | 2 +- contrib/meson/meson.build | 2 +- lib/Makefile | 2 +- programs/Makefile | 2 +- tests/Makefile | 2 +- 13 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.buckconfig b/.buckconfig index d698b35ba..483f6053b 100644 --- a/.buckconfig +++ b/.buckconfig @@ -1,7 +1,7 @@ [cxx] - cppflags = -DXXH_NAMESPACE=ZSTD_ -DZSTD_LEGACY_SUPPORT=1 + cppflags = -DXXH_NAMESPACE=ZSTD_ -DZSTD_LEGACY_SUPPORT=4 cflags = -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef -Wpointer-arith - cxxppflags = -DXXH_NAMESPACE=ZSTD_ -DZSTD_LEGACY_SUPPORT=1 + cxxppflags = -DXXH_NAMESPACE=ZSTD_ -DZSTD_LEGACY_SUPPORT=4 cxxflags = -std=c++11 -Wno-deprecated-declarations gtest_dep = //contrib/pzstd:gtest diff --git a/build/VS2005/zstd/zstd.vcproj b/build/VS2005/zstd/zstd.vcproj index 58f254bc8..1f4febead 100644 --- a/build/VS2005/zstd/zstd.vcproj +++ b/build/VS2005/zstd/zstd.vcproj @@ -44,7 +44,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder;$(SolutionDir)..\..\lib\compress" - PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -121,7 +121,7 @@ EnableIntrinsicFunctions="true" OmitFramePointers="true" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder;$(SolutionDir)..\..\lib\compress" - PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" @@ -196,7 +196,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder;$(SolutionDir)..\..\lib\compress" - PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -274,7 +274,7 @@ EnableIntrinsicFunctions="true" OmitFramePointers="true" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder;$(SolutionDir)..\..\lib\compress" - PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/build/VS2005/zstdlib/zstdlib.vcproj b/build/VS2005/zstdlib/zstdlib.vcproj index f4c9950ff..8da313673 100644 --- a/build/VS2005/zstdlib/zstdlib.vcproj +++ b/build/VS2005/zstdlib/zstdlib.vcproj @@ -44,7 +44,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder" - PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -120,7 +120,7 @@ EnableIntrinsicFunctions="true" OmitFramePointers="true" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder" - PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" @@ -194,7 +194,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder" - PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -271,7 +271,7 @@ EnableIntrinsicFunctions="true" OmitFramePointers="true" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder" - PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/build/VS2008/zstd/zstd.vcproj b/build/VS2008/zstd/zstd.vcproj index 2dfaf3937..468d25672 100644 --- a/build/VS2008/zstd/zstd.vcproj +++ b/build/VS2008/zstd/zstd.vcproj @@ -45,7 +45,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder;$(SolutionDir)..\..\lib\compress" - PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -122,7 +122,7 @@ EnableIntrinsicFunctions="true" OmitFramePointers="true" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder;$(SolutionDir)..\..\lib\compress" - PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" @@ -197,7 +197,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder;$(SolutionDir)..\..\lib\compress" - PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -275,7 +275,7 @@ EnableIntrinsicFunctions="true" OmitFramePointers="true" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder;$(SolutionDir)..\..\lib\compress" - PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/build/VS2008/zstdlib/zstdlib.vcproj b/build/VS2008/zstdlib/zstdlib.vcproj index cba0ff908..857e1463e 100644 --- a/build/VS2008/zstdlib/zstdlib.vcproj +++ b/build/VS2008/zstdlib/zstdlib.vcproj @@ -45,7 +45,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder" - PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -121,7 +121,7 @@ EnableIntrinsicFunctions="true" OmitFramePointers="true" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder" - PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" @@ -195,7 +195,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder" - PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -272,7 +272,7 @@ EnableIntrinsicFunctions="true" OmitFramePointers="true" AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder" - PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj index f78598fb4..866d04a04 100644 --- a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj +++ b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj @@ -149,7 +149,7 @@ Level4 Disabled - ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -169,7 +169,7 @@ Level4 Disabled - ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -189,7 +189,7 @@ MaxSpeed true true - ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) false MultiThreaded ProgramDatabase @@ -211,7 +211,7 @@ MaxSpeed true true - ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) false false MultiThreaded diff --git a/build/VS2010/libzstd/libzstd.vcxproj b/build/VS2010/libzstd/libzstd.vcxproj index 727795514..186b4c4da 100644 --- a/build/VS2010/libzstd/libzstd.vcxproj +++ b/build/VS2010/libzstd/libzstd.vcxproj @@ -146,7 +146,7 @@ Level4 Disabled - ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -166,7 +166,7 @@ Level4 Disabled - ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -186,7 +186,7 @@ MaxSpeed true true - ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) false MultiThreaded ProgramDatabase @@ -208,7 +208,7 @@ MaxSpeed true true - ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) false false MultiThreaded diff --git a/build/VS2010/zstd/zstd.vcxproj b/build/VS2010/zstd/zstd.vcxproj index 62c0fe10f..7568e4902 100644 --- a/build/VS2010/zstd/zstd.vcxproj +++ b/build/VS2010/zstd/zstd.vcxproj @@ -155,7 +155,7 @@ Level4 Disabled - ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true false @@ -171,7 +171,7 @@ Level4 Disabled - ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ZSTD_LEGACY_SUPPORT=4;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true false @@ -189,7 +189,7 @@ MaxSpeed true true - ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) false false MultiThreaded @@ -210,7 +210,7 @@ MaxSpeed true true - ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ZSTD_LEGACY_SUPPORT=4;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) false false MultiThreaded diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt index 6b7c28925..4805cc2c9 100644 --- a/build/cmake/CMakeLists.txt +++ b/build/cmake/CMakeLists.txt @@ -16,7 +16,7 @@ OPTION(ZSTD_BUILD_CONTRIB "BUILD CONTRIB" OFF) IF (ZSTD_LEGACY_SUPPORT) MESSAGE(STATUS "ZSTD_LEGACY_SUPPORT defined!") - ADD_DEFINITIONS(-DZSTD_LEGACY_SUPPORT=1) + ADD_DEFINITIONS(-DZSTD_LEGACY_SUPPORT=4) ELSE (ZSTD_LEGACY_SUPPORT) MESSAGE(STATUS "ZSTD_LEGACY_SUPPORT not defined!") ADD_DEFINITIONS(-DZSTD_LEGACY_SUPPORT=0) diff --git a/contrib/meson/meson.build b/contrib/meson/meson.build index 369461335..8cbdcabec 100644 --- a/contrib/meson/meson.build +++ b/contrib/meson/meson.build @@ -15,7 +15,7 @@ libzstd_includes = [include_directories(common_dir, dictbuilder_dir, compress_di if get_option('legacy_support') message('Enabling legacy support') - libzstd_cflags = ['-DZSTD_LEGACY_SUPPORT=1'] + libzstd_cflags = ['-DZSTD_LEGACY_SUPPORT=4'] legacy_dir = join_paths(lib_dir, 'legacy') libzstd_includes += [include_directories(legacy_dir)] diff --git a/lib/Makefile b/lib/Makefile index 18b08a11d..197fdeeea 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -31,7 +31,7 @@ FLAGS = $(CPPFLAGS) $(CFLAGS) ZSTD_FILES := $(wildcard common/*.c compress/*.c decompress/*.c dictBuilder/*.c deprecated/*.c) -ZSTD_LEGACY_SUPPORT ?= 1 +ZSTD_LEGACY_SUPPORT ?= 4 ifneq ($(ZSTD_LEGACY_SUPPORT), 0) ifeq ($(shell test $(ZSTD_LEGACY_SUPPORT) -lt 8; echo $$?), 0) diff --git a/programs/Makefile b/programs/Makefile index 0bf194260..beeb0711c 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -42,7 +42,7 @@ ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) ZDICT_FILES := $(ZSTDDIR)/dictBuilder/*.c ZSTDDECOMP_O = $(ZSTDDIR)/decompress/zstd_decompress.o -ZSTD_LEGACY_SUPPORT ?= 1 +ZSTD_LEGACY_SUPPORT ?= 4 ZSTDLEGACY_FILES:= ifneq ($(ZSTD_LEGACY_SUPPORT), 0) ifeq ($(shell test $(ZSTD_LEGACY_SUPPORT) -lt 8; echo $$?), 0) diff --git a/tests/Makefile b/tests/Makefile index 59256f841..9382fe80d 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -167,7 +167,7 @@ longmatch : $(ZSTD_FILES) longmatch.c invalidDictionaries : $(ZSTD_FILES) invalidDictionaries.c $(CC) $(FLAGS) $^ -o $@$(EXT) -legacy : CFLAGS+= -DZSTD_LEGACY_SUPPORT=1 +legacy : CFLAGS+= -DZSTD_LEGACY_SUPPORT=4 legacy : CPPFLAGS+= -I$(ZSTDDIR)/legacy legacy : $(ZSTD_FILES) $(wildcard $(ZSTDDIR)/legacy/*.c) legacy.c $(CC) $(FLAGS) $^ -o $@$(EXT) From 9830aeeea6458124f6086ae9cdbf79a24244e30c Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Mon, 13 Mar 2017 17:19:37 -0700 Subject: [PATCH 49/60] Fix legacy support=0 case and accidental double include of version headers --- lib/legacy/zstd_legacy.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/legacy/zstd_legacy.h b/lib/legacy/zstd_legacy.h index 18e22e651..3c9798f88 100644 --- a/lib/legacy/zstd_legacy.h +++ b/lib/legacy/zstd_legacy.h @@ -20,15 +20,9 @@ extern "C" { #include "mem.h" /* MEM_STATIC */ #include "error_private.h" /* ERROR */ #include "zstd.h" /* ZSTD_inBuffer, ZSTD_outBuffer */ -#include "zstd_v01.h" -#include "zstd_v02.h" -#include "zstd_v03.h" -#include "zstd_v04.h" -#include "zstd_v05.h" -#include "zstd_v06.h" -#include "zstd_v07.h" -#ifndef ZSTD_LEGACY_SUPPORT +#if !defined (ZSTD_LEGACY_SUPPORT) || (ZSTD_LEGACY_SUPPORT == 0) +# undef ZSTD_LEGACY_SUPPORT # define ZSTD_LEGACY_SUPPORT 8 #endif From aa8bcf360fe7e4da8b00c250f81893e71aa90c0c Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 13 Mar 2017 18:11:07 -0700 Subject: [PATCH 50/60] Add xz and lzma support. Finish feature started by @inikep. * Add xz and lzma compression and decompression support to target `xzstd`. * Fix bug in gzip decompression that silently accepted truncated files. * Add gzip frame composition tests. * Add xz/lzma compatibility tests. * Add xz/lzma frame composition tests. --- programs/Makefile | 32 ++++++-- programs/fileio.c | 187 +++++++++++++++++++++++++++++++++++++++------ programs/fileio.h | 6 +- programs/zstdcli.c | 14 +++- tests/playTests.sh | 58 ++++++++++++++ 5 files changed, 261 insertions(+), 36 deletions(-) diff --git a/programs/Makefile b/programs/Makefile index a935c744a..8b97592d5 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -68,12 +68,27 @@ EXT = endif # zlib detection +NO_ZLIB_MSG := ==> no zlib, building zstd without .gz support VOID = /dev/null HAVE_ZLIB := $(shell printf '\#include \nint main(){}' | $(CC) -o have_zlib -x c - -lz 2> $(VOID) && rm have_zlib$(EXT) && echo 1 || echo 0) ifeq ($(HAVE_ZLIB), 1) +ZLIB_MSG := ==> building zstd with .gz compression support ZLIBCPP = -DZSTD_GZCOMPRESS -DZSTD_GZDECOMPRESS ZLIBLD = -lz +else +ZLIB_MSG := $(NO_ZLIB_MSG) endif +# lzma detection +NO_LZMA_MSG := ==> no liblzma, building zstd without .xz/.lzma support +HAVE_LZMA := $(shell printf '\#include \nint main(){}' | $(CC) -o have_lzma -x c - -llzma 2> $(VOID) && rm have_lzma$(EXT) && echo 1 || echo 0) +ifeq ($(HAVE_LZMA), 1) +LZMA_MSG := ==> building zstd with .xz/.lzma compression support +LZMACPP = -DZSTD_LZMACOMPRESS -DZSTD_LZMADECOMPRESS +LZMALD = -llzma +else +LZMA_MSG := $(NO_LZMA_MSG) +endif + .PHONY: default all clean clean_decomp_o install uninstall generate_res @@ -85,14 +100,15 @@ $(ZSTDDECOMP_O): CFLAGS += $(ALIGN_LOOP) zstd : CPPFLAGS += $(ZLIBCPP) zstd : LDFLAGS += $(ZLIBLD) -zstd-nogz : HAVE_ZLIB=0 -zstd zstd-nogz : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) -zstd zstd-nogz : $(ZSTDLIB_OBJ) zstdcli.o fileio.o bench.o datagen.o dibio.o -ifeq ($(HAVE_ZLIB), 1) - @echo "==> building zstd with .gz decompression support " -else - @echo "==> no zlib, building zstd with .zst support only (no .gz support) " -endif +zstd : LZMA_MSG := $(NO_LZMA_MSG) +zstd-nogz : ZLIB_MSG := $(NO_ZLIB_MSG) +zstd-nogz : LZMA_MSG := $(NO_LZMA_MSG) +xzstd : CPPFLAGS += $(ZLIBCPP) $(LZMACPP) +xzstd : LDFLAGS += $(ZLIBLD) $(LZMALD) +zstd zstd-nogz xzstd : CPPFLAGS += -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) +zstd zstd-nogz xzstd : $(ZSTDLIB_OBJ) zstdcli.o fileio.o bench.o datagen.o dibio.o + @echo "$(ZLIB_MSG)" + @echo "$(LZMA_MSG)" ifneq (,$(filter Windows%,$(OS))) windres/generate_res.bat endif diff --git a/programs/fileio.c b/programs/fileio.c index 41daa125e..e6481f1fa 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -44,6 +44,9 @@ # define z_const # endif #endif +#if defined(ZSTD_LZMACOMPRESS) || defined(ZSTD_LZMADECOMPRESS) +# include +#endif /*-************************************* @@ -71,7 +74,6 @@ #define MAX_DICT_SIZE (8 MB) /* protection against large input (attack scenario) */ #define FNSPACE 30 -#define GZ_EXTENSION ".gz" /*-************************************* @@ -434,6 +436,65 @@ static unsigned long long FIO_compressGzFrame(cRess_t* ress, const char* srcFile #endif +#ifdef ZSTD_LZMACOMPRESS +static unsigned long long FIO_compressLzmaFrame(cRess_t* ress, const char* srcFileName, U64 const srcFileSize, int compressionLevel, U64* readsize, int plain_lzma) +{ + unsigned long long inFileSize = 0, outFileSize = 0; + lzma_stream strm = LZMA_STREAM_INIT; + lzma_action action = LZMA_RUN; + lzma_ret ret; + + if (compressionLevel < 0) compressionLevel = 0; + if (compressionLevel > 9) compressionLevel = 9; + + if (plain_lzma) { + lzma_options_lzma opt_lzma; + if (lzma_lzma_preset(&opt_lzma, compressionLevel)) EXM_THROW(71, "zstd: %s: lzma_lzma_preset error", srcFileName); + ret = lzma_alone_encoder(&strm, &opt_lzma); /* LZMA */ + if (ret != LZMA_OK) EXM_THROW(71, "zstd: %s: lzma_alone_encoder error %d", srcFileName, ret); + } else { + ret = lzma_easy_encoder(&strm, compressionLevel, LZMA_CHECK_CRC64); /* XZ */ + if (ret != LZMA_OK) EXM_THROW(71, "zstd: %s: lzma_easy_encoder error %d", srcFileName, ret); + } + + strm.next_in = 0; + strm.avail_in = 0; + strm.next_out = ress->dstBuffer; + strm.avail_out = ress->dstBufferSize; + + while (1) { + if (strm.avail_in == 0) { + size_t const inSize = fread(ress->srcBuffer, 1, ress->srcBufferSize, ress->srcFile); + if (inSize == 0) action = LZMA_FINISH; + inFileSize += inSize; + strm.next_in = ress->srcBuffer; + strm.avail_in = inSize; + } + + ret = lzma_code(&strm, action); + + if (ret != LZMA_OK && ret != LZMA_STREAM_END) EXM_THROW(72, "zstd: %s: lzma_code encoding error %d", srcFileName, ret); + { size_t const compBytes = ress->dstBufferSize - strm.avail_out; + if (compBytes) { + if (fwrite(ress->dstBuffer, 1, compBytes, ress->dstFile) != compBytes) EXM_THROW(73, "Write error : cannot write to output file"); + outFileSize += compBytes; + strm.next_out = ress->dstBuffer; + strm.avail_out = ress->dstBufferSize; + } + } + if (!srcFileSize) DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%", (U32)(inFileSize>>20), (double)outFileSize/inFileSize*100) + else DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%", (U32)(inFileSize>>20), (U32)(srcFileSize>>20), (double)outFileSize/inFileSize*100); + if (ret == LZMA_STREAM_END) break; + } + + lzma_end(&strm); + *readsize = inFileSize; + + return outFileSize; +} +#endif + + /*! FIO_compressFilename_internal() : * same as FIO_compressFilename_extRess(), with `ress.desFile` already opened. * @return : 0 : compression completed correctly, @@ -448,14 +509,26 @@ static int FIO_compressFilename_internal(cRess_t ress, U64 compressedfilesize = 0; U64 const fileSize = UTIL_getFileSize(srcFileName); - if (g_compressionType) { + switch (g_compressionType) { + case FIO_zstdCompression: + break; + case FIO_gzipCompression: #ifdef ZSTD_GZCOMPRESS - compressedfilesize = FIO_compressGzFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize); + compressedfilesize = FIO_compressGzFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize); #else - (void)compressionLevel; - EXM_THROW(20, "zstd: %s: file cannot be compressed as gzip (zstd compiled without ZSTD_GZCOMPRESS) -- ignored \n", srcFileName); + (void)compressionLevel; + EXM_THROW(20, "zstd: %s: file cannot be compressed as gzip (zstd compiled without ZSTD_GZCOMPRESS) -- ignored \n", srcFileName); #endif - goto finish; + goto finish; + case FIO_xzCompression: + case FIO_lzmaCompression: +#ifdef ZSTD_LZMACOMPRESS + compressedfilesize = FIO_compressLzmaFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize, g_compressionType==FIO_lzmaCompression); +#else + (void)compressionLevel; + EXM_THROW(20, "zstd: %s: file cannot be compressed as xz/lzma (zstd compiled without ZSTD_LZMACOMPRESS) -- ignored \n", srcFileName); +#endif + goto finish; } /* init */ @@ -763,10 +836,10 @@ static void FIO_fwriteSparseEnd(FILE* file, unsigned storedSkips) { if (storedSkips-->0) { /* implies g_sparseFileSupport>0 */ int const seekResult = LONG_SEEK(file, storedSkips, SEEK_CUR); - if (seekResult != 0) EXM_THROW(69, "Final skip error (sparse file)\n"); + if (seekResult != 0) EXM_THROW(69, "Final skip error (sparse file)"); { const char lastZeroByte[1] = { 0 }; size_t const sizeCheck = fwrite(lastZeroByte, 1, 1, file); - if (sizeCheck != 1) EXM_THROW(69, "Write error : cannot write last zero\n"); + if (sizeCheck != 1) EXM_THROW(69, "Write error : cannot write last zero"); } } } @@ -849,6 +922,7 @@ static unsigned long long FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, co { unsigned long long outFileSize = 0; z_stream strm; + int flush = Z_NO_FLUSH; int ret; strm.zalloc = Z_NULL; @@ -866,11 +940,12 @@ static unsigned long long FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, co for ( ; ; ) { if (strm.avail_in == 0) { ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile); - if (ress->srcBufferLoaded == 0) break; + if (ress->srcBufferLoaded == 0) flush = Z_FINISH; strm.next_in = (z_const unsigned char*)ress->srcBuffer; strm.avail_in = (uInt)ress->srcBufferLoaded; } - ret = inflate(&strm, Z_NO_FLUSH); + ret = inflate(&strm, flush); + if (ret == Z_BUF_ERROR) EXM_THROW(39, "zstd: %s: premature end", srcFileName); if (ret != Z_OK && ret != Z_STREAM_END) { DISPLAY("zstd: %s: inflate error %d \n", srcFileName, ret); return 0; } { size_t const decompBytes = ress->dstBufferSize - strm.avail_out; if (decompBytes) { @@ -886,7 +961,60 @@ static unsigned long long FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, co if (strm.avail_in > 0) memmove(ress->srcBuffer, strm.next_in, strm.avail_in); ress->srcBufferLoaded = strm.avail_in; ret = inflateEnd(&strm); - if (ret != Z_OK) EXM_THROW(32, "zstd: %s: inflateEnd error %d \n", srcFileName, ret); + if (ret != Z_OK) EXM_THROW(32, "zstd: %s: inflateEnd error %d", srcFileName, ret); + return outFileSize; +} +#endif + + +#ifdef ZSTD_LZMADECOMPRESS +static unsigned long long FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile, const char* srcFileName, int plain_lzma) +{ + unsigned long long outFileSize = 0; + lzma_stream strm = LZMA_STREAM_INIT; + lzma_action action = LZMA_RUN; + lzma_ret ret; + + strm.next_in = 0; + strm.avail_in = 0; + if (plain_lzma) { + ret = lzma_alone_decoder(&strm, UINT64_MAX); /* LZMA */ + } else { + ret = lzma_stream_decoder(&strm, UINT64_MAX, 0); /* XZ */ + } + + if (ret != LZMA_OK) EXM_THROW(71, "zstd: %s: lzma_alone_decoder/lzma_stream_decoder error %d", srcFileName, ret); + + strm.next_out = ress->dstBuffer; + strm.avail_out = ress->dstBufferSize; + strm.avail_in = ress->srcBufferLoaded; + strm.next_in = ress->srcBuffer; + + for ( ; ; ) { + if (strm.avail_in == 0) { + ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile); + if (ress->srcBufferLoaded == 0) action = LZMA_FINISH; + strm.next_in = ress->srcBuffer; + strm.avail_in = ress->srcBufferLoaded; + } + ret = lzma_code(&strm, action); + + if (ret == LZMA_BUF_ERROR) EXM_THROW(39, "zstd: %s: premature end", srcFileName); + if (ret != LZMA_OK && ret != LZMA_STREAM_END) { DISPLAY("zstd: %s: lzma_code decoding error %d \n", srcFileName, ret); return 0; } + { size_t const decompBytes = ress->dstBufferSize - strm.avail_out; + if (decompBytes) { + if (fwrite(ress->dstBuffer, 1, decompBytes, ress->dstFile) != decompBytes) EXM_THROW(31, "Write error : cannot write to output file"); + outFileSize += decompBytes; + strm.next_out = ress->dstBuffer; + strm.avail_out = ress->dstBufferSize; + } + } + if (ret == LZMA_STREAM_END) break; + } + + if (strm.avail_in > 0) memmove(ress->srcBuffer, strm.next_in, strm.avail_in); + ress->srcBufferLoaded = strm.avail_in; + lzma_end(&strm); return outFileSize; } #endif @@ -924,7 +1052,7 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* dstFileName, const ch } readSomething = 1; /* there is at least >= 4 bytes in srcFile */ if (ress.srcBufferLoaded < toRead) { DISPLAY("zstd: %s: unknown header \n", srcFileName); fclose(srcFile); return 1; } /* srcFileName is empty */ - if (buf[0] == 31 && buf[1] == 139) { /* gz header */ + if (buf[0] == 31 && buf[1] == 139) { /* gz magic number */ #ifdef ZSTD_GZDECOMPRESS unsigned long long const result = FIO_decompressGzFrame(&ress, srcFile, srcFileName); if (result == 0) return 1; @@ -932,6 +1060,16 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* dstFileName, const ch #else DISPLAYLEVEL(1, "zstd: %s: gzip file cannot be uncompressed (zstd compiled without ZSTD_GZDECOMPRESS) -- ignored \n", srcFileName); return 1; +#endif + } else if ((buf[0] == 0xFD && buf[1] == 0x37) /* xz magic number */ + || (buf[0] == 0x5D && buf[1] == 0x00)) { /* lzma header (no magic number) */ +#ifdef ZSTD_LZMADECOMPRESS + unsigned long long const result = FIO_decompressLzmaFrame(&ress, srcFile, srcFileName, buf[0] != 0xFD); + if (result == 0) return 1; + filesize += result; +#else + DISPLAYLEVEL(1, "zstd: %s: xz/lzma file cannot be uncompressed (zstd compiled without ZSTD_LZMADECOMPRESS) -- ignored \n", srcFileName); + return 1; #endif } else { if (!ZSTD_isFrame(ress.srcBuffer, toRead)) { @@ -1020,32 +1158,31 @@ int FIO_decompressMultipleFilenames(const char** srcNamesTable, unsigned nbFiles missingFiles += FIO_decompressSrcFile(ress, suffix, srcNamesTable[u]); if (fclose(ress.dstFile)) EXM_THROW(72, "Write error : cannot properly close stdout"); } else { - size_t const suffixSize = strlen(suffix); - size_t const gzipSuffixSize = strlen(GZ_EXTENSION); + size_t suffixSize; size_t dfnSize = FNSPACE; unsigned u; char* dstFileName = (char*)malloc(FNSPACE); if (dstFileName==NULL) EXM_THROW(73, "not enough memory for dstFileName"); for (u=0; u tmp + $ZSTD -f --format=gzip tmp + $ZSTD -f tmp + cat tmp.gz tmp.zst tmp.gz tmp.zst | $ZSTD -d -f -o tmp + head -c -1 tmp.gz | $ZSTD -t && die "incomplete frame not detected !" + rm tmp* +else + $ECHO "gzip mode not supported" +fi + + +$ECHO "\n**** xz compatibility tests **** " + +LZMAMODE=1 +$ZSTD --format=xz -V || LZMAMODE=0 +if [ $LZMAMODE -eq 1 ]; then + $ECHO "xz support detected" + XZEXE=1 + xz -V && lzma -V || XZEXE=0 + if [ $XZEXE -eq 1 ]; then + ./datagen > tmp + $ZSTD --format=lzma -f tmp + $ZSTD --format=xz -f tmp + xz -t -v tmp.xz + xz -t -v tmp.lzma + xz -f -k tmp + lzma -f -k --lzma1 tmp + $ZSTD -d -f -v tmp.xz + $ZSTD -d -f -v tmp.lzma + rm tmp* + else + $ECHO "xz binary not detected" + fi +else + $ECHO "xz mode not supported" +fi + + +$ECHO "\n**** xz frame tests **** " + +if [ $LZMAMODE -eq 1 ]; then + ./datagen > tmp + $ZSTD -f --format=xz tmp + $ZSTD -f --format=lzma tmp + $ZSTD -f tmp + cat tmp.xz tmp.lzma tmp.zst tmp.lzma tmp.xz tmp.zst | $ZSTD -d -f -o tmp + head -c -1 tmp.xz | $ZSTD -t && die "incomplete frame not detected !" + head -c -1 tmp.lzma | $ZSTD -t && die "incomplete frame not detected !" + rm tmp* +else + $ECHO "xz mode not supported" +fi + + $ECHO "\n**** zstd round-trip tests **** " roundTripTest From 7ae3039f41c3277a659e828752a3fe84f2a5e221 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 14 Mar 2017 04:19:51 -0700 Subject: [PATCH 51/60] updated NEWS for v1.1.4 --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 9073a8724..bbd9e1688 100644 --- a/NEWS +++ b/NEWS @@ -3,10 +3,12 @@ cli : new : can compress in *.gz format, using --format=gzip command, by Przemys cli : new : advanced benchmark command --priority=rt cli : fix : write on sparse-enabled file systems in 32-bits mode, by @ds77 cli : fix : --rm remains silent when input is stdin +cli : experimental : xzstd, with support for xz/lzma decoding, by Przemyslaw Skibinski speed : improved decompression speed in streaming mode for single shot scenarios (+5%) memory : DDict (decompression dictionary) memory usage down from 150 KB to 20 KB arch : 32-bits variant able to generate and decode very long matches (>32 MB), by Sean Purcell API : new : ZSTD_findFrameCompressedSize(), ZSTD_getFrameContentSize(), ZSTD_findDecompressedSize() +API : changed : dropped support of legacy versions <= v0.3 (can be changed by modifying ZSTD_LEGACY_SUPPORT value) build: new: meson build system in contrib/meson, by Dima Krasner build: improved cmake script, by @Majlen build: added -Wformat-security flag, as recommended by Padraig Brady From dec2b96536893f16923f149d3eab9c8474e8dd4b Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Tue, 14 Mar 2017 11:24:09 -0700 Subject: [PATCH 52/60] Add functions missing from manual, and fix parameter alignment --- contrib/gen_html/gen_html.cpp | 12 ++++--- doc/zstd_manual.html | 67 +++++++++++++++++++++++------------ lib/zstd.h | 12 ++++++- 3 files changed, 64 insertions(+), 27 deletions(-) diff --git a/contrib/gen_html/gen_html.cpp b/contrib/gen_html/gen_html.cpp index 22ff65b10..e5261c086 100644 --- a/contrib/gen_html/gen_html.cpp +++ b/contrib/gen_html/gen_html.cpp @@ -19,7 +19,7 @@ void trim(string& s, string characters) { size_t p = s.find_first_not_of(characters); s.erase(0, p); - + p = s.find_last_not_of(characters); if (string::npos != p) s.erase(p+1); @@ -48,7 +48,7 @@ vector get_lines(vector& input, int& linenum, string terminator) line = input[linenum]; if (terminator.empty() && line.empty()) { linenum--; break; } - + epos = line.find(terminator); if (!terminator.empty() && epos!=string::npos) { out.push_back(line); @@ -168,7 +168,11 @@ int main(int argc, char *argv[]) { sout << "
";
             for (l=0; l

"; for (l=0; l" << endl << "" << endl; return 0; -} \ No newline at end of file +} diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 77e8974de..204f56ea5 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -55,8 +55,8 @@

Simple API


 
 
size_t ZSTD_compress( void* dst, size_t dstCapacity,
-                            const void* src, size_t srcSize,
-                                  int compressionLevel);
+                const void* src, size_t srcSize,
+                      int compressionLevel);
 

Compresses `src` content as a single zstd compressed frame into already allocated `dst`. Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. @return : compressed size written into `dst` (<= `dstCapacity), @@ -64,7 +64,7 @@


size_t ZSTD_decompress( void* dst, size_t dstCapacity,
-                              const void* src, size_t compressedSize);
+                  const void* src, size_t compressedSize);
 

`compressedSize` : must be the _exact_ size of some number of compressed and/or skippable frames. `dstCapacity` is an upper bound of originalSize. If user cannot imply a maximum upper bound, it's better to use streaming mode to decompress data. @@ -118,7 +118,11 @@ size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx);

Same as ZSTD_compress(), requires an allocated ZSTD_CCtx (see ZSTD_createCCtx()).


-

Decompression context

typedef struct ZSTD_DCtx_s ZSTD_DCtx;
+

Decompression context

   When decompressing many times,
+   it is recommended to allocate a context just once, and re-use it for each successive compression operation.
+   This will make workload friendlier for system's memory.
+   Use one context per thread for parallel execution in multi-threaded environments. 
+
typedef struct ZSTD_DCtx_s ZSTD_DCtx;
 ZSTD_DCtx* ZSTD_createDCtx(void);
 size_t     ZSTD_freeDCtx(ZSTD_DCtx* dctx);
 

@@ -129,19 +133,19 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);

Simple dictionary API


 
 
size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx,
-                                           void* dst, size_t dstCapacity,
-                                     const void* src, size_t srcSize,
-                                     const void* dict,size_t dictSize,
-                                           int compressionLevel);
+                               void* dst, size_t dstCapacity,
+                         const void* src, size_t srcSize,
+                         const void* dict,size_t dictSize,
+                               int compressionLevel);
 

Compression using a predefined Dictionary (see dictBuilder/zdict.h). Note : This function loads the dictionary, resulting in significant startup delay. Note : When `dict == NULL || dictSize < 8` no dictionary is used.


size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx,
-                                             void* dst, size_t dstCapacity,
-                                       const void* src, size_t srcSize,
-                                       const void* dict,size_t dictSize);
+                                 void* dst, size_t dstCapacity,
+                           const void* src, size_t srcSize,
+                           const void* dict,size_t dictSize);
 

Decompression using a predefined Dictionary (see dictBuilder/zdict.h). Dictionary must be identical to the one used during compression. Note : This function loads the dictionary, resulting in significant startup delay. @@ -162,9 +166,9 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);


size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx,
-                                            void* dst, size_t dstCapacity,
-                                      const void* src, size_t srcSize,
-                                      const ZSTD_CDict* cdict);
+                                void* dst, size_t dstCapacity,
+                          const void* src, size_t srcSize,
+                          const ZSTD_CDict* cdict);
 

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. @@ -180,9 +184,9 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);


size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx,
-                                              void* dst, size_t dstCapacity,
-                                        const void* src, size_t srcSize,
-                                        const ZSTD_DDict* ddict);
+                                  void* dst, size_t dstCapacity,
+                            const void* src, size_t srcSize,
+                            const ZSTD_DDict* ddict);
 

Decompression using a digested Dictionary. Faster startup than ZSTD_decompress_usingDict(), recommended when same dictionary is used multiple times.


@@ -239,6 +243,14 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
+

ZSTD_CStream management functions

ZSTD_CStream* ZSTD_createCStream(void);
+size_t ZSTD_freeCStream(ZSTD_CStream* zcs);
+

+

Streaming compression functions

size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel);
+size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
+size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output);
+size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output);
+

size_t ZSTD_CStreamInSize(void);    /**< recommended size for input buffer */
 

size_t ZSTD_CStreamOutSize(void);   /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block in all circumstances. */
@@ -264,6 +276,12 @@ size_t     ZSTD_freeDCtx(ZSTD_DCtx* dctx);
  
 
+

ZSTD_DStream management functions

ZSTD_DStream* ZSTD_createDStream(void);
+size_t ZSTD_freeDStream(ZSTD_DStream* zds);
+

+

Streaming decompression functions

size_t ZSTD_initDStream(ZSTD_DStream* zds);
+size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
+

size_t ZSTD_DStreamInSize(void);    /*!< recommended size for input buffer */
 

size_t ZSTD_DStreamOutSize(void);   /*!< recommended size for output buffer. Guarantee to successfully flush at least one complete block in all circumstances. */
@@ -381,7 +399,7 @@ typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; v
 


ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, unsigned byReference,
-                                                  ZSTD_parameters params, ZSTD_customMem customMem);
+                                      ZSTD_parameters params, ZSTD_customMem customMem);
 

Create a ZSTD_CDict using external alloc and free, and customized compression parameters


@@ -409,10 +427,10 @@ typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; v


size_t ZSTD_compress_advanced (ZSTD_CCtx* ctx,
-                                           void* dst, size_t dstCapacity,
-                                     const void* src, size_t srcSize,
-                                     const void* dict,size_t dictSize,
-                                           ZSTD_parameters params);
+                               void* dst, size_t dstCapacity,
+                         const void* src, size_t srcSize,
+                         const void* dict,size_t dictSize,
+                               ZSTD_parameters params);
 

Same as ZSTD_compress_usingDict(), with fine-tune control of each compression parameter


@@ -443,6 +461,11 @@ typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; v It is important that dictBuffer outlives DDict, it must remain read accessible throughout the lifetime of DDict


+
ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize,
+                                      unsigned byReference, ZSTD_customMem customMem);
+

Create a ZSTD_DDict using external alloc and free, optionally by reference +


+
size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
 

Gives the amount of memory used by a given ZSTD_DDict


diff --git a/lib/zstd.h b/lib/zstd.h index 0b48b7ec6..a3237c77e 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -139,7 +139,11 @@ ZSTDLIB_API size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx); 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 */ +/*= Decompression context +* When decompressing many times, +* it is recommended to allocate a context just once, and re-use it for each successive compression operation. +* This will make workload friendlier for system's memory. +* Use one context per thread for parallel execution in multi-threaded environments. */ typedef struct ZSTD_DCtx_s ZSTD_DCtx; ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx(void); ZSTDLIB_API size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx); @@ -277,9 +281,11 @@ typedef struct ZSTD_outBuffer_s { * *******************************************************************/ typedef struct ZSTD_CStream_s ZSTD_CStream; +/*===== ZSTD_CStream management functions =====*/ ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream(void); ZSTDLIB_API size_t ZSTD_freeCStream(ZSTD_CStream* zcs); +/*===== Streaming compression functions =====*/ ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel); ZSTDLIB_API size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input); ZSTDLIB_API size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output); @@ -313,9 +319,11 @@ ZSTDLIB_API size_t ZSTD_CStreamOutSize(void); /**< recommended size for output * *******************************************************************************/ typedef struct ZSTD_DStream_s ZSTD_DStream; +/*===== ZSTD_DStream management functions =====*/ ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream(void); ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds); +/*===== Streaming decompression functions =====*/ ZSTDLIB_API size_t ZSTD_initDStream(ZSTD_DStream* zds); ZSTDLIB_API size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input); @@ -540,6 +548,8 @@ ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx); * It is important that dictBuffer outlives DDict, it must remain read accessible throughout the lifetime of DDict */ ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize); +/*! ZSTD_createDDict_advanced() : + * Create a ZSTD_DDict using external alloc and free, optionally by reference */ ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize, unsigned byReference, ZSTD_customMem customMem); From f5e50519e01679a2664273f420da6fb77f05ec48 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Wed, 15 Mar 2017 15:04:54 -0700 Subject: [PATCH 53/60] Prevent fuzz testers from combining large dicts with high clevel --- tests/fuzzer.c | 6 ++++-- tests/zstreamtest.c | 10 ++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 6fb69972a..def7542b5 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -66,6 +66,7 @@ static clock_t g_displayClock = 0; * Fuzzer functions *********************************************************/ #define MIN(a,b) ((a)<(b)?(a):(b)) +#define MAX(a,b) ((a)>(b)?(a):(b)) static clock_t FUZ_clockSpan(clock_t cStart) { @@ -799,11 +800,12 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD /*===== Streaming compression test, scattered segments and dictionary =====*/ { U32 const testLog = FUZ_rand(&lseed) % maxSrcLog; - int const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (testLog/3))) + 1; + U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog; + int const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (MAX(testLog, dictLog)/3))) + 1; maxTestSize = FUZ_rLogLength(&lseed, testLog); if (maxTestSize >= dstBufferSize) maxTestSize = dstBufferSize-1; - dictSize = FUZ_randomLength(&lseed, maxSampleLog); /* needed also for decompression */ + dictSize = FUZ_rLogLength(&lseed, dictLog); /* needed also for decompression */ dict = srcBuffer + (FUZ_rand(&lseed) % (srcBufferSize - dictSize)); if (FUZ_rand(&lseed) & 0xF) { diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 54b890266..aa7367bcf 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -645,11 +645,12 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres } } else { U32 const testLog = FUZ_rand(&lseed) % maxSrcLog; - U32 const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (testLog/3))) + 1; + U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog; + U32 const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (MAX(testLog, dictLog)/3))) + 1; maxTestSize = FUZ_rLogLength(&lseed, testLog); oldTestLog = testLog; /* random dictionary selection */ - dictSize = ((FUZ_rand(&lseed)&63)==1) ? FUZ_randomLength(&lseed, maxSampleLog) : 0; + dictSize = ((FUZ_rand(&lseed)&63)==1) ? FUZ_rLogLength(&lseed, dictLog) : 0; { size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize); dict = srcBuffer + dictStart; } @@ -886,11 +887,12 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp } } else { U32 const testLog = FUZ_rand(&lseed) % maxSrcLog; - U32 const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (testLog/3))) + 1; + U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog; + U32 const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (MAX(testLog, dictLog)/3))) + 1; maxTestSize = FUZ_rLogLength(&lseed, testLog); oldTestLog = testLog; /* random dictionary selection */ - dictSize = ((FUZ_rand(&lseed)&63)==1) ? FUZ_randomLength(&lseed, maxSampleLog) : 0; + dictSize = ((FUZ_rand(&lseed)&63)==1) ? FUZ_rLogLength(&lseed, dictLog) : 0; { size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize); dict = srcBuffer + dictStart; } From 72a243e3bdcd73cb2b646fa91c969820c98662c5 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 16 Mar 2017 15:33:21 -0700 Subject: [PATCH 54/60] Convert manpage to Markdown, converted with ronn --- programs/Makefile | 25 ++ programs/zstd.1 | 629 +++++++++++++++++++-------------------------- programs/zstd.1.md | 299 +++++++++++++++++++++ 3 files changed, 583 insertions(+), 370 deletions(-) create mode 100644 programs/zstd.1.md diff --git a/programs/Makefile b/programs/Makefile index 1475cb610..bb91e069a 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -18,6 +18,19 @@ ZSTDDIR = ../lib +# Version numbers +LIBVER_SRC := $(ZSTDDIR)/zstd.h +LIBVER_MAJOR_SCRIPT:=`sed -n '/define ZSTD_VERSION_MAJOR/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < $(LIBVER_SRC)` +LIBVER_MINOR_SCRIPT:=`sed -n '/define ZSTD_VERSION_MINOR/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < $(LIBVER_SRC)` +LIBVER_PATCH_SCRIPT:=`sed -n '/define ZSTD_VERSION_RELEASE/s/.*[[:blank:]]\([0-9][0-9]*\).*/\1/p' < $(LIBVER_SRC)` +LIBVER_SCRIPT:= $(LIBVER_MAJOR_SCRIPT).$(LIBVER_MINOR_SCRIPT).$(LIBVER_PATCH_SCRIPT) +LIBVER_MAJOR := $(shell echo $(LIBVER_MAJOR_SCRIPT)) +LIBVER_MINOR := $(shell echo $(LIBVER_MINOR_SCRIPT)) +LIBVER_PATCH := $(shell echo $(LIBVER_PATCH_SCRIPT)) +LIBVER := $(shell echo $(LIBVER_SCRIPT)) + +ZSTD_VERSION=$(LIBVER) + ifeq ($(shell $(CC) -v 2>&1 | grep -c "gcc version "), 1) ALIGN_LOOP = -falign-loops=32 else @@ -91,6 +104,8 @@ else LZMA_MSG := $(NO_LZMA_MSG) endif +MD2ROFF =ronn +MD2ROFF_FLAGS = --roff --warnings --manual="User Commands" --organization="zstd $(ZSTD_VERSION)" .PHONY: default all clean clean_decomp_o install uninstall generate_res @@ -174,6 +189,16 @@ clean: clean_decomp_o: @$(RM) $(ZSTDDECOMP_O) +zstd.1: zstd.1.md + cat $^ | $(MD2ROFF) $(MD2ROFF_FLAGS) | sed -n '/^\.\\\".*/!p' > $@ + +man: zstd.1 + +clean-man: + rm zstd.1 + +preview-man: clean-man man + man ./zstd.1 #----------------------------------------------------------------------------- # make install is validated only for Linux, OSX, BSD, Hurd and Solaris targets diff --git a/programs/zstd.1 b/programs/zstd.1 index 684fb868a..9ac4ca0a8 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -1,408 +1,297 @@ -\" -\" zstd.1: This is a manual page for 'zstd' program. This file is part of the -\" zstd project. -\" Author: Yann Collet -\" - -\" No hyphenation -.hy 0 -.nr HY 0 - -.TH zstd "1" "2015-08-22" "zstd" "User Commands" -.SH NAME -\fBzstd, unzstd, zstdcat\fR - Compress or decompress .zst files - -.SH SYNOPSIS -.TP 5 -\fBzstd\fR [\fBOPTIONS\fR] [-|INPUT-FILE] [-o ] -.PP -.B unzstd -is equivalent to -.BR "zstd \-d" -.br -.B zstdcat -is equivalent to -.BR "zstd \-dcf" -.br - -.SH DESCRIPTION -.PP -\fBzstd\fR is a fast lossless compression algorithm -and data compression tool, -with command line syntax similar to \fB gzip (1) \fR and \fB xz (1) \fR . -It is based on the \fBLZ77\fR family, with further FSE & huff0 entropy stages. -\fBzstd\fR offers highly configurable compression speed, -with fast modes at > 200 MB/s per core, -and strong modes nearing lzma compression ratios. -It also features a very fast decoder, with speeds > 500 MB/s per core. - -\fBzstd\fR command line syntax is generally similar to gzip, -but features the following differences : - - Source files are preserved by default. - It's possible to remove them automatically by using \fB--rm\fR command. - - When compressing a single file, \fBzstd\fR displays progress notifications and result summary by default. - Use \fB-q\fR to turn them off - -.PP -.B zstd -compresses or decompresses each -.I file -according to the selected operation mode. -If no -.I files -are given or -.I file -is -.BR \- , -.B zstd -reads from standard input and writes the processed data -to standard output. -.B zstd -will refuse (display an error and skip the -.IR file ) -to write compressed data to standard output if it is a terminal. -Similarly, -.B zstd -will refuse to read compressed data -from standard input if it is a terminal. - -.PP -Unless -.B \-\-stdout -or -.B \-o -is specified, -.I files -are written to a new file whose name is derived from the source -.I file -name: -.IP \(bu 3 -When compressing, the suffix -.B .zst -is appended to the source filename to get the target filename. -.IP \(bu 3 -When decompressing, the -.B .zst -suffix is removed from the filename to get the target filename. - -.SS "Concatenation with .zst files" -It is possible to concatenate -.B .zst -files as is. -.B zstd -will decompress such files as if they were a single -.B .zst -file. - - - -.SH OPTIONS - +. +.TH "ZSTD" "1" "March 2017" "zstd 1.1.4" "User Commands" +. +.SH "NAME" +\fBzstd\fR \- zstd, unzstd, zstdcat \- Compress or decompress \.zst files +. +.SH "SYNOPSIS" +\fBzstd\fR [\fIOPTIONS\fR] [\-|] [\-o ] +. +.P +\fBunzstd\fR is equivalent to \fBzstd \-d\fR \fBzstdcat\fR is equivalent to \fBzstd \-dcf\fR +. +.SH "DESCRIPTION" +\fBzstd\fR is a fast lossless compression algorithm and data compression tool, with command line syntax similar to \fBgzip (1)\fR and \fBxz (1)\fR\. It is based on the \fBLZ77\fR family, with further FSE & huff0 entropy stages\. \fBzstd\fR offers highly configurable compression speed, with fast modes at > 200 MB/s per code, and strong modes nearing lzma compression ratios\. It also features a very fast decoder, with speeds > 500 MB/s per core\. +. +.P +\fBzstd\fR command line syntax is generally similar to gzip, but features the following differences : +. +.IP "\(bu" 4 +Source files are preserved by default\. It\'s possible to remove them automatically by using the \fB\-\-rm\fR command\. +. +.IP "\(bu" 4 +When compressing a single file, \fBzstd\fR displays progress notifications and result summary by default\. Use \fB\-q\fR to turn them off\. +. +.IP "" 0 +. +.P +\fBzstd\fR compresses or decompresses each \fIfile\fR according to the selected operation mode\. If no \fIfiles\fR are given or \fIfile\fR is \fB\-\fR, \fBzstd\fR reads from standard input and writes the processed data to standard output\. \fBzstd\fR will refuse to (display an error and skip the \fIfile\fR) to write compressed data to standard output if it is a terminal\. Similarly, \fBzstd\fR will refuse to read compressed data from standard input if it is a terminal\. +. +.P +Unless \fB\-\-stdout\fR or \fB\-o\fR is specified, \fIfiles\fR are written to a new file whose name is derived from the source \fIfile\fR name: +. +.IP "\(bu" 4 +When compressing, the suffix \fB\.zst\fR is appended to the source filename to get the target filename\. +. +.IP "\(bu" 4 +When decompressing, the \fB\.zst\fR suffix is removed from the source filename to get the target filename +. +.IP "" 0 +. +.SS "Concatenation with \.zst files" +It is possible to concatenate \fB\.zst\fR files as is\. \fBzstd\fR will decompress such files as if they were a single \fB\.zst\fR file\. +. +.SH "OPTIONS" . .SS "Integer suffixes and special values" -In most places where an integer argument is expected, -an optional suffix is supported to easily indicate large integers. -There must be no space between the integer and the suffix. +In most places where an integer argument is expected, an optional suffix is supported to easily indicate large integers\. There must be no space between the integer and the suffix\. +. .TP -.B KiB -Multiply the integer by 1,024 (2^10). -.BR Ki , -.BR K , -and -.B KB -are accepted as synonyms for -.BR KiB . +\fBKiB\fR +Multiply the integer by 1,024 (2^10)\. \fBKi\fR, \fBK\fR, and \fBKB\fR are accepted as synonyms for \fBKiB\fR\. +. .TP -.B MiB -Multiply the integer by 1,048,576 (2^20). -.BR Mi , -.BR M , -and -.B MB -are accepted as synonyms for -.BR MiB . - +\fBMiB\fR +Multiply the integer by 1,048,576 (2^20)\. \fBMi\fR, \fBM\fR, and \fBMB\fR are accepted as synonyms for \fBMiB\fR\. . .SS "Operation mode" -If multiple operation mode options are given, -the last one takes effect. +If multiple operation mode options are given, the last one takes effect\. +. .TP -.BR \-z ", " \-\-compress -Compress. -This is the default operation mode when no operation mode option -is specified and no other operation mode is implied from -the command name (for example, -.B unzstd -implies -.BR \-\-decompress ). +\fB\-z\fR, \fB\-\-compress\fR +Compress\. This is the default operation mode when no operation mode option is specified and no other operation mode is implied from the command name (for example, \fBunzstd\fR implies \fB\-\-decompress\fR)\. +. .TP -.BR \-d ", " \-\-decompress ", " \-\-uncompress -Decompress. +\fB\-d\fR, \fB\-\-decompress\fR, \fB\-\-uncompress\fR +Decompress\. +. .TP -.BR \-t ", " \-\-test -Test the integrity of compressed -.IR files . -This option is equivalent to -.B "\-\-decompress \-\-stdout" -except that the decompressed data is discarded instead of being -written to standard output. -No files are created or removed. +\fB\-t\fR, \fB\-\-test\fR +Test the integrity of compressed \fIfiles\fR\. This option is equivalent to \fB\-\-decompress \-\-stdout\fR except that the decompressed data is discarded instead of being written to standard output\. No files are created or removed\. +. .TP -.B \-b# - benchmark file(s) using compression level # +\fB\-b#\fR +Benchmark file(s) using compression level # +. .TP -.B \--train FILEs - use FILEs as training set to create a dictionary. The training set should contain a lot of small files (> 100). - +\fB\-\-train FILEs\fR +Use FILEs as a training set to create a dictionary\. The training set should contain a lot of small files (> 100)\. . .SS "Operation modifiers" +. .TP -.B \-# - # compression level [1-19] (default:3) +\fB\-#\fR +\fB#\fR compression level [1\-19] (default: 3) +. .TP -.BR \--ultra - unlocks high compression levels 20+ (maximum 22), using a lot more memory. -Note that decompression will also require more memory when using these levels. +\fB\-\-ultra\fR +unlocks high compression levels 20+ (maximum 22), using a lot more memory\. Note that decompression will also require more memory when using these levels\. +. .TP -.B \-D file - use `file` as Dictionary to compress or decompress FILE(s) +\fB\-D file\fR +use \fBfile\fR as Dictionary to compress or decompress FILE(s) +. .TP -.BR \--no-dictID - do not store dictionary ID within frame header (dictionary compression). - The decoder will have to rely on implicit knowledge about which dictionary to use, -it won't be able to check if it's correct. +\fB\-\-nodictID\fR +do not store dictionary ID within frame header (dictionary compression)\. The decoder will have to rely on implicit knowledge about which dictionary to use, it won\'t be able to check if it\'s correct\. +. .TP -.B \-o file - save result into `file` (only possible with a single INPUT-FILE) +\fB\-o file\fR +save result into \fBfile\fR (only possible with a single INPUT\-FILE) +. .TP -.BR \-f ", " --force - overwrite output without prompting +\fB\-f\fR, \fB\-\-force\fR +overwrite output without prompting +. .TP -.BR \-c ", " --stdout - force write to standard output, even if it is the console +\fB\-c\fR, \fB\-\-stdout\fR +force write to standard output, even if it is the console +. .TP -.BR \--[no-]sparse - enable / disable sparse FS support, to make files with many zeroes smaller on disk. - Creating sparse files may save disk space and speed up the decompression -by reducing the amount of disk I/O. - default : enabled when output is into a file, and disabled when output is stdout. - This setting overrides default and can force sparse mode over stdout. +\fB\-\-[no\-]sparse\fR +enable / disable sparse FS support, to make files with many zeroes smaller on disk\. Creating sparse files may save disk space and speed up decompression by reducing the amount of disk I/O\. default : enabled when output is into a file, and disabled when output is stdout\. This setting overrides default and can force sparse mode over stdout\. +. .TP -.BR \--rm - remove source file(s) after successful compression or decompression +\fB\-\-rm\fR +remove source file(s) after successful compression or decompression +. .TP -.BR \-k ", " --keep - keep source file(s) after successful compression or decompression. - This is the default behavior. +\fB\-k\fR, \fB\-\-keep\fR +keep source file(s) after successful compression or decompression\. This is the default behaviour\. +. .TP -.BR \-r - operate recursively on directories +\fB\-r\fR +operate recursively on dictionaries +. .TP -.BR \-h/\-H ", " --help - display help/long help and exit +\fB\-h\fR/\fB\-H\fR, \fB\-\-help\fR +display help/long help and exit +. .TP -.BR \-V ", " --version - display Version number and exit +\fB\-V\fR, \fB\-\-version\fR +display version number and exit +. .TP -.BR \-v ", " --verbose - verbose mode +\fB\-v\fR +verbose mode +. .TP -.BR \-q ", " --quiet - suppress warnings, interactivity and notifications. - specify twice to suppress errors too. +\fB\-q\fR, \fB\-\-quiet\fR +suppress warnings, interactivity, and notifications\. specify twice to suppress errors too\. +. .TP -.BR \-C ", " --[no-]check - add integrity check computed from uncompressed data (default : enabled) +\fB\-C\fR, \fB\-\-[no\-]check\fR +add integrety check computed from uncompressed data (default : enabled) +. .TP -.BR \-t ", " --test - Test the integrity of compressed files. This option is equivalent to \fB--decompress --stdout > /dev/null\fR. - No files are created or removed. +\fB\-\-\fR +All arguments after \fB\-\-\fR are treated as files +. +.SH "DICTIONARY BUILDER" +\fBzstd\fR offers \fIdictionary\fR compression, useful for very small files and messages\. It\'s possible to train \fBzstd\fR with some samples, the result of which is saved into a file called a \fBdictionary\fR\. Then during compression and decompression, reference the same dictionary\. It will improve compression ratio of small files\. Typical gains range from 10% (at 64KB) to x5 better (at <1KB)\. +. .TP -.BR -- - All arguments after -- are treated as files - - -.SH DICTIONARY BUILDER -.PP -\fBzstd\fR offers \fIdictionary\fR compression, useful for very small files and messages. -It's possible to train \fBzstd\fR with some samples, the result of which is saved into a file called `dictionary`. -Then during compression and decompression, make reference to the same dictionary. -It will improve compression ratio of small files. -Typical gains range from ~10% (at 64KB) to x5 better (at <1KB). +\fB\-\-train FILEs\fR +use FILEs as training set to create a dictionary\. The training set should contain a lot of small files (> 100), and weight typically 100x the target dictionary size (for example, 10 MB for a 100 KB dictionary)\. +. .TP -.B \--train FILEs - use FILEs as training set to create a dictionary. The training set should contain a lot of small files (> 100), -and weight typically 100x the target dictionary size (for example, 10 MB for a 100 KB dictionary) +\fB\-o file\fR +dictionary saved into \fBfile\fR (default: dictionary) +. .TP -.B \-o file - dictionary saved into `file` (default: dictionary) +\fB\-\-maxdict #\fR +limit dictionary to specified size (default : (112640) +. .TP -.B \--maxdict # - limit dictionary to specified size (default : 112640) +\fB\-\-dictID #\fR +A dictionary ID is a locally unique ID that a decoder can use to verify it is using the right dictionary\. By default, zstd will create a 4\-bytes random number ID\. It\'s possible to give a precise number instead\. Short numbers have an advantage : an ID < 256 will only need 1 byte in the compressed frame header, and an ID < 65536 will only need 2 bytes\. This compares favorably to 4 bytes default\. However, it\'s up to the dictionary manager to not assign twice the same ID to 2 different dictionaries\. +. .TP -.B \--dictID # - A dictionary ID is a locally unique ID that a decoder can use to verify it is using the right dictionary. - By default, zstd will create a 4-bytes random number ID. - It's possible to give a precise number instead. - Short numbers have an advantage : an ID < 256 will only need 1 byte in the compressed frame header, - and an ID < 65536 will only need 2 bytes. This compares favorably to 4 bytes default. - However, it's up to the dictionary manager to not assign twice the same ID to 2 different dictionaries. +\fB\-s#\fR +dictionary selectivity level (default: 9) the smaller the value, the denser the dictionary, improving its efficiency but reducing its possible maximum size\. +. .TP -.B \-s# - dictionary selectivity level (default: 9) - the smaller the value, the denser the dictionary, improving its efficiency but reducing its possible maximum size. -.TP -.B \--cover=k=#,d=# - Use alternate dictionary builder algorithm named cover with parameters \fIk\fR and \fId\fR with \fId\fR <= \fIk\fR. - Selects segments of size \fIk\fR with the highest score to put in the dictionary. - The score of a segment is computed by the sum of the frequencies of all the subsegments of of size \fId\fR. - Generally \fId\fR should be in the range [6, 24]. - Good values for \fIk\fR vary widely based on the input data, but a safe range is [32, 2048]. - Example: \fB--train --cover=k=64,d=8 FILEs\fR. -.TP -.B \--optimize-cover[=steps=#,k=#,d=#] - If \fIsteps\fR is not specified, the default value of 32 is used. - If \fIk\fR is not specified, \fIsteps\fR values in [16, 2048] are checked for each value of \fId\fR. - If \fId\fR is not specified, the values checked are [6, 8, ..., 16]. - - Runs the cover dictionary builder for each parameter set saves the optimal parameters and dictionary. - Prints the optimal parameters and writes the optimal dictionary to the output file. - Supports multithreading if \fBzstd\fR is compiled with threading support. - - The parameter \fIk\fR is more sensitve than \fId\fR, and is faster to optimize over. - Suggested use is to run with a \fIsteps\fR <= 32 with neither \fIk\fR nor \fId\fR set. - Once it completes, use the value of \fId\fR it selects with a higher \fIsteps\fR (in the range [256, 1024]). - \fBzstd --train --optimize-cover FILEs - \fBzstd --train --optimize-cover=d=d,steps=512 FILEs -.TP - -.SH BENCHMARK -.TP -.B \-b# - benchmark file(s) using compression level # -.TP -.B \-e# - benchmark file(s) using multiple compression levels, from -b# to -e# (included). -.TP -.B \-i# - minimum evaluation time, in seconds (default : 3s), benchmark mode only -.TP -.B \-B# - cut file into independent blocks of size # (default: no block) -.B \--priority=rt - set process priority to real-time - -.SH ADVANCED COMPRESSION OPTIONS -.TP -.B \--zstd[=\fIoptions\fR] -.PD -\fBzstd\fR provides 22 predefined compression levels. The selected or default predefined compression level can be changed with advanced compression options. -The \fIoptions\fR are provided as a comma-separated list. You may specify only the \fIoptions\fR you want to change and the rest will be taken from the selected or default compression level. -The list of available \fIoptions\fR: -.RS - -.TP -.BI strategy= strat -.PD 0 -.TP -.BI strat= strat -.PD -Specify a strategy used by a match finder. -.IP "" -There are 8 strategies numbered from 0 to 7, from faster to stronger: -0=ZSTD_fast, 1=ZSTD_dfast, 2=ZSTD_greedy, 3=ZSTD_lazy, 4=ZSTD_lazy2, 5=ZSTD_btlazy2, 6=ZSTD_btopt, 7=ZSTD_btopt2. -.IP "" - -.TP -.BI windowLog= wlog -.PD 0 -.TP -.BI wlog= wlog -.PD -Specify the maximum number of bits for a match distance. -.IP "" -The higher number of bits increases the chance to find a match what usually improves compression ratio. -It also increases memory requirements for compressor and decompressor. -.IP "" -The minimum \fIwlog\fR is 10 (1 KiB) and the maximum is 25 (32 MiB) for 32-bit compilation and 27 (128 MiB) for 64-bit compilation. -.IP "" - -.TP -.BI hashLog= hlog -.PD 0 -.TP -.BI hlog= hlog -.PD -Specify the maximum number of bits for a hash table. -.IP "" -The bigger hash table causes less collisions what usually make compression faster but requires more memory during compression. -.IP "" -The minimum \fIhlog\fR is 6 (64 B) and the maximum is 25 (32 MiB) for 32-bit compilation and 27 (128 MiB) for 64-bit compilation. - -.TP -.BI chainLog= clog -.PD 0 -.TP -.BI clog= clog -.PD -Specify the maximum number of bits for a hash chain or a binary tree. -.IP "" -The higher number of bits increases the chance to find a match what usually improves compression ratio. -It also slows down compression speed and increases memory requirements for compression. -This option is ignored for the ZSTD_fast strategy. -.IP "" -The minimum \fIclog\fR is 6 (64 B) and the maximum is 26 (64 MiB) for 32-bit compilation and 28 (256 MiB) for 64-bit compilation. -.IP "" - -.TP -.BI searchLog= slog -.PD 0 -.TP -.BI slog= slog -.PD -Specify the maximum number of searches in a hash chain or a binary tree using logarithmic scale. -.IP "" -The bigger number of searches increases the chance to find a match what usually improves compression ratio but decreases compression speed. -.IP "" -The minimum \fIslog\fR is 1 and the maximum is 24 for 32-bit compilation and 26 for 64-bit compilation. -.IP "" - -.TP -.BI searchLength= slen -.PD 0 -.TP -.BI slen= slen -.PD -Specify the minimum searched length of a match in a hash table. -.IP "" -The bigger search length usually decreases compression ratio but improves decompression speed. -.IP "" -The minimum \fIslen\fR is 3 and the maximum is 7. -.IP "" - -.TP -.BI targetLength= tlen -.PD 0 -.TP -.BI tlen= tlen -.PD -Specify the minimum match length that causes a match finder to interrupt searching of better matches. -.IP "" -The bigger minimum match length usually improves compression ratio but decreases compression speed. -This option is used only with ZSTD_btopt and ZSTD_btopt2 strategies. -.IP "" -The minimum \fItlen\fR is 4 and the maximum is 999. -.IP "" - -.PP -.B An example +\fB\-\-cover=k#,d=#\fR +Use alternate dictionary builder algorithm named cover with parameters \fIk\fR and \fId\fR with \fId\fR <= \fIk\fR\. Selects segments of size \fIk\fR with the highest score to put in the dictionary\. The score of a segment is computed by the sum of the frequencies of all the subsegments of of size \fId\fR\. Generally \fId\fR should be in the range [6, 24]\. Good values for \fIk\fR vary widely based on the input data, but a safe range is [32, 2048]\. +. .br -The following parameters sets advanced compression options to predefined level 19 for files bigger than 256 KB: -.IP "" -\fB--zstd=\fRwindowLog=23,chainLog=23,hashLog=22,searchLog=6,searchLength=3,targetLength=48,strategy=6 - -.SH BUGS -Report bugs at:- https://github.com/facebook/zstd/issues - -.SH AUTHOR +Example: \fB\-\-train \-\-cover=k=64,d=8 FILEs\fR\. +. +.TP +\fB\-\-optimize\-cover[=steps=#,k=#,d=#]\fR +If \fIsteps\fR is not specified, the default value of 32 is used\. If \fIk\fR is not specified, the \fIk\fR values in [16, 2048] are checked for each value of \fId\fR\. If \fId\fR is not specified, the values checked are [6, 8, \.\.\., 16]\. +. +.IP +Runs the cover dictionary builder for each parameter set and saves the optimal parameters and dictionary\. Prints the optimal parameters and writes the optimal dictionary to the output file\. Supports multithreading if \fBzstd\fR is compiled with threading support\. +. +.IP +The parameter \fIk\fR is more sensitive than \fId\fR, and is faster to optimize over\. Suggested use is to run with a \fIsteps\fR <= 32 with neither \fIk\fR nor \fId\fR set\. Once it completes, use the value of \fId\fR it selects with a higher \fIsteps\fR (in the range [256, 1024])\. +. +.IP +\fBzstd \-\-train \-\-optimize\-cover FILEs\fR +. +.br +\fBzstd \-\-train \-\-optimize\-cover=d=d,steps=512 FILEs\fR +. +.SH "BENCHMARK" +. +.TP +\fB\-b#\fR +benchmark file(s) using compression level # +. +.TP +\fB\-e#\fR +benchmark file(s) using multiple compression levels, from \fB\-b#\fR to \fB\-e#\fR (inclusive) +. +.TP +\fB\-i#\fR +minimum evaluation time, in seconds (default : 3s), benchmark mode only +. +.TP +\fB\-B#\fR +cut file into independent blocks of size # (default: no block) +. +.TP +\fB\-\-priority=rt\fR +set process priority to real\-time +. +.SH "ADVANCED COMPRESSION OPTIONS" +. +.SS "\-\-zstd[=options]:" +\fBzstd\fR provides 22 predefined compression levels\. The selected or default predefined compression level can be changed with advanced compression options\. The \fIoptions\fR are provided as a comma\-separated list\. You may specify only the options you want to change and the rest will be taken from the selected or default compression level\. The list of available \fIoptions\fR: +. +.TP +\fBstrategy\fR=\fIstrat\fR, \fBstrat\fR=\fIstrat\fR +Specify a strategy used by a match finder\. +. +.IP +There are 8 strategies numbered from 0 to 7, from faster to stronger: 0=ZSTD_fast, 1=ZSTD_dfast, 2=ZSTD_greedy, 3=ZSTD_lazy, 4=ZSTD_lazy2, 5=ZSTD_btlazy2, 6=ZSTD_btopt, 7=ZSTD_btopt2\. +. +.TP +\fBwindowLog\fR=\fIwlog\fR, \fBwlog\fR=\fIwlog\fR +Specify the maximum number of bits for a match distance\. +. +.IP +The higher number of increases the chance to find a match which usually improves compression ratio\. It also increases memory requirements for the compressor and decompressor\. The minimum \fIwlog\fR is 10 (1 KiB) and the maximum is 27 (128 MiB)\. +. +.TP +\fBhashLog\fR=\fIhlog\fR, \fBhlog\fR=\fIhlog\fR +Specify the maximum number of bits for a hash table\. +. +.IP +Bigger hash tables cause less collisions which usually makes compression faster, but requires more memory during compression\. +. +.IP +The minimum \fIhlog\fR is 6 (64 B) and the maximum is 26 (128 MiB)\. +. +.TP +\fBchainLog\fR=\fIclog\fR, \fBclog\fR=\fIclog\fR +Specify the maximum number of bits for a hash chain or a binary tree\. +. +.IP +Higher numbers of bits increases the chance to find a match which usually improves compression ratio\. It also slows down compression speed and increases memory requirements for compression\. This option is ignored for the ZSTD_fast strategy\. +. +.IP +The minimum \fIclog\fR is 6 (64 B) and the maximum is 28 (256 MiB)\. +. +.TP +\fBsearchLog\fR=\fIslog\fR, \fBslog\fR=\fIslog\fR +Specify the maximum number of searches in a hash chain or a binary tree using logarithmic scale\. +. +.IP +More searches increases the chance to find a match which usually increases compression ratio but decreases compression speed\. +. +.IP +The minimum \fIslog\fR is 1 and the maximum is 26\. +. +.TP +\fBsearchLength\fR=\fIslen\fR, \fBslen\fR=\fIslen\fR +Specify the minimum searched length of a match in a hash table\. +. +.IP +Larger search lengths usually decrease compression ratio but improve decompression speed\. +. +.IP +The minimum \fIslen\fR is 3 and the maximum is 7\. +. +.TP +\fBtargetLen\fR=\fItlen\fR, \fBtlen\fR=\fItlen\fR +Specify the minimum match length that causes a match finder to stop searching for better matches\. +. +.IP +A larger minimum match length usually improves compression ratio but decreases compression speed\. This option is only used with strategies ZSTD_btopt and ZSTD_btopt2\. +. +.IP +The minimum \fItlen\fR is 4 and the maximum is 999\. +. +.SS "Example" +The following parameters sets advanced compression options to those of predefined level 19 for files bigger than 256 KB: +. +.P +\fB\-\-zstd\fR=windowLog=23,chainLog=23,hashLog=22,searchLog=6,searchLength=3,targetLength=48,strategy=6 +. +.SH "BUGS" +Report bugs at: https://github\.com/facebook/zstd/issues +. +.SH "AUTHOR" Yann Collet diff --git a/programs/zstd.1.md b/programs/zstd.1.md new file mode 100644 index 000000000..d170dcc8c --- /dev/null +++ b/programs/zstd.1.md @@ -0,0 +1,299 @@ +zstd(1) -- zstd, unzstd, zstdcat - Compress or decompress .zst files +==================================================================== + +SYNOPSIS +-------- + +`zstd` [*OPTIONS*] [-|<INPUT-FILE>] [-o <OUTPUT-FILE>] + +`unzstd` is equivalent to `zstd -d` +`zstdcat` is equivalent to `zstd -dcf` + +DESCRIPTION +----------- +`zstd` is a fast lossless compression algorithm and data compression tool, +with command line syntax similar to `gzip (1)` and `xz (1)`. +It is based on the **LZ77** family, with further FSE & huff0 entropy stages. +`zstd` offers highly configurable compression speed, +with fast modes at > 200 MB/s per code, +and strong modes nearing lzma compression ratios. +It also features a very fast decoder, with speeds > 500 MB/s per core. + +`zstd` command line syntax is generally similar to gzip, +but features the following differences : + + - Source files are preserved by default. + It's possible to remove them automatically by using the `--rm` command. + - When compressing a single file, `zstd` displays progress notifications + and result summary by default. + Use `-q` to turn them off. + +`zstd` compresses or decompresses each _file_ according to the selected +operation mode. +If no _files_ are given or _file_ is `-`, `zstd` reads from standard input +and writes the processed data to standard output. +`zstd` will refuse to (display an error and skip the _file_) to write +compressed data to standard output if it is a terminal. +Similarly, `zstd` will refuse to read compressed data from standard input if it +is a terminal. + +Unless `--stdout` or `-o` is specified, _files_ are written to a new file +whose name is derived from the source _file_ name: + +* When compressing, the suffix `.zst` is appended to the source filename to + get the target filename. +* When decompressing, the `.zst` suffix is removed from the source filename to + get the target filename + +### Concatenation with .zst files +It is possible to concatenate `.zst` files as is. +`zstd` will decompress such files as if they were a single `.zst` file. + +OPTIONS +------- + +### Integer suffixes and special values +In most places where an integer argument is expected, +an optional suffix is supported to easily indicate large integers. +There must be no space between the integer and the suffix. + +* `KiB`: + Multiply the integer by 1,024 (2\^10). + `Ki`, `K`, and `KB` are accepted as synonyms for `KiB`. +* `MiB`: + Multiply the integer by 1,048,576 (2\^20). + `Mi`, `M`, and `MB` are accepted as synonyms for `MiB`. + +### Operation mode +If multiple operation mode options are given, +the last one takes effect. + +* `-z`, `--compress`: + Compress. + This is the default operation mode when no operation mode option is specified + and no other operation mode is implied from the command name + (for example, `unzstd` implies `--decompress`). +* `-d`, `--decompress`, `--uncompress`: + Decompress. +* `-t`, `--test`: + Test the integrity of compressed _files_. + This option is equivalent to `--decompress --stdout` except that the + decompressed data is discarded instead of being written to standard output. + No files are created or removed. +* `-b#`: + Benchmark file(s) using compression level # +* `--train FILEs`: + Use FILEs as a training set to create a dictionary. + The training set should contain a lot of small files (> 100). + +### Operation modifiers + +* `-#`: + `#` compression level \[1-19] (default: 3) +* `--ultra`: + unlocks high compression levels 20+ (maximum 22), using a lot more memory. + Note that decompression will also require more memory when using these levels. +* `-D file`: + use `file` as Dictionary to compress or decompress FILE(s) +* `--nodictID`: + do not store dictionary ID within frame header (dictionary compression). + The decoder will have to rely on implicit knowledge about which dictionary to use, + it won't be able to check if it's correct. +* `-o file`: + save result into `file` (only possible with a single INPUT-FILE) +* `-f`, `--force`: + overwrite output without prompting +* `-c`, `--stdout`: + force write to standard output, even if it is the console +* `--[no-]sparse`: + enable / disable sparse FS support, + to make files with many zeroes smaller on disk. + Creating sparse files may save disk space and speed up decompression by + reducing the amount of disk I/O. + default : enabled when output is into a file, + and disabled when output is stdout. + This setting overrides default and can force sparse mode over stdout. +* `--rm`: + remove source file(s) after successful compression or decompression +* `-k`, `--keep`: + keep source file(s) after successful compression or decompression. + This is the default behaviour. +* `-r`: + operate recursively on dictionaries +* `-h`/`-H`, `--help`: + display help/long help and exit +* `-V`, `--version`: + display version number and exit +* `-v`: + verbose mode +* `-q`, `--quiet`: + suppress warnings, interactivity, and notifications. + specify twice to suppress errors too. +* `-C`, `--[no-]check`: + add integrety check computed from uncompressed data (default : enabled) +* `--`: + All arguments after `--` are treated as files + +DICTIONARY BUILDER +------------------ +`zstd` offers _dictionary_ compression, +useful for very small files and messages. +It's possible to train `zstd` with some samples, +the result of which is saved into a file called a `dictionary`. +Then during compression and decompression, reference the same dictionary. +It will improve compression ratio of small files. +Typical gains range from 10% (at 64KB) to x5 better (at <1KB). + +* `--train FILEs`: + use FILEs as training set to create a dictionary. + The training set should contain a lot of small files (> 100), + and weight typically 100x the target dictionary size + (for example, 10 MB for a 100 KB dictionary). +* `-o file`: + dictionary saved into `file` (default: dictionary) +* `--maxdict #`: + limit dictionary to specified size (default : (112640) +* `--dictID #`: + A dictionary ID is a locally unique ID that a decoder can use to verify it is + using the right dictionary. + By default, zstd will create a 4-bytes random number ID. + It's possible to give a precise number instead. + Short numbers have an advantage : an ID < 256 will only need 1 byte in the + compressed frame header, and an ID < 65536 will only need 2 bytes. + This compares favorably to 4 bytes default. + However, it's up to the dictionary manager to not assign twice the same ID to + 2 different dictionaries. +* `-s#`: + dictionary selectivity level (default: 9) + the smaller the value, the denser the dictionary, + improving its efficiency but reducing its possible maximum size. +* `--cover=k#,d=#`: + Use alternate dictionary builder algorithm named cover with parameters + _k_ and _d_ with _d_ <= _k_. + Selects segments of size _k_ with the highest score to put in the dictionary. + The score of a segment is computed by the sum of the frequencies of all the + subsegments of of size _d_. + Generally _d_ should be in the range [6, 24]. + Good values for _k_ vary widely based on the input data, + but a safe range is [32, 2048].
+ Example: `--train --cover=k=64,d=8 FILEs`. + +* `--optimize-cover[=steps=#,k=#,d=#]`: + If _steps_ is not specified, the default value of 32 is used. + If _k_ is not specified, the _k_ values in [16, 2048] are checked for each + value of _d_. + If _d_ is not specified, the values checked are [6, 8, ..., 16]. + + Runs the cover dictionary builder for each parameter set and saves the + optimal parameters and dictionary. + Prints the optimal parameters and writes the optimal dictionary to the output file. + Supports multithreading if `zstd` is compiled with threading support. + + The parameter _k_ is more sensitive than _d_, and is faster to optimize over. + Suggested use is to run with a _steps_ <= 32 with neither _k_ nor _d_ set. + Once it completes, use the value of _d_ it selects with a higher _steps_ + (in the range [256, 1024]). + + `zstd --train --optimize-cover FILEs`
+ `zstd --train --optimize-cover=d=d,steps=512 FILEs` + + +BENCHMARK +--------- + +* `-b#`: + benchmark file(s) using compression level # +* `-e#`: + benchmark file(s) using multiple compression levels, from `-b#` to `-e#` (inclusive) +* `-i#`: + minimum evaluation time, in seconds (default : 3s), benchmark mode only +* `-B#`: + cut file into independent blocks of size # (default: no block) +* `--priority=rt`: + set process priority to real-time + +ADVANCED COMPRESSION OPTIONS +---------------------------- +### --zstd[=options]: +`zstd` provides 22 predefined compression levels. +The selected or default predefined compression level can be changed with +advanced compression options. +The _options_ are provided as a comma-separated list. +You may specify only the options you want to change and the rest will be +taken from the selected or default compression level. +The list of available _options_: + +- `strategy`=_strat_, `strat`=_strat_: + Specify a strategy used by a match finder. + + There are 8 strategies numbered from 0 to 7, from faster to stronger: + 0=ZSTD\_fast, 1=ZSTD\_dfast, 2=ZSTD\_greedy, 3=ZSTD\_lazy, + 4=ZSTD\_lazy2, 5=ZSTD\_btlazy2, 6=ZSTD\_btopt, 7=ZSTD\_btopt2. + +- `windowLog`=_wlog_, `wlog`=_wlog_: + Specify the maximum number of bits for a match distance. + + The higher number of increases the chance to find a match which usually + improves compression ratio. + It also increases memory requirements for the compressor and decompressor. + The minimum _wlog_ is 10 (1 KiB) and the maximum is 27 (128 MiB). + +- `hashLog`=_hlog_, `hlog`=_hlog_: + Specify the maximum number of bits for a hash table. + + Bigger hash tables cause less collisions which usually makes compression + faster, but requires more memory during compression. + + The minimum _hlog_ is 6 (64 B) and the maximum is 26 (128 MiB). + +- `chainLog`=_clog_, `clog`=_clog_: + Specify the maximum number of bits for a hash chain or a binary tree. + + Higher numbers of bits increases the chance to find a match which usually + improves compression ratio. + It also slows down compression speed and increases memory requirements for + compression. + This option is ignored for the ZSTD_fast strategy. + + The minimum _clog_ is 6 (64 B) and the maximum is 28 (256 MiB). + +- `searchLog`=_slog_, `slog`=_slog_: + Specify the maximum number of searches in a hash chain or a binary tree + using logarithmic scale. + + More searches increases the chance to find a match which usually increases + compression ratio but decreases compression speed. + + The minimum _slog_ is 1 and the maximum is 26. + +- `searchLength`=_slen_, `slen`=_slen_: + Specify the minimum searched length of a match in a hash table. + + Larger search lengths usually decrease compression ratio but improve + decompression speed. + + The minimum _slen_ is 3 and the maximum is 7. + +- `targetLen`=_tlen_, `tlen`=_tlen_: + Specify the minimum match length that causes a match finder to stop + searching for better matches. + + A larger minimum match length usually improves compression ratio but + decreases compression speed. + This option is only used with strategies ZSTD_btopt and ZSTD_btopt2. + + The minimum _tlen_ is 4 and the maximum is 999. + +### Example +The following parameters sets advanced compression options to those of +predefined level 19 for files bigger than 256 KB: + +`--zstd`=windowLog=23,chainLog=23,hashLog=22,searchLog=6,searchLength=3,targetLength=48,strategy=6 + +BUGS +---- +Report bugs at: https://github.com/facebook/zstd/issues + +AUTHOR +------ +Yann Collet From b8e52d3c83eeeeec088836b5840ee1ca307a612d Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 16 Mar 2017 16:06:03 -0700 Subject: [PATCH 55/60] Fix zstd not erroring on compressing to terminal without forceStdout --- programs/zstdcli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index ae49da7b1..fc7ca9e36 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -635,7 +635,7 @@ int main(int argCount, const char* argv[]) /* Check if input/output defined as console; trigger an error in this case */ if (!strcmp(filenameTable[0], stdinmark) && IS_CONSOLE(stdin) ) CLEAN_RETURN(badusage(programName)); - if (outFileName && !strcmp(outFileName, stdoutmark) && IS_CONSOLE(stdout) && strcmp(filenameTable[0], stdinmark) && !(forceStdout && (operation==zom_decompress))) + if (outFileName && !strcmp(outFileName, stdoutmark) && IS_CONSOLE(stdout) && !strcmp(filenameTable[0], stdinmark) && !forceStdout && operation!=zom_decompress) CLEAN_RETURN(badusage(programName)); /* user-selected output filename, only possible with a single file */ From d973071e907ba2b9f99db6ea14c625a1307c1ac9 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Thu, 16 Mar 2017 16:25:19 -0700 Subject: [PATCH 56/60] Add tests for compressed data on console --- tests/playTests.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index c493fed55..c584fe560 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -72,6 +72,10 @@ cp tmp tmp2 $ZSTD tmp2 -fo && die "-o must be followed by filename " $ECHO "test : implied stdout when input is stdin" $ECHO bob | $ZSTD | $ZSTD -d +$ECHO "test : compressed data to terminal" +$ECHO bob | $ZSTD && die "should have refused : compressed data to terminal" +$ECHO "test : compressed data from terminal (a hang here is a test fail, zstd is wrongly waiting on data from terminal)" +$ZSTD -d > $INTOVOID && die "should have refused : compressed data from terminal" $ECHO "test : null-length file roundtrip" $ECHO -n '' | $ZSTD - --stdout | $ZSTD -d --stdout $ECHO "test : decompress file with wrong suffix (must fail)" From 0a189b63fee91518796bc225eb249dd4e927fcf1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 16 Mar 2017 16:33:53 -0700 Subject: [PATCH 57/60] fix minor details in man page --- programs/zstd.1 | 13 +++++++++++-- programs/zstd.1.md | 22 ++++++++++++++-------- programs/zstdcli.c | 2 +- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/programs/zstd.1 b/programs/zstd.1 index 9ac4ca0a8..f79b3ce16 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -8,7 +8,10 @@ \fBzstd\fR [\fIOPTIONS\fR] [\-|] [\-o ] . .P -\fBunzstd\fR is equivalent to \fBzstd \-d\fR \fBzstdcat\fR is equivalent to \fBzstd \-dcf\fR +\fBunzstd\fR is equivalent to \fBzstd \-d\fR +. +.P +\fBzstdcat\fR is equivalent to \fBzstd \-dcf\fR . .SH "DESCRIPTION" \fBzstd\fR is a fast lossless compression algorithm and data compression tool, with command line syntax similar to \fBgzip (1)\fR and \fBxz (1)\fR\. It is based on the \fBLZ77\fR family, with further FSE & huff0 entropy stages\. \fBzstd\fR offers highly configurable compression speed, with fast modes at > 200 MB/s per code, and strong modes nearing lzma compression ratios\. It also features a very fast decoder, with speeds > 500 MB/s per core\. @@ -22,10 +25,16 @@ Source files are preserved by default\. It\'s possible to remove them automatica .IP "\(bu" 4 When compressing a single file, \fBzstd\fR displays progress notifications and result summary by default\. Use \fB\-q\fR to turn them off\. . +.IP "\(bu" 4 +\fBzstd\fR does not accept input from console, but it properly accepts \fBstdin\fR when it\'s not the console\. +. +.IP "\(bu" 4 +\fBzstd\fR displays a short help page when command line is an error\. Use \fB\-q\fR to turn it off\. +. .IP "" 0 . .P -\fBzstd\fR compresses or decompresses each \fIfile\fR according to the selected operation mode\. If no \fIfiles\fR are given or \fIfile\fR is \fB\-\fR, \fBzstd\fR reads from standard input and writes the processed data to standard output\. \fBzstd\fR will refuse to (display an error and skip the \fIfile\fR) to write compressed data to standard output if it is a terminal\. Similarly, \fBzstd\fR will refuse to read compressed data from standard input if it is a terminal\. +\fBzstd\fR compresses or decompresses each \fIfile\fR according to the selected operation mode\. If no \fIfiles\fR are given or \fIfile\fR is \fB\-\fR, \fBzstd\fR reads from standard input and writes the processed data to standard output\. \fBzstd\fR will refuse to write compressed data to standard output if it is a terminal : it will display an error message and skip the \fIfile\fR\. Similarly, \fBzstd\fR will refuse to read compressed data from standard input if it is a terminal\. . .P Unless \fB\-\-stdout\fR or \fB\-o\fR is specified, \fIfiles\fR are written to a new file whose name is derived from the source \fIfile\fR name: diff --git a/programs/zstd.1.md b/programs/zstd.1.md index d170dcc8c..bb5b22664 100644 --- a/programs/zstd.1.md +++ b/programs/zstd.1.md @@ -7,8 +7,10 @@ SYNOPSIS `zstd` [*OPTIONS*] [-|<INPUT-FILE>] [-o <OUTPUT-FILE>] `unzstd` is equivalent to `zstd -d` + `zstdcat` is equivalent to `zstd -dcf` + DESCRIPTION ----------- `zstd` is a fast lossless compression algorithm and data compression tool, @@ -27,15 +29,19 @@ but features the following differences : - When compressing a single file, `zstd` displays progress notifications and result summary by default. Use `-q` to turn them off. + - `zstd` does not accept input from console, + but it properly accepts `stdin` when it's not the console. + - `zstd` displays a short help page when command line is an error. + Use `-q` to turn it off. `zstd` compresses or decompresses each _file_ according to the selected operation mode. If no _files_ are given or _file_ is `-`, `zstd` reads from standard input and writes the processed data to standard output. -`zstd` will refuse to (display an error and skip the _file_) to write -compressed data to standard output if it is a terminal. -Similarly, `zstd` will refuse to read compressed data from standard input if it -is a terminal. +`zstd` will refuse to write compressed data to standard output +if it is a terminal : it will display an error message and skip the _file_. +Similarly, `zstd` will refuse to read compressed data from standard input +if it is a terminal. Unless `--stdout` or `-o` is specified, _files_ are written to a new file whose name is derived from the source _file_ name: @@ -162,8 +168,8 @@ Typical gains range from 10% (at 64KB) to x5 better (at <1KB). compressed frame header, and an ID < 65536 will only need 2 bytes. This compares favorably to 4 bytes default. However, it's up to the dictionary manager to not assign twice the same ID to - 2 different dictionaries. -* `-s#`: + 2 different dictionaries. +* `-s#`: dictionary selectivity level (default: 9) the smaller the value, the denser the dictionary, improving its efficiency but reducing its possible maximum size. @@ -194,7 +200,7 @@ Typical gains range from 10% (at 64KB) to x5 better (at <1KB). Once it completes, use the value of _d_ it selects with a higher _steps_ (in the range [256, 1024]). - `zstd --train --optimize-cover FILEs`
+ `zstd --train --optimize-cover FILEs`
`zstd --train --optimize-cover=d=d,steps=512 FILEs` @@ -226,7 +232,7 @@ The list of available _options_: - `strategy`=_strat_, `strat`=_strat_: Specify a strategy used by a match finder. - There are 8 strategies numbered from 0 to 7, from faster to stronger: + There are 8 strategies numbered from 0 to 7, from faster to stronger: 0=ZSTD\_fast, 1=ZSTD\_dfast, 2=ZSTD\_greedy, 3=ZSTD\_lazy, 4=ZSTD\_lazy2, 5=ZSTD\_btlazy2, 6=ZSTD\_btopt, 7=ZSTD\_btopt2. diff --git a/programs/zstdcli.c b/programs/zstdcli.c index ae49da7b1..50ff21d53 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -167,7 +167,7 @@ static int usage_advanced(const char* programName) static int badusage(const char* programName) { DISPLAYLEVEL(1, "Incorrect parameters\n"); - if (displayLevel >= 1) usage(programName); + if (displayLevel >= 2) usage(programName); return 1; } From 9a38dfa7948c8ea80b07a2f3af152d02b481c4b9 Mon Sep 17 00:00:00 2001 From: Sean Purcell Date: Fri, 17 Mar 2017 12:32:18 -0700 Subject: [PATCH 58/60] Only run IS_CONSOLE tests with a TTY --- tests/playTests.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index c584fe560..38a66d327 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -20,6 +20,12 @@ roundTripTest() { $DIFF -q tmp1 tmp2 } +isTerminal=false +if [ -t 0 ] && [ -t 1 ] +then + isTerminal=true +fi + isWindows=false ECHO="echo" INTOVOID="/dev/null" @@ -27,6 +33,7 @@ case "$OS" in Windows*) isWindows=true ECHO="echo -e" + INTOVOID="NUL" ;; esac @@ -72,10 +79,12 @@ cp tmp tmp2 $ZSTD tmp2 -fo && die "-o must be followed by filename " $ECHO "test : implied stdout when input is stdin" $ECHO bob | $ZSTD | $ZSTD -d +if [ "$isTerminal" = true ]; then $ECHO "test : compressed data to terminal" $ECHO bob | $ZSTD && die "should have refused : compressed data to terminal" $ECHO "test : compressed data from terminal (a hang here is a test fail, zstd is wrongly waiting on data from terminal)" $ZSTD -d > $INTOVOID && die "should have refused : compressed data from terminal" +fi $ECHO "test : null-length file roundtrip" $ECHO -n '' | $ZSTD - --stdout | $ZSTD -d --stdout $ECHO "test : decompress file with wrong suffix (must fail)" @@ -244,7 +253,7 @@ $ZSTD -f tmp -D tmpDict1 --no-dictID $ZSTD -d tmp.zst -D tmpDict -fo result $DIFF $TESTFILE result $ECHO "- Compress with wrong argument order (must fail)" -$ZSTD tmp -Df tmpDict1 -c > /dev/null && die "-D must be followed by dictionary name " +$ZSTD tmp -Df tmpDict1 -c > $INTOVOID && die "-D must be followed by dictionary name " $ECHO "- Compress multiple files with dictionary" rm -rf dirTestDict mkdir dirTestDict From e5c4f0403397e32a63baadd701816d5720c0d2a1 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 17 Mar 2017 14:25:57 -0700 Subject: [PATCH 59/60] updated compression graph --- README.md | 11 ++++++++--- doc/images/Cspeed4.png | Bin 35361 -> 71276 bytes doc/images/Dspeed4.png | Bin 8984 -> 29425 bytes 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6de5a1079..f1d8eda1b 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ you can consult a list of known ports on [Zstandard homepage](http://www.zstd.ne As a reference, several fast compression algorithms were tested and compared on a server running Linux Mint Debian Edition (`Linux version 4.8.0-1-amd64`), with a Core i7-6700K CPU @ 4.0GHz, -using [lzbench v1.6], an open-source in-memory benchmark by @inikep +using [lzbench], an open-source in-memory benchmark by @inikep compiled with GCC 6.3.0, on the [Silesia compression corpus]. -[lzbench v1.6]: https://github.com/inikep/lzbench +[lzbench]: https://github.com/inikep/lzbench [Silesia compression corpus]: http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia | Compressor name | Ratio | Compression| Decompress.| @@ -38,7 +38,12 @@ on the [Silesia compression corpus]. Zstd can also offer stronger compression ratios at the cost of compression speed. Speed vs Compression trade-off is configurable by small increments. Decompression speed is preserved and remains roughly the same at all settings, a property shared by most LZ compression algorithms, such as [zlib] or lzma. -The following tests were run on a Core i7-3930K CPU @ 4.5GHz, using [lzbench], an open-source in-memory benchmark by @inikep compiled with GCC 5.2.1, on the [Silesia compression corpus]. +The following tests were run +on a server running Linux Mint Debian Edition (`Linux version 4.8.0-1-amd64`) +with a Core i7-6700K CPU @ 4.0GHz, +using [lzbench], an open-source in-memory benchmark by @inikep +compiled with GCC 6.3.0, +on the [Silesia compression corpus]. Compression Speed vs Ratio | Decompression Speed ---------------------------|-------------------- diff --git a/doc/images/Cspeed4.png b/doc/images/Cspeed4.png index f0ca0ffba9c4413cc2a8a19dd5768e03e236c095..318204c00e96311529ed76351481db81d8212770 100644 GIT binary patch literal 71276 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�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 Sv^=p6ess^_w2Cxs!~YMALH64K literal 35361 zcmc$`c{r5oA3v`8bXp{*#ZtmK2}LME2$dwVW*=M1J_d=g&vaS{NyrG<_hqaz#9%^_ zJ=*Q4PJkR~y_j`H0U+>p@eAQJI7!IC1NJmG< zpm^)X9Xh)IxzW+>PTRj5{7uRi%Lwpe*TXvs*XgnvPmO^;{Lk{5$~8K=+(7yj)4kx& z2OMtcKcu5OTuJ+HSG9er8TiW+PV#zAn)Vh>E~bv=bPlH0c20tJ=1zZI61*UI@dEnb z!S{4@zCnsNu4%a%lU4&=v=*aAC`f9KbpNCouhi;lW0y=fF3Qf~BZt3lp#*Uuld*Tq z_gFJ&;kli+Z_EEGnmSRBVG@6y^nVojoTt3td*Gj3wsKF;KH0nb5Gu(0pF?@CK9w~u zCrs3#Ir-WHb+x_5DVG-=rUKP^)7{g~_lDWGqp3}tyTD56us1tsulk6X0Hyu;DybPu z`|DWpD1`P?-NQ+M_A^zwn3?vojl+nZ_R}UXi}v<(ScU)lmybhTU0oMWM;^$aqk}!l z+WQwmPEM}3x0jur9Z`a_>n&beTl4kx%{gKIX~+>3mm8j=)aa+Arlw|TX*v6_r_9S^ z52qSsl4&%s%c6o0J-91T|50gasq+$JIpU6ykx_tq8QteI;C=5|R0ruWR>TceX1YHJ zj`M`8PZf!dkGkEx`=0ZvH~VLwBPy8#M*NhSp^R$e{+c=LGD8MktN_@t54OF<&WQSs z*4B_y>W2>>cHaf{t(@l>P-qI^OD!_Ao2hp zb>VQhHx$a3dgncH5=IyyL9pgib&A%N1f z_Pko|wbax*)yqQi)%$C0x5(I{Hgm}TSW#ieJ?@g!aO-0j#jb-N1T9zY{<9ZF;av|1YtRfljRh=cWFVILlafd$M_H=|%mX z3uS#Dm)VoLq`j}ncV>AeN_W-UiFoSGI<-s|gcpYnPB%#^3eRy}7z#YkdP`Ua?{Q{@ z3CdOJ=qJmPZCaz&qm#_3I97Zo6Usf&T62bfwdSM=#zT3^yen(SKYYaC``+o5u3cBn zz+rzK-1nSpuP0b@_)-Y`;^oVu{G`;260yoU5rendI_YGWlm$h;_*79QU=}k3Mpyfp zAb93vbpn|+xN?5}f#uTF4L#D%7qcG0N6rx45F4>>G|!(u@gA$Sx*sgcJhuwJZ^8{awhkK*HDz^P6n9y zl7jl$r!z@|M~D%Lhr^GmwUWQx7glRP{8`rbHBTNXNKTlMzt7k-b^4sd-XRwOT$ou> z!&9vPWO+Bgz^Xbdr#B%06LVb;oB9E1?k|NHrSM!N{upc~=FfSaufHd&NoQpQ0?h-Z zm*W+ViZ!{eYt7M|D#->4j!z?G9K>bvI+y0T6kmQqcb`$HZw!=b72qzyIURAVDTfZ5 zcFUCh(fLni_DuDw^TP)4tFvz^XHVn9>gPmjzBnE()%#W^L2Ai4+pR#io!EcI(L z&Q?z4z8wsh{(DteY%Q-Q)5#x)9HlVR^STuc^B$r@zofaKzk>S^TtL?BylZM0vfyv( z{_arY7{nPxrkdjdA(*n95~qh!*Bdwuzklzbh~(`m8}%t7ch0(eMX|r?#|Dd1o>jX( zQBoLxGB@$o#a9@4hx~m~F^NsLxbcKZI8sPaNaf|4h`;ZO<+%S)VEvUdAusCt6(baa z5C^*E7I-7Ca3P;{)+(C|G??&FgiEuYJ{A%~hBK{n2~4fUjZ8piD=)5nn1iy?VI+&0 zA6=&8BEl8E`|tbbS)kNhV6(sI+u4p0&II0Im2ex~@4lT%-DW<$Vj%1 zop`umB=Sw!5l`)B@2?Jy>E-Gl znuHd76sMqTIjv=dP3rNBX1sej^-iBat<0B>r^pqIME7JrGyJM$200ogL|iNFd-Hgx zx-*)jGms|e^|Z;NVpzW54ZcNSYI$NL@(Ok(3{^ulIYjs8zV#Q-h-DA;9tvK$oZb1r z!2+6Vr@po{_)g?}%9%)1snoZo+^0?+nEO5Ru8~cr7Em0osBdmxxe$VPo+0eLE?63K zbaH?qgLZtX&5e9rRx{N&9Fn4`e8--TH5@t3EoSbTq=;}Z))$#(#T18L;=V7gU+}=~ z6VG{}*{JSsh3wwi{w>aeTyyvHs#Zc9t9hHm72mUnDB16QM3C>;{b$PHoW$P#0n`9j zKhn)1wRQwNVZrN-ml)uZ9*$&?7C{u~5 zeJS+HxxFW41%>8%j3U^tE?+DrUCDIUTLXt|yYFim4?ZV+Wp(+>N}k5o1A=vG(~0&t zSKcJzOU?;iQHw5->qCK;WQ3_kZZ)MNvNdI(LIA;<|u#>NVMve&Z@;SrFpdap<-;>@9JRymW1r~i7BVZhN z&tC*btBAM*ZiDz7PPd8=sxN*0ZlcQT%DgAN_x;KC3E67%m~(WPQ^5ADy8bEStUMug zR{7;J>?2jyy#*VbAT4!DS=FTTf|Q_P$%0%|T<45AWg?_f|Ni1AElRI+5)%W}Z=Y&B zDT}}&TLV9L=HEIPN$QHWUh;g*KpvUk4`aU@Ts`UE7>Ey$zSZrE&fViAb&uSg%$6_{ zG?p@~Bzw)0u?b;DP7L0CcW$jGpdULY`jO*_tSip9|5@${PK)4(AY*G=`vRV*n-nfs zp8_#q!N;aa-0dw7b!=o#yv=PwY|PTq)*hascv$S)vXIeU5>HuS&fBjpuX}pnqg*7P z38=E;Tz%j<{b?cDWpTH=WiqRtA*`flK_!N;Bw4y;@gf1b8&5#2vw+_UJC$$Ind|EK z-ZD60&~xmG^|$JAJ+kuodK7wi@8lw2eX>NsjI zVt)8S&Hcl5{*&G{Uk(|>NH~!fDCP*>&IGtE4o!5ZR23@#_C zyQDvCb>hC^Qe5%i0SjiM@)-YI+%0PLe!AvZfE?bk&nzx3@+u^K9EPbRA7GVoz}zn% zWF=bIUZnm>P?It{EU7}7Xf5UTLVWz#86T*4yc<>#{hVDeMJkY?pmJ!QtLcg;n=Ou^ zn^{2W{o!X`ntH0ar%e|}4FVc0mjdVH$oGu+A09nlX=mPa)=P-IU!WN6eSyL|98(FdY2RiSSSDUQe!eyUPWc#Mp)$zLPY_MuD)&0}TJ6It~Zp~k!(nOy8 z1;7gPQ8m6*LtB|5`p6j-u^6hPGX4>%h9@wwGf?VS0@VuTwL9rM3!g|t>8F-!gP`?jXmiCqUZ+_uU4t{n8$Q=~J7rfZ13h<}i& zj)@=g(MR8FRvxNe@Wu&U-@Ti&j z8eqZJ)hy5Ul>51gj(h&t{luCOWcz4t+*~^SLk(DG#cuU|#aT?u>S{O8H;77!0&;Ci ziQxZ2!%g1xKO9xMgR$)+JcU(h_hVngp!pua`3IzH6`0R!t$9~&yrulJ)X-Z7z8Lf8 z5MNB_E!^(#`1Zx6g&b1}(J{y#Mw8Cc|3TGjPUP&fKX~3Tw|vKoJd*eXA<21J=JIEI zJu~-nGt_$MmK(p8SPea4?QymsWS%EhbrhoUw+(lT&S}_*c<>u{i*b;RA1W;qO7Ts; zv2;o+nI}mo&eYSqVjpf^Re9qqNz-f=b`p5sjQ#aoU~kC2&0|%n3l7an zDtH7^_z+rjhgFFwd$g9%C>PZ#v+es>`56M(iQk@9iRqYXG_TdRfaKr5^nbcdWY*qG z+t0bp+gfOuqk>_dpidl)|JdQ*Ii*AvVl@SfoG&0HJ^i5f=@#TW{0?rMO5OS)VNHqC zgUQsdF*k)*`N9d)>=~_l`x$;e#XGp9)xIi&46!gih^EM5#b?PKY``W1(`2+d+l(G5 z(UER<8F)V}(|!3unMHM~3fo~r8Bd!$geB(G4mV#;^m2A2&%hFlF_JNL?F%c~7kAq9 zVDg9go@2O1kLJ7+6Go;_n0L69RHC%#!|Aj#dSeVnulrO*fRyQzogO)F-5+J}qtBnE z)Cx_!;y5)PW-7-8BAmju4XTPFuT-pBE}AI$5u}R7a^kM-;2K|5J?eroj4`&7a0|Xn z7(iCP8R+xYzPXP9iB=>JkJFj3vpal3uNCZ&@HvQ7pfNp^E#P<0Wo)gE?6ip2@MJ5Mh8}u0$i5vMBO{Uh+K#bIAP9bH$2E&0iDT2vHe)*m z?}#fTY5Sh%$vZ~lWEBs@z}ouZK=3ZM76nyVl5YUly4`NlBwEX)3UkK3xPqizhAmUb zVBO&eyi>gHIT6#_h`kb#w!@v_X}}`OsI%3I%|tO*7-9gy+WU0}KREe@TtDgA6vwZa zqLi#q@b>obV#x!p7cY13usM5Ljrky!Ar=dF#}9~Phy|XKkUru4@$n8T*07FYjWV9p zVq`(L&ChjY`@|jDDTing$8MV(e=BzQ#sBdGAX5ns@rj_jE}rzhxr4K$X;`;nAHi>K z3rM18NR(&GRF`RmaL3Sau~Y9(-Bj4&YQ-4xSZ+5knEg>UW9d&Qtpl35Mo&NQV4CMz z%OoXm1Mi2FQ}`~U`=yv07FkkuuubchGG|M~Vy+r6V?|=yx9*#aD^vNj$VfO7!h{AP zWB~PGhrLL5A&TVBE%!y|ScKYXg6MXe*cQ|p0y1L9BrDds@}D{%HIf?nFPC0?>gf@Z&khH|Va;NzAJsIZ`a;#`2+41UPZU_IHT2Zk>#I#lB7d|E_LN%n zpU{)p!O$Q{(ujxfk!EP1EoEt(1(WHygPXOdc)3r)*Ft+h+HTI9-C@z%Z^>w>IlTMy za7VvV$t5r9P@viiRRvFha+@8tU;7ogi(e$t1Q+av`;y+k6?yn&3~tGs_38B;Y`Rtr z*Xf%nDs>Z>^0%cnUDH#3J75YvUeaUUJX?2*{-k3Gt)6Eyc6tx)7D2Dm6XS!ntUh@= zyabbS%YMYBU!?0G4@j?I4R4JhEq}^p{gD>!+jaNvL3U~0)?6UkLhFYJm?r7 zABU7Crljy@Hnf6@<&UI!GI@z$D9EBCFdLfnfhU&ZR{%$He-j=xE#bti8E*@r&K7BM zM$1aWyVE<;8@i6^%eoGg@9mKY(&i6!7T~|VRp!LoB~k7!i-&WvvVK=F?(-}I_uYoM zN$z^UuUoapyQLY|RvXpsK$%mj3j<`R-4ox$!42<+I-_4SHmYXGnZ})Avv?9hnMeOr z4*i0-p;G{1-t%>TXT=mj3qRG3$`4LwsJ;e;aY5~Hi1;zd?!XvM{@eqp$9@IfxM8qi zO-||c^I?$+Ak)AHK1Mn0X;WA*s^fd5&4o0$Yc10Z|H-5E)R@9A; zo*hs7nl)5a6RL?3W=Q|?Yb^y*H{B!F66q^ToU<}Pq2X&>Mz`a? zW`%yYX)BsPmw7MKyz2tnq<{GM(BKyP$ee)%)fS^-tdtTxK$f6I~}o33Q{;V583%poYek%P^!F{>^;D807_3DR3~=ZD_jY8f}P zI=PP09BUB3$NL5K)TL6;pws$mp#n;&p3-HVBm`JyxSx4aUApdGU6%7EY{|V>w};<0 z9cwk^rqV0(V-f6K6UJ946$-R4TW|Y}c7;JwufuOw@K`T93kjQqn1`S-DyHhISVf}B zZ+RWy_l+Pn;VBrFrUw!Qb@p@0vDSyCZ~_4=twrXdWT{>ZhajXXWx=ERWmLFLu)p!^G`AMJeZL+1 z6>EFR%hA@FdsBE#epvN)A5&Q~F25JSOM(NE{#OG`Y84z2W)i(9B89hDrYNIpmHWGd zg@F(D#EOQhvSC3N1mML^!qK_6iQZ840+Lq5_q;P_y3Mxm z+k>iF1RsR^(dzYUb-5K{7p23N2OB}|uWnKWRpGtvFIY>m5d)oIOh;O%$*U4o=il~s^U#ehEeaA zOgWZRx0wJ>4U^>fzg=P5qqDP0y--h+m*Yr#NDS<s)gXn#$g0KwS}pgEJ0g)xBYEpHx7VP6GHzc-WA z05#Ztn!O-Ux3CZLG)iQN$Le?Y?u>gW1z+&IDBmK!4yzr?06=EpZC4}?r zg!n!SkTZVIFA~6KJHvp_nkxg0Ucg{1g@BLKfU5~NjT+p(iP$cuoYACnvneS{f|D|2 z-}d}_{YMOU)5yeM6OwYIg0#r;m%3~YeuO!F%$nmICk;rb+7;LGYh&Dq_qL~{R&R|a zkks8L$WZ>U63(~p`F>vMImOx1m zoN3UlF<4$X$^wi|Xy!;J?TA21pUKxe*19)~(+W%KTJ^Uq6{m)zR; zmxlRcK6r9X(gBUuRj?qsb}9yKZ99iAqen?EWl&=RtcF^yk&0O*>2oa@9?&-Hf? z=B#HiTyn&1#7+bA0&q@og|ZyN{N&ngV9pIr=__MW1`3VF;nu^5P=c*>bpR&x*W7cG zZ2{C|gx7~-B6_?gL%^eE5)7Km)7$bi(Pb2+gk0-Fww7%%L*u5Gi`@WV5CPI(C`I&2 zwnVVk#T2;Dr}&dH*7`k@;*{C8zmR4}s(oLyl-mG7sBg&$z*%6(W;S=qYw+o=AP{yj zkcJD6HHe#7w~4~cwxcX%SVEc1LOY<60JY)@rzSb78c632m5U0{ss>QH`h;29^&aRy z|2CO2%pX8Z4rtkK20ZKwfG<i^F-nJ9l&B3+pw)})`@i_|9IY3o-Iy(|-TTooOF~GQe{uorD1*5|R z(0c*r$%>cg_DhK#uWda{pDMEbFmm4_-Fo_$7af}U?19@leS80-PL8qfn5)-#Mvmt3 za(d9u*>>jVNAMnQXIf*GlmqRg7wgMzd-S~vGUWYp=-V?|kXHMTiC4DjjWx{HZ)HL6+tCTo!z*T)`13SQCz#?Cok%)}3QTyu6c-5&nn zXpn1=(_hiia=Nok_qrkg3p2jDFj~XNVR-vOzG!%6^vnq~WZ?JrY-htu;6?)20K|t?@n(vw|?15 z4}HKM1FGEqvV#OQo@HY3CA(CJSN7eDH`W`5+m5IL6rsi(OP9bW0VRi+A*2+DzTjuv z=#u=JphhBbF4P}gX_jR3Z02`1b5AA?mbuL6n0Ms<_4|@-VlaUA0Wh=-?$XE;DFIpo zwzq1$94$d`2dxRg);xgKO5JDk`Lb*JWHiM0&>Dq5zq5_c7T9iE2iqQA#cm#r>1~=` zEW&#LQoAR91#l#bno02I#ctcP?;WYcQY}nlvPWB?2gk~?xRCLA*Jk<&BTz3TBlkT( zW8~Tzo`B}(FGd)zM(d*8^P+Ja;b4t+fD0>9n|jLDp*61|&a{1jeljEwX+)l-f1yQU z`)sDGgr+$hZfj z;ptM>i&RHaohmJPFQ?}ZzmU`cF8Yh}x4pFvd*}ECxjJ%NCkq%tn|sGG9LfzDs{%!$ zJTBzBc0UH>X~=-_M5aW-OQ!Ab1xhOZV#m8{gZ7tus8g2HMkS&En8cF^wZiv5`dIs$TDGmU-2=^C>?VV2^~{-R3Ai_p{Ruv>M|)W{i+kDfpfhpR$f@d`Kh* zUV_a|(;MJw@xw2&H-R-evZm3hi06eQ4O$VE{v*x?Ou36uy=T1mWrK!R(|_jRY@#t} zil?ia1z0$eqE=^kMIAo$o@f)D+P!OiEB4?}=xGv(f!^O%7J;BKG5_*+Hr|+rF+Fae z5J6uvm(oqOpp;eI%j?s?rzD|1)h*<$VQuzwi)F28HT9<)11 zf0Z2+H?faIxJFDQ;8XU?2emS(KU!s13Qbu z-h17Q)61YICgl5KD;ysn{<+4fpQAqGCUEKCR$u=yh8NIDrP3PT#~*|2>jdI8=<8jW zH)X(sn;eM0`IzR%X*@>!5(YpMH>ZO#DAD@(6}Ow-#5U*WJxw9gkcbff59(lH;AAUU@57+Hvb`OPKeQ~mN&0f25b= zY+wR$kXO195CPKmuL1I?wQTO3XYwXoQGXqGqb*fp{m4gggbyEX*xOGRJ5T5cAqE`P z)SAA3cj){h@$;tcLF)pkht~zr*i`?L&90N(BjMvOPC8$woy~EVj=cCGfx0ykq^-Hx z)E`VXnEo$ki|tKPCh`jPfJWeCoGR9}y?i5_TAY>Rv1wZmr+0dcGQbEyt@hFl6gC^g z#m2_QoI~fg6fZTc(ID*t0!$Y0ec@u-Utv#mYKB>U47J8P&8N&2xC_aO9kx%n#Ct8uuDi6J-F*hD~V{(_D97OZxAu{=R0N6x&{WM3lWof*Oq~RX!q2`oU3>#}5|tjf z2;6V5Sr~8E$>K14mXlF8tWwVSAg*Ckykf+7|CkHl+thT=N0u#qtuo=@lm-O$!Jppy zV?-#6Kx=Pr120zn&&`*|0=h+cZ9yQq%Z0{c7ldxfrVP^9i<#9AZ}vK%RS=YOx^f-y z@&&tz%Ka_Ozk|3eN?iT3Eg2hxm=l)l{r8MJ=?(6Hz%E~en46A1u-1F#v zn+s_FL^VVq^$7r`RXq3kS5?Gc)qyR>;dHZ&NnuWfszWT5pQ=*|UR~RW5O8xHV%{n1 z4Ur4jR!<}7y?c+R8?W!(Gz;UjS_q13OPHnOmkr$&x@mAY&u`+E{C4^;|9w+%npm%8 z^5cKy;uhFmy$V89frIYB^AOivevWB&Q)pZ41{@s?{p_J z1?ZP(GPjn5U|QS?jm)RTJhQqEfjS;GL;X5OSaiYBP4HSp#3~v7^I2~n{kKQlTi36{ zLi%!EsyDhkL%)J2RF8eD6t;O zq(LYP754&U3ZTM$y0ucGTA8^vfN+u?4mB|~HC+1m?6LK8Y-_Z(6y$p&>t$J2^vx2# zHmS2flz?5_kdW#4;h?*QHuPxV-dNm2T5p>$$;u!HP5kMm>w)S6fK0CYOb>Q=D*Ul^ z>sIXPn;W`0;00(y!H(wUBZALoF34b?9a=Zgw5Zm#Yt-tkmbo|KmQ;$sPnVFo3$qOh z8?$|a45_6lDb)m@BkzxHTq)fO@X#^AwA5v2i zQy1~(&FJXp7wM;;V%PO`L;+HG7i9$LA^lK#@p0=?>3vX~R5sWo_O{slwe|XCQ0{4_ z^PRX&qX&HsT7?BdO)`Busv~Qqw=Z}8WK8C|yhA)Asz3>syIJ@E(;Nybzcq}*;b0$| zpbrFG%{GEIESL;<63!A(vgg}gq}YA^70xlnL}XG<2z2mRi|^T51r9poSOmJENL*|~ zws9$u8Ew^tm!Z40al_^mX~0SX{xrG&(hrc60Ny_$29=M2&utbzN$+r|6C50>G7+Me zFL$=LSFxN*_-lO+&rM;}0(+-CrzYre2H$AvM9@X3v?zMlYPTs}L?8F+*j;;L-Fd4b z$RHaxWE&|$P%8aM%7i$Bm{O*-_}`oBw5iMrts7ceUIv3^ht9{Z?{d8IGN{YuMK6G= zB@4k>4hcHjYNvW*qux7i-f@zHV+#C&yf*aseCW`jPWQ4p*1a2nF(}Dvj@&EV^tD?+g zqOi-IyamFxb|)Vs_8#3j-?FUs_I6RA{$*y?uTv@%irw7O$CzrKDH)Iv73&~4+?c8* z_vu?W?M>z79nh7Y)6c9=DR}gFJ>Vtjs4denXx`AobmO>$(-0xb+o;7$A1I{gH#MqB zVq*})1AFQ=HWVUGs$c$t6G-TQ;3fyHkF_HT_-rUhL*pkQAUx$GkAd06a8d7Uk(-;2 z7;kM7_10){j$W~u#(ct5Itd>jmqAA zyLq7hveBSM1L`FATy<$f%$?Scy497Rby}ZVS8uKUUm7yv!g{d`6hE+2u~5?!o3{Bc zUDWo}8f1sM*BSM+DwmaL11`|p;b|%z*B7>IV<}7Y;>G6v{{Bc_P(L1de|jTgftrpi z{ghVn${DDDHXi?f4z3?JK02yr^y%|wb=LoF`VM$d0w-cERx=5xw88;Hdg!;y7Ut(O z4GQhRoa6aDRU2;8fT9T_ANOa5Dg%P0>&=DY_DcrDBU|zR9ZpAAmqni5e8Zd~_(+a^ z1mM;eiua0Y6CeEaF#t9}MAxGOMnThCfKP&gi3ghwiU|c(HM>)+Ci(Z6?{Okx-R?~NE38C~FTRb}O;=CK?4GSCq$a10fMn8FM}!*6uU1=o?)T`=LQQ&>Ff zz)1<*ZUpXfXL(US19W z&_Dm|l1YAcZ^PdIpxKt>IS8owj(ar=GbrPFx2~Bts$0@lP*C{siV#0FWtXM=d;??N zNoYavikI5VJ!e6KcEMDmz*7bds1_0Se9N7tAOM~lXMyw%>jSbJ$N~|}7PIc!gxe>B z$)G9$_3Cjnpsq3V9mle7$+g&RYVB!Ae;l-+;+dFHhZq?Rc71DO-UQlSK;Q_1Z=*kf z*jq$C2e}yo@7~0KATa=A=unS?;#dp#{ST4Q4@uK1E?wPMq}fRy)hBknPoR z^3j7&yomaQnwr9-LQWUC;ayhG3YPPivC--v?S8f9Z1*4G~K2fuQ;K$61eHFMK(vBud;~nrLpP7UpV(cJ`=fthjz#9B5Z z2T&4$timKi#kf0z5pz9*TfRX>hJvkP~2cb$lwWD~<(;g_MQHe40gQ z-#ezxyYYh3Ei_yPTHVGNKxJGIeJEsW<*{&!ZSR)D19quBrLWI8#-*UW$vw1FD zdY8A4kCde33h6N%Utmz^wG8K^I|In3s|QkKuJjj4l{kNwlBHCBPRrq-dxX+_&@uaU zP>BWon2Wwl;4@LHWu0R(&&R2fL7fGA*5Do%CMc5)P%86h*XbB`6+gmw%*w#1RDvlO zRuV{EmvC(^Lw|!`inE0uvv(n6+QukOD-re_*dUwIwTWO4fq;K59u!!g0DY3P>l{{z zt!~Q)fuJkb*i0q)EjgG2FiZCC|=|=#q;HL~{v%X?-?`*k~OUKt`>{d+A!-&XdfIMWnM=AY0 zOzY8qA=DGkT)Uxw(0(N*pK*^VF`#xUGy^unA3UgQBZh}2t2C#7RBF9hwUxD}vZ@xL zaU0>f#jhpVO50lAI^pDhEAacWj0JMmUt0%OhmHIzuKRBUL%*ab>N4n(R;;^Xx&{!6 zYz2*~Uaay20#5)9yd~rsPb&bnpw2W^PXOC4DJ4}(7kQMIRm6l<{cDQO@ZsAV_nU1M zBri|o#=PX2JC3d$ObL+O0)?YEoe4hI*jW~TsvT&j97-3NxF9VpZ5sFLU@@va&Mb!D z;g97Kg4A@~;)fLI<=BN5Rhn-g1d6rL>Y8Na%B=f^1y5-_;2JB`Gu08q)MH!Oy9}zL ztlT+MsJmOrvrrUX(zn9$_mzv1QKv$VzkP}L?#2pH6P~{i+#M|pn!H|Ext~WJOAoN%j@6;g0^jQ-fY!V#@qO8p>w%q=LYV#jKq4{lkL}B zaVGfcLwLo!kotS^E9`>)>vmR~XoT-Kxe$)gkp1uY@*w5{bxi@CM~c|9mzU;64}>BX z`Wiqpz3!BAn+IThD35u|TS(kq@K1y@DS@+1N4j#%0_1J{!_&WU9FJ{t?~YsKToxDU zlQPzGeIv^U#w=?rf)zpw;@MFfYH;w{IH@p-=SSrvpibx{WB}vzy4TCeH|nw2;54oXeNfoXm=vuI^;0O!q-M`EYNP zeBqceh;yhS+rs9Eol>(4`tfSIMt^qut2tU)?OKGB&qRZvWGKY;QdfvpIM4mL@W#1V zYo>9?N3w-Vjm3(9|}yZVAJ`d8++pUqbbGYOycrf}8u59DolqSI!C^!V<<=iFXiuhlRul-PRHXe(dz z7}eFvfjW=fe|cZwWNbe%44qp!b0OXrcIxaB-kWsGf;MYV=*WD_S1fpnPfR!}G=_m5 z^e)Vj$IoPTE!}1N!uw9ST>ALRgHE)thoD}3?Z3}HL(28VO&}0|&(ZUXXj7Ky6^W@o zf!J`s`AGdJsGPas;^H!AGS{oz(BlCF0QDMvD)87l4K62^d1Hm`Kj!O%WGZ&>Mk;o& z$e+cp8y^x`?n*S>wXju~HMhS_t_hJW*ue>gV7~CZgnDlp_E0)IA)2@R*wBUadX5Fwhko6 zBc`ufuLtq>pa5B&Miki0b*zW!hl`gVYMYc4Y|eH5Ou{}2rr7`xfjN9@1g*5o*80?$ z6y#;M0i8~7_pjKk8B!X1G%zsm^5x-i=Gt}W-_ZnmEubxyqGq|O(*LQ3@rV1pBgWmE zoL$jc_dF;nRC?J5(g-0%CIF5^l9@zsJ{Aes;-4$*k|&7sks$T6rD7^?xjNU)n>7}^-XYWmbm zTJvXfw)0;B1eE(B!=Xr^8W9tb;uI62&7S_dhwCKTQ+$X6D22%Op!x!};1<>2UWfnV zBj_(T8m$W3s?wTM|6T?#ykOV_C>~4Qoh>a#zNTP`S1MTd?Af!0n6p}d@?-^iw_fP@ z+SK`NF97Q=^8(K!ysMb~YMm7w&B1@y^n~8JoLtw|_UjbLW84CD$%7|Zt7x6)U^b;-IOQVIyARD}6>1ohgq7`On5ztCM z8335ZHuBf~YtkqWaK!Byy1<2fhUCMAy~*o*R#@f1(Drnvy1W+ic79tyAcXj3vBC6= zsE^ABOxNwR1+=gGfj((oA)ljPYze6Jm~kz4t+zc8kXZ#B*z=9h^7Tbl=|$2;;O3e$ zqZG1f17KV_zZk{WDachYC*UFkC!p_PDgi{sQ>xvhK2DA345zRzQwb2KPQLE=|b(4^%z?{9dvc@!%`~>Mzr({s`@|-|h^GAb_|T>CrVy?SaNijmWMg z*-kSN=X8TANo|#4RG7GKJhUr0(S)+foM}*20|^%*-p{X!XcGXjk;pgxGcP`ofNrt7 z{Ih$a^a0ip6_k3}v_CS^pxCM%YuAp2c5%47yPtm;c}~_1=NnMC?@v^iYmriRp%O|f z8;m2xx9DiJVu=TT_BhZfi3c$0aKoG^vbR;wAr8}59x}LHnu5|Yzv|{CmK-jWn(10E{_xT1k-Zbt&$;BQ#Vx&;G zDy>g*aSpjO6|P&!bt{)A1e4HW#D5O*(L+`5)(r9fH?L-RblO#Z@M?wgs}?`#G&2LW zA7wFxzDuArB0r7QWE<$z0BOfXsQCbD;fM>d!TaY2g2tcp<;$$h5s%uVm5;P2!|?-0 z&d=1o)=HSgp~Bt{dRvDZn16iTk6jtw+Y~J^p)2;6w8`37-cjJAzM|tmYG)!%=>l`k zDc!UwyTua3x9P=zB;6%5`HbQIzNMj@a4&W!^B82WM&KT1Wd?0b-CMv;dfA2#z%iyh+U z2M9==Exf<@7$rSw{|Ci6V<~=!H(6K>@6=Iy9))l*$rVQRV`mpF{P!AYx+hM{b~WWO zrqPP48-HRKQh_{q4a_$y*DaOva{>Z`@HU4}^MBTbfBx(_gzw*T*k7}>t&Cs|i|w>c zFvJe_N|g7lNDxwQ4AHfIFC@0WDz> z&RVeZ{6Oka5j)%(Z(Vryza_#)A;*p#1ECcJACPg2uCqAP9ily)1&hrbK}N!81CRg` zD+_uxV<3Vg)Xo2%>eZ2lXa{k$4_1&0nnc=tSNC&+Z~{3yUgWX3SSmdO7?d#-c4~wL zxRXsfqj~7(>(&a7el}=PjH~n?RFnYqjgGy2wstmXmTk7jAg66I?&zcno1kcT4GawM zzpy$228u8J5>Y|DsDr7f2nHaR|L$Ajby~NefpekrgHeX|#-kxw=0)pj6{v zni*(puhsghfesICQc0KBva$i9zo*qs?q^%wXgxLqZmqwc`lXgtPL5FKsj@+?g1ZSZ zq4O0}KslT^%mVP_cxfbhIl27BAIZJmzigO=tLwY`{JC;$UzlF=*Fmi;b6uipN&gsY3Wi%{XDYtyT+xI{G$O% zIO+f3#}>h;ln_rVD9`DJTQ|IHzHsUEzaMKqLozT?os#^Xs3LYuC+<~5Vt_Dcyz}tz zNJ>hAwwPXHEBMvj&BnamV7|m>Q_MUb0F1|7K7CT6Jq5P7oDfzvXHu}nDAskzIpgVg z>rD#i=zxe!NUpg4H=_JS4jqkpPSOCn^UZIwIAX0uB+)=gv1j|@TfSCR*+(`UHzQMp zVVSo;BMn&b7Z;l(v_auZMmt&`DfFhi7Z61~QC;}1y{{FB^^UBO?6+>AEIKC9o)|@? z^NhD+(o`YprTr+@k?7Z~cR9gDGmrqkB_Qz*w3P@O)2+b(^{pq%#|vBUAC{okM6vf+ zo2?Cact6I|VP-r>h3me3sramrOsU6kJ1x}8u)9C3E4$BT>pt}!nOifmSVG3&*>k^` zxfITfAfuJ3pBM4cr6gp1kB6{E;lZz1^l_7p@u?{enpQ>L2@Y?;(o_pda`7@ixY&$N z+US#95@b-s!Ga3Fq=T0wnLN_k0h`U6A5f@n6}6_?i$Ts2oxjMskbV?~ySLi55=#3S z)DGMxu3!b2O@J83Ts!@PxD~_U!?SMd1iC!u0-yx7BNf0mbCkg_(?1ph_I!iFUl3_o z8t9f1t|Sc2TNjG7pwblE3M0PNy6S}6As$rmwJLnW7uGt>GWwJVvtM3I7`)Q*ay1H( zJURyJ|I`+WibnKM^=|FDZ8J-}84eECqBnj;L|5lMXwRap9WDqx1;RbKTnlh<_=BxTeG7P zIl(wgd4eQOGh%8u62+w>`&g$I#hE$QNc3Wq6(3Ia#SrVgDZWiYlmp^1Sv{8*`^hWj z!-x>y#Xp&~R-tS|{&`T)Br@oLl)7Wv)3c5n+*dM5`V&TDaLn6)beUUz0(@UcZka~T z|ICSh!NU0xR9VoDFPEnqkmd~oL`#W`V>&S_ZLL9tE)WT~v0Gv7p)n;broGmyjrK{4 z`uH)QZu}SwR=uW*Ta?b0CaKg&wcsp`v3KT|gOHT!GOF~$N;8N?QyLFxCDkk5YQqZ8 z;!td?thbgz`9~5AtqefI`VoyyJ6zRUjoz2Kadg?@TzqF+hx3#+L z6taFvxwOAvk+Bqs!X%rV@)Ao^w)e{E=WAUqx{s*+)O6fS)P?s-fN)#-h*n5J4U+XZ z`e?c`8~@P<9lLdCk}Q=r(8>be4FU+l&d(N?tSf^G>iy!QiW(P?p{Uh+gXQbW+6p zTc#u<^(9kvj8cXS?DghXKKxGuOmCew!mKx=ND8mSjwTxM!_V6pRVClsH?%jQOq|+| zZF1y9IIW2DE|aMi-NW@MEe(vIW0f$<$jEk)(ud@v3>F2_-W4!LaLoWC6#&j)E)fo9N zt~xA>i<9(+V!S1Pd>ponSvC>g6P%XUD%1S)D8e0l>NyByJWOmh2p&4?k5Z#{E{@<4 zmWy-ysKxWo)}s5!3^=#`IK1@yFe+|wF}n1}FxWO13wxhZp>uGdtl&V+w3 zcdt|9#O$aun|d;ZFKYn8rtK>iF5`E>w4X;@?0?b{#V#t|t2N5NNhiMZfhWnFAR1Y84~8ggCR zVTtBpZgO{3;tLQ}p)<7_wBrZMcIi+WbP+JXgqoTLk*b>lL)X6mjh_!2Ah{|HIga+k zs<0&m!NaE-kj`tfIEB7GN{Ftc@U!&J*Ao8{W9=>i8CP`zyuH0E9FK%&4Xt;AoC~(T zh3pxjWqG1G(Mq~i*3s@#=DyEQy%CE-_>~3O)z;h8v3=cZD#=O|45&Ja;Va$sN|9>= z&+PHY;YCv+I(0547lCX&yRLZigJ7Uz`#gW#qdoSXMi?*$KUjZDk1^Kmtvi+Uq#8c8 zG*pc5rpQ{Je62>nAzG+Ny?q`Ni%-P*Btu0r=IQ*vH>;(+gL0Rgq!p<{r-AyAr`ltG z!kv)Rf4@BlsM5u#6`y6Q?M$!+pQyClM&2~Bk?154@J&e?J zkkCbMHN3aJ7CIVJtsD&P6dqew9t$d@Rj8VyNNUXe2= zg0SYQG=+Ym$)(B4(tNWEuhZHxzh=g_PhIA`{TfoZ&ieVGXuFO724=>~K~xg@h~i_W zq5-ucxDA7>qw@d!vR)OZny4Wn)1Ze9Nzuo+(^XQ#o5pG;22euXMeD2+Nl_4L&w`^P zK|i4S4-4x6LV=!IM#DnswzJpjftEU3P(*OmB4;iYW22Y#|1}2Zv{;(Tmqi%9x?DSN z^bJ1A)YPeUj)Vb6VZ~b?7*1Ifh^(-|9BI^KAJ{LV^em>VR?TAD=@w}!TvamHNxVt= z9>yN-*PRBFUaOcovB0(pAnCnXE=16pD2P!B5Sv2T+wPF-XYcQSL89?5C21iOnuIiU zD!jdiGurMaqqi+Tfe3W1Io@OP?>zv3KID^fy#~O^oq2v)g)1MM#zJALCgzaixSz83 zbRchPyi}5Y!M>@!WAm>!Cd@~2tNI-`8ZU` zItp8N8ikHzPy6}j;(NLvh$9+f>lAW0ou5QFPG7o<|17s`AnT|jdV)VFHD&jvdOG_c zg3q%;)X;#_*mOfsTFSOw(=jgGguLeTDf~8>=tZ0oS&>md$v~#Zm^@L{A&n*~StkIV zhhoKIeJ3>XOJ&H<@jPbex_~tYO)Z4c%2hnGV@kGPycAT6jivyu$7odqHx<)`!WDi9 zuDzq9et*FLHID;Qfz!+i$wf5K7@xaB(|!t)exzib(p<~7{$53xo;0}v%5;bYHg!6Q zr6hdeVy$N}OA=9S4xNc8G!X*FH8`ES{MhJj0Ng;l!0sK0AXtt^UJ28+)HL}kE%SHn z`u-aWp6sj2^2<65#cl|k3k}Z4>(df^EKjs#*F9Ixx{~slecP5JnfL8nuMoO!txgsn za`tAKQM)#c0-qJ6pWH?a^!*YAwM3k2o5+~R{D}4pJVW!{;I~hxEGhX5@2x<$N?61> zX8}mHMY;?*9R@)Q*;&OTRb?1HePo(2Bod;YA;1wsK(9!{=bL#_3U0bFD6Z$!P29)sdl-b?nQ?1+Nb2;LCme<*gU6xHS1kp}YZJIv0Ne z_?465H-t57y>CeNhvt}c5_Ttf83flIU#E_s-)C1uqaKI?UZ4L+88nvCplwKSrGazs z)uc49)gU;@wf@9HPJsUbug`Dbu=?00C~B+7KC9{p8OxhY^=@l#VKF1)45`}2bgQD^ zt{#*kW5E=ByFB|^b#@<20AAz^F|3?yMFE^85d!-wMDp0`MDWelZl|4{uBbKL(eT-}fSKe}?hbowZC=56+d)q0!81h(>D;oz%uYe~Er|O=0SYy@ z$@l!?Tm*xc#F|XYj=#JaUkeITRrV~{vjqwsD))KhY|!%?4vpo~L$ z2yn7=L&w1F&2@^AY0W^cHJ`SR{BJJsHC_tRF;|_RLS{^Cl(UjidVcHhr*_2_&W4rG ztB`L$9lPM@y0V>haZ2VTcy$lF-YMx%uh~AB<{PcHV!_uV5W+|og0c?dmcxDBJMIDz zhE^1PoAY7%sz!MRwi>4m(?BUj|5X;HX0hQMjlB$g;RgU`tCZR$hpyNYD{LDj73Qv_W;oz}Am{ zesb8vofX-JCJ<>-mWv&PSg&%$9_cij`h=|u#`P6&sH?)QhZ1kEh%btNbw3ll(^!6N zJCS2O*curNm}E#aID#W*s-TzfgW58?73itsmnue*u1pd0AAylG-O_UpMLC;JmNOQm zSOy_uq~;`h-L2GrZKe|~wqQaLQ1alImcQ02W|aC;U+~>CxC39r)4&UDaC2-ttngKjczN&j-$lXcjIoI1?P3Yd3W8iXG{jTix~M}&1{D+U0L ze)lwd(50eQOPisRN!$}v)FRqZWVpE9Mr2Rg zU}2nQh#FZE9Qi&$cZHM zu};=f;e0I^h#QC@y+Dzmy4jX{2t^G>3i?LJV%v{d5!*1s9*aM&@!-@)qs;x6JO;V` zSvdl?NFmADX(zWsL;-MnJ#zBB`TQW)YFVoRVF3}5GA0AOj2Bma1bK!boa$hxih|NK zX+8tWz3szlKA-zc^b>EEcW`zH#qq@<0E@;CO;X}JSUsG{=7MTa{hdotb4^zg9kfLe zkrh`Gu=eg`c5w$5J~h^&$NsyUSp{*fO((b+Ak9SCN^)hD&DH4u+vf;K@;Q3TZ*y zodP!hYMvF64ZuY19_64GkIHXmw>U8hNog*3kq0D=*FM@kV6f^TXJ@kXt+}N9%*4+< zfpSzS>M$d!0SH0*IS$CV)6JeN7UaYPvv44K`s?CJ6as(4y~f?MmNm~isHx%y85n^q&YYFVFKKX$+X5{8&$>E-|2vFOY|6e1iCiXP!<6gBRw2OBBHGpVPH@drht4pJ-NB*(=+N`akyIw77 zv_`ypL8Xu--vsOl1lKt#t{^6uOhV`e{T3H%rfN>M5BZMXj@7mnB@7g&xk)VAUJXXT zn=F;s3({ADRH`~r9j!)-jrG%$M?yl^t79&k()s5Pl%OjK3TF)`q3mA>rM8tT3tu%P z0gJDQraF)CUY)QiThFUf?jr|iI(b$g)v-UBM09y*;7>leQ=e=zl?oU=+cmfK=C2$` zQ8cu)5JzNE&ldLt+lvYdGgOIL0o}utTA_ak3p6x>pG;RpYHWyto^31HvEDt#NGm<6 zE6_Iance&%03ucX0P5~tpXRBUOQBAq835$NZ!4b$7Cq8!pyz#Akgn{2L23jOPIx@! zLH5=YIZu#~b_CD`5M&j7Sg@iE0QXeyLBs_3vf?Tv%~ghm&1WPl6QhAV;!$G&yd;Er zM{~DsNf5b3_G?ZrP&k(&U*|t9&U>tUzGQmdtC2BvKKYj}dxlt7R+-#+K!p4d6-a7j zGA0s`m^V*Fn7YjN6HB3hZ0lHwA z@^`W8dhZn7KM{G)8!5UdfX4MV3Hee9oUe|i=nqP5`$t>i2~!WW$}{dot>Ec@R+eJ% zU}pi!LSH>&Z&gyr1G#IAL~fUbp9Z!j`vKI)tp>r8hBgaAE0SW`o}QK4`pxpga$fd_ z5Q4xm>$>Q!Ri87n=T9{>+%SCz&lyExzGi&)4ou}AEJhNvfmJwyX$A2sU(@vUz6`#h}JsA;IopL>8Nl| z_8g=U`J70J72G0j2LwZT4jpxSPVDSRAa20MH)L9Ep0g96dmywyJX=pU7}5SOpK__b zO~~O~WUV|ZaIOB-f5-x(yO%f~h~}_daFUOop7jCXT9_tiChI6m)w)3HiU}zbXY|r< zAWE9nCk9u>-+Qs2*wCPph3J zmmHq_%_f29a@z3zEZm*zG3g>a!1?$qaf(4*^H$i|(Yl4~4@H?QSz>l1r%ps!&U@Wx zJq6qQj|U~%Y}x5rwtm9#ueP1%q=d%Te(o;MPX7#Le;sy9}7wn4#+ zrA#aXM+M}K%E+?BWUJ=5nbiZ>7Yj@iRNKNgl0>If1}fUtxAdegpwx1WpziV|46+xP z=&mUL>=$HRB*UmhFdRCmo()%ikXFVFFT852JnvC)Qk1*vj`jz-6#Zjf z(CFeUF~)0hs(B>Ds$rnT_arWmO#BI~;6JqO0Frc7F8>9SpTtkw+AsNoH9RLkRTCf1 z<|iR#m=z{bx<^N_%1el2zAF7t@o|TM9~>wMl-Gy|@Ayt8{^M04qA{vZ$Dm9psoJY9 z=k=8u(qrFMg}Mg#q=id~u6SuWGuY7rMizTkgA`W-}Z7gcX<*hk^g1td|Nn=AUA;F3`2N!HxnJzRcmLoWL*uo z*)?uFCA`@~CfQb$6y$PpmIb$9VKr4I9Y}DFMfEZtQ#Es9m+TM`VzUk`S(E2v0z=csm4rYtOm&m6&&h_OOWu_xtyF__)rC_Eb2hb2!UmDIBhl?NItXqTG;`1P434>luYBqPr+YLdlcOwBlql3#fmL5}ON1%)1FiyM zg3|6HnouJd`|bw?eD1u!N0z@MbOQeVL%X*wDHU7yaEWvp@UIb*_<79Uy>ZHlIc{p( zi>o3>{~#aC4=Hr4fc@P~&H&>UyFJb)mT{QsV{O@QcFc|$tJbI8omacL69ry%Rt^HYDrXc?ZZt=-nAiedxJxZWaz-# z))(=!JgH*#FF(fV3|bHFYIu{7o@&g${?xI#vexMbY#Mis>h!!Tf5m!}TYg^h;Dffk z=Mm4qI323T&|4007p9Jvgd<$0m+Y02IA{-2Is)hIBeO$u)2|^Vajkkww#H2?O4dM$ z)tW*;b&33P3eaoHOQw-P_7g-$+MIjroTP)z`ay`CXE2|qA+>!vK*a!X;r$%JYRz$r z=XQl~&={bE5VZ{i9<)jLky>o4_mbi0V28zy)(b9~_nv+;XBjG#Z=Sg1EHMQzQHJvlm^4zwEpka_>H3xJ{dil70Y>*pCO1Aq(&?-up_3Ial{ zUT^>&q$DmIB^+OZNoC=mGk}!fE#!UWto1O*LBXklPojj9il+ zSc;k5GY8r%O9|R!vPG=S#W?rZhHVd@>VRAYJZuP2AmG28W?y z<#cSDfZxEH&I5O>gt%?Oe~$pZu2k5X{G_y*Rs;8kW;OGWFAgQ|yND_qw0`_TLy~Z+ zQ2Dji=9w4%|D?0HZc8Qv_Ienb%a?z%F+>#pWOA~z3mN?4ee%kG>=m=z2Ag0VfoTq1 z!}W7^sYSKn07dsrx(t1J<@#@YDpWNJR28HM~t9K*OGA=~Fj}mdgVN^5s$w`}| z#eE_7y08WMg$}-XRflX*)F`2e%H*p)E)g#JD`~pxvi05+Y_9V6(dr%`qa|5ooTulq z%1r-|Sj7rmf7!42p$bdeBb~HUa#F>#)~K8+m3oyk9ez>ExB}n#R(v#wv1xc(w@|5> zH~%e8%tEu^2@9o~q2J;B(08(3#XPkw&H7$3uD58JwQqZcd+C6Hal;YOoM6s@mz=gf zjFJGg`}ykgC2SjgyHas~{&(TA!|H#qGsul;Uv2zMkBOC2S5D*>bbGbM64Z8{qw{xR zgjG&Q;ToRiJ@I25)-I^h+03yplWS!b5X;#Se<&g|E7;rT91(f&gLjXIad|VrK~lE! zIZn5Pqa;N@Yi@C6sK*MIi!E=)sZ1Jn+z#XzgDTHtI)>VG)OSdO>F<9C7D2X3&sjG3!{)`%GU z`c_B*BiVk(VWRit@x}}iJtRxrmNV79$+$_T>t?)3Iz9Sqd(W^Fi6+hR%{(Q6e_*cK z=S-v)-=N|_CBGBhLC zj$owO#NBj*{L&2*z7t8=`i%_Cj!+J!M`qo-IKjcc(M4&@L7n`{Y2KEm@>flSWjjq6 z4vHF-%_~g4COpZrQz*D&Tf@Je_h(~qrX@8k#5Ex8>i6LzEr}M5^7zN%LcvW0=VE;3 z%+Z^&ccC+G+~>dMMj6Z6AuE@mSn6NWy-p|f1pZU0N$*JH zpXU2p!qRRS=zG4gB-})KhDQsJWmhtnRTK6Gy4hZgU|I?dnxHf&;3Cw&!U-}5tkbOp zzZn>lYZZCA(lDFK*$hqjaT8BE$LLg}!6UP4ZfT)K+shAfHk1)I>q6OF_!;kjl^f$t6ZI9Oba|pvi)ICg#NY z0KKvR^2frX*XSm;N|9`0_?FjETUO)*qvHflH~C>+W_cZtCdwNuQY=W z?XsmK;sF(VS{}oURN;+2W84hrvCp-{;9WbUj$=mc*L7fA%$DWHf=#BBJ zVLMFV)JR|KqhrD)hjR+es@EQY{rqvKkOMK4LmuCkHnFN-m$9xLO+iCdb>g8HxK z-N&d{g7ag1c=R$h#!fyU#MNC!FsmCLwEhb7UtoBCT{TG8#L%;0e>IqhsqKB zc{T?9Tj9?c5AMbKKk2$^Yiea>1)_Ig0ZsS&^Sy`GsIRNwjs&iI(b27xhLSrgpu^|} zP-xiYl7@TNzFK7qpGb$*Lr(n{$QYXOCqp<9Q|tZ)MHPl93(wT~XW;4?SGL)6{YoJ@8q(vfDJ zV3hm2q6aRA3Y^K8nRZP#6zn%B@$PaZF{b0&J@jF;4IpH?@l&LnW@OjS_a!^zzlz&D z5ZyU#So~BxRVwmQ8D?kJ8~lcW+IJnLd$a5{#q+0yLtgysg33S4$-gwUx5X?@aEG#T z=%J>lb*C{j{=E8C8`svkbof7SI;N;$COQ1eDLRSXIu(tk{%LcryPHxXX86YO9OavT z9eZ?h$VFM|3ub)i`W-CU+TL=vq^&$Wr~Fb@JmXP$Pci?PcIE_o>nn7e|4ZAN9Tp|7 zR?C_1TkIoj6!R6EZ;M}%@}c9M>}q{lL*IQ;);QqbFWp?wdazk{N>VBCzILiI^y_YXlUt(~TWvz;ni+_GnCq*f;k9ytCbnAdlP+J(jqSX9^!-?S?3PhU+ z187G%$;~#dkMQKsVy4IekwE-Yp`-E@4kzq0iaYbc@z@VLbw$&V-PE)FCTt#z?CA?3 zBY(UU?~UgRt5a_(=~hr77hieMy2sHmkh7^4$9N|qCs=x0>*c6MW7$EaK~G4o z)hw@mJ0R0cO!v^Z8qobZ=ocZL8+{@9h|_ zPsIK@oUgqx=U(;nx%a6;ZL4bi-^Pwxnlt>_buz=NIx$af**wv(BjW$wZRFu=JG#oG znaVt(#nyh;6xMky?yJK7O_9o>s`i?6MM=)hNj~Mkc;o;Z2*YXl34i1%U1>FDSrx`w zTU_Ys`bWe5-3qRcHrq2dd|VatRe%!rEY0nOU1-JUeDu`R$WR_y$YZd|PB+c|RYjZT zpn6wz@v*_t-Qgtr$`HGAlRY(=Af(VFza72m%dq52E1N5p)f6iaUfz?;uf4Sx3+?TM zgP?eF6Sl|==CMw)nfl-KpwOQLozUW+hKsZHx+PakI+n^E&9A<~7dputqHYTaqPtxf zg#XPl_)RGi|8TeHc{Y_CWH)*!;nI-@OX`(oJSYTVw8@rk(HJ(Cq&@kW}JNt22^$dCXAj;b>mumF!^K%8xNm z%(g@Ocl_T_?(k}}IB;yryNY5--58i6b2#$582ze2cIe}_p6SwdN5;n5+}bNj&0!-7 z8Syqa`68Rx)x2(glEaB=Zt+QOzAV3Dd!9_Yqa`1G zr_JHKhASmfHXOQ$yIpBG?GP<0-Gf}r+|*B@NXp8t`-QjMHgmEwrC$t=n`^i!Q4_PB z&?(6}m&THEHI?@(DUrg--^{5nr+8=$ycwu3`ibXR7hE5Y%~s{Rq!CYuk51`0Y;#;| ziwo#6%gx)-T6(?m#QKk3o(@OsYsb5Ew|YGukoVu}<*)HzFTLvWFjU;XDEO7UdsQLn z7we>;aT3EHh&Rp$C`e%G$445N%L3HhyYdv1jofXN&L!iTZ!p6iyQxj$`XuRw7Gn1` zFyUsHw91u8CyGzM{fXxT6Rz8^WkyM#d))b7+`3vO^2<*=q=&H2c74bc zuF^iFsGUXc>$52e`I+Z8$^V_90y~%q-LK%U|CO1<5tv^%yf+mm$`Y`#d*DGl8mgy{ KryRX}>;C{7W5ebE diff --git a/doc/images/Dspeed4.png b/doc/images/Dspeed4.png index eba485d0d1ed779b61c1dd1698049ad6628a8a5e..a0c7f65c25312279b4b25294cbcdb6ecfa51ae4e 100644 GIT binary patch literal 29425 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 w>=O?O?_zidM(_WRU$OtcAGmD}WHL2Zn Date: Fri, 17 Mar 2017 15:33:09 -0700 Subject: [PATCH 60/60] fixed typo in server type --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f1d8eda1b..7caee5fd3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ you can consult a list of known ports on [Zstandard homepage](http://www.zstd.ne |dev | [![Build Status](https://travis-ci.org/facebook/zstd.svg?branch=dev)](https://travis-ci.org/facebook/zstd) | As a reference, several fast compression algorithms were tested and compared -on a server running Linux Mint Debian Edition (`Linux version 4.8.0-1-amd64`), +on a server running Linux Debian (`Linux version 4.8.0-1-amd64`), with a Core i7-6700K CPU @ 4.0GHz, using [lzbench], an open-source in-memory benchmark by @inikep compiled with GCC 6.3.0, @@ -39,7 +39,7 @@ Zstd can also offer stronger compression ratios at the cost of compression speed Speed vs Compression trade-off is configurable by small increments. Decompression speed is preserved and remains roughly the same at all settings, a property shared by most LZ compression algorithms, such as [zlib] or lzma. The following tests were run -on a server running Linux Mint Debian Edition (`Linux version 4.8.0-1-amd64`) +on a server running Linux Debian (`Linux version 4.8.0-1-amd64`) with a Core i7-6700K CPU @ 4.0GHz, using [lzbench], an open-source in-memory benchmark by @inikep compiled with GCC 6.3.0,