diff --git a/.travis.yml b/.travis.yml index 80cae76fc..ff6ab0f79 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ matrix: packages: - gcc-4.8 - g++-4.8 - env: PLATFORM="Ubuntu 12.04 container" CMD="make -C tests test-zstd_nolegacy && make clean && make zlibwrapper && make clean && make cmaketest && make clean && make -C contrib/pzstd pzstd && make -C contrib/pzstd googletest && make -C contrib/pzstd test && make -C contrib/pzstd clean" + env: PLATFORM="Ubuntu 12.04 container" CMD="make zlibwrapper && make clean && make -C tests test-zstd_nolegacy && make clean && make clean && make cmaketest && make clean && make -C contrib/pzstd pzstd && make -C contrib/pzstd googletest && make -C contrib/pzstd test && make -C contrib/pzstd clean" - os: linux sudo: false env: PLATFORM="Ubuntu 12.04 container" CMD="make usan" @@ -55,23 +55,30 @@ matrix: packages: - libc6-dev-i386 - gcc-multilib - - os: linux - sudo: required - env: PLATFORM="Ubuntu 12.04" CMD="make -C tests valgrindTest" - addons: - apt: - packages: - - valgrind # Ubuntu 14.04 LTS Server Edition 64 bit - os: linux dist: trusty sudo: required - env: PLATFORM="Ubuntu 14.04" CMD="make gpptest && make clean && make gnu90test && make clean && make c99test && make clean && make gnu99test && make clean && make clangtest" + env: PLATFORM="Ubuntu 14.04" CMD='make -C lib all && CFLAGS="-O1 -g" make -C zlibWrapper valgrindTest && make -C tests valgrindTest' + addons: + apt: + packages: + - valgrind + - os: linux + dist: trusty + sudo: required + install: + - export CXX="g++-4.8" CC="gcc-4.8" + env: PLATFORM="Ubuntu 14.04" CMD="make gpptest && make clean && make gnu90test && make clean && make c99test && make clean && make gnu99test && make clean && make clangtest && make clean && make -C contrib/pzstd pzstd32 && make -C contrib/pzstd googletest32 && make -C contrib/pzstd test32 && make -C contrib/pzstd clean" addons: apt: packages: - libc6-dev-i386 - - g++-multilib + - g++-multilib + - gcc-4.8 + - gcc-4.8-multilib + - g++-4.8 + - g++-4.8-multilib - os: linux dist: trusty sudo: required @@ -84,7 +91,7 @@ matrix: - os: linux dist: trusty sudo: required - env: PLATFORM="Ubuntu 14.04" CMD="make zlibwrapper && make clean && make gcc5test && make clean && make gcc6test && sudo apt-get install -y -q qemu-system-ppc binfmt-support qemu-user-static gcc-powerpc-linux-gnu && make clean && make ppctest" + env: PLATFORM="Ubuntu 14.04" CMD="make gcc5test && make clean && make gcc6test && sudo apt-get install -y -q qemu-system-ppc binfmt-support qemu-user-static gcc-powerpc-linux-gnu && make clean && make ppctest" addons: apt: sources: diff --git a/Makefile b/Makefile index 7860ce1db..ac0c583f4 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,9 @@ # of patent rights can be found in the PATENTS file in the same directory. # ################################################################ -PRGDIR = programs -ZSTDDIR = lib +PRGDIR = programs +ZSTDDIR = lib +BUILDIR = build ZWRAPDIR = zlibWrapper TESTDIR = tests @@ -33,8 +34,7 @@ zstd: cp $(PRGDIR)/zstd . zlibwrapper: - $(MAKE) -C $(ZSTDDIR) all - $(MAKE) -C $(ZWRAPDIR) all + $(MAKE) -C $(ZWRAPDIR) test test: $(MAKE) -C $(TESTDIR) $@ @@ -121,9 +121,9 @@ endif ifneq (,$(filter $(HOST_OS),MSYS POSIX)) cmaketest: cmake --version - $(RM) -r projects/cmake/build - mkdir projects/cmake/build - cd projects/cmake/build ; cmake -DPREFIX:STRING=~/install_test_dir $(CMAKE_PARAMS) .. ; $(MAKE) install ; $(MAKE) uninstall + $(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 CFLAGS="-std=c90" $(MAKE) all # will fail, due to // and long long diff --git a/NEWS b/NEWS index ace6d4826..a2d77f020 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,7 @@ -v1.0.1 +v1.1.0 New : contrib/pzstd, parallel version of zstd, by Nick Terrell added : NetBSD install target (#338) -Improved : variable compression speed improvements on batches of small files. +Improved : speed improvements for batches of small files. Fixed : CLI -d output to stdout by default when input is stdin (#322) Fixed : CLI correctly detects console on Mac OS-X Fixed : CLI supports recursive mode `-r` on Mac OS-X diff --git a/README.md b/README.md index 2c8e707e9..53609a146 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,35 @@ Hence, deploying one dictionary per type of data will provide the greatest benef `zstd --decompress FILE.zst -D dictionaryName` +### Build + +Once you have the repository cloned, there are multiple ways provided to build Zstandard. + +#### Makefile + +If your system is compatible with a standard `make` (or `gmake`) binary generator, +you can simply run it at the root directory. +It will generate `zstd` within root directory. + +Other available options include : +- `make install` : create and install zstd binary, library and man page +- `make test` : create and run `zstd` and test tools on local platform + +#### cmake + +A `cmake` project generator is provided within `build/cmake`. +It can generate Makefiles or other build scripts +to create `zstd` binary, and `libzstd` dynamic and static libraries. + +#### Visual (Windows) + +Going into `build` directory, you will find additional possibilities : +- Projects for Visual Studio 2005, 2008 and 2010 + + VS2010 project is compatible with VS2012, VS2013 and VS2015 +- Automated build scripts for Visual compiler by @KrzysFR , in `build/VS_scripts`, + which will build `zstd` cli and `libzstd` library without any need to open Visual Studio solution. + + ### Status Zstandard is currently deployed within Facebook. It is used daily to compress and decompress very large amounts of data in multiple formats and use cases. diff --git a/appveyor.yml b/appveyor.yml index 280cbae86..6345c7b39 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -36,8 +36,8 @@ install: build_script: - ECHO Building %COMPILER% %PLATFORM% %CONFIGURATION% - - if [%PLATFORM%]==[mingw32] SET PATH=%PATH_MINGW32%;%PATH_ORIGINAL% - - if [%PLATFORM%]==[mingw64] SET PATH=%PATH_MINGW64%;%PATH_ORIGINAL% + - if [%PLATFORM%]==[mingw32] SET PATH=%PATH_MINGW32%;%PATH_ORIGINAL% + - if [%PLATFORM%]==[mingw64] SET PATH=%PATH_MINGW64%;%PATH_ORIGINAL% - if [%PLATFORM%]==[mingw64] ( make clean && ECHO *** && @@ -52,6 +52,8 @@ build_script: ECHO *** && ECHO make -C contrib\pzstd pzstd && make -C contrib\pzstd pzstd && + make -C contrib\pzstd googletest-mingw64 && + make -C contrib\pzstd test && make -C contrib\pzstd clean ) - if [%COMPILER%]==[gcc] ( @@ -76,51 +78,51 @@ build_script: ECHO *** && ECHO *** Building Visual Studio 2008 %PLATFORM%\%CONFIGURATION% in %APPVEYOR_BUILD_FOLDER% && ECHO *** && - msbuild "projects\VS2008\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v90 /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && - DIR projects\VS2008\bin\%PLATFORM%\%CONFIGURATION%\*.exe && - MD5sum projects/VS2008/bin/%PLATFORM%/%CONFIGURATION%/*.exe && - COPY projects\VS2008\bin\%PLATFORM%\%CONFIGURATION%\fuzzer.exe tests\fuzzer_VS2008_%PLATFORM%_%CONFIGURATION%.exe && + msbuild "build\VS2008\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v90 /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && + DIR build\VS2008\bin\%PLATFORM%\%CONFIGURATION%\*.exe && + MD5sum build/VS2008/bin/%PLATFORM%/%CONFIGURATION%/*.exe && + COPY build\VS2008\bin\%PLATFORM%\%CONFIGURATION%\fuzzer.exe tests\fuzzer_VS2008_%PLATFORM%_%CONFIGURATION%.exe && ECHO *** && ECHO *** Building Visual Studio 2010 %PLATFORM%\%CONFIGURATION% && ECHO *** && - msbuild "projects\VS2010\zstd.sln" %ADDITIONALPARAM% /m /verbosity:minimal /property:PlatformToolset=v100 /p:ForceImportBeforeCppTargets=%APPVEYOR_BUILD_FOLDER%\projects\VS2010\CompileAsCpp.props /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && - DIR projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && - MD5sum projects/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && - msbuild "projects\VS2010\zstd.sln" %ADDITIONALPARAM% /m /verbosity:minimal /property:PlatformToolset=v100 /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && - DIR projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && - MD5sum projects/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && - COPY projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\fuzzer.exe tests\fuzzer_VS2010_%PLATFORM%_%CONFIGURATION%.exe && + msbuild "build\VS2010\zstd.sln" %ADDITIONALPARAM% /m /verbosity:minimal /property:PlatformToolset=v100 /p:ForceImportBeforeCppTargets=%APPVEYOR_BUILD_FOLDER%\build\VS2010\CompileAsCpp.props /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && + DIR build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && + MD5sum build/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && + msbuild "build\VS2010\zstd.sln" %ADDITIONALPARAM% /m /verbosity:minimal /property:PlatformToolset=v100 /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && + DIR build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && + MD5sum build/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && + COPY build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\fuzzer.exe tests\fuzzer_VS2010_%PLATFORM%_%CONFIGURATION%.exe && ECHO *** && ECHO *** Building Visual Studio 2012 %PLATFORM%\%CONFIGURATION% && ECHO *** && - msbuild "projects\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v110 /p:ForceImportBeforeCppTargets=%APPVEYOR_BUILD_FOLDER%\projects\VS2010\CompileAsCpp.props /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && - DIR projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && - MD5sum projects/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && - msbuild "projects\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v110 /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && - DIR projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && - MD5sum projects/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && - COPY projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\fuzzer.exe tests\fuzzer_VS2012_%PLATFORM%_%CONFIGURATION%.exe && + msbuild "build\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v110 /p:ForceImportBeforeCppTargets=%APPVEYOR_BUILD_FOLDER%\build\VS2010\CompileAsCpp.props /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && + DIR build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && + MD5sum build/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && + msbuild "build\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v110 /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && + DIR build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && + MD5sum build/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && + COPY build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\fuzzer.exe tests\fuzzer_VS2012_%PLATFORM%_%CONFIGURATION%.exe && ECHO *** && ECHO *** Building Visual Studio 2013 %PLATFORM%\%CONFIGURATION% && ECHO *** && - msbuild "projects\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v120 /p:ForceImportBeforeCppTargets=%APPVEYOR_BUILD_FOLDER%\projects\VS2010\CompileAsCpp.props /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && - DIR projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && - MD5sum projects/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && - msbuild "projects\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v120 /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && - DIR projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && - MD5sum projects/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && - COPY projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\fuzzer.exe tests\fuzzer_VS2013_%PLATFORM%_%CONFIGURATION%.exe && + msbuild "build\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v120 /p:ForceImportBeforeCppTargets=%APPVEYOR_BUILD_FOLDER%\build\VS2010\CompileAsCpp.props /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && + DIR build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && + MD5sum build/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && + msbuild "build\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v120 /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && + DIR build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && + MD5sum build/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && + COPY build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\fuzzer.exe tests\fuzzer_VS2013_%PLATFORM%_%CONFIGURATION%.exe && ECHO *** && ECHO *** Building Visual Studio 2015 %PLATFORM%\%CONFIGURATION% && ECHO *** && - msbuild "projects\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v140 /p:ForceImportBeforeCppTargets=%APPVEYOR_BUILD_FOLDER%\projects\VS2010\CompileAsCpp.props /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && - DIR projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && - MD5sum projects/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && - msbuild "projects\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v140 /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && - DIR projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && - MD5sum projects/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && - COPY projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\fuzzer.exe tests\fuzzer_VS2015_%PLATFORM%_%CONFIGURATION%.exe && - COPY projects\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe tests\ + msbuild "build\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v140 /p:ForceImportBeforeCppTargets=%APPVEYOR_BUILD_FOLDER%\build\VS2010\CompileAsCpp.props /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && + DIR build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && + MD5sum build/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && + msbuild "build\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v140 /t:Clean,Build /p:Platform=%PLATFORM% /p:Configuration=%CONFIGURATION% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" && + DIR build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe && + MD5sum build/VS2010/bin/%PLATFORM%/%CONFIGURATION%/*.exe && + COPY build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\fuzzer.exe tests\fuzzer_VS2015_%PLATFORM%_%CONFIGURATION%.exe && + COPY build\VS2010\bin\%PLATFORM%\%CONFIGURATION%\*.exe tests\ ) test_script: @@ -144,7 +146,7 @@ test_script: artifacts: - path: bin\zstd.exe - - path: bin\zstd32.exe + - path: bin\zstd32.exe deploy: - provider: GitHub @@ -160,7 +162,7 @@ deploy: - provider: GitHub auth_token: secure: LgJo8emYc3sFnlNWkGl4/VYK3nk/8+RagcsqDlAi3xeqNGNutnKjcftjg84uJoT4 - artifact: bin\zstd32.exe + artifact: bin\zstd32.exe force_update: true on: branch: autobuild diff --git a/projects/.gitignore b/build/.gitignore similarity index 92% rename from projects/.gitignore rename to build/.gitignore index 7ceb958ea..86ed710bd 100644 --- a/projects/.gitignore +++ b/build/.gitignore @@ -1,7 +1,7 @@ *Copy # Visual C++ -build/ +bin/ VS2005/ VS2008/ VS2010/ diff --git a/projects/README.md b/build/README.md similarity index 92% rename from projects/README.md rename to build/README.md index dd60b56e8..8dc67326b 100644 --- a/projects/README.md +++ b/build/README.md @@ -8,7 +8,7 @@ The following projects are included with the zstd distribution: - `VS2005` - Visual Studio 2005 project - `VS2008` - Visual Studio 2008 project - `VS2010` - Visual Studio 2010 project (which also works well with Visual Studio 2012, 2013, 2015) -- `build` - command line scripts prepared for Visual Studio compilation without IDE +- `VS_scripts` - command line scripts prepared for Visual Studio compilation without IDE #### How to compile zstd with Visual Studio diff --git a/projects/VS2005/fullbench/fullbench.vcproj b/build/VS2005/fullbench/fullbench.vcproj similarity index 100% rename from projects/VS2005/fullbench/fullbench.vcproj rename to build/VS2005/fullbench/fullbench.vcproj diff --git a/projects/VS2005/fuzzer/fuzzer.vcproj b/build/VS2005/fuzzer/fuzzer.vcproj similarity index 100% rename from projects/VS2005/fuzzer/fuzzer.vcproj rename to build/VS2005/fuzzer/fuzzer.vcproj diff --git a/projects/VS2005/zstd.sln b/build/VS2005/zstd.sln similarity index 100% rename from projects/VS2005/zstd.sln rename to build/VS2005/zstd.sln diff --git a/projects/VS2005/zstd/zstd.vcproj b/build/VS2005/zstd/zstd.vcproj similarity index 97% rename from projects/VS2005/zstd/zstd.vcproj rename to build/VS2005/zstd/zstd.vcproj index ff45e3932..68c3578ea 100644 --- a/projects/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" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;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" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;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" - PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;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" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/projects/VS2005/zstdlib/zstdlib.vcproj b/build/VS2005/zstdlib/zstdlib.vcproj similarity index 90% rename from projects/VS2005/zstdlib/zstdlib.vcproj rename to build/VS2005/zstdlib/zstdlib.vcproj index 2313c87aa..7ea3d9b7b 100644 --- a/projects/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_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;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_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;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_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;_DEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;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_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;NDEBUG;_CONSOLE" + PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" @@ -379,6 +379,34 @@ RelativePath="..\..\..\lib\decompress\zstd_decompress.c" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level4 Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true false @@ -165,7 +165,7 @@ Level4 Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true false @@ -183,7 +183,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) false false MultiThreaded @@ -204,7 +204,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) false false MultiThreaded diff --git a/projects/VS2010/zstdlib/zstdlib.rc b/build/VS2010/zstdlib/zstdlib.rc similarity index 100% rename from projects/VS2010/zstdlib/zstdlib.rc rename to build/VS2010/zstdlib/zstdlib.rc diff --git a/projects/VS2010/zstdlib/zstdlib.vcxproj b/build/VS2010/zstdlib/zstdlib.vcxproj similarity index 87% rename from projects/VS2010/zstdlib/zstdlib.vcxproj rename to build/VS2010/zstdlib/zstdlib.vcxproj index 232fdf442..b97808dd0 100644 --- a/projects/VS2010/zstdlib/zstdlib.vcxproj +++ b/build/VS2010/zstdlib/zstdlib.vcxproj @@ -32,6 +32,13 @@ + + + + + + + @@ -40,6 +47,14 @@ + + + + + + + + @@ -126,7 +141,7 @@ Level4 Disabled - ZSTD_DLL_EXPORT=1;ZSTD_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -146,7 +161,7 @@ Level4 Disabled - ZSTD_DLL_EXPORT=1;ZSTD_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -166,7 +181,7 @@ MaxSpeed true true - ZSTD_DLL_EXPORT=1;ZSTD_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) false MultiThreaded ProgramDatabase @@ -188,7 +203,7 @@ MaxSpeed true true - ZSTD_DLL_EXPORT=1;ZSTD_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) false false MultiThreaded diff --git a/projects/build/README.md b/build/VS_scripts/README.md similarity index 100% rename from projects/build/README.md rename to build/VS_scripts/README.md diff --git a/projects/build/build.VS2010.cmd b/build/VS_scripts/build.VS2010.cmd similarity index 100% rename from projects/build/build.VS2010.cmd rename to build/VS_scripts/build.VS2010.cmd diff --git a/projects/build/build.VS2012.cmd b/build/VS_scripts/build.VS2012.cmd similarity index 100% rename from projects/build/build.VS2012.cmd rename to build/VS_scripts/build.VS2012.cmd diff --git a/projects/build/build.VS2013.cmd b/build/VS_scripts/build.VS2013.cmd similarity index 100% rename from projects/build/build.VS2013.cmd rename to build/VS_scripts/build.VS2013.cmd diff --git a/projects/build/build.VS2015.cmd b/build/VS_scripts/build.VS2015.cmd similarity index 100% rename from projects/build/build.VS2015.cmd rename to build/VS_scripts/build.VS2015.cmd diff --git a/projects/build/build.generic.cmd b/build/VS_scripts/build.generic.cmd similarity index 100% rename from projects/build/build.generic.cmd rename to build/VS_scripts/build.generic.cmd diff --git a/projects/cmake/.gitignore b/build/cmake/.gitignore similarity index 100% rename from projects/cmake/.gitignore rename to build/cmake/.gitignore diff --git a/projects/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt similarity index 100% rename from projects/cmake/CMakeLists.txt rename to build/cmake/CMakeLists.txt diff --git a/projects/cmake/CMakeModules/AddExtraCompilationFlags.cmake b/build/cmake/CMakeModules/AddExtraCompilationFlags.cmake similarity index 100% rename from projects/cmake/CMakeModules/AddExtraCompilationFlags.cmake rename to build/cmake/CMakeModules/AddExtraCompilationFlags.cmake diff --git a/projects/cmake/cmake_uninstall.cmake.in b/build/cmake/cmake_uninstall.cmake.in similarity index 100% rename from projects/cmake/cmake_uninstall.cmake.in rename to build/cmake/cmake_uninstall.cmake.in diff --git a/projects/cmake/lib/CMakeLists.txt b/build/cmake/lib/CMakeLists.txt similarity index 99% rename from projects/cmake/lib/CMakeLists.txt rename to build/cmake/lib/CMakeLists.txt index 36e8afa1d..c984145ba 100644 --- a/projects/cmake/lib/CMakeLists.txt +++ b/build/cmake/lib/CMakeLists.txt @@ -108,7 +108,7 @@ IF (ZSTD_LEGACY_SUPPORT) ENDIF (ZSTD_LEGACY_SUPPORT) IF (MSVC) - SET(MSVC_RESOURCE_DIR ${ROOT_DIR}/projects/VS2010/zstdlib) + SET(MSVC_RESOURCE_DIR ${ROOT_DIR}/build/VS2010/zstdlib) SET(PlatformDependResources ${MSVC_RESOURCE_DIR}/zstdlib.rc) ENDIF (MSVC) diff --git a/projects/cmake/programs/.gitignore b/build/cmake/programs/.gitignore similarity index 100% rename from projects/cmake/programs/.gitignore rename to build/cmake/programs/.gitignore diff --git a/projects/cmake/programs/CMakeLists.txt b/build/cmake/programs/CMakeLists.txt similarity index 100% rename from projects/cmake/programs/CMakeLists.txt rename to build/cmake/programs/CMakeLists.txt diff --git a/projects/cmake/tests/.gitignore b/build/cmake/tests/.gitignore similarity index 100% rename from projects/cmake/tests/.gitignore rename to build/cmake/tests/.gitignore diff --git a/projects/cmake/tests/CMakeLists.txt b/build/cmake/tests/CMakeLists.txt similarity index 100% rename from projects/cmake/tests/CMakeLists.txt rename to build/cmake/tests/CMakeLists.txt diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile index d71cf5b34..e30be0bed 100644 --- a/contrib/pzstd/Makefile +++ b/contrib/pzstd/Makefile @@ -30,7 +30,7 @@ else EXT = endif -.PHONY: default all test clean +.PHONY: default all test clean test32 googletest googletest32 default: pzstd @@ -41,7 +41,6 @@ libzstd.a: $(ZSTD_FILES) $(MAKE) -C $(ZSTDDIR) libzstd @cp $(ZSTDDIR)/libzstd.a . - Pzstd.o: Pzstd.h Pzstd.cpp ErrorHolder.h utils/*.h $(CXX) $(FLAGS) -c Pzstd.cpp -o $@ @@ -54,23 +53,66 @@ Options.o: Options.h Options.cpp main.o: main.cpp *.h utils/*.h $(CXX) $(FLAGS) -c main.cpp -o $@ -pzstd: Pzstd.o SkippableFrame.o Options.o main.o libzstd.a +pzstd: Pzstd.o SkippableFrame.o Options.o main.o libzstd.a $(CXX) $(FLAGS) $^ -o $@$(EXT) -lpthread +libzstd32.a: $(ZSTD_FILES) + $(MAKE) -C $(ZSTDDIR) libzstd MOREFLAGS="-m32" + @cp $(ZSTDDIR)/libzstd.a libzstd32.a + +Pzstd32.o: Pzstd.h Pzstd.cpp ErrorHolder.h utils/*.h + $(CXX) -m32 $(FLAGS) -c Pzstd.cpp -o $@ + +SkippableFrame32.o: SkippableFrame.h SkippableFrame.cpp utils/*.h + $(CXX) -m32 $(FLAGS) -c SkippableFrame.cpp -o $@ + +Options32.o: Options.h Options.cpp + $(CXX) -m32 $(FLAGS) -c Options.cpp -o $@ + +main32.o: main.cpp *.h utils/*.h + $(CXX) -m32 $(FLAGS) -c main.cpp -o $@ + +pzstd32: Pzstd32.o SkippableFrame32.o Options32.o main32.o libzstd32.a + $(CXX) -m32 $(FLAGS) $^ -o $@$(EXT) -lpthread + googletest: + @$(RM) -rf googletest @git clone https://github.com/google/googletest @mkdir -p googletest/build @cd googletest/build && cmake .. && make -test: libzstd.a Pzstd.o Options.o SkippableFrame.o +googletest32: + @$(RM) -rf googletest + @git clone https://github.com/google/googletest + @mkdir -p googletest/build + @cd googletest/build && cmake .. -DCMAKE_CXX_FLAGS=-m32 && make + +googletest-mingw64: + $(RM) -rf googletest + git clone https://github.com/google/googletest + mkdir -p googletest/build + cd googletest/build && cmake -G "MSYS Makefiles" .. && $(MAKE) + +test: + $(MAKE) libzstd.a + $(MAKE) pzstd MOREFLAGS="-Wall -Wextra -pedantic -Werror" $(MAKE) -C utils/test clean - $(MAKE) -C utils/test test + $(MAKE) -C utils/test test MOREFLAGS="-Wall -Wextra -pedantic -Werror" $(MAKE) -C test clean - $(MAKE) -C test test + $(MAKE) -C test test MOREFLAGS="-Wall -Wextra -pedantic -Werror" + +test32: + $(MAKE) libzstd.a MOREFLAGS="-m32" + $(MAKE) pzstd MOREFLAGS="-m32 -Wall -Wextra -pedantic -Werror" + $(MAKE) -C utils/test clean + $(MAKE) -C utils/test test MOREFLAGS="-m32 -Wall -Wextra -pedantic -Werror" + $(MAKE) -C test clean + $(MAKE) -C test test MOREFLAGS="-m32 -Wall -Wextra -pedantic -Werror" + clean: $(MAKE) -C $(ZSTDDIR) clean $(MAKE) -C utils/test clean $(MAKE) -C test clean - @$(RM) -rf libzstd.a *.o pzstd$(EXT) + @$(RM) -rf libzstd.a *.o pzstd$(EXT) pzstd32$(EXT) @echo Cleaning completed diff --git a/contrib/pzstd/Options.cpp b/contrib/pzstd/Options.cpp index 122f4fb36..ece8c0782 100644 --- a/contrib/pzstd/Options.cpp +++ b/contrib/pzstd/Options.cpp @@ -7,182 +7,420 @@ * of patent rights can be found in the PATENTS file in the same directory. */ #include "Options.h" +#include "utils/ScopeGuard.h" +#include +#include #include #include +#include #include +#include +#include + +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || \ + defined(__CYGWIN__) +#include /* _isatty */ +#define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) +#else +#if defined(_POSIX_C_SOURCE) || defined(_XOPEN_SOURCE) || \ + defined(_POSIX_SOURCE) || \ + (defined(__APPLE__) && \ + defined( \ + __MACH__)) /* https://sourceforge.net/p/predef/wiki/OperatingSystems/ \ + */ +#include /* isatty */ +#define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) +#else +#define IS_CONSOLE(stdStream) 0 +#endif +#endif namespace pzstd { namespace { -unsigned parseUnsigned(const char* arg) { +unsigned defaultNumThreads() { +#ifdef PZSTD_NUM_THREADS + return PZSTD_NUM_THREADS; +#else + return std::thread::hardware_concurrency(); +#endif +} + +unsigned parseUnsigned(const char **arg) { unsigned result = 0; - while (*arg >= '0' && *arg <= '9') { + while (**arg >= '0' && **arg <= '9') { result *= 10; - result += *arg - '0'; - ++arg; + result += **arg - '0'; + ++(*arg); } return result; } -const std::string zstdExtension = ".zst"; -constexpr unsigned defaultCompressionLevel = 3; -constexpr unsigned maxNonUltraCompressionLevel = 19; +const char *getArgument(const char *options, const char **argv, int &i, + int argc) { + if (options[1] != 0) { + return options + 1; + } + ++i; + if (i == argc) { + std::fprintf(stderr, "Option -%c requires an argument, but none provided\n", + *options); + return nullptr; + } + return argv[i]; +} + +const std::string kZstdExtension = ".zst"; +constexpr char kStdIn[] = "-"; +constexpr char kStdOut[] = "-"; +constexpr unsigned kDefaultCompressionLevel = 3; +constexpr unsigned kMaxNonUltraCompressionLevel = 19; + +#ifdef _WIN32 +const char nullOutput[] = "nul"; +#else +const char nullOutput[] = "/dev/null"; +#endif + +void notSupported(const char *option) { + std::fprintf(stderr, "Operation not supported: %s\n", option); +} void usage() { std::fprintf(stderr, "Usage:\n"); - std::fprintf(stderr, "\tpzstd [args] FILE\n"); + std::fprintf(stderr, " pzstd [args] [FILE(s)]\n"); std::fprintf(stderr, "Parallel ZSTD options:\n"); - std::fprintf(stderr, "\t-n/--num-threads #: Number of threads to spawn\n"); - std::fprintf(stderr, "\t-p/--pzstd-headers: Write pzstd headers to enable parallel decompression\n"); + std::fprintf(stderr, " -p, --processes # : number of threads to use for (de)compression (default:%d)\n", defaultNumThreads()); std::fprintf(stderr, "ZSTD options:\n"); - std::fprintf(stderr, "\t-u/--ultra : enable levels beyond %i, up to %i (requires more memory)\n", maxNonUltraCompressionLevel, ZSTD_maxCLevel()); - std::fprintf(stderr, "\t-h/--help : display help and exit\n"); - std::fprintf(stderr, "\t-V/--version : display version number and exit\n"); - std::fprintf(stderr, "\t-d/--decompress : decompression\n"); - std::fprintf(stderr, "\t-f/--force : overwrite output\n"); - std::fprintf(stderr, "\t-o/--output file : result stored into `file`\n"); - std::fprintf(stderr, "\t-c/--stdout : write output to standard output\n"); - std::fprintf(stderr, "\t-# : # compression level (1-%d, default:%d)\n", maxNonUltraCompressionLevel, defaultCompressionLevel); + std::fprintf(stderr, " -# : # compression level (1-%d, default:%d)\n", kMaxNonUltraCompressionLevel, kDefaultCompressionLevel); + std::fprintf(stderr, " -d, --decompress : decompression\n"); + std::fprintf(stderr, " -o file : result stored into `file` (only if 1 input file)\n"); + std::fprintf(stderr, " -f, --force : overwrite output without prompting\n"); + std::fprintf(stderr, " --rm : remove source file(s) after successful (de)compression\n"); + std::fprintf(stderr, " -k, --keep : preserve source file(s) (default)\n"); + std::fprintf(stderr, " -h, --help : display help and exit\n"); + std::fprintf(stderr, " -V, --version : display version number and exit\n"); + std::fprintf(stderr, " -v, --verbose : verbose mode; specify multiple times to increase log level (default:2)\n"); + std::fprintf(stderr, " -q, --quiet : suppress warnings; specify twice to suppress errors too\n"); + std::fprintf(stderr, " -c, --stdout : force write to standard output, even if it is the console\n"); +#ifdef UTIL_HAS_CREATEFILELIST + std::fprintf(stderr, " -r : operate recursively on directories\n"); +#endif + std::fprintf(stderr, " --ultra : enable levels beyond %i, up to %i (requires more memory)\n", kMaxNonUltraCompressionLevel, ZSTD_maxCLevel()); + std::fprintf(stderr, " -C, --check : integrity check (default)\n"); + std::fprintf(stderr, " --no-check : no integrity check\n"); + std::fprintf(stderr, " -t, --test : test compressed file integrity\n"); + std::fprintf(stderr, " -- : all arguments after \"--\" are treated as files\n"); } } // anonymous namespace Options::Options() - : numThreads(0), - maxWindowLog(23), - compressionLevel(defaultCompressionLevel), - decompress(false), - overwrite(false), - pzstdHeaders(false) {} + : numThreads(defaultNumThreads()), maxWindowLog(23), + compressionLevel(kDefaultCompressionLevel), decompress(false), + overwrite(false), keepSource(true), writeMode(WriteMode::Auto), + checksum(true), verbosity(2) {} -bool Options::parse(int argc, const char** argv) { +Options::Status Options::parse(int argc, const char **argv) { + bool test = false; + bool recursive = false; bool ultra = false; + bool forceStdout = false; + // Local copy of input files, which are pointers into argv. + std::vector localInputFiles; for (int i = 1; i < argc; ++i) { - const char* arg = argv[i]; - // Arguments with a short option - char option = 0; - if (!std::strcmp(arg, "--num-threads")) { - option = 'n'; - } else if (!std::strcmp(arg, "--pzstd-headers")) { - option = 'p'; - } else if (!std::strcmp(arg, "--ultra")) { - option = 'u'; - } else if (!std::strcmp(arg, "--version")) { - option = 'V'; - } else if (!std::strcmp(arg, "--help")) { - option = 'h'; - } else if (!std::strcmp(arg, "--decompress")) { - option = 'd'; - } else if (!std::strcmp(arg, "--force")) { - option = 'f'; - } else if (!std::strcmp(arg, "--output")) { - option = 'o'; - } else if (!std::strcmp(arg, "--stdout")) { - option = 'c'; - }else if (arg[0] == '-' && arg[1] != 0) { - // Parse the compression level or short option - if (arg[1] >= '0' && arg[1] <= '9') { - compressionLevel = parseUnsigned(arg + 1); - continue; - } - option = arg[1]; - } else if (inputFile.empty()) { - inputFile = arg; + const char *arg = argv[i]; + // Protect against empty arguments + if (arg[0] == 0) { continue; - } else { - std::fprintf(stderr, "Invalid argument: %s.\n", arg); - return false; } - - switch (option) { - case 'n': - if (++i == argc) { - std::fprintf(stderr, "Invalid argument: -n requires an argument.\n"); - return false; - } - numThreads = parseUnsigned(argv[i]); - if (numThreads == 0) { - std::fprintf(stderr, "Invalid argument: # of threads must be > 0.\n"); - return false; - } - break; - case 'p': - pzstdHeaders = true; - break; - case 'u': + // Everything after "--" is an input file + if (!std::strcmp(arg, "--")) { + ++i; + std::copy(argv + i, argv + argc, std::back_inserter(localInputFiles)); + break; + } + // Long arguments that don't have a short option + { + bool isLongOption = true; + if (!std::strcmp(arg, "--rm")) { + keepSource = false; + } else if (!std::strcmp(arg, "--ultra")) { ultra = true; maxWindowLog = 0; - break; - case 'V': - std::fprintf(stderr, "ZSTD version: %s.\n", ZSTD_VERSION_STRING); - return false; + } else if (!std::strcmp(arg, "--no-check")) { + checksum = false; + } else if (!std::strcmp(arg, "--sparse")) { + writeMode = WriteMode::Sparse; + notSupported("Sparse mode"); + return Status::Failure; + } else if (!std::strcmp(arg, "--no-sparse")) { + writeMode = WriteMode::Regular; + notSupported("Sparse mode"); + return Status::Failure; + } else if (!std::strcmp(arg, "--dictID")) { + notSupported(arg); + return Status::Failure; + } else if (!std::strcmp(arg, "--no-dictID")) { + notSupported(arg); + return Status::Failure; + } else { + isLongOption = false; + } + if (isLongOption) { + continue; + } + } + // Arguments with a short option simply set their short option. + const char *options = nullptr; + if (!std::strcmp(arg, "--processes")) { + options = "p"; + } else if (!std::strcmp(arg, "--version")) { + options = "V"; + } else if (!std::strcmp(arg, "--help")) { + options = "h"; + } else if (!std::strcmp(arg, "--decompress")) { + options = "d"; + } else if (!std::strcmp(arg, "--force")) { + options = "f"; + } else if (!std::strcmp(arg, "--stdout")) { + options = "c"; + } else if (!std::strcmp(arg, "--keep")) { + options = "k"; + } else if (!std::strcmp(arg, "--verbose")) { + options = "v"; + } else if (!std::strcmp(arg, "--quiet")) { + options = "q"; + } else if (!std::strcmp(arg, "--check")) { + options = "C"; + } else if (!std::strcmp(arg, "--test")) { + options = "t"; + } else if (arg[0] == '-' && arg[1] != 0) { + options = arg + 1; + } else { + localInputFiles.emplace_back(arg); + continue; + } + assert(options != nullptr); + + bool finished = false; + while (!finished && *options != 0) { + // Parse the compression level + if (*options >= '0' && *options <= '9') { + compressionLevel = parseUnsigned(&options); + continue; + } + + switch (*options) { case 'h': + case 'H': usage(); - return false; + return Status::Message; + case 'V': + std::fprintf(stderr, "PZSTD version: %s.\n", ZSTD_VERSION_STRING); + return Status::Message; + case 'p': { + finished = true; + const char *optionArgument = getArgument(options, argv, i, argc); + if (optionArgument == nullptr) { + return Status::Failure; + } + if (*optionArgument < '0' || *optionArgument > '9') { + std::fprintf(stderr, "Option -p expects a number, but %s provided\n", + optionArgument); + return Status::Failure; + } + numThreads = parseUnsigned(&optionArgument); + if (*optionArgument != 0) { + std::fprintf(stderr, + "Option -p expects a number, but %u%s provided\n", + numThreads, optionArgument); + return Status::Failure; + } + break; + } + case 'o': { + finished = true; + const char *optionArgument = getArgument(options, argv, i, argc); + if (optionArgument == nullptr) { + return Status::Failure; + } + outputFile = optionArgument; + break; + } + case 'C': + checksum = true; + break; + case 'k': + keepSource = true; + break; case 'd': decompress = true; break; case 'f': overwrite = true; + forceStdout = true; break; - case 'o': - if (++i == argc) { - std::fprintf(stderr, "Invalid argument: -o requires an argument.\n"); - return false; - } - outputFile = argv[i]; + case 't': + test = true; + decompress = true; break; +#ifdef UTIL_HAS_CREATEFILELIST + case 'r': + recursive = true; + break; +#endif case 'c': - outputFile = '-'; + outputFile = kStdOut; + forceStdout = true; break; + case 'v': + ++verbosity; + break; + case 'q': + --verbosity; + // Ignore them for now + break; + // Unsupported options from Zstd + case 'D': + case 's': + notSupported("Zstd dictionaries."); + return Status::Failure; + case 'b': + case 'e': + case 'i': + case 'B': + notSupported("Zstd benchmarking options."); + return Status::Failure; default: - std::fprintf(stderr, "Invalid argument: %s.\n", arg); - return false; - } - } - // Determine input file if not specified - if (inputFile.empty()) { - inputFile = "-"; - } - // Determine output file if not specified - if (outputFile.empty()) { - if (inputFile == "-") { - outputFile = "-"; - } else { - // Attempt to add/remove zstd extension from the input file - if (decompress) { - int stemSize = inputFile.size() - zstdExtension.size(); - if (stemSize > 0 && inputFile.substr(stemSize) == zstdExtension) { - outputFile = inputFile.substr(0, stemSize); - } else { - std::fprintf( - stderr, "Invalid argument: Unable to determine output file.\n"); - return false; - } - } else { - outputFile = inputFile + zstdExtension; + std::fprintf(stderr, "Invalid argument: %s\n", arg); + return Status::Failure; } + if (!finished) { + ++options; + } + } // while (*options != 0); + } // for (int i = 1; i < argc; ++i); + + // Input file defaults to standard input if not provided. + if (localInputFiles.empty()) { + localInputFiles.emplace_back(kStdIn); + } + + // Check validity of input files + if (localInputFiles.size() > 1) { + const auto it = std::find(localInputFiles.begin(), localInputFiles.end(), + std::string{kStdIn}); + if (it != localInputFiles.end()) { + std::fprintf( + stderr, + "Cannot specify standard input when handling multiple files\n"); + return Status::Failure; } } + if (localInputFiles.size() > 1 || recursive) { + if (!outputFile.empty() && outputFile != nullOutput) { + std::fprintf( + stderr, + "Cannot specify an output file when handling multiple inputs\n"); + return Status::Failure; + } + } + + // Translate input files/directories into files to (de)compress + if (recursive) { + char *scratchBuffer = nullptr; + unsigned numFiles = 0; + const char **files = + UTIL_createFileList(localInputFiles.data(), localInputFiles.size(), + &scratchBuffer, &numFiles); + if (files == nullptr) { + std::fprintf(stderr, "Error traversing directories\n"); + return Status::Failure; + } + auto guard = + makeScopeGuard([&] { UTIL_freeFileList(files, scratchBuffer); }); + if (numFiles == 0) { + std::fprintf(stderr, "No files found\n"); + return Status::Failure; + } + inputFiles.resize(numFiles); + std::copy(files, files + numFiles, inputFiles.begin()); + } else { + inputFiles.resize(localInputFiles.size()); + std::copy(localInputFiles.begin(), localInputFiles.end(), + inputFiles.begin()); + } + localInputFiles.clear(); + assert(!inputFiles.empty()); + + // If reading from standard input, default to standard output + if (inputFiles[0] == kStdIn && outputFile.empty()) { + assert(inputFiles.size() == 1); + outputFile = "-"; + } + + if (inputFiles[0] == kStdIn && IS_CONSOLE(stdin)) { + assert(inputFiles.size() == 1); + std::fprintf(stderr, "Cannot read input from interactive console\n"); + return Status::Failure; + } + if (outputFile == "-" && IS_CONSOLE(stdout) && !(forceStdout && decompress)) { + std::fprintf(stderr, "Will not write to console stdout unless -c or -f is " + "specified and decompressing\n"); + return Status::Failure; + } + // Check compression level { - unsigned maxCLevel = ultra ? ZSTD_maxCLevel() : maxNonUltraCompressionLevel; - if (compressionLevel > maxCLevel) { - std::fprintf( - stderr, "Invalid compression level %u.\n", compressionLevel); - return false; + unsigned maxCLevel = + ultra ? ZSTD_maxCLevel() : kMaxNonUltraCompressionLevel; + if (compressionLevel > maxCLevel || compressionLevel == 0) { + std::fprintf(stderr, "Invalid compression level %u.\n", compressionLevel); + return Status::Failure; } } + // Check that numThreads is set if (numThreads == 0) { - numThreads = std::thread::hardware_concurrency(); - if (numThreads == 0) { - std::fprintf(stderr, "Invalid arguments: # of threads not specified " - "and unable to determine hardware concurrency.\n"); - return false; + std::fprintf(stderr, "Invalid arguments: # of threads not specified " + "and unable to determine hardware concurrency.\n"); + return Status::Failure; + } + + // Modify verbosity + // If we are piping input and output, turn off interaction + if (inputFiles[0] == kStdIn && outputFile == kStdOut && verbosity == 2) { + verbosity = 1; + } + // If we are in multi-file mode, turn off interaction + if (inputFiles.size() > 1 && verbosity == 2) { + verbosity = 1; + } + + // Set options for test mode + if (test) { + outputFile = nullOutput; + keepSource = true; + } + return Status::Success; +} + +std::string Options::getOutputFile(const std::string &inputFile) const { + if (!outputFile.empty()) { + return outputFile; + } + // Attempt to add/remove zstd extension from the input file + if (decompress) { + int stemSize = inputFile.size() - kZstdExtension.size(); + if (stemSize > 0 && inputFile.substr(stemSize) == kZstdExtension) { + return inputFile.substr(0, stemSize); + } else { + return ""; } + } else { + return inputFile + kZstdExtension; } - return true; } } diff --git a/contrib/pzstd/Options.h b/contrib/pzstd/Options.h index 47c5f78a6..97c3885ec 100644 --- a/contrib/pzstd/Options.h +++ b/contrib/pzstd/Options.h @@ -14,47 +14,55 @@ #include #include +#include namespace pzstd { struct Options { + enum class WriteMode { Regular, Auto, Sparse }; + unsigned numThreads; unsigned maxWindowLog; unsigned compressionLevel; bool decompress; - std::string inputFile; + std::vector inputFiles; std::string outputFile; bool overwrite; - bool pzstdHeaders; + bool keepSource; + WriteMode writeMode; + bool checksum; + int verbosity; + + enum class Status { + Success, // Successfully parsed options + Failure, // Failure to parse options + Message // Options specified to print a message (e.g. "-h") + }; Options(); - Options( - unsigned numThreads, - unsigned maxWindowLog, - unsigned compressionLevel, - bool decompress, - const std::string& inputFile, - const std::string& outputFile, - bool overwrite, - bool pzstdHeaders) - : numThreads(numThreads), - maxWindowLog(maxWindowLog), - compressionLevel(compressionLevel), - decompress(decompress), - inputFile(inputFile), - outputFile(outputFile), - overwrite(overwrite), - pzstdHeaders(pzstdHeaders) {} + Options(unsigned numThreads, unsigned maxWindowLog, unsigned compressionLevel, + bool decompress, std::vector inputFiles, + std::string outputFile, bool overwrite, bool keepSource, + WriteMode writeMode, bool checksum, int verbosity) + : numThreads(numThreads), maxWindowLog(maxWindowLog), + compressionLevel(compressionLevel), decompress(decompress), + inputFiles(std::move(inputFiles)), outputFile(std::move(outputFile)), + overwrite(overwrite), keepSource(keepSource), writeMode(writeMode), + checksum(checksum), verbosity(verbosity) {} - bool parse(int argc, const char** argv); + Status parse(int argc, const char **argv); ZSTD_parameters determineParameters() const { ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, 0); + params.fParams.contentSizeFlag = 1; + params.fParams.checksumFlag = checksum; if (maxWindowLog != 0 && params.cParams.windowLog > maxWindowLog) { params.cParams.windowLog = maxWindowLog; params.cParams = ZSTD_adjustCParams(params.cParams, 0, 0); } return params; } + + std::string getOutputFile(const std::string &inputFile) const; }; } diff --git a/contrib/pzstd/Pzstd.cpp b/contrib/pzstd/Pzstd.cpp index 87c4c202f..e0826b9d8 100644 --- a/contrib/pzstd/Pzstd.cpp +++ b/contrib/pzstd/Pzstd.cpp @@ -14,11 +14,21 @@ #include "utils/ThreadPool.h" #include "utils/WorkQueue.h" +#include #include #include #include #include +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) +# include /* _O_BINARY */ +# include /* _setmode, _isatty */ +# define SET_BINARY_MODE(file) { if (_setmode(_fileno(file), _O_BINARY) == -1) perror("Cannot set _O_BINARY"); } +#else +# include /* isatty */ +# define SET_BINARY_MODE(file) +#endif + namespace pzstd { namespace { @@ -31,44 +41,30 @@ const std::string nullOutput = "/dev/null"; using std::size_t; -size_t pzstdMain(const Options& options, ErrorHolder& errorHolder) { - // Open the input file and attempt to determine its size - FILE* inputFd = stdin; - std::uintmax_t inputSize = 0; - if (options.inputFile != "-") { - inputFd = std::fopen(options.inputFile.c_str(), "rb"); - if (!errorHolder.check(inputFd != nullptr, "Failed to open input file")) { - return 0; - } - std::error_code ec; - inputSize = file_size(options.inputFile, ec); - if (ec) { - inputSize = 0; - } +static std::uintmax_t fileSizeOrZero(const std::string &file) { + if (file == "-") { + return 0; } - auto closeInputGuard = makeScopeGuard([&] { std::fclose(inputFd); }); - - // Check if the output file exists and then open it - FILE* outputFd = stdout; - if (options.outputFile != "-") { - if (!options.overwrite && options.outputFile != nullOutput) { - outputFd = std::fopen(options.outputFile.c_str(), "rb"); - if (!errorHolder.check(outputFd == nullptr, "Output file exists")) { - return 0; - } - } - outputFd = std::fopen(options.outputFile.c_str(), "wb"); - if (!errorHolder.check( - outputFd != nullptr, "Failed to open output file")) { - return 0; - } + std::error_code ec; + auto size = file_size(file, ec); + if (ec) { + size = 0; } - auto closeOutputGuard = makeScopeGuard([&] { std::fclose(outputFd); }); + return size; +} +static std::uint64_t handleOneInput(const Options &options, + const std::string &inputFile, + FILE* inputFd, + const std::string &outputFile, + FILE* outputFd, + ErrorHolder &errorHolder) { + auto inputSize = fileSizeOrZero(inputFile); // WorkQueue outlives ThreadPool so in the case of error we are certain // we don't accidently try to call push() on it after it is destroyed. - WorkQueue> outs{2 * options.numThreads}; - size_t bytesWritten; + WorkQueue> outs{options.numThreads + 1}; + std::uint64_t bytesRead; + std::uint64_t bytesWritten; { // Initialize the thread pool with numThreads + 1 // We add one because the read thread spends most of its time waiting. @@ -78,8 +74,9 @@ size_t pzstdMain(const Options& options, ErrorHolder& errorHolder) { if (!options.decompress) { // Add a job that reads the input and starts all the compression jobs executor.add( - [&errorHolder, &outs, &executor, inputFd, inputSize, &options] { - asyncCompressChunks( + [&errorHolder, &outs, &executor, inputFd, inputSize, &options, + &bytesRead] { + bytesRead = asyncCompressChunks( errorHolder, outs, executor, @@ -89,21 +86,152 @@ size_t pzstdMain(const Options& options, ErrorHolder& errorHolder) { options.determineParameters()); }); // Start writing - bytesWritten = - writeFile(errorHolder, outs, outputFd, options.pzstdHeaders); + bytesWritten = writeFile(errorHolder, outs, outputFd, options.decompress, + options.verbosity); } else { // Add a job that reads the input and starts all the decompression jobs - executor.add([&errorHolder, &outs, &executor, inputFd] { - asyncDecompressFrames(errorHolder, outs, executor, inputFd); + executor.add([&errorHolder, &outs, &executor, inputFd, &bytesRead] { + bytesRead = asyncDecompressFrames(errorHolder, outs, executor, inputFd); }); // Start writing - bytesWritten = writeFile( - errorHolder, outs, outputFd, /* writeSkippableFrames */ false); + bytesWritten = writeFile(errorHolder, outs, outputFd, options.decompress, + options.verbosity); + } + } + if (options.verbosity > 1 && !errorHolder.hasError()) { + std::string inputFileName = inputFile == "-" ? "stdin" : inputFile; + std::string outputFileName = outputFile == "-" ? "stdout" : outputFile; + if (!options.decompress) { + double ratio = static_cast(bytesWritten) / + static_cast(bytesRead + !bytesRead); + std::fprintf(stderr, "%-20s :%6.2f%% (%6llu => %6llu bytes, %s)\n", + inputFileName.c_str(), ratio * 100, bytesRead, bytesWritten, + outputFileName.c_str()); + } else { + std::fprintf(stderr, "%-20s: %llu bytes \n", + inputFileName.c_str(),bytesWritten); } } return bytesWritten; } +static FILE *openInputFile(const std::string &inputFile, + ErrorHolder &errorHolder) { + if (inputFile == "-") { + SET_BINARY_MODE(stdin); + return stdin; + } + // Check if input file is a directory + { + std::error_code ec; + if (is_directory(inputFile, ec)) { + errorHolder.setError("Output file is a directory -- ignored"); + return nullptr; + } + } + auto inputFd = std::fopen(inputFile.c_str(), "rb"); + if (!errorHolder.check(inputFd != nullptr, "Failed to open input file")) { + return nullptr; + } + return inputFd; +} + +static FILE *openOutputFile(const Options &options, + const std::string &outputFile, + ErrorHolder &errorHolder) { + if (outputFile == "-") { + SET_BINARY_MODE(stdout); + return stdout; + } + // Check if the output file exists and then open it + if (!options.overwrite && outputFile != nullOutput) { + auto outputFd = std::fopen(outputFile.c_str(), "rb"); + if (outputFd != nullptr) { + std::fclose(outputFd); + if (options.verbosity <= 1) { + errorHolder.setError("Output file exists"); + return nullptr; + } + std::fprintf( + stderr, + "pzstd: %s already exists; do you wish to overwrite (y/n) ? ", + outputFile.c_str()); + int c = getchar(); + if (c != 'y' && c != 'Y') { + errorHolder.setError("Not overwritten"); + return nullptr; + } + } + } + auto outputFd = std::fopen(outputFile.c_str(), "wb"); + if (!errorHolder.check( + outputFd != nullptr, "Failed to open output file")) { + return 0; + } + return outputFd; +} + +int pzstdMain(const Options &options) { + int returnCode = 0; + for (const auto& input : options.inputFiles) { + // Setup the error holder + ErrorHolder errorHolder; + auto printErrorGuard = makeScopeGuard([&] { + if (errorHolder.hasError()) { + returnCode = 1; + if (options.verbosity > 0) { + std::fprintf(stderr, "pzstd: %s: %s.\n", input.c_str(), + errorHolder.getError().c_str()); + } + } else { + + } + }); + // Open the input file + auto inputFd = openInputFile(input, errorHolder); + if (inputFd == nullptr) { + continue; + } + auto closeInputGuard = makeScopeGuard([&] { std::fclose(inputFd); }); + // Open the output file + auto outputFile = options.getOutputFile(input); + if (!errorHolder.check(outputFile != "", + "Input file does not have extension .zst")) { + continue; + } + auto outputFd = openOutputFile(options, outputFile, errorHolder); + if (outputFd == nullptr) { + continue; + } + auto closeOutputGuard = makeScopeGuard([&] { std::fclose(outputFd); }); + // (de)compress the file + handleOneInput(options, input, inputFd, outputFile, outputFd, errorHolder); + if (errorHolder.hasError()) { + continue; + } + // Delete the input file if necessary + if (!options.keepSource) { + // Be sure that we are done and have written everything before we delete + if (!errorHolder.check(std::fclose(inputFd) == 0, + "Failed to close input file")) { + continue; + } + closeInputGuard.dismiss(); + if (!errorHolder.check(std::fclose(outputFd) == 0, + "Failed to close output file")) { + continue; + } + closeOutputGuard.dismiss(); + if (std::remove(input.c_str()) != 0) { + errorHolder.setError("Failed to remove input file"); + continue; + } + } + } + // Returns 1 if any of the files failed to (de)compress. + return returnCode; +} + /// Construct a `ZSTD_inBuffer` that points to the data in `buffer`. static ZSTD_inBuffer makeZstdInBuffer(const Buffer& buffer) { return ZSTD_inBuffer{buffer.data(), buffer.size(), 0}; @@ -224,10 +352,9 @@ static size_t calculateStep( size_t step = size_t{1} << (params.cParams.windowLog + 2); // If file size is known, see if a smaller step will spread work more evenly if (size != 0) { - const std::uintmax_t newStep = size / std::uintmax_t{numThreads}; - if (newStep != 0 && - newStep <= std::uintmax_t{std::numeric_limits::max()}) { - step = std::min(step, size_t{newStep}); + const std::uintmax_t newStep = size / numThreads; + if (newStep != 0 && newStep <= std::numeric_limits::max()) { + step = std::min(step, static_cast(newStep)); } } return step; @@ -252,11 +379,13 @@ FileStatus fileStatus(FILE* fd) { * Returns the status of the file after all of the reads have occurred. */ static FileStatus -readData(BufferWorkQueue& queue, size_t chunkSize, size_t size, FILE* fd) { +readData(BufferWorkQueue& queue, size_t chunkSize, size_t size, FILE* fd, + std::uint64_t *totalBytesRead) { Buffer buffer(size); while (!buffer.empty()) { auto bytesRead = std::fread(buffer.data(), 1, std::min(chunkSize, buffer.size()), fd); + *totalBytesRead += bytesRead; queue.push(buffer.splitAt(bytesRead)); auto status = fileStatus(fd); if (status != FileStatus::Continue) { @@ -266,7 +395,7 @@ readData(BufferWorkQueue& queue, size_t chunkSize, size_t size, FILE* fd) { return FileStatus::Continue; } -void asyncCompressChunks( +std::uint64_t asyncCompressChunks( ErrorHolder& errorHolder, WorkQueue>& chunks, ThreadPool& executor, @@ -275,6 +404,7 @@ void asyncCompressChunks( size_t numThreads, ZSTD_parameters params) { auto chunksGuard = makeScopeGuard([&] { chunks.finish(); }); + std::uint64_t bytesRead = 0; // Break the input up into chunks of size `step` and compress each chunk // independently. @@ -294,9 +424,10 @@ void asyncCompressChunks( // Pass the output queue to the writer thread. chunks.push(std::move(out)); // Fill the input queue for the compression job we just started - status = readData(*in, ZSTD_CStreamInSize(), step, fd); + status = readData(*in, ZSTD_CStreamInSize(), step, fd, &bytesRead); } errorHolder.check(status != FileStatus::Error, "Error reading input"); + return bytesRead; } /** @@ -377,12 +508,14 @@ static void decompress( } } -void asyncDecompressFrames( +std::uint64_t asyncDecompressFrames( ErrorHolder& errorHolder, WorkQueue>& frames, ThreadPool& executor, FILE* fd) { auto framesGuard = makeScopeGuard([&] { frames.finish(); }); + std::uint64_t totalBytesRead = 0; + // Split the source up into its component frames. // If we find our recognized skippable frame we know the next frames size // which means that we can decompress each standard frame in independently. @@ -402,6 +535,7 @@ void asyncDecompressFrames( // frameSize is 0 if the frame info can't be decoded. Buffer buffer(SkippableFrame::kSize); auto bytesRead = std::fread(buffer.data(), 1, buffer.size(), fd); + totalBytesRead += bytesRead; status = fileStatus(fd); if (bytesRead == 0 && status != FileStatus::Continue) { break; @@ -426,14 +560,15 @@ void asyncDecompressFrames( // We hit a non SkippableFrame ==> not compressed by pzstd or corrupted // Pass the rest of the source to this decompression task while (status == FileStatus::Continue && !errorHolder.hasError()) { - status = readData(*in, chunkSize, chunkSize, fd); + status = readData(*in, chunkSize, chunkSize, fd, &totalBytesRead); } break; } // Fill the input queue for the decompression job we just started - status = readData(*in, chunkSize, frameSize, fd); + status = readData(*in, chunkSize, frameSize, fd, &totalBytesRead); } errorHolder.check(status != FileStatus::Error, "Error reading input"); + return totalBytesRead; } /// Write `data` to `fd`, returns true iff success. @@ -447,16 +582,38 @@ static bool writeData(ByteRange data, FILE* fd) { return true; } -size_t writeFile( +void updateWritten(int verbosity, std::uint64_t bytesWritten) { + if (verbosity <= 1) { + return; + } + using Clock = std::chrono::system_clock; + static Clock::time_point then; + constexpr std::chrono::milliseconds refreshRate{150}; + + auto now = Clock::now(); + if (now - then > refreshRate) { + then = now; + std::fprintf(stderr, "\rWritten: %u MB ", + static_cast(bytesWritten >> 20)); + } +} + +std::uint64_t writeFile( ErrorHolder& errorHolder, WorkQueue>& outs, FILE* outputFd, - bool writeSkippableFrames) { - size_t bytesWritten = 0; + bool decompress, + int verbosity) { + auto lineClearGuard = makeScopeGuard([verbosity] { + if (verbosity > 1) { + std::fprintf(stderr, "\r%79s\r", ""); + } + }); + std::uint64_t bytesWritten = 0; std::shared_ptr out; // Grab the output queue for each decompression job (in order). while (outs.pop(out) && !errorHolder.hasError()) { - if (writeSkippableFrames) { + if (!decompress) { // If we are compressing and want to write skippable frames we can't // start writing before compression is done because we need to know the // compressed size. @@ -476,6 +633,7 @@ size_t writeFile( return bytesWritten; } bytesWritten += buffer.size(); + updateWritten(verbosity, bytesWritten); } } return bytesWritten; diff --git a/contrib/pzstd/Pzstd.h b/contrib/pzstd/Pzstd.h index 51d15846c..fe44ccfde 100644 --- a/contrib/pzstd/Pzstd.h +++ b/contrib/pzstd/Pzstd.h @@ -28,11 +28,9 @@ namespace pzstd { * An error occurred if `errorHandler.hasError()`. * * @param options The pzstd options to use for (de)compression - * @param errorHolder Used to report errors and coordinate early shutdown - * if an error occured - * @returns The number of bytes written. + * @returns 0 upon success and non-zero on failure. */ -std::size_t pzstdMain(const Options& options, ErrorHolder& errorHolder); +int pzstdMain(const Options& options); /** * Streams input from `fd`, breaks input up into chunks, and compresses each @@ -47,8 +45,9 @@ std::size_t pzstdMain(const Options& options, ErrorHolder& errorHolder); * @param size The size of the input file if known, 0 otherwise * @param numThreads The number of threads in the thread pool * @param parameters The zstd parameters to use for compression + * @returns The number of bytes read from the file */ -void asyncCompressChunks( +std::uint64_t asyncCompressChunks( ErrorHolder& errorHolder, WorkQueue>& chunks, ThreadPool& executor, @@ -68,8 +67,9 @@ void asyncCompressChunks( * as soon as it is available * @param executor The thread pool to run compression jobs in * @param fd The input file descriptor + * @returns The number of bytes read from the file */ -void asyncDecompressFrames( +std::uint64_t asyncDecompressFrames( ErrorHolder& errorHolder, WorkQueue>& frames, ThreadPool& executor, @@ -79,16 +79,18 @@ void asyncDecompressFrames( * Streams input in from each queue in `outs` in order, and writes the data to * `outputFd`. * - * @param errorHolder Used to report errors and coordinate early exit - * @param outs A queue of output queues, one for each - * (de)compression job. - * @param outputFd The file descriptor to write to - * @param writeSkippableFrames Should we write pzstd headers? - * @returns The number of bytes written + * @param errorHolder Used to report errors and coordinate early exit + * @param outs A queue of output queues, one for each + * (de)compression job. + * @param outputFd The file descriptor to write to + * @param decompress Are we decompressing? + * @param verbosity The verbosity level to log at + * @returns The number of bytes written */ -std::size_t writeFile( +std::uint64_t writeFile( ErrorHolder& errorHolder, WorkQueue>& outs, FILE* outputFd, - bool writeSkippableFrames); + bool decompress, + int verbosity); } diff --git a/contrib/pzstd/README.md b/contrib/pzstd/README.md index eba64085a..05ceb5599 100644 --- a/contrib/pzstd/README.md +++ b/contrib/pzstd/README.md @@ -4,24 +4,31 @@ Parallel Zstandard is a Pigz-like tool for Zstandard. It provides Zstandard format compatible compression and decompression that is able to utilize multiple cores. It breaks the input up into equal sized chunks and compresses each chunk independently into a Zstandard frame. It then concatenates the frames together to produce the final compressed output. -Optionally, with the `-p` option, PZstandard will write a 12 byte header for each frame that is a skippable frame in the Zstandard format, which tells PZstandard the size of the next compressed frame. -When `-p` is specified for compression, PZstandard can decompress the output in parallel. +Pzstandard will write a 12 byte header for each frame that is a skippable frame in the Zstandard format, which tells PZstandard the size of the next compressed frame. +PZstandard supports parallel decompression of files compressed with PZstandard. +When decompressing files compressed with Zstandard, PZstandard does IO in one thread, and decompression in another. ## Usage +PZstandard supports the same command line interface as Zstandard, but also provies the `-p` option to specify the number of threads. +Dictionary mode is not currently supported. + Basic usage - pzstd input-file -o output-file -n num-threads [ -p ] -# # Compression - pzstd -d input-file -o output-file -n num-threads # Decompression + pzstd input-file -o output-file -p num-threads -# # Compression + pzstd -d input-file -o output-file -p num-threads # Decompression PZstandard also supports piping and fifo pipes - cat input-file | pzstd -n num-threads [ -p ] -# -c > /dev/null + cat input-file | pzstd -p num-threads -# -c > /dev/null For more options pzstd --help +PZstandard tries to pick a smart default number of threads if not specified (displayed in `pzstd --help`). +If this number is not suitable, during compilation you can define `PZSTD_NUM_THREADS` to the number of threads you prefer. + ## Benchmarks As a reference, PZstandard and Pigz were compared on an Intel Core i7 @ 3.1 GHz, each using 4 threads, with the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia). @@ -32,8 +39,8 @@ Compression Speed vs Ratio with 4 Threads | Decompression Speed with 4 Threads The test procedure was to run each of the following commands 2 times for each compression level, and take the minimum time. - time pzstd -# -n 4 -p -c silesia.tar > silesia.tar.zst - time pzstd -d -n 4 -c silesia.tar.zst > /dev/null + time pzstd -# -p 4 -c silesia.tar > silesia.tar.zst + time pzstd -d -p 4 -c silesia.tar.zst > /dev/null time pigz -# -p 4 -k -c silesia.tar > silesia.tar.gz time pigz -d -p 4 -k -c silesia.tar.gz > /dev/null diff --git a/contrib/pzstd/main.cpp b/contrib/pzstd/main.cpp index 7ff2cef74..279cbfb5e 100644 --- a/contrib/pzstd/main.cpp +++ b/contrib/pzstd/main.cpp @@ -19,16 +19,14 @@ using namespace pzstd; int main(int argc, const char** argv) { Options options; - if (!options.parse(argc, argv)) { + switch (options.parse(argc, argv)) { + case Options::Status::Failure: return 1; + case Options::Status::Message: + return 0; + default: + break; } - ErrorHolder errorHolder; - pzstdMain(options, errorHolder); - - if (errorHolder.hasError()) { - std::fprintf(stderr, "Error: %s.\n", errorHolder.getError().c_str()); - return 1; - } - return 0; + return pzstdMain(options); } diff --git a/contrib/pzstd/test/Makefile b/contrib/pzstd/test/Makefile index 5fd167d18..4f6ba9997 100644 --- a/contrib/pzstd/test/Makefile +++ b/contrib/pzstd/test/Makefile @@ -21,19 +21,19 @@ ZSTDDIR = ../../../lib # Set GTEST_INC and GTEST_LIB to work with your install of gtest GTEST_INC ?= -isystem $(PZSTDDIR)/googletest/googletest/include GTEST_LIB ?= -L $(PZSTDDIR)/googletest/build/googlemock/gtest - -CPPFLAGS = -I$(PZSTDDIR) $(GTEST_INC) $(GTEST_LIB) -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(PROGDIR) -I. +GTEST_FLAGS = $(GTEST_INC) $(GTEST_LIB) +CPPFLAGS = -I$(PZSTDDIR) -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(PROGDIR) -I. CXXFLAGS ?= -O3 -CXXFLAGS += -std=c++11 +CXXFLAGS += -std=c++11 -Wno-deprecated-declarations CXXFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) datagen.o: $(PROGDIR)/datagen.* - $(CXX) $(FLAGS) $(PROGDIR)/datagen.c -c -o $@ + $(CC) $(CPPFLAGS) -O3 $(MOREFLAGS) $(LDFLAGS) -Wno-long-long -Wno-variadic-macros $(PROGDIR)/datagen.c -c -o $@ %: %.cpp *.h datagen.o - $(CXX) $(FLAGS) $@.cpp datagen.o $(PZSTDDIR)/Pzstd.o $(PZSTDDIR)/SkippableFrame.o $(PZSTDDIR)/Options.o $(PZSTDDIR)/libzstd.a -o $@$(EXT) -lgtest -lgtest_main -lpthread + $(CXX) $(FLAGS) $@.cpp datagen.o $(PZSTDDIR)/Pzstd.o $(PZSTDDIR)/SkippableFrame.o $(PZSTDDIR)/Options.o $(PZSTDDIR)/libzstd.a -o $@$(EXT) $(GTEST_FLAGS) -lgtest -lgtest_main -lpthread .PHONY: test clean diff --git a/contrib/pzstd/test/OptionsTest.cpp b/contrib/pzstd/test/OptionsTest.cpp index b87358c04..e7d4b2b3e 100644 --- a/contrib/pzstd/test/OptionsTest.cpp +++ b/contrib/pzstd/test/OptionsTest.cpp @@ -8,172 +8,535 @@ */ #include "Options.h" -#include #include +#include using namespace pzstd; namespace pzstd { -bool operator==(const Options& lhs, const Options& rhs) { +bool operator==(const Options &lhs, const Options &rhs) { return lhs.numThreads == rhs.numThreads && - lhs.maxWindowLog == rhs.maxWindowLog && - lhs.compressionLevel == rhs.compressionLevel && - lhs.decompress == rhs.decompress && lhs.inputFile == rhs.inputFile && - lhs.outputFile == rhs.outputFile && lhs.overwrite == rhs.overwrite && - lhs.pzstdHeaders == rhs.pzstdHeaders; + lhs.maxWindowLog == rhs.maxWindowLog && + lhs.compressionLevel == rhs.compressionLevel && + lhs.decompress == rhs.decompress && lhs.inputFiles == rhs.inputFiles && + lhs.outputFile == rhs.outputFile && lhs.overwrite == rhs.overwrite && + lhs.keepSource == rhs.keepSource && lhs.writeMode == rhs.writeMode && + lhs.checksum == rhs.checksum && lhs.verbosity == rhs.verbosity; } + +std::ostream &operator<<(std::ostream &out, const Options &opt) { + out << "{"; + { + out << "\n\t" + << "numThreads: " << opt.numThreads; + out << ",\n\t" + << "maxWindowLog: " << opt.maxWindowLog; + out << ",\n\t" + << "compressionLevel: " << opt.compressionLevel; + out << ",\n\t" + << "decompress: " << opt.decompress; + out << ",\n\t" + << "inputFiles: {"; + { + bool first = true; + for (const auto &file : opt.inputFiles) { + if (!first) { + out << ","; + } + first = false; + out << "\n\t\t" << file; + } + } + out << "\n\t}"; + out << ",\n\t" + << "outputFile: " << opt.outputFile; + out << ",\n\t" + << "overwrite: " << opt.overwrite; + out << ",\n\t" + << "keepSource: " << opt.keepSource; + out << ",\n\t" + << "writeMode: " << static_cast(opt.writeMode); + out << ",\n\t" + << "checksum: " << opt.checksum; + out << ",\n\t" + << "verbosity: " << opt.verbosity; + } + out << "\n}"; + return out; +} +} + +namespace { +#ifdef _WIN32 +const char nullOutput[] = "nul"; +#else +const char nullOutput[] = "/dev/null"; +#endif + +constexpr auto autoMode = Options::WriteMode::Auto; +} // anonymous namespace + +#define EXPECT_SUCCESS(...) EXPECT_EQ(Options::Status::Success, __VA_ARGS__) +#define EXPECT_FAILURE(...) EXPECT_EQ(Options::Status::Failure, __VA_ARGS__) +#define EXPECT_MESSAGE(...) EXPECT_EQ(Options::Status::Message, __VA_ARGS__) + +template +std::array makeArray(Args... args) { + return {{nullptr, args...}}; } TEST(Options, ValidInputs) { { Options options; - std::array args = { - {nullptr, "--num-threads", "5", "-o", "-", "-f"}}; - EXPECT_TRUE(options.parse(args.size(), args.data())); - Options expected = {5, 23, 3, false, "-", "-", true, false}; + auto args = makeArray("--processes", "5", "-o", "x", "y", "-f"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + Options expected = {5, 23, 3, false, {"y"}, "x", + true, true, autoMode, true, 2}; EXPECT_EQ(expected, options); } { Options options; - std::array args = { - {nullptr, "-n", "1", "input", "-19", "-p"}}; - EXPECT_TRUE(options.parse(args.size(), args.data())); - Options expected = {1, 23, 19, false, "input", "input.zst", false, true}; + auto args = makeArray("-p", "1", "input", "-19"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + Options expected = {1, 23, 19, false, {"input"}, "", + false, true, autoMode, true, 2}; EXPECT_EQ(expected, options); } { Options options; - std::array args = {{nullptr, - "--ultra", - "-22", - "-n", - "1", - "--output", - "x", - "-d", - "x.zst", - "-f"}}; - EXPECT_TRUE(options.parse(args.size(), args.data())); - Options expected = {1, 0, 22, true, "x.zst", "x", true, false}; + auto args = + makeArray("--ultra", "-22", "-p", "1", "-o", "x", "-d", "x.zst", "-f"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + Options expected = {1, 0, 22, true, {"x.zst"}, "x", + true, true, autoMode, true, 2}; EXPECT_EQ(expected, options); } { Options options; - std::array args = {{nullptr, - "--num-threads", - "100", - "hello.zst", - "--decompress", - "--force"}}; - EXPECT_TRUE(options.parse(args.size(), args.data())); - Options expected = {100, 23, 3, true, "hello.zst", "hello", true, false}; + auto args = makeArray("--processes", "100", "hello.zst", "--decompress", + "--force"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + Options expected = {100, 23, 3, true, {"hello.zst"}, "", true, + true, autoMode, true, 2}; EXPECT_EQ(expected, options); } { Options options; - std::array args = {{nullptr, "-", "-n", "1", "-c"}}; - EXPECT_TRUE(options.parse(args.size(), args.data())); - Options expected = {1, 23, 3, false, "-", "-", false, false}; + auto args = makeArray("x", "-dp", "1", "-c"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + Options expected = {1, 23, 3, true, {"x"}, "-", + false, true, autoMode, true, 2}; EXPECT_EQ(expected, options); } { Options options; - std::array args = {{nullptr, "-", "-n", "1", "--stdout"}}; - EXPECT_TRUE(options.parse(args.size(), args.data())); - Options expected = {1, 23, 3, false, "-", "-", false, false}; + auto args = makeArray("x", "-dp", "1", "--stdout"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + Options expected = {1, 23, 3, true, {"x"}, "-", + false, true, autoMode, true, 2}; EXPECT_EQ(expected, options); } { Options options; - std::array args = {{nullptr, - "-n", - "1", - "-", - "-5", - "-o", - "-", - "-u", - "-d", - "--pzstd-headers"}}; - EXPECT_TRUE(options.parse(args.size(), args.data())); - Options expected = {1, 0, 5, true, "-", "-", false, true}; + auto args = makeArray("-p", "1", "x", "-5", "-fo", "-", "--ultra", "-d"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + Options expected = {1, 0, 5, true, {"x"}, "-", + true, true, autoMode, true, 2}; + EXPECT_EQ(expected, options); } { Options options; - std::array args = { - {nullptr, "silesia.tar", "-o", "silesia.tar.pzstd", "-n", "2"}}; - EXPECT_TRUE(options.parse(args.size(), args.data())); - Options expected = { - 2, 23, 3, false, "silesia.tar", "silesia.tar.pzstd", false, false}; + auto args = makeArray("silesia.tar", "-o", "silesia.tar.pzstd", "-p", "2"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + Options expected = {2, + 23, + 3, + false, + {"silesia.tar"}, + "silesia.tar.pzstd", + false, + true, + autoMode, + true, + 2}; + EXPECT_EQ(expected, options); } { Options options; - std::array args = {{nullptr, "-n", "1"}}; - EXPECT_TRUE(options.parse(args.size(), args.data())); + auto args = makeArray("x", "-p", "1"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); } { Options options; - std::array args = {{nullptr, "-", "-n", "1"}}; - EXPECT_TRUE(options.parse(args.size(), args.data())); + auto args = makeArray("x", "-p", "1"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + } +} + +TEST(Options, GetOutputFile) { + { + Options options; + auto args = makeArray("x"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ("x.zst", options.getOutputFile(options.inputFiles[0])); + } + { + Options options; + auto args = makeArray("-o-"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + EXPECT_EQ("-", options.getOutputFile(options.inputFiles[0])); + } + { + Options options; + auto args = makeArray("x", "y", "-o", nullOutput); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(nullOutput, options.getOutputFile(options.inputFiles[0])); + } + { + Options options; + auto args = makeArray("x.zst", "-do", nullOutput); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(nullOutput, options.getOutputFile(options.inputFiles[0])); + } + { + Options options; + auto args = makeArray("x.zst", "-d"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ("x", options.getOutputFile(options.inputFiles[0])); + } + { + Options options; + auto args = makeArray("xzst", "-d"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ("", options.getOutputFile(options.inputFiles[0])); + } + { + Options options; + auto args = makeArray("xzst", "-doxx"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ("xx", options.getOutputFile(options.inputFiles[0])); + } +} + +TEST(Options, MultipleFiles) { + { + Options options; + auto args = makeArray("x", "y", "z"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + Options expected; + expected.inputFiles = {"x", "y", "z"}; + expected.verbosity = 1; + EXPECT_EQ(expected, options); + } + { + Options options; + auto args = makeArray("x", "y", "z", "-o", nullOutput); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + Options expected; + expected.inputFiles = {"x", "y", "z"}; + expected.outputFile = nullOutput; + expected.verbosity = 1; + EXPECT_EQ(expected, options); + } + { + Options options; + auto args = makeArray("x", "y", "-o-"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("x", "y", "-o", "file"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("-qqvd12qp4", "-f", "x", "--", "--rm", "-c"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + Options expected = {4, 23, 12, true, {"x", "--rm", "-c"}, + "", true, true, autoMode, true, + 0}; + EXPECT_EQ(expected, options); } } TEST(Options, NumThreads) { { Options options; - std::array args = {{nullptr, "-o", "-"}}; - EXPECT_TRUE(options.parse(args.size(), args.data())); + auto args = makeArray("x", "-dfo", "-"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); } { Options options; - std::array args = {{nullptr, "-n", "0", "-o", "-"}}; - EXPECT_FALSE(options.parse(args.size(), args.data())); + auto args = makeArray("x", "-p", "0", "-fo", "-"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); } { Options options; - std::array args = {{nullptr, "-n", "-o", "-"}}; - EXPECT_FALSE(options.parse(args.size(), args.data())); + auto args = makeArray("-f", "-p", "-o", "-"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); } } TEST(Options, BadCompressionLevel) { { Options options; - std::array args = {{nullptr, "x", "-20"}}; - EXPECT_FALSE(options.parse(args.size(), args.data())); + auto args = makeArray("x", "-20"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); } { Options options; - std::array args = {{nullptr, "x", "-u", "-23"}}; - EXPECT_FALSE(options.parse(args.size(), args.data())); + auto args = makeArray("x", "--ultra", "-23"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("x", "--1"); // negative 1? + EXPECT_FAILURE(options.parse(args.size(), args.data())); } } TEST(Options, InvalidOption) { { Options options; - std::array args = {{nullptr, "x", "-x"}}; - EXPECT_FALSE(options.parse(args.size(), args.data())); + auto args = makeArray("x", "-x"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); } } TEST(Options, BadOutputFile) { { Options options; - std::array args = {{nullptr, "notzst", "-d", "-n", "1"}}; - EXPECT_FALSE(options.parse(args.size(), args.data())); + auto args = makeArray("notzst", "-d", "-p", "1"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ("", options.getOutputFile(options.inputFiles.front())); + } +} + +TEST(Options, BadOptionsWithArguments) { + { + Options options; + auto args = makeArray("x", "-pf"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("x", "-p", "10f"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("x", "-p"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("x", "-o"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("x", "-o"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } +} + +TEST(Options, KeepSource) { + { + Options options; + auto args = makeArray("x", "--rm", "-k"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(true, options.keepSource); + } + { + Options options; + auto args = makeArray("x", "--rm", "--keep"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(true, options.keepSource); + } + { + Options options; + auto args = makeArray("x"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(true, options.keepSource); + } + { + Options options; + auto args = makeArray("x", "--rm"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(false, options.keepSource); + } +} + +TEST(Options, Verbosity) { + { + Options options; + auto args = makeArray("x"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(2, options.verbosity); + } + { + Options options; + auto args = makeArray("--quiet", "-qq", "x"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(-1, options.verbosity); + } + { + Options options; + auto args = makeArray("x", "y"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(1, options.verbosity); + } + { + Options options; + auto args = makeArray("--", "x", "y"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(1, options.verbosity); + } + { + Options options; + auto args = makeArray("-qv", "x", "y"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(1, options.verbosity); + } + { + Options options; + auto args = makeArray("-v", "x", "y"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(3, options.verbosity); + } + { + Options options; + auto args = makeArray("-v", "x"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(3, options.verbosity); + } +} + +TEST(Options, TestMode) { + { + Options options; + auto args = makeArray("x", "-t"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(true, options.keepSource); + EXPECT_EQ(true, options.decompress); + EXPECT_EQ(nullOutput, options.outputFile); + } + { + Options options; + auto args = makeArray("x", "--test", "--rm", "-ohello"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(true, options.keepSource); + EXPECT_EQ(true, options.decompress); + EXPECT_EQ(nullOutput, options.outputFile); + } +} + +TEST(Options, Checksum) { + { + Options options; + auto args = makeArray("x.zst", "--no-check", "-Cd"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(true, options.checksum); + } + { + Options options; + auto args = makeArray("x"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(true, options.checksum); + } + { + Options options; + auto args = makeArray("x", "--no-check", "--check"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(true, options.checksum); + } + { + Options options; + auto args = makeArray("x", "--no-check"); + EXPECT_SUCCESS(options.parse(args.size(), args.data())); + EXPECT_EQ(false, options.checksum); + } +} + +TEST(Options, InputFiles) { + { + Options options; + auto args = makeArray("-cd"); + options.parse(args.size(), args.data()); + EXPECT_EQ(1, options.inputFiles.size()); + EXPECT_EQ("-", options.inputFiles[0]); + EXPECT_EQ("-", options.outputFile); + } + { + Options options; + auto args = makeArray(); + options.parse(args.size(), args.data()); + EXPECT_EQ(1, options.inputFiles.size()); + EXPECT_EQ("-", options.inputFiles[0]); + EXPECT_EQ("-", options.outputFile); + } + { + Options options; + auto args = makeArray("-d"); + options.parse(args.size(), args.data()); + EXPECT_EQ(1, options.inputFiles.size()); + EXPECT_EQ("-", options.inputFiles[0]); + EXPECT_EQ("-", options.outputFile); + } + { + Options options; + auto args = makeArray("x", "-"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } +} + +TEST(Options, InvalidOptions) { + { + Options options; + auto args = makeArray("-ibasdf"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("- "); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("-n15"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("-0", "x"); + EXPECT_FAILURE(options.parse(args.size(), args.data())); } } TEST(Options, Extras) { { Options options; - std::array args = {{nullptr, "-h"}}; - EXPECT_FALSE(options.parse(args.size(), args.data())); + auto args = makeArray("-h"); + EXPECT_MESSAGE(options.parse(args.size(), args.data())); } { Options options; - std::array args = {{nullptr, "-V"}}; - EXPECT_FALSE(options.parse(args.size(), args.data())); + auto args = makeArray("-H"); + EXPECT_MESSAGE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("-V"); + EXPECT_MESSAGE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("--help"); + EXPECT_MESSAGE(options.parse(args.size(), args.data())); + } + { + Options options; + auto args = makeArray("--version"); + EXPECT_MESSAGE(options.parse(args.size(), args.data())); } } diff --git a/contrib/pzstd/test/PzstdTest.cpp b/contrib/pzstd/test/PzstdTest.cpp index 9d1256fa5..c85f73a39 100644 --- a/contrib/pzstd/test/PzstdTest.cpp +++ b/contrib/pzstd/test/PzstdTest.cpp @@ -6,14 +6,16 @@ * 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. */ -#include "datagen.h" #include "Pzstd.h" +extern "C" { +#include "datagen.h" +} #include "test/RoundTrip.h" #include "utils/ScopeGuard.h" -#include #include #include +#include #include #include @@ -25,11 +27,14 @@ TEST(Pzstd, SmallSizes) { std::fprintf(stderr, "Pzstd.SmallSizes seed: %u\n", seed); std::mt19937 gen(seed); - for (unsigned len = 1; len < 1028; ++len) { + for (unsigned len = 1; len < 256; ++len) { + if (len % 16 == 0) { + std::fprintf(stderr, "%u / 16\n", len / 16); + } std::string inputFile = std::tmpnam(nullptr); auto guard = makeScopeGuard([&] { std::remove(inputFile.c_str()); }); { - static uint8_t buf[1028]; + static uint8_t buf[256]; RDG_genBuffer(buf, len, 0.5, 0.0, gen()); auto fd = std::fopen(inputFile.c_str(), "wb"); auto written = std::fwrite(buf, 1, len, fd); @@ -37,21 +42,19 @@ TEST(Pzstd, SmallSizes) { ASSERT_EQ(written, len); } for (unsigned headers = 0; headers <= 1; ++headers) { - for (unsigned numThreads = 1; numThreads <= 4; numThreads *= 2) { - for (unsigned level = 1; level <= 8; level *= 8) { + for (unsigned numThreads = 1; numThreads <= 2; ++numThreads) { + for (unsigned level = 1; level <= 4; level *= 4) { auto errorGuard = makeScopeGuard([&] { - guard.dismiss(); - std::fprintf(stderr, "file: %s\n", inputFile.c_str()); std::fprintf(stderr, "pzstd headers: %u\n", headers); std::fprintf(stderr, "# threads: %u\n", numThreads); std::fprintf(stderr, "compression level: %u\n", level); }); Options options; - options.pzstdHeaders = headers; options.overwrite = true; - options.inputFile = inputFile; + options.inputFiles = {inputFile}; options.numThreads = numThreads; options.compressionLevel = level; + options.verbosity = 1; ASSERT_TRUE(roundTrip(options)); errorGuard.dismiss(); } @@ -80,18 +83,16 @@ TEST(Pzstd, LargeSizes) { for (unsigned numThreads = 1; numThreads <= 16; numThreads *= 4) { for (unsigned level = 1; level <= 4; level *= 2) { auto errorGuard = makeScopeGuard([&] { - guard.dismiss(); - std::fprintf(stderr, "file: %s\n", inputFile.c_str()); std::fprintf(stderr, "pzstd headers: %u\n", headers); std::fprintf(stderr, "# threads: %u\n", numThreads); std::fprintf(stderr, "compression level: %u\n", level); }); Options options; - options.pzstdHeaders = headers; options.overwrite = true; - options.inputFile = inputFile; - options.numThreads = numThreads; + options.inputFiles = {inputFile}; + options.numThreads = std::min(numThreads, options.numThreads); options.compressionLevel = level; + options.verbosity = 1; ASSERT_TRUE(roundTrip(options)); errorGuard.dismiss(); } @@ -100,6 +101,40 @@ TEST(Pzstd, LargeSizes) { } } +TEST(Pzstd, ExtremelyLargeSize) { + unsigned seed = std::random_device{}(); + std::fprintf(stderr, "Pzstd.ExtremelyLargeSize seed: %u\n", seed); + std::mt19937 gen(seed); + + std::string inputFile = std::tmpnam(nullptr); + auto guard = makeScopeGuard([&] { std::remove(inputFile.c_str()); }); + + { + // Write 4GB + 64 MB + constexpr size_t kLength = 1 << 26; + std::unique_ptr buf(new uint8_t[kLength]); + auto fd = std::fopen(inputFile.c_str(), "wb"); + auto closeGuard = makeScopeGuard([&] { std::fclose(fd); }); + for (size_t i = 0; i < (1 << 6) + 1; ++i) { + RDG_genBuffer(buf.get(), kLength, 0.5, 0.0, gen()); + auto written = std::fwrite(buf.get(), 1, kLength, fd); + if (written != kLength) { + std::fprintf(stderr, "Failed to write file, skipping test\n"); + return; + } + } + } + + Options options; + options.overwrite = true; + options.inputFiles = {inputFile}; + options.compressionLevel = 1; + if (options.numThreads == 0) { + options.numThreads = 1; + } + ASSERT_TRUE(roundTrip(options)); +} + TEST(Pzstd, ExtremelyCompressible) { std::string inputFile = std::tmpnam(nullptr); auto guard = makeScopeGuard([&] { std::remove(inputFile.c_str()); }); @@ -112,9 +147,8 @@ TEST(Pzstd, ExtremelyCompressible) { ASSERT_EQ(written, 10000); } Options options; - options.pzstdHeaders = false; options.overwrite = true; - options.inputFile = inputFile; + options.inputFiles = {inputFile}; options.numThreads = 1; options.compressionLevel = 1; ASSERT_TRUE(roundTrip(options)); diff --git a/contrib/pzstd/test/RoundTrip.h b/contrib/pzstd/test/RoundTrip.h index 829c95cac..8b9088459 100644 --- a/contrib/pzstd/test/RoundTrip.h +++ b/contrib/pzstd/test/RoundTrip.h @@ -55,7 +55,10 @@ inline bool check(std::string source, std::string decompressed) { } inline bool roundTrip(Options& options) { - std::string source = options.inputFile; + if (options.inputFiles.size() != 1) { + return false; + } + std::string source = options.inputFiles.front(); std::string compressedFile = std::tmpnam(nullptr); std::string decompressedFile = std::tmpnam(nullptr); auto guard = makeScopeGuard([&] { @@ -66,21 +69,15 @@ inline bool roundTrip(Options& options) { { options.outputFile = compressedFile; options.decompress = false; - ErrorHolder errorHolder; - pzstdMain(options, errorHolder); - if (errorHolder.hasError()) { - errorHolder.getError(); + if (pzstdMain(options) != 0) { return false; } } { options.decompress = true; - options.inputFile = compressedFile; + options.inputFiles.front() = compressedFile; options.outputFile = decompressedFile; - ErrorHolder errorHolder; - pzstdMain(options, errorHolder); - if (errorHolder.hasError()) { - errorHolder.getError(); + if (pzstdMain(options) != 0) { return false; } } diff --git a/contrib/pzstd/test/RoundTripTest.cpp b/contrib/pzstd/test/RoundTripTest.cpp index 01c1c8113..ed2ea770c 100644 --- a/contrib/pzstd/test/RoundTripTest.cpp +++ b/contrib/pzstd/test/RoundTripTest.cpp @@ -6,7 +6,9 @@ * 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. */ +extern "C" { #include "datagen.h" +} #include "Options.h" #include "test/RoundTrip.h" #include "utils/ScopeGuard.h" @@ -46,14 +48,12 @@ string generateInputFile(Generator& gen) { template Options generateOptions(Generator& gen, const string& inputFile) { Options options; - options.inputFile = inputFile; + options.inputFiles = {inputFile}; options.overwrite = true; - std::bernoulli_distribution pzstdHeaders{0.75}; std::uniform_int_distribution numThreads{1, 32}; std::uniform_int_distribution compressionLevel{1, 10}; - options.pzstdHeaders = pzstdHeaders(gen); options.numThreads = numThreads(gen); options.compressionLevel = compressionLevel(gen); @@ -61,7 +61,7 @@ Options generateOptions(Generator& gen, const string& inputFile) { } } -int main(int argc, char** argv) { +int main() { std::mt19937 gen(std::random_device{}()); auto newlineGuard = makeScopeGuard([] { std::fprintf(stderr, "\n"); }); @@ -77,8 +77,6 @@ int main(int argc, char** argv) { std::fprintf(stderr, "numThreads: %u\n", options.numThreads); std::fprintf(stderr, "level: %u\n", options.compressionLevel); std::fprintf(stderr, "decompress? %u\n", (unsigned)options.decompress); - std::fprintf( - stderr, "pzstd headers? %u\n", (unsigned)options.pzstdHeaders); std::fprintf(stderr, "file: %s\n", inputFile.c_str()); return 1; } diff --git a/contrib/pzstd/utils/FileSystem.h b/contrib/pzstd/utils/FileSystem.h index 979c82b7a..7d597047f 100644 --- a/contrib/pzstd/utils/FileSystem.h +++ b/contrib/pzstd/utils/FileSystem.h @@ -21,10 +21,11 @@ namespace pzstd { +// using file_status = ... causes gcc to emit a false positive warning #if defined(_MSC_VER) -using file_status = struct ::_stat64; +typedef struct ::_stat64 file_status; #else -using file_status = struct ::stat; +typedef struct ::stat file_status; #endif /// http://en.cppreference.com/w/cpp/filesystem/status @@ -59,6 +60,22 @@ inline bool is_regular_file(StringPiece path, std::error_code& ec) noexcept { return is_regular_file(status(path, ec)); } +/// http://en.cppreference.com/w/cpp/filesystem/is_directory +inline bool is_directory(file_status status) noexcept { +#if defined(S_ISDIR) + return S_ISDIR(status.st_mode); +#elif !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) + return (status.st_mode & S_IFMT) == S_IFDIR; +#else + static_assert(false, "NO POSIX stat() support."); +#endif +} + +/// http://en.cppreference.com/w/cpp/filesystem/is_directory +inline bool is_directory(StringPiece path, std::error_code& ec) noexcept { + return is_directory(status(path, ec)); +} + /// http://en.cppreference.com/w/cpp/filesystem/file_size inline std::uintmax_t file_size( StringPiece path, diff --git a/contrib/pzstd/utils/test/ThreadPoolTest.cpp b/contrib/pzstd/utils/test/ThreadPoolTest.cpp index 9b9868cb1..1d857aae8 100644 --- a/contrib/pzstd/utils/test/ThreadPoolTest.cpp +++ b/contrib/pzstd/utils/test/ThreadPoolTest.cpp @@ -20,12 +20,12 @@ TEST(ThreadPool, Ordering) { { ThreadPool executor(1); - for (int i = 0; i < 100; ++i) { + for (int i = 0; i < 10; ++i) { executor.add([ &results, i ] { results.push_back(i); }); } } - for (int i = 0; i < 100; ++i) { + for (int i = 0; i < 10; ++i) { EXPECT_EQ(i, results[i]); } } @@ -35,7 +35,7 @@ TEST(ThreadPool, AllJobsFinished) { std::atomic start{false}; { ThreadPool executor(5); - for (int i = 0; i < 1000; ++i) { + for (int i = 0; i < 10; ++i) { executor.add([ &numFinished, &start ] { while (!start.load()) { // spin @@ -45,7 +45,7 @@ TEST(ThreadPool, AllJobsFinished) { } start.store(true); } - EXPECT_EQ(1000, numFinished.load()); + EXPECT_EQ(10, numFinished.load()); } TEST(ThreadPool, AddJobWhileJoining) { diff --git a/contrib/pzstd/utils/test/WorkQueueTest.cpp b/contrib/pzstd/utils/test/WorkQueueTest.cpp index 84d8573c3..ebf375a84 100644 --- a/contrib/pzstd/utils/test/WorkQueueTest.cpp +++ b/contrib/pzstd/utils/test/WorkQueueTest.cpp @@ -89,14 +89,14 @@ TEST(WorkQueue, SPSC) { TEST(WorkQueue, SPMC) { WorkQueue queue; - std::vector results(10000, -1); + std::vector results(50, -1); std::mutex mutex; std::vector threads; - for (int i = 0; i < 100; ++i) { + for (int i = 0; i < 5; ++i) { threads.emplace_back(Popper{&queue, results.data(), &mutex}); } - for (int i = 0; i < 10000; ++i) { + for (int i = 0; i < 50; ++i) { queue.push(i); } queue.finish(); @@ -105,24 +105,24 @@ TEST(WorkQueue, SPMC) { thread.join(); } - for (int i = 0; i < 10000; ++i) { + for (int i = 0; i < 50; ++i) { EXPECT_EQ(i, results[i]); } } TEST(WorkQueue, MPMC) { WorkQueue queue; - std::vector results(10000, -1); + std::vector results(100, -1); std::mutex mutex; std::vector popperThreads; - for (int i = 0; i < 100; ++i) { + for (int i = 0; i < 4; ++i) { popperThreads.emplace_back(Popper{&queue, results.data(), &mutex}); } std::vector pusherThreads; - for (int i = 0; i < 10; ++i) { - auto min = i * 1000; - auto max = (i + 1) * 1000; + for (int i = 0; i < 2; ++i) { + auto min = i * 50; + auto max = (i + 1) * 50; pusherThreads.emplace_back( [ &queue, min, max ] { for (int i = min; i < max; ++i) { @@ -140,7 +140,7 @@ TEST(WorkQueue, MPMC) { thread.join(); } - for (int i = 0; i < 10000; ++i) { + for (int i = 0; i < 100; ++i) { EXPECT_EQ(i, results[i]); } } @@ -197,16 +197,16 @@ TEST(WorkQueue, SetMaxSize) { } TEST(WorkQueue, BoundedSizeMPMC) { - WorkQueue queue(100); - std::vector results(10000, -1); + WorkQueue queue(10); + std::vector results(200, -1); std::mutex mutex; std::vector popperThreads; - for (int i = 0; i < 10; ++i) { + for (int i = 0; i < 4; ++i) { popperThreads.emplace_back(Popper{&queue, results.data(), &mutex}); } std::vector pusherThreads; - for (int i = 0; i < 100; ++i) { + for (int i = 0; i < 2; ++i) { auto min = i * 100; auto max = (i + 1) * 100; pusherThreads.emplace_back( @@ -226,7 +226,7 @@ TEST(WorkQueue, BoundedSizeMPMC) { thread.join(); } - for (int i = 0; i < 10000; ++i) { + for (int i = 0; i < 200; ++i) { EXPECT_EQ(i, results[i]); } } diff --git a/examples/streaming_compression.c b/examples/streaming_compression.c index 4e87130de..108a63c83 100644 --- a/examples/streaming_compression.c +++ b/examples/streaming_compression.c @@ -65,29 +65,31 @@ static void compressFile_orDie(const char* fname, const char* outName, int cLeve { FILE* const fin = fopen_orDie(fname, "rb"); FILE* const fout = fopen_orDie(outName, "wb"); - size_t const buffInSize = ZSTD_CStreamInSize();; + size_t const buffInSize = ZSTD_CStreamInSize(); /* can always read one full block */ void* const buffIn = malloc_orDie(buffInSize); - size_t const buffOutSize = ZSTD_CStreamOutSize();; + size_t const buffOutSize = ZSTD_CStreamOutSize(); /* can always flush a full block */ void* const buffOut = malloc_orDie(buffOutSize); ZSTD_CStream* const cstream = ZSTD_createCStream(); if (cstream==NULL) { fprintf(stderr, "ZSTD_createCStream() error \n"); exit(10); } size_t const initResult = ZSTD_initCStream(cstream, cLevel); - if (ZSTD_isError(initResult)) { fprintf(stderr, "ZSTD_initCStream() error \n"); exit(11); } + if (ZSTD_isError(initResult)) { fprintf(stderr, "ZSTD_initCStream() error : %s \n", ZSTD_getErrorName(initResult)); exit(11); } size_t read, toRead = buffInSize; while( (read = fread_orDie(buffIn, toRead, fin)) ) { ZSTD_inBuffer input = { buffIn, read, 0 }; while (input.pos < input.size) { ZSTD_outBuffer output = { buffOut, buffOutSize, 0 }; - toRead = ZSTD_compressStream(cstream, &output , &input); + toRead = ZSTD_compressStream(cstream, &output , &input); /* toRead is guaranteed to be <= ZSTD_CStreamInSize() */ + if (ZSTD_isError(toRead)) { fprintf(stderr, "ZSTD_compressStream() error : %s \n", ZSTD_getErrorName(toRead)); exit(12); } + if (toRead > buffInSize) toRead = buffInSize; /* Safely handle when `buffInSize` is manually changed to a smaller value */ fwrite_orDie(buffOut, output.pos, fout); } } ZSTD_outBuffer output = { buffOut, buffOutSize, 0 }; size_t const remainingToFlush = ZSTD_endStream(cstream, &output); /* close frame */ - if (remainingToFlush) { fprintf(stderr, "not fully flushed"); exit(12); } + if (remainingToFlush) { fprintf(stderr, "not fully flushed"); exit(13); } fwrite_orDie(buffOut, output.pos, fout); ZSTD_freeCStream(cstream); diff --git a/examples/streaming_decompression.c b/examples/streaming_decompression.c index 51340bae7..1ba1a3d81 100644 --- a/examples/streaming_decompression.c +++ b/examples/streaming_decompression.c @@ -80,6 +80,7 @@ static void decompressFile_orDie(const char* fname) while (input.pos < input.size) { ZSTD_outBuffer output = { buffOut, buffOutSize, 0 }; toRead = ZSTD_decompressStream(dstream, &output , &input); /* toRead : size of next compressed block */ + if (ZSTD_isError(toRead)) { fprintf(stderr, "ZSTD_decompressStream() error : %s \n", ZSTD_getErrorName(toRead)); exit(12); } fwrite_orDie(buffOut, output.pos, fout); } } diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 987d9386e..f40e00aab 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -108,7 +108,8 @@ static const U32 LL_bits[MaxLL+1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, static const S16 LL_defaultNorm[MaxLL+1] = { 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, -1,-1,-1,-1 }; -static const U32 LL_defaultNormLog = 6; +#define LL_DEFAULTNORMLOG 6 /* for static allocation */ +static const U32 LL_defaultNormLog = LL_DEFAULTNORMLOG; static const U32 ML_bits[MaxML+1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -118,11 +119,13 @@ static const S16 ML_defaultNorm[MaxML+1] = { 1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,-1,-1, -1,-1,-1,-1,-1 }; -static const U32 ML_defaultNormLog = 6; +#define ML_DEFAULTNORMLOG 6 /* for static allocation */ +static const U32 ML_defaultNormLog = ML_DEFAULTNORMLOG; static const S16 OF_defaultNorm[MaxOff+1] = { 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,-1,-1,-1,-1,-1 }; -static const U32 OF_defaultNormLog = 5; +#define OF_DEFAULTNORMLOG 5 /* for static allocation */ +static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG; /*-******************************************* diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index f5b712fc3..94f4b5a25 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -142,21 +142,8 @@ size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams) } -/** ZSTD_checkCParams_advanced() : - temporary work-around, while the compressor compatibility remains limited regarding windowLog < 18 */ -size_t ZSTD_checkCParams_advanced(ZSTD_compressionParameters cParams, U64 srcSize) -{ - if (srcSize > (1ULL << ZSTD_WINDOWLOG_MIN)) return ZSTD_checkCParams(cParams); - if (cParams.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN) return ERROR(compressionParameter_unsupported); - if (srcSize <= (1ULL << cParams.windowLog)) cParams.windowLog = ZSTD_WINDOWLOG_MIN; /* fake value - temporary work around */ - if (srcSize <= (1ULL << cParams.chainLog)) cParams.chainLog = ZSTD_CHAINLOG_MIN; /* fake value - temporary work around */ - if ((srcSize <= (1ULL << cParams.hashLog)) & ((U32)cParams.strategy < (U32)ZSTD_btlazy2)) cParams.hashLog = ZSTD_HASHLOG_MIN; /* fake value - temporary work around */ - return ZSTD_checkCParams(cParams); -} - - /** ZSTD_adjustCParams() : - optimize cPar for a given input (`srcSize` and `dictSize`). + optimize `cPar` for a given input (`srcSize` and `dictSize`). mostly downsizing to reduce memory consumption and initialization. Both `srcSize` and `dictSize` are optional (use 0 if unknown), but if both are 0, no optimization can be done. @@ -169,7 +156,7 @@ ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, u { U32 const minSrcSize = (srcSize==0) ? 500 : 0; U64 const rSize = srcSize + dictSize + minSrcSize; if (rSize < ((U64)1< srcLog) cPar.windowLog = srcLog; } } if (cPar.hashLog > cPar.windowLog) cPar.hashLog = cPar.windowLog; @@ -178,7 +165,6 @@ ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, u if (cPar.chainLog > maxChainLog) cPar.chainLog = maxChainLog; } /* <= ZSTD_CHAINLOG_MAX */ if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN) cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN; /* required for frame header */ - if ((cPar.hashLog < ZSTD_HASHLOG_MIN) & ((U32)cPar.strategy >= (U32)ZSTD_btlazy2)) cPar.hashLog = ZSTD_HASHLOG_MIN; /* required to ensure collision resistance in bt */ return cPar; } @@ -322,13 +308,12 @@ static size_t ZSTD_resetCCtx_advanced (ZSTD_CCtx* zc, * Duplicate an existing context `srcCCtx` into another one `dstCCtx`. * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()). * @return : 0, or an error code */ -size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx) +size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long long pledgedSrcSize) { if (srcCCtx->stage!=ZSTDcs_init) return ERROR(stage_wrong); memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem)); - ZSTD_resetCCtx_advanced(dstCCtx, srcCCtx->params, srcCCtx->frameContentSize, ZSTDcrp_noMemset); - dstCCtx->params.fParams.contentSizeFlag = 0; /* content size different from the one set during srcCCtx init */ + ZSTD_resetCCtx_advanced(dstCCtx, srcCCtx->params, pledgedSrcSize, ZSTDcrp_noMemset); /* copy tables */ { size_t const chainSize = (srcCCtx->params.cParams.strategy == ZSTD_fast) ? 0 : (1 << srcCCtx->params.cParams.chainLog); @@ -2557,7 +2542,7 @@ size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, ZSTD_parameters params, unsigned long long pledgedSrcSize) { /* compression parameters verification and optimization */ - CHECK_F(ZSTD_checkCParams_advanced(params.cParams, pledgedSrcSize)); + CHECK_F(ZSTD_checkCParams(params.cParams)); return ZSTD_compressBegin_internal(cctx, dict, dictSize, params, pledgedSrcSize); } @@ -2645,7 +2630,7 @@ size_t ZSTD_compress_advanced (ZSTD_CCtx* ctx, const void* dict,size_t dictSize, ZSTD_parameters params) { - CHECK_F(ZSTD_checkCParams_advanced(params.cParams, srcSize)); + CHECK_F(ZSTD_checkCParams(params.cParams)); return ZSTD_compress_internal(ctx, dst, dstCapacity, src, srcSize, dict, dictSize, params); } @@ -2730,7 +2715,7 @@ ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionL size_t ZSTD_freeCDict(ZSTD_CDict* cdict) { if (cdict==NULL) return 0; /* support free on NULL */ - { ZSTD_customMem cMem = cdict->refContext->customMem; + { ZSTD_customMem const cMem = cdict->refContext->customMem; ZSTD_freeCCtx(cdict->refContext); ZSTD_free(cdict->dictContent, cMem); ZSTD_free(cdict, cMem); @@ -2740,7 +2725,7 @@ size_t ZSTD_freeCDict(ZSTD_CDict* cdict) size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, U64 pledgedSrcSize) { - if (cdict->dictContentSize) CHECK_F(ZSTD_copyCCtx(cctx, cdict->refContext)) + if (cdict->dictContentSize) CHECK_F(ZSTD_copyCCtx(cctx, cdict->refContext, pledgedSrcSize)) else CHECK_F(ZSTD_compressBegin_advanced(cctx, NULL, 0, cdict->refContext->params, pledgedSrcSize)); return 0; } @@ -2852,7 +2837,7 @@ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, { size_t const neededInBuffSize = (size_t)1 << params.cParams.windowLog; if (zcs->inBuffSize < neededInBuffSize) { zcs->inBuffSize = neededInBuffSize; - ZSTD_free(zcs->inBuff, zcs->customMem); /* should not be necessary */ + ZSTD_free(zcs->inBuff, zcs->customMem); zcs->inBuff = (char*) ZSTD_malloc(neededInBuffSize, zcs->customMem); if (zcs->inBuff == NULL) return ERROR(memory_allocation); } @@ -2860,7 +2845,7 @@ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, } if (zcs->outBuffSize < ZSTD_compressBound(zcs->blockSize)+1) { zcs->outBuffSize = ZSTD_compressBound(zcs->blockSize)+1; - ZSTD_free(zcs->outBuff, zcs->customMem); /* should not be necessary */ + ZSTD_free(zcs->outBuff, zcs->customMem); zcs->outBuff = (char*) ZSTD_malloc(zcs->outBuffSize, zcs->customMem); if (zcs->outBuff == NULL) return ERROR(memory_allocation); } diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 2b2539a4a..47b5f42c7 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -482,23 +482,203 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx, } +typedef union { + FSE_decode_t realData; + U32 alignedBy4; +} FSE_decode_t4; + +static const FSE_decode_t4 LL_defaultDTable[(1< max) return ERROR(corruption_detected); - FSE_buildDTable_rle(DTable, *(const BYTE*)src); /* if *src > max, data is corrupted */ + FSE_buildDTable_rle(DTableSpace, *(const BYTE*)src); + *DTablePtr = DTableSpace; return 1; case set_basic : - FSE_buildDTable(DTable, defaultNorm, max, defaultLog); + *DTablePtr = (const FSE_DTable*)tmpPtr; return 0; case set_repeat: if (!flagRepeatTable) return ERROR(corruption_detected); @@ -510,12 +690,12 @@ static size_t ZSTD_buildSeqTable(FSE_DTable* DTable, symbolEncodingType_e type, size_t const headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize); if (FSE_isError(headerSize)) return ERROR(corruption_detected); if (tableLog > maxLog) return ERROR(corruption_detected); - FSE_buildDTable(DTable, norm, max, tableLog); + FSE_buildDTable(DTableSpace, norm, max, tableLog); + *DTablePtr = DTableSpace; return headerSize; } } } - size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, const void* src, size_t srcSize) { @@ -546,21 +726,25 @@ size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr, ip++; /* Build DTables */ - { size_t const llhSize = ZSTD_buildSeqTable(dctx->LLTable, LLtype, MaxLL, LLFSELog, ip, iend-ip, LL_defaultNorm, LL_defaultNormLog, dctx->fseEntropy); + { size_t const llhSize = ZSTD_buildSeqTable(dctx->LLTable, &dctx->LLTptr, + LLtype, MaxLL, LLFSELog, + ip, iend-ip, LL_defaultDTable, dctx->fseEntropy); if (ZSTD_isError(llhSize)) return ERROR(corruption_detected); - if (LLtype != set_repeat) dctx->LLTptr = dctx->LLTable; ip += llhSize; } - { size_t const ofhSize = ZSTD_buildSeqTable(dctx->OFTable, OFtype, MaxOff, OffFSELog, ip, iend-ip, OF_defaultNorm, OF_defaultNormLog, dctx->fseEntropy); + { size_t const ofhSize = ZSTD_buildSeqTable(dctx->OFTable, &dctx->OFTptr, + OFtype, MaxOff, OffFSELog, + ip, iend-ip, OF_defaultDTable, dctx->fseEntropy); if (ZSTD_isError(ofhSize)) return ERROR(corruption_detected); - if (OFtype != set_repeat) dctx->OFTptr = dctx->OFTable; ip += ofhSize; } - { size_t const mlhSize = ZSTD_buildSeqTable(dctx->MLTable, MLtype, MaxML, MLFSELog, ip, iend-ip, ML_defaultNorm, ML_defaultNormLog, dctx->fseEntropy); + { size_t const mlhSize = ZSTD_buildSeqTable(dctx->MLTable, &dctx->MLTptr, + MLtype, MaxML, MLFSELog, + ip, iend-ip, ML_defaultDTable, dctx->fseEntropy); if (ZSTD_isError(mlhSize)) return ERROR(corruption_detected); - if (MLtype != set_repeat) dctx->MLTptr = dctx->MLTable; ip += mlhSize; - } } + } + } return ip-istart; } @@ -1370,6 +1554,7 @@ size_t ZSTD_initDStream(ZSTD_DStream* zds) size_t ZSTD_resetDStream(ZSTD_DStream* zds) { + if (zds->ddict == NULL) return ERROR(stage_wrong); /* must be init at least once */ zds->stage = zdss_loadHeader; zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; zds->legacyVersion = 0; diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index cfabb20ba..874351ebf 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -505,7 +505,8 @@ static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, { size_t pos; for (pos=0; pos < bufferSize; pos++) reverseSuffix[suffix[pos]] = (U32)pos; - /* build file pos */ + /* note filePos tracks borders between samples. + It's not used at this stage, but planned to become useful in a later update */ filePos[0] = 0; for (pos=1; pos blockSizeMax) srcSize = blockSizeMax; /* protection vs large samples */ - { size_t const errorCode = ZSTD_copyCCtx(esr.zc, esr.ref); + { size_t const errorCode = ZSTD_copyCCtx(esr.zc, esr.ref, 0); if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_copyCCtx failed \n"); return; } } cSize = ZSTD_compressBlock(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_ABSOLUTEMAX, src, srcSize); diff --git a/lib/legacy/zstd_v06.c b/lib/legacy/zstd_v06.c index 5a9bc40e1..d9e89f806 100644 --- a/lib/legacy/zstd_v06.c +++ b/lib/legacy/zstd_v06.c @@ -326,7 +326,7 @@ extern "C" { * It avoids reloading the dictionary each time. * `preparedDCtx` must have been properly initialized using ZSTDv06_decompressBegin_usingDict(). * Requires 2 contexts : 1 for reference (preparedDCtx), which will not be modified, and 1 to run the decompression operation (dctx) */ -ZSTDLIB_API size_t ZSTDv06_decompress_usingPreparedDCtx( +ZSTDLIBv06_API size_t ZSTDv06_decompress_usingPreparedDCtx( ZSTDv06_DCtx* dctx, const ZSTDv06_DCtx* preparedDCtx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); @@ -337,7 +337,7 @@ ZSTDLIB_API size_t ZSTDv06_decompress_usingPreparedDCtx( static const size_t ZSTDv06_frameHeaderSize_min = 5; static const size_t ZSTDv06_frameHeaderSize_max = ZSTDv06_FRAMEHEADERSIZE_MAX; -ZSTDLIB_API size_t ZSTDv06_decompressBegin(ZSTDv06_DCtx* dctx); +ZSTDLIBv06_API size_t ZSTDv06_decompressBegin(ZSTDv06_DCtx* dctx); /* Streaming decompression, direct mode (bufferless) @@ -396,7 +396,7 @@ ZSTDLIB_API size_t ZSTDv06_decompressBegin(ZSTDv06_DCtx* dctx); */ #define ZSTDv06_BLOCKSIZE_MAX (128 * 1024) /* define, for static allocation */ -ZSTDLIB_API size_t ZSTDv06_decompressBlock(ZSTDv06_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTDLIBv06_API size_t ZSTDv06_decompressBlock(ZSTDv06_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); diff --git a/lib/legacy/zstd_v06.h b/lib/legacy/zstd_v06.h index bcc6efbc3..14040abdd 100644 --- a/lib/legacy/zstd_v06.h +++ b/lib/legacy/zstd_v06.h @@ -14,23 +14,19 @@ extern "C" { #endif -/*-************************************* -* Dependencies -***************************************/ +/*====== Dependency ======*/ #include /* size_t */ -/*-*************************************************************** -* Export parameters -*****************************************************************/ +/*====== Export for Windows ======*/ /*! * ZSTDv06_DLL_EXPORT : * Enable exporting of functions when building a Windows DLL */ #if defined(_WIN32) && defined(ZSTDv06_DLL_EXPORT) && (ZSTDv06_DLL_EXPORT==1) -# define ZSTDLIB_API __declspec(dllexport) +# define ZSTDLIBv06_API __declspec(dllexport) #else -# define ZSTDLIB_API +# define ZSTDLIBv06_API #endif @@ -42,18 +38,18 @@ extern "C" { `dstCapacity` must be large enough, equal or larger than originalSize. @return : the number of bytes decompressed into `dst` (<= `dstCapacity`), or an errorCode if it fails (which can be tested using ZSTDv06_isError()) */ -ZSTDLIB_API size_t ZSTDv06_decompress( void* dst, size_t dstCapacity, - const void* src, size_t compressedSize); +ZSTDLIBv06_API size_t ZSTDv06_decompress( void* dst, size_t dstCapacity, + const void* src, size_t compressedSize); /* ************************************* * Helper functions ***************************************/ -ZSTDLIB_API size_t ZSTDv06_compressBound(size_t srcSize); /*!< maximum compressed size (worst case scenario) */ +ZSTDLIBv06_API size_t ZSTDv06_compressBound(size_t srcSize); /*!< maximum compressed size (worst case scenario) */ /* Error Management */ -ZSTDLIB_API unsigned ZSTDv06_isError(size_t code); /*!< tells if a `size_t` function result is an error code */ -ZSTDLIB_API const char* ZSTDv06_getErrorName(size_t code); /*!< provides readable string for an error code */ +ZSTDLIBv06_API unsigned ZSTDv06_isError(size_t code); /*!< tells if a `size_t` function result is an error code */ +ZSTDLIBv06_API const char* ZSTDv06_getErrorName(size_t code); /*!< provides readable string for an error code */ /* ************************************* @@ -61,12 +57,12 @@ ZSTDLIB_API const char* ZSTDv06_getErrorName(size_t code); /*!< provides rea ***************************************/ /** Decompression context */ typedef struct ZSTDv06_DCtx_s ZSTDv06_DCtx; -ZSTDLIB_API ZSTDv06_DCtx* ZSTDv06_createDCtx(void); -ZSTDLIB_API size_t ZSTDv06_freeDCtx(ZSTDv06_DCtx* dctx); /*!< @return : errorCode */ +ZSTDLIBv06_API ZSTDv06_DCtx* ZSTDv06_createDCtx(void); +ZSTDLIBv06_API size_t ZSTDv06_freeDCtx(ZSTDv06_DCtx* dctx); /*!< @return : errorCode */ /** ZSTDv06_decompressDCtx() : * Same as ZSTDv06_decompress(), but requires an already allocated ZSTDv06_DCtx (see ZSTDv06_createDCtx()) */ -ZSTDLIB_API size_t ZSTDv06_decompressDCtx(ZSTDv06_DCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTDLIBv06_API size_t ZSTDv06_decompressDCtx(ZSTDv06_DCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); /*-*********************** @@ -76,10 +72,10 @@ ZSTDLIB_API size_t ZSTDv06_decompressDCtx(ZSTDv06_DCtx* ctx, void* dst, size_t d * Decompression using a pre-defined Dictionary content (see dictBuilder). * Dictionary must be identical to the one used during compression, otherwise regenerated data will be corrupted. * Note : dict can be NULL, in which case, it's equivalent to ZSTDv06_decompressDCtx() */ -ZSTDLIB_API size_t ZSTDv06_decompress_usingDict(ZSTDv06_DCtx* dctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const void* dict,size_t dictSize); +ZSTDLIBv06_API size_t ZSTDv06_decompress_usingDict(ZSTDv06_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const void* dict,size_t dictSize); /*-************************ @@ -88,12 +84,12 @@ ZSTDLIB_API size_t ZSTDv06_decompress_usingDict(ZSTDv06_DCtx* dctx, struct ZSTDv06_frameParams_s { unsigned long long frameContentSize; unsigned windowLog; }; typedef struct ZSTDv06_frameParams_s ZSTDv06_frameParams; -ZSTDLIB_API size_t ZSTDv06_getFrameParams(ZSTDv06_frameParams* fparamsPtr, const void* src, size_t srcSize); /**< doesn't consume input */ -ZSTDLIB_API size_t ZSTDv06_decompressBegin_usingDict(ZSTDv06_DCtx* dctx, const void* dict, size_t dictSize); -ZSTDLIB_API void ZSTDv06_copyDCtx(ZSTDv06_DCtx* dctx, const ZSTDv06_DCtx* preparedDCtx); +ZSTDLIBv06_API size_t ZSTDv06_getFrameParams(ZSTDv06_frameParams* fparamsPtr, const void* src, size_t srcSize); /**< doesn't consume input */ +ZSTDLIBv06_API size_t ZSTDv06_decompressBegin_usingDict(ZSTDv06_DCtx* dctx, const void* dict, size_t dictSize); +ZSTDLIBv06_API void ZSTDv06_copyDCtx(ZSTDv06_DCtx* dctx, const ZSTDv06_DCtx* preparedDCtx); -ZSTDLIB_API size_t ZSTDv06_nextSrcSizeToDecompress(ZSTDv06_DCtx* dctx); -ZSTDLIB_API size_t ZSTDv06_decompressContinue(ZSTDv06_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTDLIBv06_API size_t ZSTDv06_nextSrcSizeToDecompress(ZSTDv06_DCtx* dctx); +ZSTDLIBv06_API size_t ZSTDv06_decompressContinue(ZSTDv06_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); @@ -102,15 +98,15 @@ ZSTDLIB_API size_t ZSTDv06_decompressContinue(ZSTDv06_DCtx* dctx, void* dst, siz ***************************************/ typedef struct ZBUFFv06_DCtx_s ZBUFFv06_DCtx; -ZSTDLIB_API ZBUFFv06_DCtx* ZBUFFv06_createDCtx(void); -ZSTDLIB_API size_t ZBUFFv06_freeDCtx(ZBUFFv06_DCtx* dctx); +ZSTDLIBv06_API ZBUFFv06_DCtx* ZBUFFv06_createDCtx(void); +ZSTDLIBv06_API size_t ZBUFFv06_freeDCtx(ZBUFFv06_DCtx* dctx); -ZSTDLIB_API size_t ZBUFFv06_decompressInit(ZBUFFv06_DCtx* dctx); -ZSTDLIB_API size_t ZBUFFv06_decompressInitDictionary(ZBUFFv06_DCtx* dctx, const void* dict, size_t dictSize); +ZSTDLIBv06_API size_t ZBUFFv06_decompressInit(ZBUFFv06_DCtx* dctx); +ZSTDLIBv06_API size_t ZBUFFv06_decompressInitDictionary(ZBUFFv06_DCtx* dctx, const void* dict, size_t dictSize); -ZSTDLIB_API size_t ZBUFFv06_decompressContinue(ZBUFFv06_DCtx* dctx, - void* dst, size_t* dstCapacityPtr, - const void* src, size_t* srcSizePtr); +ZSTDLIBv06_API size_t ZBUFFv06_decompressContinue(ZBUFFv06_DCtx* dctx, + void* dst, size_t* dstCapacityPtr, + const void* src, size_t* srcSizePtr); /*-*************************************************************************** * Streaming decompression howto @@ -140,13 +136,13 @@ ZSTDLIB_API size_t ZBUFFv06_decompressContinue(ZBUFFv06_DCtx* dctx, /* ************************************* * Tool functions ***************************************/ -ZSTDLIB_API unsigned ZBUFFv06_isError(size_t errorCode); -ZSTDLIB_API const char* ZBUFFv06_getErrorName(size_t errorCode); +ZSTDLIBv06_API unsigned ZBUFFv06_isError(size_t errorCode); +ZSTDLIBv06_API const char* ZBUFFv06_getErrorName(size_t errorCode); /** Functions below provide recommended buffer sizes for Compression or Decompression operations. * These sizes are just hints, they tend to offer better latency */ -ZSTDLIB_API size_t ZBUFFv06_recommendedDInSize(void); -ZSTDLIB_API size_t ZBUFFv06_recommendedDOutSize(void); +ZSTDLIBv06_API size_t ZBUFFv06_recommendedDInSize(void); +ZSTDLIBv06_API size_t ZBUFFv06_recommendedDOutSize(void); /*-************************************* diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c index dac71aeb3..f4c8073f9 100644 --- a/lib/legacy/zstd_v07.c +++ b/lib/legacy/zstd_v07.c @@ -68,27 +68,27 @@ typedef struct { ZSTDv07_allocFunction customAlloc; ZSTDv07_freeFunction customF /*! ZSTDv07_estimateDCtxSize() : * Gives the potential amount of memory allocated to create a ZSTDv07_DCtx */ -ZSTDLIB_API size_t ZSTDv07_estimateDCtxSize(void); +ZSTDLIBv07_API size_t ZSTDv07_estimateDCtxSize(void); /*! ZSTDv07_createDCtx_advanced() : * Create a ZSTD decompression context using external alloc and free functions */ -ZSTDLIB_API ZSTDv07_DCtx* ZSTDv07_createDCtx_advanced(ZSTDv07_customMem customMem); +ZSTDLIBv07_API ZSTDv07_DCtx* ZSTDv07_createDCtx_advanced(ZSTDv07_customMem customMem); /*! ZSTDv07_sizeofDCtx() : * Gives the amount of memory used by a given ZSTDv07_DCtx */ -ZSTDLIB_API size_t ZSTDv07_sizeofDCtx(const ZSTDv07_DCtx* dctx); +ZSTDLIBv07_API size_t ZSTDv07_sizeofDCtx(const ZSTDv07_DCtx* dctx); /* ****************************************************************** * Buffer-less streaming functions (synchronous mode) ********************************************************************/ -ZSTDLIB_API size_t ZSTDv07_decompressBegin(ZSTDv07_DCtx* dctx); -ZSTDLIB_API size_t ZSTDv07_decompressBegin_usingDict(ZSTDv07_DCtx* dctx, const void* dict, size_t dictSize); -ZSTDLIB_API void ZSTDv07_copyDCtx(ZSTDv07_DCtx* dctx, const ZSTDv07_DCtx* preparedDCtx); +ZSTDLIBv07_API size_t ZSTDv07_decompressBegin(ZSTDv07_DCtx* dctx); +ZSTDLIBv07_API size_t ZSTDv07_decompressBegin_usingDict(ZSTDv07_DCtx* dctx, const void* dict, size_t dictSize); +ZSTDLIBv07_API void ZSTDv07_copyDCtx(ZSTDv07_DCtx* dctx, const ZSTDv07_DCtx* preparedDCtx); -ZSTDLIB_API size_t ZSTDv07_nextSrcSizeToDecompress(ZSTDv07_DCtx* dctx); -ZSTDLIB_API size_t ZSTDv07_decompressContinue(ZSTDv07_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTDLIBv07_API size_t ZSTDv07_nextSrcSizeToDecompress(ZSTDv07_DCtx* dctx); +ZSTDLIBv07_API size_t ZSTDv07_decompressContinue(ZSTDv07_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); /* Buffer-less streaming decompression (synchronous mode) @@ -169,8 +169,8 @@ ZSTDLIB_API size_t ZSTDv07_decompressContinue(ZSTDv07_DCtx* dctx, void* dst, siz */ #define ZSTDv07_BLOCKSIZE_ABSOLUTEMAX (128 * 1024) /* define, for static allocation */ -ZSTDLIB_API size_t ZSTDv07_decompressBlock(ZSTDv07_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -ZSTDLIB_API size_t ZSTDv07_insertBlock(ZSTDv07_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert block into `dctx` history. Useful for uncompressed blocks */ +ZSTDLIBv07_API size_t ZSTDv07_decompressBlock(ZSTDv07_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTDLIBv07_API size_t ZSTDv07_insertBlock(ZSTDv07_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert block into `dctx` history. Useful for uncompressed blocks */ #endif /* ZSTDv07_STATIC_LINKING_ONLY */ @@ -650,8 +650,8 @@ MEM_STATIC size_t BITv07_readBitsFast(BITv07_DStream_t* bitD, U32 nbBits) if status == unfinished, internal register is filled with >= (sizeof(bitD->bitContainer)*8 - 7) bits */ MEM_STATIC BITv07_DStream_status BITv07_reloadDStream(BITv07_DStream_t* bitD) { - if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* should not happen => corruption detected */ - return BITv07_DStream_overflow; + if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* should not happen => corruption detected */ + return BITv07_DStream_overflow; if (bitD->ptr >= bitD->start + sizeof(bitD->bitContainer)) { bitD->ptr -= bitD->bitsConsumed >> 3; @@ -3831,7 +3831,7 @@ size_t ZSTDv07_decompressBlock(ZSTDv07_DCtx* dctx, /** ZSTDv07_insertBlock() : insert `src` block into `dctx` history. Useful to track uncompressed blocks. */ -ZSTDLIB_API size_t ZSTDv07_insertBlock(ZSTDv07_DCtx* dctx, const void* blockStart, size_t blockSize) +ZSTDLIBv07_API size_t ZSTDv07_insertBlock(ZSTDv07_DCtx* dctx, const void* blockStart, size_t blockSize) { ZSTDv07_checkContinuity(dctx, blockStart); dctx->previousDstEnd = (const char*)blockStart + blockSize; @@ -4233,7 +4233,7 @@ size_t ZSTDv07_freeDDict(ZSTDv07_DDict* ddict) /*! ZSTDv07_decompress_usingDDict() : * Decompression using a pre-digested Dictionary * Use dictionary without significant overhead. */ -ZSTDLIB_API size_t ZSTDv07_decompress_usingDDict(ZSTDv07_DCtx* dctx, +ZSTDLIBv07_API size_t ZSTDv07_decompress_usingDDict(ZSTDv07_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const ZSTDv07_DDict* ddict) @@ -4320,7 +4320,7 @@ struct ZBUFFv07_DCtx_s { ZSTDv07_customMem customMem; }; /* typedef'd to ZBUFFv07_DCtx within "zstd_buffered.h" */ -ZSTDLIB_API ZBUFFv07_DCtx* ZBUFFv07_createDCtx_advanced(ZSTDv07_customMem customMem); +ZSTDLIBv07_API ZBUFFv07_DCtx* ZBUFFv07_createDCtx_advanced(ZSTDv07_customMem customMem); ZBUFFv07_DCtx* ZBUFFv07_createDCtx(void) { diff --git a/lib/legacy/zstd_v07.h b/lib/legacy/zstd_v07.h index d1fbc0830..30725dcf7 100644 --- a/lib/legacy/zstd_v07.h +++ b/lib/legacy/zstd_v07.h @@ -24,13 +24,12 @@ extern "C" { * Enable exporting of functions when building a Windows DLL */ #if defined(_WIN32) && defined(ZSTDv07_DLL_EXPORT) && (ZSTDv07_DLL_EXPORT==1) -# define ZSTDLIB_API __declspec(dllexport) +# define ZSTDLIBv07_API __declspec(dllexport) #else -# define ZSTDLIB_API +# define ZSTDLIBv07_API #endif - /* ************************************* * Simple API ***************************************/ @@ -46,12 +45,12 @@ unsigned long long ZSTDv07_getDecompressedSize(const void* src, size_t srcSize); `dstCapacity` must be equal or larger than originalSize. @return : the number of bytes decompressed into `dst` (<= `dstCapacity`), or an errorCode if it fails (which can be tested using ZSTDv07_isError()) */ -ZSTDLIB_API size_t ZSTDv07_decompress( void* dst, size_t dstCapacity, - const void* src, size_t compressedSize); +ZSTDLIBv07_API size_t ZSTDv07_decompress( void* dst, size_t dstCapacity, + const void* src, size_t compressedSize); /*====== Helper functions ======*/ -ZSTDLIB_API unsigned ZSTDv07_isError(size_t code); /*!< tells if a `size_t` function result is an error code */ -ZSTDLIB_API const char* ZSTDv07_getErrorName(size_t code); /*!< provides readable string from an error code */ +ZSTDLIBv07_API unsigned ZSTDv07_isError(size_t code); /*!< tells if a `size_t` function result is an error code */ +ZSTDLIBv07_API const char* ZSTDv07_getErrorName(size_t code); /*!< provides readable string from an error code */ /*-************************************* @@ -59,12 +58,12 @@ ZSTDLIB_API const char* ZSTDv07_getErrorName(size_t code); /*!< provides rea ***************************************/ /** Decompression context */ typedef struct ZSTDv07_DCtx_s ZSTDv07_DCtx; -ZSTDLIB_API ZSTDv07_DCtx* ZSTDv07_createDCtx(void); -ZSTDLIB_API size_t ZSTDv07_freeDCtx(ZSTDv07_DCtx* dctx); /*!< @return : errorCode */ +ZSTDLIBv07_API ZSTDv07_DCtx* ZSTDv07_createDCtx(void); +ZSTDLIBv07_API size_t ZSTDv07_freeDCtx(ZSTDv07_DCtx* dctx); /*!< @return : errorCode */ /** ZSTDv07_decompressDCtx() : * Same as ZSTDv07_decompress(), requires an allocated ZSTDv07_DCtx (see ZSTDv07_createDCtx()) */ -ZSTDLIB_API size_t ZSTDv07_decompressDCtx(ZSTDv07_DCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTDLIBv07_API size_t ZSTDv07_decompressDCtx(ZSTDv07_DCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); /*-************************ @@ -74,10 +73,10 @@ ZSTDLIB_API size_t ZSTDv07_decompressDCtx(ZSTDv07_DCtx* ctx, void* dst, size_t d * Decompression using a pre-defined Dictionary content (see dictBuilder). * Dictionary must be identical to the one used during compression. * Note : This function load the dictionary, resulting in a significant startup time */ -ZSTDLIB_API size_t ZSTDv07_decompress_usingDict(ZSTDv07_DCtx* dctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const void* dict,size_t dictSize); +ZSTDLIBv07_API size_t ZSTDv07_decompress_usingDict(ZSTDv07_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const void* dict,size_t dictSize); /*-************************** @@ -87,16 +86,16 @@ ZSTDLIB_API size_t ZSTDv07_decompress_usingDict(ZSTDv07_DCtx* dctx, * Create a digested dictionary, ready to start decompression operation without startup delay. * `dict` can be released after creation */ typedef struct ZSTDv07_DDict_s ZSTDv07_DDict; -ZSTDLIB_API ZSTDv07_DDict* ZSTDv07_createDDict(const void* dict, size_t dictSize); -ZSTDLIB_API size_t ZSTDv07_freeDDict(ZSTDv07_DDict* ddict); +ZSTDLIBv07_API ZSTDv07_DDict* ZSTDv07_createDDict(const void* dict, size_t dictSize); +ZSTDLIBv07_API size_t ZSTDv07_freeDDict(ZSTDv07_DDict* ddict); /*! ZSTDv07_decompress_usingDDict() : * Decompression using a pre-digested Dictionary * Faster startup than ZSTDv07_decompress_usingDict(), recommended when same dictionary is used multiple times. */ -ZSTDLIB_API size_t ZSTDv07_decompress_usingDDict(ZSTDv07_DCtx* dctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const ZSTDv07_DDict* ddict); +ZSTDLIBv07_API size_t ZSTDv07_decompress_usingDDict(ZSTDv07_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const ZSTDv07_DDict* ddict); typedef struct { unsigned long long frameContentSize; @@ -105,7 +104,7 @@ typedef struct { unsigned checksumFlag; } ZSTDv07_frameParams; -ZSTDLIB_API size_t ZSTDv07_getFrameParams(ZSTDv07_frameParams* fparamsPtr, const void* src, size_t srcSize); /**< doesn't consume input */ +ZSTDLIBv07_API size_t ZSTDv07_getFrameParams(ZSTDv07_frameParams* fparamsPtr, const void* src, size_t srcSize); /**< doesn't consume input */ @@ -114,13 +113,13 @@ ZSTDLIB_API size_t ZSTDv07_getFrameParams(ZSTDv07_frameParams* fparamsPtr, const * Streaming functions ***************************************/ typedef struct ZBUFFv07_DCtx_s ZBUFFv07_DCtx; -ZSTDLIB_API ZBUFFv07_DCtx* ZBUFFv07_createDCtx(void); -ZSTDLIB_API size_t ZBUFFv07_freeDCtx(ZBUFFv07_DCtx* dctx); +ZSTDLIBv07_API ZBUFFv07_DCtx* ZBUFFv07_createDCtx(void); +ZSTDLIBv07_API size_t ZBUFFv07_freeDCtx(ZBUFFv07_DCtx* dctx); -ZSTDLIB_API size_t ZBUFFv07_decompressInit(ZBUFFv07_DCtx* dctx); -ZSTDLIB_API size_t ZBUFFv07_decompressInitDictionary(ZBUFFv07_DCtx* dctx, const void* dict, size_t dictSize); +ZSTDLIBv07_API size_t ZBUFFv07_decompressInit(ZBUFFv07_DCtx* dctx); +ZSTDLIBv07_API size_t ZBUFFv07_decompressInitDictionary(ZBUFFv07_DCtx* dctx, const void* dict, size_t dictSize); -ZSTDLIB_API size_t ZBUFFv07_decompressContinue(ZBUFFv07_DCtx* dctx, +ZSTDLIBv07_API size_t ZBUFFv07_decompressContinue(ZBUFFv07_DCtx* dctx, void* dst, size_t* dstCapacityPtr, const void* src, size_t* srcSizePtr); @@ -152,13 +151,13 @@ ZSTDLIB_API size_t ZBUFFv07_decompressContinue(ZBUFFv07_DCtx* dctx, /* ************************************* * Tool functions ***************************************/ -ZSTDLIB_API unsigned ZBUFFv07_isError(size_t errorCode); -ZSTDLIB_API const char* ZBUFFv07_getErrorName(size_t errorCode); +ZSTDLIBv07_API unsigned ZBUFFv07_isError(size_t errorCode); +ZSTDLIBv07_API const char* ZBUFFv07_getErrorName(size_t errorCode); /** Functions below provide recommended buffer sizes for Compression or Decompression operations. * These sizes are just hints, they tend to offer better latency */ -ZSTDLIB_API size_t ZBUFFv07_recommendedDInSize(void); -ZSTDLIB_API size_t ZBUFFv07_recommendedDOutSize(void); +ZSTDLIBv07_API size_t ZBUFFv07_recommendedDInSize(void); +ZSTDLIBv07_API size_t ZBUFFv07_recommendedDOutSize(void); /*-************************************* diff --git a/lib/zstd.h b/lib/zstd.h index f79a5dcac..dd3f5df4c 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -32,8 +32,8 @@ extern "C" { /*======= Version =======*/ #define ZSTD_VERSION_MAJOR 1 -#define ZSTD_VERSION_MINOR 0 -#define ZSTD_VERSION_RELEASE 1 +#define ZSTD_VERSION_MINOR 1 +#define ZSTD_VERSION_RELEASE 0 #define ZSTD_LIB_VERSION ZSTD_VERSION_MAJOR.ZSTD_VERSION_MINOR.ZSTD_VERSION_RELEASE #define ZSTD_QUOTE(str) #str @@ -290,11 +290,11 @@ ZSTDLIB_API size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* outp #define ZSTD_WINDOWLOG_MAX_32 25 #define ZSTD_WINDOWLOG_MAX_64 27 #define ZSTD_WINDOWLOG_MAX ((U32)(MEM_32bits() ? ZSTD_WINDOWLOG_MAX_32 : ZSTD_WINDOWLOG_MAX_64)) -#define ZSTD_WINDOWLOG_MIN 18 -#define ZSTD_CHAINLOG_MAX (ZSTD_WINDOWLOG_MAX+1) -#define ZSTD_CHAINLOG_MIN 4 +#define ZSTD_WINDOWLOG_MIN 10 #define ZSTD_HASHLOG_MAX ZSTD_WINDOWLOG_MAX -#define ZSTD_HASHLOG_MIN 12 +#define ZSTD_HASHLOG_MIN 6 +#define ZSTD_CHAINLOG_MAX (ZSTD_WINDOWLOG_MAX+1) +#define ZSTD_CHAINLOG_MIN ZSTD_HASHLOG_MIN #define ZSTD_HASHLOG3_MAX 17 #define ZSTD_SEARCHLOG_MAX (ZSTD_WINDOWLOG_MAX-1) #define ZSTD_SEARCHLOG_MIN 1 @@ -447,7 +447,7 @@ ZSTDLIB_API size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds); ZSTDLIB_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel); ZSTDLIB_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel); ZSTDLIB_API size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); -ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx); +ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); diff --git a/programs/Makefile b/programs/Makefile index 76130fe50..6e78d0ea9 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -57,8 +57,8 @@ endif ifneq (,$(filter Windows%,$(OS))) EXT =.exe VOID = nul -RES64_FILE = ..\projects\VS2010\zstd\generate_res\zstd64.res -RES32_FILE = ..\projects\VS2010\zstd\generate_res\zstd32.res +RES64_FILE = ..\build\VS2010\zstd\generate_res\zstd64.res +RES32_FILE = ..\build\VS2010\zstd\generate_res\zstd32.res ifneq (,$(filter x86_64%,$(shell $(CC) -dumpmachine))) RES_FILE = $(RES64_FILE) else diff --git a/programs/datagen.h b/programs/datagen.h index 55f9d8283..094056b69 100644 --- a/programs/datagen.h +++ b/programs/datagen.h @@ -6,7 +6,8 @@ * 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. */ - +#ifndef DATAGEN_H +#define DATAGEN_H #include /* size_t */ @@ -22,3 +23,5 @@ void RDG_genBuffer(void* buffer, size_t size, double matchProba, double litProba RDG_genStdout Same as RDG_genBuffer, but generates data into stdout */ + +#endif diff --git a/programs/fileio.c b/programs/fileio.c index 7dee7c11b..56f22fe75 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -255,7 +255,7 @@ typedef struct { FILE* srcFile; } cRess_t; -static cRess_t FIO_createCResources(const char* dictFileName, int cLevel) +static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, U64 srcSize) { cRess_t ress; memset(&ress, 0, sizeof(ress)); @@ -272,11 +272,11 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel) { void* dictBuffer; size_t const dictBuffSize = FIO_loadFile(&dictBuffer, dictFileName); if (dictFileName && (dictBuffer==NULL)) EXM_THROW(32, "zstd: allocation error : can't create dictBuffer"); - { ZSTD_parameters params = ZSTD_getParams(cLevel, 0, dictBuffSize); + { ZSTD_parameters params = ZSTD_getParams(cLevel, srcSize, dictBuffSize); params.fParams.contentSizeFlag = 1; params.fParams.checksumFlag = g_checksumFlag; params.fParams.noDictIDFlag = !g_dictIDFlag; - { size_t const errorCode = ZSTD_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, 0); + { size_t const errorCode = ZSTD_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, srcSize); if (ZSTD_isError(errorCode)) EXM_THROW(33, "Error initializing CStream : %s", ZSTD_getErrorName(errorCode)); } } free(dictBuffer); @@ -408,8 +408,9 @@ int FIO_compressFilename(const char* dstFileName, const char* srcFileName, const char* dictFileName, int compressionLevel) { clock_t const start = clock(); + U64 const srcSize = UTIL_getFileSize(srcFileName); - cRess_t const ress = FIO_createCResources(dictFileName, compressionLevel); + cRess_t const ress = FIO_createCResources(dictFileName, compressionLevel, srcSize); int const result = FIO_compressFilename_dstFile(ress, dstFileName, srcFileName); double const seconds = (double)(clock() - start) / CLOCKS_PER_SEC; @@ -428,7 +429,8 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile size_t dfnSize = FNSPACE; char* dstFileName = (char*)malloc(FNSPACE); size_t const suffixSize = suffix ? strlen(suffix) : 0; - cRess_t ress = FIO_createCResources(dictFileName, compressionLevel); + U64 const srcSize = (nbFiles != 1) ? 0 : UTIL_getFileSize(inFileNamesTable[0]) ; + cRess_t ress = FIO_createCResources(dictFileName, compressionLevel, srcSize); /* init */ if (dstFileName==NULL) EXM_THROW(27, "FIO_compressMultipleFilenames : allocation error for dstFileName"); diff --git a/programs/util.h b/programs/util.h index 9d28c82d7..aabebe961 100644 --- a/programs/util.h +++ b/programs/util.h @@ -31,12 +31,12 @@ extern "C" { /* Unix Large Files support (>4GB) */ -#if !defined(__LP64__) /* No point defining Large file for 64 bit */ -# define _FILE_OFFSET_BITS 64 /* turn off_t into a 64-bit type for ftello, fseeko */ -# if defined(__sun__) /* Sun Solaris 32-bits requires specific definitions */ -# define _LARGEFILE_SOURCE /* fseeko, ftello */ -# else -# define _LARGEFILE64_SOURCE /* off64_t, fseeko64, ftello64 */ +#if !defined(__LP64__) /* No point defining Large file for 64 bit */ +# define _FILE_OFFSET_BITS 64 /* turn off_t into a 64-bit type for ftello, fseeko */ +# if defined(__sun__) && !defined(_LARGEFILE_SOURCE) /* Sun Solaris 32-bits requires specific definitions */ +# define _LARGEFILE_SOURCE /* fseeko, ftello */ +# elif !defined(_LARGEFILE64_SOURCE) +# define _LARGEFILE64_SOURCE /* off64_t, fseeko64, ftello64 */ # endif #endif diff --git a/programs/zstd.1 b/programs/zstd.1 index a23616529..c262a0c6a 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -89,6 +89,10 @@ It also features a very fast decoder, with speed > 500 MB/s per core. .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. +.TP +.BR -- + All arguments after -- are treated as files + .SH DICTIONARY .PP diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 4a58b05b8..6d1d9f649 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -23,7 +23,7 @@ #endif #ifndef ZSTDCLI_CLEVEL_MAX -# define ZSTDCLI_CLEVEL_MAX 19 +# define ZSTDCLI_CLEVEL_MAX 19 /* when not using --ultra */ #endif @@ -51,13 +51,11 @@ #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) # include /* _isatty */ # define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) +#elif defined(_POSIX_C_SOURCE) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || (defined(__APPLE__) && defined(__MACH__)) /* https://sourceforge.net/p/predef/wiki/OperatingSystems/ */ +# include /* isatty */ +# define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) #else -# if defined(_POSIX_C_SOURCE) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || (defined(__APPLE__) && defined(__MACH__)) /* https://sourceforge.net/p/predef/wiki/OperatingSystems/ */ -# include /* isatty */ -# define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) -# else -# define IS_CONSOLE(stdStream) 0 -# endif +# define IS_CONSOLE(stdStream) 0 #endif @@ -144,6 +142,7 @@ static int usage_advanced(const char* programName) DISPLAY( "--test : test compressed file integrity \n"); DISPLAY( "--[no-]sparse : sparse mode (default:enabled on file, disabled on stdout)\n"); #endif + DISPLAY( "-- : All arguments after \"--\" are treated as files \n"); #ifndef ZSTD_NODICT DISPLAY( "\n"); DISPLAY( "Dictionary builder :\n"); @@ -194,7 +193,7 @@ static unsigned readU32FromChar(const char** stringPtr) #define CLEAN_RETURN(i) { operationResult = (i); goto _end; } -int main(int argCount, char** argv) +int main(int argCount, const char* argv[]) { int argNb, bench=0, @@ -209,7 +208,8 @@ int main(int argCount, char** argv) nextArgumentIsMaxDict=0, nextArgumentIsDictID=0, nextArgumentIsFile=0, - ultra=0; + ultra=0, + lastCommand = 0; int cLevel = ZSTDCLI_CLEVEL_DEFAULT; int cLevelLast = 1; unsigned recursive = 0; @@ -218,13 +218,12 @@ int main(int argCount, char** argv) const char* programName = argv[0]; const char* outFileName = NULL; const char* dictFileName = NULL; - char* dynNameSpace = NULL; unsigned maxDictSize = g_defaultMaxDictSize; unsigned dictID = 0; int dictCLevel = g_defaultDictCLevel; unsigned dictSelect = g_defaultSelectivityLevel; #ifdef UTIL_HAS_CREATEFILELIST - const char** fileNamesTable = NULL; + const char** extendedFileList = NULL; char* fileNamesBuf = NULL; unsigned fileNamesNb; #endif @@ -270,8 +269,8 @@ int main(int argCount, char** argv) if (!strcmp(argument, "--no-sparse")) { FIO_setSparseWrite(0); continue; } if (!strcmp(argument, "--test")) { testmode=1; decode=1; continue; } if (!strcmp(argument, "--train")) { dictBuild=1; outFileName=g_defaultDictName; continue; } - if (!strcmp(argument, "--maxdict")) { nextArgumentIsMaxDict=1; continue; } - if (!strcmp(argument, "--dictID")) { nextArgumentIsDictID=1; continue; } + if (!strcmp(argument, "--maxdict")) { nextArgumentIsMaxDict=1; lastCommand=1; continue; } + if (!strcmp(argument, "--dictID")) { nextArgumentIsDictID=1; lastCommand=1; continue; } if (!strcmp(argument, "--keep")) { FIO_setRemoveSrcFile(0); continue; } if (!strcmp(argument, "--rm")) { FIO_setRemoveSrcFile(1); continue; } @@ -289,6 +288,10 @@ int main(int argCount, char** argv) argument++; while (argument[0]!=0) { + if (lastCommand) { + DISPLAY("error : command must be followed by argument \n"); + return 1; + } #ifndef ZSTD_NOCOMPRESS /* compression Level */ if ((*argument>='0') && (*argument<='9')) { @@ -311,7 +314,7 @@ int main(int argCount, char** argv) case 'c': forceStdout=1; outFileName=stdoutmark; argument++; break; /* Use file content as dictionary */ - case 'D': nextEntryIsDictionary = 1; argument++; break; + case 'D': nextEntryIsDictionary = 1; lastCommand = 1; argument++; break; /* Overwrite */ case 'f': FIO_overwriteMode(); forceStdout=1; argument++; break; @@ -332,7 +335,7 @@ int main(int argCount, char** argv) case 't': testmode=1; decode=1; argument++; break; /* destination file name */ - case 'o': nextArgumentIsOutFileName=1; argument++; break; + case 'o': nextArgumentIsOutFileName=1; lastCommand=1; argument++; break; #ifdef UTIL_HAS_CREATEFILELIST /* recursive */ @@ -396,6 +399,7 @@ int main(int argCount, char** argv) if (nextArgumentIsMaxDict) { nextArgumentIsMaxDict = 0; + lastCommand = 0; maxDictSize = readU32FromChar(&argument); if (toupper(*argument)=='K') maxDictSize <<= 10; if (toupper(*argument)=='M') maxDictSize <<= 20; @@ -404,6 +408,7 @@ int main(int argCount, char** argv) if (nextArgumentIsDictID) { nextArgumentIsDictID = 0; + lastCommand = 0; dictID = readU32FromChar(&argument); continue; } @@ -412,12 +417,14 @@ int main(int argCount, char** argv) if (nextEntryIsDictionary) { nextEntryIsDictionary = 0; + lastCommand = 0; dictFileName = argument; continue; } if (nextArgumentIsOutFileName) { nextArgumentIsOutFileName = 0; + lastCommand = 0; outFileName = argument; if (!strcmp(outFileName, "-")) outFileName = stdoutmark; continue; @@ -427,17 +434,19 @@ int main(int argCount, char** argv) filenameTable[filenameIdx++] = argument; } + if (lastCommand) { DISPLAY("error : command must be followed by argument \n"); return 1; } /* forgotten argument */ + /* Welcome message (if verbose) */ DISPLAYLEVEL(3, WELCOME_MESSAGE); #ifdef UTIL_HAS_CREATEFILELIST - if (recursive) { - fileNamesTable = UTIL_createFileList(filenameTable, filenameIdx, &fileNamesBuf, &fileNamesNb); - if (fileNamesTable) { + if (recursive) { /* at this stage, filenameTable is a list of paths, which can contain both files and directories */ + extendedFileList = UTIL_createFileList(filenameTable, filenameIdx, &fileNamesBuf, &fileNamesNb); + if (extendedFileList) { unsigned u; - for (u=0; u tmpCompressed $ZSTD tmp --stdout > tmpCompressed # long command format +$ECHO "test : compress to named file" +rm tmpCompressed +$ZSTD tmp -o tmpCompressed +ls tmpCompressed # must work +$ECHO "test : -o must be followed by filename (must fail)" +$ZSTD tmp -of tmpCompressed && die "-o must be followed by filename " +$ECHO "test : force write, correct order" +$ZSTD tmp -fo tmpCompressed +$ECHO "test : forgotten argument" +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 : null-length file roundtrip" @@ -183,18 +194,26 @@ $ECHO "- Create first dictionary" $ZSTD --train *.c ../programs/*.c -o tmpDict cp $TESTFILE tmp $ZSTD -f tmp -D tmpDict -$ZSTD -d tmp.zst -D tmpDict -of result +$ZSTD -d tmp.zst -D tmpDict -fo result diff $TESTFILE result $ECHO "- Create second (different) dictionary" $ZSTD --train *.c ../programs/*.c ../programs/*.h -o tmpDictC -$ZSTD -d tmp.zst -D tmpDictC -of result && die "wrong dictionary not detected!" +$ZSTD -d tmp.zst -D tmpDictC -fo result && die "wrong dictionary not detected!" $ECHO "- Create dictionary with short dictID" $ZSTD --train *.c ../programs/*.c --dictID 1 -o tmpDict1 cmp tmpDict tmpDict1 && die "dictionaries should have different ID !" +$ECHO "- Create dictionary with wrong dictID parameter order (must fail)" +$ZSTD --train *.c ../programs/*.c --dictID -o 1 tmpDict1 && die "wrong order : --dictID must be followed by argument " +$ECHO "- Create dictionary with size limit" +$ZSTD --train *.c ../programs/*.c -o tmpDict2 --maxdict 4K -v +$ECHO "- Create dictionary with wrong parameter order (must fail)" +$ZSTD --train *.c ../programs/*.c -o tmpDict2 --maxdict -v 4K && die "wrong order : --maxdict must be followed by argument " $ECHO "- Compress without dictID" $ZSTD -f tmp -D tmpDict1 --no-dictID -$ZSTD -d tmp.zst -D tmpDict -of result +$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 " $ECHO "- Compress multiple files with dictionary" rm -rf dirTestDict mkdir dirTestDict diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index d10d4f125..8486013c2 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -358,34 +358,33 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres { static const U32 maxSrcLog = 24; static const U32 maxSampleLog = 19; + size_t const srcBufferSize = (size_t)1<= srcBufferSize) maxTestSize = srcBufferSize-1; + { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? 0 : maxTestSize; + size_t const resetError = ZSTD_resetCStream(zc, pledgedSrcSize); + CHECK(ZSTD_isError(resetError), "ZSTD_resetCStream error : %s", ZSTD_getErrorName(resetError)); + } + } else { + U32 const testLog = FUZ_rand(&lseed) % maxSrcLog; U32 const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (testLog/3))) + 1; maxTestSize = FUZ_rLogLength(&lseed, testLog); - dictSize = ((FUZ_rand(&lseed)&63)==1) ? FUZ_randomLength(&lseed, maxSampleLog) : 0; + oldTestLog = testLog; /* random dictionary selection */ + dictSize = ((FUZ_rand(&lseed)&63)==1) ? FUZ_randomLength(&lseed, maxSampleLog) : 0; { size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize); dict = srcBuffer + dictStart; } - { ZSTD_parameters params = ZSTD_getParams(cLevel, 0, dictSize); + { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? 0 : maxTestSize; + ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize); params.fParams.checksumFlag = FUZ_rand(&lseed) & 1; params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1; - { size_t const initError = ZSTD_initCStream_advanced(zc, dict, dictSize, params, 0); + { size_t const initError = ZSTD_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize); CHECK (ZSTD_isError(initError),"ZSTD_initCStream_advanced error : %s", ZSTD_getErrorName(initError)); } } } /* multi-segments compression test */ XXH64_reset(&xxhState, 0); - nbChunks = (FUZ_rand(&lseed) & 127) + 2; { ZSTD_outBuffer outBuff = { cBuffer, cBufferSize, 0 } ; - for (n=0, cSize=0, totalTestSize=0 ; (nmsg and return Z_STREAM_ERROR. Supported methods: - deflateInit -- deflate (with exception of Z_FULL_FLUSH) +- deflate (with exception of Z_FULL_FLUSH, Z_BLOCK, and Z_TREES) - deflateSetDictionary - deflateEnd +- deflateReset - deflateBound - inflateInit - inflate - inflateSetDictionary +- inflateReset +- inflateReset2 - compress - compress2 - compressBound @@ -88,15 +144,13 @@ Ignored methods (they do nothing): Unsupported methods: - gzip file access functions - deflateCopy -- deflateReset - deflateTune - deflatePending - deflatePrime - deflateSetHeader - inflateGetDictionary - inflateCopy -- inflateReset -- inflateReset2 +- inflateSync - inflatePrime - inflateMark - inflateGetHeader diff --git a/zlibWrapper/examples/example.c b/zlibWrapper/examples/example.c index bbb2cd5a3..20ed81d57 100644 --- a/zlibWrapper/examples/example.c +++ b/zlibWrapper/examples/example.c @@ -45,12 +45,12 @@ } \ } -z_const char hello[] = "hello, hello!"; +z_const char hello[] = "hello, hello! I said hello, hello!"; /* "hello world" would be more standard, but the repeated "hello" * stresses the compression code better, sorry... */ -const char dictionary[] = "hello"; +const char dictionary[] = "hello, hello!"; uLong dictId; /* Adler32 value of the dictionary */ void test_deflate OF((Byte *compr, uLong comprLen)); @@ -156,7 +156,7 @@ void test_gzio(fname, uncompr, uncomprLen) fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err)); exit(1); } - if (gzprintf(file, ", %s!", "hello") != 8) { + if (gzprintf(file, ", %s! I said hello, hello!", "hello") != 8+21) { fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err)); exit(1); } @@ -182,7 +182,7 @@ void test_gzio(fname, uncompr, uncomprLen) } pos = gzseek(file, -8L, SEEK_CUR); - if (pos != 6 || gztell(file) != pos) { + if (pos != 6+21 || gztell(file) != pos) { fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n", (long)pos, (long)gztell(file)); exit(1); @@ -203,7 +203,7 @@ void test_gzio(fname, uncompr, uncomprLen) fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err)); exit(1); } - if (strcmp((char*)uncompr, hello + 6)) { + if (strcmp((char*)uncompr, hello + 6+21)) { fprintf(stderr, "bad gzgets after gzseek\n"); exit(1); } else { @@ -583,7 +583,7 @@ int main(argc, argv) printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n", ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags()); - if (isUsingZSTD()) printf("zstd version %s\n", zstdVersion()); + if (ZWRAP_isUsingZSTDcompression()) printf("zstd version %s\n", zstdVersion()); compr = (Byte*)calloc((uInt)comprLen, 1); uncompr = (Byte*)calloc((uInt)uncomprLen, 1); @@ -600,7 +600,7 @@ int main(argc, argv) #else test_compress(compr, comprLen, uncompr, uncomprLen); - if (!isUsingZSTD()) + if (!ZWRAP_isUsingZSTDcompression()) test_gzio((argc > 1 ? argv[1] : TESTFILE), uncompr, uncomprLen); #endif @@ -611,7 +611,7 @@ int main(argc, argv) test_large_deflate(compr, comprLen, uncompr, uncomprLen); test_large_inflate(compr, comprLen, uncompr, uncomprLen); - if (!isUsingZSTD()) { + if (!ZWRAP_isUsingZSTDcompression()) { test_flush(compr, &comprLen); test_sync(compr, comprLen, uncompr, uncomprLen); } diff --git a/zlibWrapper/examples/fitblk.c b/zlibWrapper/examples/fitblk.c new file mode 100644 index 000000000..e3fda3c80 --- /dev/null +++ b/zlibWrapper/examples/fitblk.c @@ -0,0 +1,253 @@ +/* fitblk.c: example of fitting compressed output to a specified size + Not copyrighted -- provided to the public domain + Version 1.1 25 November 2004 Mark Adler */ + +/* Version history: + 1.0 24 Nov 2004 First version + 1.1 25 Nov 2004 Change deflateInit2() to deflateInit() + Use fixed-size, stack-allocated raw buffers + Simplify code moving compression to subroutines + Use assert() for internal errors + Add detailed description of approach + */ + +/* Approach to just fitting a requested compressed size: + + fitblk performs three compression passes on a portion of the input + data in order to determine how much of that input will compress to + nearly the requested output block size. The first pass generates + enough deflate blocks to produce output to fill the requested + output size plus a specfied excess amount (see the EXCESS define + below). The last deflate block may go quite a bit past that, but + is discarded. The second pass decompresses and recompresses just + the compressed data that fit in the requested plus excess sized + buffer. The deflate process is terminated after that amount of + input, which is less than the amount consumed on the first pass. + The last deflate block of the result will be of a comparable size + to the final product, so that the header for that deflate block and + the compression ratio for that block will be about the same as in + the final product. The third compression pass decompresses the + result of the second step, but only the compressed data up to the + requested size minus an amount to allow the compressed stream to + complete (see the MARGIN define below). That will result in a + final compressed stream whose length is less than or equal to the + requested size. Assuming sufficient input and a requested size + greater than a few hundred bytes, the shortfall will typically be + less than ten bytes. + + If the input is short enough that the first compression completes + before filling the requested output size, then that compressed + stream is return with no recompression. + + EXCESS is chosen to be just greater than the shortfall seen in a + two pass approach similar to the above. That shortfall is due to + the last deflate block compressing more efficiently with a smaller + header on the second pass. EXCESS is set to be large enough so + that there is enough uncompressed data for the second pass to fill + out the requested size, and small enough so that the final deflate + block of the second pass will be close in size to the final deflate + block of the third and final pass. MARGIN is chosen to be just + large enough to assure that the final compression has enough room + to complete in all cases. + */ + +#include +#include +#include +//#include "zlib.h" +#include "zstd_zlibwrapper.h" + +#define LOG_FITBLK(...) /*printf(__VA_ARGS__)*/ +#define local static + +/* print nastygram and leave */ +local void quit(char *why) +{ + fprintf(stderr, "fitblk abort: %s\n", why); + exit(1); +} + +#define RAWLEN 4096 /* intermediate uncompressed buffer size */ + +/* compress from file to def until provided buffer is full or end of + input reached; return last deflate() return value, or Z_ERRNO if + there was read error on the file */ +local int partcompress(FILE *in, z_streamp def) +{ + int ret, flush; + unsigned char raw[RAWLEN]; + + flush = Z_SYNC_FLUSH; + do { + def->avail_in = fread(raw, 1, RAWLEN, in); + if (ferror(in)) + return Z_ERRNO; + def->next_in = raw; + if (feof(in)) + flush = Z_FINISH; + LOG_FITBLK("partcompress1 avail_in=%d total_in=%d avail_out=%d total_out=%d\n", (int)def->avail_in, (int)def->total_in, (int)def->avail_out, (int)def->total_out); + ret = deflate(def, flush); + LOG_FITBLK("partcompress2 avail_in=%d total_in=%d avail_out=%d total_out=%d\n", (int)def->avail_in, (int)def->total_in, (int)def->avail_out, (int)def->total_out); + assert(ret != Z_STREAM_ERROR); + } while (def->avail_out != 0 && flush == Z_SYNC_FLUSH); + return ret; +} + +/* recompress from inf's input to def's output; the input for inf and + the output for def are set in those structures before calling; + return last deflate() return value, or Z_MEM_ERROR if inflate() + was not able to allocate enough memory when it needed to */ +local int recompress(z_streamp inf, z_streamp def) +{ + int ret, flush; + unsigned char raw[RAWLEN]; + + flush = Z_NO_FLUSH; + do { + /* decompress */ + inf->avail_out = RAWLEN; + inf->next_out = raw; + LOG_FITBLK("recompress1inflate avail_in=%d total_in=%d avail_out=%d total_out=%d\n", (int)inf->avail_in, (int)inf->total_in, (int)inf->avail_out, (int)inf->total_out); + ret = inflate(inf, Z_NO_FLUSH); + LOG_FITBLK("recompress2inflate avail_in=%d total_in=%d avail_out=%d total_out=%d\n", (int)inf->avail_in, (int)inf->total_in, (int)inf->avail_out, (int)inf->total_out); + assert(ret != Z_STREAM_ERROR && ret != Z_DATA_ERROR && + ret != Z_NEED_DICT); + if (ret == Z_MEM_ERROR) + return ret; + + /* compress what was decompresed until done or no room */ + def->avail_in = RAWLEN - inf->avail_out; + def->next_in = raw; + if (inf->avail_out != 0) + flush = Z_FINISH; + LOG_FITBLK("recompress1deflate avail_in=%d total_in=%d avail_out=%d total_out=%d\n", (int)def->avail_in, (int)def->total_in, (int)def->avail_out, (int)def->total_out); + ret = deflate(def, flush); + LOG_FITBLK("recompress2deflate avail_in=%d total_in=%d avail_out=%d total_out=%d\n", (int)def->avail_in, (int)def->total_in, (int)def->avail_out, (int)def->total_out); + assert(ret != Z_STREAM_ERROR); + } while (ret != Z_STREAM_END && def->avail_out != 0); + return ret; +} + +#define EXCESS 256 /* empirically determined stream overage */ +#define MARGIN 8 /* amount to back off for completion */ + +/* compress from stdin to fixed-size block on stdout */ +int main(int argc, char **argv) +{ + int ret; /* return code */ + unsigned size; /* requested fixed output block size */ + unsigned have; /* bytes written by deflate() call */ + unsigned char *blk; /* intermediate and final stream */ + unsigned char *tmp; /* close to desired size stream */ + z_stream def, inf; /* zlib deflate and inflate states */ + + /* get requested output size */ + if (argc != 2) + quit("need one argument: size of output block"); + ret = strtol(argv[1], argv + 1, 10); + if (argv[1][0] != 0) + quit("argument must be a number"); + if (ret < 8) /* 8 is minimum zlib stream size */ + quit("need positive size of 8 or greater"); + size = (unsigned)ret; + + printf("zlib version %s\n", ZLIB_VERSION); + if (ZWRAP_isUsingZSTDcompression()) printf("zstd version %s\n", zstdVersion()); + + /* allocate memory for buffers and compression engine */ + blk = malloc(size + EXCESS); + def.zalloc = Z_NULL; + def.zfree = Z_NULL; + def.opaque = Z_NULL; + ret = deflateInit(&def, Z_DEFAULT_COMPRESSION); + if (ret != Z_OK || blk == NULL) + quit("out of memory"); + ret = ZWRAP_setPledgedSrcSize(&def, 1<<16); + if (ret != Z_OK) + quit("ZWRAP_setPledgedSrcSize"); + + /* compress from stdin until output full, or no more input */ + def.avail_out = size + EXCESS; + def.next_out = blk; + LOG_FITBLK("partcompress1 total_in=%d total_out=%d\n", (int)def.total_in, (int)def.total_out); + ret = partcompress(stdin, &def); + printf("partcompress total_in=%d total_out=%d\n", (int)def.total_in, (int)def.total_out); + if (ret == Z_ERRNO) + quit("error reading input"); + + /* if it all fit, then size was undersubscribed -- done! */ + if (ret == Z_STREAM_END && def.avail_out >= EXCESS) { + /* write block to stdout */ + have = size + EXCESS - def.avail_out; + // if (fwrite(blk, 1, have, stdout) != have || ferror(stdout)) + // quit("error writing output"); + + /* clean up and print results to stderr */ + ret = deflateEnd(&def); + assert(ret != Z_STREAM_ERROR); + free(blk); + fprintf(stderr, + "%u bytes unused out of %u requested (all input)\n", + size - have, size); + return 0; + } + + /* it didn't all fit -- set up for recompression */ + inf.zalloc = Z_NULL; + inf.zfree = Z_NULL; + inf.opaque = Z_NULL; + inf.avail_in = 0; + inf.next_in = Z_NULL; + ret = inflateInit(&inf); + tmp = malloc(size + EXCESS); + if (ret != Z_OK || tmp == NULL) + quit("out of memory"); + ret = deflateReset(&def); + assert(ret != Z_STREAM_ERROR); + + /* do first recompression close to the right amount */ + inf.avail_in = size + EXCESS; + inf.next_in = blk; + def.avail_out = size + EXCESS; + def.next_out = tmp; + LOG_FITBLK("recompress1 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out); + ret = recompress(&inf, &def); + LOG_FITBLK("recompress1 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out); + if (ret == Z_MEM_ERROR) + quit("out of memory"); + + /* set up for next reocmpression */ + ret = inflateReset(&inf); + assert(ret != Z_STREAM_ERROR); + ret = deflateReset(&def); + assert(ret != Z_STREAM_ERROR); + + /* do second and final recompression (third compression) */ + inf.avail_in = size - MARGIN; /* assure stream will complete */ + inf.next_in = tmp; + def.avail_out = size; + def.next_out = blk; + LOG_FITBLK("recompress2 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out); + ret = recompress(&inf, &def); + LOG_FITBLK("recompress2 inf.total_in=%d def.total_out=%d\n", (int)inf.total_in, (int)def.total_out); + if (ret == Z_MEM_ERROR) + quit("out of memory"); + assert(ret == Z_STREAM_END); /* otherwise MARGIN too small */ + + /* done -- write block to stdout */ + have = size - def.avail_out; +// if (fwrite(blk, 1, have, stdout) != have || ferror(stdout)) +// quit("error writing output"); + + /* clean up and print results to stderr */ + free(tmp); + ret = inflateEnd(&inf); + assert(ret != Z_STREAM_ERROR); + ret = deflateEnd(&def); + assert(ret != Z_STREAM_ERROR); + free(blk); + fprintf(stderr, + "%u bytes unused out of %u requested (%lu input)\n", + size - have, size, def.total_in); + return 0; +} diff --git a/zlibWrapper/examples/fitblk_original.c b/zlibWrapper/examples/fitblk_original.c new file mode 100644 index 000000000..c61de5c99 --- /dev/null +++ b/zlibWrapper/examples/fitblk_original.c @@ -0,0 +1,233 @@ +/* fitblk.c: example of fitting compressed output to a specified size + Not copyrighted -- provided to the public domain + Version 1.1 25 November 2004 Mark Adler */ + +/* Version history: + 1.0 24 Nov 2004 First version + 1.1 25 Nov 2004 Change deflateInit2() to deflateInit() + Use fixed-size, stack-allocated raw buffers + Simplify code moving compression to subroutines + Use assert() for internal errors + Add detailed description of approach + */ + +/* Approach to just fitting a requested compressed size: + + fitblk performs three compression passes on a portion of the input + data in order to determine how much of that input will compress to + nearly the requested output block size. The first pass generates + enough deflate blocks to produce output to fill the requested + output size plus a specfied excess amount (see the EXCESS define + below). The last deflate block may go quite a bit past that, but + is discarded. The second pass decompresses and recompresses just + the compressed data that fit in the requested plus excess sized + buffer. The deflate process is terminated after that amount of + input, which is less than the amount consumed on the first pass. + The last deflate block of the result will be of a comparable size + to the final product, so that the header for that deflate block and + the compression ratio for that block will be about the same as in + the final product. The third compression pass decompresses the + result of the second step, but only the compressed data up to the + requested size minus an amount to allow the compressed stream to + complete (see the MARGIN define below). That will result in a + final compressed stream whose length is less than or equal to the + requested size. Assuming sufficient input and a requested size + greater than a few hundred bytes, the shortfall will typically be + less than ten bytes. + + If the input is short enough that the first compression completes + before filling the requested output size, then that compressed + stream is return with no recompression. + + EXCESS is chosen to be just greater than the shortfall seen in a + two pass approach similar to the above. That shortfall is due to + the last deflate block compressing more efficiently with a smaller + header on the second pass. EXCESS is set to be large enough so + that there is enough uncompressed data for the second pass to fill + out the requested size, and small enough so that the final deflate + block of the second pass will be close in size to the final deflate + block of the third and final pass. MARGIN is chosen to be just + large enough to assure that the final compression has enough room + to complete in all cases. + */ + +#include +#include +#include +#include "zlib.h" + +#define local static + +/* print nastygram and leave */ +local void quit(char *why) +{ + fprintf(stderr, "fitblk abort: %s\n", why); + exit(1); +} + +#define RAWLEN 4096 /* intermediate uncompressed buffer size */ + +/* compress from file to def until provided buffer is full or end of + input reached; return last deflate() return value, or Z_ERRNO if + there was read error on the file */ +local int partcompress(FILE *in, z_streamp def) +{ + int ret, flush; + unsigned char raw[RAWLEN]; + + flush = Z_NO_FLUSH; + do { + def->avail_in = fread(raw, 1, RAWLEN, in); + if (ferror(in)) + return Z_ERRNO; + def->next_in = raw; + if (feof(in)) + flush = Z_FINISH; + ret = deflate(def, flush); + assert(ret != Z_STREAM_ERROR); + } while (def->avail_out != 0 && flush == Z_NO_FLUSH); + return ret; +} + +/* recompress from inf's input to def's output; the input for inf and + the output for def are set in those structures before calling; + return last deflate() return value, or Z_MEM_ERROR if inflate() + was not able to allocate enough memory when it needed to */ +local int recompress(z_streamp inf, z_streamp def) +{ + int ret, flush; + unsigned char raw[RAWLEN]; + + flush = Z_NO_FLUSH; + do { + /* decompress */ + inf->avail_out = RAWLEN; + inf->next_out = raw; + ret = inflate(inf, Z_NO_FLUSH); + assert(ret != Z_STREAM_ERROR && ret != Z_DATA_ERROR && + ret != Z_NEED_DICT); + if (ret == Z_MEM_ERROR) + return ret; + + /* compress what was decompresed until done or no room */ + def->avail_in = RAWLEN - inf->avail_out; + def->next_in = raw; + if (inf->avail_out != 0) + flush = Z_FINISH; + ret = deflate(def, flush); + assert(ret != Z_STREAM_ERROR); + } while (ret != Z_STREAM_END && def->avail_out != 0); + return ret; +} + +#define EXCESS 256 /* empirically determined stream overage */ +#define MARGIN 8 /* amount to back off for completion */ + +/* compress from stdin to fixed-size block on stdout */ +int main(int argc, char **argv) +{ + int ret; /* return code */ + unsigned size; /* requested fixed output block size */ + unsigned have; /* bytes written by deflate() call */ + unsigned char *blk; /* intermediate and final stream */ + unsigned char *tmp; /* close to desired size stream */ + z_stream def, inf; /* zlib deflate and inflate states */ + + /* get requested output size */ + if (argc != 2) + quit("need one argument: size of output block"); + ret = strtol(argv[1], argv + 1, 10); + if (argv[1][0] != 0) + quit("argument must be a number"); + if (ret < 8) /* 8 is minimum zlib stream size */ + quit("need positive size of 8 or greater"); + size = (unsigned)ret; + + /* allocate memory for buffers and compression engine */ + blk = malloc(size + EXCESS); + def.zalloc = Z_NULL; + def.zfree = Z_NULL; + def.opaque = Z_NULL; + ret = deflateInit(&def, Z_DEFAULT_COMPRESSION); + if (ret != Z_OK || blk == NULL) + quit("out of memory"); + + /* compress from stdin until output full, or no more input */ + def.avail_out = size + EXCESS; + def.next_out = blk; + ret = partcompress(stdin, &def); + if (ret == Z_ERRNO) + quit("error reading input"); + + /* if it all fit, then size was undersubscribed -- done! */ + if (ret == Z_STREAM_END && def.avail_out >= EXCESS) { + /* write block to stdout */ + have = size + EXCESS - def.avail_out; + if (fwrite(blk, 1, have, stdout) != have || ferror(stdout)) + quit("error writing output"); + + /* clean up and print results to stderr */ + ret = deflateEnd(&def); + assert(ret != Z_STREAM_ERROR); + free(blk); + fprintf(stderr, + "%u bytes unused out of %u requested (all input)\n", + size - have, size); + return 0; + } + + /* it didn't all fit -- set up for recompression */ + inf.zalloc = Z_NULL; + inf.zfree = Z_NULL; + inf.opaque = Z_NULL; + inf.avail_in = 0; + inf.next_in = Z_NULL; + ret = inflateInit(&inf); + tmp = malloc(size + EXCESS); + if (ret != Z_OK || tmp == NULL) + quit("out of memory"); + ret = deflateReset(&def); + assert(ret != Z_STREAM_ERROR); + + /* do first recompression close to the right amount */ + inf.avail_in = size + EXCESS; + inf.next_in = blk; + def.avail_out = size + EXCESS; + def.next_out = tmp; + ret = recompress(&inf, &def); + if (ret == Z_MEM_ERROR) + quit("out of memory"); + + /* set up for next reocmpression */ + ret = inflateReset(&inf); + assert(ret != Z_STREAM_ERROR); + ret = deflateReset(&def); + assert(ret != Z_STREAM_ERROR); + + /* do second and final recompression (third compression) */ + inf.avail_in = size - MARGIN; /* assure stream will complete */ + inf.next_in = tmp; + def.avail_out = size; + def.next_out = blk; + ret = recompress(&inf, &def); + if (ret == Z_MEM_ERROR) + quit("out of memory"); + assert(ret == Z_STREAM_END); /* otherwise MARGIN too small */ + + /* done -- write block to stdout */ + have = size - def.avail_out; + if (fwrite(blk, 1, have, stdout) != have || ferror(stdout)) + quit("error writing output"); + + /* clean up and print results to stderr */ + free(tmp); + ret = inflateEnd(&inf); + assert(ret != Z_STREAM_ERROR); + ret = deflateEnd(&def); + assert(ret != Z_STREAM_ERROR); + free(blk); + fprintf(stderr, + "%u bytes unused out of %u requested (%lu input)\n", + size - have, size, def.total_in); + return 0; +} diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c new file mode 100644 index 000000000..d16fcfdd5 --- /dev/null +++ b/zlibWrapper/examples/zwrapbench.c @@ -0,0 +1,1002 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Przemyslaw Skibinski, 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. + */ + + +/* ************************************* +* Includes +***************************************/ +#include "util.h" /* Compiler options, UTIL_GetFileSize, UTIL_sleep */ +#include /* malloc, free */ +#include /* memset */ +#include /* fprintf, fopen, ftello64 */ +#include /* clock_t, clock, CLOCKS_PER_SEC */ +#include /* toupper */ + +#include "mem.h" +#define ZSTD_STATIC_LINKING_ONLY +#include "zstd.h" +#include "datagen.h" /* RDG_genBuffer */ +#include "xxhash.h" + +#include "zstd_zlibwrapper.h" + + + +/*-************************************ +* Tuning parameters +**************************************/ +#ifndef ZSTDCLI_CLEVEL_DEFAULT +# define ZSTDCLI_CLEVEL_DEFAULT 3 +#endif + + + +/*-************************************ +* Constants +**************************************/ +#define COMPRESSOR_NAME "Zstandard wrapper for zlib command line interface" +#ifndef ZSTD_VERSION +# define ZSTD_VERSION "v" ZSTD_VERSION_STRING +#endif +#define AUTHOR "Yann Collet" +#define WELCOME_MESSAGE "*** %s %i-bits %s, by %s ***\n", COMPRESSOR_NAME, (int)(sizeof(size_t)*8), ZSTD_VERSION, AUTHOR + +#ifndef ZSTD_GIT_COMMIT +# define ZSTD_GIT_COMMIT_STRING "" +#else +# define ZSTD_GIT_COMMIT_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_GIT_COMMIT) +#endif + +#define NBLOOPS 3 +#define TIMELOOP_MICROSEC 1*1000000ULL /* 1 second */ +#define ACTIVEPERIOD_MICROSEC 70*1000000ULL /* 70 seconds */ +#define COOLPERIOD_SEC 10 + +#define KB *(1 <<10) +#define MB *(1 <<20) +#define GB *(1U<<30) + +static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31)); + +static U32 g_compressibilityDefault = 50; + + +/* ************************************* +* console display +***************************************/ +#define DEFAULT_DISPLAY_LEVEL 2 +#define DISPLAY(...) fprintf(displayOut, __VA_ARGS__) +#define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } +static U32 g_displayLevel = DEFAULT_DISPLAY_LEVEL; /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + progression; 4 : + information */ +static FILE* displayOut; + +#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ + if ((clock() - g_time > refreshRate) || (g_displayLevel>=4)) \ + { g_time = clock(); DISPLAY(__VA_ARGS__); \ + if (g_displayLevel>=4) fflush(stdout); } } +static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; +static clock_t g_time = 0; + + +/* ************************************* +* Exceptions +***************************************/ +#ifndef DEBUG +# define DEBUG 0 +#endif +#define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__); +#define EXM_THROW(error, ...) \ +{ \ + DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \ + DISPLAYLEVEL(1, "Error %i : ", error); \ + DISPLAYLEVEL(1, __VA_ARGS__); \ + DISPLAYLEVEL(1, "\n"); \ + exit(error); \ +} + + +/* ************************************* +* Benchmark Parameters +***************************************/ +static U32 g_nbIterations = NBLOOPS; +static size_t g_blockSize = 0; +int g_additionalParam = 0; + +void BMK_setNotificationLevel(unsigned level) { g_displayLevel=level; } + +void BMK_setAdditionalParam(int additionalParam) { g_additionalParam=additionalParam; } + +void BMK_SetNbIterations(unsigned nbLoops) +{ + g_nbIterations = nbLoops; + DISPLAYLEVEL(3, "- test >= %u seconds per compression / decompression -\n", g_nbIterations); +} + +void BMK_SetBlockSize(size_t blockSize) +{ + g_blockSize = blockSize; + DISPLAYLEVEL(2, "using blocks of size %u KB \n", (U32)(blockSize>>10)); +} + + +/* ******************************************************** +* Bench functions +**********************************************************/ +typedef struct +{ + const char* srcPtr; + size_t srcSize; + char* cPtr; + size_t cRoom; + size_t cSize; + char* resPtr; + size_t resSize; +} blockParam_t; + +typedef enum { BMK_ZSTD, BMK_ZSTD_STREAM, BMK_ZLIB, BMK_ZWRAP_ZLIB, BMK_ZWRAP_ZSTD, BMK_ZLIB_REUSE, BMK_ZWRAP_ZLIB_REUSE, BMK_ZWRAP_ZSTD_REUSE } BMK_compressor; + + +#define MIN(a,b) ((a)<(b) ? (a) : (b)) +#define MAX(a,b) ((a)>(b) ? (a) : (b)) + +static int BMK_benchMem(const void* srcBuffer, size_t srcSize, + const char* displayName, int cLevel, + const size_t* fileSizes, U32 nbFiles, + const void* dictBuffer, size_t dictBufferSize, BMK_compressor compressor) +{ + size_t const blockSize = (g_blockSize>=32 ? g_blockSize : srcSize) + (!srcSize) /* avoid div by 0 */ ; + size_t const avgSize = MIN(g_blockSize, (srcSize / nbFiles)); + U32 const maxNbBlocks = (U32) ((srcSize + (blockSize-1)) / blockSize) + nbFiles; + blockParam_t* const blockTable = (blockParam_t*) malloc(maxNbBlocks * sizeof(blockParam_t)); + size_t const maxCompressedSize = ZSTD_compressBound(srcSize) + (maxNbBlocks * 1024); /* add some room for safety */ + void* const compressedBuffer = malloc(maxCompressedSize); + void* const resultBuffer = malloc(srcSize); + ZSTD_CCtx* const ctx = ZSTD_createCCtx(); + ZSTD_DCtx* const dctx = ZSTD_createDCtx(); + U32 nbBlocks; + UTIL_time_t ticksPerSecond; + + /* checks */ + if (!compressedBuffer || !resultBuffer || !blockTable || !ctx || !dctx) + EXM_THROW(31, "allocation error : not enough memory"); + + /* init */ + if (strlen(displayName)>17) displayName += strlen(displayName)-17; /* can only display 17 characters */ + UTIL_initTimer(&ticksPerSecond); + + /* Init blockTable data */ + { const char* srcPtr = (const char*)srcBuffer; + char* cPtr = (char*)compressedBuffer; + char* resPtr = (char*)resultBuffer; + U32 fileNb; + for (nbBlocks=0, fileNb=0; fileNb ACTIVEPERIOD_MICROSEC) { + DISPLAYLEVEL(2, "\rcooling down ... \r"); + UTIL_sleep(COOLPERIOD_SEC); + UTIL_getTime(&coolTime); + } + + /* Compression */ + DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->\r", marks[markNb], displayName, (U32)srcSize); + if (!cCompleted) memset(compressedBuffer, 0xE5, maxCompressedSize); /* warm up and erase result buffer */ + + UTIL_sleepMilli(1); /* give processor time to other processes */ + UTIL_waitForNextTick(ticksPerSecond); + UTIL_getTime(&clockStart); + + if (!cCompleted) { /* still some time to do compression tests */ + U32 nbLoops = 0; + if (compressor == BMK_ZSTD) { + ZSTD_parameters const zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize); + ZSTD_customMem const cmem = { NULL, NULL, NULL }; + ZSTD_CDict* cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, zparams, cmem); + if (cdict==NULL) EXM_THROW(1, "ZSTD_createCDict_advanced() allocation failure"); + + do { + U32 blockNb; + for (blockNb=0; blockNbmaxTime; + } } + + cSize = 0; + { U32 blockNb; for (blockNb=0; blockNb%10u (%5.3f),%6.1f MB/s\r", + marks[markNb], displayName, (U32)srcSize, (U32)cSize, ratio, + (double)srcSize / fastestC ); + + (void)fastestD; (void)crcOrig; /* unused when decompression disabled */ +#if 1 + /* Decompression */ + if (!dCompleted) memset(resultBuffer, 0xD6, srcSize); /* warm result buffer */ + + UTIL_sleepMilli(1); /* give processor time to other processes */ + UTIL_waitForNextTick(ticksPerSecond); + UTIL_getTime(&clockStart); + + if (!dCompleted) { + U32 nbLoops = 0; + if (compressor == BMK_ZSTD) { + ZSTD_DDict* ddict = ZSTD_createDDict(dictBuffer, dictBufferSize); + if (!ddict) EXM_THROW(2, "ZSTD_createDDict() allocation failure"); + do { + U32 blockNb; + for (blockNb=0; blockNbmaxTime; + } } + + markNb = (markNb+1) % NB_MARKS; + DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.3f),%6.1f MB/s ,%6.1f MB/s\r", + marks[markNb], displayName, (U32)srcSize, (U32)cSize, ratio, + (double)srcSize / fastestC, + (double)srcSize / fastestD ); + + /* CRC Checking */ + { U64 const crcCheck = XXH64(resultBuffer, srcSize, 0); + if (crcOrig!=crcCheck) { + size_t u; + DISPLAY("!!! WARNING !!! %14s : Invalid Checksum : %x != %x \n", displayName, (unsigned)crcOrig, (unsigned)crcCheck); + for (u=0; u u) break; + bacc += blockTable[segNb].srcSize; + } + pos = (U32)(u - bacc); + bNb = pos / (128 KB); + DISPLAY("(block %u, sub %u, pos %u) \n", segNb, bNb, pos); + break; + } + if (u==srcSize-1) { /* should never happen */ + DISPLAY("no difference detected\n"); + } } + break; + } } /* CRC Checking */ +#endif + } /* for (testNb = 1; testNb <= (g_nbIterations + !g_nbIterations); testNb++) */ + + if (g_displayLevel == 1) { + double cSpeed = (double)srcSize / fastestC; + double dSpeed = (double)srcSize / fastestD; + if (g_additionalParam) + DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s (param=%d)\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName, g_additionalParam); + else + DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName); + } + DISPLAYLEVEL(2, "%2i#\n", cLevel); + } /* Bench */ + + /* clean up */ + free(blockTable); + free(compressedBuffer); + free(resultBuffer); + ZSTD_freeCCtx(ctx); + ZSTD_freeDCtx(dctx); + return 0; +} + + +static size_t BMK_findMaxMem(U64 requiredMem) +{ + size_t const step = 64 MB; + BYTE* testmem = NULL; + + requiredMem = (((requiredMem >> 26) + 1) << 26); + requiredMem += step; + if (requiredMem > maxMemory) requiredMem = maxMemory; + + do { + testmem = (BYTE*)malloc((size_t)requiredMem); + requiredMem -= step; + } while (!testmem); + + free(testmem); + return (size_t)(requiredMem); +} + +static void BMK_benchCLevel(void* srcBuffer, size_t benchedSize, + const char* displayName, int cLevel, int cLevelLast, + const size_t* fileSizes, unsigned nbFiles, + const void* dictBuffer, size_t dictBufferSize) +{ + int l; + + const char* pch = strrchr(displayName, '\\'); /* Windows */ + if (!pch) pch = strrchr(displayName, '/'); /* Linux */ + if (pch) displayName = pch+1; + + SET_HIGH_PRIORITY; + + if (g_displayLevel == 1 && !g_additionalParam) + DISPLAY("bench %s %s: input %u bytes, %u seconds, %u KB blocks\n", ZSTD_VERSION_STRING, ZSTD_GIT_COMMIT_STRING, (U32)benchedSize, g_nbIterations, (U32)(g_blockSize>>10)); + + if (cLevelLast < cLevel) cLevelLast = cLevel; + + DISPLAY("benchmarking zstd %s (using ZSTD_CCtx)\n", ZSTD_VERSION_STRING); + for (l=cLevel; l <= cLevelLast; l++) { + BMK_benchMem(srcBuffer, benchedSize, + displayName, l, + fileSizes, nbFiles, + dictBuffer, dictBufferSize, BMK_ZSTD); + } + + DISPLAY("benchmarking zstd %s (using ZSTD_CStream)\n", ZSTD_VERSION_STRING); + for (l=cLevel; l <= cLevelLast; l++) { + BMK_benchMem(srcBuffer, benchedSize, + displayName, l, + fileSizes, nbFiles, + dictBuffer, dictBufferSize, BMK_ZSTD_STREAM); + } + + DISPLAY("benchmarking zstd %s (using zlibWrapper)\n", ZSTD_VERSION_STRING); + for (l=cLevel; l <= cLevelLast; l++) { + BMK_benchMem(srcBuffer, benchedSize, + displayName, l, + fileSizes, nbFiles, + dictBuffer, dictBufferSize, BMK_ZWRAP_ZSTD_REUSE); + } + + DISPLAY("benchmarking zstd %s (zlibWrapper not reusing a context)\n", ZSTD_VERSION_STRING); + for (l=cLevel; l <= cLevelLast; l++) { + BMK_benchMem(srcBuffer, benchedSize, + displayName, l, + fileSizes, nbFiles, + dictBuffer, dictBufferSize, BMK_ZWRAP_ZSTD); + } + + + if (cLevelLast > Z_BEST_COMPRESSION) cLevelLast = Z_BEST_COMPRESSION; + + DISPLAY("\n"); + DISPLAY("benchmarking zlib %s\n", ZLIB_VERSION); + for (l=cLevel; l <= cLevelLast; l++) { + BMK_benchMem(srcBuffer, benchedSize, + displayName, l, + fileSizes, nbFiles, + dictBuffer, dictBufferSize, BMK_ZLIB_REUSE); + } + + DISPLAY("benchmarking zlib %s (zlib not reusing a context)\n", ZLIB_VERSION); + for (l=cLevel; l <= cLevelLast; l++) { + BMK_benchMem(srcBuffer, benchedSize, + displayName, l, + fileSizes, nbFiles, + dictBuffer, dictBufferSize, BMK_ZLIB); + } + + DISPLAY("benchmarking zlib %s (using zlibWrapper)\n", ZLIB_VERSION); + for (l=cLevel; l <= cLevelLast; l++) { + BMK_benchMem(srcBuffer, benchedSize, + displayName, l, + fileSizes, nbFiles, + dictBuffer, dictBufferSize, BMK_ZWRAP_ZLIB_REUSE); + } + + DISPLAY("benchmarking zlib %s (zlibWrapper not reusing a context)\n", ZLIB_VERSION); + for (l=cLevel; l <= cLevelLast; l++) { + BMK_benchMem(srcBuffer, benchedSize, + displayName, l, + fileSizes, nbFiles, + dictBuffer, dictBufferSize, BMK_ZWRAP_ZLIB); + } +} + + +/*! BMK_loadFiles() : + Loads `buffer` with content of files listed within `fileNamesTable`. + At most, fills `buffer` entirely */ +static void BMK_loadFiles(void* buffer, size_t bufferSize, + size_t* fileSizes, + const char** fileNamesTable, unsigned nbFiles) +{ + size_t pos = 0, totalSize = 0; + unsigned n; + for (n=0; n bufferSize-pos) fileSize = bufferSize-pos, nbFiles=n; /* buffer too small - stop after this file */ + { size_t const readSize = fread(((char*)buffer)+pos, 1, (size_t)fileSize, f); + if (readSize != (size_t)fileSize) EXM_THROW(11, "could not read %s", fileNamesTable[n]); + pos += readSize; } + fileSizes[n] = (size_t)fileSize; + totalSize += (size_t)fileSize; + fclose(f); + } + + if (totalSize == 0) EXM_THROW(12, "no data to bench"); +} + +static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, + const char* dictFileName, int cLevel, int cLevelLast) +{ + void* srcBuffer; + size_t benchedSize; + void* dictBuffer = NULL; + size_t dictBufferSize = 0; + size_t* fileSizes = (size_t*)malloc(nbFiles * sizeof(size_t)); + U64 const totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, nbFiles); + char mfName[20] = {0}; + + if (!fileSizes) EXM_THROW(12, "not enough memory for fileSizes"); + + /* Load dictionary */ + if (dictFileName != NULL) { + U64 dictFileSize = UTIL_getFileSize(dictFileName); + if (dictFileSize > 64 MB) EXM_THROW(10, "dictionary file %s too large", dictFileName); + dictBufferSize = (size_t)dictFileSize; + dictBuffer = malloc(dictBufferSize); + if (dictBuffer==NULL) EXM_THROW(11, "not enough memory for dictionary (%u bytes)", (U32)dictBufferSize); + BMK_loadFiles(dictBuffer, dictBufferSize, fileSizes, &dictFileName, 1); + } + + /* Memory allocation & restrictions */ + benchedSize = BMK_findMaxMem(totalSizeToLoad * 3) / 3; + if ((U64)benchedSize > totalSizeToLoad) benchedSize = (size_t)totalSizeToLoad; + if (benchedSize < totalSizeToLoad) + DISPLAY("Not enough memory; testing %u MB only...\n", (U32)(benchedSize >> 20)); + srcBuffer = malloc(benchedSize); + if (!srcBuffer) EXM_THROW(12, "not enough memory"); + + /* Load input buffer */ + BMK_loadFiles(srcBuffer, benchedSize, fileSizes, fileNamesTable, nbFiles); + + /* Bench */ + snprintf (mfName, sizeof(mfName), " %u files", nbFiles); + { const char* displayName = (nbFiles > 1) ? mfName : fileNamesTable[0]; + BMK_benchCLevel(srcBuffer, benchedSize, + displayName, cLevel, cLevelLast, + fileSizes, nbFiles, + dictBuffer, dictBufferSize); + } + + /* clean up */ + free(srcBuffer); + free(dictBuffer); + free(fileSizes); +} + + +static void BMK_syntheticTest(int cLevel, int cLevelLast, double compressibility) +{ + char name[20] = {0}; + size_t benchedSize = 10000000; + void* const srcBuffer = malloc(benchedSize); + + /* Memory allocation */ + if (!srcBuffer) EXM_THROW(21, "not enough memory"); + + /* Fill input buffer */ + RDG_genBuffer(srcBuffer, benchedSize, compressibility, 0.0, 0); + + /* Bench */ + snprintf (name, sizeof(name), "Synthetic %2u%%", (unsigned)(compressibility*100)); + BMK_benchCLevel(srcBuffer, benchedSize, name, cLevel, cLevelLast, &benchedSize, 1, NULL, 0); + + /* clean up */ + free(srcBuffer); +} + + +int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, + const char* dictFileName, int cLevel, int cLevelLast) +{ + double const compressibility = (double)g_compressibilityDefault / 100; + + if (nbFiles == 0) + BMK_syntheticTest(cLevel, cLevelLast, compressibility); + else + BMK_benchFileTable(fileNamesTable, nbFiles, dictFileName, cLevel, cLevelLast); + return 0; +} + + + + +/*-************************************ +* Command Line +**************************************/ +static int usage(const char* programName) +{ + DISPLAY(WELCOME_MESSAGE); + DISPLAY( "Usage :\n"); + DISPLAY( " %s [args] [FILE(s)] [-o file]\n", programName); + DISPLAY( "\n"); + DISPLAY( "FILE : a filename\n"); + DISPLAY( " with no FILE, or when FILE is - , read standard input\n"); + DISPLAY( "Arguments :\n"); + DISPLAY( " -D file: use `file` as Dictionary \n"); + DISPLAY( " -h/-H : display help/long help and exit\n"); + DISPLAY( " -V : display Version number and exit\n"); + DISPLAY( " -v : verbose mode; specify multiple times to increase log level (default:%d)\n", DEFAULT_DISPLAY_LEVEL); + DISPLAY( " -q : suppress warnings; specify twice to suppress errors too\n"); +#ifdef UTIL_HAS_CREATEFILELIST + DISPLAY( " -r : operate recursively on directories\n"); +#endif + DISPLAY( "\n"); + DISPLAY( "Benchmark arguments :\n"); + DISPLAY( " -b# : benchmark file(s), using # compression level (default : %d) \n", ZSTDCLI_CLEVEL_DEFAULT); + DISPLAY( " -e# : test all compression levels from -bX to # (default: %d)\n", ZSTDCLI_CLEVEL_DEFAULT); + DISPLAY( " -i# : minimum evaluation time in seconds (default : 3s)\n"); + DISPLAY( " -B# : cut file into independent blocks of size # (default: no block)\n"); + return 0; +} + +static int badusage(const char* programName) +{ + DISPLAYLEVEL(1, "Incorrect parameters\n"); + if (g_displayLevel >= 1) usage(programName); + return 1; +} + +static void waitEnter(void) +{ + int unused; + DISPLAY("Press enter to continue...\n"); + unused = getchar(); + (void)unused; +} + +/*! readU32FromChar() : + @return : unsigned integer value reach from input in `char` format + Will also modify `*stringPtr`, advancing it to position where it stopped reading. + Note : this function can overflow if digit string > MAX_UINT */ +static unsigned readU32FromChar(const char** stringPtr) +{ + unsigned result = 0; + while ((**stringPtr >='0') && (**stringPtr <='9')) + result *= 10, result += **stringPtr - '0', (*stringPtr)++ ; + return result; +} + + +#define CLEAN_RETURN(i) { operationResult = (i); goto _end; } + +int main(int argCount, char** argv) +{ + int argNb, + main_pause=0, + nextEntryIsDictionary=0, + operationResult=0, + nextArgumentIsFile=0; + int cLevel = ZSTDCLI_CLEVEL_DEFAULT; + int cLevelLast = 1; + unsigned recursive = 0; + const char** filenameTable = (const char**)malloc(argCount * sizeof(const char*)); /* argCount >= 1 */ + unsigned filenameIdx = 0; + const char* programName = argv[0]; + const char* dictFileName = NULL; + char* dynNameSpace = NULL; +#ifdef UTIL_HAS_CREATEFILELIST + const char** fileNamesTable = NULL; + char* fileNamesBuf = NULL; + unsigned fileNamesNb; +#endif + + /* init */ + if (filenameTable==NULL) { DISPLAY("zstd: %s \n", strerror(errno)); exit(1); } + displayOut = stderr; + + /* Pick out program name from path. Don't rely on stdlib because of conflicting behavior */ + { size_t pos; + for (pos = (int)strlen(programName); pos > 0; pos--) { if (programName[pos] == '/') { pos++; break; } } + programName += pos; + } + + /* command switches */ + for(argNb=1; argNb='0') && (*argument<='9')) { + BMK_setAdditionalParam(readU32FromChar(&argument)); + } else + main_pause=1; + break; + /* unknown command */ + default : CLEAN_RETURN(badusage(programName)); + } + } + continue; + } /* if (argument[0]=='-') */ + + } /* if (nextArgumentIsAFile==0) */ + + if (nextEntryIsDictionary) { + nextEntryIsDictionary = 0; + dictFileName = argument; + continue; + } + + /* add filename to list */ + filenameTable[filenameIdx++] = argument; + } + + /* Welcome message (if verbose) */ + DISPLAYLEVEL(3, WELCOME_MESSAGE); + +#ifdef UTIL_HAS_CREATEFILELIST + if (recursive) { + fileNamesTable = UTIL_createFileList(filenameTable, filenameIdx, &fileNamesBuf, &fileNamesNb); + if (fileNamesTable) { + unsigned u; + for (u=0; umsg = message; \ - return Z_MEM_ERROR; \ -} - -#define FINISH_WITH_NULL_ERR(msg) { \ - (void)msg; \ - return NULL; \ -} #ifndef ZWRAP_USE_ZSTD #define ZWRAP_USE_ZSTD 0 #endif -static int g_useZSTD = ZWRAP_USE_ZSTD; /* 0 = don't use ZSTD */ +static int g_ZWRAP_useZSTDcompression = ZWRAP_USE_ZSTD; /* 0 = don't use ZSTD */ + +void ZWRAP_useZSTDcompression(int turn_on) { g_ZWRAP_useZSTDcompression = turn_on; } + +int ZWRAP_isUsingZSTDcompression(void) { return g_ZWRAP_useZSTDcompression; } -void useZSTD(int turn_on) { g_useZSTD = turn_on; } +static ZWRAP_decompress_type g_ZWRAPdecompressionType = ZWRAP_AUTO; + +void ZWRAP_setDecompressionType(ZWRAP_decompress_type type) { g_ZWRAPdecompressionType = type; }; + +ZWRAP_decompress_type ZWRAP_getDecompressionType(void) { return g_ZWRAPdecompressionType; } + -int isUsingZSTD(void) { return g_useZSTD; } const char * zstdVersion(void) { return ZSTD_VERSION_STRING; } ZEXTERN const char * ZEXPORT z_zlibVersion OF((void)) { return zlibVersion(); } + static void* ZWRAP_allocFunction(void* opaque, size_t size) { z_streamp strm = (z_streamp) opaque; @@ -76,20 +74,24 @@ static void ZWRAP_freeFunction(void* opaque, void* address) /* *** Compression *** */ +typedef enum { ZWRAP_useInit, ZWRAP_useReset, ZWRAP_streamEnd } ZWRAP_state_t; typedef struct { - ZBUFF_CCtx* zbc; - size_t bytesLeft; + ZSTD_CStream* zbc; int compressionLevel; ZSTD_customMem customMem; z_stream allocFunc; /* copy of zalloc, zfree, opaque */ + ZSTD_inBuffer inBuffer; + ZSTD_outBuffer outBuffer; + ZWRAP_state_t comprState; + unsigned long long pledgedSrcSize; } ZWRAP_CCtx; size_t ZWRAP_freeCCtx(ZWRAP_CCtx* zwc) { if (zwc==NULL) return 0; /* support free on NULL */ - ZBUFF_freeCCtx(zwc->zbc); + if (zwc->zbc) ZSTD_freeCStream(zwc->zbc); zwc->customMem.customFree(zwc->customMem.opaque, zwc); return 0; } @@ -114,36 +116,77 @@ ZWRAP_CCtx* ZWRAP_createCCtx(z_streamp strm) memcpy(&zwc->customMem, &defaultCustomMem, sizeof(ZSTD_customMem)); } - zwc->zbc = ZBUFF_createCCtx_advanced(zwc->customMem); - if (zwc->zbc == NULL) { ZWRAP_freeCCtx(zwc); return NULL; } return zwc; } +int ZWRAP_initializeCStream(ZWRAP_CCtx* zwc, const void* dict, size_t dictSize, unsigned long long pledgedSrcSize) +{ + LOG_WRAPPERC("- ZWRAP_initializeCStream=%p\n", zwc); + if (zwc == NULL || zwc->zbc == NULL) return Z_STREAM_ERROR; + + if (!pledgedSrcSize) pledgedSrcSize = zwc->pledgedSrcSize; + { ZSTD_parameters const params = ZSTD_getParams(zwc->compressionLevel, pledgedSrcSize, dictSize); + size_t errorCode; + LOG_WRAPPERC("pledgedSrcSize=%d windowLog=%d chainLog=%d hashLog=%d searchLog=%d searchLength=%d strategy=%d\n", (int)pledgedSrcSize, params.cParams.windowLog, params.cParams.chainLog, params.cParams.hashLog, params.cParams.searchLog, params.cParams.searchLength, params.cParams.strategy); + errorCode = ZSTD_initCStream_advanced(zwc->zbc, dict, dictSize, params, pledgedSrcSize); + if (ZSTD_isError(errorCode)) return Z_STREAM_ERROR; } + + return Z_OK; +} + + +int ZWRAPC_finishWithError(ZWRAP_CCtx* zwc, z_streamp strm, int error) +{ + LOG_WRAPPERC("- ZWRAPC_finishWithError=%d\n", error); + if (zwc) ZWRAP_freeCCtx(zwc); + if (strm) strm->state = NULL; + return (error) ? error : Z_STREAM_ERROR; +} + + +int ZWRAPC_finishWithErrorMsg(z_streamp strm, char* message) +{ + ZWRAP_CCtx* zwc = (ZWRAP_CCtx*) strm->state; + strm->msg = message; + if (zwc == NULL) return Z_STREAM_ERROR; + + return ZWRAPC_finishWithError(zwc, strm, 0); +} + + +int ZWRAP_setPledgedSrcSize(z_streamp strm, unsigned long long pledgedSrcSize) +{ + ZWRAP_CCtx* zwc = (ZWRAP_CCtx*) strm->state; + if (zwc == NULL) return Z_STREAM_ERROR; + + zwc->pledgedSrcSize = pledgedSrcSize; + zwc->comprState = ZWRAP_useInit; + return Z_OK; +} + + ZEXTERN int ZEXPORT z_deflateInit_ OF((z_streamp strm, int level, const char *version, int stream_size)) { ZWRAP_CCtx* zwc; - if (!g_useZSTD) { - LOG_WRAPPER("- deflateInit level=%d\n", level); + LOG_WRAPPERC("- deflateInit level=%d\n", level); + if (!g_ZWRAP_useZSTDcompression) { return deflateInit_((strm), (level), version, stream_size); } - LOG_WRAPPER("- deflateInit level=%d\n", level); zwc = ZWRAP_createCCtx(strm); if (zwc == NULL) return Z_MEM_ERROR; if (level == Z_DEFAULT_COMPRESSION) level = ZWRAP_DEFAULT_CLEVEL; - { size_t const errorCode = ZBUFF_compressInit(zwc->zbc, level); - if (ZSTD_isError(errorCode)) return Z_MEM_ERROR; } - zwc->compressionLevel = level; strm->state = (struct internal_state*) zwc; /* use state which in not used by user */ strm->total_in = 0; strm->total_out = 0; + strm->adler = 0; return Z_OK; } @@ -153,24 +196,60 @@ ZEXTERN int ZEXPORT z_deflateInit2_ OF((z_streamp strm, int level, int method, int strategy, const char *version, int stream_size)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return deflateInit2_(strm, level, method, windowBits, memLevel, strategy, version, stream_size); return z_deflateInit_ (strm, level, version, stream_size); } +int ZWRAP_deflateReset_keepDict(z_streamp strm) +{ + LOG_WRAPPERC("- ZWRAP_deflateReset_keepDict\n"); + if (!g_ZWRAP_useZSTDcompression) + return deflateReset(strm); + + strm->total_in = 0; + strm->total_out = 0; + strm->adler = 0; + return Z_OK; +} + + +ZEXTERN int ZEXPORT z_deflateReset OF((z_streamp strm)) +{ + LOG_WRAPPERC("- deflateReset\n"); + if (!g_ZWRAP_useZSTDcompression) + return deflateReset(strm); + + ZWRAP_deflateReset_keepDict(strm); + + { ZWRAP_CCtx* zwc = (ZWRAP_CCtx*) strm->state; + if (zwc) zwc->comprState = 0; + } + return Z_OK; +} + + ZEXTERN int ZEXPORT z_deflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) { + LOG_WRAPPERC("- deflateSetDictionary\n"); return deflateSetDictionary(strm, dictionary, dictLength); + } { ZWRAP_CCtx* zwc = (ZWRAP_CCtx*) strm->state; - LOG_WRAPPER("- deflateSetDictionary level=%d\n", (int)strm->data_type); - { size_t const errorCode = ZBUFF_compressInitDictionary(zwc->zbc, dictionary, dictLength, zwc->compressionLevel); - if (ZSTD_isError(errorCode)) return Z_MEM_ERROR; } + LOG_WRAPPERC("- deflateSetDictionary level=%d\n", (int)zwc->compressionLevel); + if (!zwc) return Z_STREAM_ERROR; + if (zwc->zbc == NULL) { + zwc->zbc = ZSTD_createCStream_advanced(zwc->customMem); + if (zwc->zbc == NULL) return ZWRAPC_finishWithError(zwc, strm, 0); + } + { int res = ZWRAP_initializeCStream(zwc, dictionary, dictLength, 0); + if (res != Z_OK) return ZWRAPC_finishWithError(zwc, strm, res); } + zwc->comprState = ZWRAP_useReset; } return Z_OK; @@ -181,74 +260,102 @@ ZEXTERN int ZEXPORT z_deflate OF((z_streamp strm, int flush)) { ZWRAP_CCtx* zwc; - if (!g_useZSTD) { - int res = deflate(strm, flush); - LOG_WRAPPER("- avail_in=%d total_in=%d total_out=%d\n", (int)strm->avail_in, (int)strm->total_in, (int)strm->total_out); + if (!g_ZWRAP_useZSTDcompression) { + int res; + LOG_WRAPPERC("- deflate1 flush=%d avail_in=%d avail_out=%d total_in=%d total_out=%d\n", (int)flush, (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out); + res = deflate(strm, flush); return res; } zwc = (ZWRAP_CCtx*) strm->state; + if (zwc == NULL) { LOG_WRAPPERC("zwc == NULL\n"); return Z_STREAM_ERROR; } - LOG_WRAPPER("deflate flush=%d avail_in=%d avail_out=%d total_in=%d total_out=%d\n", (int)flush, (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out); - if (strm->avail_in > 0) { - size_t dstCapacity = strm->avail_out; - size_t srcSize = strm->avail_in; - size_t const errorCode = ZBUFF_compressContinue(zwc->zbc, strm->next_out, &dstCapacity, strm->next_in, &srcSize); - LOG_WRAPPER("ZBUFF_compressContinue srcSize=%d dstCapacity=%d\n", (int)srcSize, (int)dstCapacity); - if (ZSTD_isError(errorCode)) return Z_MEM_ERROR; - strm->next_out += dstCapacity; - strm->total_out += dstCapacity; - strm->avail_out -= dstCapacity; - strm->total_in += srcSize; - strm->next_in += srcSize; - strm->avail_in -= srcSize; + if (zwc->zbc == NULL) { + int res; + zwc->zbc = ZSTD_createCStream_advanced(zwc->customMem); + if (zwc->zbc == NULL) return ZWRAPC_finishWithError(zwc, strm, 0); + res = ZWRAP_initializeCStream(zwc, NULL, 0, (flush == Z_FINISH) ? strm->avail_in : 0); + if (res != Z_OK) return ZWRAPC_finishWithError(zwc, strm, res); + if (flush != Z_FINISH) zwc->comprState = ZWRAP_useReset; + } else { + if (strm->total_in == 0) { + if (zwc->comprState == ZWRAP_useReset) { + size_t const errorCode = ZSTD_resetCStream(zwc->zbc, (flush == Z_FINISH) ? strm->avail_in : zwc->pledgedSrcSize); + if (ZSTD_isError(errorCode)) { LOG_WRAPPERC("ERROR: ZSTD_resetCStream errorCode=%s\n", ZSTD_getErrorName(errorCode)); return ZWRAPC_finishWithError(zwc, strm, 0); } + } else { + int res = ZWRAP_initializeCStream(zwc, NULL, 0, (flush == Z_FINISH) ? strm->avail_in : 0); + if (res != Z_OK) return ZWRAPC_finishWithError(zwc, strm, res); + if (flush != Z_FINISH) zwc->comprState = ZWRAP_useReset; + } + } } - if (flush == Z_FULL_FLUSH) FINISH_WITH_ERR(strm, "Z_FULL_FLUSH is not supported!"); + LOG_WRAPPERC("- deflate1 flush=%d avail_in=%d avail_out=%d total_in=%d total_out=%d\n", (int)flush, (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out); + if (strm->avail_in > 0) { + zwc->inBuffer.src = strm->next_in; + zwc->inBuffer.size = strm->avail_in; + zwc->inBuffer.pos = 0; + zwc->outBuffer.dst = strm->next_out; + zwc->outBuffer.size = strm->avail_out; + zwc->outBuffer.pos = 0; + { size_t const errorCode = ZSTD_compressStream(zwc->zbc, &zwc->outBuffer, &zwc->inBuffer); + LOG_WRAPPERC("deflate ZSTD_compressStream srcSize=%d dstCapacity=%d\n", (int)zwc->inBuffer.size, (int)zwc->outBuffer.size); + if (ZSTD_isError(errorCode)) return ZWRAPC_finishWithError(zwc, strm, 0); + } + strm->next_out += zwc->outBuffer.pos; + strm->total_out += zwc->outBuffer.pos; + strm->avail_out -= zwc->outBuffer.pos; + strm->total_in += zwc->inBuffer.pos; + strm->next_in += zwc->inBuffer.pos; + strm->avail_in -= zwc->inBuffer.pos; + } + + if (flush == Z_FULL_FLUSH || flush == Z_BLOCK || flush == Z_TREES) return ZWRAPC_finishWithErrorMsg(strm, "Z_FULL_FLUSH, Z_BLOCK and Z_TREES are not supported!"); if (flush == Z_FINISH) { size_t bytesLeft; - size_t dstCapacity = strm->avail_out; - if (zwc->bytesLeft) { - bytesLeft = ZBUFF_compressFlush(zwc->zbc, strm->next_out, &dstCapacity); - LOG_WRAPPER("ZBUFF_compressFlush avail_out=%d dstCapacity=%d bytesLeft=%d\n", (int)strm->avail_out, (int)dstCapacity, (int)bytesLeft); - } else { - bytesLeft = ZBUFF_compressEnd(zwc->zbc, strm->next_out, &dstCapacity); - LOG_WRAPPER("ZBUFF_compressEnd dstCapacity=%d bytesLeft=%d\n", (int)dstCapacity, (int)bytesLeft); - } - if (ZSTD_isError(bytesLeft)) return Z_MEM_ERROR; - strm->next_out += dstCapacity; - strm->total_out += dstCapacity; - strm->avail_out -= dstCapacity; - if (flush == Z_FINISH && bytesLeft == 0) return Z_STREAM_END; - zwc->bytesLeft = bytesLeft; + zwc->outBuffer.dst = strm->next_out; + zwc->outBuffer.size = strm->avail_out; + zwc->outBuffer.pos = 0; + bytesLeft = ZSTD_endStream(zwc->zbc, &zwc->outBuffer); + LOG_WRAPPERC("deflate ZSTD_endStream dstCapacity=%d bytesLeft=%d\n", (int)strm->avail_out, (int)bytesLeft); + if (ZSTD_isError(bytesLeft)) return ZWRAPC_finishWithError(zwc, strm, 0); + strm->next_out += zwc->outBuffer.pos; + strm->total_out += zwc->outBuffer.pos; + strm->avail_out -= zwc->outBuffer.pos; + if (bytesLeft == 0) { LOG_WRAPPERC("Z_STREAM_END2 strm->total_in=%d strm->avail_out=%d strm->total_out=%d\n", (int)strm->total_in, (int)strm->avail_out, (int)strm->total_out); return Z_STREAM_END; } } - - if (flush == Z_SYNC_FLUSH) { + else + if (flush == Z_SYNC_FLUSH || flush == Z_PARTIAL_FLUSH) { size_t bytesLeft; - size_t dstCapacity = strm->avail_out; - bytesLeft = ZBUFF_compressFlush(zwc->zbc, strm->next_out, &dstCapacity); - LOG_WRAPPER("ZBUFF_compressFlush avail_out=%d dstCapacity=%d bytesLeft=%d\n", (int)strm->avail_out, (int)dstCapacity, (int)bytesLeft); - if (ZSTD_isError(bytesLeft)) return Z_MEM_ERROR; - strm->next_out += dstCapacity; - strm->total_out += dstCapacity; - strm->avail_out -= dstCapacity; - zwc->bytesLeft = bytesLeft; + zwc->outBuffer.dst = strm->next_out; + zwc->outBuffer.size = strm->avail_out; + zwc->outBuffer.pos = 0; + bytesLeft = ZSTD_flushStream(zwc->zbc, &zwc->outBuffer); + LOG_WRAPPERC("deflate ZSTD_flushStream dstCapacity=%d bytesLeft=%d\n", (int)strm->avail_out, (int)bytesLeft); + if (ZSTD_isError(bytesLeft)) return ZWRAPC_finishWithError(zwc, strm, 0); + strm->next_out += zwc->outBuffer.pos; + strm->total_out += zwc->outBuffer.pos; + strm->avail_out -= zwc->outBuffer.pos; } + LOG_WRAPPERC("- deflate2 flush=%d avail_in=%d avail_out=%d total_in=%d total_out=%d\n", (int)flush, (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out); return Z_OK; } ZEXTERN int ZEXPORT z_deflateEnd OF((z_streamp strm)) { - if (!g_useZSTD) { - LOG_WRAPPER("- deflateEnd\n"); + if (!g_ZWRAP_useZSTDcompression) { + LOG_WRAPPERC("- deflateEnd\n"); return deflateEnd(strm); } - LOG_WRAPPER("- deflateEnd total_in=%d total_out=%d\n", (int)(strm->total_in), (int)(strm->total_out)); - { ZWRAP_CCtx* zwc = (ZWRAP_CCtx*) strm->state; - size_t const errorCode = ZWRAP_freeCCtx(zwc); - if (ZSTD_isError(errorCode)) return Z_MEM_ERROR; + LOG_WRAPPERC("- deflateEnd total_in=%d total_out=%d\n", (int)(strm->total_in), (int)(strm->total_out)); + { size_t errorCode; + ZWRAP_CCtx* zwc = (ZWRAP_CCtx*) strm->state; + if (zwc == NULL) return Z_OK; /* structures are already freed */ + strm->state = NULL; + errorCode = ZWRAP_freeCCtx(zwc); + if (ZSTD_isError(errorCode)) return Z_STREAM_ERROR; } return Z_OK; } @@ -257,7 +364,7 @@ ZEXTERN int ZEXPORT z_deflateEnd OF((z_streamp strm)) ZEXTERN uLong ZEXPORT z_deflateBound OF((z_streamp strm, uLong sourceLen)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return deflateBound(strm, sourceLen); return ZSTD_compressBound(sourceLen); @@ -268,8 +375,8 @@ ZEXTERN int ZEXPORT z_deflateParams OF((z_streamp strm, int level, int strategy)) { - if (!g_useZSTD) { - LOG_WRAPPER("- deflateParams level=%d strategy=%d\n", level, strategy); + if (!g_ZWRAP_useZSTDcompression) { + LOG_WRAPPERC("- deflateParams level=%d strategy=%d\n", level, strategy); return deflateParams(strm, level, strategy); } @@ -281,11 +388,15 @@ ZEXTERN int ZEXPORT z_deflateParams OF((z_streamp strm, /* *** Decompression *** */ +typedef enum { ZWRAP_ZLIB_STREAM, ZWRAP_ZSTD_STREAM, ZWRAP_UNKNOWN_STREAM } ZWRAP_stream_type; typedef struct { - ZBUFF_DCtx* zbd; - char headerBuf[ZWRAP_HEADERSIZE]; + ZSTD_DStream* zbd; + char headerBuf[16]; /* should be equal or bigger than ZSTD_frameHeaderSize_min */ int errorCount; + ZWRAP_state_t decompState; + ZSTD_inBuffer inBuffer; + ZSTD_outBuffer outBuffer; /* zlib params */ int stream_size; @@ -296,6 +407,21 @@ typedef struct { } ZWRAP_DCtx; +int ZWRAP_isUsingZSTDdecompression(z_streamp strm) +{ + if (strm == NULL) return 0; + return (strm->reserved == ZWRAP_ZSTD_STREAM); +} + + +void ZWRAP_initDCtx(ZWRAP_DCtx* zwd) +{ + zwd->errorCount = 0; + zwd->outBuffer.pos = 0; + zwd->outBuffer.size = 0; +} + + ZWRAP_DCtx* ZWRAP_createDCtx(z_streamp strm) { ZWRAP_DCtx* zwd; @@ -315,6 +441,8 @@ ZWRAP_DCtx* ZWRAP_createDCtx(z_streamp strm) memcpy(&zwd->customMem, &defaultCustomMem, sizeof(ZSTD_customMem)); } + MEM_STATIC_ASSERT(sizeof(zwd->headerBuf) >= ZSTD_frameHeaderSize_min); /* if compilation fails here, assertion is false */ + ZWRAP_initDCtx(zwd); return zwd; } @@ -322,29 +450,56 @@ ZWRAP_DCtx* ZWRAP_createDCtx(z_streamp strm) size_t ZWRAP_freeDCtx(ZWRAP_DCtx* zwd) { if (zwd==NULL) return 0; /* support free on null */ - ZBUFF_freeDCtx(zwd->zbd); + if (zwd->zbd) ZSTD_freeDStream(zwd->zbd); if (zwd->version) zwd->customMem.customFree(zwd->customMem.opaque, zwd->version); zwd->customMem.customFree(zwd->customMem.opaque, zwd); return 0; } +int ZWRAPD_finishWithError(ZWRAP_DCtx* zwd, z_streamp strm, int error) +{ + LOG_WRAPPERD("- ZWRAPD_finishWithError=%d\n", error); + if (zwd) ZWRAP_freeDCtx(zwd); + if (strm) strm->state = NULL; + return (error) ? error : Z_STREAM_ERROR; +} + + +int ZWRAPD_finishWithErrorMsg(z_streamp strm, char* message) +{ + ZWRAP_DCtx* zwd = (ZWRAP_DCtx*) strm->state; + strm->msg = message; + if (zwd == NULL) return Z_STREAM_ERROR; + + return ZWRAPD_finishWithError(zwd, strm, 0); +} + + ZEXTERN int ZEXPORT z_inflateInit_ OF((z_streamp strm, const char *version, int stream_size)) { + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB) { + strm->reserved = ZWRAP_ZLIB_STREAM; /* mark as zlib stream */ + return inflateInit(strm); + } + + { ZWRAP_DCtx* zwd = ZWRAP_createDCtx(strm); - LOG_WRAPPER("- inflateInit\n"); - if (zwd == NULL) { strm->state = NULL; return Z_MEM_ERROR; } + LOG_WRAPPERD("- inflateInit\n"); + if (zwd == NULL) return ZWRAPD_finishWithError(zwd, strm, 0); zwd->version = zwd->customMem.customAlloc(zwd->customMem.opaque, strlen(version) + 1); - if (zwd->version == NULL) { ZWRAP_freeDCtx(zwd); strm->state = NULL; return Z_MEM_ERROR; } + if (zwd->version == NULL) return ZWRAPD_finishWithError(zwd, strm, 0); strcpy(zwd->version, version); zwd->stream_size = stream_size; strm->state = (struct internal_state*) zwd; /* use state which in not used by user */ strm->total_in = 0; strm->total_out = 0; - strm->reserved = 1; /* mark as unknown steam */ + strm->reserved = ZWRAP_UNKNOWN_STREAM; /* mark as unknown steam */ + strm->adler = 0; + } return Z_OK; } @@ -353,38 +508,102 @@ ZEXTERN int ZEXPORT z_inflateInit_ OF((z_streamp strm, ZEXTERN int ZEXPORT z_inflateInit2_ OF((z_streamp strm, int windowBits, const char *version, int stream_size)) { + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB) { + return inflateInit2_(strm, windowBits, version, stream_size); + } + + { int ret = z_inflateInit_ (strm, version, stream_size); if (ret == Z_OK) { ZWRAP_DCtx* zwd = (ZWRAP_DCtx*)strm->state; + if (zwd == NULL) return Z_STREAM_ERROR; zwd->windowBits = windowBits; } return ret; + } } +int ZWRAP_inflateReset_keepDict(z_streamp strm) +{ + LOG_WRAPPERD("- ZWRAP_inflateReset_keepDict\n"); + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) + return inflateReset(strm); + + { ZWRAP_DCtx* zwd = (ZWRAP_DCtx*) strm->state; + if (zwd == NULL) return Z_STREAM_ERROR; + ZWRAP_initDCtx(zwd); + zwd->decompState = ZWRAP_useReset; + } + + strm->total_in = 0; + strm->total_out = 0; + return Z_OK; +} + + +ZEXTERN int ZEXPORT z_inflateReset OF((z_streamp strm)) +{ + LOG_WRAPPERD("- inflateReset\n"); + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) + return inflateReset(strm); + + { int ret = ZWRAP_inflateReset_keepDict(strm); + if (ret != Z_OK) return ret; } + + { ZWRAP_DCtx* zwd = (ZWRAP_DCtx*) strm->state; + if (zwd == NULL) return Z_STREAM_ERROR; + zwd->decompState = ZWRAP_useInit; } + + return Z_OK; +} + + +#if ZLIB_VERNUM >= 0x1240 +ZEXTERN int ZEXPORT z_inflateReset2 OF((z_streamp strm, + int windowBits)) +{ + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) + return inflateReset2(strm, windowBits); + + { int ret = z_inflateReset (strm); + if (ret == Z_OK) { + ZWRAP_DCtx* zwd = (ZWRAP_DCtx*)strm->state; + if (zwd == NULL) return Z_STREAM_ERROR; + zwd->windowBits = windowBits; + } + return ret; + } +} +#endif + ZEXTERN int ZEXPORT z_inflateSetDictionary OF((z_streamp strm, const Bytef *dictionary, uInt dictLength)) { - if (!strm->reserved) + LOG_WRAPPERD("- inflateSetDictionary\n"); + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) return inflateSetDictionary(strm, dictionary, dictLength); - LOG_WRAPPER("- inflateSetDictionary\n"); { size_t errorCode; ZWRAP_DCtx* zwd = (ZWRAP_DCtx*) strm->state; - if (strm->state == NULL) return Z_MEM_ERROR; - errorCode = ZBUFF_decompressInitDictionary(zwd->zbd, dictionary, dictLength); - if (ZSTD_isError(errorCode)) { ZWRAP_freeDCtx(zwd); strm->state = NULL; return Z_MEM_ERROR; } + if (zwd == NULL || zwd->zbd == NULL) return Z_STREAM_ERROR; + errorCode = ZSTD_initDStream_usingDict(zwd->zbd, dictionary, dictLength); + if (ZSTD_isError(errorCode)) return ZWRAPD_finishWithError(zwd, strm, 0); + zwd->decompState = ZWRAP_useReset; - if (strm->total_in == ZSTD_frameHeaderSize_min) { - size_t dstCapacity = 0; - size_t srcSize = strm->total_in; - errorCode = ZBUFF_decompressContinue(zwd->zbd, strm->next_out, &dstCapacity, zwd->headerBuf, &srcSize); - LOG_WRAPPER("ZBUFF_decompressContinue3 errorCode=%d srcSize=%d dstCapacity=%d\n", (int)errorCode, (int)srcSize, (int)dstCapacity); - if (dstCapacity > 0 || ZSTD_isError(errorCode)) { - LOG_WRAPPER("ERROR: ZBUFF_decompressContinue %s\n", ZSTD_getErrorName(errorCode)); - ZWRAP_freeDCtx(zwd); strm->state = NULL; - return Z_MEM_ERROR; + if (strm->total_in == ZSTD_HEADERSIZE) { + zwd->inBuffer.src = zwd->headerBuf; + zwd->inBuffer.size = strm->total_in; + zwd->inBuffer.pos = 0; + zwd->outBuffer.dst = strm->next_out; + zwd->outBuffer.size = 0; + zwd->outBuffer.pos = 0; + errorCode = ZSTD_decompressStream(zwd->zbd, &zwd->outBuffer, &zwd->inBuffer); + LOG_WRAPPERD("inflateSetDictionary ZSTD_decompressStream errorCode=%d srcSize=%d dstCapacity=%d\n", (int)errorCode, (int)zwd->inBuffer.size, (int)zwd->outBuffer.size); + if (zwd->inBuffer.pos < zwd->outBuffer.size || ZSTD_isError(errorCode)) { + LOG_WRAPPERD("ERROR: ZSTD_decompressStream %s\n", ZSTD_getErrorName(errorCode)); + return ZWRAPD_finishWithError(zwd, strm, 0); } } } @@ -395,141 +614,212 @@ ZEXTERN int ZEXPORT z_inflateSetDictionary OF((z_streamp strm, ZEXTERN int ZEXPORT z_inflate OF((z_streamp strm, int flush)) { - if (!strm->reserved) - return inflate(strm, flush); + ZWRAP_DCtx* zwd; + int res; + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) { + LOG_WRAPPERD("- inflate1 flush=%d avail_in=%d avail_out=%d total_in=%d total_out=%d\n", (int)flush, (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out); + res = inflate(strm, flush); + LOG_WRAPPERD("- inflate2 flush=%d avail_in=%d avail_out=%d total_in=%d total_out=%d res=%d\n", (int)flush, (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out, res); + return res; + } - if (strm->avail_in > 0) { - size_t errorCode, dstCapacity, srcSize; - ZWRAP_DCtx* zwd = (ZWRAP_DCtx*) strm->state; - if (strm->state == NULL) return Z_MEM_ERROR; - LOG_WRAPPER("inflate avail_in=%d avail_out=%d total_in=%d total_out=%d\n", (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out); - if (strm->total_in < ZWRAP_HEADERSIZE) - { - srcSize = MIN(strm->avail_in, ZWRAP_HEADERSIZE - strm->total_in); - memcpy(zwd->headerBuf+strm->total_in, strm->next_in, srcSize); - strm->total_in += srcSize; - strm->next_in += srcSize; - strm->avail_in -= srcSize; - if (strm->total_in < ZWRAP_HEADERSIZE) return Z_OK; + if (strm->avail_in <= 0) return Z_OK; - if (MEM_readLE32(zwd->headerBuf) != ZSTD_MAGICNUMBER) { - z_stream strm2; - strm2.next_in = strm->next_in; - strm2.avail_in = strm->avail_in; - strm2.next_out = strm->next_out; - strm2.avail_out = strm->avail_out; + { size_t errorCode, srcSize; + zwd = (ZWRAP_DCtx*) strm->state; + LOG_WRAPPERD("- inflate1 flush=%d avail_in=%d avail_out=%d total_in=%d total_out=%d\n", (int)flush, (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out); - if (zwd->windowBits) - errorCode = inflateInit2_(strm, zwd->windowBits, zwd->version, zwd->stream_size); - else - errorCode = inflateInit_(strm, zwd->version, zwd->stream_size); - LOG_WRAPPER("ZLIB inflateInit errorCode=%d\n", (int)errorCode); - if (errorCode != Z_OK) { ZWRAP_freeDCtx(zwd); strm->state = NULL; return errorCode; } + if (zwd == NULL) return Z_STREAM_ERROR; + if (zwd->decompState == ZWRAP_streamEnd) return Z_STREAM_END; - /* inflate header */ - strm->next_in = (unsigned char*)zwd->headerBuf; - strm->avail_in = ZWRAP_HEADERSIZE; - strm->avail_out = 0; - errorCode = inflate(strm, Z_NO_FLUSH); - LOG_WRAPPER("ZLIB inflate errorCode=%d strm->avail_in=%d\n", (int)errorCode, (int)strm->avail_in); - if (errorCode != Z_OK) { ZWRAP_freeDCtx(zwd); strm->state = NULL; return errorCode; } - if (strm->avail_in > 0) goto error; + if (strm->total_in < ZLIB_HEADERSIZE) { + if (strm->total_in == 0 && strm->avail_in >= ZLIB_HEADERSIZE) { + if (MEM_readLE32(strm->next_in) != ZSTD_MAGICNUMBER) { + if (zwd->windowBits) + errorCode = inflateInit2_(strm, zwd->windowBits, zwd->version, zwd->stream_size); + else + errorCode = inflateInit_(strm, zwd->version, zwd->stream_size); - strm->next_in = strm2.next_in; - strm->avail_in = strm2.avail_in; - strm->next_out = strm2.next_out; - strm->avail_out = strm2.avail_out; + strm->reserved = ZWRAP_ZLIB_STREAM; /* mark as zlib stream */ + errorCode = ZWRAP_freeDCtx(zwd); + if (ZSTD_isError(errorCode)) goto error; - strm->reserved = 0; /* mark as zlib stream */ - errorCode = ZWRAP_freeDCtx(zwd); - if (ZSTD_isError(errorCode)) goto error; + if (flush == Z_INFLATE_SYNC) res = inflateSync(strm); + else res = inflate(strm, flush); + LOG_WRAPPERD("- inflate3 flush=%d avail_in=%d avail_out=%d total_in=%d total_out=%d res=%d\n", (int)flush, (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out, res); + return res; + } + } else { + srcSize = MIN(strm->avail_in, ZLIB_HEADERSIZE - strm->total_in); + memcpy(zwd->headerBuf+strm->total_in, strm->next_in, srcSize); + strm->total_in += srcSize; + strm->next_in += srcSize; + strm->avail_in -= srcSize; + if (strm->total_in < ZLIB_HEADERSIZE) return Z_OK; - if (flush == Z_INFLATE_SYNC) return inflateSync(strm); - return inflate(strm, flush); + if (MEM_readLE32(zwd->headerBuf) != ZSTD_MAGICNUMBER) { + z_stream strm2; + strm2.next_in = strm->next_in; + strm2.avail_in = strm->avail_in; + strm2.next_out = strm->next_out; + strm2.avail_out = strm->avail_out; + + if (zwd->windowBits) + errorCode = inflateInit2_(strm, zwd->windowBits, zwd->version, zwd->stream_size); + else + errorCode = inflateInit_(strm, zwd->version, zwd->stream_size); + LOG_WRAPPERD("ZLIB inflateInit errorCode=%d\n", (int)errorCode); + if (errorCode != Z_OK) return ZWRAPD_finishWithError(zwd, strm, (int)errorCode); + + /* inflate header */ + strm->next_in = (unsigned char*)zwd->headerBuf; + strm->avail_in = ZLIB_HEADERSIZE; + strm->avail_out = 0; + errorCode = inflate(strm, Z_NO_FLUSH); + LOG_WRAPPERD("ZLIB inflate errorCode=%d strm->avail_in=%d\n", (int)errorCode, (int)strm->avail_in); + if (errorCode != Z_OK) return ZWRAPD_finishWithError(zwd, strm, (int)errorCode); + if (strm->avail_in > 0) goto error; + + strm->next_in = strm2.next_in; + strm->avail_in = strm2.avail_in; + strm->next_out = strm2.next_out; + strm->avail_out = strm2.avail_out; + + strm->reserved = ZWRAP_ZLIB_STREAM; /* mark as zlib stream */ + errorCode = ZWRAP_freeDCtx(zwd); + if (ZSTD_isError(errorCode)) goto error; + + if (flush == Z_INFLATE_SYNC) res = inflateSync(strm); + else res = inflate(strm, flush); + LOG_WRAPPERD("- inflate2 flush=%d avail_in=%d avail_out=%d total_in=%d total_out=%d res=%d\n", (int)flush, (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out, res); + return res; + } } - - zwd->zbd = ZBUFF_createDCtx_advanced(zwd->customMem); - if (zwd->zbd == NULL) goto error; - - errorCode = ZBUFF_decompressInit(zwd->zbd); - if (ZSTD_isError(errorCode)) goto error; - - srcSize = ZWRAP_HEADERSIZE; - dstCapacity = 0; - errorCode = ZBUFF_decompressContinue(zwd->zbd, strm->next_out, &dstCapacity, zwd->headerBuf, &srcSize); - LOG_WRAPPER("ZBUFF_decompressContinue1 errorCode=%d srcSize=%d dstCapacity=%d\n", (int)errorCode, (int)srcSize, (int)dstCapacity); - if (ZSTD_isError(errorCode)) { - LOG_WRAPPER("ERROR: ZBUFF_decompressContinue %s\n", ZSTD_getErrorName(errorCode)); - goto error; - } - if (strm->avail_in == 0) return Z_OK; } - srcSize = strm->avail_in; - dstCapacity = strm->avail_out; - errorCode = ZBUFF_decompressContinue(zwd->zbd, strm->next_out, &dstCapacity, strm->next_in, &srcSize); - LOG_WRAPPER("ZBUFF_decompressContinue2 errorCode=%d srcSize=%d dstCapacity=%d\n", (int)errorCode, (int)srcSize, (int)dstCapacity); + strm->reserved = ZWRAP_ZSTD_STREAM; /* mark as zstd steam */ + + if (flush == Z_INFLATE_SYNC) { strm->msg = "inflateSync is not supported!"; goto error; } + + if (!zwd->zbd) { + zwd->zbd = ZSTD_createDStream_advanced(zwd->customMem); + if (zwd->zbd == NULL) { LOG_WRAPPERD("ERROR: ZSTD_createDStream_advanced\n"); goto error; } + zwd->decompState = ZWRAP_useInit; + } + + if (strm->total_in < ZSTD_HEADERSIZE) + { + if (strm->total_in == 0 && strm->avail_in >= ZSTD_HEADERSIZE) { + if (zwd->decompState == ZWRAP_useInit) { + errorCode = ZSTD_initDStream(zwd->zbd); + if (ZSTD_isError(errorCode)) { LOG_WRAPPERD("ERROR: ZSTD_initDStream errorCode=%s\n", ZSTD_getErrorName(errorCode)); goto error; } + } else { + errorCode = ZSTD_resetDStream(zwd->zbd); + if (ZSTD_isError(errorCode)) goto error; + } + } else { + srcSize = MIN(strm->avail_in, ZSTD_HEADERSIZE - strm->total_in); + memcpy(zwd->headerBuf+strm->total_in, strm->next_in, srcSize); + strm->total_in += srcSize; + strm->next_in += srcSize; + strm->avail_in -= srcSize; + if (strm->total_in < ZSTD_HEADERSIZE) return Z_OK; + + if (zwd->decompState == ZWRAP_useInit) { + errorCode = ZSTD_initDStream(zwd->zbd); + if (ZSTD_isError(errorCode)) { LOG_WRAPPERD("ERROR: ZSTD_initDStream errorCode=%s\n", ZSTD_getErrorName(errorCode)); goto error; } + } else { + errorCode = ZSTD_resetDStream(zwd->zbd); + if (ZSTD_isError(errorCode)) goto error; + } + + zwd->inBuffer.src = zwd->headerBuf; + zwd->inBuffer.size = ZSTD_HEADERSIZE; + zwd->inBuffer.pos = 0; + zwd->outBuffer.dst = strm->next_out; + zwd->outBuffer.size = 0; + zwd->outBuffer.pos = 0; + errorCode = ZSTD_decompressStream(zwd->zbd, &zwd->outBuffer, &zwd->inBuffer); + LOG_WRAPPERD("inflate ZSTD_decompressStream1 errorCode=%d srcSize=%d dstCapacity=%d\n", (int)errorCode, (int)zwd->inBuffer.size, (int)zwd->outBuffer.size); + if (ZSTD_isError(errorCode)) { + LOG_WRAPPERD("ERROR: ZSTD_decompressStream1 %s\n", ZSTD_getErrorName(errorCode)); + goto error; + } + if (zwd->inBuffer.pos != zwd->inBuffer.size) goto error; /* not consumed */ + } + } + + zwd->inBuffer.src = strm->next_in; + zwd->inBuffer.size = strm->avail_in; + zwd->inBuffer.pos = 0; + zwd->outBuffer.dst = strm->next_out; + zwd->outBuffer.size = strm->avail_out; + zwd->outBuffer.pos = 0; + errorCode = ZSTD_decompressStream(zwd->zbd, &zwd->outBuffer, &zwd->inBuffer); + LOG_WRAPPERD("inflate ZSTD_decompressStream2 errorCode=%d srcSize=%d dstCapacity=%d\n", (int)errorCode, (int)strm->avail_in, (int)strm->avail_out); if (ZSTD_isError(errorCode)) { - LOG_WRAPPER("ERROR: ZBUFF_decompressContinue %s\n", ZSTD_getErrorName(errorCode)); zwd->errorCount++; + LOG_WRAPPERD("ERROR: ZSTD_decompressStream2 %s zwd->errorCount=%d\n", ZSTD_getErrorName(errorCode), zwd->errorCount); if (zwd->errorCount<=1) return Z_NEED_DICT; else goto error; } - strm->next_out += dstCapacity; - strm->total_out += dstCapacity; - strm->avail_out -= dstCapacity; - strm->total_in += srcSize; - strm->next_in += srcSize; - strm->avail_in -= srcSize; - if (errorCode == 0) return Z_STREAM_END; - return Z_OK; -error: - ZWRAP_freeDCtx(zwd); - strm->state = NULL; - return Z_MEM_ERROR; + LOG_WRAPPERD("inflate inBuffer.pos=%d inBuffer.size=%d outBuffer.pos=%d outBuffer.size=%d o\n", (int)zwd->inBuffer.pos, (int)zwd->inBuffer.size, (int)zwd->outBuffer.pos, (int)zwd->outBuffer.size); + strm->next_out += zwd->outBuffer.pos; + strm->total_out += zwd->outBuffer.pos; + strm->avail_out -= zwd->outBuffer.pos; + strm->total_in += zwd->inBuffer.pos; + strm->next_in += zwd->inBuffer.pos; + strm->avail_in -= zwd->inBuffer.pos; + if (errorCode == 0) { + LOG_WRAPPERD("inflate Z_STREAM_END1 avail_in=%d avail_out=%d total_in=%d total_out=%d\n", (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out); + zwd->decompState = ZWRAP_streamEnd; + return Z_STREAM_END; + } } + LOG_WRAPPERD("- inflate2 flush=%d avail_in=%d avail_out=%d total_in=%d total_out=%d res=%d\n", (int)flush, (int)strm->avail_in, (int)strm->avail_out, (int)strm->total_in, (int)strm->total_out, Z_OK); return Z_OK; + +error: + return ZWRAPD_finishWithError(zwd, strm, 0); } ZEXTERN int ZEXPORT z_inflateEnd OF((z_streamp strm)) { - int ret = Z_OK; - if (!strm->reserved) + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) return inflateEnd(strm); - LOG_WRAPPER("- inflateEnd total_in=%d total_out=%d\n", (int)(strm->total_in), (int)(strm->total_out)); - { ZWRAP_DCtx* zwd = (ZWRAP_DCtx*) strm->state; - size_t const errorCode = ZWRAP_freeDCtx(zwd); + LOG_WRAPPERD("- inflateEnd total_in=%d total_out=%d\n", (int)(strm->total_in), (int)(strm->total_out)); + { size_t errorCode; + ZWRAP_DCtx* zwd = (ZWRAP_DCtx*) strm->state; + if (zwd == NULL) return Z_OK; /* structures are already freed */ strm->state = NULL; - if (ZSTD_isError(errorCode)) return Z_MEM_ERROR; + errorCode = ZWRAP_freeDCtx(zwd); + if (ZSTD_isError(errorCode)) return Z_STREAM_ERROR; } - return ret; + return Z_OK; } ZEXTERN int ZEXPORT z_inflateSync OF((z_streamp strm)) { + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) { + return inflateSync(strm); + } + return z_inflate(strm, Z_INFLATE_SYNC); } + /* Advanced compression functions */ ZEXTERN int ZEXPORT z_deflateCopy OF((z_streamp dest, z_streamp source)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return deflateCopy(dest, source); - FINISH_WITH_ERR(source, "deflateCopy is not supported!"); -} - - -ZEXTERN int ZEXPORT z_deflateReset OF((z_streamp strm)) -{ - if (!g_useZSTD) - return deflateReset(strm); - FINISH_WITH_ERR(strm, "deflateReset is not supported!"); + return ZWRAPC_finishWithErrorMsg(source, "deflateCopy is not supported!"); } @@ -539,9 +829,9 @@ ZEXTERN int ZEXPORT z_deflateTune OF((z_streamp strm, int nice_length, int max_chain)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return deflateTune(strm, good_length, max_lazy, nice_length, max_chain); - FINISH_WITH_ERR(strm, "deflateTune is not supported!"); + return ZWRAPC_finishWithErrorMsg(strm, "deflateTune is not supported!"); } @@ -550,9 +840,9 @@ ZEXTERN int ZEXPORT z_deflatePending OF((z_streamp strm, unsigned *pending, int *bits)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return deflatePending(strm, pending, bits); - FINISH_WITH_ERR(strm, "deflatePending is not supported!"); + return ZWRAPC_finishWithErrorMsg(strm, "deflatePending is not supported!"); } #endif @@ -561,32 +851,32 @@ ZEXTERN int ZEXPORT z_deflatePrime OF((z_streamp strm, int bits, int value)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return deflatePrime(strm, bits, value); - FINISH_WITH_ERR(strm, "deflatePrime is not supported!"); + return ZWRAPC_finishWithErrorMsg(strm, "deflatePrime is not supported!"); } ZEXTERN int ZEXPORT z_deflateSetHeader OF((z_streamp strm, gz_headerp head)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return deflateSetHeader(strm, head); - FINISH_WITH_ERR(strm, "deflateSetHeader is not supported!"); + return ZWRAPC_finishWithErrorMsg(strm, "deflateSetHeader is not supported!"); } -/* Advanced compression functions */ +/* Advanced decompression functions */ #if ZLIB_VERNUM >= 0x1280 ZEXTERN int ZEXPORT z_inflateGetDictionary OF((z_streamp strm, Bytef *dictionary, uInt *dictLength)) { - if (!strm->reserved) + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) return inflateGetDictionary(strm, dictionary, dictLength); - FINISH_WITH_ERR(strm, "inflateGetDictionary is not supported!"); + return ZWRAPD_finishWithErrorMsg(strm, "inflateGetDictionary is not supported!"); } #endif @@ -594,37 +884,18 @@ ZEXTERN int ZEXPORT z_inflateGetDictionary OF((z_streamp strm, ZEXTERN int ZEXPORT z_inflateCopy OF((z_streamp dest, z_streamp source)) { - if (!g_useZSTD) + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !source->reserved) return inflateCopy(dest, source); - FINISH_WITH_ERR(source, "inflateCopy is not supported!"); + return ZWRAPD_finishWithErrorMsg(source, "inflateCopy is not supported!"); } -ZEXTERN int ZEXPORT z_inflateReset OF((z_streamp strm)) -{ - if (!strm->reserved) - return inflateReset(strm); - FINISH_WITH_ERR(strm, "inflateReset is not supported!"); -} - - -#if ZLIB_VERNUM >= 0x1240 -ZEXTERN int ZEXPORT z_inflateReset2 OF((z_streamp strm, - int windowBits)) -{ - if (!strm->reserved) - return inflateReset2(strm, windowBits); - FINISH_WITH_ERR(strm, "inflateReset2 is not supported!"); -} -#endif - - #if ZLIB_VERNUM >= 0x1240 ZEXTERN long ZEXPORT z_inflateMark OF((z_streamp strm)) { - if (!strm->reserved) + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) return inflateMark(strm); - FINISH_WITH_ERR(strm, "inflateMark is not supported!"); + return ZWRAPD_finishWithErrorMsg(strm, "inflateMark is not supported!"); } #endif @@ -633,18 +904,18 @@ ZEXTERN int ZEXPORT z_inflatePrime OF((z_streamp strm, int bits, int value)) { - if (!strm->reserved) + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) return inflatePrime(strm, bits, value); - FINISH_WITH_ERR(strm, "inflatePrime is not supported!"); + return ZWRAPD_finishWithErrorMsg(strm, "inflatePrime is not supported!"); } ZEXTERN int ZEXPORT z_inflateGetHeader OF((z_streamp strm, gz_headerp head)) { - if (!strm->reserved) + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) return inflateGetHeader(strm, head); - FINISH_WITH_ERR(strm, "inflateGetHeader is not supported!"); + return ZWRAPD_finishWithErrorMsg(strm, "inflateGetHeader is not supported!"); } @@ -653,9 +924,9 @@ ZEXTERN int ZEXPORT z_inflateBackInit_ OF((z_streamp strm, int windowBits, const char *version, int stream_size)) { - if (!strm->reserved) + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) return inflateBackInit_(strm, windowBits, window, version, stream_size); - FINISH_WITH_ERR(strm, "inflateBackInit is not supported!"); + return ZWRAPD_finishWithErrorMsg(strm, "inflateBackInit is not supported!"); } @@ -663,17 +934,17 @@ ZEXTERN int ZEXPORT z_inflateBack OF((z_streamp strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc)) { - if (!strm->reserved) + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) return inflateBack(strm, in, in_desc, out, out_desc); - FINISH_WITH_ERR(strm, "inflateBack is not supported!"); + return ZWRAPD_finishWithErrorMsg(strm, "inflateBack is not supported!"); } ZEXTERN int ZEXPORT z_inflateBackEnd OF((z_streamp strm)) { - if (!strm->reserved) + if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) return inflateBackEnd(strm); - FINISH_WITH_ERR(strm, "inflateBackEnd is not supported!"); + return ZWRAPD_finishWithErrorMsg(strm, "inflateBackEnd is not supported!"); } @@ -687,13 +958,13 @@ ZEXTERN uLong ZEXPORT z_zlibCompileFlags OF((void)) { return zlibCompileFlags(); ZEXTERN int ZEXPORT z_compress OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return compress(dest, destLen, source, sourceLen); { size_t dstCapacity = *destLen; size_t const errorCode = ZSTD_compress(dest, dstCapacity, source, sourceLen, ZWRAP_DEFAULT_CLEVEL); - LOG_WRAPPER("z_compress sourceLen=%d dstCapacity=%d\n", (int)sourceLen, (int)dstCapacity); - if (ZSTD_isError(errorCode)) return Z_MEM_ERROR; + LOG_WRAPPERD("z_compress sourceLen=%d dstCapacity=%d\n", (int)sourceLen, (int)dstCapacity); + if (ZSTD_isError(errorCode)) return Z_STREAM_ERROR; *destLen = errorCode; } return Z_OK; @@ -704,12 +975,12 @@ ZEXTERN int ZEXPORT z_compress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return compress2(dest, destLen, source, sourceLen, level); { size_t dstCapacity = *destLen; size_t const errorCode = ZSTD_compress(dest, dstCapacity, source, sourceLen, level); - if (ZSTD_isError(errorCode)) return Z_MEM_ERROR; + if (ZSTD_isError(errorCode)) return Z_STREAM_ERROR; *destLen = errorCode; } return Z_OK; @@ -718,7 +989,7 @@ ZEXTERN int ZEXPORT z_compress2 OF((Bytef *dest, uLongf *destLen, ZEXTERN uLong ZEXPORT z_compressBound OF((uLong sourceLen)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return compressBound(sourceLen); return ZSTD_compressBound(sourceLen); @@ -733,7 +1004,7 @@ ZEXTERN int ZEXPORT z_uncompress OF((Bytef *dest, uLongf *destLen, { size_t dstCapacity = *destLen; size_t const errorCode = ZSTD_decompress(dest, dstCapacity, source, sourceLen); - if (ZSTD_isError(errorCode)) return Z_MEM_ERROR; + if (ZSTD_isError(errorCode)) return Z_STREAM_ERROR; *destLen = errorCode; } return Z_OK; @@ -744,7 +1015,7 @@ ZEXTERN int ZEXPORT z_uncompress OF((Bytef *dest, uLongf *destLen, /* gzip file access functions */ ZEXTERN gzFile ZEXPORT z_gzopen OF((const char *path, const char *mode)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzopen(path, mode); FINISH_WITH_NULL_ERR("gzopen is not supported!"); } @@ -752,7 +1023,7 @@ ZEXTERN gzFile ZEXPORT z_gzopen OF((const char *path, const char *mode)) ZEXTERN gzFile ZEXPORT z_gzdopen OF((int fd, const char *mode)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzdopen(fd, mode); FINISH_WITH_NULL_ERR("gzdopen is not supported!"); } @@ -761,7 +1032,7 @@ ZEXTERN gzFile ZEXPORT z_gzdopen OF((int fd, const char *mode)) #if ZLIB_VERNUM >= 0x1240 ZEXTERN int ZEXPORT z_gzbuffer OF((gzFile file, unsigned size)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzbuffer(file, size); FINISH_WITH_GZ_ERR("gzbuffer is not supported!"); } @@ -769,7 +1040,7 @@ ZEXTERN int ZEXPORT z_gzbuffer OF((gzFile file, unsigned size)) ZEXTERN z_off_t ZEXPORT z_gzoffset OF((gzFile file)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzoffset(file); FINISH_WITH_GZ_ERR("gzoffset is not supported!"); } @@ -777,7 +1048,7 @@ ZEXTERN z_off_t ZEXPORT z_gzoffset OF((gzFile file)) ZEXTERN int ZEXPORT z_gzclose_r OF((gzFile file)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzclose_r(file); FINISH_WITH_GZ_ERR("gzclose_r is not supported!"); } @@ -785,7 +1056,7 @@ ZEXTERN int ZEXPORT z_gzclose_r OF((gzFile file)) ZEXTERN int ZEXPORT z_gzclose_w OF((gzFile file)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzclose_w(file); FINISH_WITH_GZ_ERR("gzclose_w is not supported!"); } @@ -794,7 +1065,7 @@ ZEXTERN int ZEXPORT z_gzclose_w OF((gzFile file)) ZEXTERN int ZEXPORT z_gzsetparams OF((gzFile file, int level, int strategy)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzsetparams(file, level, strategy); FINISH_WITH_GZ_ERR("gzsetparams is not supported!"); } @@ -802,7 +1073,7 @@ ZEXTERN int ZEXPORT z_gzsetparams OF((gzFile file, int level, int strategy)) ZEXTERN int ZEXPORT z_gzread OF((gzFile file, voidp buf, unsigned len)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzread(file, buf, len); FINISH_WITH_GZ_ERR("gzread is not supported!"); } @@ -811,7 +1082,7 @@ ZEXTERN int ZEXPORT z_gzread OF((gzFile file, voidp buf, unsigned len)) ZEXTERN int ZEXPORT z_gzwrite OF((gzFile file, voidpc buf, unsigned len)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzwrite(file, buf, len); FINISH_WITH_GZ_ERR("gzwrite is not supported!"); } @@ -823,7 +1094,7 @@ ZEXTERN int ZEXPORTVA z_gzprintf Z_ARG((gzFile file, const char *format, ...)) ZEXTERN int ZEXPORTVA z_gzprintf OF((gzFile file, const char *format, ...)) #endif { - if (!g_useZSTD) { + if (!g_ZWRAP_useZSTDcompression) { int ret; char buf[1024]; va_list args; @@ -840,7 +1111,7 @@ ZEXTERN int ZEXPORTVA z_gzprintf OF((gzFile file, const char *format, ...)) ZEXTERN int ZEXPORT z_gzputs OF((gzFile file, const char *s)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzputs(file, s); FINISH_WITH_GZ_ERR("gzputs is not supported!"); } @@ -848,7 +1119,7 @@ ZEXTERN int ZEXPORT z_gzputs OF((gzFile file, const char *s)) ZEXTERN char * ZEXPORT z_gzgets OF((gzFile file, char *buf, int len)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzgets(file, buf, len); FINISH_WITH_NULL_ERR("gzgets is not supported!"); } @@ -856,7 +1127,7 @@ ZEXTERN char * ZEXPORT z_gzgets OF((gzFile file, char *buf, int len)) ZEXTERN int ZEXPORT z_gzputc OF((gzFile file, int c)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzputc(file, c); FINISH_WITH_GZ_ERR("gzputc is not supported!"); } @@ -868,7 +1139,7 @@ ZEXTERN int ZEXPORT z_gzgetc_ OF((gzFile file)) ZEXTERN int ZEXPORT z_gzgetc OF((gzFile file)) #endif { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzgetc(file); FINISH_WITH_GZ_ERR("gzgetc is not supported!"); } @@ -876,7 +1147,7 @@ ZEXTERN int ZEXPORT z_gzgetc OF((gzFile file)) ZEXTERN int ZEXPORT z_gzungetc OF((int c, gzFile file)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzungetc(c, file); FINISH_WITH_GZ_ERR("gzungetc is not supported!"); } @@ -884,7 +1155,7 @@ ZEXTERN int ZEXPORT z_gzungetc OF((int c, gzFile file)) ZEXTERN int ZEXPORT z_gzflush OF((gzFile file, int flush)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzflush(file, flush); FINISH_WITH_GZ_ERR("gzflush is not supported!"); } @@ -892,7 +1163,7 @@ ZEXTERN int ZEXPORT z_gzflush OF((gzFile file, int flush)) ZEXTERN z_off_t ZEXPORT z_gzseek OF((gzFile file, z_off_t offset, int whence)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzseek(file, offset, whence); FINISH_WITH_GZ_ERR("gzseek is not supported!"); } @@ -900,7 +1171,7 @@ ZEXTERN z_off_t ZEXPORT z_gzseek OF((gzFile file, z_off_t offset, int whence)) ZEXTERN int ZEXPORT z_gzrewind OF((gzFile file)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzrewind(file); FINISH_WITH_GZ_ERR("gzrewind is not supported!"); } @@ -908,7 +1179,7 @@ ZEXTERN int ZEXPORT z_gzrewind OF((gzFile file)) ZEXTERN z_off_t ZEXPORT z_gztell OF((gzFile file)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gztell(file); FINISH_WITH_GZ_ERR("gztell is not supported!"); } @@ -916,7 +1187,7 @@ ZEXTERN z_off_t ZEXPORT z_gztell OF((gzFile file)) ZEXTERN int ZEXPORT z_gzeof OF((gzFile file)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzeof(file); FINISH_WITH_GZ_ERR("gzeof is not supported!"); } @@ -924,7 +1195,7 @@ ZEXTERN int ZEXPORT z_gzeof OF((gzFile file)) ZEXTERN int ZEXPORT z_gzdirect OF((gzFile file)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzdirect(file); FINISH_WITH_GZ_ERR("gzdirect is not supported!"); } @@ -932,7 +1203,7 @@ ZEXTERN int ZEXPORT z_gzdirect OF((gzFile file)) ZEXTERN int ZEXPORT z_gzclose OF((gzFile file)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzclose(file); FINISH_WITH_GZ_ERR("gzclose is not supported!"); } @@ -940,7 +1211,7 @@ ZEXTERN int ZEXPORT z_gzclose OF((gzFile file)) ZEXTERN const char * ZEXPORT z_gzerror OF((gzFile file, int *errnum)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) return gzerror(file, errnum); FINISH_WITH_NULL_ERR("gzerror is not supported!"); } @@ -948,7 +1219,7 @@ ZEXTERN const char * ZEXPORT z_gzerror OF((gzFile file, int *errnum)) ZEXTERN void ZEXPORT z_gzclearerr OF((gzFile file)) { - if (!g_useZSTD) + if (!g_ZWRAP_useZSTDcompression) gzclearerr(file); } diff --git a/zlibWrapper/zstd_zlibwrapper.h b/zlibWrapper/zstd_zlibwrapper.h index 24247b2ca..873413907 100644 --- a/zlibWrapper/zstd_zlibwrapper.h +++ b/zlibWrapper/zstd_zlibwrapper.h @@ -26,11 +26,51 @@ extern "C" { #endif #endif -void useZSTD(int turn_on); -int isUsingZSTD(void); +/* returns a string with version of zstd library */ const char * zstdVersion(void); +/*** COMPRESSION ***/ +/* enables/disables zstd compression during runtime */ +void ZWRAP_useZSTDcompression(int turn_on); + +/* checks if zstd compression is turned on */ +int ZWRAP_isUsingZSTDcompression(void); + +/* Changes a pledged source size for a given compression stream. + It will change ZSTD compression parameters what may improve compression speed and/or ratio. + The function should be called just after deflateInit() or deflateReset() and before deflate() or deflateSetDictionary(). + It's only helpful when data is compressed in blocks. + There will be no change in case of deflateInit() or deflateReset() immediately followed by deflate(strm, Z_FINISH) + as this case is automatically detected. */ +int ZWRAP_setPledgedSrcSize(z_streamp strm, unsigned long long pledgedSrcSize); + +/* Similar to deflateReset but preserves dictionary set using deflateSetDictionary. + It should improve compression speed because there will be less calls to deflateSetDictionary + When using zlib compression this method redirects to deflateReset. */ +int ZWRAP_deflateReset_keepDict(z_streamp strm); + + + +/*** DECOMPRESSION ***/ +typedef enum { ZWRAP_FORCE_ZLIB, ZWRAP_AUTO } ZWRAP_decompress_type; + +/* enables/disables automatic recognition of zstd/zlib compressed data during runtime */ +void ZWRAP_setDecompressionType(ZWRAP_decompress_type type); + +/* checks zstd decompression type */ +ZWRAP_decompress_type ZWRAP_getDecompressionType(void); + +/* Checks if zstd decompression is used for a given stream. + If will return 1 only when inflate() was called and zstd header was detected. */ +int ZWRAP_isUsingZSTDdecompression(z_streamp strm); + +/* Similar to inflateReset but preserves dictionary set using inflateSetDictionary. + inflate() will return Z_NEED_DICT only for the first time what will improve decompression speed. + For zlib streams this method redirects to inflateReset. */ +int ZWRAP_inflateReset_keepDict(z_streamp strm); + + #if defined (__cplusplus) } #endif