[med-svn] [libzstd] 01/04: New upstream version 1.1.0

Kevin Murray daube-guest at moszumanska.debian.org
Sun Oct 16 07:31:21 UTC 2016


This is an automated email from the git hooks/post-receive script.

daube-guest pushed a commit to branch master
in repository libzstd.

commit c3f5428c4cdfbd00a66543ca54b3be89e900f0db
Author: Kevin Murray <spam at kdmurray.id.au>
Date:   Sun Oct 16 15:56:53 2016 +1100

    New upstream version 1.1.0
---
 .gitattributes                                     |    2 +-
 .gitignore                                         |    2 +
 .travis.yml                                        |   61 +-
 Makefile                                           |   18 +-
 NEWS                                               |   19 +-
 README.md                                          |   53 +-
 appveyor.yml                                       |  131 ++-
 {projects => build}/.gitignore                     |    2 +-
 {projects => build}/README.md                      |    4 +-
 .../VS2005/fullbench/fullbench.vcproj              |    0
 {projects => build}/VS2005/fuzzer/fuzzer.vcproj    |    0
 {projects => build}/VS2005/zstd.sln                |    0
 {projects => build}/VS2005/zstd/zstd.vcproj        |    8 +-
 {projects => build}/VS2005/zstdlib/zstdlib.vcproj  |   44 +-
 .../VS2008/fullbench/fullbench.vcproj              |    0
 {projects => build}/VS2008/fuzzer/fuzzer.vcproj    |    0
 {projects => build}/VS2008/zstd.sln                |    0
 {projects => build}/VS2008/zstd/zstd.vcproj        |    8 +-
 {projects => build}/VS2008/zstdlib/zstdlib.vcproj  |   44 +-
 {projects => build}/VS2010/CompileAsCpp.props      |    0
 {projects => build}/VS2010/datagen/datagen.vcxproj |    0
 .../VS2010/fullbench/fullbench.vcxproj             |    0
 {projects => build}/VS2010/fuzzer/fuzzer.vcxproj   |    0
 {projects => build}/VS2010/zstd.sln                |    0
 build/VS2010/zstd/generate_res/generate_res.bat    |    3 +
 build/VS2010/zstd/generate_res/verrsrc.h           |  172 ++++
 build/VS2010/zstd/generate_res/zstd32.res          |  Bin 0 -> 948 bytes
 build/VS2010/zstd/generate_res/zstd64.res          |  Bin 0 -> 948 bytes
 .../zstdlib.rc => build/VS2010/zstd/zstd.rc        |  102 +-
 {projects => build}/VS2010/zstd/zstd.vcxproj       |   13 +-
 {projects => build}/VS2010/zstdlib/zstdlib.rc      |  102 +-
 {projects => build}/VS2010/zstdlib/zstdlib.vcxproj |   23 +-
 {projects/build => build/VS_scripts}/README.md     |    0
 .../build => build/VS_scripts}/build.VS2010.cmd    |    0
 .../build => build/VS_scripts}/build.VS2012.cmd    |    0
 .../build => build/VS_scripts}/build.VS2013.cmd    |    0
 .../build => build/VS_scripts}/build.VS2015.cmd    |    0
 .../build => build/VS_scripts}/build.generic.cmd   |    0
 {projects => build}/cmake/.gitignore               |    0
 build/cmake/CMakeLists.txt                         |   36 +
 .../CMakeModules/AddExtraCompilationFlags.cmake    |    0
 {projects => build}/cmake/cmake_uninstall.cmake.in |    0
 {projects => build}/cmake/lib/CMakeLists.txt       |    2 +-
 {projects => build}/cmake/programs/.gitignore      |    0
 {projects => build}/cmake/programs/CMakeLists.txt  |    0
 {projects => build}/cmake/tests/.gitignore         |    0
 {projects => build}/cmake/tests/CMakeLists.txt     |    0
 contrib/pzstd/.gitignore                           |    2 +
 contrib/pzstd/ErrorHolder.h                        |   54 ++
 contrib/pzstd/Makefile                             |  118 +++
 contrib/pzstd/Options.cpp                          |  426 +++++++++
 contrib/pzstd/Options.h                            |   68 ++
 contrib/pzstd/Pzstd.cpp                            |  641 +++++++++++++
 contrib/pzstd/Pzstd.h                              |   96 ++
 contrib/pzstd/README.md                            |   55 ++
 contrib/pzstd/SkippableFrame.cpp                   |   30 +
 contrib/pzstd/SkippableFrame.h                     |   64 ++
 contrib/pzstd/images/Cspeed.png                    |  Bin 0 -> 69804 bytes
 contrib/pzstd/images/Dspeed.png                    |  Bin 0 -> 26335 bytes
 contrib/pzstd/main.cpp                             |   32 +
 contrib/pzstd/test/Makefile                        |   48 +
 contrib/pzstd/test/OptionsTest.cpp                 |  542 +++++++++++
 contrib/pzstd/test/PzstdTest.cpp                   |  155 +++
 contrib/pzstd/test/RoundTrip.h                     |   86 ++
 contrib/pzstd/test/RoundTripTest.cpp               |   86 ++
 contrib/pzstd/utils/Buffer.h                       |   99 ++
 contrib/pzstd/utils/FileSystem.h                   |   94 ++
 contrib/pzstd/utils/Likely.h                       |   28 +
 contrib/pzstd/utils/Range.h                        |  131 +++
 contrib/pzstd/utils/ScopeGuard.h                   |   50 +
 contrib/pzstd/utils/ThreadPool.h                   |   58 ++
 contrib/pzstd/utils/WorkQueue.h                    |  184 ++++
 contrib/pzstd/utils/test/BufferTest.cpp            |   89 ++
 contrib/pzstd/utils/test/Makefile                  |   42 +
 contrib/pzstd/utils/test/RangeTest.cpp             |   82 ++
 contrib/pzstd/utils/test/ScopeGuardTest.cpp        |   28 +
 contrib/pzstd/utils/test/ThreadPoolTest.cpp        |   67 ++
 contrib/pzstd/utils/test/WorkQueueTest.cpp         |  262 +++++
 examples/Makefile                                  |   13 +-
 examples/README.md                                 |   23 +-
 examples/dictionary_compression.c                  |   10 +-
 examples/dictionary_decompression.c                |    4 +-
 examples/simple_compression.c                      |    4 +-
 examples/simple_decompression.c                    |    4 +-
 examples/streaming_compression.c                   |   17 +-
 examples/streaming_decompression.c                 |   36 +-
 lib/Makefile                                       |   12 +-
 lib/common/fse_decompress.c                        |   13 +-
 lib/common/xxhash.c                                |    2 +-
 lib/common/zstd_internal.h                         |   35 +-
 lib/compress/fse_compress.c                        |   13 +-
 lib/compress/huf_compress.c                        |   16 -
 lib/compress/zstd_compress.c                       |  346 +++----
 lib/decompress/huf_decompress.c                    |    8 -
 lib/decompress/zstd_decompress.c                   |  532 +++++++----
 lib/dictBuilder/zdict.c                            |   83 +-
 lib/dictBuilder/zdict.h                            |    2 +-
 lib/legacy/zstd_v01.c                              |  185 ++--
 lib/legacy/zstd_v02.c                              |   77 +-
 lib/legacy/zstd_v03.c                              |   80 +-
 lib/legacy/zstd_v04.c                              |  214 +----
 lib/legacy/zstd_v05.c                              |  218 +----
 lib/legacy/zstd_v06.c                              |  238 +----
 lib/legacy/zstd_v06.h                              |   68 +-
 lib/legacy/zstd_v07.c                              |  265 +-----
 lib/legacy/zstd_v07.h                              |   59 +-
 lib/libzstd.pc.in                                  |    2 +-
 lib/zstd.h                                         |  105 +-
 programs/.gitignore                                |    1 +
 programs/Makefile                                  |   23 +-
 programs/bench.c                                   |    3 +-
 programs/datagen.c                                 |    5 +-
 programs/datagen.h                                 |    5 +-
 programs/fileio.c                                  |  110 +--
 programs/fileio.h                                  |    6 +-
 programs/util.h                                    |   96 +-
 programs/zstd.1                                    |    6 +-
 programs/zstdcli.c                                 |   85 +-
 projects/cmake/CMakeLists.txt                      |   60 --
 tests/Makefile                                     |    6 +-
 tests/datagencli.c                                 |    2 +-
 tests/fullbench.c                                  |    6 +-
 tests/fuzzer.c                                     |   21 +-
 tests/playTests.sh                                 |   32 +-
 tests/test-zstd-speed.py                           |   83 +-
 tests/test-zstd-versions.py                        |    2 +-
 tests/zbufftest.c                                  |   52 +-
 tests/zstreamtest.c                                |  197 ++--
 zlibWrapper/.gitignore                             |    2 +-
 zlibWrapper/Makefile                               |   79 +-
 zlibWrapper/README.md                              |   74 +-
 zlibWrapper/examples/example.c                     |   16 +-
 zlibWrapper/examples/fitblk.c                      |  253 +++++
 zlibWrapper/examples/fitblk_original.c             |  233 +++++
 zlibWrapper/examples/zwrapbench.c                  | 1002 ++++++++++++++++++++
 zlibWrapper/zstd_zlibwrapper.c                     |  829 ++++++++++------
 zlibWrapper/zstd_zlibwrapper.h                     |   44 +-
 zstd.rb                                            |   18 -
 zstd_compression_format.md                         |  242 ++++-
 139 files changed, 8183 insertions(+), 2660 deletions(-)

diff --git a/.gitattributes b/.gitattributes
index 3870801..6212bd4 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -14,7 +14,7 @@
 *.vcxproj* text eol=crlf
 *.vcproj* text eol=crlf
 *.suo binary
-*.rc binary
+*.rc text eol=crlf
 
 # Windows
 *.bat text eol=crlf
diff --git a/.gitignore b/.gitignore
index 5f2ca97..751b674 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 # Object files
 *.o
 *.ko
+*.dSYM
 
 # Libraries
 *.lib
@@ -30,3 +31,4 @@ _zstdbench/
 *.idea
 *.swp
 .DS_Store
+googletest/
diff --git a/.travis.yml b/.travis.yml
index 350fc1e..ff6ab0f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,13 +3,26 @@ compiler: gcc
 matrix:
   fast_finish: true
   include:
+    # OS X Mavericks
+    - os: osx
+      env: PLATFORM="OS X Mavericks" CMD="make gnu90test && make clean && make test && make clean && make travis-install"
     # Container-based Ubuntu 12.04 LTS Server Edition 64 bit (doesn't support 32-bit includes)
     - os: linux
       sudo: false
       env: PLATFORM="Ubuntu 12.04 container" CMD="make test && make clean && make travis-install"
     - os: linux
       sudo: false
-      env: PLATFORM="Ubuntu 12.04 container" CMD="make -C tests test-zstd_nolegacy && make clean && make zlibwrapper && make clean && make cmaketest"
+      language: cpp
+      install:
+        - export CXX="g++-4.8" CC="gcc-4.8"
+      addons:
+        apt:
+          sources:
+            - ubuntu-toolchain-r-test
+          packages:
+            - gcc-4.8
+            - g++-4.8
+      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"
@@ -19,14 +32,6 @@ matrix:
     # Standard Ubuntu 12.04 LTS Server Edition 64 bit
     - os: linux
       sudo: required
-      env: PLATFORM="Ubuntu 12.04" CMD="make gpptest && make clean && make gnu90test && make clean && make c99test && make clean && make gnu99test && make clean && make clangtest"
-      addons:
-        apt:
-          packages:
-            - libc6-dev-i386
-            - g++-multilib 
-    - os: linux
-      sudo: required
       env: PLATFORM="Ubuntu 12.04" CMD="make armtest"
       addons:
         apt:
@@ -39,35 +44,54 @@ matrix:
             - qemu-user-static
     - os: linux
       sudo: required
-      env: PLATFORM="Ubuntu 12.04" CMD="make -C tests test32"
+      env: PLATFORM="Ubuntu 12.04" CMD="make -C tests versionsTest"
+    - os: linux
+      sudo: required
+      env: PLATFORM="Ubuntu 12.04" CMD="make asan32"
       addons:
         apt:
+          sources:
+            - ubuntu-toolchain-r-test
           packages:
             - libc6-dev-i386
             - gcc-multilib
+    # Ubuntu 14.04 LTS Server Edition 64 bit
     - os: linux
+      dist: trusty
       sudo: required
-      env: PLATFORM="Ubuntu 12.04" CMD="make -C tests versionsTest"
+      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
-      env: PLATFORM="Ubuntu 12.04" CMD="make asan32"
+      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
-            - gcc-multilib
+            - g++-multilib
+            - gcc-4.8
+            - gcc-4.8-multilib
+            - g++-4.8
+            - g++-4.8-multilib
     - os: linux
+      dist: trusty
       sudo: required
-      env: PLATFORM="Ubuntu 12.04" CMD="make -C tests valgrindTest"
+      env: PLATFORM="Ubuntu 14.04" CMD="make -C tests test32"
       addons:
         apt:
           packages:
-            - valgrind
-    # Ubuntu 14.04 LTS Server Edition 64 bit
+            - libc6-dev-i386
+            - gcc-multilib
     - 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:
@@ -78,9 +102,6 @@ matrix:
             - gcc-5-multilib
             - gcc-6
             - gcc-6-multilib
-    # OS X Mavericks
-    - os: osx
-      env: PLATFORM="OS X Mavericks" CMD="make gnu90test && make clean && make test && make clean && make travis-install"
   exclude:
     - compiler: gcc
 
diff --git a/Makefile b/Makefile
index 0ca7714..ac0c583 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) $@
@@ -44,14 +44,14 @@ clean:
 	@$(MAKE) -C $(PRGDIR) $@ > $(VOID)
 	@$(MAKE) -C $(TESTDIR) $@ > $(VOID)
 	@$(MAKE) -C $(ZWRAPDIR) $@ > $(VOID)
-	@rm -f zstd
+	@$(RM) zstd
 	@echo Cleaning completed
 
 
 #----------------------------------------------------------------------------------
 #make install is validated only for Linux, OSX, kFreeBSD, Hurd and some BSD targets
 #----------------------------------------------------------------------------------
-ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU FreeBSD DragonFly))
+ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU FreeBSD DragonFly NetBSD))
 HOST_OS = POSIX
 install:
 	$(MAKE) -C $(ZSTDDIR) $@
@@ -121,9 +121,9 @@ endif
 ifneq (,$(filter $(HOST_OS),MSYS POSIX))
 cmaketest:
 	cmake --version
-	rm -rf 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 2e30072..ad56d17 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,17 @@
+v1.1.0
+New : contrib/pzstd, parallel version of zstd, by Nick Terrell
+added : NetBSD install target (#338)
+Improved : speed for batches of small files
+Improved : speed of zlib wrapper, by Przemyslaw Skibinski
+Changed : libzstd on Windows supports legacy formats, by Christophe Chevalier
+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
+Fixed : Legacy decoders use unified error codes, reported by benrg (#341), fixed by Przemyslaw Skibinski
+Fixed : compatibility with OpenBSD, reported by Juan Francisco Cantero Hurtado (#319)
+Fixed : compatibility with Hurd, by Przemyslaw Skibinski (#365)
+Fixed : zstd-pgo, reported by octoploid (#329)
+
 v1.0.0
 Change Licensing, all project is now BSD, Copyright Facebook
 Small decompression speed improvement
@@ -29,6 +43,9 @@ Modified : minor compression level adaptations
 Updated : compression format specification to v0.2.0
 changed : zstd.h moved to /lib directory
 
+v0.7.5
+Transition version, supporting decoding of v0.8.x
+
 v0.7.4
 Added : homebrew for Mac, by Daniel Cade
 Added : more examples
@@ -179,6 +196,6 @@ frame concatenation support
 v0.1.1
 fix compression bug
 detects write-flush errors
-git at github.com:Cyan4973/zstd.git
+
 v0.1.0
 first release
diff --git a/README.md b/README.md
index cf4a2ab..53609a1 100644
--- a/README.md
+++ b/README.md
@@ -7,10 +7,10 @@ you can consult a list of known ports on [Zstandard homepage](http://www.zstd.ne
 
 |Branch      |Status   |
 |------------|---------|
-|master      | [![Build Status](https://travis-ci.org/Cyan4973/zstd.svg?branch=master)](https://travis-ci.org/Cyan4973/zstd) |
-|dev         | [![Build Status](https://travis-ci.org/Cyan4973/zstd.svg?branch=dev)](https://travis-ci.org/Cyan4973/zstd) |
+|master      | [![Build Status](https://travis-ci.org/facebook/zstd.svg?branch=master)](https://travis-ci.org/facebook/zstd) |
+|dev         | [![Build Status](https://travis-ci.org/facebook/zstd.svg?branch=dev)](https://travis-ci.org/facebook/zstd) |
 
-As a reference, several fast compression algorithms were tested and compared on a Core i7-3930K CPU @ 4.5GHz, using [lzbench], an open-source in-memory benchmark by @inikep compiled with gcc 5.4.0, with the [Silesia compression corpus].
+As a reference, several fast compression algorithms were tested and compared on a Core i7-3930K CPU @ 4.5GHz, using [lzbench], an open-source in-memory benchmark by @inikep compiled with GCC 5.4.0, with the [Silesia compression corpus].
 
 [lzbench]: https://github.com/inikep/lzbench
 [Silesia compression corpus]: http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia
@@ -32,32 +32,32 @@ As a reference, several fast compression algorithms were tested and compared on
 [LZ4]: http://www.lz4.org/
 
 Zstd can also offer stronger compression ratios at the cost of compression speed.
-Speed vs Compression trade-off is configurable by small increment. Decompression speed is preserved and remain roughly the same at all settings, a property shared by most LZ compression algorithms, such as [zlib] or lzma.
+Speed vs Compression trade-off is configurable by small increments. Decompression speed is preserved and remains roughly the same at all settings, a property shared by most LZ compression algorithms, such as [zlib] or lzma.
 
-The following tests were run on a Core i7-3930K CPU @ 4.5GHz, using [lzbench], an open-source in-memory benchmark by @inikep compiled with gcc 5.2.1, on the [Silesia compression corpus].
+The following tests were run on a Core i7-3930K CPU @ 4.5GHz, using [lzbench], an open-source in-memory benchmark by @inikep compiled with GCC 5.2.1, on the [Silesia compression corpus].
 
 Compression Speed vs Ratio | Decompression Speed
 ---------------------------|--------------------
 ![Compression Speed vs Ratio](images/Cspeed4.png "Compression Speed vs Ratio") | ![Decompression Speed](images/Dspeed4.png "Decompression Speed")
 
-Several algorithms can produce higher compression ratio but at slower speed, falling outside of the graph.
+Several algorithms can produce higher compression ratios, but at slower speeds, falling outside of the graph.
 For a larger picture including very slow modes, [click on this link](images/DCspeed5.png) .
 
 
 ### The case for Small Data compression
 
-Previous charts provide results applicable to typical files and streams scenarios (several MB). Small data come with different perspectives. The smaller the amount of data to compress, the more difficult it is to achieve any significant compression.
+Previous charts provide results applicable to typical file and stream scenarios (several MB). Small data comes with different perspectives. The smaller the amount of data to compress, the more difficult it is to achieve any significant compression.
 
-This problem is common to any compression algorithm. The reason is, compression algorithms learn from past data how to compress future data. But at the beginning of a new file, there is no "past" to build upon.
+This problem is common to many compression algorithms. The reason is, compression algorithms learn from past data how to compress future data. But at the beginning of a new file, there is no "past" to build upon.
 
-To solve this situation, Zstd offers a __training mode__, which can be used to tune the algorithm for a selected type of data, by providing it with a few samples. The result of the training is stored in a file called "dictionary", which can be loaded before compression and decompression. Using this dictionary, the compression ratio achievable on small data improves dramatically :
+To solve this situation, Zstd offers a __training mode__, which can be used to tune the algorithm for a selected type of data, by providing it with a few samples. The result of the training is stored in a file called "dictionary", which can be loaded before compression and decompression. Using this dictionary, the compression ratio achievable on small data improves dramatically:
 
 ![Compressing Small Data](images/smallData.png "Compressing Small Data")
 
 These compression gains are achieved while simultaneously providing faster compression and decompression speeds.
 
-Dictionary work if there is some correlation in a family of small data (there is no _universal dictionary_).
-Hence, deploying one dictionary per type of data will provide the greater benefits. Dictionary gains are mostly effective in the first few KB. Then, the compression algorithm will rely more and more on previously decoded content to compress the rest of the file.
+Dictionary works if there is some correlation in a family of small data (there is no _universal dictionary_).
+Hence, deploying one dictionary per type of data will provide the greatest benefits. Dictionary gains are mostly effective in the first few KB. Then, the compression algorithm will rely more and more on previously decoded content to compress the rest of the file.
 
 #### Dictionary compression How To :
 
@@ -73,9 +73,38 @@ Hence, deploying one dictionary per type of data will provide the greater benefi
 
 `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 amount of data in multiple formats and use cases.
+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.
 Zstandard is considered safe for production environments.
 
 ### License
diff --git a/appveyor.yml b/appveyor.yml
index 4e21db1..6345c7b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,6 +1,12 @@
 version: 1.0.{build}
 environment:
   matrix:
+  - COMPILER: "gcc"
+    MAKE_PARAMS: "test"
+    PLATFORM: "mingw64"
+  - COMPILER: "gcc"
+    MAKE_PARAMS: "test"
+    PLATFORM: "mingw32"
   - COMPILER: "visual"
     CONFIGURATION: "Debug"
     PLATFORM: "x64"
@@ -13,15 +19,10 @@ environment:
   - COMPILER: "visual"
     CONFIGURATION: "Release"
     PLATFORM: "Win32"
-  - COMPILER: "gcc"
-    MAKE_PARAMS: "test"
-    PLATFORM: "mingw64"
-  - COMPILER: "gcc"
-    MAKE_PARAMS: "test"
-    PLATFORM: "mingw32"
 
 install:
   - ECHO Installing %COMPILER% %PLATFORM% %CONFIGURATION%
+  - MKDIR bin
   - if [%COMPILER%]==[gcc] SET PATH_ORIGINAL=%PATH%
   - if [%COMPILER%]==[gcc] (
       SET "CLANG_PARAMS=-C tests zstd fullbench fuzzer zbufftest paramgrill datagen CC=clang MOREFLAGS="--target=x86_64-w64-mingw32 -Werror -Wconversion -Wno-sign-conversion"" &&
@@ -35,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 *** &&
@@ -45,7 +46,15 @@ build_script:
       ECHO make %CLANG_PARAMS% &&
       make %CLANG_PARAMS% &&
       COPY tests\fuzzer.exe tests\fuzzer_clang.exe &&
-      make clean
+      make clean &&
+      ECHO *** &&
+      ECHO *** Building pzstd for %PLATFORM% &&
+      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] (
       ECHO *** &&
@@ -54,58 +63,66 @@ build_script:
       make -v &&
       cc -v &&
       ECHO make %MAKE_PARAMS% &&
-      make %MAKE_PARAMS% &&
-      make clean
+      make %MAKE_PARAMS%
+    )
+  - if [%COMPILER%]==[gcc] if [%PLATFORM%]==[mingw64] (
+      COPY programs\zstd.exe bin\zstd.exe &&
+      appveyor PushArtifact bin\zstd.exe
     )
+  - if [%COMPILER%]==[gcc] if [%PLATFORM%]==[mingw32] (
+      COPY programs\zstd.exe bin\zstd32.exe &&
+      appveyor PushArtifact bin\zstd32.exe
+    )
+  - if [%COMPILER%]==[gcc] make clean
   - if [%COMPILER%]==[visual] (
       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:
@@ -126,3 +143,29 @@ test_script:
       fuzzer_VS2013_%PLATFORM%_Release.exe %FUZZERTEST% &&
       fuzzer_VS2015_%PLATFORM%_Release.exe %FUZZERTEST%
     )
+
+artifacts:
+  - path: bin\zstd.exe
+  - path: bin\zstd32.exe
+
+deploy:
+- provider: GitHub
+  auth_token:
+    secure: LgJo8emYc3sFnlNWkGl4/VYK3nk/8+RagcsqDlAi3xeqNGNutnKjcftjg84uJoT4
+  artifact: bin\zstd.exe
+  force_update: true
+  on:
+    branch: autobuild
+    COMPILER: gcc
+    PLATFORM: "mingw64"
+    appveyor_repo_tag: true
+- provider: GitHub
+  auth_token:
+    secure: LgJo8emYc3sFnlNWkGl4/VYK3nk/8+RagcsqDlAi3xeqNGNutnKjcftjg84uJoT4
+  artifact: bin\zstd32.exe
+  force_update: true
+  on:
+    branch: autobuild
+    COMPILER: gcc
+    PLATFORM: "mingw32"
+    appveyor_repo_tag: true
diff --git a/projects/.gitignore b/build/.gitignore
similarity index 92%
rename from projects/.gitignore
rename to build/.gitignore
index 7ceb958..86ed710 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 85%
rename from projects/README.md
rename to build/README.md
index 39742fb..8dc6732 100644
--- a/projects/README.md
+++ b/build/README.md
@@ -8,13 +8,13 @@ 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
 
 1. Install Visual Studio e.g. VS 2015 Community Edition (it's free).
-2. Download the latest version of zstd from https://github.com/Cyan4973/zstd/releases
+2. Download the latest version of zstd from https://github.com/facebook/zstd/releases
 3. Decompress ZIP archive.
 4. Go to decompressed directory then to `projects` then `VS2010` and open `zstd.sln`
 5. Visual Studio will ask about converting VS2010 project to VS2015 and you should agree.
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 ff45e39..68c3578 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 2313c87..7ea3d9b 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"
 				>
 			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v01.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v02.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v03.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v04.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v05.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v06.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v07.c"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="Header Files"
@@ -481,6 +509,14 @@
 				RelativePath="..\..\..\lib\legacy\zstd_v05.h"
 				>
 			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v06.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v07.h"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="Resource Files"
diff --git a/projects/VS2008/fullbench/fullbench.vcproj b/build/VS2008/fullbench/fullbench.vcproj
similarity index 100%
rename from projects/VS2008/fullbench/fullbench.vcproj
rename to build/VS2008/fullbench/fullbench.vcproj
diff --git a/projects/VS2008/fuzzer/fuzzer.vcproj b/build/VS2008/fuzzer/fuzzer.vcproj
similarity index 100%
rename from projects/VS2008/fuzzer/fuzzer.vcproj
rename to build/VS2008/fuzzer/fuzzer.vcproj
diff --git a/projects/VS2008/zstd.sln b/build/VS2008/zstd.sln
similarity index 100%
rename from projects/VS2008/zstd.sln
rename to build/VS2008/zstd.sln
diff --git a/projects/VS2008/zstd/zstd.vcproj b/build/VS2008/zstd/zstd.vcproj
similarity index 97%
rename from projects/VS2008/zstd/zstd.vcproj
rename to build/VS2008/zstd/zstd.vcproj
index 9d71f6e..b272ffd 100644
--- a/projects/VS2008/zstd/zstd.vcproj
+++ b/build/VS2008/zstd/zstd.vcproj
@@ -45,7 +45,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder"
-				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
 				RuntimeLibrary="3"
@@ -122,7 +122,7 @@
 				EnableIntrinsicFunctions="true"
 				OmitFramePointers="true"
 				AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder"
-				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE"
 				RuntimeLibrary="0"
 				EnableFunctionLevelLinking="true"
 				UsePrecompiledHeader="0"
@@ -197,7 +197,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder"
-				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
 				RuntimeLibrary="3"
@@ -275,7 +275,7 @@
 				EnableIntrinsicFunctions="true"
 				OmitFramePointers="true"
 				AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\lib\dictBuilder"
-				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				PreprocessorDefinitions="ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE"
 				RuntimeLibrary="0"
 				EnableFunctionLevelLinking="true"
 				UsePrecompiledHeader="0"
diff --git a/projects/VS2008/zstdlib/zstdlib.vcproj b/build/VS2008/zstdlib/zstdlib.vcproj
similarity index 90%
rename from projects/VS2008/zstdlib/zstdlib.vcproj
rename to build/VS2008/zstdlib/zstdlib.vcproj
index db596b4..fa4cd26 100644
--- a/projects/VS2008/zstdlib/zstdlib.vcproj
+++ b/build/VS2008/zstdlib/zstdlib.vcproj
@@ -45,7 +45,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder"
-				PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_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"
@@ -121,7 +121,7 @@
 				EnableIntrinsicFunctions="true"
 				OmitFramePointers="true"
 				AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder"
-				PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_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"
@@ -195,7 +195,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="0"
 				AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder"
-				PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_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"
@@ -272,7 +272,7 @@
 				EnableIntrinsicFunctions="true"
 				OmitFramePointers="true"
 				AdditionalIncludeDirectories="$(SolutionDir)..\..\lib;$(SolutionDir)..\..\lib\common;$(SolutionDir)..\..\lib\legacy;$(SolutionDir)..\..\programs\legacy;$(SolutionDir)..\..\lib\dictBuilder"
-				PreprocessorDefinitions="ZSTD_DLL_EXPORT=1;ZSTD_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"
@@ -380,6 +380,34 @@
 				RelativePath="..\..\..\lib\decompress\zstd_decompress.c"
 				>
 			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v01.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v02.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v03.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v04.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v05.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v06.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v07.c"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="Header Files"
@@ -482,6 +510,14 @@
 				RelativePath="..\..\..\lib\legacy\zstd_v05.h"
 				>
 			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v06.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\..\lib\legacy\zstd_v07.h"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="Resource Files"
diff --git a/projects/VS2010/CompileAsCpp.props b/build/VS2010/CompileAsCpp.props
similarity index 100%
rename from projects/VS2010/CompileAsCpp.props
rename to build/VS2010/CompileAsCpp.props
diff --git a/projects/VS2010/datagen/datagen.vcxproj b/build/VS2010/datagen/datagen.vcxproj
similarity index 100%
rename from projects/VS2010/datagen/datagen.vcxproj
rename to build/VS2010/datagen/datagen.vcxproj
diff --git a/projects/VS2010/fullbench/fullbench.vcxproj b/build/VS2010/fullbench/fullbench.vcxproj
similarity index 100%
rename from projects/VS2010/fullbench/fullbench.vcxproj
rename to build/VS2010/fullbench/fullbench.vcxproj
diff --git a/projects/VS2010/fuzzer/fuzzer.vcxproj b/build/VS2010/fuzzer/fuzzer.vcxproj
similarity index 100%
rename from projects/VS2010/fuzzer/fuzzer.vcxproj
rename to build/VS2010/fuzzer/fuzzer.vcxproj
diff --git a/projects/VS2010/zstd.sln b/build/VS2010/zstd.sln
similarity index 100%
rename from projects/VS2010/zstd.sln
rename to build/VS2010/zstd.sln
diff --git a/build/VS2010/zstd/generate_res/generate_res.bat b/build/VS2010/zstd/generate_res/generate_res.bat
new file mode 100644
index 0000000..b552dcc
--- /dev/null
+++ b/build/VS2010/zstd/generate_res/generate_res.bat
@@ -0,0 +1,3 @@
+REM http://stackoverflow.com/questions/708238/how-do-i-add-an-icon-to-a-mingw-gcc-compiled-executable
+REM copy "c:\Program Files (x86)\Windows Kits\8.1\Include\um\verrsrc.h" .
+windres -I ..\..\..\..\lib -O coff -I . -i ..\zstd.rc -o zstd.res
diff --git a/build/VS2010/zstd/generate_res/verrsrc.h b/build/VS2010/zstd/generate_res/verrsrc.h
new file mode 100644
index 0000000..37e48d3
--- /dev/null
+++ b/build/VS2010/zstd/generate_res/verrsrc.h
@@ -0,0 +1,172 @@
+#include <winapifamily.h>
+
+/*****************************************************************************\
+*                                                                             *
+* verrsrc.h -   Version Resource definitions                                  *
+*                                                                             *
+*               Include file declaring version resources in rc files          *
+*                                                                             *
+*               Copyright (c) Microsoft Corporation. All rights reserved.     *
+*                                                                             *
+\*****************************************************************************/
+
+#pragma region Application Family
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
+
+/* ----- Symbols ----- */
+#define VS_FILE_INFO            RT_VERSION
+#define VS_VERSION_INFO         1
+#define VS_USER_DEFINED         100
+
+/* ----- VS_VERSION.dwFileFlags ----- */
+#ifndef _MAC
+#define VS_FFI_SIGNATURE        0xFEEF04BDL
+#else
+#define VS_FFI_SIGNATURE        0xBD04EFFEL
+#endif
+#define VS_FFI_STRUCVERSION     0x00010000L
+#define VS_FFI_FILEFLAGSMASK    0x0000003FL
+
+/* ----- VS_VERSION.dwFileFlags ----- */
+#define VS_FF_DEBUG             0x00000001L
+#define VS_FF_PRERELEASE        0x00000002L
+#define VS_FF_PATCHED           0x00000004L
+#define VS_FF_PRIVATEBUILD      0x00000008L
+#define VS_FF_INFOINFERRED      0x00000010L
+#define VS_FF_SPECIALBUILD      0x00000020L
+
+/* ----- VS_VERSION.dwFileOS ----- */
+#define VOS_UNKNOWN             0x00000000L
+#define VOS_DOS                 0x00010000L
+#define VOS_OS216               0x00020000L
+#define VOS_OS232               0x00030000L
+#define VOS_NT                  0x00040000L
+#define VOS_WINCE               0x00050000L
+
+#define VOS__BASE               0x00000000L
+#define VOS__WINDOWS16          0x00000001L
+#define VOS__PM16               0x00000002L
+#define VOS__PM32               0x00000003L
+#define VOS__WINDOWS32          0x00000004L
+
+#define VOS_DOS_WINDOWS16       0x00010001L
+#define VOS_DOS_WINDOWS32       0x00010004L
+#define VOS_OS216_PM16          0x00020002L
+#define VOS_OS232_PM32          0x00030003L
+#define VOS_NT_WINDOWS32        0x00040004L
+
+/* ----- VS_VERSION.dwFileType ----- */
+#define VFT_UNKNOWN             0x00000000L
+#define VFT_APP                 0x00000001L
+#define VFT_DLL                 0x00000002L
+#define VFT_DRV                 0x00000003L
+#define VFT_FONT                0x00000004L
+#define VFT_VXD                 0x00000005L
+#define VFT_STATIC_LIB          0x00000007L
+
+/* ----- VS_VERSION.dwFileSubtype for VFT_WINDOWS_DRV ----- */
+#define VFT2_UNKNOWN            0x00000000L
+#define VFT2_DRV_PRINTER        0x00000001L
+#define VFT2_DRV_KEYBOARD       0x00000002L
+#define VFT2_DRV_LANGUAGE       0x00000003L
+#define VFT2_DRV_DISPLAY        0x00000004L
+#define VFT2_DRV_MOUSE          0x00000005L
+#define VFT2_DRV_NETWORK        0x00000006L
+#define VFT2_DRV_SYSTEM         0x00000007L
+#define VFT2_DRV_INSTALLABLE    0x00000008L
+#define VFT2_DRV_SOUND          0x00000009L
+#define VFT2_DRV_COMM           0x0000000AL
+#define VFT2_DRV_INPUTMETHOD    0x0000000BL
+#define VFT2_DRV_VERSIONED_PRINTER    0x0000000CL
+
+/* ----- VS_VERSION.dwFileSubtype for VFT_WINDOWS_FONT ----- */
+#define VFT2_FONT_RASTER        0x00000001L
+#define VFT2_FONT_VECTOR        0x00000002L
+#define VFT2_FONT_TRUETYPE      0x00000003L
+
+#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) */
+#pragma endregion
+
+#pragma region Desktop Family
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+
+/* ----- VerFindFile() flags ----- */
+#define VFFF_ISSHAREDFILE       0x0001
+
+#define VFF_CURNEDEST           0x0001
+#define VFF_FILEINUSE           0x0002
+#define VFF_BUFFTOOSMALL        0x0004
+
+/* ----- VerInstallFile() flags ----- */
+#define VIFF_FORCEINSTALL       0x0001
+#define VIFF_DONTDELETEOLD      0x0002
+
+#define VIF_TEMPFILE            0x00000001L
+#define VIF_MISMATCH            0x00000002L
+#define VIF_SRCOLD              0x00000004L
+
+#define VIF_DIFFLANG            0x00000008L
+#define VIF_DIFFCODEPG          0x00000010L
+#define VIF_DIFFTYPE            0x00000020L
+
+#define VIF_WRITEPROT           0x00000040L
+#define VIF_FILEINUSE           0x00000080L
+#define VIF_OUTOFSPACE          0x00000100L
+#define VIF_ACCESSVIOLATION     0x00000200L
+#define VIF_SHARINGVIOLATION    0x00000400L
+#define VIF_CANNOTCREATE        0x00000800L
+#define VIF_CANNOTDELETE        0x00001000L
+#define VIF_CANNOTRENAME        0x00002000L
+#define VIF_CANNOTDELETECUR     0x00004000L
+#define VIF_OUTOFMEMORY         0x00008000L
+
+#define VIF_CANNOTREADSRC       0x00010000L
+#define VIF_CANNOTREADDST       0x00020000L
+
+#define VIF_BUFFTOOSMALL        0x00040000L
+#define VIF_CANNOTLOADLZ32      0x00080000L
+#define VIF_CANNOTLOADCABINET   0x00100000L
+
+#ifndef RC_INVOKED              /* RC doesn't need to see the rest of this */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    
+/* 
+    FILE_VER_GET_... flags are for use by 
+    GetFileVersionInfoSizeEx
+    GetFileVersionInfoExW
+*/
+#define FILE_VER_GET_LOCALISED  0x01
+#define FILE_VER_GET_NEUTRAL    0x02
+#define FILE_VER_GET_PREFETCHED 0x04
+
+/* ----- Types and structures ----- */
+
+typedef struct tagVS_FIXEDFILEINFO
+{
+    DWORD   dwSignature;            /* e.g. 0xfeef04bd */
+    DWORD   dwStrucVersion;         /* e.g. 0x00000042 = "0.42" */
+    DWORD   dwFileVersionMS;        /* e.g. 0x00030075 = "3.75" */
+    DWORD   dwFileVersionLS;        /* e.g. 0x00000031 = "0.31" */
+    DWORD   dwProductVersionMS;     /* e.g. 0x00030010 = "3.10" */
+    DWORD   dwProductVersionLS;     /* e.g. 0x00000031 = "0.31" */
+    DWORD   dwFileFlagsMask;        /* = 0x3F for version "0.42" */
+    DWORD   dwFileFlags;            /* e.g. VFF_DEBUG | VFF_PRERELEASE */
+    DWORD   dwFileOS;               /* e.g. VOS_DOS_WINDOWS16 */
+    DWORD   dwFileType;             /* e.g. VFT_DRIVER */
+    DWORD   dwFileSubtype;          /* e.g. VFT2_DRV_KEYBOARD */
+    DWORD   dwFileDateMS;           /* e.g. 0 */
+    DWORD   dwFileDateLS;           /* e.g. 0 */
+} VS_FIXEDFILEINFO;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* !RC_INVOKED */
+
+#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */
+#pragma endregion
+
diff --git a/build/VS2010/zstd/generate_res/zstd32.res b/build/VS2010/zstd/generate_res/zstd32.res
new file mode 100644
index 0000000..362d9c2
Binary files /dev/null and b/build/VS2010/zstd/generate_res/zstd32.res differ
diff --git a/build/VS2010/zstd/generate_res/zstd64.res b/build/VS2010/zstd/generate_res/zstd64.res
new file mode 100644
index 0000000..d726146
Binary files /dev/null and b/build/VS2010/zstd/generate_res/zstd64.res differ
diff --git a/projects/VS2010/zstdlib/zstdlib.rc b/build/VS2010/zstd/zstd.rc
similarity index 76%
copy from projects/VS2010/zstdlib/zstdlib.rc
copy to build/VS2010/zstd/zstd.rc
index 6c4dde4..464b2f1 100644
--- a/projects/VS2010/zstdlib/zstdlib.rc
+++ b/build/VS2010/zstd/zstd.rc
@@ -1,51 +1,51 @@
-// Microsoft Visual C++ generated resource script.
-//
-
-#include "zstd.h" /* ZSTD_VERSION_STRING */
-#define APSTUDIO_READONLY_SYMBOLS
-#include "verrsrc.h"
-#undef APSTUDIO_READONLY_SYMBOLS
-
-
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
-LANGUAGE 9, 1
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Version
-//
-
-VS_VERSION_INFO     VERSIONINFO
-  FILEVERSION       ZSTD_LIB_VERSION
-  PRODUCTVERSION    ZSTD_LIB_VERSION
- FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
-#ifdef _DEBUG
- FILEFLAGS VS_FF_DEBUG
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS VOS_NT_WINDOWS32
- FILETYPE VFT_DLL
- FILESUBTYPE VFT2_UNKNOWN
-BEGIN
-    BLOCK "StringFileInfo"
-    BEGIN
-        BLOCK "040904B0"
-        BEGIN
-            VALUE "CompanyName", "Yann Collet"
-            VALUE "FileDescription", "Fast and efficient compression algorithm"
-            VALUE "FileVersion", ZSTD_VERSION_STRING
-            VALUE "InternalName", "zstdlib.dll"
-            VALUE "LegalCopyright", "Copyright (C) 2013-2015, Yann Collet"
-            VALUE "OriginalFilename", "zstdlib.dll"
-            VALUE "ProductName", "Zstandard"
-            VALUE "ProductVersion", ZSTD_VERSION_STRING
-        END
-    END
-    BLOCK "VarFileInfo"
-    BEGIN
-        VALUE "Translation", 0x0409, 1200
-    END
-END
-
-#endif
+// Microsoft Visual C++ generated resource script.
+//
+
+#include "zstd.h" /* ZSTD_VERSION_STRING */
+#define APSTUDIO_READONLY_SYMBOLS
+#include "verrsrc.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE 9, 1
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO     VERSIONINFO
+  FILEVERSION       ZSTD_VERSION_MAJOR,ZSTD_VERSION_MINOR,ZSTD_VERSION_RELEASE,0
+  PRODUCTVERSION    ZSTD_VERSION_MAJOR,ZSTD_VERSION_MINOR,ZSTD_VERSION_RELEASE,0
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904B0"
+        BEGIN
+            VALUE "CompanyName", "Yann Collet"
+            VALUE "FileDescription", "Fast and efficient compression algorithm"
+            VALUE "FileVersion", ZSTD_VERSION_STRING
+            VALUE "InternalName", "zstd.exe"
+            VALUE "LegalCopyright", "Copyright (C) 2013-2016, Yann Collet"
+            VALUE "OriginalFilename", "zstd.exe"
+            VALUE "ProductName", "Zstandard"
+            VALUE "ProductVersion", ZSTD_VERSION_STRING
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x0409, 1200
+    END
+END
+
+#endif
diff --git a/projects/VS2010/zstd/zstd.vcxproj b/build/VS2010/zstd/zstd.vcxproj
similarity index 95%
rename from projects/VS2010/zstd/zstd.vcxproj
rename to build/VS2010/zstd/zstd.vcxproj
index 5922d92..eb7e7b5 100644
--- a/projects/VS2010/zstd/zstd.vcxproj
+++ b/build/VS2010/zstd/zstd.vcxproj
@@ -69,6 +69,9 @@
     <ClInclude Include="..\..\..\programs\fileio.h" />
     <ClInclude Include="..\..\..\programs\util.h" />
   </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="zstd.rc" />
+  </ItemGroup>
   <PropertyGroup Label="Globals">
     <ProjectGuid>{4E52A41A-F33B-4C7A-8C36-A1A6B4F4277C}</ProjectGuid>
     <Keyword>Win32Proj</Keyword>
@@ -146,7 +149,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level4</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <TreatWarningAsError>true</TreatWarningAsError>
       <EnablePREfast>false</EnablePREfast>
     </ClCompile>
@@ -162,7 +165,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level4</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <TreatWarningAsError>true</TreatWarningAsError>
       <EnablePREfast>false</EnablePREfast>
     </ClCompile>
@@ -180,7 +183,7 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <EnablePREfast>false</EnablePREfast>
       <TreatWarningAsError>false</TreatWarningAsError>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -201,7 +204,7 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <TreatWarningAsError>false</TreatWarningAsError>
       <EnablePREfast>false</EnablePREfast>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -217,4 +220,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/projects/VS2010/zstdlib/zstdlib.rc b/build/VS2010/zstdlib/zstdlib.rc
similarity index 83%
rename from projects/VS2010/zstdlib/zstdlib.rc
rename to build/VS2010/zstdlib/zstdlib.rc
index 6c4dde4..de8ecbc 100644
--- a/projects/VS2010/zstdlib/zstdlib.rc
+++ b/build/VS2010/zstdlib/zstdlib.rc
@@ -1,51 +1,51 @@
-// Microsoft Visual C++ generated resource script.
-//
-
-#include "zstd.h" /* ZSTD_VERSION_STRING */
-#define APSTUDIO_READONLY_SYMBOLS
-#include "verrsrc.h"
-#undef APSTUDIO_READONLY_SYMBOLS
-
-
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
-LANGUAGE 9, 1
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Version
-//
-
-VS_VERSION_INFO     VERSIONINFO
-  FILEVERSION       ZSTD_LIB_VERSION
-  PRODUCTVERSION    ZSTD_LIB_VERSION
- FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
-#ifdef _DEBUG
- FILEFLAGS VS_FF_DEBUG
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS VOS_NT_WINDOWS32
- FILETYPE VFT_DLL
- FILESUBTYPE VFT2_UNKNOWN
-BEGIN
-    BLOCK "StringFileInfo"
-    BEGIN
-        BLOCK "040904B0"
-        BEGIN
-            VALUE "CompanyName", "Yann Collet"
-            VALUE "FileDescription", "Fast and efficient compression algorithm"
-            VALUE "FileVersion", ZSTD_VERSION_STRING
-            VALUE "InternalName", "zstdlib.dll"
-            VALUE "LegalCopyright", "Copyright (C) 2013-2015, Yann Collet"
-            VALUE "OriginalFilename", "zstdlib.dll"
-            VALUE "ProductName", "Zstandard"
-            VALUE "ProductVersion", ZSTD_VERSION_STRING
-        END
-    END
-    BLOCK "VarFileInfo"
-    BEGIN
-        VALUE "Translation", 0x0409, 1200
-    END
-END
-
-#endif
+// Microsoft Visual C++ generated resource script.
+//
+
+#include "zstd.h" /* ZSTD_VERSION_STRING */
+#define APSTUDIO_READONLY_SYMBOLS
+#include "verrsrc.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE 9, 1
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO     VERSIONINFO
+  FILEVERSION       ZSTD_VERSION_MAJOR,ZSTD_VERSION_MINOR,ZSTD_VERSION_RELEASE,0
+  PRODUCTVERSION    ZSTD_VERSION_MAJOR,ZSTD_VERSION_MINOR,ZSTD_VERSION_RELEASE,0
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904B0"
+        BEGIN
+            VALUE "CompanyName", "Yann Collet"
+            VALUE "FileDescription", "Fast and efficient compression algorithm"
+            VALUE "FileVersion", ZSTD_VERSION_STRING
+            VALUE "InternalName", "zstdlib.dll"
+            VALUE "LegalCopyright", "Copyright (C) 2013-2016, Yann Collet"
+            VALUE "OriginalFilename", "zstdlib.dll"
+            VALUE "ProductName", "Zstandard"
+            VALUE "ProductVersion", ZSTD_VERSION_STRING
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x0409, 1200
+    END
+END
+
+#endif
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 232fdf4..b97808d 100644
--- a/projects/VS2010/zstdlib/zstdlib.vcxproj
+++ b/build/VS2010/zstdlib/zstdlib.vcxproj
@@ -32,6 +32,13 @@
     <ClCompile Include="..\..\..\lib\decompress\zstd_decompress.c" />
     <ClCompile Include="..\..\..\lib\dictBuilder\divsufsort.c" />
     <ClCompile Include="..\..\..\lib\dictBuilder\zdict.c" />
+    <ClCompile Include="..\..\..\lib\legacy\zstd_v01.c" />
+    <ClCompile Include="..\..\..\lib\legacy\zstd_v02.c" />
+    <ClCompile Include="..\..\..\lib\legacy\zstd_v03.c" />
+    <ClCompile Include="..\..\..\lib\legacy\zstd_v04.c" />
+    <ClCompile Include="..\..\..\lib\legacy\zstd_v05.c" />
+    <ClCompile Include="..\..\..\lib\legacy\zstd_v06.c" />
+    <ClCompile Include="..\..\..\lib\legacy\zstd_v07.c" />
     <ClInclude Include="..\..\..\lib\common\bitstream.h" />
     <ClInclude Include="..\..\..\lib\common\error_private.h" />
     <ClInclude Include="..\..\..\lib\common\error_public.h" />
@@ -40,6 +47,14 @@
     <ClInclude Include="..\..\..\lib\common\huf.h" />
     <ClInclude Include="..\..\..\lib\common\xxhash.h" />
     <ClInclude Include="..\..\..\lib\common\zbuff.h" />
+    <ClInclude Include="..\..\..\lib\legacy\zstd_legacy.h" />
+    <ClInclude Include="..\..\..\lib\legacy\zstd_v01.h" />
+    <ClInclude Include="..\..\..\lib\legacy\zstd_v02.h" />
+    <ClInclude Include="..\..\..\lib\legacy\zstd_v03.h" />
+    <ClInclude Include="..\..\..\lib\legacy\zstd_v04.h" />
+    <ClInclude Include="..\..\..\lib\legacy\zstd_v05.h" />
+    <ClInclude Include="..\..\..\lib\legacy\zstd_v06.h" />
+    <ClInclude Include="..\..\..\lib\legacy\zstd_v07.h" />
     <ClInclude Include="..\..\..\lib\zstd.h" />
     <ClInclude Include="..\..\..\lib\common\zstd_internal.h" />
     <ClInclude Include="..\..\..\lib\compress\zstd_opt.h" />
@@ -126,7 +141,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level4</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>ZSTD_DLL_EXPORT=1;ZSTD_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <MinimalRebuild>true</MinimalRebuild>
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -146,7 +161,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level4</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>ZSTD_DLL_EXPORT=1;ZSTD_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <TreatWarningAsError>true</TreatWarningAsError>
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -166,7 +181,7 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>ZSTD_DLL_EXPORT=1;ZSTD_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <EnablePREfast>false</EnablePREfast>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
@@ -188,7 +203,7 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>ZSTD_DLL_EXPORT=1;ZSTD_HEAPMODE=0;ZSTD_LEGACY_SUPPORT=0;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>ZSTD_DLL_EXPORT=1;ZSTD_LEGACY_SUPPORT=1;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <TreatWarningAsError>false</TreatWarningAsError>
       <EnablePREfast>false</EnablePREfast>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
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/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt
new file mode 100644
index 0000000..b8f5d18
--- /dev/null
+++ b/build/cmake/CMakeLists.txt
@@ -0,0 +1,36 @@
+# ################################################################
+# Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+# All rights reserved.
+#
+# This source code is licensed under the BSD-style license found in the
+# LICENSE file in the root directory of this source tree. An additional grant
+# of patent rights can be found in the PATENTS file in the same directory.
+# ################################################################
+
+PROJECT(zstd)
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.7)
+
+OPTION(ZSTD_LEGACY_SUPPORT "LEGACY SUPPORT" OFF)
+
+IF (ZSTD_LEGACY_SUPPORT)
+    MESSAGE(STATUS "ZSTD_LEGACY_SUPPORT defined!")
+    ADD_DEFINITIONS(-DZSTD_LEGACY_SUPPORT=1)
+ELSE (ZSTD_LEGACY_SUPPORT)
+    MESSAGE(STATUS "ZSTD_LEGACY_SUPPORT not defined!")
+    ADD_DEFINITIONS(-DZSTD_LEGACY_SUPPORT=0)
+ENDIF (ZSTD_LEGACY_SUPPORT)
+
+ADD_SUBDIRECTORY(lib)
+ADD_SUBDIRECTORY(programs)
+ADD_SUBDIRECTORY(tests)
+
+#-----------------------------------------------------------------------------
+# Add extra compilation flags
+#-----------------------------------------------------------------------------
+INCLUDE(CMakeModules/AddExtraCompilationFlags.cmake)
+ADD_EXTRA_COMPILATION_FLAGS()
+
+ADD_CUSTOM_TARGET(clean-all
+   COMMAND ${CMAKE_BUILD_TOOL} clean
+   COMMAND rm -rf ${CMAKE_BINARY_DIR}/
+)
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 36e8afa..c984145 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/.gitignore b/contrib/pzstd/.gitignore
new file mode 100644
index 0000000..84e68fb
--- /dev/null
+++ b/contrib/pzstd/.gitignore
@@ -0,0 +1,2 @@
+# compilation result
+pzstd
diff --git a/contrib/pzstd/ErrorHolder.h b/contrib/pzstd/ErrorHolder.h
new file mode 100644
index 0000000..188badc
--- /dev/null
+++ b/contrib/pzstd/ErrorHolder.h
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#pragma once
+
+#include <atomic>
+#include <cassert>
+#include <stdexcept>
+#include <string>
+
+namespace pzstd {
+
+// Coordinates graceful shutdown of the pzstd pipeline
+class ErrorHolder {
+  std::atomic<bool> error_;
+  std::string message_;
+
+ public:
+  ErrorHolder() : error_(false) {}
+
+  bool hasError() noexcept {
+    return error_.load();
+  }
+
+  void setError(std::string message) noexcept {
+    // Given multiple possibly concurrent calls, exactly one will ever succeed.
+    bool expected = false;
+    if (error_.compare_exchange_strong(expected, true)) {
+      message_ = std::move(message);
+    }
+  }
+
+  bool check(bool predicate, std::string message) noexcept {
+    if (!predicate) {
+      setError(std::move(message));
+    }
+    return !hasError();
+  }
+
+  std::string getError() noexcept {
+    error_.store(false);
+    return std::move(message_);
+  }
+
+  ~ErrorHolder() {
+    assert(!hasError());
+  }
+};
+}
diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile
new file mode 100644
index 0000000..e30be0b
--- /dev/null
+++ b/contrib/pzstd/Makefile
@@ -0,0 +1,118 @@
+# ##########################################################################
+# Copyright (c) 2016-present, 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.
+# ##########################################################################
+
+ZSTDDIR = ../../lib
+PROGDIR = ../../programs
+
+CPPFLAGS = -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(PROGDIR) -I.
+CXXFLAGS  ?= -O3
+CXXFLAGS  += -std=c++11
+CXXFLAGS  += $(MOREFLAGS)
+FLAGS    = $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS)
+
+
+ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c
+ZSTDCOMP_FILES := $(ZSTDDIR)/compress/zstd_compress.c $(ZSTDDIR)/compress/fse_compress.c $(ZSTDDIR)/compress/huf_compress.c
+ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/huf_decompress.c
+ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES)
+
+
+# Define *.exe as extension for Windows systems
+ifneq (,$(filter Windows%,$(OS)))
+EXT =.exe
+else
+EXT =
+endif
+
+.PHONY: default all test clean test32 googletest googletest32
+
+default: pzstd
+
+all: pzstd
+
+
+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 $@
+
+SkippableFrame.o: SkippableFrame.h SkippableFrame.cpp utils/*.h
+	$(CXX) $(FLAGS) -c SkippableFrame.cpp -o $@
+
+Options.o: Options.h Options.cpp
+	$(CXX) $(FLAGS) -c Options.cpp -o $@
+
+main.o: main.cpp *.h utils/*.h
+	$(CXX) $(FLAGS) -c main.cpp -o $@
+
+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
+
+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 MOREFLAGS="-Wall -Wextra -pedantic -Werror"
+	$(MAKE) -C test clean
+	$(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) pzstd32$(EXT)
+	@echo Cleaning completed
diff --git a/contrib/pzstd/Options.cpp b/contrib/pzstd/Options.cpp
new file mode 100644
index 0000000..ece8c07
--- /dev/null
+++ b/contrib/pzstd/Options.cpp
@@ -0,0 +1,426 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#include "Options.h"
+#include "utils/ScopeGuard.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <iterator>
+#include <thread>
+#include <util.h>
+#include <vector>
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) ||     \
+    defined(__CYGWIN__)
+#include <io.h> /* _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 <unistd.h> /* isatty */
+#define IS_CONSOLE(stdStream) isatty(fileno(stdStream))
+#else
+#define IS_CONSOLE(stdStream) 0
+#endif
+#endif
+
+namespace pzstd {
+
+namespace {
+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') {
+    result *= 10;
+    result += **arg - '0';
+    ++(*arg);
+  }
+  return result;
+}
+
+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, "  pzstd [args] [FILE(s)]\n");
+  std::fprintf(stderr, "Parallel ZSTD options:\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, "  -#                     : # 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(defaultNumThreads()), maxWindowLog(23),
+      compressionLevel(kDefaultCompressionLevel), decompress(false),
+      overwrite(false), keepSource(true), writeMode(WriteMode::Auto),
+      checksum(true), verbosity(2) {}
+
+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<const char *> localInputFiles;
+  for (int i = 1; i < argc; ++i) {
+    const char *arg = argv[i];
+    // Protect against empty arguments
+    if (arg[0] == 0) {
+      continue;
+    }
+    // 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;
+      } 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 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 't':
+        test = true;
+        decompress = true;
+        break;
+#ifdef UTIL_HAS_CREATEFILELIST
+      case 'r':
+        recursive = true;
+        break;
+#endif
+      case 'c':
+        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 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() : 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) {
+    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;
+  }
+}
+}
diff --git a/contrib/pzstd/Options.h b/contrib/pzstd/Options.h
new file mode 100644
index 0000000..97c3885
--- /dev/null
+++ b/contrib/pzstd/Options.h
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#pragma once
+
+#define ZSTD_STATIC_LINKING_ONLY
+#include "zstd.h"
+#undef ZSTD_STATIC_LINKING_ONLY
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace pzstd {
+
+struct Options {
+  enum class WriteMode { Regular, Auto, Sparse };
+
+  unsigned numThreads;
+  unsigned maxWindowLog;
+  unsigned compressionLevel;
+  bool decompress;
+  std::vector<std::string> inputFiles;
+  std::string outputFile;
+  bool overwrite;
+  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, std::vector<std::string> 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) {}
+
+  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
new file mode 100644
index 0000000..e0826b9
--- /dev/null
+++ b/contrib/pzstd/Pzstd.cpp
@@ -0,0 +1,641 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#include "Pzstd.h"
+#include "SkippableFrame.h"
+#include "utils/FileSystem.h"
+#include "utils/Range.h"
+#include "utils/ScopeGuard.h"
+#include "utils/ThreadPool.h"
+#include "utils/WorkQueue.h"
+
+#include <chrono>
+#include <cstddef>
+#include <cstdio>
+#include <memory>
+#include <string>
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)
+#  include <fcntl.h>    /* _O_BINARY */
+#  include <io.h>       /* _setmode, _isatty */
+#  define SET_BINARY_MODE(file) { if (_setmode(_fileno(file), _O_BINARY) == -1) perror("Cannot set _O_BINARY"); }
+#else
+#  include <unistd.h>   /* isatty */
+#  define SET_BINARY_MODE(file)
+#endif
+
+namespace pzstd {
+
+namespace {
+#ifdef _WIN32
+const std::string nullOutput = "nul";
+#else
+const std::string nullOutput = "/dev/null";
+#endif
+}
+
+using std::size_t;
+
+static std::uintmax_t fileSizeOrZero(const std::string &file) {
+  if (file == "-") {
+    return 0;
+  }
+  std::error_code ec;
+  auto size = file_size(file, ec);
+  if (ec) {
+    size = 0;
+  }
+  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<std::shared_ptr<BufferWorkQueue>> 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.
+    // This also sets the minimum number of threads to 2, so the algorithm
+    // doesn't deadlock.
+    ThreadPool executor(options.numThreads + 1);
+    if (!options.decompress) {
+      // Add a job that reads the input and starts all the compression jobs
+      executor.add(
+          [&errorHolder, &outs, &executor, inputFd, inputSize, &options,
+                                                               &bytesRead] {
+            bytesRead = asyncCompressChunks(
+                errorHolder,
+                outs,
+                executor,
+                inputFd,
+                inputSize,
+                options.numThreads,
+                options.determineParameters());
+          });
+      // Start writing
+      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, &bytesRead] {
+        bytesRead = asyncDecompressFrames(errorHolder, outs, executor, inputFd);
+      });
+      // Start writing
+      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<double>(bytesWritten) /
+                     static_cast<double>(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};
+}
+
+/**
+ * Advance `buffer` and `inBuffer` by the amount of data read, as indicated by
+ * `inBuffer.pos`.
+ */
+void advance(Buffer& buffer, ZSTD_inBuffer& inBuffer) {
+  auto pos = inBuffer.pos;
+  inBuffer.src = static_cast<const unsigned char*>(inBuffer.src) + pos;
+  inBuffer.size -= pos;
+  inBuffer.pos = 0;
+  return buffer.advance(pos);
+}
+
+/// Construct a `ZSTD_outBuffer` that points to the data in `buffer`.
+static ZSTD_outBuffer makeZstdOutBuffer(Buffer& buffer) {
+  return ZSTD_outBuffer{buffer.data(), buffer.size(), 0};
+}
+
+/**
+ * Split `buffer` and advance `outBuffer` by the amount of data written, as
+ * indicated by `outBuffer.pos`.
+ */
+Buffer split(Buffer& buffer, ZSTD_outBuffer& outBuffer) {
+  auto pos = outBuffer.pos;
+  outBuffer.dst = static_cast<unsigned char*>(outBuffer.dst) + pos;
+  outBuffer.size -= pos;
+  outBuffer.pos = 0;
+  return buffer.splitAt(pos);
+}
+
+/**
+ * Stream chunks of input from `in`, compress it, and stream it out to `out`.
+ *
+ * @param errorHolder Used to report errors and check if an error occured
+ * @param in           Queue that we `pop()` input buffers from
+ * @param out          Queue that we `push()` compressed output buffers to
+ * @param maxInputSize An upper bound on the size of the input
+ * @param parameters   The zstd parameters to use for compression
+ */
+static void compress(
+    ErrorHolder& errorHolder,
+    std::shared_ptr<BufferWorkQueue> in,
+    std::shared_ptr<BufferWorkQueue> out,
+    size_t maxInputSize,
+    ZSTD_parameters parameters) {
+  auto guard = makeScopeGuard([&] { out->finish(); });
+  // Initialize the CCtx
+  std::unique_ptr<ZSTD_CStream, size_t (*)(ZSTD_CStream*)> ctx(
+      ZSTD_createCStream(), ZSTD_freeCStream);
+  if (!errorHolder.check(ctx != nullptr, "Failed to allocate ZSTD_CStream")) {
+    return;
+  }
+  {
+    auto err = ZSTD_initCStream_advanced(ctx.get(), nullptr, 0, parameters, 0);
+    if (!errorHolder.check(!ZSTD_isError(err), ZSTD_getErrorName(err))) {
+      return;
+    }
+  }
+
+  // Allocate space for the result
+  auto outBuffer = Buffer(ZSTD_compressBound(maxInputSize));
+  auto zstdOutBuffer = makeZstdOutBuffer(outBuffer);
+  {
+    Buffer inBuffer;
+    // Read a buffer in from the input queue
+    while (in->pop(inBuffer) && !errorHolder.hasError()) {
+      auto zstdInBuffer = makeZstdInBuffer(inBuffer);
+      // Compress the whole buffer and send it to the output queue
+      while (!inBuffer.empty() && !errorHolder.hasError()) {
+        if (!errorHolder.check(
+                !outBuffer.empty(), "ZSTD_compressBound() was too small")) {
+          return;
+        }
+        // Compress
+        auto err =
+            ZSTD_compressStream(ctx.get(), &zstdOutBuffer, &zstdInBuffer);
+        if (!errorHolder.check(!ZSTD_isError(err), ZSTD_getErrorName(err))) {
+          return;
+        }
+        // Split the compressed data off outBuffer and pass to the output queue
+        out->push(split(outBuffer, zstdOutBuffer));
+        // Forget about the data we already compressed
+        advance(inBuffer, zstdInBuffer);
+      }
+    }
+  }
+  // Write the epilog
+  size_t bytesLeft;
+  do {
+    if (!errorHolder.check(
+            !outBuffer.empty(), "ZSTD_compressBound() was too small")) {
+      return;
+    }
+    bytesLeft = ZSTD_endStream(ctx.get(), &zstdOutBuffer);
+    if (!errorHolder.check(
+            !ZSTD_isError(bytesLeft), ZSTD_getErrorName(bytesLeft))) {
+      return;
+    }
+    out->push(split(outBuffer, zstdOutBuffer));
+  } while (bytesLeft != 0 && !errorHolder.hasError());
+}
+
+/**
+ * Calculates how large each independently compressed frame should be.
+ *
+ * @param size       The size of the source if known, 0 otherwise
+ * @param numThreads The number of threads available to run compression jobs on
+ * @param params     The zstd parameters to be used for compression
+ */
+static size_t calculateStep(
+    std::uintmax_t size,
+    size_t numThreads,
+    const ZSTD_parameters &params) {
+  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 / numThreads;
+    if (newStep != 0 && newStep <= std::numeric_limits<size_t>::max()) {
+      step = std::min(step, static_cast<size_t>(newStep));
+    }
+  }
+  return step;
+}
+
+namespace {
+enum class FileStatus { Continue, Done, Error };
+/// Determines the status of the file descriptor `fd`.
+FileStatus fileStatus(FILE* fd) {
+  if (std::feof(fd)) {
+    return FileStatus::Done;
+  } else if (std::ferror(fd)) {
+    return FileStatus::Error;
+  }
+  return FileStatus::Continue;
+}
+} // anonymous namespace
+
+/**
+ * Reads `size` data in chunks of `chunkSize` and puts it into `queue`.
+ * Will read less if an error or EOF occurs.
+ * 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,
+         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) {
+      return status;
+    }
+  }
+  return FileStatus::Continue;
+}
+
+std::uint64_t asyncCompressChunks(
+    ErrorHolder& errorHolder,
+    WorkQueue<std::shared_ptr<BufferWorkQueue>>& chunks,
+    ThreadPool& executor,
+    FILE* fd,
+    std::uintmax_t size,
+    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.
+  size_t step = calculateStep(size, numThreads, params);
+  auto status = FileStatus::Continue;
+  while (status == FileStatus::Continue && !errorHolder.hasError()) {
+    // Make a new input queue that we will put the chunk's input data into.
+    auto in = std::make_shared<BufferWorkQueue>();
+    auto inGuard = makeScopeGuard([&] { in->finish(); });
+    // Make a new output queue that compress will put the compressed data into.
+    auto out = std::make_shared<BufferWorkQueue>();
+    // Start compression in the thread pool
+    executor.add([&errorHolder, in, out, step, params] {
+      return compress(
+          errorHolder, std::move(in), std::move(out), step, params);
+    });
+    // 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, &bytesRead);
+  }
+  errorHolder.check(status != FileStatus::Error, "Error reading input");
+  return bytesRead;
+}
+
+/**
+ * Decompress a frame, whose data is streamed into `in`, and stream the output
+ * to `out`.
+ *
+ * @param errorHolder Used to report errors and check if an error occured
+ * @param in           Queue that we `pop()` input buffers from. It contains
+ *                      exactly one compressed frame.
+ * @param out          Queue that we `push()` decompressed output buffers to
+ */
+static void decompress(
+    ErrorHolder& errorHolder,
+    std::shared_ptr<BufferWorkQueue> in,
+    std::shared_ptr<BufferWorkQueue> out) {
+  auto guard = makeScopeGuard([&] { out->finish(); });
+  // Initialize the DCtx
+  std::unique_ptr<ZSTD_DStream, size_t (*)(ZSTD_DStream*)> ctx(
+      ZSTD_createDStream(), ZSTD_freeDStream);
+  if (!errorHolder.check(ctx != nullptr, "Failed to allocate ZSTD_DStream")) {
+    return;
+  }
+  {
+    auto err = ZSTD_initDStream(ctx.get());
+    if (!errorHolder.check(!ZSTD_isError(err), ZSTD_getErrorName(err))) {
+      return;
+    }
+  }
+
+  const size_t outSize = ZSTD_DStreamOutSize();
+  Buffer inBuffer;
+  size_t returnCode = 0;
+  // Read a buffer in from the input queue
+  while (in->pop(inBuffer) && !errorHolder.hasError()) {
+    auto zstdInBuffer = makeZstdInBuffer(inBuffer);
+    // Decompress the whole buffer and send it to the output queue
+    while (!inBuffer.empty() && !errorHolder.hasError()) {
+      // Allocate a buffer with at least outSize bytes.
+      Buffer outBuffer(outSize);
+      auto zstdOutBuffer = makeZstdOutBuffer(outBuffer);
+      // Decompress
+      returnCode =
+          ZSTD_decompressStream(ctx.get(), &zstdOutBuffer, &zstdInBuffer);
+      if (!errorHolder.check(
+              !ZSTD_isError(returnCode), ZSTD_getErrorName(returnCode))) {
+        return;
+      }
+      // Pass the buffer with the decompressed data to the output queue
+      out->push(split(outBuffer, zstdOutBuffer));
+      // Advance past the input we already read
+      advance(inBuffer, zstdInBuffer);
+      if (returnCode == 0) {
+        // The frame is over, prepare to (maybe) start a new frame
+        ZSTD_initDStream(ctx.get());
+      }
+    }
+  }
+  if (!errorHolder.check(returnCode <= 1, "Incomplete block")) {
+    return;
+  }
+  // We've given ZSTD_decompressStream all of our data, but there may still
+  // be data to read.
+  while (returnCode == 1) {
+    // Allocate a buffer with at least outSize bytes.
+    Buffer outBuffer(outSize);
+    auto zstdOutBuffer = makeZstdOutBuffer(outBuffer);
+    // Pass in no input.
+    ZSTD_inBuffer zstdInBuffer{nullptr, 0, 0};
+    // Decompress
+    returnCode =
+        ZSTD_decompressStream(ctx.get(), &zstdOutBuffer, &zstdInBuffer);
+    if (!errorHolder.check(
+            !ZSTD_isError(returnCode), ZSTD_getErrorName(returnCode))) {
+      return;
+    }
+    // Pass the buffer with the decompressed data to the output queue
+    out->push(split(outBuffer, zstdOutBuffer));
+  }
+}
+
+std::uint64_t asyncDecompressFrames(
+    ErrorHolder& errorHolder,
+    WorkQueue<std::shared_ptr<BufferWorkQueue>>& 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.
+  // Otherwise, we will decompress using only one decompression task.
+  const size_t chunkSize = ZSTD_DStreamInSize();
+  auto status = FileStatus::Continue;
+  while (status == FileStatus::Continue && !errorHolder.hasError()) {
+    // Make a new input queue that we will put the frames's bytes into.
+    auto in = std::make_shared<BufferWorkQueue>();
+    auto inGuard = makeScopeGuard([&] { in->finish(); });
+    // Make a output queue that decompress will put the decompressed data into
+    auto out = std::make_shared<BufferWorkQueue>();
+
+    size_t frameSize;
+    {
+      // Calculate the size of the next frame.
+      // 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;
+      }
+      buffer.subtract(buffer.size() - bytesRead);
+      frameSize = SkippableFrame::tryRead(buffer.range());
+      in->push(std::move(buffer));
+    }
+    if (frameSize == 0) {
+      // We hit a non SkippableFrame, so this will be the last job.
+      // Make sure that we don't use too much memory
+      in->setMaxSize(64);
+      out->setMaxSize(64);
+    }
+    // Start decompression in the thread pool
+    executor.add([&errorHolder, in, out] {
+      return decompress(errorHolder, std::move(in), std::move(out));
+    });
+    // Pass the output queue to the writer thread
+    frames.push(std::move(out));
+    if (frameSize == 0) {
+      // 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, &totalBytesRead);
+      }
+      break;
+    }
+    // Fill the input queue for the decompression job we just started
+    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.
+static bool writeData(ByteRange data, FILE* fd) {
+  while (!data.empty()) {
+    data.advance(std::fwrite(data.begin(), 1, data.size(), fd));
+    if (std::ferror(fd)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+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<std::uint32_t>(bytesWritten >> 20));
+  }
+}
+
+std::uint64_t writeFile(
+    ErrorHolder& errorHolder,
+    WorkQueue<std::shared_ptr<BufferWorkQueue>>& outs,
+    FILE* outputFd,
+    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<BufferWorkQueue> out;
+  // Grab the output queue for each decompression job (in order).
+  while (outs.pop(out) && !errorHolder.hasError()) {
+    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.
+      // Wait for the compressed size to be available and write skippable frame
+      SkippableFrame frame(out->size());
+      if (!writeData(frame.data(), outputFd)) {
+        errorHolder.setError("Failed to write output");
+        return bytesWritten;
+      }
+      bytesWritten += frame.kSize;
+    }
+    // For each chunk of the frame: Pop it from the queue and write it
+    Buffer buffer;
+    while (out->pop(buffer) && !errorHolder.hasError()) {
+      if (!writeData(buffer.range(), outputFd)) {
+        errorHolder.setError("Failed to write output");
+        return bytesWritten;
+      }
+      bytesWritten += buffer.size();
+      updateWritten(verbosity, bytesWritten);
+    }
+  }
+  return bytesWritten;
+}
+}
diff --git a/contrib/pzstd/Pzstd.h b/contrib/pzstd/Pzstd.h
new file mode 100644
index 0000000..fe44ccf
--- /dev/null
+++ b/contrib/pzstd/Pzstd.h
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#pragma once
+
+#include "ErrorHolder.h"
+#include "Options.h"
+#include "utils/Buffer.h"
+#include "utils/Range.h"
+#include "utils/ThreadPool.h"
+#include "utils/WorkQueue.h"
+#define ZSTD_STATIC_LINKING_ONLY
+#include "zstd.h"
+#undef ZSTD_STATIC_LINKING_ONLY
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+
+namespace pzstd {
+/**
+ * Runs pzstd with `options` and returns the number of bytes written.
+ * An error occurred if `errorHandler.hasError()`.
+ *
+ * @param options      The pzstd options to use for (de)compression
+ * @returns            0 upon success and non-zero on failure.
+ */
+int pzstdMain(const Options& options);
+
+/**
+ * Streams input from `fd`, breaks input up into chunks, and compresses each
+ * chunk independently.  Output of each chunk gets streamed to a queue, and
+ * the output queues get put into `chunks` in order.
+ *
+ * @param errorHolder  Used to report errors and coordinate early shutdown
+ * @param chunks       Each compression jobs output queue gets `pushed()` here
+ *                      as soon as it is available
+ * @param executor     The thread pool to run compression jobs in
+ * @param fd           The input file descriptor
+ * @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
+ */
+std::uint64_t asyncCompressChunks(
+    ErrorHolder& errorHolder,
+    WorkQueue<std::shared_ptr<BufferWorkQueue>>& chunks,
+    ThreadPool& executor,
+    FILE* fd,
+    std::uintmax_t size,
+    std::size_t numThreads,
+    ZSTD_parameters parameters);
+
+/**
+ * Streams input from `fd`.  If pzstd headers are available it breaks the input
+ * up into independent frames.  It sends each frame to an independent
+ * decompression job.  Output of each frame gets streamed to a queue, and
+ * the output queues get put into `frames` in order.
+ *
+ * @param errorHolder  Used to report errors and coordinate early shutdown
+ * @param frames       Each decompression jobs output queue gets `pushed()` here
+ *                      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
+ */
+std::uint64_t asyncDecompressFrames(
+    ErrorHolder& errorHolder,
+    WorkQueue<std::shared_ptr<BufferWorkQueue>>& frames,
+    ThreadPool& executor,
+    FILE* fd);
+
+/**
+ * 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 decompress   Are we decompressing?
+ * @param verbosity    The verbosity level to log at
+ * @returns            The number of bytes written
+ */
+std::uint64_t writeFile(
+    ErrorHolder& errorHolder,
+    WorkQueue<std::shared_ptr<BufferWorkQueue>>& outs,
+    FILE* outputFd,
+    bool decompress,
+    int verbosity);
+}
diff --git a/contrib/pzstd/README.md b/contrib/pzstd/README.md
new file mode 100644
index 0000000..05ceb55
--- /dev/null
+++ b/contrib/pzstd/README.md
@@ -0,0 +1,55 @@
+# Parallel Zstandard (PZstandard)
+
+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.
+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 -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 -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).
+
+Compression Speed vs Ratio with 4 Threads | Decompression Speed with 4 Threads
+------------------------------------------|-----------------------------------
+![Compression Speed vs Ratio](images/Cspeed.png "Compression Speed vs Ratio") | ![Decompression Speed](images/Dspeed.png "Decompression Speed")
+
+The test procedure was to run each of the following commands 2 times for each compression level, and take the minimum time.
+
+    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
+
+PZstandard was tested using compression levels 1-19, and Pigz was tested using compression levels 1-9.
+Pigz cannot do parallel decompression, it simply does each of reading, decompression, and writing on separate threads.
+
+## Tests
+
+Tests require that you have [gtest](https://github.com/google/googletest) installed.
+Modify `GTEST_INC` and `GTEST_LIB` in `test/Makefile` and `utils/test/Makefile` to work for your install of gtest.
+Then run `make test` in the `contrib/pzstd` directory.
diff --git a/contrib/pzstd/SkippableFrame.cpp b/contrib/pzstd/SkippableFrame.cpp
new file mode 100644
index 0000000..5dc95e5
--- /dev/null
+++ b/contrib/pzstd/SkippableFrame.cpp
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#include "SkippableFrame.h"
+#include "mem.h"
+#include "utils/Range.h"
+
+#include <cstdio>
+
+using namespace pzstd;
+
+SkippableFrame::SkippableFrame(std::uint32_t size) : frameSize_(size) {
+  MEM_writeLE32(data_.data(), kSkippableFrameMagicNumber);
+  MEM_writeLE32(data_.data() + 4, kFrameContentsSize);
+  MEM_writeLE32(data_.data() + 8, frameSize_);
+}
+
+/* static */ std::size_t SkippableFrame::tryRead(ByteRange bytes) {
+  if (bytes.size() < SkippableFrame::kSize ||
+      MEM_readLE32(bytes.begin()) != kSkippableFrameMagicNumber ||
+      MEM_readLE32(bytes.begin() + 4) != kFrameContentsSize) {
+    return 0;
+  }
+  return MEM_readLE32(bytes.begin() + 8);
+}
diff --git a/contrib/pzstd/SkippableFrame.h b/contrib/pzstd/SkippableFrame.h
new file mode 100644
index 0000000..9dc95c1
--- /dev/null
+++ b/contrib/pzstd/SkippableFrame.h
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#pragma once
+
+#include "utils/Range.h"
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+
+namespace pzstd {
+/**
+ * We put a skippable frame before each frame.
+ * It contains a skippable frame magic number, the size of the skippable frame,
+ * and the size of the next frame.
+ * Each skippable frame is exactly 12 bytes in little endian format.
+ * The first 8 bytes are for compatibility with the ZSTD format.
+ * If we have N threads, the output will look like
+ *
+ * [0x184D2A50|4|size1] [frame1 of size size1]
+ * [0x184D2A50|4|size2] [frame2 of size size2]
+ * ...
+ * [0x184D2A50|4|sizeN] [frameN of size sizeN]
+ *
+ * Each sizeX is 4 bytes.
+ *
+ * These skippable frames should allow us to skip through the compressed file
+ * and only load at most N pages.
+ */
+class SkippableFrame {
+ public:
+  static constexpr std::size_t kSize = 12;
+
+ private:
+  std::uint32_t frameSize_;
+  std::array<std::uint8_t, kSize> data_;
+  static constexpr std::uint32_t kSkippableFrameMagicNumber = 0x184D2A50;
+  // Could be improved if the size fits in less bytes
+  static constexpr std::uint32_t kFrameContentsSize = kSize - 8;
+
+ public:
+   // Write the skippable frame to data_ in LE format.
+  explicit SkippableFrame(std::uint32_t size);
+
+  // Read the skippable frame from bytes in LE format.
+  static std::size_t tryRead(ByteRange bytes);
+
+  ByteRange data() const {
+    return {data_.data(), data_.size()};
+  }
+
+  // Size of the next frame.
+  std::size_t frameSize() const {
+    return frameSize_;
+  }
+};
+}
diff --git a/contrib/pzstd/images/Cspeed.png b/contrib/pzstd/images/Cspeed.png
new file mode 100644
index 0000000..aca4f66
Binary files /dev/null and b/contrib/pzstd/images/Cspeed.png differ
diff --git a/contrib/pzstd/images/Dspeed.png b/contrib/pzstd/images/Dspeed.png
new file mode 100644
index 0000000..e48881b
Binary files /dev/null and b/contrib/pzstd/images/Dspeed.png differ
diff --git a/contrib/pzstd/main.cpp b/contrib/pzstd/main.cpp
new file mode 100644
index 0000000..279cbfb
--- /dev/null
+++ b/contrib/pzstd/main.cpp
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#include "ErrorHolder.h"
+#include "Options.h"
+#include "Pzstd.h"
+#include "utils/FileSystem.h"
+#include "utils/Range.h"
+#include "utils/ScopeGuard.h"
+#include "utils/ThreadPool.h"
+#include "utils/WorkQueue.h"
+
+using namespace pzstd;
+
+int main(int argc, const char** argv) {
+  Options options;
+  switch (options.parse(argc, argv)) {
+  case Options::Status::Failure:
+    return 1;
+  case Options::Status::Message:
+    return 0;
+  default:
+    break;
+  }
+
+  return pzstdMain(options);
+}
diff --git a/contrib/pzstd/test/Makefile b/contrib/pzstd/test/Makefile
new file mode 100644
index 0000000..4f6ba99
--- /dev/null
+++ b/contrib/pzstd/test/Makefile
@@ -0,0 +1,48 @@
+# ##########################################################################
+# Copyright (c) 2016-present, 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.
+# ##########################################################################
+
+# Define *.exe as extension for Windows systems
+ifneq (,$(filter Windows%,$(OS)))
+EXT =.exe
+else
+EXT =
+endif
+
+PZSTDDIR = ..
+PROGDIR = ../../../programs
+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
+GTEST_FLAGS = $(GTEST_INC) $(GTEST_LIB)
+CPPFLAGS = -I$(PZSTDDIR) -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(PROGDIR) -I.
+
+CXXFLAGS  ?= -O3
+CXXFLAGS  += -std=c++11 -Wno-deprecated-declarations
+CXXFLAGS  += $(MOREFLAGS)
+FLAGS    = $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS)
+
+datagen.o: $(PROGDIR)/datagen.*
+	$(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) $(GTEST_FLAGS) -lgtest -lgtest_main -lpthread
+
+.PHONY: test clean
+
+test: OptionsTest PzstdTest
+	@./OptionsTest$(EXT)
+	@./PzstdTest$(EXT)
+
+roundtrip: RoundTripTest
+	@./RoundTripTest$(EXT)
+
+clean:
+	@rm -f datagen.o OptionsTest PzstdTest RoundTripTest
diff --git a/contrib/pzstd/test/OptionsTest.cpp b/contrib/pzstd/test/OptionsTest.cpp
new file mode 100644
index 0000000..e7d4b2b
--- /dev/null
+++ b/contrib/pzstd/test/OptionsTest.cpp
@@ -0,0 +1,542 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#include "Options.h"
+
+#include <array>
+#include <gtest/gtest.h>
+
+using namespace pzstd;
+
+namespace pzstd {
+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.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<int>(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 <typename... Args>
+std::array<const char *, sizeof...(Args) + 1> makeArray(Args... args) {
+  return {{nullptr, args...}};
+}
+
+TEST(Options, ValidInputs) {
+  {
+    Options options;
+    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;
+    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;
+    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;
+    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;
+    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;
+    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;
+    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;
+    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;
+    auto args = makeArray("x", "-p", "1");
+    EXPECT_SUCCESS(options.parse(args.size(), args.data()));
+  }
+  {
+    Options options;
+    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;
+    auto args = makeArray("x", "-dfo", "-");
+    EXPECT_SUCCESS(options.parse(args.size(), args.data()));
+  }
+  {
+    Options options;
+    auto args = makeArray("x", "-p", "0", "-fo", "-");
+    EXPECT_FAILURE(options.parse(args.size(), args.data()));
+  }
+  {
+    Options options;
+    auto args = makeArray("-f", "-p", "-o", "-");
+    EXPECT_FAILURE(options.parse(args.size(), args.data()));
+  }
+}
+
+TEST(Options, BadCompressionLevel) {
+  {
+    Options options;
+    auto args = makeArray("x", "-20");
+    EXPECT_FAILURE(options.parse(args.size(), args.data()));
+  }
+  {
+    Options options;
+    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;
+    auto args = makeArray("x", "-x");
+    EXPECT_FAILURE(options.parse(args.size(), args.data()));
+  }
+}
+
+TEST(Options, BadOutputFile) {
+  {
+    Options options;
+    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;
+    auto args = makeArray("-h");
+    EXPECT_MESSAGE(options.parse(args.size(), args.data()));
+  }
+  {
+    Options options;
+    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
new file mode 100644
index 0000000..c85f73a
--- /dev/null
+++ b/contrib/pzstd/test/PzstdTest.cpp
@@ -0,0 +1,155 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#include "Pzstd.h"
+extern "C" {
+#include "datagen.h"
+}
+#include "test/RoundTrip.h"
+#include "utils/ScopeGuard.h"
+
+#include <cstddef>
+#include <cstdio>
+#include <gtest/gtest.h>
+#include <memory>
+#include <random>
+
+using namespace std;
+using namespace pzstd;
+
+TEST(Pzstd, SmallSizes) {
+  unsigned seed = std::random_device{}();
+  std::fprintf(stderr, "Pzstd.SmallSizes seed: %u\n", seed);
+  std::mt19937 gen(seed);
+
+  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[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);
+      std::fclose(fd);
+      ASSERT_EQ(written, len);
+    }
+    for (unsigned headers = 0; headers <= 1; ++headers) {
+      for (unsigned numThreads = 1; numThreads <= 2; ++numThreads) {
+        for (unsigned level = 1; level <= 4; level *= 4) {
+          auto errorGuard = makeScopeGuard([&] {
+            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.overwrite = true;
+          options.inputFiles = {inputFile};
+          options.numThreads = numThreads;
+          options.compressionLevel = level;
+          options.verbosity = 1;
+          ASSERT_TRUE(roundTrip(options));
+          errorGuard.dismiss();
+        }
+      }
+    }
+  }
+}
+
+TEST(Pzstd, LargeSizes) {
+  unsigned seed = std::random_device{}();
+  std::fprintf(stderr, "Pzstd.LargeSizes seed: %u\n", seed);
+  std::mt19937 gen(seed);
+
+  for (unsigned len = 1 << 20; len <= (1 << 24); len *= 2) {
+    std::string inputFile = std::tmpnam(nullptr);
+    auto guard = makeScopeGuard([&] { std::remove(inputFile.c_str()); });
+    {
+      std::unique_ptr<uint8_t[]> buf(new uint8_t[len]);
+      RDG_genBuffer(buf.get(), len, 0.5, 0.0, gen());
+      auto fd = std::fopen(inputFile.c_str(), "wb");
+      auto written = std::fwrite(buf.get(), 1, len, fd);
+      std::fclose(fd);
+      ASSERT_EQ(written, len);
+    }
+    for (unsigned headers = 0; headers <= 1; ++headers) {
+      for (unsigned numThreads = 1; numThreads <= 16; numThreads *= 4) {
+        for (unsigned level = 1; level <= 4; level *= 2) {
+          auto errorGuard = makeScopeGuard([&] {
+            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.overwrite = true;
+          options.inputFiles = {inputFile};
+          options.numThreads = std::min(numThreads, options.numThreads);
+          options.compressionLevel = level;
+          options.verbosity = 1;
+          ASSERT_TRUE(roundTrip(options));
+          errorGuard.dismiss();
+        }
+      }
+    }
+  }
+}
+
+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<uint8_t[]> 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()); });
+  {
+    std::unique_ptr<uint8_t[]> buf(new uint8_t[10000]);
+    std::memset(buf.get(), 'a', 10000);
+    auto fd = std::fopen(inputFile.c_str(), "wb");
+    auto written = std::fwrite(buf.get(), 1, 10000, fd);
+    std::fclose(fd);
+    ASSERT_EQ(written, 10000);
+  }
+  Options options;
+  options.overwrite = true;
+  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
new file mode 100644
index 0000000..8b90884
--- /dev/null
+++ b/contrib/pzstd/test/RoundTrip.h
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#pragma once
+
+#include "Options.h"
+#include "Pzstd.h"
+#include "utils/ScopeGuard.h"
+
+#include <cstdio>
+#include <string>
+#include <cstdint>
+#include <memory>
+
+namespace pzstd {
+
+inline bool check(std::string source, std::string decompressed) {
+  std::unique_ptr<std::uint8_t[]> sBuf(new std::uint8_t[1024]);
+  std::unique_ptr<std::uint8_t[]> dBuf(new std::uint8_t[1024]);
+
+  auto sFd = std::fopen(source.c_str(), "rb");
+  auto dFd = std::fopen(decompressed.c_str(), "rb");
+  auto guard = makeScopeGuard([&] {
+    std::fclose(sFd);
+    std::fclose(dFd);
+  });
+
+  size_t sRead, dRead;
+
+  do {
+    sRead = std::fread(sBuf.get(), 1, 1024, sFd);
+    dRead = std::fread(dBuf.get(), 1, 1024, dFd);
+    if (std::ferror(sFd) || std::ferror(dFd)) {
+      return false;
+    }
+    if (sRead != dRead) {
+      return false;
+    }
+
+    for (size_t i = 0; i < sRead; ++i) {
+      if (sBuf.get()[i] != dBuf.get()[i]) {
+        return false;
+      }
+    }
+  } while (sRead == 1024);
+  if (!std::feof(sFd) || !std::feof(dFd)) {
+    return false;
+  }
+  return true;
+}
+
+inline bool roundTrip(Options& options) {
+  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([&] {
+    std::remove(compressedFile.c_str());
+    std::remove(decompressedFile.c_str());
+  });
+
+  {
+    options.outputFile = compressedFile;
+    options.decompress = false;
+    if (pzstdMain(options) != 0) {
+      return false;
+    }
+  }
+  {
+    options.decompress = true;
+    options.inputFiles.front() = compressedFile;
+    options.outputFile = decompressedFile;
+    if (pzstdMain(options) != 0) {
+      return false;
+    }
+  }
+  return check(source, decompressedFile);
+}
+}
diff --git a/contrib/pzstd/test/RoundTripTest.cpp b/contrib/pzstd/test/RoundTripTest.cpp
new file mode 100644
index 0000000..ed2ea77
--- /dev/null
+++ b/contrib/pzstd/test/RoundTripTest.cpp
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+extern "C" {
+#include "datagen.h"
+}
+#include "Options.h"
+#include "test/RoundTrip.h"
+#include "utils/ScopeGuard.h"
+
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <memory>
+#include <random>
+
+using namespace std;
+using namespace pzstd;
+
+namespace {
+string
+writeData(size_t size, double matchProba, double litProba, unsigned seed) {
+  std::unique_ptr<uint8_t[]> buf(new uint8_t[size]);
+  RDG_genBuffer(buf.get(), size, matchProba, litProba, seed);
+  string file = tmpnam(nullptr);
+  auto fd = std::fopen(file.c_str(), "wb");
+  auto guard = makeScopeGuard([&] { std::fclose(fd); });
+  auto bytesWritten = std::fwrite(buf.get(), 1, size, fd);
+  if (bytesWritten != size) {
+    std::abort();
+  }
+  return file;
+}
+
+template <typename Generator>
+string generateInputFile(Generator& gen) {
+  // Use inputs ranging from 1 Byte to 2^16 Bytes
+  std::uniform_int_distribution<size_t> size{1, 1 << 16};
+  std::uniform_real_distribution<> prob{0, 1};
+  return writeData(size(gen), prob(gen), prob(gen), gen());
+}
+
+template <typename Generator>
+Options generateOptions(Generator& gen, const string& inputFile) {
+  Options options;
+  options.inputFiles = {inputFile};
+  options.overwrite = true;
+
+  std::uniform_int_distribution<unsigned> numThreads{1, 32};
+  std::uniform_int_distribution<unsigned> compressionLevel{1, 10};
+
+  options.numThreads = numThreads(gen);
+  options.compressionLevel = compressionLevel(gen);
+
+  return options;
+}
+}
+
+int main() {
+  std::mt19937 gen(std::random_device{}());
+
+  auto newlineGuard = makeScopeGuard([] { std::fprintf(stderr, "\n"); });
+  for (unsigned i = 0; i < 10000; ++i) {
+    if (i % 100 == 0) {
+      std::fprintf(stderr, "Progress: %u%%\r", i / 100);
+    }
+    auto inputFile = generateInputFile(gen);
+    auto inputGuard = makeScopeGuard([&] { std::remove(inputFile.c_str()); });
+    for (unsigned i = 0; i < 10; ++i) {
+      auto options = generateOptions(gen, inputFile);
+      if (!roundTrip(options)) {
+        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, "file: %s\n", inputFile.c_str());
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
diff --git a/contrib/pzstd/utils/Buffer.h b/contrib/pzstd/utils/Buffer.h
new file mode 100644
index 0000000..ab25bac
--- /dev/null
+++ b/contrib/pzstd/utils/Buffer.h
@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#pragma once
+
+#include "utils/Range.h"
+
+#include <array>
+#include <cstddef>
+#include <memory>
+
+namespace pzstd {
+
+/**
+ * A `Buffer` has a pointer to a shared buffer, and a range of the buffer that
+ * it owns.
+ * The idea is that you can allocate one buffer, and write chunks into it
+ * and break off those chunks.
+ * The underlying buffer is reference counted, and will be destroyed when all
+ * `Buffer`s that reference it are destroyed.
+ */
+class Buffer {
+  std::shared_ptr<unsigned char> buffer_;
+  MutableByteRange range_;
+
+  static void delete_buffer(unsigned char* buffer) {
+    delete[] buffer;
+  }
+
+ public:
+  /// Construct an empty buffer that owns no data.
+  explicit Buffer() {}
+
+  /// Construct a `Buffer` that owns a new underlying buffer of size `size`.
+  explicit Buffer(std::size_t size)
+      : buffer_(new unsigned char[size], delete_buffer),
+        range_(buffer_.get(), buffer_.get() + size) {}
+
+  explicit Buffer(std::shared_ptr<unsigned char> buffer, MutableByteRange data)
+      : buffer_(buffer), range_(data) {}
+
+  Buffer(Buffer&&) = default;
+  Buffer& operator=(Buffer&&) & = default;
+
+  /**
+   * Splits the data into two pieces: [begin, begin + n), [begin + n, end).
+   * Their data both points into the same underlying buffer.
+   * Modifies the original `Buffer` to point to only [begin + n, end).
+   *
+   * @param n  The offset to split at.
+   * @returns  A buffer that owns the data [begin, begin + n).
+   */
+  Buffer splitAt(std::size_t n) {
+    auto firstPiece = range_.subpiece(0, n);
+    range_.advance(n);
+    return Buffer(buffer_, firstPiece);
+  }
+
+  /// Modifies the buffer to point to the range [begin + n, end).
+  void advance(std::size_t n) {
+    range_.advance(n);
+  }
+
+  /// Modifies the buffer to point to the range [begin, end - n).
+  void subtract(std::size_t n) {
+    range_.subtract(n);
+  }
+
+  /// Returns a read only `Range` pointing to the `Buffer`s data.
+  ByteRange range() const {
+    return range_;
+  }
+  /// Returns a mutable `Range` pointing to the `Buffer`s data.
+  MutableByteRange range() {
+    return range_;
+  }
+
+  const unsigned char* data() const {
+    return range_.data();
+  }
+
+  unsigned char* data() {
+    return range_.data();
+  }
+
+  std::size_t size() const {
+    return range_.size();
+  }
+
+  bool empty() const {
+    return range_.empty();
+  }
+};
+}
diff --git a/contrib/pzstd/utils/FileSystem.h b/contrib/pzstd/utils/FileSystem.h
new file mode 100644
index 0000000..7d59704
--- /dev/null
+++ b/contrib/pzstd/utils/FileSystem.h
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#pragma once
+
+#include "utils/Range.h"
+
+#include <sys/stat.h>
+#include <cerrno>
+#include <cstdint>
+#include <system_error>
+
+// A small subset of `std::filesystem`.
+// `std::filesystem` should be a drop in replacement.
+// See http://en.cppreference.com/w/cpp/filesystem for documentation.
+
+namespace pzstd {
+
+// using file_status = ... causes gcc to emit a false positive warning
+#if defined(_MSC_VER)
+typedef struct ::_stat64 file_status;
+#else
+typedef struct ::stat file_status;
+#endif
+
+/// http://en.cppreference.com/w/cpp/filesystem/status
+inline file_status status(StringPiece path, std::error_code& ec) noexcept {
+  file_status status;
+#if defined(_MSC_VER)
+  const auto error = ::_stat64(path.data(), &status);
+#else
+  const auto error = ::stat(path.data(), &status);
+#endif
+  if (error) {
+    ec.assign(errno, std::generic_category());
+  } else {
+    ec.clear();
+  }
+  return status;
+}
+
+/// http://en.cppreference.com/w/cpp/filesystem/is_regular_file
+inline bool is_regular_file(file_status status) noexcept {
+#if defined(S_ISREG)
+  return S_ISREG(status.st_mode);
+#elif !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
+  return (status.st_mode & S_IFMT) == S_IFREG;
+#else
+  static_assert(false, "No POSIX stat() support.");
+#endif
+}
+
+/// http://en.cppreference.com/w/cpp/filesystem/is_regular_file
+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,
+    std::error_code& ec) noexcept {
+  auto stat = status(path, ec);
+  if (ec) {
+    return -1;
+  }
+  if (!is_regular_file(stat)) {
+    ec.assign(ENOTSUP, std::generic_category());
+    return -1;
+  }
+  ec.clear();
+  return stat.st_size;
+}
+}
diff --git a/contrib/pzstd/utils/Likely.h b/contrib/pzstd/utils/Likely.h
new file mode 100644
index 0000000..c8ea102
--- /dev/null
+++ b/contrib/pzstd/utils/Likely.h
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+ 
+/**
+ * Compiler hints to indicate the fast path of an "if" branch: whether
+ * the if condition is likely to be true or false.
+ *
+ * @author Tudor Bosman (tudorb at fb.com)
+ */
+
+#pragma once
+
+#undef LIKELY
+#undef UNLIKELY
+
+#if defined(__GNUC__) && __GNUC__ >= 4
+#define LIKELY(x) (__builtin_expect((x), 1))
+#define UNLIKELY(x) (__builtin_expect((x), 0))
+#else
+#define LIKELY(x) (x)
+#define UNLIKELY(x) (x)
+#endif
diff --git a/contrib/pzstd/utils/Range.h b/contrib/pzstd/utils/Range.h
new file mode 100644
index 0000000..111e98f
--- /dev/null
+++ b/contrib/pzstd/utils/Range.h
@@ -0,0 +1,131 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+ 
+/**
+ * A subset of `folly/Range.h`.
+ * All code copied verbatiam modulo formatting
+ */
+#pragma once
+
+#include "utils/Likely.h"
+
+#include <cstddef>
+#include <cstring>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+
+namespace pzstd {
+
+namespace detail {
+/*
+ *Use IsCharPointer<T>::type to enable const char* or char*.
+ *Use IsCharPointer<T>::const_type to enable only const char*.
+*/
+template <class T>
+struct IsCharPointer {};
+
+template <>
+struct IsCharPointer<char*> {
+  typedef int type;
+};
+
+template <>
+struct IsCharPointer<const char*> {
+  typedef int const_type;
+  typedef int type;
+};
+
+} // namespace detail
+
+template <typename Iter>
+class Range {
+  Iter b_;
+  Iter e_;
+
+ public:
+  using size_type = std::size_t;
+  using iterator = Iter;
+  using const_iterator = Iter;
+  using value_type = typename std::remove_reference<
+      typename std::iterator_traits<Iter>::reference>::type;
+  using reference = typename std::iterator_traits<Iter>::reference;
+
+  constexpr Range() : b_(), e_() {}
+  constexpr Range(Iter begin, Iter end) : b_(begin), e_(end) {}
+
+  constexpr Range(Iter begin, size_type size) : b_(begin), e_(begin + size) {}
+
+  template <class T = Iter, typename detail::IsCharPointer<T>::type = 0>
+  /* implicit */ Range(Iter str) : b_(str), e_(str + std::strlen(str)) {}
+
+  template <class T = Iter, typename detail::IsCharPointer<T>::const_type = 0>
+  /* implicit */ Range(const std::string& str)
+      : b_(str.data()), e_(b_ + str.size()) {}
+
+  // Allow implicit conversion from Range<From> to Range<To> if From is
+  // implicitly convertible to To.
+  template <
+      class OtherIter,
+      typename std::enable_if<
+          (!std::is_same<Iter, OtherIter>::value &&
+           std::is_convertible<OtherIter, Iter>::value),
+          int>::type = 0>
+  constexpr /* implicit */ Range(const Range<OtherIter>& other)
+      : b_(other.begin()), e_(other.end()) {}
+
+  Range(const Range&) = default;
+  Range(Range&&) = default;
+
+  Range& operator=(const Range&) & = default;
+  Range& operator=(Range&&) & = default;
+
+  constexpr size_type size() const {
+    return e_ - b_;
+  }
+  bool empty() const {
+    return b_ == e_;
+  }
+  Iter data() const {
+    return b_;
+  }
+  Iter begin() const {
+    return b_;
+  }
+  Iter end() const {
+    return e_;
+  }
+
+  void advance(size_type n) {
+    if (UNLIKELY(n > size())) {
+      throw std::out_of_range("index out of range");
+    }
+    b_ += n;
+  }
+
+  void subtract(size_type n) {
+    if (UNLIKELY(n > size())) {
+      throw std::out_of_range("index out of range");
+    }
+    e_ -= n;
+  }
+
+  Range subpiece(size_type first, size_type length = std::string::npos) const {
+    if (UNLIKELY(first > size())) {
+      throw std::out_of_range("index out of range");
+    }
+
+    return Range(b_ + first, std::min(length, size() - first));
+  }
+};
+
+using ByteRange = Range<const unsigned char*>;
+using MutableByteRange = Range<unsigned char*>;
+using StringPiece = Range<const char*>;
+}
diff --git a/contrib/pzstd/utils/ScopeGuard.h b/contrib/pzstd/utils/ScopeGuard.h
new file mode 100644
index 0000000..5a333e0
--- /dev/null
+++ b/contrib/pzstd/utils/ScopeGuard.h
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#pragma once
+
+#include <utility>
+
+namespace pzstd {
+
+/**
+ * Dismissable scope guard.
+ * `Function` must be callable and take no parameters.
+ * Unless `dissmiss()` is called, the callable is executed upon destruction of
+ * `ScopeGuard`.
+ *
+ * Example:
+ *
+ *   auto guard = makeScopeGuard([&] { cleanup(); });
+ */
+template <typename Function>
+class ScopeGuard {
+  Function function;
+  bool dismissed;
+
+ public:
+  explicit ScopeGuard(Function&& function)
+      : function(std::move(function)), dismissed(false) {}
+
+  void dismiss() {
+    dismissed = true;
+  }
+
+  ~ScopeGuard() noexcept {
+    if (!dismissed) {
+      function();
+    }
+  }
+};
+
+/// Creates a scope guard from `function`.
+template <typename Function>
+ScopeGuard<Function> makeScopeGuard(Function&& function) {
+  return ScopeGuard<Function>(std::forward<Function>(function));
+}
+}
diff --git a/contrib/pzstd/utils/ThreadPool.h b/contrib/pzstd/utils/ThreadPool.h
new file mode 100644
index 0000000..a1d1fc0
--- /dev/null
+++ b/contrib/pzstd/utils/ThreadPool.h
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#pragma once
+
+#include "utils/WorkQueue.h"
+
+#include <cstddef>
+#include <functional>
+#include <thread>
+#include <vector>
+
+namespace pzstd {
+/// A simple thread pool that pulls tasks off its queue in FIFO order.
+class ThreadPool {
+  std::vector<std::thread> threads_;
+
+  WorkQueue<std::function<void()>> tasks_;
+
+ public:
+  /// Constructs a thread pool with `numThreads` threads.
+  explicit ThreadPool(std::size_t numThreads) {
+    threads_.reserve(numThreads);
+    for (std::size_t i = 0; i < numThreads; ++i) {
+      threads_.emplace_back([&] {
+        std::function<void()> task;
+        while (tasks_.pop(task)) {
+          task();
+        }
+      });
+    }
+  }
+
+  /// Finishes all tasks currently in the queue.
+  ~ThreadPool() {
+    tasks_.finish();
+    for (auto& thread : threads_) {
+      thread.join();
+    }
+  }
+
+  /**
+   * Adds `task` to the queue of tasks to execute. Since `task` is a
+   * `std::function<>`, it cannot be a move only type. So any lambda passed must
+   * not capture move only types (like `std::unique_ptr`).
+   *
+   * @param task  The task to execute.
+   */
+  void add(std::function<void()> task) {
+    tasks_.push(std::move(task));
+  }
+};
+}
diff --git a/contrib/pzstd/utils/WorkQueue.h b/contrib/pzstd/utils/WorkQueue.h
new file mode 100644
index 0000000..5382135
--- /dev/null
+++ b/contrib/pzstd/utils/WorkQueue.h
@@ -0,0 +1,184 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#pragma once
+
+#include "utils/Buffer.h"
+
+#include <atomic>
+#include <cassert>
+#include <cstddef>
+#include <condition_variable>
+#include <cstddef>
+#include <functional>
+#include <mutex>
+#include <queue>
+
+namespace pzstd {
+
+/// Unbounded thread-safe work queue.
+template <typename T>
+class WorkQueue {
+  // Protects all member variable access
+  std::mutex mutex_;
+  std::condition_variable readerCv_;
+  std::condition_variable writerCv_;
+
+  std::queue<T> queue_;
+  bool done_;
+  std::size_t maxSize_;
+
+  // Must have lock to call this function
+  bool full() const {
+    if (maxSize_ == 0) {
+      return false;
+    }
+    return queue_.size() >= maxSize_;
+  }
+
+ public:
+  /**
+   * Constructs an empty work queue with an optional max size.
+   * If `maxSize == 0` the queue size is unbounded.
+   *
+   * @param maxSize The maximum allowed size of the work queue.
+   */
+  WorkQueue(std::size_t maxSize = 0) : done_(false), maxSize_(maxSize) {}
+
+  /**
+   * Push an item onto the work queue.  Notify a single thread that work is
+   * available.  If `finish()` has been called, do nothing and return false.
+   *
+   * @param item  Item to push onto the queue.
+   * @returns     True upon success, false if `finish()` has been called.  An
+   *               item was pushed iff `push()` returns true.
+   */
+  bool push(T item) {
+    {
+      std::unique_lock<std::mutex> lock(mutex_);
+      while (full() && !done_) {
+        writerCv_.wait(lock);
+      }
+      if (done_) {
+        return false;
+      }
+      queue_.push(std::move(item));
+    }
+    readerCv_.notify_one();
+    return true;
+  }
+
+  /**
+   * Attempts to pop an item off the work queue.  It will block until data is
+   * available or `finish()` has been called.
+   *
+   * @param[out] item  If `pop` returns `true`, it contains the popped item.
+   *                    If `pop` returns `false`, it is unmodified.
+   * @returns          True upon success.  False if the queue is empty and
+   *                    `finish()` has been called.
+   */
+  bool pop(T& item) {
+    {
+      std::unique_lock<std::mutex> lock(mutex_);
+      while (queue_.empty() && !done_) {
+        readerCv_.wait(lock);
+      }
+      if (queue_.empty()) {
+        assert(done_);
+        return false;
+      }
+      item = std::move(queue_.front());
+      queue_.pop();
+    }
+    writerCv_.notify_one();
+    return true;
+  }
+
+  /**
+   * Sets the maximum queue size.  If `maxSize == 0` then it is unbounded.
+   *
+   * @param maxSize The new maximum queue size.
+   */
+  void setMaxSize(std::size_t maxSize) {
+    {
+      std::lock_guard<std::mutex> lock(mutex_);
+      maxSize_ = maxSize;
+    }
+    writerCv_.notify_all();
+  }
+
+  /**
+   * Promise that `push()` won't be called again, so once the queue is empty
+   * there will never any more work.
+   */
+  void finish() {
+    {
+      std::lock_guard<std::mutex> lock(mutex_);
+      assert(!done_);
+      done_ = true;
+    }
+    readerCv_.notify_all();
+    writerCv_.notify_all();
+  }
+
+  /// Blocks until `finish()` has been called (but the queue may not be empty).
+  void waitUntilFinished() {
+    std::unique_lock<std::mutex> lock(mutex_);
+    while (!done_) {
+      readerCv_.wait(lock);
+      // If we were woken by a push, we need to wake a thread waiting on pop().
+      if (!done_) {
+        lock.unlock();
+        readerCv_.notify_one();
+        lock.lock();
+      }
+    }
+  }
+};
+
+/// Work queue for `Buffer`s that knows the total number of bytes in the queue.
+class BufferWorkQueue {
+  WorkQueue<Buffer> queue_;
+  std::atomic<std::size_t> size_;
+
+ public:
+  BufferWorkQueue(std::size_t maxSize = 0) : queue_(maxSize), size_(0) {}
+
+  void push(Buffer buffer) {
+    size_.fetch_add(buffer.size());
+    queue_.push(std::move(buffer));
+  }
+
+  bool pop(Buffer& buffer) {
+    bool result = queue_.pop(buffer);
+    if (result) {
+      size_.fetch_sub(buffer.size());
+    }
+    return result;
+  }
+
+  void setMaxSize(std::size_t maxSize) {
+    queue_.setMaxSize(maxSize);
+  }
+
+  void finish() {
+    queue_.finish();
+  }
+
+  /**
+   * Blocks until `finish()` has been called.
+   *
+   * @returns The total number of bytes of all the `Buffer`s currently in the
+   *           queue.
+   */
+  std::size_t size() {
+    queue_.waitUntilFinished();
+    return size_.load();
+  }
+};
+}
diff --git a/contrib/pzstd/utils/test/BufferTest.cpp b/contrib/pzstd/utils/test/BufferTest.cpp
new file mode 100644
index 0000000..66ec961
--- /dev/null
+++ b/contrib/pzstd/utils/test/BufferTest.cpp
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#include "utils/Buffer.h"
+#include "utils/Range.h"
+
+#include <gtest/gtest.h>
+#include <memory>
+
+using namespace pzstd;
+
+namespace {
+void deleter(const unsigned char* buf) {
+  delete[] buf;
+}
+}
+
+TEST(Buffer, Constructors) {
+  Buffer empty;
+  EXPECT_TRUE(empty.empty());
+  EXPECT_EQ(0, empty.size());
+
+  Buffer sized(5);
+  EXPECT_FALSE(sized.empty());
+  EXPECT_EQ(5, sized.size());
+
+  Buffer moved(std::move(sized));
+  EXPECT_FALSE(sized.empty());
+  EXPECT_EQ(5, sized.size());
+
+  Buffer assigned;
+  assigned = std::move(moved);
+  EXPECT_FALSE(sized.empty());
+  EXPECT_EQ(5, sized.size());
+}
+
+TEST(Buffer, BufferManagement) {
+  std::shared_ptr<unsigned char> buf(new unsigned char[10], deleter);
+  {
+    Buffer acquired(buf, MutableByteRange(buf.get(), buf.get() + 10));
+    EXPECT_EQ(2, buf.use_count());
+    Buffer moved(std::move(acquired));
+    EXPECT_EQ(2, buf.use_count());
+    Buffer assigned;
+    assigned = std::move(moved);
+    EXPECT_EQ(2, buf.use_count());
+
+    Buffer split = assigned.splitAt(5);
+    EXPECT_EQ(3, buf.use_count());
+
+    split.advance(1);
+    assigned.subtract(1);
+    EXPECT_EQ(3, buf.use_count());
+  }
+  EXPECT_EQ(1, buf.use_count());
+}
+
+TEST(Buffer, Modifiers) {
+  Buffer buf(10);
+  {
+    unsigned char i = 0;
+    for (auto& byte : buf.range()) {
+      byte = i++;
+    }
+  }
+
+  auto prefix = buf.splitAt(2);
+
+  ASSERT_EQ(2, prefix.size());
+  EXPECT_EQ(0, *prefix.data());
+
+  ASSERT_EQ(8, buf.size());
+  EXPECT_EQ(2, *buf.data());
+
+  buf.advance(2);
+  EXPECT_EQ(4, *buf.data());
+
+  EXPECT_EQ(9, *(buf.range().end() - 1));
+
+  buf.subtract(2);
+  EXPECT_EQ(7, *(buf.range().end() - 1));
+
+  EXPECT_EQ(4, buf.size());
+}
diff --git a/contrib/pzstd/utils/test/Makefile b/contrib/pzstd/utils/test/Makefile
new file mode 100644
index 0000000..b9ea73e
--- /dev/null
+++ b/contrib/pzstd/utils/test/Makefile
@@ -0,0 +1,42 @@
+# ##########################################################################
+# Copyright (c) 2016-present, 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.
+# ##########################################################################
+
+# Define *.exe as extension for Windows systems
+ifneq (,$(filter Windows%,$(OS)))
+EXT =.exe
+else
+EXT =
+endif
+
+PZSTDDIR = ../..
+
+# 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)
+CXXFLAGS  ?= -O3
+CXXFLAGS  += -std=c++11
+CXXFLAGS  += $(MOREFLAGS)
+FLAGS    = $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS)
+
+%: %.cpp
+	$(CXX) $(FLAGS) $^ -o $@$(EXT) -lgtest -lgtest_main -lpthread
+
+.PHONY: test clean
+
+test: BufferTest RangeTest ScopeGuardTest ThreadPoolTest WorkQueueTest
+	@./BufferTest$(EXT)
+	@./RangeTest$(EXT)
+	@./ScopeGuardTest$(EXT)
+	@./ThreadPoolTest$(EXT)
+	@./WorkQueueTest$(EXT)
+
+clean:
+	@rm -f BufferTest RangeTest ScopeGuardTest ThreadPoolTest WorkQueueTest
diff --git a/contrib/pzstd/utils/test/RangeTest.cpp b/contrib/pzstd/utils/test/RangeTest.cpp
new file mode 100644
index 0000000..c761c8a
--- /dev/null
+++ b/contrib/pzstd/utils/test/RangeTest.cpp
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#include "utils/Range.h"
+
+#include <gtest/gtest.h>
+#include <string>
+
+using namespace pzstd;
+
+// Range is directly copied from folly.
+// Just some sanity tests to make sure everything seems to work.
+
+TEST(Range, Constructors) {
+  StringPiece empty;
+  EXPECT_TRUE(empty.empty());
+  EXPECT_EQ(0, empty.size());
+
+  std::string str = "hello";
+  {
+    Range<std::string::const_iterator> piece(str.begin(), str.end());
+    EXPECT_EQ(5, piece.size());
+    EXPECT_EQ('h', *piece.data());
+    EXPECT_EQ('o', *(piece.end() - 1));
+  }
+
+  {
+    StringPiece piece(str.data(), str.size());
+    EXPECT_EQ(5, piece.size());
+    EXPECT_EQ('h', *piece.data());
+    EXPECT_EQ('o', *(piece.end() - 1));
+  }
+
+  {
+    StringPiece piece(str);
+    EXPECT_EQ(5, piece.size());
+    EXPECT_EQ('h', *piece.data());
+    EXPECT_EQ('o', *(piece.end() - 1));
+  }
+
+  {
+    StringPiece piece(str.c_str());
+    EXPECT_EQ(5, piece.size());
+    EXPECT_EQ('h', *piece.data());
+    EXPECT_EQ('o', *(piece.end() - 1));
+  }
+}
+
+TEST(Range, Modifiers) {
+  StringPiece range("hello world");
+  ASSERT_EQ(11, range.size());
+
+  {
+    auto hello = range.subpiece(0, 5);
+    EXPECT_EQ(5, hello.size());
+    EXPECT_EQ('h', *hello.data());
+    EXPECT_EQ('o', *(hello.end() - 1));
+  }
+  {
+    auto hello = range;
+    hello.subtract(6);
+    EXPECT_EQ(5, hello.size());
+    EXPECT_EQ('h', *hello.data());
+    EXPECT_EQ('o', *(hello.end() - 1));
+  }
+  {
+    auto world = range;
+    world.advance(6);
+    EXPECT_EQ(5, world.size());
+    EXPECT_EQ('w', *world.data());
+    EXPECT_EQ('d', *(world.end() - 1));
+  }
+
+  std::string expected = "hello world";
+  EXPECT_EQ(expected, std::string(range.begin(), range.end()));
+  EXPECT_EQ(expected, std::string(range.data(), range.size()));
+}
diff --git a/contrib/pzstd/utils/test/ScopeGuardTest.cpp b/contrib/pzstd/utils/test/ScopeGuardTest.cpp
new file mode 100644
index 0000000..0c4dc03
--- /dev/null
+++ b/contrib/pzstd/utils/test/ScopeGuardTest.cpp
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#include "utils/ScopeGuard.h"
+
+#include <gtest/gtest.h>
+
+using namespace pzstd;
+
+TEST(ScopeGuard, Dismiss) {
+  {
+    auto guard = makeScopeGuard([&] { EXPECT_TRUE(false); });
+    guard.dismiss();
+  }
+}
+
+TEST(ScopeGuard, Executes) {
+  bool executed = false;
+  {
+    auto guard = makeScopeGuard([&] { executed = true; });
+  }
+  EXPECT_TRUE(executed);
+}
diff --git a/contrib/pzstd/utils/test/ThreadPoolTest.cpp b/contrib/pzstd/utils/test/ThreadPoolTest.cpp
new file mode 100644
index 0000000..1d857aa
--- /dev/null
+++ b/contrib/pzstd/utils/test/ThreadPoolTest.cpp
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#include "utils/ThreadPool.h"
+
+#include <gtest/gtest.h>
+#include <atomic>
+#include <thread>
+#include <vector>
+
+using namespace pzstd;
+
+TEST(ThreadPool, Ordering) {
+  std::vector<int> results;
+
+  {
+    ThreadPool executor(1);
+    for (int i = 0; i < 10; ++i) {
+      executor.add([ &results, i ] { results.push_back(i); });
+    }
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    EXPECT_EQ(i, results[i]);
+  }
+}
+
+TEST(ThreadPool, AllJobsFinished) {
+  std::atomic<unsigned> numFinished{0};
+  std::atomic<bool> start{false};
+  {
+    ThreadPool executor(5);
+    for (int i = 0; i < 10; ++i) {
+      executor.add([ &numFinished, &start ] {
+        while (!start.load()) {
+          // spin
+        }
+        ++numFinished;
+      });
+    }
+    start.store(true);
+  }
+  EXPECT_EQ(10, numFinished.load());
+}
+
+TEST(ThreadPool, AddJobWhileJoining) {
+  std::atomic<bool> done{false};
+  {
+    ThreadPool executor(1);
+    executor.add([&executor, &done] {
+      while (!done.load()) {
+        std::this_thread::yield();
+      }
+      // Sleep for a second to be sure that we are joining
+      std::this_thread::sleep_for(std::chrono::seconds(1));
+      executor.add([] {
+        EXPECT_TRUE(false);
+      });
+    });
+    done.store(true);
+  }
+}
diff --git a/contrib/pzstd/utils/test/WorkQueueTest.cpp b/contrib/pzstd/utils/test/WorkQueueTest.cpp
new file mode 100644
index 0000000..ebf375a
--- /dev/null
+++ b/contrib/pzstd/utils/test/WorkQueueTest.cpp
@@ -0,0 +1,262 @@
+/**
+ * Copyright (c) 2016-present, 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.
+ */
+#include "utils/Buffer.h"
+#include "utils/WorkQueue.h"
+
+#include <gtest/gtest.h>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+using namespace pzstd;
+
+namespace {
+struct Popper {
+  WorkQueue<int>* queue;
+  int* results;
+  std::mutex* mutex;
+
+  void operator()() {
+    int result;
+    while (queue->pop(result)) {
+      std::lock_guard<std::mutex> lock(*mutex);
+      results[result] = result;
+    }
+  }
+};
+}
+
+TEST(WorkQueue, SingleThreaded) {
+  WorkQueue<int> queue;
+  int result;
+
+  queue.push(5);
+  EXPECT_TRUE(queue.pop(result));
+  EXPECT_EQ(5, result);
+
+  queue.push(1);
+  queue.push(2);
+  EXPECT_TRUE(queue.pop(result));
+  EXPECT_EQ(1, result);
+  EXPECT_TRUE(queue.pop(result));
+  EXPECT_EQ(2, result);
+
+  queue.push(1);
+  queue.push(2);
+  queue.finish();
+  EXPECT_TRUE(queue.pop(result));
+  EXPECT_EQ(1, result);
+  EXPECT_TRUE(queue.pop(result));
+  EXPECT_EQ(2, result);
+  EXPECT_FALSE(queue.pop(result));
+
+  queue.waitUntilFinished();
+}
+
+TEST(WorkQueue, SPSC) {
+  WorkQueue<int> queue;
+  const int max = 100;
+
+  for (int i = 0; i < 10; ++i) {
+    queue.push(i);
+  }
+
+  std::thread thread([ &queue, max ] {
+    int result;
+    for (int i = 0;; ++i) {
+      if (!queue.pop(result)) {
+        EXPECT_EQ(i, max);
+        break;
+      }
+      EXPECT_EQ(i, result);
+    }
+  });
+
+  std::this_thread::yield();
+  for (int i = 10; i < max; ++i) {
+    queue.push(i);
+  }
+  queue.finish();
+
+  thread.join();
+}
+
+TEST(WorkQueue, SPMC) {
+  WorkQueue<int> queue;
+  std::vector<int> results(50, -1);
+  std::mutex mutex;
+  std::vector<std::thread> threads;
+  for (int i = 0; i < 5; ++i) {
+    threads.emplace_back(Popper{&queue, results.data(), &mutex});
+  }
+
+  for (int i = 0; i < 50; ++i) {
+    queue.push(i);
+  }
+  queue.finish();
+
+  for (auto& thread : threads) {
+    thread.join();
+  }
+
+  for (int i = 0; i < 50; ++i) {
+    EXPECT_EQ(i, results[i]);
+  }
+}
+
+TEST(WorkQueue, MPMC) {
+  WorkQueue<int> queue;
+  std::vector<int> results(100, -1);
+  std::mutex mutex;
+  std::vector<std::thread> popperThreads;
+  for (int i = 0; i < 4; ++i) {
+    popperThreads.emplace_back(Popper{&queue, results.data(), &mutex});
+  }
+
+  std::vector<std::thread> pusherThreads;
+  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) {
+            queue.push(i);
+          }
+        });
+  }
+
+  for (auto& thread : pusherThreads) {
+    thread.join();
+  }
+  queue.finish();
+
+  for (auto& thread : popperThreads) {
+    thread.join();
+  }
+
+  for (int i = 0; i < 100; ++i) {
+    EXPECT_EQ(i, results[i]);
+  }
+}
+
+TEST(WorkQueue, BoundedSizeWorks) {
+  WorkQueue<int> queue(1);
+  int result;
+  queue.push(5);
+  queue.pop(result);
+  queue.push(5);
+  queue.pop(result);
+  queue.push(5);
+  queue.finish();
+  queue.pop(result);
+  EXPECT_EQ(5, result);
+}
+
+TEST(WorkQueue, BoundedSizePushAfterFinish) {
+  WorkQueue<int> queue(1);
+  int result;
+  queue.push(5);
+  std::thread pusher([&queue] {
+    queue.push(6);
+  });
+  // Dirtily try and make sure that pusher has run.
+  std::this_thread::sleep_for(std::chrono::seconds(1));
+  queue.finish();
+  EXPECT_TRUE(queue.pop(result));
+  EXPECT_EQ(5, result);
+  EXPECT_FALSE(queue.pop(result));
+
+  pusher.join();
+}
+
+TEST(WorkQueue, SetMaxSize) {
+  WorkQueue<int> queue(2);
+  int result;
+  queue.push(5);
+  queue.push(6);
+  queue.setMaxSize(1);
+  std::thread pusher([&queue] {
+    queue.push(7);
+  });
+  // Dirtily try and make sure that pusher has run.
+  std::this_thread::sleep_for(std::chrono::seconds(1));
+  queue.finish();
+  EXPECT_TRUE(queue.pop(result));
+  EXPECT_EQ(5, result);
+  EXPECT_TRUE(queue.pop(result));
+  EXPECT_EQ(6, result);
+  EXPECT_FALSE(queue.pop(result));
+
+  pusher.join();
+}
+
+TEST(WorkQueue, BoundedSizeMPMC) {
+  WorkQueue<int> queue(10);
+  std::vector<int> results(200, -1);
+  std::mutex mutex;
+  std::vector<std::thread> popperThreads;
+  for (int i = 0; i < 4; ++i) {
+    popperThreads.emplace_back(Popper{&queue, results.data(), &mutex});
+  }
+
+  std::vector<std::thread> pusherThreads;
+  for (int i = 0; i < 2; ++i) {
+    auto min = i * 100;
+    auto max = (i + 1) * 100;
+    pusherThreads.emplace_back(
+        [ &queue, min, max ] {
+          for (int i = min; i < max; ++i) {
+            queue.push(i);
+          }
+        });
+  }
+
+  for (auto& thread : pusherThreads) {
+    thread.join();
+  }
+  queue.finish();
+
+  for (auto& thread : popperThreads) {
+    thread.join();
+  }
+
+  for (int i = 0; i < 200; ++i) {
+    EXPECT_EQ(i, results[i]);
+  }
+}
+
+TEST(BufferWorkQueue, SizeCalculatedCorrectly) {
+  {
+    BufferWorkQueue queue;
+    queue.finish();
+    EXPECT_EQ(0, queue.size());
+  }
+  {
+    BufferWorkQueue queue;
+    queue.push(Buffer(10));
+    queue.finish();
+    EXPECT_EQ(10, queue.size());
+  }
+  {
+    BufferWorkQueue queue;
+    queue.push(Buffer(10));
+    queue.push(Buffer(5));
+    queue.finish();
+    EXPECT_EQ(15, queue.size());
+  }
+  {
+    BufferWorkQueue queue;
+    queue.push(Buffer(10));
+    queue.push(Buffer(5));
+    queue.finish();
+    Buffer buffer;
+    queue.pop(buffer);
+    EXPECT_EQ(5, queue.size());
+  }
+}
diff --git a/examples/Makefile b/examples/Makefile
index a9d6087..54602df 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -55,20 +55,21 @@ streaming_decompression : streaming_decompression.c
 clean:
 	@rm -f core *.o tmp* result* *.zst \
         simple_compression simple_decompression \
-		dictionary_compression dictionary_decompression \
-		streaming_compression streaming_decompression
+        dictionary_compression dictionary_decompression \
+        streaming_compression streaming_decompression
 	@echo Cleaning completed
 
 test: all
 	cp README.md tmp
+	cp Makefile tmp2
 	@echo starting simple compression
 	./simple_compression tmp
 	./simple_decompression tmp.zst
-	./streaming_decompression tmp.zst
+	./streaming_decompression tmp.zst > /dev/null
 	@echo starting streaming compression
 	./streaming_compression tmp
-	./streaming_decompression tmp.zst
+	./streaming_decompression tmp.zst > /dev/null
 	@echo starting dictionary compression
-	./dictionary_compression tmp README.md
-	./dictionary_decompression tmp.zst README.md
+	./dictionary_compression tmp2 tmp README.md
+	./dictionary_decompression tmp2.zst tmp.zst README.md
 	@echo tests completed
diff --git a/examples/README.md b/examples/README.md
index 2f46038..ba132f6 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,18 +1,31 @@
 Zstandard library : usage examples
 ==================================
 
-- [Simple compression](simple_compression.c)
+- [Simple compression](simple_compression.c) :
   Compress a single file.
   Introduces usage of : `ZSTD_compress()`
 
-- [Simple decompression](simple_decompression.c)
-  Decompress a single file compressed by zstd.
+- [Simple decompression](simple_decompression.c) :
+  Decompress a single file.
+  Only compatible with simple compression.
+  Result remains in memory.
   Introduces usage of : `ZSTD_decompress()`
 
-- [Dictionary compression](dictionary_compression.c)
+- [Streaming compression](streaming_compression.c) :
+  Compress a single file.
+  Introduces usage of : `ZSTD_compressStream()`
+
+- [Streaming decompression](streaming_decompression.c) :
+  Decompress a single file compressed by zstd.
+  Compatible with both simple and streaming compression.
+  Result is sent to stdout.
+  Introduces usage of : `ZSTD_decompressStream()`
+
+- [Dictionary compression](dictionary_compression.c) :
   Compress multiple files using the same dictionary.
   Introduces usage of : `ZSTD_createCDict()` and `ZSTD_compress_usingCDict()`
 
-- [Dictionary decompression](dictionary_decompression.c)
+- [Dictionary decompression](dictionary_decompression.c) :
   Decompress multiple files using the same dictionary.
+  Result remains in memory.
   Introduces usage of : `ZSTD_createDDict()` and `ZSTD_decompress_usingDDict()`
diff --git a/examples/dictionary_compression.c b/examples/dictionary_compression.c
index 08d639c..adcc3b4 100644
--- a/examples/dictionary_compression.c
+++ b/examples/dictionary_compression.c
@@ -73,12 +73,12 @@ static void saveFile_orDie(const char* fileName, const void* buff, size_t buffSi
 
 /* createDict() :
    `dictFileName` is supposed to have been created using `zstd --train` */
-static ZSTD_CDict* createCDict_orDie(const char* dictFileName)
+static ZSTD_CDict* createCDict_orDie(const char* dictFileName, int cLevel)
 {
     size_t dictSize;
     printf("loading dictionary %s \n", dictFileName);
     void* const dictBuffer = loadFile_orDie(dictFileName, &dictSize);
-    ZSTD_CDict* const cdict = ZSTD_createCDict(dictBuffer, dictSize, 3);
+    ZSTD_CDict* const cdict = ZSTD_createCDict(dictBuffer, dictSize, cLevel);
     if (!cdict) {
         fprintf(stderr, "ZSTD_createCDict error \n");
         exit(7);
@@ -96,6 +96,7 @@ static void compress(const char* fname, const char* oname, const ZSTD_CDict* cdi
     void* const cBuff = malloc_orDie(cBuffSize);
 
     ZSTD_CCtx* const cctx = ZSTD_createCCtx();
+    if (cctx==NULL) { fprintf(stderr, "ZSTD_createCCtx() error \n"); exit(10); }
     size_t const cSize = ZSTD_compress_usingCDict(cctx, cBuff, cBuffSize, fBuff, fSize, cdict);
     if (ZSTD_isError(cSize)) {
         fprintf(stderr, "error compressing %s : %s \n", fname, ZSTD_getErrorName(cSize));
@@ -107,7 +108,7 @@ static void compress(const char* fname, const char* oname, const ZSTD_CDict* cdi
     /* success */
     printf("%25s : %6u -> %7u - %s \n", fname, (unsigned)fSize, (unsigned)cSize, oname);
 
-    ZSTD_freeCCtx(cctx);
+    ZSTD_freeCCtx(cctx);   /* never fails */
     free(fBuff);
     free(cBuff);
 }
@@ -127,6 +128,7 @@ static char* createOutFilename_orDie(const char* filename)
 int main(int argc, const char** argv)
 {
     const char* const exeName = argv[0];
+    int const cLevel = 3;
 
     if (argc<3) {
         fprintf(stderr, "wrong arguments\n");
@@ -137,7 +139,7 @@ int main(int argc, const char** argv)
 
     /* load dictionary only once */
     const char* const dictName = argv[argc-1];
-    ZSTD_CDict* const dictPtr = createCDict_orDie(dictName);
+    ZSTD_CDict* const dictPtr = createCDict_orDie(dictName, cLevel);
 
     int u;
     for (u=1; u<argc-1; u++) {
diff --git a/examples/dictionary_decompression.c b/examples/dictionary_decompression.c
index b53d41e..db9417b 100644
--- a/examples/dictionary_decompression.c
+++ b/examples/dictionary_decompression.c
@@ -81,11 +81,11 @@ static void decompress(const char* fname, const ZSTD_DDict* ddict)
         fprintf(stderr, "%s : original size unknown \n", fname);
         exit(6);
     }
-    void* const rBuff = malloc_orDie(rSize);
+    void* const rBuff = malloc_orDie((size_t)rSize);
 
     ZSTD_DCtx* const dctx = ZSTD_createDCtx();
+    if (dctx==NULL) { fprintf(stderr, "ZSTD_createDCtx() error \n"); exit(10); }
     size_t const dSize = ZSTD_decompress_usingDDict(dctx, rBuff, rSize, cBuff, cSize, ddict);
-
     if (dSize != rSize) {
         fprintf(stderr, "error decoding %s : %s \n", fname, ZSTD_getErrorName(dSize));
         exit(7);
diff --git a/examples/simple_compression.c b/examples/simple_compression.c
index c377b86..332ce72 100644
--- a/examples/simple_compression.c
+++ b/examples/simple_compression.c
@@ -53,7 +53,7 @@ static void* loadFile_orDie(const char* fileName, size_t* size)
         fprintf(stderr, "fread: %s : %s \n", fileName, strerror(errno));
         exit(4);
     }
-    fclose(inFile);
+    fclose(inFile);  /* can't fail, read only */
     *size = buffSize;
     return buffer;
 }
@@ -101,7 +101,7 @@ static const char* createOutFilename_orDie(const char* filename)
 {
     size_t const inL = strlen(filename);
     size_t const outL = inL + 5;
-    void* outSpace = malloc_orDie(outL);
+    void* const outSpace = malloc_orDie(outL);
     memset(outSpace, 0, outL);
     strcat(outSpace, filename);
     strcat(outSpace, ".zst");
diff --git a/examples/simple_decompression.c b/examples/simple_decompression.c
index 745dfd3..907ee33 100644
--- a/examples/simple_decompression.c
+++ b/examples/simple_decompression.c
@@ -53,7 +53,7 @@ static void* loadFile_X(const char* fileName, size_t* size)
         printf("fread: %s : %s \n", fileName, strerror(errno));
         exit(4);
     }
-    fclose(inFile);
+    fclose(inFile);   /* can't fail (read only) */
     *size = buffSize;
     return buffer;
 }
@@ -68,7 +68,7 @@ static void decompress(const char* fname)
         printf("%s : original size unknown \n", fname);
         exit(5);
     }
-    void* const rBuff = malloc_X(rSize);
+    void* const rBuff = malloc_X((size_t)rSize);
 
     size_t const dSize = ZSTD_decompress(rBuff, rSize, cBuff, cSize);
 
diff --git a/examples/streaming_compression.c b/examples/streaming_compression.c
index ec9c588..108a63c 100644
--- a/examples/streaming_compression.c
+++ b/examples/streaming_compression.c
@@ -65,31 +65,34 @@ 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);
-    size_t read, toRead = buffInSize;
 
     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);
-    if (remainingToFlush) { fprintf(stderr, "not fully flushed"); exit(12); }
+    size_t const remainingToFlush = ZSTD_endStream(cstream, &output);   /* close frame */
+    if (remainingToFlush) { fprintf(stderr, "not fully flushed"); exit(13); }
     fwrite_orDie(buffOut, output.pos, fout);
 
+    ZSTD_freeCStream(cstream);
     fclose_orDie(fout);
     fclose_orDie(fin);
     free(buffIn);
diff --git a/examples/streaming_decompression.c b/examples/streaming_decompression.c
index d4dfacb..1ba1a3d 100644
--- a/examples/streaming_decompression.c
+++ b/examples/streaming_decompression.c
@@ -42,6 +42,15 @@ static size_t fread_orDie(void* buffer, size_t sizeToRead, FILE* file)
     exit(4);
 }
 
+static size_t fwrite_orDie(const void* buffer, size_t sizeToWrite, FILE* file)
+{
+    size_t const writtenSize = fwrite(buffer, 1, sizeToWrite, file);
+    if (writtenSize == sizeToWrite) return sizeToWrite;   /* good */
+    /* error */
+    perror("fwrite");
+    exit(5);
+}
+
 static size_t fclose_orDie(FILE* file)
 {
     if (!fclose(file)) return 0;
@@ -54,28 +63,31 @@ static size_t fclose_orDie(FILE* file)
 static void decompressFile_orDie(const char* fname)
 {
     FILE* const fin  = fopen_orDie(fname, "rb");
-    size_t const buffInSize = ZSTD_DStreamInSize();;
+    size_t const buffInSize = ZSTD_DStreamInSize();
     void*  const buffIn  = malloc_orDie(buffInSize);
-    size_t const buffOutSize = ZSTD_DStreamOutSize();;
+    FILE* const fout = stdout;
+    size_t const buffOutSize = ZSTD_DStreamOutSize();  /* Guarantee to successfully flush at least one complete compressed block in all circumstances. */
     void*  const buffOut = malloc_orDie(buffOutSize);
-    size_t read, toRead = buffInSize;
 
     ZSTD_DStream* const dstream = ZSTD_createDStream();
     if (dstream==NULL) { fprintf(stderr, "ZSTD_createDStream() error \n"); exit(10); }
     size_t const initResult = ZSTD_initDStream(dstream);
-    if (ZSTD_isError(initResult)) { fprintf(stderr, "ZSTD_initDStream() error \n"); exit(11); }
+    if (ZSTD_isError(initResult)) { fprintf(stderr, "ZSTD_initDStream() error : %s \n", ZSTD_getErrorName(initResult)); exit(11); }
 
-    while( (read = fread_orDie(buffIn, toRead, fin)) ) {
+    size_t read, toRead = initResult;
+    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_decompressStream(dstream, &output , &input);
-            /* note : data is just "sinked" into buffOut
-               a more complete example would write it to disk or stdout */
+            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);
         }
     }
 
+    ZSTD_freeDStream(dstream);
     fclose_orDie(fin);
+    fclose_orDie(fout);
     free(buffIn);
     free(buffOut);
 }
@@ -87,14 +99,12 @@ int main(int argc, const char** argv)
     const char* const inFilename = argv[1];
 
     if (argc!=2) {
-        printf("wrong arguments\n");
-        printf("usage:\n");
-        printf("%s FILE\n", exeName);
+        fprintf(stderr, "wrong arguments\n");
+        fprintf(stderr, "usage:\n");
+        fprintf(stderr, "%s FILE\n", exeName);
         return 1;
     }
 
     decompressFile_orDie(inFilename);
-    printf("%s correctly decoded (in memory). \n", inFilename);
-
     return 0;
 }
diff --git a/lib/Makefile b/lib/Makefile
index 35522da..4fb8ed9 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -78,7 +78,7 @@ clean:
 
 #------------------------------------------------------------------------
 #make install is validated only for Linux, OSX, kFreeBSD, Hurd and some BSD targets
-ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU FreeBSD DragonFly))
+ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU FreeBSD DragonFly NetBSD))
 
 libzstd.pc:
 libzstd.pc: libzstd.pc.in
@@ -105,11 +105,11 @@ uninstall:
 	$(RM) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT)
 	$(RM) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT_MAJOR)
 	$(RM) $(DESTDIR)$(LIBDIR)/pkgconfig/libzstd.pc
-	[ -x $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT_VER) ] && $(RM) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT_VER)
-	@[ -f $(DESTDIR)$(LIBDIR)/libzstd.a ] && $(RM) $(DESTDIR)$(LIBDIR)/libzstd.a
-	@[ -f $(DESTDIR)$(INCLUDEDIR)/zstd.h ] && $(RM) $(DESTDIR)$(INCLUDEDIR)/zstd.h
-	@[ -f $(DESTDIR)$(INCLUDEDIR)/zbuff.h ] && $(RM) $(DESTDIR)$(INCLUDEDIR)/zbuff.h
-	@[ -f $(DESTDIR)$(INCLUDEDIR)/zdict.h ] && $(RM) $(DESTDIR)$(INCLUDEDIR)/zdict.h
+	$(RM) $(DESTDIR)$(LIBDIR)/libzstd.$(SHARED_EXT_VER)
+	$(RM) $(DESTDIR)$(LIBDIR)/libzstd.a
+	$(RM) $(DESTDIR)$(INCLUDEDIR)/zstd.h
+	$(RM) $(DESTDIR)$(INCLUDEDIR)/zbuff.h
+	$(RM) $(DESTDIR)$(INCLUDEDIR)/zdict.h
 	@echo zstd libraries successfully uninstalled
 
 endif
diff --git a/lib/common/fse_decompress.c b/lib/common/fse_decompress.c
index 032e657..7492a38 100644
--- a/lib/common/fse_decompress.c
+++ b/lib/common/fse_decompress.c
@@ -42,12 +42,15 @@
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4214)        /* disable: C4214: non-int bitfields */
 #else
-#  ifdef __GNUC__
-#    define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#    define FORCE_INLINE static inline __attribute__((always_inline))
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#    ifdef __GNUC__
+#      define FORCE_INLINE static inline __attribute__((always_inline))
+#    else
+#      define FORCE_INLINE static inline
+#    endif
 #  else
-#    define FORCE_INLINE static inline
-#  endif
+#    define FORCE_INLINE static
+#  endif /* __STDC_VERSION__ */
 #endif
 
 
diff --git a/lib/common/xxhash.c b/lib/common/xxhash.c
index f462c57..29e4fa6 100644
--- a/lib/common/xxhash.c
+++ b/lib/common/xxhash.c
@@ -115,7 +115,7 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcp
 #  pragma warning(disable : 4127)      /* disable: C4127: conditional expression is constant */
 #  define FORCE_INLINE static __forceinline
 #else
-#  if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
 #    ifdef __GNUC__
 #      define FORCE_INLINE static inline __attribute__((always_inline))
 #    else
diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h
index 51e7170..f40e00a 100644
--- a/lib/common/zstd_internal.h
+++ b/lib/common/zstd_internal.h
@@ -10,6 +10,28 @@
 #ifndef ZSTD_CCOMMON_H_MODULE
 #define ZSTD_CCOMMON_H_MODULE
 
+/*-*******************************************************
+*  Compiler specifics
+*********************************************************/
+#ifdef _MSC_VER    /* Visual Studio */
+#  define FORCE_INLINE static __forceinline
+#  include <intrin.h>                    /* For Visual 2005 */
+#  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
+#  pragma warning(disable : 4324)        /* disable: C4324: padded structure */
+#  pragma warning(disable : 4100)        /* disable: C4100: unreferenced formal parameter */
+#else
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#    ifdef __GNUC__
+#      define FORCE_INLINE static inline __attribute__((always_inline))
+#    else
+#      define FORCE_INLINE static inline
+#    endif
+#  else
+#    define FORCE_INLINE static
+#  endif /* __STDC_VERSION__ */
+#endif
+
+
 /*-*************************************
 *  Dependencies
 ***************************************/
@@ -20,10 +42,12 @@
 
 
 /*-*************************************
-*  Common macros
+*  shared macros
 ***************************************/
 #define MIN(a,b) ((a)<(b) ? (a) : (b))
 #define MAX(a,b) ((a)>(b) ? (a) : (b))
+#define CHECK_F(f) { size_t const errcod = f; if (ERR_isError(errcod)) return errcod; }  /* check and Forward error code */
+#define CHECK_E(f, e) { size_t const errcod = f; if (ERR_isError(errcod)) return ERROR(e); }  /* check and send Error code */
 
 
 /*-*************************************
@@ -84,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,
@@ -94,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/fse_compress.c b/lib/compress/fse_compress.c
index 386b2c0..679dbdb 100644
--- a/lib/compress/fse_compress.c
+++ b/lib/compress/fse_compress.c
@@ -41,12 +41,15 @@
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4214)        /* disable: C4214: non-int bitfields */
 #else
-#  ifdef __GNUC__
-#    define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#    define FORCE_INLINE static inline __attribute__((always_inline))
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#    ifdef __GNUC__
+#      define FORCE_INLINE static inline __attribute__((always_inline))
+#    else
+#      define FORCE_INLINE static inline
+#    endif
 #  else
-#    define FORCE_INLINE static inline
-#  endif
+#    define FORCE_INLINE static
+#  endif /* __STDC_VERSION__ */
 #endif
 
 
diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c
index c2dd13c..b7d3d77 100644
--- a/lib/compress/huf_compress.c
+++ b/lib/compress/huf_compress.c
@@ -35,24 +35,8 @@
 /* **************************************************************
 *  Compiler specifics
 ****************************************************************/
-#if defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-/* inline is defined */
-#elif defined(_MSC_VER)
-#  define inline __inline
-#else
-#  define inline /* disable inline */
-#endif
-
-
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
-#else
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 0116136..94f4b5a 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -8,30 +8,13 @@
  */
 
 
-
-/*-*******************************************************
-*  Compiler specifics
-*********************************************************/
-#ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
-#  include <intrin.h>                    /* For Visual 2005 */
-#  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
-#else
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
-#endif
-
-
 /*-*************************************
 *  Dependencies
 ***************************************/
 #include <string.h>         /* memset */
 #include "mem.h"
 #define XXH_STATIC_LINKING_ONLY   /* XXH64_state_t */
-#include "xxhash.h"         /* XXH_reset, update, digest */
+#include "xxhash.h"               /* XXH_reset, update, digest */
 #define FSE_STATIC_LINKING_ONLY   /* FSE_encodeSymbol */
 #include "fse.h"
 #define HUF_STATIC_LINKING_ONLY
@@ -116,7 +99,7 @@ ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem)
     cctx = (ZSTD_CCtx*) ZSTD_malloc(sizeof(ZSTD_CCtx), customMem);
     if (!cctx) return NULL;
     memset(cctx, 0, sizeof(ZSTD_CCtx));
-    memcpy(&(cctx->customMem), &customMem, sizeof(ZSTD_customMem));
+    memcpy(&(cctx->customMem), &customMem, sizeof(customMem));
     return cctx;
 }
 
@@ -130,6 +113,7 @@ size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx)
 
 size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx)
 {
+    if (cctx==NULL) return 0;   /* support sizeof on NULL */
     return sizeof(*cctx) + cctx->workSpaceSize;
 }
 
@@ -139,19 +123,17 @@ const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx)   /* hidden interface *
 }
 
 
-#define CLAMP(val,min,max) { if (val<min) val=min; else if (val>max) val=max; }
-#define CLAMPCHECK(val,min,max) { if ((val<min) || (val>max)) return ERROR(compressionParameter_unsupported); }
-
 /** ZSTD_checkParams() :
     ensure param values remain within authorized range.
     @return : 0, or an error code if one value is beyond authorized range */
 size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams)
 {
+#   define CLAMPCHECK(val,min,max) { if ((val<min) | (val>max)) return ERROR(compressionParameter_unsupported); }
     CLAMPCHECK(cParams.windowLog, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX);
     CLAMPCHECK(cParams.chainLog, ZSTD_CHAINLOG_MIN, ZSTD_CHAINLOG_MAX);
     CLAMPCHECK(cParams.hashLog, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX);
     CLAMPCHECK(cParams.searchLog, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX);
-    { U32 const searchLengthMin = (cParams.strategy == ZSTD_fast || cParams.strategy == ZSTD_greedy) ? ZSTD_SEARCHLENGTH_MIN+1 : ZSTD_SEARCHLENGTH_MIN;
+    { U32 const searchLengthMin = ((cParams.strategy == ZSTD_fast) | (cParams.strategy == ZSTD_greedy)) ? ZSTD_SEARCHLENGTH_MIN+1 : ZSTD_SEARCHLENGTH_MIN;
       U32 const searchLengthMax = (cParams.strategy == ZSTD_fast) ? ZSTD_SEARCHLENGTH_MAX : ZSTD_SEARCHLENGTH_MAX-1;
       CLAMPCHECK(cParams.searchLength, searchLengthMin, searchLengthMax); }
     CLAMPCHECK(cParams.targetLength, ZSTD_TARGETLENGTH_MIN, ZSTD_TARGETLENGTH_MAX);
@@ -160,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.
@@ -187,16 +156,15 @@ 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<<ZSTD_WINDOWLOG_MAX)) {
-            U32 const srcLog = ZSTD_highbit32((U32)(rSize)-1) + 1;
+            U32 const srcLog = MAX(ZSTD_HASHLOG_MIN, ZSTD_highbit32((U32)(rSize)-1) + 1);
             if (cPar.windowLog > srcLog) cPar.windowLog = srcLog;
     }   }
     if (cPar.hashLog > cPar.windowLog) cPar.hashLog = cPar.windowLog;
-    {   U32 const btPlus = (cPar.strategy == ZSTD_btlazy2) || (cPar.strategy == ZSTD_btopt);
+    {   U32 const btPlus = (cPar.strategy == ZSTD_btlazy2) | (cPar.strategy == ZSTD_btopt);
         U32 const maxChainLog = cPar.windowLog+btPlus;
         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;
 }
@@ -223,81 +191,116 @@ size_t ZSTD_estimateCCtxSize(ZSTD_compressionParameters cParams)
     return sizeof(ZSTD_CCtx) + neededSpace;
 }
 
+
+static U32 ZSTD_equivalentParams(ZSTD_parameters param1, ZSTD_parameters param2)
+{
+    return (param1.cParams.hashLog  == param2.cParams.hashLog)
+         & (param1.cParams.chainLog == param2.cParams.chainLog)
+         & (param1.cParams.strategy == param2.cParams.strategy)
+         & ((param1.cParams.searchLength==3) == (param2.cParams.searchLength==3));
+}
+
+/*! ZSTD_continueCCtx() :
+    reuse CCtx without reset (note : requires no dictionary) */
+static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_parameters params, U64 frameContentSize)
+{
+    U32 const end = (U32)(cctx->nextSrc - cctx->base);
+    cctx->params = params;
+    cctx->frameContentSize = frameContentSize;
+    cctx->lowLimit = end;
+    cctx->dictLimit = end;
+    cctx->nextToUpdate = end+1;
+    cctx->stage = ZSTDcs_init;
+    cctx->dictID = 0;
+    cctx->loadedDictEnd = 0;
+    { int i; for (i=0; i<ZSTD_REP_NUM; i++) cctx->rep[i] = repStartValue[i]; }
+    cctx->seqStore.litLengthSum = 0;  /* force reset of btopt stats */
+    XXH64_reset(&cctx->xxhState, 0);
+    return 0;
+}
+
+typedef enum { ZSTDcrp_continue, ZSTDcrp_noMemset, ZSTDcrp_fullReset } ZSTD_compResetPolicy_e;
+
 /*! ZSTD_resetCCtx_advanced() :
-    note : 'params' is expected to be validated */
+    note : 'params' must be validated */
 static size_t ZSTD_resetCCtx_advanced (ZSTD_CCtx* zc,
                                        ZSTD_parameters params, U64 frameContentSize,
-                                       U32 reset)
-{   /* note : params considered validated here */
-    size_t const blockSize = MIN(ZSTD_BLOCKSIZE_ABSOLUTEMAX, (size_t)1 << params.cParams.windowLog);
-    U32    const divider = (params.cParams.searchLength==3) ? 3 : 4;
-    size_t const maxNbSeq = blockSize / divider;
-    size_t const tokenSpace = blockSize + 11*maxNbSeq;
-    size_t const chainSize = (params.cParams.strategy == ZSTD_fast) ? 0 : (1 << params.cParams.chainLog);
-    size_t const hSize = ((size_t)1) << params.cParams.hashLog;
-    U32    const hashLog3 = (params.cParams.searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, params.cParams.windowLog);
-    size_t const h3Size = ((size_t)1) << hashLog3;
-    size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
-    void* ptr;
-
-    /* Check if workSpace is large enough, alloc a new one if needed */
-    {   size_t const optSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<<Litbits))*sizeof(U32)
-                              + (ZSTD_OPT_NUM+1)*(sizeof(ZSTD_match_t) + sizeof(ZSTD_optimal_t));
-        size_t const neededSpace = tableSpace + (256*sizeof(U32)) /* huffTable */ + tokenSpace
-                              + ((params.cParams.strategy == ZSTD_btopt) ? optSpace : 0);
-        if (zc->workSpaceSize < neededSpace) {
-            ZSTD_free(zc->workSpace, zc->customMem);
-            zc->workSpace = ZSTD_malloc(neededSpace, zc->customMem);
-            if (zc->workSpace == NULL) return ERROR(memory_allocation);
-            zc->workSpaceSize = neededSpace;
-    }   }
+                                       ZSTD_compResetPolicy_e const crp)
+{
+    if (crp == ZSTDcrp_continue)
+        if (ZSTD_equivalentParams(params, zc->params))
+            return ZSTD_continueCCtx(zc, params, frameContentSize);
+
+    {   size_t const blockSize = MIN(ZSTD_BLOCKSIZE_ABSOLUTEMAX, (size_t)1 << params.cParams.windowLog);
+        U32    const divider = (params.cParams.searchLength==3) ? 3 : 4;
+        size_t const maxNbSeq = blockSize / divider;
+        size_t const tokenSpace = blockSize + 11*maxNbSeq;
+        size_t const chainSize = (params.cParams.strategy == ZSTD_fast) ? 0 : (1 << params.cParams.chainLog);
+        size_t const hSize = ((size_t)1) << params.cParams.hashLog;
+        U32    const hashLog3 = (params.cParams.searchLength>3) ? 0 : MIN(ZSTD_HASHLOG3_MAX, params.cParams.windowLog);
+        size_t const h3Size = ((size_t)1) << hashLog3;
+        size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
+        void* ptr;
+
+        /* Check if workSpace is large enough, alloc a new one if needed */
+        {   size_t const optSpace = ((MaxML+1) + (MaxLL+1) + (MaxOff+1) + (1<<Litbits))*sizeof(U32)
+                                  + (ZSTD_OPT_NUM+1)*(sizeof(ZSTD_match_t) + sizeof(ZSTD_optimal_t));
+            size_t const neededSpace = tableSpace + (256*sizeof(U32)) /* huffTable */ + tokenSpace
+                                  + ((params.cParams.strategy == ZSTD_btopt) ? optSpace : 0);
+            if (zc->workSpaceSize < neededSpace) {
+                ZSTD_free(zc->workSpace, zc->customMem);
+                zc->workSpace = ZSTD_malloc(neededSpace, zc->customMem);
+                if (zc->workSpace == NULL) return ERROR(memory_allocation);
+                zc->workSpaceSize = neededSpace;
+        }   }
 
-    if (reset) memset(zc->workSpace, 0, tableSpace );   /* reset only tables */
-    XXH64_reset(&zc->xxhState, 0);
-    zc->hashLog3 = hashLog3;
-    zc->hashTable = (U32*)(zc->workSpace);
-    zc->chainTable = zc->hashTable + hSize;
-    zc->hashTable3 = zc->chainTable + chainSize;
-    ptr = zc->hashTable3 + h3Size;
-    zc->hufTable = (HUF_CElt*)ptr;
-    zc->flagStaticTables = 0;
-    ptr = ((U32*)ptr) + 256;  /* note : HUF_CElt* is incomplete type, size is simulated using U32 */
-
-    zc->nextToUpdate = 1;
-    zc->nextSrc = NULL;
-    zc->base = NULL;
-    zc->dictBase = NULL;
-    zc->dictLimit = 0;
-    zc->lowLimit = 0;
-    zc->params = params;
-    zc->blockSize = blockSize;
-    zc->frameContentSize = frameContentSize;
-    { int i; for (i=0; i<ZSTD_REP_NUM; i++) zc->rep[i] = repStartValue[i]; }
-
-    if (params.cParams.strategy == ZSTD_btopt) {
-        zc->seqStore.litFreq = (U32*)ptr;
-        zc->seqStore.litLengthFreq = zc->seqStore.litFreq + (1<<Litbits);
-        zc->seqStore.matchLengthFreq = zc->seqStore.litLengthFreq + (MaxLL+1);
-        zc->seqStore.offCodeFreq = zc->seqStore.matchLengthFreq + (MaxML+1);
-        ptr = zc->seqStore.offCodeFreq + (MaxOff+1);
-        zc->seqStore.matchTable = (ZSTD_match_t*)ptr;
-        ptr = zc->seqStore.matchTable + ZSTD_OPT_NUM+1;
-        zc->seqStore.priceTable = (ZSTD_optimal_t*)ptr;
-        ptr = zc->seqStore.priceTable + ZSTD_OPT_NUM+1;
-        zc->seqStore.litLengthSum = 0;
-    }
-    zc->seqStore.sequencesStart = (seqDef*)ptr;
-    ptr = zc->seqStore.sequencesStart + maxNbSeq;
-    zc->seqStore.llCode = (BYTE*) ptr;
-    zc->seqStore.mlCode = zc->seqStore.llCode + maxNbSeq;
-    zc->seqStore.ofCode = zc->seqStore.mlCode + maxNbSeq;
-    zc->seqStore.litStart = zc->seqStore.ofCode + maxNbSeq;
+        if (crp!=ZSTDcrp_noMemset) memset(zc->workSpace, 0, tableSpace);   /* reset tables only */
+        XXH64_reset(&zc->xxhState, 0);
+        zc->hashLog3 = hashLog3;
+        zc->hashTable = (U32*)(zc->workSpace);
+        zc->chainTable = zc->hashTable + hSize;
+        zc->hashTable3 = zc->chainTable + chainSize;
+        ptr = zc->hashTable3 + h3Size;
+        zc->hufTable = (HUF_CElt*)ptr;
+        zc->flagStaticTables = 0;
+        ptr = ((U32*)ptr) + 256;  /* note : HUF_CElt* is incomplete type, size is simulated using U32 */
+
+        zc->nextToUpdate = 1;
+        zc->nextSrc = NULL;
+        zc->base = NULL;
+        zc->dictBase = NULL;
+        zc->dictLimit = 0;
+        zc->lowLimit = 0;
+        zc->params = params;
+        zc->blockSize = blockSize;
+        zc->frameContentSize = frameContentSize;
+        { int i; for (i=0; i<ZSTD_REP_NUM; i++) zc->rep[i] = repStartValue[i]; }
+
+        if (params.cParams.strategy == ZSTD_btopt) {
+            zc->seqStore.litFreq = (U32*)ptr;
+            zc->seqStore.litLengthFreq = zc->seqStore.litFreq + (1<<Litbits);
+            zc->seqStore.matchLengthFreq = zc->seqStore.litLengthFreq + (MaxLL+1);
+            zc->seqStore.offCodeFreq = zc->seqStore.matchLengthFreq + (MaxML+1);
+            ptr = zc->seqStore.offCodeFreq + (MaxOff+1);
+            zc->seqStore.matchTable = (ZSTD_match_t*)ptr;
+            ptr = zc->seqStore.matchTable + ZSTD_OPT_NUM+1;
+            zc->seqStore.priceTable = (ZSTD_optimal_t*)ptr;
+            ptr = zc->seqStore.priceTable + ZSTD_OPT_NUM+1;
+            zc->seqStore.litLengthSum = 0;
+        }
+        zc->seqStore.sequencesStart = (seqDef*)ptr;
+        ptr = zc->seqStore.sequencesStart + maxNbSeq;
+        zc->seqStore.llCode = (BYTE*) ptr;
+        zc->seqStore.mlCode = zc->seqStore.llCode + maxNbSeq;
+        zc->seqStore.ofCode = zc->seqStore.mlCode + maxNbSeq;
+        zc->seqStore.litStart = zc->seqStore.ofCode + maxNbSeq;
 
-    zc->stage = ZSTDcs_init;
-    zc->dictID = 0;
-    zc->loadedDictEnd = 0;
+        zc->stage = ZSTDcs_init;
+        zc->dictID = 0;
+        zc->loadedDictEnd = 0;
 
-    return 0;
+        return 0;
+    }
 }
 
 
@@ -305,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, 0);
-    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);
@@ -665,8 +667,7 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* zc,
         FSE_CState_t  stateOffsetBits;
         FSE_CState_t  stateLitLength;
 
-        { size_t const errorCode = BIT_initCStream(&blockStream, op, oend-op);
-          if (ERR_isError(errorCode)) return ERROR(dstSize_tooSmall); }   /* not enough space remaining */
+        CHECK_E(BIT_initCStream(&blockStream, op, oend-op), dstSize_tooSmall); /* not enough space remaining */
 
         /* first symbols */
         FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]);
@@ -1080,7 +1081,7 @@ static void ZSTD_compressBlock_fast_extDict_generic(ZSTD_CCtx* ctx,
 
         if (ip <= ilimit) {
             /* Fill Table */
-			hashTable[ZSTD_hashPtr(base+current+2, hBits, mls)] = current+2;
+            hashTable[ZSTD_hashPtr(base+current+2, hBits, mls)] = current+2;
             hashTable[ZSTD_hashPtr(ip-2, hBits, mls)] = (U32)(ip-2-base);
             /* check immediate repcode */
             while (ip <= ilimit) {
@@ -2473,8 +2474,7 @@ static size_t ZSTD_loadDictEntropyStats(ZSTD_CCtx* cctx, const void* dict, size_
         unsigned offcodeMaxValue = MaxOff, offcodeLog = OffFSELog;
         size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr);
         if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted);
-        { size_t const errorCode = FSE_buildCTable(cctx->offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog);
-          if (FSE_isError(errorCode)) return ERROR(dictionary_corrupted); }
+        CHECK_E (FSE_buildCTable(cctx->offcodeCTable, offcodeNCount, offcodeMaxValue, offcodeLog), dictionary_corrupted);
         dictPtr += offcodeHeaderSize;
     }
 
@@ -2482,8 +2482,7 @@ static size_t ZSTD_loadDictEntropyStats(ZSTD_CCtx* cctx, const void* dict, size_
         unsigned matchlengthMaxValue = MaxML, matchlengthLog = MLFSELog;
         size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr);
         if (FSE_isError(matchlengthHeaderSize)) return ERROR(dictionary_corrupted);
-        { size_t const errorCode = FSE_buildCTable(cctx->matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog);
-          if (FSE_isError(errorCode)) return ERROR(dictionary_corrupted); }
+        CHECK_E (FSE_buildCTable(cctx->matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog), dictionary_corrupted);
         dictPtr += matchlengthHeaderSize;
     }
 
@@ -2491,8 +2490,7 @@ static size_t ZSTD_loadDictEntropyStats(ZSTD_CCtx* cctx, const void* dict, size_
         unsigned litlengthMaxValue = MaxLL, litlengthLog = LLFSELog;
         size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr);
         if (FSE_isError(litlengthHeaderSize)) return ERROR(dictionary_corrupted);
-        { size_t const errorCode = FSE_buildCTable(cctx->litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog);
-          if (FSE_isError(errorCode)) return ERROR(dictionary_corrupted); }
+        CHECK_E(FSE_buildCTable(cctx->litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog), dictionary_corrupted);
         dictPtr += litlengthHeaderSize;
     }
 
@@ -2517,9 +2515,9 @@ static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* zc, const void* dict, si
     zc->dictID = zc->params.fParams.noDictIDFlag ? 0 :  MEM_readLE32((const char*)dict+4);
 
     /* known magic number : dict is parsed for entropy stats and content */
-    {   size_t const eSize_8 = ZSTD_loadDictEntropyStats(zc, (const char*)dict+8 /* skip dictHeader */, dictSize-8);
-        size_t const eSize = eSize_8 + 8;
-        if (ZSTD_isError(eSize_8)) return eSize_8;
+    {   size_t const loadError = ZSTD_loadDictEntropyStats(zc, (const char*)dict+8 /* skip dictHeader */, dictSize-8);
+        size_t const eSize = loadError + 8;
+        if (ZSTD_isError(loadError)) return loadError;
         return ZSTD_loadDictionaryContent(zc, (const char*)dict+eSize, dictSize-eSize);
     }
 }
@@ -2527,14 +2525,13 @@ static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* zc, const void* dict, si
 
 /*! ZSTD_compressBegin_internal() :
 *   @return : 0, or an error code */
-static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* zc,
+static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx,
                              const void* dict, size_t dictSize,
                                    ZSTD_parameters params, U64 pledgedSrcSize)
 {
-    size_t const resetError = ZSTD_resetCCtx_advanced(zc, params, pledgedSrcSize, 1);
-    if (ZSTD_isError(resetError)) return resetError;
-
-    return ZSTD_compress_insertDictionary(zc, dict, dictSize);
+    ZSTD_compResetPolicy_e const crp = dictSize ? ZSTDcrp_fullReset : ZSTDcrp_continue;
+    CHECK_F(ZSTD_resetCCtx_advanced(cctx, params, pledgedSrcSize, crp));
+    return ZSTD_compress_insertDictionary(cctx, dict, dictSize);
 }
 
 
@@ -2545,9 +2542,7 @@ size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx,
                                    ZSTD_parameters params, unsigned long long pledgedSrcSize)
 {
     /* compression parameters verification and optimization */
-    { size_t const errorCode = ZSTD_checkCParams_advanced(params.cParams, pledgedSrcSize);
-      if (ZSTD_isError(errorCode)) return errorCode; }
-
+    CHECK_F(ZSTD_checkCParams(params.cParams));
     return ZSTD_compressBegin_internal(cctx, dict, dictSize, params, pledgedSrcSize);
 }
 
@@ -2625,9 +2620,7 @@ static size_t ZSTD_compress_internal (ZSTD_CCtx* cctx,
                          const void* dict,size_t dictSize,
                                ZSTD_parameters params)
 {
-    size_t const errorCode = ZSTD_compressBegin_internal(cctx, dict, dictSize, params, srcSize);
-    if(ZSTD_isError(errorCode)) return errorCode;
-
+    CHECK_F(ZSTD_compressBegin_internal(cctx, dict, dictSize, params, srcSize));
     return ZSTD_compressEnd(cctx, dst,  dstCapacity, src, srcSize);
 }
 
@@ -2637,8 +2630,7 @@ size_t ZSTD_compress_advanced (ZSTD_CCtx* ctx,
                          const void* dict,size_t dictSize,
                                ZSTD_parameters params)
 {
-    size_t const errorCode = ZSTD_checkCParams_advanced(params.cParams, srcSize);
-    if (ZSTD_isError(errorCode)) return errorCode;
+    CHECK_F(ZSTD_checkCParams(params.cParams));
     return ZSTD_compress_internal(ctx, dst, dstCapacity, src, srcSize, dict, dictSize, params);
 }
 
@@ -2674,6 +2666,12 @@ struct ZSTD_CDict_s {
     ZSTD_CCtx* refContext;
 };  /* typedef'd tp ZSTD_CDict within "zstd.h" */
 
+size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict)
+{
+    if (cdict==NULL) return 0;   /* support sizeof on NULL */
+    return ZSTD_sizeof_CCtx(cdict->refContext) + cdict->dictContentSize;
+}
+
 ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize, ZSTD_parameters params, ZSTD_customMem customMem)
 {
     if (!customMem.customAlloc && !customMem.customFree) customMem = defaultCustomMem;
@@ -2717,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);
@@ -2725,17 +2723,23 @@ 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, pledgedSrcSize))
+    else CHECK_F(ZSTD_compressBegin_advanced(cctx, NULL, 0, cdict->refContext->params, pledgedSrcSize));
+    return 0;
+}
+
 /*! ZSTD_compress_usingCDict() :
 *   Compression using a digested Dictionary.
 *   Faster startup than ZSTD_compress_usingDict(), recommended when same dictionary is used multiple times.
 *   Note that compression level is decided during dictionary creation */
-ZSTDLIB_API size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx,
-                                           void* dst, size_t dstCapacity,
-                                     const void* src, size_t srcSize,
-                                     const ZSTD_CDict* cdict)
+size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx,
+                                void* dst, size_t dstCapacity,
+                                const void* src, size_t srcSize,
+                                const ZSTD_CDict* cdict)
 {
-    size_t const errorCode = ZSTD_copyCCtx(cctx, cdict->refContext);
-    if (ZSTD_isError(errorCode)) return errorCode;
+    CHECK_F(ZSTD_compressBegin_usingCDict(cctx, cdict, srcSize));
 
     if (cdict->refContext->params.fParams.contentSizeFlag==1) {
         cctx->params.fParams.contentSizeFlag = 1;
@@ -2754,7 +2758,8 @@ ZSTDLIB_API size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx,
 typedef enum { zcss_init, zcss_load, zcss_flush, zcss_final } ZSTD_cStreamStage;
 
 struct ZSTD_CStream_s {
-    ZSTD_CCtx* zc;
+    ZSTD_CCtx* cctx;
+    ZSTD_CDict* cdict;
     char*  inBuff;
     size_t inBuffSize;
     size_t inToCompress;
@@ -2787,8 +2792,8 @@ ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem)
     if (zcs==NULL) return NULL;
     memset(zcs, 0, sizeof(ZSTD_CStream));
     memcpy(&zcs->customMem, &customMem, sizeof(ZSTD_customMem));
-    zcs->zc = ZSTD_createCCtx_advanced(customMem);
-    if (zcs->zc == NULL) { ZSTD_freeCStream(zcs); return NULL; }
+    zcs->cctx = ZSTD_createCCtx_advanced(customMem);
+    if (zcs->cctx == NULL) { ZSTD_freeCStream(zcs); return NULL; }
     return zcs;
 }
 
@@ -2796,7 +2801,8 @@ size_t ZSTD_freeCStream(ZSTD_CStream* zcs)
 {
     if (zcs==NULL) return 0;   /* support free on NULL */
     {   ZSTD_customMem const cMem = zcs->customMem;
-        ZSTD_freeCCtx(zcs->zc);
+        ZSTD_freeCCtx(zcs->cctx);
+        ZSTD_freeCDict(zcs->cdict);
         ZSTD_free(zcs->inBuff, cMem);
         ZSTD_free(zcs->outBuff, cMem);
         ZSTD_free(zcs, cMem);
@@ -2810,6 +2816,19 @@ size_t ZSTD_freeCStream(ZSTD_CStream* zcs)
 size_t ZSTD_CStreamInSize(void)  { return ZSTD_BLOCKSIZE_ABSOLUTEMAX; }
 size_t ZSTD_CStreamOutSize(void) { return ZSTD_compressBound(ZSTD_BLOCKSIZE_ABSOLUTEMAX) + ZSTD_blockHeaderSize + 4 /* 32-bits hash */ ; }
 
+size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize)
+{
+    CHECK_F(ZSTD_compressBegin_usingCDict(zcs->cctx, zcs->cdict, pledgedSrcSize));
+
+    zcs->inToCompress = 0;
+    zcs->inBuffPos = 0;
+    zcs->inBuffTarget = zcs->blockSize;
+    zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0;
+    zcs->stage = zcss_load;
+    zcs->frameEnded = 0;
+    return 0;   /* ready to go */
+}
+
 size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
                                  const void* dict, size_t dictSize,
                                  ZSTD_parameters params, unsigned long long pledgedSrcSize)
@@ -2818,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);
         }
@@ -2826,22 +2845,18 @@ 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);
     }
 
-    { size_t const errorCode = ZSTD_compressBegin_advanced(zcs->zc, dict, dictSize, params, pledgedSrcSize);
-      if (ZSTD_isError(errorCode)) return errorCode; }
+    ZSTD_freeCDict(zcs->cdict);
+    zcs->cdict = ZSTD_createCDict_advanced(dict, dictSize, params, zcs->customMem);
+    if (zcs->cdict == NULL) return ERROR(memory_allocation);
 
-    zcs->inToCompress = 0;
-    zcs->inBuffPos = 0;
-    zcs->inBuffTarget = zcs->blockSize;
-    zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0;
-    zcs->stage = zcss_load;
     zcs->checksum = params.fParams.checksumFlag > 0;
-    zcs->frameEnded = 0;
-    return 0;   /* ready to go */
+
+    return ZSTD_resetCStream(zcs, pledgedSrcSize);
 }
 
 size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel)
@@ -2857,7 +2872,8 @@ size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel)
 
 size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs)
 {
-    return sizeof(zcs) + ZSTD_sizeof_CCtx(zcs->zc) + zcs->outBuffSize + zcs->inBuffSize;
+    if (zcs==NULL) return 0;   /* support sizeof on NULL */
+    return sizeof(zcs) + ZSTD_sizeof_CCtx(zcs->cctx) + ZSTD_sizeof_CDict(zcs->cdict) + zcs->outBuffSize + zcs->inBuffSize;
 }
 
 /*======   Compression   ======*/
@@ -2908,8 +2924,8 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
                 else
                     cDst = zcs->outBuff, oSize = zcs->outBuffSize;
                 cSize = (flush == zsf_end) ?
-                        ZSTD_compressEnd(zcs->zc, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize) :
-                        ZSTD_compressContinue(zcs->zc, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize);
+                        ZSTD_compressEnd(zcs->cctx, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize) :
+                        ZSTD_compressContinue(zcs->cctx, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize);
                 if (ZSTD_isError(cSize)) return cSize;
                 if (flush == zsf_end) zcs->frameEnded = 1;
                 /* prepare next block */
@@ -2974,8 +2990,8 @@ size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
     size_t srcSize = 0;
     size_t sizeWritten = output->size - output->pos;
     size_t const result = ZSTD_compressStream_generic(zcs,
-                                                      (char*)(output->dst) + output->pos, &sizeWritten,
-                                                      &srcSize, &srcSize, /* use a valid src address instead of NULL */
+                                                     (char*)(output->dst) + output->pos, &sizeWritten,
+                                                     &srcSize, &srcSize, /* use a valid src address instead of NULL */
                                                       zsf_flush);
     output->pos += sizeWritten;
     if (ZSTD_isError(result)) return result;
@@ -3003,7 +3019,7 @@ size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
         /* create epilogue */
         zcs->stage = zcss_final;
         zcs->outBuffContentSize = !notEnded ? 0 :
-            ZSTD_compressEnd(zcs->zc, zcs->outBuff, zcs->outBuffSize, NULL, 0);  /* write epilogue, including final empty block, into outBuff */
+            ZSTD_compressEnd(zcs->cctx, zcs->outBuff, zcs->outBuffSize, NULL, 0);  /* write epilogue, including final empty block, into outBuff */
     }
 
     /* flush epilogue */
diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c
index a5521bd..e94fa83 100644
--- a/lib/decompress/huf_decompress.c
+++ b/lib/decompress/huf_decompress.c
@@ -43,16 +43,8 @@
 #  define inline /* disable inline */
 #endif
 
-
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
-#else
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
index fb1ee35..47b5f42 100644
--- a/lib/decompress/zstd_decompress.c
+++ b/lib/decompress/zstd_decompress.c
@@ -28,7 +28,6 @@
 #  define ZSTD_LEGACY_SUPPORT 0
 #endif
 
-
 /*!
 *  MAXWINDOWSIZE_DEFAULT :
 *  maximum window size accepted by DStream, by default.
@@ -57,24 +56,6 @@
 #endif
 
 
-/*-*******************************************************
-*  Compiler specifics
-*********************************************************/
-#ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
-#  include <intrin.h>                    /* For Visual 2005 */
-#  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
-#  pragma warning(disable : 4324)        /* disable: C4324: padded structure */
-#  pragma warning(disable : 4100)        /* disable: C4100: unreferenced formal parameter */
-#else
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
-#endif
-
-
 /*-*************************************
 *  Macros
 ***************************************/
@@ -99,8 +80,12 @@ typedef enum { ZSTDds_getFrameHeaderSize, ZSTDds_decodeFrameHeader,
 
 struct ZSTD_DCtx_s
 {
+    const FSE_DTable* LLTptr;
+    const FSE_DTable* MLTptr;
+    const FSE_DTable* OFTptr;
+    const HUF_DTable* HUFptr;
     FSE_DTable LLTable[FSE_DTABLE_SIZE_U32(LLFSELog)];
-    FSE_DTable OffTable[FSE_DTABLE_SIZE_U32(OffFSELog)];
+    FSE_DTable OFTable[FSE_DTABLE_SIZE_U32(OffFSELog)];
     FSE_DTable MLTable[FSE_DTABLE_SIZE_U32(MLFSELog)];
     HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)];  /* can accommodate HUF_decompress4X */
     const void* previousDstEnd;
@@ -126,23 +111,27 @@ struct ZSTD_DCtx_s
     BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
 };  /* typedef'd to ZSTD_DCtx within "zstd.h" */
 
-size_t ZSTD_sizeof_DCtx (const ZSTD_DCtx* dctx) { return sizeof(*dctx); }
+size_t ZSTD_sizeof_DCtx (const ZSTD_DCtx* dctx) { if (dctx==NULL) return 0; return sizeof(ZSTD_DCtx); }  /* support sizeof on NULL */
 
 size_t ZSTD_estimateDCtxSize(void) { return sizeof(ZSTD_DCtx); }
 
 size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx)
 {
-    dctx->expected = ZSTD_frameHeaderSize_min;
+    dctx->expected = ZSTD_frameHeaderSize_prefix;
     dctx->stage = ZSTDds_getFrameHeaderSize;
     dctx->previousDstEnd = NULL;
     dctx->base = NULL;
     dctx->vBase = NULL;
     dctx->dictEnd = NULL;
-    dctx->hufTable[0] = (HUF_DTable)((HufLog)*0x1000001);
+    dctx->hufTable[0] = (HUF_DTable)((HufLog)*0x1000001);  /* cover both little and big endian */
     dctx->litEntropy = dctx->fseEntropy = 0;
     dctx->dictID = 0;
-    MEM_STATIC_ASSERT(sizeof(dctx->rep)==sizeof(repStartValue));
-    memcpy(dctx->rep, repStartValue, sizeof(repStartValue));
+    MEM_STATIC_ASSERT(sizeof(dctx->rep) == sizeof(repStartValue));
+    memcpy(dctx->rep, repStartValue, sizeof(repStartValue));  /* initial repcodes */
+    dctx->LLTptr = dctx->LLTable;
+    dctx->MLTptr = dctx->MLTable;
+    dctx->OFTptr = dctx->OFTable;
+    dctx->HUFptr = dctx->hufTable;
     return 0;
 }
 
@@ -153,7 +142,7 @@ ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem)
     if (!customMem.customAlloc && !customMem.customFree) customMem = defaultCustomMem;
     if (!customMem.customAlloc || !customMem.customFree) return NULL;
 
-    dctx = (ZSTD_DCtx*) ZSTD_malloc(sizeof(ZSTD_DCtx), customMem);
+    dctx = (ZSTD_DCtx*)ZSTD_malloc(sizeof(ZSTD_DCtx), customMem);
     if (!dctx) return NULL;
     memcpy(&dctx->customMem, &customMem, sizeof(customMem));
     ZSTD_decompressBegin(dctx);
@@ -178,6 +167,25 @@ void ZSTD_copyDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx)
     memcpy(dstDCtx, srcDCtx, sizeof(ZSTD_DCtx) - workSpaceSize);  /* no need to copy workspace */
 }
 
+static void ZSTD_refDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx)
+{
+    ZSTD_decompressBegin(dstDCtx);  /* init */
+    dstDCtx->dictEnd = srcDCtx->dictEnd;
+    dstDCtx->vBase = srcDCtx->vBase;
+    dstDCtx->base = srcDCtx->base;
+    dstDCtx->previousDstEnd = srcDCtx->previousDstEnd;
+    dstDCtx->dictID = srcDCtx->dictID;
+    dstDCtx->litEntropy = srcDCtx->litEntropy;
+    dstDCtx->fseEntropy = srcDCtx->fseEntropy;
+    dstDCtx->LLTptr = srcDCtx->LLTable;
+    dstDCtx->MLTptr = srcDCtx->MLTable;
+    dstDCtx->OFTptr = srcDCtx->OFTable;
+    dstDCtx->HUFptr = srcDCtx->hufTable;
+    dstDCtx->rep[0] = srcDCtx->rep[0];
+    dstDCtx->rep[1] = srcDCtx->rep[1];
+    dstDCtx->rep[2] = srcDCtx->rep[2];
+}
+
 
 /*-*************************************************************
 *   Decompression section
@@ -186,16 +194,16 @@ void ZSTD_copyDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx)
 /* See compression format details in : zstd_compression_format.md */
 
 /** ZSTD_frameHeaderSize() :
-*   srcSize must be >= ZSTD_frameHeaderSize_min.
+*   srcSize must be >= ZSTD_frameHeaderSize_prefix.
 *   @return : size of the Frame Header */
 static size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize)
 {
-    if (srcSize < ZSTD_frameHeaderSize_min) return ERROR(srcSize_wrong);
+    if (srcSize < ZSTD_frameHeaderSize_prefix) return ERROR(srcSize_wrong);
     {   BYTE const fhd = ((const BYTE*)src)[4];
         U32 const dictID= fhd & 3;
         U32 const singleSegment = (fhd >> 5) & 1;
         U32 const fcsId = fhd >> 6;
-        return ZSTD_frameHeaderSize_min + !singleSegment + ZSTD_did_fieldSize[dictID] + ZSTD_fcs_fieldSize[fcsId]
+        return ZSTD_frameHeaderSize_prefix + !singleSegment + ZSTD_did_fieldSize[dictID] + ZSTD_fcs_fieldSize[fcsId]
                 + (singleSegment && !fcsId);
     }
 }
@@ -210,7 +218,7 @@ size_t ZSTD_getFrameParams(ZSTD_frameParams* fparamsPtr, const void* src, size_t
 {
     const BYTE* ip = (const BYTE*)src;
 
-    if (srcSize < ZSTD_frameHeaderSize_min) return ZSTD_frameHeaderSize_min;
+    if (srcSize < ZSTD_frameHeaderSize_prefix) return ZSTD_frameHeaderSize_prefix;
     if (MEM_readLE32(src) != ZSTD_MAGICNUMBER) {
         if ((MEM_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) {
             if (srcSize < ZSTD_skippableHeaderSize) return ZSTD_skippableHeaderSize; /* magic number + skippable frame length */
@@ -369,34 +377,31 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
                 {
                 case 0: case 1: default:   /* note : default is impossible, since lhlCode into [0..3] */
                     /* 2 - 2 - 10 - 10 */
-                    {   singleStream = !lhlCode;
-                        lhSize = 3;
-                        litSize  = (lhc >> 4) & 0x3FF;
-                        litCSize = (lhc >> 14) & 0x3FF;
-                        break;
-                    }
+                    singleStream = !lhlCode;
+                    lhSize = 3;
+                    litSize  = (lhc >> 4) & 0x3FF;
+                    litCSize = (lhc >> 14) & 0x3FF;
+                    break;
                 case 2:
                     /* 2 - 2 - 14 - 14 */
-                    {   lhSize = 4;
-                        litSize  = (lhc >> 4) & 0x3FFF;
-                        litCSize = lhc >> 18;
-                        break;
-                    }
+                    lhSize = 4;
+                    litSize  = (lhc >> 4) & 0x3FFF;
+                    litCSize = lhc >> 18;
+                    break;
                 case 3:
                     /* 2 - 2 - 18 - 18 */
-                    {   lhSize = 5;
-                        litSize  = (lhc >> 4) & 0x3FFFF;
-                        litCSize = (lhc >> 22) + (istart[4] << 10);
-                        break;
-                    }
+                    lhSize = 5;
+                    litSize  = (lhc >> 4) & 0x3FFFF;
+                    litCSize = (lhc >> 22) + (istart[4] << 10);
+                    break;
                 }
                 if (litSize > ZSTD_BLOCKSIZE_ABSOLUTEMAX) return ERROR(corruption_detected);
                 if (litCSize + lhSize > srcSize) return ERROR(corruption_detected);
 
                 if (HUF_isError((litEncType==set_repeat) ?
                                     ( singleStream ?
-                                        HUF_decompress1X_usingDTable(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->hufTable) :
-                                        HUF_decompress4X_usingDTable(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->hufTable) ) :
+                                        HUF_decompress1X_usingDTable(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr) :
+                                        HUF_decompress4X_usingDTable(dctx->litBuffer, litSize, istart+lhSize, litCSize, dctx->HUFptr) ) :
                                     ( singleStream ?
                                         HUF_decompress1X2_DCtx(dctx->hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize) :
                                         HUF_decompress4X_hufOnly (dctx->hufTable, dctx->litBuffer, litSize, istart+lhSize, litCSize)) ))
@@ -406,6 +411,7 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
                 dctx->litBufSize = ZSTD_BLOCKSIZE_ABSOLUTEMAX+WILDCOPY_OVERLENGTH;
                 dctx->litSize = litSize;
                 dctx->litEntropy = 1;
+                if (litEncType==set_compressed) dctx->HUFptr = dctx->hufTable;
                 return litCSize + lhSize;
             }
 
@@ -472,28 +478,207 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
         default:
             return ERROR(corruption_detected);   /* impossible */
         }
-
     }
 }
 
 
+typedef union {
+    FSE_decode_t realData;
+    U32 alignedBy4;
+} FSE_decode_t4;
+
+static const FSE_decode_t4 LL_defaultDTable[(1<<LL_DEFAULTNORMLOG)+1] = {
+    { { LL_DEFAULTNORMLOG, 1, 1 } }, /* header : tableLog, fastMode, fastMode */
+    { {  0,  0,  4 } },              /* 0 : base, symbol, bits */
+    { { 16,  0,  4 } },
+    { { 32,  1,  5 } },
+    { {  0,  3,  5 } },
+    { {  0,  4,  5 } },
+    { {  0,  6,  5 } },
+    { {  0,  7,  5 } },
+    { {  0,  9,  5 } },
+    { {  0, 10,  5 } },
+    { {  0, 12,  5 } },
+    { {  0, 14,  6 } },
+    { {  0, 16,  5 } },
+    { {  0, 18,  5 } },
+    { {  0, 19,  5 } },
+    { {  0, 21,  5 } },
+    { {  0, 22,  5 } },
+    { {  0, 24,  5 } },
+    { { 32, 25,  5 } },
+    { {  0, 26,  5 } },
+    { {  0, 27,  6 } },
+    { {  0, 29,  6 } },
+    { {  0, 31,  6 } },
+    { { 32,  0,  4 } },
+    { {  0,  1,  4 } },
+    { {  0,  2,  5 } },
+    { { 32,  4,  5 } },
+    { {  0,  5,  5 } },
+    { { 32,  7,  5 } },
+    { {  0,  8,  5 } },
+    { { 32, 10,  5 } },
+    { {  0, 11,  5 } },
+    { {  0, 13,  6 } },
+    { { 32, 16,  5 } },
+    { {  0, 17,  5 } },
+    { { 32, 19,  5 } },
+    { {  0, 20,  5 } },
+    { { 32, 22,  5 } },
+    { {  0, 23,  5 } },
+    { {  0, 25,  4 } },
+    { { 16, 25,  4 } },
+    { { 32, 26,  5 } },
+    { {  0, 28,  6 } },
+    { {  0, 30,  6 } },
+    { { 48,  0,  4 } },
+    { { 16,  1,  4 } },
+    { { 32,  2,  5 } },
+    { { 32,  3,  5 } },
+    { { 32,  5,  5 } },
+    { { 32,  6,  5 } },
+    { { 32,  8,  5 } },
+    { { 32,  9,  5 } },
+    { { 32, 11,  5 } },
+    { { 32, 12,  5 } },
+    { {  0, 15,  6 } },
+    { { 32, 17,  5 } },
+    { { 32, 18,  5 } },
+    { { 32, 20,  5 } },
+    { { 32, 21,  5 } },
+    { { 32, 23,  5 } },
+    { { 32, 24,  5 } },
+    { {  0, 35,  6 } },
+    { {  0, 34,  6 } },
+    { {  0, 33,  6 } },
+    { {  0, 32,  6 } },
+};   /* LL_defaultDTable */
+
+static const FSE_decode_t4 ML_defaultDTable[(1<<ML_DEFAULTNORMLOG)+1] = {
+    { { ML_DEFAULTNORMLOG, 1, 1 } }, /* header : tableLog, fastMode, fastMode */
+    { {  0,  0,  6 } },              /* 0 : base, symbol, bits */
+    { {  0,  1,  4 } },
+    { { 32,  2,  5 } },
+    { {  0,  3,  5 } },
+    { {  0,  5,  5 } },
+    { {  0,  6,  5 } },
+    { {  0,  8,  5 } },
+    { {  0, 10,  6 } },
+    { {  0, 13,  6 } },
+    { {  0, 16,  6 } },
+    { {  0, 19,  6 } },
+    { {  0, 22,  6 } },
+    { {  0, 25,  6 } },
+    { {  0, 28,  6 } },
+    { {  0, 31,  6 } },
+    { {  0, 33,  6 } },
+    { {  0, 35,  6 } },
+    { {  0, 37,  6 } },
+    { {  0, 39,  6 } },
+    { {  0, 41,  6 } },
+    { {  0, 43,  6 } },
+    { {  0, 45,  6 } },
+    { { 16,  1,  4 } },
+    { {  0,  2,  4 } },
+    { { 32,  3,  5 } },
+    { {  0,  4,  5 } },
+    { { 32,  6,  5 } },
+    { {  0,  7,  5 } },
+    { {  0,  9,  6 } },
+    { {  0, 12,  6 } },
+    { {  0, 15,  6 } },
+    { {  0, 18,  6 } },
+    { {  0, 21,  6 } },
+    { {  0, 24,  6 } },
+    { {  0, 27,  6 } },
+    { {  0, 30,  6 } },
+    { {  0, 32,  6 } },
+    { {  0, 34,  6 } },
+    { {  0, 36,  6 } },
+    { {  0, 38,  6 } },
+    { {  0, 40,  6 } },
+    { {  0, 42,  6 } },
+    { {  0, 44,  6 } },
+    { { 32,  1,  4 } },
+    { { 48,  1,  4 } },
+    { { 16,  2,  4 } },
+    { { 32,  4,  5 } },
+    { { 32,  5,  5 } },
+    { { 32,  7,  5 } },
+    { { 32,  8,  5 } },
+    { {  0, 11,  6 } },
+    { {  0, 14,  6 } },
+    { {  0, 17,  6 } },
+    { {  0, 20,  6 } },
+    { {  0, 23,  6 } },
+    { {  0, 26,  6 } },
+    { {  0, 29,  6 } },
+    { {  0, 52,  6 } },
+    { {  0, 51,  6 } },
+    { {  0, 50,  6 } },
+    { {  0, 49,  6 } },
+    { {  0, 48,  6 } },
+    { {  0, 47,  6 } },
+    { {  0, 46,  6 } },
+};   /* ML_defaultDTable */
+
+static const FSE_decode_t4 OF_defaultDTable[(1<<OF_DEFAULTNORMLOG)+1] = {
+    { { OF_DEFAULTNORMLOG, 1, 1 } }, /* header : tableLog, fastMode, fastMode */
+    { {  0,  0,  5 } },              /* 0 : base, symbol, bits */
+    { {  0,  6,  4 } },
+    { {  0,  9,  5 } },
+    { {  0, 15,  5 } },
+    { {  0, 21,  5 } },
+    { {  0,  3,  5 } },
+    { {  0,  7,  4 } },
+    { {  0, 12,  5 } },
+    { {  0, 18,  5 } },
+    { {  0, 23,  5 } },
+    { {  0,  5,  5 } },
+    { {  0,  8,  4 } },
+    { {  0, 14,  5 } },
+    { {  0, 20,  5 } },
+    { {  0,  2,  5 } },
+    { { 16,  7,  4 } },
+    { {  0, 11,  5 } },
+    { {  0, 17,  5 } },
+    { {  0, 22,  5 } },
+    { {  0,  4,  5 } },
+    { { 16,  8,  4 } },
+    { {  0, 13,  5 } },
+    { {  0, 19,  5 } },
+    { {  0,  1,  5 } },
+    { { 16,  6,  4 } },
+    { {  0, 10,  5 } },
+    { {  0, 16,  5 } },
+    { {  0, 28,  5 } },
+    { {  0, 27,  5 } },
+    { {  0, 26,  5 } },
+    { {  0, 25,  5 } },
+    { {  0, 24,  5 } },
+};   /* OF_defaultDTable */
+
 /*! ZSTD_buildSeqTable() :
     @return : nb bytes read from src,
               or an error code if it fails, testable with ZSTD_isError()
 */
-FORCE_INLINE size_t ZSTD_buildSeqTable(FSE_DTable* DTable, symbolEncodingType_e type, U32 max, U32 maxLog,
+static size_t ZSTD_buildSeqTable(FSE_DTable* DTableSpace, const FSE_DTable** DTablePtr,
+                                 symbolEncodingType_e type, U32 max, U32 maxLog,
                                  const void* src, size_t srcSize,
-                                 const S16* defaultNorm, U32 defaultLog, U32 flagRepeatTable)
+                                 const FSE_decode_t4* defaultTable, U32 flagRepeatTable)
 {
+    const void* const tmpPtr = defaultTable;   /* bypass strict aliasing */
     switch(type)
     {
     case set_rle :
         if (!srcSize) return ERROR(srcSize_wrong);
         if ( (*(const BYTE*)src) > 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);
@@ -505,14 +690,13 @@ FORCE_INLINE size_t ZSTD_buildSeqTable(FSE_DTable* DTable, symbolEncodingType_e
             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(int* nbSeqPtr,
-                             FSE_DTable* DTableLL, FSE_DTable* DTableML, FSE_DTable* DTableOffb, U32 flagRepeatTable,
+size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr,
                              const void* src, size_t srcSize)
 {
     const BYTE* const istart = (const BYTE* const)src;
@@ -542,18 +726,25 @@ size_t ZSTD_decodeSeqHeaders(int* nbSeqPtr,
         ip++;
 
         /* Build DTables */
-        {   size_t const llhSize = ZSTD_buildSeqTable(DTableLL, LLtype, MaxLL, LLFSELog, ip, iend-ip, LL_defaultNorm, LL_defaultNormLog, flagRepeatTable);
+        {   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);
             ip += llhSize;
         }
-        {   size_t const ofhSize = ZSTD_buildSeqTable(DTableOffb, OFtype, MaxOff, OffFSELog, ip, iend-ip, OF_defaultNorm, OF_defaultNormLog, flagRepeatTable);
+        {   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);
             ip += ofhSize;
         }
-        {   size_t const mlhSize = ZSTD_buildSeqTable(DTableML, MLtype, MaxML, MLFSELog, ip, iend-ip, ML_defaultNorm, ML_defaultNormLog, flagRepeatTable);
+        {   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);
             ip += mlhSize;
-    }   }
+        }
+    }
 
     return ip-istart;
 }
@@ -734,16 +925,13 @@ static size_t ZSTD_decompressSequences(
     const BYTE* litPtr = dctx->litPtr;
     const BYTE* const litLimit_w = litPtr + dctx->litBufSize - WILDCOPY_OVERLENGTH;
     const BYTE* const litEnd = litPtr + dctx->litSize;
-    FSE_DTable* DTableLL = dctx->LLTable;
-    FSE_DTable* DTableML = dctx->MLTable;
-    FSE_DTable* DTableOffb = dctx->OffTable;
     const BYTE* const base = (const BYTE*) (dctx->base);
     const BYTE* const vBase = (const BYTE*) (dctx->vBase);
     const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd);
     int nbSeq;
 
     /* Build Decoding Tables */
-    {   size_t const seqHSize = ZSTD_decodeSeqHeaders(&nbSeq, DTableLL, DTableML, DTableOffb, dctx->fseEntropy, ip, seqSize);
+    {   size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize);
         if (ZSTD_isError(seqHSize)) return seqHSize;
         ip += seqHSize;
     }
@@ -753,11 +941,10 @@ static size_t ZSTD_decompressSequences(
         seqState_t seqState;
         dctx->fseEntropy = 1;
         { U32 i; for (i=0; i<ZSTD_REP_NUM; i++) seqState.prevOffset[i] = dctx->rep[i]; }
-        { size_t const errorCode = BIT_initDStream(&(seqState.DStream), ip, iend-ip);
-          if (ERR_isError(errorCode)) return ERROR(corruption_detected); }
-        FSE_initDState(&(seqState.stateLL), &(seqState.DStream), DTableLL);
-        FSE_initDState(&(seqState.stateOffb), &(seqState.DStream), DTableOffb);
-        FSE_initDState(&(seqState.stateML), &(seqState.DStream), DTableML);
+        CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected);
+        FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr);
+        FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
+        FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
 
         for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) {
             nbSeq--;
@@ -859,12 +1046,10 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx,
     if (srcSize < ZSTD_frameHeaderSize_min+ZSTD_blockHeaderSize) return ERROR(srcSize_wrong);
 
     /* Frame Header */
-    {   size_t const frameHeaderSize = ZSTD_frameHeaderSize(src, ZSTD_frameHeaderSize_min);
-        size_t result;
+    {   size_t const frameHeaderSize = ZSTD_frameHeaderSize(src, ZSTD_frameHeaderSize_prefix);
         if (ZSTD_isError(frameHeaderSize)) return frameHeaderSize;
         if (srcSize < frameHeaderSize+ZSTD_blockHeaderSize) return ERROR(srcSize_wrong);
-        result = ZSTD_decodeFrameHeader(dctx, src, frameHeaderSize);
-        if (ZSTD_isError(result)) return result;
+        CHECK_F(ZSTD_decodeFrameHeader(dctx, src, frameHeaderSize));
         ip += frameHeaderSize; remainingSize -= frameHeaderSize;
     }
 
@@ -917,25 +1102,10 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx,
 }
 
 
-/*! ZSTD_decompress_usingPreparedDCtx() :
-*   Same as ZSTD_decompress_usingDict, but using a reference context `preparedDCtx`, where dictionary has been loaded.
-*   It avoids reloading the dictionary each time.
-*   `preparedDCtx` must have been properly initialized using ZSTD_decompressBegin_usingDict().
-*   Requires 2 contexts : 1 for reference (preparedDCtx), which will not be modified, and 1 to run the decompression operation (dctx) */
-size_t ZSTD_decompress_usingPreparedDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* refDCtx,
-                                         void* dst, size_t dstCapacity,
-                                   const void* src, size_t srcSize)
-{
-    ZSTD_copyDCtx(dctx, refDCtx);
-    ZSTD_checkContinuity(dctx, dst);
-    return ZSTD_decompressFrame(dctx, dst, dstCapacity, src, srcSize);
-}
-
-
 size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx,
                                  void* dst, size_t dstCapacity,
-                                 const void* src, size_t srcSize,
-                                 const void* dict, size_t dictSize)
+                           const void* src, size_t srcSize,
+                           const void* dict, size_t dictSize)
 {
 #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT==1)
     if (ZSTD_isLegacy(src, srcSize)) return ZSTD_decompressLegacy(dst, dstCapacity, src, srcSize, dict, dictSize);
@@ -1009,32 +1179,30 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c
     switch (dctx->stage)
     {
     case ZSTDds_getFrameHeaderSize :
-        if (srcSize != ZSTD_frameHeaderSize_min) return ERROR(srcSize_wrong);   /* impossible */
-        if ((MEM_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) {
-            memcpy(dctx->headerBuffer, src, ZSTD_frameHeaderSize_min);
-            dctx->expected = ZSTD_skippableHeaderSize - ZSTD_frameHeaderSize_min; /* magic number + skippable frame length */
+        if (srcSize != ZSTD_frameHeaderSize_prefix) return ERROR(srcSize_wrong);      /* impossible */
+        if ((MEM_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) {        /* skippable frame */
+            memcpy(dctx->headerBuffer, src, ZSTD_frameHeaderSize_prefix);
+            dctx->expected = ZSTD_skippableHeaderSize - ZSTD_frameHeaderSize_prefix;  /* magic number + skippable frame length */
             dctx->stage = ZSTDds_decodeSkippableHeader;
             return 0;
         }
-        dctx->headerSize = ZSTD_frameHeaderSize(src, ZSTD_frameHeaderSize_min);
+        dctx->headerSize = ZSTD_frameHeaderSize(src, ZSTD_frameHeaderSize_prefix);
         if (ZSTD_isError(dctx->headerSize)) return dctx->headerSize;
-        memcpy(dctx->headerBuffer, src, ZSTD_frameHeaderSize_min);
-        if (dctx->headerSize > ZSTD_frameHeaderSize_min) {
-            dctx->expected = dctx->headerSize - ZSTD_frameHeaderSize_min;
+        memcpy(dctx->headerBuffer, src, ZSTD_frameHeaderSize_prefix);
+        if (dctx->headerSize > ZSTD_frameHeaderSize_prefix) {
+            dctx->expected = dctx->headerSize - ZSTD_frameHeaderSize_prefix;
             dctx->stage = ZSTDds_decodeFrameHeader;
             return 0;
         }
         dctx->expected = 0;   /* not necessary to copy more */
 
     case ZSTDds_decodeFrameHeader:
-        {   size_t result;
-            memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_min, src, dctx->expected);
-            result = ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize);
-            if (ZSTD_isError(result)) return result;
-            dctx->expected = ZSTD_blockHeaderSize;
-            dctx->stage = ZSTDds_decodeBlockHeader;
-            return 0;
-        }
+        memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_prefix, src, dctx->expected);
+        CHECK_F(ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize));
+        dctx->expected = ZSTD_blockHeaderSize;
+        dctx->stage = ZSTDds_decodeBlockHeader;
+        return 0;
+
     case ZSTDds_decodeBlockHeader:
         {   blockProperties_t bp;
             size_t const cBlockSize = ZSTD_getcBlockSize(src, ZSTD_blockHeaderSize, &bp);
@@ -1106,7 +1274,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c
             return 0;
         }
     case ZSTDds_decodeSkippableHeader:
-        {   memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_min, src, dctx->expected);
+        {   memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_prefix, src, dctx->expected);
             dctx->expected = MEM_readLE32(dctx->headerBuffer + 4);
             dctx->stage = ZSTDds_skipFrame;
             return 0;
@@ -1145,8 +1313,7 @@ static size_t ZSTD_loadEntropy(ZSTD_DCtx* dctx, const void* const dict, size_t c
         U32 offcodeMaxValue=MaxOff, offcodeLog=OffFSELog;
         size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr);
         if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted);
-        { size_t const errorCode = FSE_buildDTable(dctx->OffTable, offcodeNCount, offcodeMaxValue, offcodeLog);
-          if (FSE_isError(errorCode)) return ERROR(dictionary_corrupted); }
+        CHECK_E(FSE_buildDTable(dctx->OFTable, offcodeNCount, offcodeMaxValue, offcodeLog), dictionary_corrupted);
         dictPtr += offcodeHeaderSize;
     }
 
@@ -1154,8 +1321,7 @@ static size_t ZSTD_loadEntropy(ZSTD_DCtx* dctx, const void* const dict, size_t c
         unsigned matchlengthMaxValue = MaxML, matchlengthLog = MLFSELog;
         size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr);
         if (FSE_isError(matchlengthHeaderSize)) return ERROR(dictionary_corrupted);
-        { size_t const errorCode = FSE_buildDTable(dctx->MLTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog);
-          if (FSE_isError(errorCode)) return ERROR(dictionary_corrupted); }
+        CHECK_E(FSE_buildDTable(dctx->MLTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog), dictionary_corrupted);
         dictPtr += matchlengthHeaderSize;
     }
 
@@ -1163,8 +1329,7 @@ static size_t ZSTD_loadEntropy(ZSTD_DCtx* dctx, const void* const dict, size_t c
         unsigned litlengthMaxValue = MaxLL, litlengthLog = LLFSELog;
         size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr);
         if (FSE_isError(litlengthHeaderSize)) return ERROR(dictionary_corrupted);
-        { size_t const errorCode = FSE_buildDTable(dctx->LLTable, litlengthNCount, litlengthMaxValue, litlengthLog);
-          if (FSE_isError(errorCode)) return ERROR(dictionary_corrupted); }
+        CHECK_E(FSE_buildDTable(dctx->LLTable, litlengthNCount, litlengthMaxValue, litlengthLog), dictionary_corrupted);
         dictPtr += litlengthHeaderSize;
     }
 
@@ -1200,21 +1365,16 @@ static size_t ZSTD_decompress_insertDictionary(ZSTD_DCtx* dctx, const void* dict
     return ZSTD_refDictContent(dctx, dict, dictSize);
 }
 
-
 size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize)
 {
-    { size_t const errorCode = ZSTD_decompressBegin(dctx);
-      if (ZSTD_isError(errorCode)) return errorCode; }
-
-    if (dict && dictSize) {
-        size_t const errorCode = ZSTD_decompress_insertDictionary(dctx, dict, dictSize);
-        if (ZSTD_isError(errorCode)) return ERROR(dictionary_corrupted);
-    }
-
+    CHECK_F(ZSTD_decompressBegin(dctx));
+    if (dict && dictSize) CHECK_E(ZSTD_decompress_insertDictionary(dctx, dict, dictSize), dictionary_corrupted);
     return 0;
 }
 
 
+/* ======   ZSTD_DDict   ====== */
+
 struct ZSTD_DDict_s {
     void* dict;
     size_t dictSize;
@@ -1273,20 +1433,27 @@ size_t ZSTD_freeDDict(ZSTD_DDict* ddict)
     }
 }
 
+size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict)
+{
+    if (ddict==NULL) return 0;   /* support sizeof on NULL */
+    return sizeof(*ddict) + sizeof(ddict->refContext) + ddict->dictSize;
+}
+
+
 /*! ZSTD_decompress_usingDDict() :
 *   Decompression using a pre-digested Dictionary
 *   Use dictionary without significant overhead. */
-ZSTDLIB_API size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx,
-                                           void* dst, size_t dstCapacity,
-                                     const void* src, size_t srcSize,
-                                     const ZSTD_DDict* ddict)
+size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx,
+                                  void* dst, size_t dstCapacity,
+                            const void* src, size_t srcSize,
+                            const ZSTD_DDict* ddict)
 {
 #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT==1)
     if (ZSTD_isLegacy(src, srcSize)) return ZSTD_decompressLegacy(dst, dstCapacity, src, srcSize, ddict->dict, ddict->dictSize);
 #endif
-    return ZSTD_decompress_usingPreparedDCtx(dctx, ddict->refContext,
-                                             dst, dstCapacity,
-                                             src, srcSize);
+    ZSTD_refDCtx(dctx, ddict->refContext);
+    ZSTD_checkContinuity(dctx, dst);
+    return ZSTD_decompressFrame(dctx, dst, dstCapacity, src, srcSize);
 }
 
 
@@ -1299,7 +1466,8 @@ typedef enum { zdss_init, zdss_loadHeader,
 
 /* *** Resource management *** */
 struct ZSTD_DStream_s {
-    ZSTD_DCtx* zd;
+    ZSTD_DCtx* dctx;
+    ZSTD_DDict* ddict;
     ZSTD_frameParams fParams;
     ZSTD_dStreamStage stage;
     char*  inBuff;
@@ -1311,15 +1479,13 @@ struct ZSTD_DStream_s {
     size_t outStart;
     size_t outEnd;
     size_t blockSize;
-    BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
+    BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];   /* tmp buffer to store frame header */
     size_t lhSize;
     ZSTD_customMem customMem;
-    void* dictContent;
-    size_t dictSize;
-    const void* dictSource;
     void* legacyContext;
     U32 previousLegacyVersion;
     U32 legacyVersion;
+    U32 hostageByte;
 };   /* typedef'd to ZSTD_DStream within "zstd.h" */
 
 
@@ -1339,8 +1505,8 @@ ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem)
     if (zds==NULL) return NULL;
     memset(zds, 0, sizeof(ZSTD_DStream));
     memcpy(&zds->customMem, &customMem, sizeof(ZSTD_customMem));
-    zds->zd = ZSTD_createDCtx_advanced(customMem);
-    if (zds->zd == NULL) { ZSTD_freeDStream(zds); return NULL; }
+    zds->dctx = ZSTD_createDCtx_advanced(customMem);
+    if (zds->dctx == NULL) { ZSTD_freeDStream(zds); return NULL; }
     zds->stage = zdss_init;
     zds->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT;
     return zds;
@@ -1350,10 +1516,10 @@ size_t ZSTD_freeDStream(ZSTD_DStream* zds)
 {
     if (zds==NULL) return 0;   /* support free on null */
     {   ZSTD_customMem const cMem = zds->customMem;
-        ZSTD_freeDCtx(zds->zd);
+        ZSTD_freeDCtx(zds->dctx);
+        ZSTD_freeDDict(zds->ddict);
         ZSTD_free(zds->inBuff, cMem);
         ZSTD_free(zds->outBuff, cMem);
-        ZSTD_free(zds->dictContent, cMem);
 #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
         if (zds->legacyContext)
             ZSTD_freeLegacyStreamContext(zds->legacyContext, zds->previousLegacyVersion);
@@ -1373,17 +1539,12 @@ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t di
 {
     zds->stage = zdss_loadHeader;
     zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0;
-    if ((dict != zds->dictSource) | (dictSize != zds->dictSize)) {   /* new dictionary */
-        if (dictSize > zds->dictSize) {
-            ZSTD_free(zds->dictContent, zds->customMem);
-            zds->dictContent = ZSTD_malloc(dictSize, zds->customMem);
-            if (zds->dictContent == NULL) return ERROR(memory_allocation);
-        }
-        memcpy(zds->dictContent, dict, dictSize);
-        zds->dictSize = dictSize;
-    }
+    ZSTD_freeDDict(zds->ddict);
+    zds->ddict = ZSTD_createDDict(dict, dictSize);
+    if (zds->ddict == NULL) return ERROR(memory_allocation);
     zds->legacyVersion = 0;
-    return 0;
+    zds->hostageByte = 0;
+    return ZSTD_frameHeaderSize_prefix;
 }
 
 size_t ZSTD_initDStream(ZSTD_DStream* zds)
@@ -1391,6 +1552,16 @@ size_t ZSTD_initDStream(ZSTD_DStream* zds)
     return ZSTD_initDStream_usingDict(zds, NULL, 0);
 }
 
+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;
+    zds->hostageByte = 0;
+    return ZSTD_frameHeaderSize_prefix;
+}
+
 size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds,
                                 ZSTD_DStreamParameter_e paramType, unsigned paramValue)
 {
@@ -1405,11 +1576,12 @@ size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds,
 
 size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds)
 {
-    return sizeof(*zds) + ZSTD_sizeof_DCtx(zds->zd) + zds->inBuffSize + zds->outBuffSize;
+    if (zds==NULL) return 0;   /* support sizeof on NULL */
+    return sizeof(*zds) + ZSTD_sizeof_DCtx(zds->dctx) + ZSTD_sizeof_DDict(zds->ddict) + zds->inBuffSize + zds->outBuffSize;
 }
 
 
-/* *** Decompression *** */
+/* *****   Decompression   ***** */
 
 MEM_STATIC size_t ZSTD_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize)
 {
@@ -1446,10 +1618,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
 #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
                 {   U32 const legacyVersion = ZSTD_isLegacy(istart, iend-istart);
                     if (legacyVersion) {
-                        size_t initResult;
-                        initResult = ZSTD_initLegacyStream(&zds->legacyContext, zds->previousLegacyVersion, legacyVersion,
-                                                           zds->dictContent, zds->dictSize);
-                        if (ZSTD_isError(initResult)) return initResult;
+                        CHECK_F(ZSTD_initLegacyStream(&zds->legacyContext, zds->previousLegacyVersion, legacyVersion,
+                                                       zds->ddict->dict, zds->ddict->dictSize));
                         zds->legacyVersion = zds->previousLegacyVersion = legacyVersion;
                         return ZSTD_decompressLegacyStream(zds->legacyContext, zds->legacyVersion, output, input);
                     } else {
@@ -1464,27 +1634,24 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
                         memcpy(zds->headerBuffer + zds->lhSize, ip, iend-ip);
                         zds->lhSize += iend-ip;
                         input->pos = input->size;
-                        return (hSize - zds->lhSize) + ZSTD_blockHeaderSize;   /* remaining header bytes + next block header */
+                        return (MAX(ZSTD_frameHeaderSize_min, hSize) - zds->lhSize) + ZSTD_blockHeaderSize;   /* remaining header bytes + next block header */
                     }
                     memcpy(zds->headerBuffer + zds->lhSize, ip, toLoad); zds->lhSize = hSize; ip += toLoad;
                     break;
             }   }
 
             /* Consume header */
-            ZSTD_decompressBegin_usingDict(zds->zd, zds->dictContent, zds->dictSize);
-            {   size_t const h1Size = ZSTD_nextSrcSizeToDecompress(zds->zd);  /* == ZSTD_frameHeaderSize_min */
-                size_t const h1Result = ZSTD_decompressContinue(zds->zd, NULL, 0, zds->headerBuffer, h1Size);
-                if (ZSTD_isError(h1Result)) return h1Result;   /* should not happen : already checked */
-                if (h1Size < zds->lhSize) {   /* long header */
-                    size_t const h2Size = ZSTD_nextSrcSizeToDecompress(zds->zd);
-                    size_t const h2Result = ZSTD_decompressContinue(zds->zd, NULL, 0, zds->headerBuffer+h1Size, h2Size);
-                    if (ZSTD_isError(h2Result)) return h2Result;
+            ZSTD_refDCtx(zds->dctx, zds->ddict->refContext);
+            {   size_t const h1Size = ZSTD_nextSrcSizeToDecompress(zds->dctx);  /* == ZSTD_frameHeaderSize_prefix */
+                CHECK_F(ZSTD_decompressContinue(zds->dctx, NULL, 0, zds->headerBuffer, h1Size));
+                {   size_t const h2Size = ZSTD_nextSrcSizeToDecompress(zds->dctx);
+                    CHECK_F(ZSTD_decompressContinue(zds->dctx, NULL, 0, zds->headerBuffer+h1Size, h2Size));
             }   }
 
             zds->fParams.windowSize = MAX(zds->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN);
             if (zds->fParams.windowSize > zds->maxWindowSize) return ERROR(frameParameter_unsupported);
 
-            /* Frame header instruct buffer sizes */
+            /* Adapt buffer sizes to frame header instructions */
             {   size_t const blockSize = MIN(zds->fParams.windowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX);
                 size_t const neededOutSize = zds->fParams.windowSize + blockSize;
                 zds->blockSize = blockSize;
@@ -1504,21 +1671,21 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
             /* pass-through */
 
         case zdss_read:
-            {   size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->zd);
+            {   size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->dctx);
                 if (neededInSize==0) {  /* end of frame */
                     zds->stage = zdss_init;
                     someMoreWork = 0;
                     break;
                 }
                 if ((size_t)(iend-ip) >= neededInSize) {  /* decode directly from src */
-                    const int isSkipFrame = ZSTD_isSkipFrame(zds->zd);
-                    size_t const decodedSize = ZSTD_decompressContinue(zds->zd,
+                    const int isSkipFrame = ZSTD_isSkipFrame(zds->dctx);
+                    size_t const decodedSize = ZSTD_decompressContinue(zds->dctx,
                         zds->outBuff + zds->outStart, (isSkipFrame ? 0 : zds->outBuffSize - zds->outStart),
                         ip, neededInSize);
                     if (ZSTD_isError(decodedSize)) return decodedSize;
                     ip += neededInSize;
                     if (!decodedSize && !isSkipFrame) break;   /* this was just a header */
-                    zds->outEnd = zds->outStart +  decodedSize;
+                    zds->outEnd = zds->outStart + decodedSize;
                     zds->stage = zdss_flush;
                     break;
                 }
@@ -1528,7 +1695,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
             }
 
         case zdss_load:
-            {   size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->zd);
+            {   size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->dctx);
                 size_t const toLoad = neededInSize - zds->inPos;   /* should always be <= remaining space within inBuff */
                 size_t loadedSize;
                 if (toLoad > zds->inBuffSize - zds->inPos) return ERROR(corruption_detected);   /* should never happen */
@@ -1538,8 +1705,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
                 if (loadedSize < toLoad) { someMoreWork = 0; break; }   /* not enough input, wait for more */
 
                 /* decode loaded input */
-                {  const int isSkipFrame = ZSTD_isSkipFrame(zds->zd);
-                   size_t const decodedSize = ZSTD_decompressContinue(zds->zd,
+                {  const int isSkipFrame = ZSTD_isSkipFrame(zds->dctx);
+                   size_t const decodedSize = ZSTD_decompressContinue(zds->dctx,
                         zds->outBuff + zds->outStart, zds->outBuffSize - zds->outStart,
                         zds->inBuff, neededInSize);
                     if (ZSTD_isError(decodedSize)) return decodedSize;
@@ -1561,7 +1728,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
                         zds->outStart = zds->outEnd = 0;
                     break;
                 }
-                /* cannot flush everything */
+                /* cannot complete flush */
                 someMoreWork = 0;
                 break;
             }
@@ -1571,9 +1738,22 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
     /* result */
     input->pos += (size_t)(ip-istart);
     output->pos += (size_t)(op-ostart);
-    {   size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zds->zd);
-        if (!nextSrcSizeHint) return (zds->outEnd != zds->outStart);   /* return 0 only if fully flushed too */
-        nextSrcSizeHint += ZSTD_blockHeaderSize * (ZSTD_nextInputType(zds->zd) == ZSTDnit_block);
+    {   size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zds->dctx);
+        if (!nextSrcSizeHint) {   /* frame fully decoded */
+            if (zds->outEnd == zds->outStart) {  /* output fully flushed */
+                if (zds->hostageByte) {
+                    if (input->pos >= input->size) { zds->stage = zdss_read; return 1; }  /* can't release hostage (not present) */
+                    input->pos++;  /* release hostage */
+                }
+                return 0;
+            }
+            if (!zds->hostageByte) { /* output not fully flushed; keep last byte as hostage; will be released when all output is flushed */
+                input->pos--;   /* note : pos > 0, otherwise, impossible to finish reading last block */
+                zds->hostageByte=1;
+            }
+            return 1;
+        }
+        nextSrcSizeHint += ZSTD_blockHeaderSize * (ZSTD_nextInputType(zds->dctx) == ZSTDnit_block);   /* preload header of next block */
         if (zds->inPos > nextSrcSizeHint) return ERROR(GENERIC);   /* should never happen */
         nextSrcSizeHint -= zds->inPos;   /* already loaded*/
         return nextSrcSizeHint;
diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c
index adfe55c..47a82af 100644
--- a/lib/dictBuilder/zdict.c
+++ b/lib/dictBuilder/zdict.c
@@ -71,26 +71,18 @@ static const size_t g_min_fast_dictContent = 192;
 *  Console display
 ***************************************/
 #define DISPLAY(...)         { fprintf(stderr, __VA_ARGS__); fflush( stderr ); }
-#define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); }
-static unsigned g_displayLevel = 0;   /* 0 : no display;   1: errors;   2: default;  4: full information */
-
-#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
-            if (ZDICT_clockSpan(g_time) > refreshRate)  \
-            { g_time = clock(); DISPLAY(__VA_ARGS__); \
-            if (g_displayLevel>=4) fflush(stdout); } }
-static const clock_t refreshRate = CLOCKS_PER_SEC * 3 / 10;
-static clock_t g_time = 0;
+#define DISPLAYLEVEL(l, ...) if (notificationLevel>=l) { DISPLAY(__VA_ARGS__); }    /* 0 : no display;   1: errors;   2: default;  3: details;  4: debug */
 
 static clock_t ZDICT_clockSpan(clock_t nPrevious) { return clock() - nPrevious; }
 
-static void ZDICT_printHex(U32 dlevel, const void* ptr, size_t length)
+static void ZDICT_printHex(const void* ptr, size_t length)
 {
     const BYTE* const b = (const BYTE*)ptr;
     size_t u;
     for (u=0; u<length; u++) {
         BYTE c = b[u];
         if (c<32 || c>126) c = '.';   /* non-printable char */
-        DISPLAYLEVEL(dlevel, "%c", c);
+        DISPLAY("%c", c);
     }
 }
 
@@ -211,7 +203,7 @@ static void ZDICT_initDictItem(dictItem* d)
 static dictItem ZDICT_analyzePos(
                        BYTE* doneMarks,
                        const int* suffix, U32 start,
-                       const void* buffer, U32 minRatio)
+                       const void* buffer, U32 minRatio, U32 notificationLevel)
 {
     U32 lengthList[LLIMIT] = {0};
     U32 cumulLength[LLIMIT] = {0};
@@ -473,7 +465,7 @@ static U32 ZDICT_dictSize(const dictItem* dictList)
 static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize,
                             const void* const buffer, size_t bufferSize,   /* buffer must end with noisy guard band */
                             const size_t* fileSizes, unsigned nbFiles,
-                            U32 minRatio)
+                            U32 minRatio, U32 notificationLevel)
 {
     int* const suffix0 = (int*)malloc((bufferSize+2)*sizeof(*suffix0));
     int* const suffix = suffix0+1;
@@ -481,6 +473,13 @@ static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize,
     BYTE* doneMarks = (BYTE*)malloc((bufferSize+16)*sizeof(*doneMarks));   /* +16 for overflow security */
     U32* filePos = (U32*)malloc(nbFiles * sizeof(*filePos));
     size_t result = 0;
+    clock_t displayClock = 0;
+    clock_t const refreshRate = CLOCKS_PER_SEC * 3 / 10;
+
+#   define DISPLAYUPDATE(l, ...) if (notificationLevel>=l) { \
+            if (ZDICT_clockSpan(displayClock) > refreshRate)  \
+            { displayClock = clock(); DISPLAY(__VA_ARGS__); \
+            if (notificationLevel>=4) fflush(stdout); } }
 
     /* init */
     DISPLAYLEVEL(2, "\r%70s\r", "");   /* clean display line */
@@ -506,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<nbFiles; pos++)
             filePos[pos] = (U32)(filePos[pos-1] + fileSizes[pos-1]);
@@ -518,7 +518,7 @@ static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize,
     {   U32 cursor; for (cursor=0; cursor < bufferSize; ) {
             dictItem solution;
             if (doneMarks[cursor]) { cursor++; continue; }
-            solution = ZDICT_analyzePos(doneMarks, suffix, reverseSuffix[cursor], buffer, minRatio);
+            solution = ZDICT_analyzePos(doneMarks, suffix, reverseSuffix[cursor], buffer, minRatio, notificationLevel);
             if (solution.length==0) { cursor++; continue; }
             ZDICT_insertDictItem(dictList, dictListSize, solution);
             cursor += solution.length;
@@ -558,15 +558,15 @@ typedef struct
 
 static void ZDICT_countEStats(EStats_ress_t esr, ZSTD_parameters params,
                             U32* countLit, U32* offsetcodeCount, U32* matchlengthCount, U32* litlengthCount, U32* repOffsets,
-                            const void* src, size_t srcSize)
+                            const void* src, size_t srcSize, U32 notificationLevel)
 {
     size_t const blockSizeMax = MIN (ZSTD_BLOCKSIZE_ABSOLUTEMAX, 1 << params.cParams.windowLog);
     size_t cSize;
 
     if (srcSize > blockSizeMax) srcSize = blockSizeMax;   /* protection vs large samples */
-	{	size_t const errorCode = ZSTD_copyCCtx(esr.zc, esr.ref);
-		if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_copyCCtx failed \n"); return; }
-	}
+    {  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);
     if (ZSTD_isError(cSize)) { DISPLAYLEVEL(1, "warning : could not compress sample size %u \n", (U32)srcSize); return; }
 
@@ -647,9 +647,10 @@ static void ZDICT_insertSortCount(offsetCount_t table[ZSTD_REP_NUM+1], U32 val,
 
 #define OFFCODE_MAX 30  /* only applicable to first block */
 static size_t ZDICT_analyzeEntropy(void*  dstBuffer, size_t maxDstSize,
-                                 unsigned compressionLevel,
-                           const void*  srcBuffer, const size_t* fileSizes, unsigned nbFiles,
-                           const void* dictBuffer, size_t  dictBufferSize)
+                                   unsigned compressionLevel,
+                             const void*  srcBuffer, const size_t* fileSizes, unsigned nbFiles,
+                             const void* dictBuffer, size_t  dictBufferSize,
+                                   unsigned notificationLevel)
 {
     U32 countLit[256];
     HUF_CREATE_STATIC_CTABLE(hufTable, 255);
@@ -690,18 +691,19 @@ static size_t ZDICT_analyzeEntropy(void*  dstBuffer, size_t maxDstSize,
     memset(bestRepOffset, 0, sizeof(bestRepOffset));
     if (compressionLevel==0) compressionLevel=g_compressionLevel_default;
     params = ZSTD_getParams(compressionLevel, averageSampleSize, dictBufferSize);
-	{	size_t const beginResult = ZSTD_compressBegin_advanced(esr.ref, dictBuffer, dictBufferSize, params, 0);
-		if (ZSTD_isError(beginResult)) {
-			eSize = ERROR(GENERIC);
-			DISPLAYLEVEL(1, "error : ZSTD_compressBegin_advanced failed \n");
-			goto _cleanup;
-	}	}
+    {   size_t const beginResult = ZSTD_compressBegin_advanced(esr.ref, dictBuffer, dictBufferSize, params, 0);
+            if (ZSTD_isError(beginResult)) {
+            eSize = ERROR(GENERIC);
+            DISPLAYLEVEL(1, "error : ZSTD_compressBegin_advanced failed \n");
+            goto _cleanup;
+    }   }
 
     /* collect stats on all files */
     for (u=0; u<nbFiles; u++) {
         ZDICT_countEStats(esr, params,
-                        countLit, offcodeCount, matchLengthCount, litLengthCount, repOffset,
-           (const char*)srcBuffer + pos, fileSizes[u]);
+                          countLit, offcodeCount, matchLengthCount, litLengthCount, repOffset,
+                         (const char*)srcBuffer + pos, fileSizes[u],
+                          notificationLevel);
         pos += fileSizes[u];
     }
 
@@ -748,7 +750,6 @@ static size_t ZDICT_analyzeEntropy(void*  dstBuffer, size_t maxDstSize,
     }
     llLog = (U32)errorCode;
 
-
     /* write result to buffer */
     {   size_t const hhSize = HUF_writeCTable(dstPtr, maxDstSize, hufTable, 255, huffLog);
         if (HUF_isError(hhSize)) {
@@ -828,6 +829,7 @@ size_t ZDICT_addEntropyTablesFromBuffer_advanced(void* dictBuffer, size_t dictCo
 {
     size_t hSize;
     int const compressionLevel = (params.compressionLevel <= 0) ? g_compressionLevel_default : params.compressionLevel;
+    U32 const notificationLevel = params.notificationLevel;
 
     /* dictionary header */
     MEM_writeLE32(dictBuffer, ZSTD_DICT_MAGIC);
@@ -844,7 +846,8 @@ size_t ZDICT_addEntropyTablesFromBuffer_advanced(void* dictBuffer, size_t dictCo
     {   size_t const eSize = ZDICT_analyzeEntropy((char*)dictBuffer+hSize, dictBufferCapacity-hSize,
                                   compressionLevel,
                                   samplesBuffer, samplesSizes, nbSamples,
-                                  (char*)dictBuffer + dictBufferCapacity - dictContentSize, dictContentSize);
+                                  (char*)dictBuffer + dictBufferCapacity - dictContentSize, dictContentSize,
+                                  notificationLevel);
         if (ZDICT_isError(eSize)) return eSize;
         hSize += eSize;
     }
@@ -872,6 +875,7 @@ size_t ZDICT_trainFromBuffer_unsafe(
     size_t const targetDictSize = maxDictSize;
     size_t const samplesBuffSize = ZDICT_totalSampleSize(samplesSizes, nbSamples);
     size_t dictSize = 0;
+    U32 const notificationLevel = params.notificationLevel;
 
     /* checks */
     if (!dictList) return ERROR(memory_allocation);
@@ -880,16 +884,15 @@ size_t ZDICT_trainFromBuffer_unsafe(
 
     /* init */
     ZDICT_initDictItem(dictList);
-    g_displayLevel = params.notificationLevel;
 
     /* build dictionary */
     ZDICT_trainBuffer(dictList, dictListSize,
                     samplesBuffer, samplesBuffSize,
                     samplesSizes, nbSamples,
-                    minRep);
+                    minRep, notificationLevel);
 
     /* display best matches */
-    if (g_displayLevel>= 3) {
+    if (params.notificationLevel>= 3) {
         U32 const nb = MIN(25, dictList[0].pos);
         U32 const dictContentSize = ZDICT_dictSize(dictList);
         U32 u;
@@ -901,14 +904,14 @@ size_t ZDICT_trainFromBuffer_unsafe(
             U32 printedLength = MIN(40, length);
             DISPLAYLEVEL(3, "%3u:%3u bytes at pos %8u, savings %7u bytes |",
                          u, length, pos, dictList[u].savings);
-            ZDICT_printHex(3, (const char*)samplesBuffer+pos, printedLength);
+            ZDICT_printHex((const char*)samplesBuffer+pos, printedLength);
             DISPLAYLEVEL(3, "| \n");
     }   }
 
 
     /* create dictionary */
     {   U32 dictContentSize = ZDICT_dictSize(dictList);
-        if (dictContentSize < targetDictSize/2) {
+        if (dictContentSize < targetDictSize/3) {
             DISPLAYLEVEL(2, "!  warning : selected content significantly smaller than requested (%u < %u) \n", dictContentSize, (U32)maxDictSize);
             if (minRep > MINRATIO) {
                 DISPLAYLEVEL(2, "!  consider increasing selectivity to produce larger dictionary (-s%u) \n", selectivity+1);
@@ -918,12 +921,12 @@ size_t ZDICT_trainFromBuffer_unsafe(
                 DISPLAYLEVEL(2, "!  consider increasing the number of samples (total size : %u MB)\n", (U32)(samplesBuffSize>>20));
         }
 
-        if ((dictContentSize > targetDictSize*2) && (nbSamples > 2*MINRATIO) && (selectivity>1)) {
+        if ((dictContentSize > targetDictSize*3) && (nbSamples > 2*MINRATIO) && (selectivity>1)) {
             U32 proposedSelectivity = selectivity-1;
             while ((nbSamples >> proposedSelectivity) <= MINRATIO) { proposedSelectivity--; }
             DISPLAYLEVEL(2, "!  note : calculated dictionary significantly larger than requested (%u > %u) \n", dictContentSize, (U32)maxDictSize);
-            DISPLAYLEVEL(2, "!  you may consider decreasing selectivity to produce denser dictionary (-s%u) \n", proposedSelectivity);
-            DISPLAYLEVEL(2, "!  but test its efficiency on samples \n");
+            DISPLAYLEVEL(2, "!  consider increasing dictionary size, or produce denser dictionary (-s%u) \n", proposedSelectivity);
+            DISPLAYLEVEL(2, "!  always test dictionary efficiency on samples \n");
         }
 
         /* limit dictionary size */
diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h
index c84aedd..642a435 100644
--- a/lib/dictBuilder/zdict.h
+++ b/lib/dictBuilder/zdict.h
@@ -68,7 +68,7 @@ typedef struct {
     int      compressionLevel;   /* 0 means default; target a specific zstd compression level */
     unsigned notificationLevel;  /* Write to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */
     unsigned dictID;             /* 0 means auto mode (32-bits random value); other : force dictID value */
-    unsigned reserved[2];        /* space for future parameters */
+    unsigned reserved[2];        /* reserved space for future parameters */
 } ZDICT_params_t;
 
 
diff --git a/lib/legacy/zstd_v01.c b/lib/legacy/zstd_v01.c
index 94847d5..fe9c5cc 100644
--- a/lib/legacy/zstd_v01.c
+++ b/lib/legacy/zstd_v01.c
@@ -13,6 +13,7 @@
 ******************************************/
 #include <stddef.h>    /* size_t, ptrdiff_t */
 #include "zstd_v01.h"
+#include "error_private.h"
 
 
 /******************************************
@@ -139,11 +140,15 @@ typedef struct
 #  pragma warning(disable : 4214)        /* disable: C4214: non-int bitfields */
 #else
 #  define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#    ifdef __GNUC__
+#      define FORCE_INLINE static inline __attribute__((always_inline))
+#    else
+#      define FORCE_INLINE static inline
+#    endif
 #  else
-#    define FORCE_INLINE static inline
-#  endif
+#    define FORCE_INLINE static
+#  endif /* __STDC_VERSION__ */
 #endif
 
 
@@ -1173,57 +1178,6 @@ static size_t HUF_decompress (void* dst, size_t maxDstSize, const void* cSrc, si
 
 /*
     zstd - standard compression library
-    Header File for static linking only
-    Copyright (C) 2014-2015, Yann Collet.
-
-    BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
-    Redistribution and use in source and binary forms, with or without
-    modification, are permitted provided that the following conditions are
-    met:
-    * Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above
-    copyright notice, this list of conditions and the following disclaimer
-    in the documentation and/or other materials provided with the
-    distribution.
-    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-    You can contact the author at :
-    - zstd source repository : https://github.com/Cyan4973/zstd
-    - ztsd public forum : https://groups.google.com/forum/#!forum/lz4c
-*/
-
-/* The objects defined into this file should be considered experimental.
- * They are not labelled stable, as their prototype may change in the future.
- * You can use them for tests, provide feedback, or if you can endure risk of future changes.
- */
-
-/**************************************
-*  Error management
-**************************************/
-#define ZSTD_LIST_ERRORS(ITEM) \
-        ITEM(ZSTD_OK_NoError) ITEM(ZSTD_ERROR_GENERIC) \
-        ITEM(ZSTD_ERROR_MagicNumber) \
-        ITEM(ZSTD_ERROR_SrcSize) ITEM(ZSTD_ERROR_maxDstSize_tooSmall) \
-        ITEM(ZSTD_ERROR_corruption) \
-        ITEM(ZSTD_ERROR_maxCode)
-
-#define ZSTD_GENERATE_ENUM(ENUM) ENUM,
-typedef enum { ZSTD_LIST_ERRORS(ZSTD_GENERATE_ENUM) } ZSTD_errorCodes;   /* exposed list of errors; static linking only */
-
-/*
-    zstd - standard compression library
     Copyright (C) 2014-2015, Yann Collet.
 
     BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
@@ -1300,17 +1254,9 @@ typedef enum { ZSTD_LIST_ERRORS(ZSTD_GENERATE_ENUM) } ZSTD_errorCodes;   /* expo
 #endif
 
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  include <intrin.h>                    /* For Visual 2005 */
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4324)        /* disable: C4324: padded structure */
-#else
-#  define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
@@ -1491,11 +1437,8 @@ typedef struct ZSTD_Cctx_s
 /**************************************
 *  Error Management
 **************************************/
-/* tells if a return value is an error code */
-static unsigned ZSTD_isError(size_t code) { return (code > (size_t)(-ZSTD_ERROR_maxCode)); }
-
 /* published entry point */
-unsigned ZSTDv01_isError(size_t code) { return ZSTD_isError(code); }
+unsigned ZSTDv01_isError(size_t code) { return ERR_isError(code); }
 
 
 /**************************************
@@ -1510,13 +1453,13 @@ unsigned ZSTDv01_isError(size_t code) { return ZSTD_isError(code); }
 *   Decompression code
 **************************************************************/
 
-static size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* bpPtr)
+size_t ZSTDv01_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* bpPtr)
 {
     const BYTE* const in = (const BYTE* const)src;
     BYTE headerFlags;
     U32 cSize;
 
-    if (srcSize < 3) return (size_t)-ZSTD_ERROR_SrcSize;
+    if (srcSize < 3) return ERROR(srcSize_wrong);
 
     headerFlags = *in;
     cSize = in[2] + (in[1]<<8) + ((in[0] & 7)<<16);
@@ -1532,7 +1475,7 @@ static size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockPropertie
 
 static size_t ZSTD_copyUncompressedBlock(void* dst, size_t maxDstSize, const void* src, size_t srcSize)
 {
-    if (srcSize > maxDstSize) return (size_t)-ZSTD_ERROR_maxDstSize_tooSmall;
+    if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall);
     memcpy(dst, src, srcSize);
     return srcSize;
 }
@@ -1549,21 +1492,21 @@ static size_t ZSTD_decompressLiterals(void* ctx,
     size_t litSize;
 
     /* check : minimum 2, for litSize, +1, for content */
-    if (srcSize <= 3) return (size_t)-ZSTD_ERROR_corruption;
+    if (srcSize <= 3) return ERROR(corruption_detected);
 
     litSize = ip[1] + (ip[0]<<8);
     litSize += ((ip[-3] >> 3) & 7) << 16;   // mmmmh....
     op = oend - litSize;
 
     (void)ctx;
-    if (litSize > maxDstSize) return (size_t)-ZSTD_ERROR_maxDstSize_tooSmall;
+    if (litSize > maxDstSize) return ERROR(dstSize_tooSmall);
     errorCode = HUF_decompress(op, litSize, ip+2, srcSize-2);
-    if (FSE_isError(errorCode)) return (size_t)-ZSTD_ERROR_GENERIC;
+    if (FSE_isError(errorCode)) return ERROR(GENERIC);
     return litSize;
 }
 
 
-static size_t ZSTD_decodeLiteralsBlock(void* ctx,
+size_t ZSTDv01_decodeLiteralsBlock(void* ctx,
                                 void* dst, size_t maxDstSize,
                           const BYTE** litStart, size_t* litSize,
                           const void* src, size_t srcSize)
@@ -1574,9 +1517,9 @@ static size_t ZSTD_decodeLiteralsBlock(void* ctx,
     BYTE* const oend = ostart + maxDstSize;
     blockProperties_t litbp;
 
-    size_t litcSize = ZSTD_getcBlockSize(src, srcSize, &litbp);
-    if (ZSTD_isError(litcSize)) return litcSize;
-    if (litcSize > srcSize - ZSTD_blockHeaderSize) return (size_t)-ZSTD_ERROR_SrcSize;
+    size_t litcSize = ZSTDv01_getcBlockSize(src, srcSize, &litbp);
+    if (ZSTDv01_isError(litcSize)) return litcSize;
+    if (litcSize > srcSize - ZSTD_blockHeaderSize) return ERROR(srcSize_wrong);
     ip += ZSTD_blockHeaderSize;
 
     switch(litbp.blockType)
@@ -1589,7 +1532,7 @@ static size_t ZSTD_decodeLiteralsBlock(void* ctx,
     case bt_rle:
         {
             size_t rleSize = litbp.origSize;
-            if (rleSize>maxDstSize) return (size_t)-ZSTD_ERROR_maxDstSize_tooSmall;
+            if (rleSize>maxDstSize) return ERROR(dstSize_tooSmall);
             memset(oend - rleSize, *ip, rleSize);
             *litStart = oend - rleSize;
             *litSize = rleSize;
@@ -1599,7 +1542,7 @@ static size_t ZSTD_decodeLiteralsBlock(void* ctx,
     case bt_compressed:
         {
             size_t decodedLitSize = ZSTD_decompressLiterals(ctx, dst, maxDstSize, ip, litcSize);
-            if (ZSTD_isError(decodedLitSize)) return decodedLitSize;
+            if (ZSTDv01_isError(decodedLitSize)) return decodedLitSize;
             *litStart = oend - decodedLitSize;
             *litSize = decodedLitSize;
             ip += litcSize;
@@ -1607,14 +1550,14 @@ static size_t ZSTD_decodeLiteralsBlock(void* ctx,
         }
     case bt_end:
     default:
-        return (size_t)-ZSTD_ERROR_GENERIC;
+        return ERROR(GENERIC);
     }
 
     return ip-istart;
 }
 
 
-static size_t ZSTD_decodeSeqHeaders(int* nbSeq, const BYTE** dumpsPtr, size_t* dumpsLengthPtr,
+size_t ZSTDv01_decodeSeqHeaders(int* nbSeq, const BYTE** dumpsPtr, size_t* dumpsLengthPtr,
                          FSE_DTable* DTableLL, FSE_DTable* DTableML, FSE_DTable* DTableOffb,
                          const void* src, size_t srcSize)
 {
@@ -1626,7 +1569,7 @@ static size_t ZSTD_decodeSeqHeaders(int* nbSeq, const BYTE** dumpsPtr, size_t* d
     size_t dumpsLength;
 
     /* check */
-    if (srcSize < 5) return (size_t)-ZSTD_ERROR_SrcSize;
+    if (srcSize < 5) return ERROR(srcSize_wrong);
 
     /* SeqHead */
     *nbSeq = ZSTD_readLE16(ip); ip+=2;
@@ -1650,7 +1593,7 @@ static size_t ZSTD_decodeSeqHeaders(int* nbSeq, const BYTE** dumpsPtr, size_t* d
     *dumpsLengthPtr = dumpsLength;
 
     /* check */
-    if (ip > iend-3) return (size_t)-ZSTD_ERROR_SrcSize; /* min : all 3 are "raw", hence no header, but at least xxLog bits per type */
+    if (ip > iend-3) return ERROR(srcSize_wrong); /* min : all 3 are "raw", hence no header, but at least xxLog bits per type */
 
     /* sequences */
     {
@@ -1669,8 +1612,8 @@ static size_t ZSTD_decodeSeqHeaders(int* nbSeq, const BYTE** dumpsPtr, size_t* d
         default :
             {   U32 max = MaxLL;
                 headerSize = FSE_readNCount(norm, &max, &LLlog, ip, iend-ip);
-                if (FSE_isError(headerSize)) return (size_t)-ZSTD_ERROR_GENERIC;
-                if (LLlog > LLFSELog) return (size_t)-ZSTD_ERROR_corruption;
+                if (FSE_isError(headerSize)) return ERROR(GENERIC);
+                if (LLlog > LLFSELog) return ERROR(corruption_detected);
                 ip += headerSize;
                 FSE_buildDTable(DTableLL, norm, max, LLlog);
         }   }
@@ -1679,7 +1622,7 @@ static size_t ZSTD_decodeSeqHeaders(int* nbSeq, const BYTE** dumpsPtr, size_t* d
         {
         case bt_rle :
             Offlog = 0;
-            if (ip > iend-2) return (size_t)-ZSTD_ERROR_SrcSize; /* min : "raw", hence no header, but at least xxLog bits */
+            if (ip > iend-2) return ERROR(srcSize_wrong); /* min : "raw", hence no header, but at least xxLog bits */
             FSE_buildDTable_rle(DTableOffb, *ip++); break;
         case bt_raw :
             Offlog = Offbits;
@@ -1687,8 +1630,8 @@ static size_t ZSTD_decodeSeqHeaders(int* nbSeq, const BYTE** dumpsPtr, size_t* d
         default :
             {   U32 max = MaxOff;
                 headerSize = FSE_readNCount(norm, &max, &Offlog, ip, iend-ip);
-                if (FSE_isError(headerSize)) return (size_t)-ZSTD_ERROR_GENERIC;
-                if (Offlog > OffFSELog) return (size_t)-ZSTD_ERROR_corruption;
+                if (FSE_isError(headerSize)) return ERROR(GENERIC);
+                if (Offlog > OffFSELog) return ERROR(corruption_detected);
                 ip += headerSize;
                 FSE_buildDTable(DTableOffb, norm, max, Offlog);
         }   }
@@ -1697,7 +1640,7 @@ static size_t ZSTD_decodeSeqHeaders(int* nbSeq, const BYTE** dumpsPtr, size_t* d
         {
         case bt_rle :
             MLlog = 0;
-            if (ip > iend-2) return (size_t)-ZSTD_ERROR_SrcSize; /* min : "raw", hence no header, but at least xxLog bits */
+            if (ip > iend-2) return ERROR(srcSize_wrong); /* min : "raw", hence no header, but at least xxLog bits */
             FSE_buildDTable_rle(DTableML, *ip++); break;
         case bt_raw :
             MLlog = MLbits;
@@ -1705,8 +1648,8 @@ static size_t ZSTD_decodeSeqHeaders(int* nbSeq, const BYTE** dumpsPtr, size_t* d
         default :
             {   U32 max = MaxML;
                 headerSize = FSE_readNCount(norm, &max, &MLlog, ip, iend-ip);
-                if (FSE_isError(headerSize)) return (size_t)-ZSTD_ERROR_GENERIC;
-                if (MLlog > MLFSELog) return (size_t)-ZSTD_ERROR_corruption;
+                if (FSE_isError(headerSize)) return ERROR(GENERIC);
+                if (MLlog > MLFSELog) return ERROR(corruption_detected);
                 ip += headerSize;
                 FSE_buildDTable(DTableML, norm, max, MLlog);
     }   }   }
@@ -1809,9 +1752,9 @@ static size_t ZSTD_execSequence(BYTE* op,
     const BYTE* const litEnd = *litPtr + litLength;
 
     /* check */
-    if (endMatch > oend) return (size_t)-ZSTD_ERROR_maxDstSize_tooSmall;   /* overwrite beyond dst buffer */
-    if (litEnd > litLimit) return (size_t)-ZSTD_ERROR_corruption;
-    if (sequence.matchLength > (size_t)(*litPtr-op))  return (size_t)-ZSTD_ERROR_maxDstSize_tooSmall;    /* overwrite literal segment */
+    if (endMatch > oend) return ERROR(dstSize_tooSmall);   /* overwrite beyond dst buffer */
+    if (litEnd > litLimit) return ERROR(corruption_detected);
+    if (sequence.matchLength > (size_t)(*litPtr-op))  return ERROR(dstSize_tooSmall);    /* overwrite literal segment */
 
     /* copy Literals */
     if (((size_t)(*litPtr - op) < 8) || ((size_t)(oend-litEnd) < 8) || (op+litLength > oend-8))
@@ -1822,7 +1765,7 @@ static size_t ZSTD_execSequence(BYTE* op,
     *litPtr = litEnd;   /* update for next sequence */
 
     /* check : last match must be at a minimum distance of 8 from end of dest buffer */
-    if (oend-op < 8) return (size_t)-ZSTD_ERROR_maxDstSize_tooSmall;
+    if (oend-op < 8) return ERROR(dstSize_tooSmall);
 
     /* copy Match */
     {
@@ -1832,8 +1775,8 @@ static size_t ZSTD_execSequence(BYTE* op,
         U64 saved[2];
 
         /* check */
-        if (match < base) return (size_t)-ZSTD_ERROR_corruption;
-        if (sequence.offset > (size_t)base) return (size_t)-ZSTD_ERROR_corruption;
+        if (match < base) return ERROR(corruption_detected);
+        if (sequence.offset > (size_t)base) return ERROR(corruption_detected);
 
         /* save beginning of literal sequence, in case of write overlap */
         if (overlapRisk)
@@ -1911,10 +1854,10 @@ static size_t ZSTD_decompressSequences(
     BYTE* const base = (BYTE*) (dctx->base);
 
     /* Build Decoding Tables */
-    errorCode = ZSTD_decodeSeqHeaders(&nbSeq, &dumps, &dumpsLength,
+    errorCode = ZSTDv01_decodeSeqHeaders(&nbSeq, &dumps, &dumpsLength,
                                       DTableLL, DTableML, DTableOffb,
                                       ip, iend-ip);
-    if (ZSTD_isError(errorCode)) return errorCode;
+    if (ZSTDv01_isError(errorCode)) return errorCode;
     ip += errorCode;
 
     /* Regen sequences */
@@ -1927,7 +1870,7 @@ static size_t ZSTD_decompressSequences(
         seqState.dumpsEnd = dumps + dumpsLength;
         seqState.prevOffset = 1;
         errorCode = FSE_initDStream(&(seqState.DStream), ip, iend-ip);
-        if (FSE_isError(errorCode)) return (size_t)-ZSTD_ERROR_corruption;
+        if (FSE_isError(errorCode)) return ERROR(corruption_detected);
         FSE_initDState(&(seqState.stateLL), &(seqState.DStream), DTableLL);
         FSE_initDState(&(seqState.stateOffb), &(seqState.DStream), DTableOffb);
         FSE_initDState(&(seqState.stateML), &(seqState.DStream), DTableML);
@@ -1938,18 +1881,18 @@ static size_t ZSTD_decompressSequences(
             nbSeq--;
             ZSTD_decodeSequence(&sequence, &seqState);
             oneSeqSize = ZSTD_execSequence(op, sequence, &litPtr, litEnd, base, oend);
-            if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
+            if (ZSTDv01_isError(oneSeqSize)) return oneSeqSize;
             op += oneSeqSize;
         }
 
         /* check if reached exact end */
-        if ( !FSE_endOfDStream(&(seqState.DStream)) ) return (size_t)-ZSTD_ERROR_corruption;   /* requested too much : data is corrupted */
-        if (nbSeq<0) return (size_t)-ZSTD_ERROR_corruption;   /* requested too many sequences : data is corrupted */
+        if ( !FSE_endOfDStream(&(seqState.DStream)) ) return ERROR(corruption_detected);   /* requested too much : data is corrupted */
+        if (nbSeq<0) return ERROR(corruption_detected);   /* requested too many sequences : data is corrupted */
 
         /* last literal segment */
         {
             size_t lastLLSize = litEnd - litPtr;
-            if (op+lastLLSize > oend) return (size_t)-ZSTD_ERROR_maxDstSize_tooSmall;
+            if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
             if (op != litPtr) memmove(op, litPtr, lastLLSize);
             op += lastLLSize;
         }
@@ -1971,8 +1914,8 @@ static size_t ZSTD_decompressBlock(
     size_t errorCode;
 
     /* Decode literals sub-block */
-    errorCode = ZSTD_decodeLiteralsBlock(ctx, dst, maxDstSize, &litPtr, &litSize, src, srcSize);
-    if (ZSTD_isError(errorCode)) return errorCode;
+    errorCode = ZSTDv01_decodeLiteralsBlock(ctx, dst, maxDstSize, &litPtr, &litSize, src, srcSize);
+    if (ZSTDv01_isError(errorCode)) return errorCode;
     ip += errorCode;
     srcSize -= errorCode;
 
@@ -1993,20 +1936,20 @@ size_t ZSTDv01_decompressDCtx(void* ctx, void* dst, size_t maxDstSize, const voi
     blockProperties_t blockProperties;
 
     /* Frame Header */
-    if (srcSize < ZSTD_frameHeaderSize+ZSTD_blockHeaderSize) return (size_t)-ZSTD_ERROR_SrcSize;
+    if (srcSize < ZSTD_frameHeaderSize+ZSTD_blockHeaderSize) return ERROR(srcSize_wrong);
     magicNumber = ZSTD_readBE32(src);
-    if (magicNumber != ZSTD_magicNumber) return (size_t)-ZSTD_ERROR_MagicNumber;
+    if (magicNumber != ZSTD_magicNumber) return ERROR(prefix_unknown);
     ip += ZSTD_frameHeaderSize; remainingSize -= ZSTD_frameHeaderSize;
 
     /* Loop on each block */
     while (1)
     {
-        size_t blockSize = ZSTD_getcBlockSize(ip, iend-ip, &blockProperties);
-        if (ZSTD_isError(blockSize)) return blockSize;
+        size_t blockSize = ZSTDv01_getcBlockSize(ip, iend-ip, &blockProperties);
+        if (ZSTDv01_isError(blockSize)) return blockSize;
 
         ip += ZSTD_blockHeaderSize;
         remainingSize -= ZSTD_blockHeaderSize;
-        if (blockSize > remainingSize) return (size_t)-ZSTD_ERROR_SrcSize;
+        if (blockSize > remainingSize) return ERROR(srcSize_wrong);
 
         switch(blockProperties.blockType)
         {
@@ -2017,18 +1960,18 @@ size_t ZSTDv01_decompressDCtx(void* ctx, void* dst, size_t maxDstSize, const voi
             errorCode = ZSTD_copyUncompressedBlock(op, oend-op, ip, blockSize);
             break;
         case bt_rle :
-            return (size_t)-ZSTD_ERROR_GENERIC;   /* not yet supported */
+            return ERROR(GENERIC);   /* not yet supported */
             break;
         case bt_end :
             /* end of frame */
-            if (remainingSize) return (size_t)-ZSTD_ERROR_SrcSize;
+            if (remainingSize) return ERROR(srcSize_wrong);
             break;
         default:
-            return (size_t)-ZSTD_ERROR_GENERIC;
+            return ERROR(GENERIC);
         }
         if (blockSize == 0) break;   /* bt_end */
 
-        if (ZSTD_isError(errorCode)) return errorCode;
+        if (ZSTDv01_isError(errorCode)) return errorCode;
         op += errorCode;
         ip += blockSize;
         remainingSize -= blockSize;
@@ -2082,7 +2025,7 @@ size_t ZSTDv01_decompressContinue(ZSTDv01_Dctx* dctx, void* dst, size_t maxDstSi
     dctx_t* ctx = (dctx_t*)dctx;
 
     /* Sanity check */
-    if (srcSize != ctx->expected) return (size_t)-ZSTD_ERROR_SrcSize;
+    if (srcSize != ctx->expected) return ERROR(srcSize_wrong);
     if (dst != ctx->previousDstEnd)  /* not contiguous */
         ctx->base = dst;
 
@@ -2091,7 +2034,7 @@ size_t ZSTDv01_decompressContinue(ZSTDv01_Dctx* dctx, void* dst, size_t maxDstSi
     {
         /* Check frame magic header */
         U32 magicNumber = ZSTD_readBE32(src);
-        if (magicNumber != ZSTD_magicNumber) return (size_t)-ZSTD_ERROR_MagicNumber;
+        if (magicNumber != ZSTD_magicNumber) return ERROR(prefix_unknown);
         ctx->phase = 1;
         ctx->expected = ZSTD_blockHeaderSize;
         return 0;
@@ -2101,8 +2044,8 @@ size_t ZSTDv01_decompressContinue(ZSTDv01_Dctx* dctx, void* dst, size_t maxDstSi
     if (ctx->phase == 1)
     {
         blockProperties_t bp;
-        size_t blockSize = ZSTD_getcBlockSize(src, ZSTD_blockHeaderSize, &bp);
-        if (ZSTD_isError(blockSize)) return blockSize;
+        size_t blockSize = ZSTDv01_getcBlockSize(src, ZSTD_blockHeaderSize, &bp);
+        if (ZSTDv01_isError(blockSize)) return blockSize;
         if (bp.blockType == bt_end)
         {
             ctx->expected = 0;
@@ -2130,13 +2073,13 @@ size_t ZSTDv01_decompressContinue(ZSTDv01_Dctx* dctx, void* dst, size_t maxDstSi
             rSize = ZSTD_copyUncompressedBlock(dst, maxDstSize, src, srcSize);
             break;
         case bt_rle :
-            return (size_t)-ZSTD_ERROR_GENERIC;   /* not yet handled */
+            return ERROR(GENERIC);   /* not yet handled */
             break;
         case bt_end :   /* should never happen (filtered at phase 1) */
             rSize = 0;
             break;
         default:
-            return (size_t)-ZSTD_ERROR_GENERIC;
+            return ERROR(GENERIC);
         }
         ctx->phase = 1;
         ctx->expected = ZSTD_blockHeaderSize;
diff --git a/lib/legacy/zstd_v02.c b/lib/legacy/zstd_v02.c
index af13662..de1592e 100644
--- a/lib/legacy/zstd_v02.c
+++ b/lib/legacy/zstd_v02.c
@@ -7,15 +7,11 @@
  * of patent rights can be found in the PATENTS file in the same directory.
  */
 
-#ifndef ERROR_H_MODULE
-#define ERROR_H_MODULE
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
 
 #include <stddef.h>    /* size_t, ptrdiff_t */
 #include "zstd_v02.h"
+#include "error_private.h"
+
 
 /******************************************
 *  Compiler-specific
@@ -24,46 +20,6 @@ extern "C" {
 #   include <stdlib.h>  /* _byteswap_ulong */
 #   include <intrin.h>  /* _byteswap_* */
 #endif
-#if defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-#  define ERR_STATIC static inline
-#elif defined(_MSC_VER)
-#  define ERR_STATIC static __inline
-#elif defined(__GNUC__)
-#  define ERR_STATIC static __attribute__((unused))
-#else
-#  define ERR_STATIC static  /* this version may generate warnings for unused static functions; disable the relevant warning */
-#endif
-
-
-/******************************************
-*  Error Management
-******************************************/
-#define PREFIX(name) ZSTD_error_##name
-
-#define ERROR(name) (size_t)-PREFIX(name)
-
-#define ERROR_LIST(ITEM) \
-        ITEM(PREFIX(No_Error)) ITEM(PREFIX(GENERIC)) \
-        ITEM(PREFIX(memory_allocation)) \
-        ITEM(PREFIX(dstSize_tooSmall)) ITEM(PREFIX(srcSize_wrong)) \
-        ITEM(PREFIX(prefix_unknown)) ITEM(PREFIX(corruption_detected)) \
-        ITEM(PREFIX(tableLog_tooLarge)) ITEM(PREFIX(maxSymbolValue_tooLarge)) ITEM(PREFIX(maxSymbolValue_tooSmall)) \
-        ITEM(PREFIX(maxCode))
-
-#define ERROR_GENERATE_ENUM(ENUM) ENUM,
-typedef enum { ERROR_LIST(ERROR_GENERATE_ENUM) } ERR_codes;  /* enum is exposed, to detect & handle specific errors; compare function result to -enum value */
-
-#define ERROR_CONVERTTOSTRING(STRING) #STRING,
-#define ERROR_GENERATE_STRING(EXPR) ERROR_CONVERTTOSTRING(EXPR)
-
-ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); }
-
-
-#if defined (__cplusplus)
-}
-#endif
-
-#endif /* ERROR_H_MODULE */
 
 
 /* ******************************************************************
@@ -1122,12 +1078,15 @@ typedef struct ZSTD_DCtx_s ZSTD_DCtx;
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4214)        /* disable: C4214: non-int bitfields */
 #else
-#  ifdef __GNUC__
-#    define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#    define FORCE_INLINE static inline __attribute__((always_inline))
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#    ifdef __GNUC__
+#      define FORCE_INLINE static inline __attribute__((always_inline))
+#    else
+#      define FORCE_INLINE static inline
+#    endif
 #  else
-#    define FORCE_INLINE static inline
-#  endif
+#    define FORCE_INLINE static
+#  endif /* __STDC_VERSION__ */
 #endif
 
 
@@ -1595,15 +1554,7 @@ static size_t FSE_decompress(void* dst, size_t maxDstSize, const void* cSrc, siz
 
 
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
-#else
-#  ifdef __GNUC__
-#    define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
@@ -2790,17 +2741,9 @@ static size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_
 #endif
 
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  include <intrin.h>                    /* For Visual 2005 */
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4324)        /* disable: C4324: padded structure */
-#else
-#  define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
diff --git a/lib/legacy/zstd_v03.c b/lib/legacy/zstd_v03.c
index 637204d..caad331 100644
--- a/lib/legacy/zstd_v03.c
+++ b/lib/legacy/zstd_v03.c
@@ -7,15 +7,11 @@
  * of patent rights can be found in the PATENTS file in the same directory.
  */
 
-#ifndef ERROR_H_MODULE
-#define ERROR_H_MODULE
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
 
 #include <stddef.h>    /* size_t, ptrdiff_t */
 #include "zstd_v03.h"
+#include "error_private.h"
+
 
 /******************************************
 *  Compiler-specific
@@ -24,46 +20,7 @@ extern "C" {
 #   include <stdlib.h>  /* _byteswap_ulong */
 #   include <intrin.h>  /* _byteswap_* */
 #endif
-#if defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-#  define ERR_STATIC static inline
-#elif defined(_MSC_VER)
-#  define ERR_STATIC static __inline
-#elif defined(__GNUC__)
-#  define ERR_STATIC static __attribute__((unused))
-#else
-#  define ERR_STATIC static  /* this version may generate warnings for unused static functions; disable the relevant warning */
-#endif
-
 
-/******************************************
-*  Error Management
-******************************************/
-#define PREFIX(name) ZSTD_error_##name
-
-#define ERROR(name) (size_t)-PREFIX(name)
-
-#define ERROR_LIST(ITEM) \
-        ITEM(PREFIX(No_Error)) ITEM(PREFIX(GENERIC)) \
-        ITEM(PREFIX(memory_allocation)) \
-        ITEM(PREFIX(dstSize_tooSmall)) ITEM(PREFIX(srcSize_wrong)) \
-        ITEM(PREFIX(prefix_unknown)) ITEM(PREFIX(corruption_detected)) \
-        ITEM(PREFIX(tableLog_tooLarge)) ITEM(PREFIX(maxSymbolValue_tooLarge)) ITEM(PREFIX(maxSymbolValue_tooSmall)) \
-        ITEM(PREFIX(maxCode))
-
-#define ERROR_GENERATE_ENUM(ENUM) ENUM,
-typedef enum { ERROR_LIST(ERROR_GENERATE_ENUM) } ERR_codes;  /* enum is exposed, to detect & handle specific errors; compare function result to -enum value */
-
-#define ERROR_CONVERTTOSTRING(STRING) #STRING,
-#define ERROR_GENERATE_STRING(EXPR) ERROR_CONVERTTOSTRING(EXPR)
-
-ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); }
-
-
-#if defined (__cplusplus)
-}
-#endif
-
-#endif /* ERROR_H_MODULE */
 
 
 /* ******************************************************************
@@ -1122,12 +1079,15 @@ typedef struct ZSTD_DCtx_s ZSTD_DCtx;
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4214)        /* disable: C4214: non-int bitfields */
 #else
-#  ifdef __GNUC__
-#    define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#    define FORCE_INLINE static inline __attribute__((always_inline))
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#    ifdef __GNUC__
+#      define FORCE_INLINE static inline __attribute__((always_inline))
+#    else
+#      define FORCE_INLINE static inline
+#    endif
 #  else
-#    define FORCE_INLINE static inline
-#  endif
+#    define FORCE_INLINE static
+#  endif /* __STDC_VERSION__ */
 #endif
 
 
@@ -1588,25 +1548,13 @@ static size_t FSE_decompress(void* dst, size_t maxDstSize, const void* cSrc, siz
 #if defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
 /* inline is defined */
 #elif defined(_MSC_VER)
+#  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  define inline __inline
 #else
 #  define inline /* disable inline */
 #endif
 
 
-#ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
-#  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
-#else
-#  ifdef __GNUC__
-#    define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
-#endif
-
-
 /****************************************************************
 *  Includes
 ****************************************************************/
@@ -2432,17 +2380,11 @@ static size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_
 #endif
 
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  include <intrin.h>                    /* For Visual 2005 */
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4324)        /* disable: C4324: padded structure */
 #else
 #  define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c
index 1239bdb..c9dcb94 100644
--- a/lib/legacy/zstd_v04.c
+++ b/lib/legacy/zstd_v04.c
@@ -10,6 +10,7 @@
 
 /*- Dependencies -*/
 #include "zstd_v04.h"
+#include "error_private.h"
 
 
 /* ******************************************************************
@@ -250,79 +251,6 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr)
 
 #endif /* MEM_H_MODULE */
 
-/* ******************************************************************
-   Error codes list
-   Copyright (C) 2016, Yann Collet
-
-   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met:
-
-       * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above
-   copyright notice, this list of conditions and the following disclaimer
-   in the documentation and/or other materials provided with the
-   distribution.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-   You can contact the author at :
-   - Source repository : https://github.com/Cyan4973/zstd
-****************************************************************** */
-#ifndef ERROR_PUBLIC_H_MODULE
-#define ERROR_PUBLIC_H_MODULE
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-
-/* ****************************************
-*  error list
-******************************************/
-enum {
-  ZSTD_error_No_Error,
-  ZSTD_error_GENERIC,
-  ZSTD_error_prefix_unknown,
-  ZSTD_error_frameParameter_unsupported,
-  ZSTD_error_frameParameter_unsupportedBy32bitsImplementation,
-  ZSTD_error_init_missing,
-  ZSTD_error_memory_allocation,
-  ZSTD_error_stage_wrong,
-  ZSTD_error_dstSize_tooSmall,
-  ZSTD_error_srcSize_wrong,
-  ZSTD_error_corruption_detected,
-  ZSTD_error_tableLog_tooLarge,
-  ZSTD_error_maxSymbolValue_tooLarge,
-  ZSTD_error_maxSymbolValue_tooSmall,
-  ZSTD_error_maxCode
-};
-
-/* note : functions provide error codes in reverse negative order,
-          so compare with (size_t)(0-enum) */
-
-
-#if defined (__cplusplus)
-}
-#endif
-
-#endif /* ERROR_PUBLIC_H_MODULE */
-
-
-
 /*
     zstd - standard compression library
     Header File for static linking only
@@ -456,115 +384,6 @@ static size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t maxDstS
 }
 #endif
 
-/* ******************************************************************
-   Error codes and messages
-   Copyright (C) 2013-2016, Yann Collet
-
-   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met:
-
-       * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above
-   copyright notice, this list of conditions and the following disclaimer
-   in the documentation and/or other materials provided with the
-   distribution.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-   You can contact the author at :
-   - Source repository : https://github.com/Cyan4973/zstd
-****************************************************************** */
-/* Note : this module is expected to remain private, do not expose it */
-
-#ifndef ERROR_H_MODULE
-#define ERROR_H_MODULE
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-
-/* *****************************************
-*  Includes
-******************************************/
-#include <stddef.h>        /* size_t, ptrdiff_t */
-
-
-/* *****************************************
-*  Compiler-specific
-******************************************/
-#if defined(__GNUC__)
-#  define ERR_STATIC static __attribute__((unused))
-#elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-#  define ERR_STATIC static inline
-#elif defined(_MSC_VER)
-#  define ERR_STATIC static __inline
-#else
-#  define ERR_STATIC static  /* this version may generate warnings for unused static functions; disable the relevant warning */
-#endif
-
-
-/* *****************************************
-*  Error Codes
-******************************************/
-#define PREFIX(name) ZSTD_error_##name
-
-#ifdef ERROR
-#  undef ERROR   /* reported already defined on VS 2015 by Rich Geldreich */
-#endif
-#define ERROR(name) (size_t)-PREFIX(name)
-
-ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); }
-
-
-/* *****************************************
-*  Error Strings
-******************************************/
-
-ERR_STATIC const char* ERR_getErrorName(size_t code)
-{
-    static const char* codeError = "Unspecified error code";
-    switch( (size_t)(0-code) )
-    {
-    case ZSTD_error_No_Error: return "No error detected";
-    case ZSTD_error_GENERIC:  return "Error (generic)";
-    case ZSTD_error_prefix_unknown: return "Unknown frame descriptor";
-    case ZSTD_error_frameParameter_unsupported: return "Unsupported frame parameter";
-    case ZSTD_error_frameParameter_unsupportedBy32bitsImplementation: return "Frame parameter unsupported in 32-bits mode";
-    case ZSTD_error_init_missing: return "Context should be init first";
-    case ZSTD_error_memory_allocation: return "Allocation error : not enough memory";
-    case ZSTD_error_dstSize_tooSmall: return "Destination buffer is too small";
-    case ZSTD_error_srcSize_wrong: return "Src size incorrect";
-    case ZSTD_error_corruption_detected: return "Corrupted block detected";
-    case ZSTD_error_tableLog_tooLarge: return "tableLog requires too much memory";
-    case ZSTD_error_maxSymbolValue_tooLarge: return "Unsupported max possible Symbol Value : too large";
-    case ZSTD_error_maxSymbolValue_tooSmall: return "Specified maxSymbolValue is too small";
-    case ZSTD_error_maxCode:
-    default: return codeError;
-    }
-}
-
-
-#if defined (__cplusplus)
-}
-#endif
-
-#endif /* ERROR_H_MODULE */
-
 
 #endif  /* ZSTD_STATIC_H */
 
@@ -1380,12 +1199,15 @@ MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr)
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4214)        /* disable: C4214: non-int bitfields */
 #else
-#  ifdef __GNUC__
-#    define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#    define FORCE_INLINE static inline __attribute__((always_inline))
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#    ifdef __GNUC__
+#      define FORCE_INLINE static inline __attribute__((always_inline))
+#    else
+#      define FORCE_INLINE static inline
+#    endif
 #  else
-#    define FORCE_INLINE static inline
-#  endif
+#    define FORCE_INLINE static
+#  endif /* __STDC_VERSION__ */
 #endif
 
 
@@ -2021,15 +1843,7 @@ static size_t HUF_decompress4X4_usingDTable(void* dst, size_t maxDstSize, const
 
 
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
-#else
-#  ifdef __GNUC__
-#    define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
@@ -2837,17 +2651,9 @@ static size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_
 *  Compiler specifics
 *********************************************************/
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  include <intrin.h>                    /* For Visual 2005 */
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4324)        /* disable: C4324: padded structure */
-#else
-#  define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
@@ -2968,7 +2774,7 @@ static size_t ZSTD_decodeFrameHeader_Part2(ZSTD_DCtx* zc, const void* src, size_
     size_t result;
     if (srcSize != zc->headerSize) return ERROR(srcSize_wrong);
     result = ZSTD_getFrameParams(&(zc->params), src, srcSize);
-    if ((MEM_32bits()) && (zc->params.windowLog > 25)) return ERROR(frameParameter_unsupportedBy32bitsImplementation);
+    if ((MEM_32bits()) && (zc->params.windowLog > 25)) return ERROR(frameParameter_unsupportedBy32bits);
     return result;
 }
 
diff --git a/lib/legacy/zstd_v05.c b/lib/legacy/zstd_v05.c
index 96fdf35..5027e2b 100644
--- a/lib/legacy/zstd_v05.c
+++ b/lib/legacy/zstd_v05.c
@@ -10,6 +10,7 @@
 
 /*- Dependencies -*/
 #include "zstd_v05.h"
+#include "error_private.h"
 
 
 /* ******************************************************************
@@ -254,79 +255,6 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr)
 
 #endif /* MEM_H_MODULE */
 
-/* ******************************************************************
-   Error codes list
-   Copyright (C) 2016, Yann Collet
-
-   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met:
-
-       * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above
-   copyright notice, this list of conditions and the following disclaimer
-   in the documentation and/or other materials provided with the
-   distribution.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-   You can contact the author at :
-   - Source repository : https://github.com/Cyan4973/zstd
-****************************************************************** */
-#ifndef ERROR_PUBLIC_H_MODULE
-#define ERROR_PUBLIC_H_MODULE
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-
-/* ****************************************
-*  error codes list
-******************************************/
-typedef enum {
-  ZSTDv05_error_no_error,
-  ZSTDv05_error_GENERIC,
-  ZSTDv05_error_prefix_unknown,
-  ZSTDv05_error_frameParameter_unsupported,
-  ZSTDv05_error_frameParameter_unsupportedBy32bits,
-  ZSTDv05_error_init_missing,
-  ZSTDv05_error_memory_allocation,
-  ZSTDv05_error_stage_wrong,
-  ZSTDv05_error_dstSize_tooSmall,
-  ZSTDv05_error_srcSize_wrong,
-  ZSTDv05_error_corruption_detected,
-  ZSTDv05_error_tableLog_tooLarge,
-  ZSTDv05_error_maxSymbolValue_tooLarge,
-  ZSTDv05_error_maxSymbolValue_tooSmall,
-  ZSTDv05_error_dictionary_corrupted,
-  ZSTDv05_error_maxCode
-} ZSTDv05_ErrorCode;
-
-/* note : functions provide error codes in reverse negative order,
-          so compare with (size_t)(0-enum) */
-
-
-#if defined (__cplusplus)
-}
-#endif
-
-#endif /* ERROR_PUBLIC_H_MODULE */
-
-
 /*
     zstd - standard compression library
     Header File for static linking only
@@ -470,119 +398,6 @@ size_t ZSTDv05_decompressBlock(ZSTDv05_DCtx* dctx, void* dst, size_t dstCapacity
 #endif  /* ZSTDv05_STATIC_H */
 
 
-
-/* ******************************************************************
-   Error codes and messages
-   Copyright (C) 2013-2016, Yann Collet
-
-   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met:
-
-       * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above
-   copyright notice, this list of conditions and the following disclaimer
-   in the documentation and/or other materials provided with the
-   distribution.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-   You can contact the author at :
-   - Source repository : https://github.com/Cyan4973/zstd
-****************************************************************** */
-/* Note : this module is expected to remain private, do not expose it */
-
-#ifndef ERROR_H_MODULE
-#define ERROR_H_MODULE
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-
-
-/* ****************************************
-*  Compiler-specific
-******************************************/
-#if defined(__GNUC__)
-#  define ERR_STATIC static __attribute__((unused))
-#elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-#  define ERR_STATIC static inline
-#elif defined(_MSC_VER)
-#  define ERR_STATIC static __inline
-#else
-#  define ERR_STATIC static  /* this version may generate warnings for unused static functions; disable the relevant warning */
-#endif
-
-
-/*-****************************************
-*  Customization
-******************************************/
-typedef ZSTDv05_ErrorCode ERR_enum;
-#define PREFIX(name) ZSTDv05_error_##name
-
-
-/*-****************************************
-*  Error codes handling
-******************************************/
-#ifdef ERROR
-#  undef ERROR   /* reported already defined on VS 2015 (Rich Geldreich) */
-#endif
-#define ERROR(name) (size_t)-PREFIX(name)
-
-ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); }
-
-ERR_STATIC ERR_enum ERR_getError(size_t code) { if (!ERR_isError(code)) return (ERR_enum)0; return (ERR_enum) (0-code); }
-
-
-/*-****************************************
-*  Error Strings
-******************************************/
-
-ERR_STATIC const char* ERR_getErrorName(size_t code)
-{
-    static const char* notErrorCode = "Unspecified error code";
-    switch( ERR_getError(code) )
-    {
-    case PREFIX(no_error): return "No error detected";
-    case PREFIX(GENERIC):  return "Error (generic)";
-    case PREFIX(prefix_unknown): return "Unknown frame descriptor";
-    case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter";
-    case PREFIX(frameParameter_unsupportedBy32bits): return "Frame parameter unsupported in 32-bits mode";
-    case PREFIX(init_missing): return "Context should be init first";
-    case PREFIX(memory_allocation): return "Allocation error : not enough memory";
-    case PREFIX(stage_wrong): return "Operation not authorized at current processing stage";
-    case PREFIX(dstSize_tooSmall): return "Destination buffer is too small";
-    case PREFIX(srcSize_wrong): return "Src size incorrect";
-    case PREFIX(corruption_detected): return "Corrupted block detected";
-    case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory";
-    case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max possible Symbol Value : too large";
-    case PREFIX(maxSymbolValue_tooSmall): return "Specified maxSymbolValue is too small";
-    case PREFIX(dictionary_corrupted): return "Dictionary is corrupted";
-    case PREFIX(maxCode):
-    default: return notErrorCode;   /* should be impossible, due to ERR_getError() */
-    }
-}
-
-
-#if defined (__cplusplus)
-}
-#endif
-
-#endif /* ERROR_H_MODULE */
 /*
     zstd_internal - common functions to include
     Header File for include
@@ -1380,12 +1195,15 @@ MEM_STATIC unsigned FSEv05_endOfDState(const FSEv05_DState_t* DStatePtr)
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4214)        /* disable: C4214: non-int bitfields */
 #else
-#  ifdef __GNUC__
-#    define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#    define FORCE_INLINE static inline __attribute__((always_inline))
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#    ifdef __GNUC__
+#      define FORCE_INLINE static inline __attribute__((always_inline))
+#    else
+#      define FORCE_INLINE static inline
+#    endif
 #  else
-#    define FORCE_INLINE static inline
-#  endif
+#    define FORCE_INLINE static
+#  endif /* __STDC_VERSION__ */
 #endif
 
 
@@ -2002,14 +1820,7 @@ size_t HUFv05_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void
 
 
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
-#else
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
@@ -2858,17 +2669,9 @@ size_t HUFv05_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cS
 *  Compiler specifics
 *********************************************************/
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  include <intrin.h>                    /* For Visual 2005 */
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4324)        /* disable: C4324: padded structure */
-#else
-#  define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
@@ -2895,9 +2698,6 @@ static void ZSTDv05_copy4(void* dst, const void* src) { memcpy(dst, src, 4); }
 *   tells if a return value is an error code */
 unsigned ZSTDv05_isError(size_t code) { return ERR_isError(code); }
 
-/*! ZSTDv05_getError() :
-*   convert a `size_t` function result into a proper ZSTDv05_errorCode enum */
-ZSTDv05_ErrorCode ZSTDv05_getError(size_t code) { return ERR_getError(code); }
 
 /*! ZSTDv05_getErrorName() :
 *   provides error code string (useful for debugging) */
diff --git a/lib/legacy/zstd_v06.c b/lib/legacy/zstd_v06.c
index 6bd463b..d9e89f8 100644
--- a/lib/legacy/zstd_v06.c
+++ b/lib/legacy/zstd_v06.c
@@ -13,6 +13,7 @@
 #include <stddef.h>    /* size_t, ptrdiff_t */
 #include <string.h>    /* memcpy */
 #include <stdlib.h>    /* malloc, free, qsort */
+#include "error_private.h"
 
 
 
@@ -273,77 +274,6 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr)
 
 #endif /* MEM_H_MODULE */
 
-/* ******************************************************************
-   Error codes list
-   Copyright (C) 2016, Yann Collet
-
-   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met:
-
-       * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above
-   copyright notice, this list of conditions and the following disclaimer
-   in the documentation and/or other materials provided with the
-   distribution.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-   You can contact the author at :
-   - Homepage : http://www.zstd.net
-****************************************************************** */
-#ifndef ERROR_PUBLIC_H_MODULE
-#define ERROR_PUBLIC_H_MODULE
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-
-/* ****************************************
-*  error codes list
-******************************************/
-typedef enum {
-  ZSTDv06_error_no_error,
-  ZSTDv06_error_GENERIC,
-  ZSTDv06_error_prefix_unknown,
-  ZSTDv06_error_frameParameter_unsupported,
-  ZSTDv06_error_frameParameter_unsupportedBy32bits,
-  ZSTDv06_error_compressionParameter_unsupported,
-  ZSTDv06_error_init_missing,
-  ZSTDv06_error_memory_allocation,
-  ZSTDv06_error_stage_wrong,
-  ZSTDv06_error_dstSize_tooSmall,
-  ZSTDv06_error_srcSize_wrong,
-  ZSTDv06_error_corruption_detected,
-  ZSTDv06_error_tableLog_tooLarge,
-  ZSTDv06_error_maxSymbolValue_tooLarge,
-  ZSTDv06_error_maxSymbolValue_tooSmall,
-  ZSTDv06_error_dictionary_corrupted,
-  ZSTDv06_error_maxCode
-} ZSTDv06_ErrorCode;
-
-/* note : compare with size_t function results using ZSTDv06_getError() */
-
-
-#if defined (__cplusplus)
-}
-#endif
-
-#endif /* ERROR_PUBLIC_H_MODULE */
 /*
     zstd - standard compression library
     Header File for static linking only
@@ -396,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);
@@ -407,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)
@@ -466,140 +396,15 @@ 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);
 
 
-/*-*************************************
-*  Error management
-***************************************/
-/*! ZSTDv06_getErrorCode() :
-    convert a `size_t` function result into a `ZSTDv06_ErrorCode` enum type,
-    which can be used to compare directly with enum list published into "error_public.h" */
-ZSTDLIB_API ZSTDv06_ErrorCode ZSTDv06_getErrorCode(size_t functionResult);
-ZSTDLIB_API const char* ZSTDv06_getErrorString(ZSTDv06_ErrorCode code);
-
 
 #if defined (__cplusplus)
 }
 #endif
 
 #endif  /* ZSTDv06_STATIC_H */
-/* ******************************************************************
-   Error codes and messages
-   Copyright (C) 2013-2016, Yann Collet
-
-   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met:
-
-       * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above
-   copyright notice, this list of conditions and the following disclaimer
-   in the documentation and/or other materials provided with the
-   distribution.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-   You can contact the author at :
-   - Homepage : http://www.zstd.net
-****************************************************************** */
-/* Note : this module is expected to remain private, do not expose it */
-
-#ifndef ERROR_H_MODULE
-#define ERROR_H_MODULE
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-
-/* ****************************************
-*  Compiler-specific
-******************************************/
-#if defined(__GNUC__)
-#  define ERR_STATIC static __attribute__((unused))
-#elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-#  define ERR_STATIC static inline
-#elif defined(_MSC_VER)
-#  define ERR_STATIC static __inline
-#else
-#  define ERR_STATIC static  /* this version may generate warnings for unused static functions; disable the relevant warning */
-#endif
-
-
-/*-****************************************
-*  Customization (error_public.h)
-******************************************/
-typedef ZSTDv06_ErrorCode ERR_enum;
-#define PREFIX(name) ZSTDv06_error_##name
-
-
-/*-****************************************
-*  Error codes handling
-******************************************/
-#ifdef ERROR
-#  undef ERROR   /* reported already defined on VS 2015 (Rich Geldreich) */
-#endif
-#define ERROR(name) ((size_t)-PREFIX(name))
-
-ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); }
-
-ERR_STATIC ERR_enum ERR_getErrorCode(size_t code) { if (!ERR_isError(code)) return (ERR_enum)0; return (ERR_enum) (0-code); }
-
-
-/*-****************************************
-*  Error Strings
-******************************************/
-
-ERR_STATIC const char* ERR_getErrorString(ERR_enum code)
-{
-    static const char* notErrorCode = "Unspecified error code";
-    switch( code )
-    {
-    case PREFIX(no_error): return "No error detected";
-    case PREFIX(GENERIC):  return "Error (generic)";
-    case PREFIX(prefix_unknown): return "Unknown frame descriptor";
-    case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter";
-    case PREFIX(frameParameter_unsupportedBy32bits): return "Frame parameter unsupported in 32-bits mode";
-    case PREFIX(compressionParameter_unsupported): return "Compression parameter is out of bound";
-    case PREFIX(init_missing): return "Context should be init first";
-    case PREFIX(memory_allocation): return "Allocation error : not enough memory";
-    case PREFIX(stage_wrong): return "Operation not authorized at current processing stage";
-    case PREFIX(dstSize_tooSmall): return "Destination buffer is too small";
-    case PREFIX(srcSize_wrong): return "Src size incorrect";
-    case PREFIX(corruption_detected): return "Corrupted block detected";
-    case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported";
-    case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large";
-    case PREFIX(maxSymbolValue_tooSmall): return "Specified maxSymbolValue is too small";
-    case PREFIX(dictionary_corrupted): return "Dictionary is corrupted";
-    case PREFIX(maxCode):
-    default: return notErrorCode;
-    }
-}
-
-ERR_STATIC const char* ERR_getErrorName(size_t code)
-{
-    return ERR_getErrorString(ERR_getErrorCode(code));
-}
-
-#if defined (__cplusplus)
-}
-#endif
-
-#endif /* ERROR_H_MODULE */
 /*
     zstd_internal - common functions to include
     Header File for include
@@ -1665,12 +1470,15 @@ size_t FSEv06_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4214)        /* disable: C4214: non-int bitfields */
 #else
-#  ifdef __GNUC__
-#    define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#    define FORCE_INLINE static inline __attribute__((always_inline))
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#    ifdef __GNUC__
+#      define FORCE_INLINE static inline __attribute__((always_inline))
+#    else
+#      define FORCE_INLINE static inline
+#    endif
 #  else
-#    define FORCE_INLINE static inline
-#  endif
+#    define FORCE_INLINE static
+#  endif /* __STDC_VERSION__ */
 #endif
 
 
@@ -2238,14 +2046,7 @@ MEM_STATIC size_t HUFv06_readStats(BYTE* huffWeight, size_t hwSize, U32* rankSta
 
 
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
-#else
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
@@ -2990,14 +2791,6 @@ unsigned ZSTDv06_isError(size_t code) { return ERR_isError(code); }
 *   provides error code string from function result (useful for debugging) */
 const char* ZSTDv06_getErrorName(size_t code) { return ERR_getErrorName(code); }
 
-/*! ZSTDv06_getError() :
-*   convert a `size_t` function result into a proper ZSTDv06_errorCode enum */
-ZSTDv06_ErrorCode ZSTDv06_getErrorCode(size_t code) { return ERR_getErrorCode(code); }
-
-/*! ZSTDv06_getErrorString() :
-*   provides error code string from enum */
-const char* ZSTDv06_getErrorString(ZSTDv06_ErrorCode code) { return ERR_getErrorName(code); }
-
 
 /* **************************************************************
 *  ZBUFF Error Management
@@ -3054,16 +2847,9 @@ const char* ZBUFFv06_getErrorName(size_t errorCode) { return ERR_getErrorName(er
 *  Compiler specifics
 *********************************************************/
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  include <intrin.h>                    /* For Visual 2005 */
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4324)        /* disable: C4324: padded structure */
-#else
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
diff --git a/lib/legacy/zstd_v06.h b/lib/legacy/zstd_v06.h
index bcc6efb..14040ab 100644
--- a/lib/legacy/zstd_v06.h
+++ b/lib/legacy/zstd_v06.h
@@ -14,23 +14,19 @@
 extern "C" {
 #endif
 
-/*-*************************************
-*  Dependencies
-***************************************/
+/*======  Dependency  ======*/
 #include <stddef.h>   /* 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 f948069..f4c8073 100644
--- a/lib/legacy/zstd_v07.c
+++ b/lib/legacy/zstd_v07.c
@@ -21,6 +21,8 @@
 #define HUFv07_STATIC_LINKING_ONLY  /* HUFv07_TABLELOG_ABSOLUTEMAX */
 #define ZSTDv07_STATIC_LINKING_ONLY
 
+#include "error_private.h"
+
 
 #ifdef ZSTDv07_STATIC_LINKING_ONLY
 
@@ -66,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)
@@ -167,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 */
@@ -427,203 +429,6 @@ MEM_STATIC size_t MEM_readLEST(const void* memPtr)
 #endif
 
 #endif /* MEM_H_MODULE */
-
-/* ******************************************************************
-   Error codes list
-   Copyright (C) 2016, Yann Collet
-
-   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met:
-
-       * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above
-   copyright notice, this list of conditions and the following disclaimer
-   in the documentation and/or other materials provided with the
-   distribution.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-   You can contact the author at :
-   - Homepage : http://www.zstd.net
-****************************************************************** */
-#ifndef ERROR_PUBLIC_H_MODULE
-#define ERROR_PUBLIC_H_MODULE
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-
-/* ****************************************
-*  error codes list
-******************************************/
-typedef enum {
-  ZSTDv07_error_no_error,
-  ZSTDv07_error_GENERIC,
-  ZSTDv07_error_prefix_unknown,
-  ZSTDv07_error_frameParameter_unsupported,
-  ZSTDv07_error_frameParameter_unsupportedBy32bits,
-  ZSTDv07_error_compressionParameter_unsupported,
-  ZSTDv07_error_init_missing,
-  ZSTDv07_error_memory_allocation,
-  ZSTDv07_error_stage_wrong,
-  ZSTDv07_error_dstSize_tooSmall,
-  ZSTDv07_error_srcSize_wrong,
-  ZSTDv07_error_corruption_detected,
-  ZSTDv07_error_checksum_wrong,
-  ZSTDv07_error_tableLog_tooLarge,
-  ZSTDv07_error_maxSymbolValue_tooLarge,
-  ZSTDv07_error_maxSymbolValue_tooSmall,
-  ZSTDv07_error_dictionary_corrupted,
-  ZSTDv07_error_dictionary_wrong,
-  ZSTDv07_error_maxCode
-} ZSTDv07_ErrorCode;
-
-/*! ZSTDv07_getErrorCode() :
-    convert a `size_t` function result into a `ZSTDv07_ErrorCode` enum type,
-    which can be used to compare directly with enum list published into "error_public.h" */
-ZSTDv07_ErrorCode ZSTDv07_getErrorCode(size_t functionResult);
-const char* ZSTDv07_getErrorString(ZSTDv07_ErrorCode code);
-
-
-#if defined (__cplusplus)
-}
-#endif
-
-#endif /* ERROR_PUBLIC_H_MODULE */
-/* ******************************************************************
-   Error codes and messages
-   Copyright (C) 2013-2016, Yann Collet
-
-   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met:
-
-       * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above
-   copyright notice, this list of conditions and the following disclaimer
-   in the documentation and/or other materials provided with the
-   distribution.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-   You can contact the author at :
-   - Homepage : http://www.zstd.net
-****************************************************************** */
-/* Note : this module is expected to remain private, do not expose it */
-
-#ifndef ERROR_H_MODULE
-#define ERROR_H_MODULE
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-
-
-/* ****************************************
-*  Compiler-specific
-******************************************/
-#if defined(__GNUC__)
-#  define ERR_STATIC static __attribute__((unused))
-#elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
-#  define ERR_STATIC static inline
-#elif defined(_MSC_VER)
-#  define ERR_STATIC static __inline
-#else
-#  define ERR_STATIC static  /* this version may generate warnings for unused static functions; disable the relevant warning */
-#endif
-
-
-/*-****************************************
-*  Customization (error_public.h)
-******************************************/
-typedef ZSTDv07_ErrorCode ERR_enum;
-#define PREFIX(name) ZSTDv07_error_##name
-
-
-/*-****************************************
-*  Error codes handling
-******************************************/
-#ifdef ERROR
-#  undef ERROR   /* reported already defined on VS 2015 (Rich Geldreich) */
-#endif
-#define ERROR(name) ((size_t)-PREFIX(name))
-
-ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); }
-
-ERR_STATIC ERR_enum ERR_getErrorCode(size_t code) { if (!ERR_isError(code)) return (ERR_enum)0; return (ERR_enum) (0-code); }
-
-
-/*-****************************************
-*  Error Strings
-******************************************/
-
-ERR_STATIC const char* ERR_getErrorString(ERR_enum code)
-{
-    static const char* notErrorCode = "Unspecified error code";
-    switch( code )
-    {
-    case PREFIX(no_error): return "No error detected";
-    case PREFIX(GENERIC):  return "Error (generic)";
-    case PREFIX(prefix_unknown): return "Unknown frame descriptor";
-    case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter";
-    case PREFIX(frameParameter_unsupportedBy32bits): return "Frame parameter unsupported in 32-bits mode";
-    case PREFIX(compressionParameter_unsupported): return "Compression parameter is out of bound";
-    case PREFIX(init_missing): return "Context should be init first";
-    case PREFIX(memory_allocation): return "Allocation error : not enough memory";
-    case PREFIX(stage_wrong): return "Operation not authorized at current processing stage";
-    case PREFIX(dstSize_tooSmall): return "Destination buffer is too small";
-    case PREFIX(srcSize_wrong): return "Src size incorrect";
-    case PREFIX(corruption_detected): return "Corrupted block detected";
-    case PREFIX(checksum_wrong): return "Restored data doesn't match checksum";
-    case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported";
-    case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large";
-    case PREFIX(maxSymbolValue_tooSmall): return "Specified maxSymbolValue is too small";
-    case PREFIX(dictionary_corrupted): return "Dictionary is corrupted";
-    case PREFIX(dictionary_wrong): return "Dictionary mismatch";
-    case PREFIX(maxCode):
-    default: return notErrorCode;
-    }
-}
-
-ERR_STATIC const char* ERR_getErrorName(size_t code)
-{
-    return ERR_getErrorString(ERR_getErrorCode(code));
-}
-
-#if defined (__cplusplus)
-}
-#endif
-
-#endif /* ERROR_H_MODULE */
 /* ******************************************************************
    bitstream
    Part of FSE library
@@ -845,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;
@@ -1680,17 +1485,18 @@ size_t HUFv07_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats,
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4214)        /* disable: C4214: non-int bitfields */
 #else
-#  ifdef __GNUC__
-#    define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#    define FORCE_INLINE static inline __attribute__((always_inline))
+#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+#    ifdef __GNUC__
+#      define FORCE_INLINE static inline __attribute__((always_inline))
+#    else
+#      define FORCE_INLINE static inline
+#    endif
 #  else
-#    define FORCE_INLINE static inline
-#  endif
+#    define FORCE_INLINE static
+#  endif /* __STDC_VERSION__ */
 #endif
 
 
-
-
 /* **************************************************************
 *  Error Management
 ****************************************************************/
@@ -2006,14 +1812,7 @@ size_t FSEv07_decompress(void* dst, size_t maxDstSize, const void* cSrc, size_t
 
 
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
-#else
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
@@ -2888,13 +2687,6 @@ unsigned ZSTDv07_isError(size_t code) { return ERR_isError(code); }
 *   provides error code string from function result (useful for debugging) */
 const char* ZSTDv07_getErrorName(size_t code) { return ERR_getErrorName(code); }
 
-/*! ZSTDv07_getError() :
-*   convert a `size_t` function result into a proper ZSTDv07_errorCode enum */
-ZSTDv07_ErrorCode ZSTDv07_getErrorCode(size_t code) { return ERR_getErrorCode(code); }
-
-/*! ZSTDv07_getErrorString() :
-*   provides error code string from enum */
-const char* ZSTDv07_getErrorString(ZSTDv07_ErrorCode code) { return ERR_getErrorName(code); }
 
 
 /* **************************************************************
@@ -3176,17 +2968,10 @@ static const ZSTDv07_customMem defaultCustomMem = { ZSTDv07_defaultAllocFunction
 *  Compiler specifics
 *********************************************************/
 #ifdef _MSC_VER    /* Visual Studio */
-#  define FORCE_INLINE static __forceinline
 #  include <intrin.h>                    /* For Visual 2005 */
 #  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
 #  pragma warning(disable : 4324)        /* disable: C4324: padded structure */
 #  pragma warning(disable : 4100)        /* disable: C4100: unreferenced formal parameter */
-#else
-#  ifdef __GNUC__
-#    define FORCE_INLINE static inline __attribute__((always_inline))
-#  else
-#    define FORCE_INLINE static inline
-#  endif
 #endif
 
 
@@ -4046,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;
@@ -4448,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)
@@ -4535,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 d1fbc08..30725dc 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/libzstd.pc.in b/lib/libzstd.pc.in
index 28afc3a..9399363 100644
--- a/lib/libzstd.pc.in
+++ b/lib/libzstd.pc.in
@@ -8,7 +8,7 @@ includedir=@INCLUDEDIR@
 
 Name: zstd
 Description: lossless compression algorithm library
-URL: https://github.com/Cyan4973/zstd
+URL: https://github.com/facebook/zstd
 Version: @VERSION@
 Libs: -L at LIBDIR@ -lzstd
 Cflags: -I at INCLUDEDIR@
diff --git a/lib/zstd.h b/lib/zstd.h
index d768ded..dd3f5df 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -32,7 +32,7 @@ extern "C" {
 
 /*=======   Version   =======*/
 #define ZSTD_VERSION_MAJOR    1
-#define ZSTD_VERSION_MINOR    0
+#define ZSTD_VERSION_MINOR    1
 #define ZSTD_VERSION_RELEASE  0
 
 #define ZSTD_LIB_VERSION ZSTD_VERSION_MAJOR.ZSTD_VERSION_MINOR.ZSTD_VERSION_RELEASE
@@ -48,40 +48,41 @@ ZSTDLIB_API unsigned ZSTD_versionNumber (void);
 *  Simple API
 ***************************************/
 /*! ZSTD_compress() :
-    Compresses `src` buffer into already allocated `dst`.
+    Compresses `src` content as a single zstd compressed frame into already allocated `dst`.
     Hint : compression runs faster if `dstCapacity` >=  `ZSTD_compressBound(srcSize)`.
-    @return : the number of bytes written into `dst` (<= `dstCapacity),
+    @return : compressed size written into `dst` (<= `dstCapacity),
               or an error code if it fails (which can be tested using ZSTD_isError()) */
 ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity,
                             const void* src, size_t srcSize,
                                   int compressionLevel);
 
-/*! ZSTD_getDecompressedSize() :
-*   @return : decompressed size as a 64-bits value _if known_, 0 otherwise.
-*    note 1 : decompressed size can be very large (64-bits value),
-*             potentially larger than what local system can handle as a single memory segment.
-*             In which case, it's necessary to use streaming mode to decompress data.
-*    note 2 : decompressed size is an optional field, that may not be present.
-*             When `return==0`, data to decompress can have any size.
-*             In which case, it's necessary to use streaming mode to decompress data.
-*             Optionally, application may rely on its own implied limits.
-*             (For example, application data could be necessarily cut into blocks <= 16 KB).
-*    note 3 : decompressed size could be wrong or intentionally modified !
-*             Always ensure result fits within application's authorized limits !
-*             Each application can set its own limits.
-*    note 4 : when `return==0`, if precise failure cause is needed, use ZSTD_getFrameParams() to know more. */
-ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
-
 /*! ZSTD_decompress() :
-    `compressedSize` : must be the _exact_ size of compressed input, otherwise decompression will fail.
-    `dstCapacity` must be equal or larger than originalSize (see ZSTD_getDecompressedSize() ).
-    If originalSize is unknown, and if there is no implied application-specific limitations,
-    it's preferable to use streaming mode to decompress data.
+    `compressedSize` : must be the _exact_ size of a single compressed frame.
+    `dstCapacity` is an upper bound of originalSize.
+    If user cannot imply a maximum upper bound, it's better to use streaming mode to decompress data.
     @return : the number of bytes decompressed into `dst` (<= `dstCapacity`),
               or an errorCode if it fails (which can be tested using ZSTD_isError()) */
 ZSTDLIB_API size_t ZSTD_decompress( void* dst, size_t dstCapacity,
                               const void* src, size_t compressedSize);
 
+/*! ZSTD_getDecompressedSize() :
+*   'src' is the start of a zstd compressed frame.
+*   @return : content size to be decompressed, as a 64-bits value _if known_, 0 otherwise.
+*    note 1 : decompressed size is an optional field, that may not be present, especially in streaming mode.
+*             When `return==0`, data to decompress could be any size.
+*             In which case, it's necessary to use streaming mode to decompress data.
+*             Optionally, application can still use ZSTD_decompress() while relying on implied limits.
+*             (For example, data may be necessarily cut into blocks <= 16 KB).
+*    note 2 : decompressed size is always present when compression is done with ZSTD_compress()
+*    note 3 : decompressed size can be very large (64-bits value),
+*             potentially larger than what local system can handle as a single memory segment.
+*             In which case, it's necessary to use streaming mode to decompress data.
+*    note 4 : If source is untrusted, decompressed size could be wrong or intentionally modified.
+*             Always ensure result fits within application's authorized limits.
+*             Each application can set its own limits.
+*    note 5 : when `return==0`, if precise failure cause is needed, use ZSTD_getFrameParams() to know more. */
+ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
+
 
 /*======  Helper functions  ======*/
 ZSTDLIB_API int         ZSTD_maxCLevel(void);               /*!< maximum compression level available */
@@ -200,7 +201,7 @@ typedef struct ZSTD_outBuffer_s {
 *  Use ZSTD_initCStream_usingDict() for a compression which requires a dictionary.
 *
 *  Use ZSTD_compressStream() repetitively to consume input stream.
-*  The function will automatically update both `pos`.
+*  The function will automatically update both `pos` fields.
 *  Note that it may not consume the entire input, in which case `pos < size`,
 *  and it's up to the caller to present again remaining data.
 *  @return : a size hint, preferred nb of bytes to use as input for next function call
@@ -229,7 +230,7 @@ ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream(void);
 ZSTDLIB_API size_t ZSTD_freeCStream(ZSTD_CStream* zcs);
 
 ZSTDLIB_API size_t ZSTD_CStreamInSize(void);    /**< recommended size for input buffer */
-ZSTDLIB_API size_t ZSTD_CStreamOutSize(void);   /**< recommended size for output buffer */
+ZSTDLIB_API size_t ZSTD_CStreamOutSize(void);   /**< recommended size for output buffer. Guarantee to successfully flush at least one complete compressed block in all circumstances. */
 
 ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel);
 ZSTDLIB_API size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
@@ -248,16 +249,17 @@ ZSTDLIB_API size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output);
 *
 *  Use ZSTD_initDStream() to start a new decompression operation,
 *   or ZSTD_initDStream_usingDict() if decompression requires a dictionary.
+*   @return : recommended first input size
 *
 *  Use ZSTD_decompressStream() repetitively to consume your input.
-*  The function will update both `pos`.
-*  Note that it may not consume the entire input (pos < size),
-*  in which case it's up to the caller to present remaining input again.
+*  The function will update both `pos` fields.
+*  If `input.pos < input.size`, some input has not been consumed.
+*  It's up to the caller to present again remaining data.
+*  If `output.pos < output.size`, decoder has flushed everything it could.
 *  @return : 0 when a frame is completely decoded and fully flushed,
-*            1 when there is still some data left within internal buffer to flush,
-*            >1 when more data is expected, with value being a suggested next input size (it's just a hint, which helps latency, any size is accepted),
-*            or an error code, which can be tested using ZSTD_isError().
-*
+*            an error code, which can be tested using ZSTD_isError(),
+*            any other value > 0, which means there is still some work to do to complete the frame.
+*            The return value is a suggested next input size (just an hint, to help latency).
 * *******************************************************************************/
 
 typedef struct ZSTD_DStream_s ZSTD_DStream;
@@ -265,7 +267,7 @@ ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream(void);
 ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds);
 
 ZSTDLIB_API size_t ZSTD_DStreamInSize(void);    /*!< recommended size for input buffer */
-ZSTDLIB_API size_t ZSTD_DStreamOutSize(void);   /*!< recommended size for output buffer */
+ZSTDLIB_API size_t ZSTD_DStreamOutSize(void);   /*!< recommended size for output buffer. Guarantee to successfully flush at least one complete block in all circumstances. */
 
 ZSTDLIB_API size_t ZSTD_initDStream(ZSTD_DStream* zds);
 ZSTDLIB_API size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
@@ -288,21 +290,22 @@ 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
-#define ZSTD_SEARCHLENGTH_MAX   7
-#define ZSTD_SEARCHLENGTH_MIN   3
+#define ZSTD_SEARCHLENGTH_MAX   7   /* only for ZSTD_fast, other strategies are limited to 6 */
+#define ZSTD_SEARCHLENGTH_MIN   3   /* only for ZSTD_btopt, other strategies are limited to 4 */
 #define ZSTD_TARGETLENGTH_MIN   4
 #define ZSTD_TARGETLENGTH_MAX 999
 
 #define ZSTD_FRAMEHEADERSIZE_MAX 18    /* for static allocation */
-static const size_t ZSTD_frameHeaderSize_min = 5;
+static const size_t ZSTD_frameHeaderSize_prefix = 5;
+static const size_t ZSTD_frameHeaderSize_min = 6;
 static const size_t ZSTD_frameHeaderSize_max = ZSTD_FRAMEHEADERSIZE_MAX;
 static const size_t ZSTD_skippableHeaderSize = 8;  /* magic number + skippable frame length */
 
@@ -349,14 +352,18 @@ ZSTDLIB_API size_t ZSTD_estimateCCtxSize(ZSTD_compressionParameters cParams);
  *  Create a ZSTD compression context using external alloc and free functions */
 ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
 
+/*! ZSTD_sizeofCCtx() :
+ *  Gives the amount of memory used by a given ZSTD_CCtx */
+ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx);
+
 /*! ZSTD_createCDict_advanced() :
  *  Create a ZSTD_CDict using external alloc and free, and customized compression parameters */
 ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize,
                                                   ZSTD_parameters params, ZSTD_customMem customMem);
 
-/*! ZSTD_sizeofCCtx() :
- *  Gives the amount of memory used by a given ZSTD_CCtx */
-ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx);
+/*! ZSTD_sizeof_CDict() :
+ *  Gives the amount of memory used by a given ZSTD_sizeof_CDict */
+ZSTDLIB_API size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict);
 
 /*! ZSTD_getParams() :
 *   same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of a `ZSTD_compressionParameters`.
@@ -396,10 +403,14 @@ ZSTDLIB_API size_t ZSTD_estimateDCtxSize(void);
  *  Create a ZSTD decompression context using external alloc and free functions */
 ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem);
 
-/*! ZSTD_sizeofDCtx() :
+/*! ZSTD_sizeof_DCtx() :
  *  Gives the amount of memory used by a given ZSTD_DCtx */
 ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx);
 
+/*! ZSTD_sizeof_DDict() :
+ *  Gives the amount of memory used by a given ZSTD_DDict */
+ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
+
 
 /* ******************************************************************
 *  Advanced Streaming functions
@@ -410,7 +421,8 @@ ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx);
 ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
 ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel);
 ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize,
-                                 ZSTD_parameters params, unsigned long long pledgedSrcSize);
+                                             ZSTD_parameters params, unsigned long long pledgedSrcSize);  /**< pledgedSrcSize is optional and can be zero == unknown */
+ZSTDLIB_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);  /**< re-use compression parameters from previous init; saves dictionary loading */
 ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs);
 
 
@@ -421,6 +433,7 @@ typedef enum { ZSTDdsp_maxWindowSize } ZSTD_DStreamParameter_e;
 ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
 ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize);
 ZSTDLIB_API size_t ZSTD_setDStreamParameter(ZSTD_DStream* zds, ZSTD_DStreamParameter_e paramType, unsigned paramValue);
+ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);  /**< re-use decompression parameters from previous init; saves dictionary loading */
 ZSTDLIB_API size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds);
 
 
@@ -434,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/.gitignore b/programs/.gitignore
index 5875fc7..24f96cf 100644
--- a/programs/.gitignore
+++ b/programs/.gitignore
@@ -7,6 +7,7 @@ zstd-decompress
 # Object files
 *.o
 *.ko
+default.profraw
 
 # Executables
 *.exe
diff --git a/programs/Makefile b/programs/Makefile
index fc634b6..6e78d0e 100644
--- a/programs/Makefile
+++ b/programs/Makefile
@@ -57,6 +57,13 @@ endif
 ifneq (,$(filter Windows%,$(OS)))
 EXT =.exe
 VOID = nul
+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
+    RES_FILE = $(RES32_FILE)
+endif
 else
 EXT =
 VOID = /dev/null
@@ -78,11 +85,11 @@ $(ZSTDDECOMP32_O): $(ZSTDDIR)/decompress/zstd_decompress.c
 
 zstd  : $(ZSTDDECOMP_O) $(ZSTD_FILES) $(ZSTDLEGACY_FILES) $(ZDICT_FILES) \
         zstdcli.c fileio.c bench.c datagen.c dibio.c
-	$(CC)      $(FLAGS) -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) $^ -o $@$(EXT)
+	$(CC)      $(FLAGS) -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) $^ $(RES_FILE) -o $@$(EXT)
 
 zstd32 : $(ZSTDDECOMP32_O) $(ZSTD_FILES) $(ZSTDLEGACY_FILES) $(ZDICT_FILES) \
         zstdcli.c fileio.c bench.c datagen.c dibio.c
-	$(CC)  -m32 $(FLAGS) -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) $^ -o $@$(EXT)
+	$(CC)  -m32 $(FLAGS) -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) $^ $(RES32_FILE) -o $@$(EXT)
 
 
 zstd_nolegacy :
@@ -97,6 +104,7 @@ zstd-pgo : clean zstd
 	./zstd -b7i2 $(PROFILE_WITH)
 	./zstd -b5 $(PROFILE_WITH)
 	$(RM) zstd
+	$(RM) $(ZSTDDIR)/decompress/zstd_decompress.o
 	$(MAKE) zstd MOREFLAGS=-fprofile-use
 
 zstd-frugal: $(ZSTDDECOMP_O) $(ZSTD_FILES) zstdcli.c fileio.c
@@ -116,16 +124,17 @@ zstd-small: clean
 
 clean:
 	$(MAKE) -C ../lib clean
-	@$(RM) ../lib/decompress/*.o
+	@$(RM) $(ZSTDDIR)/decompress/*.o $(ZSTDDIR)/decompress/zstd_decompress.gcda
 	@$(RM) core *.o tmp* result* *.gcda dictionary *.zst \
-        zstd$(EXT) zstd32$(EXT) zstd-compress$(EXT) zstd-decompress$(EXT)
+        zstd$(EXT) zstd32$(EXT) zstd-compress$(EXT) zstd-decompress$(EXT) \
+        *.gcda default.profraw
 	@echo Cleaning completed
 
 
 #----------------------------------------------------------------------------------
 #make install is validated only for Linux, OSX, kFreeBSD, Hurd and some BSD targets
 #----------------------------------------------------------------------------------
-ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD DragonFly))
+ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD DragonFly NetBSD))
 install: zstd
 	@echo Installing binaries
 	@install -d -m 755 $(DESTDIR)$(BINDIR)/ $(DESTDIR)$(MANDIR)/
@@ -141,9 +150,9 @@ install: zstd
 uninstall:
 	$(RM) $(DESTDIR)$(BINDIR)/zstdcat
 	$(RM) $(DESTDIR)$(BINDIR)/unzstd
-	[ -x $(DESTDIR)$(BINDIR)/zstd$(EXT) ] && $(RM) $(DESTDIR)$(BINDIR)/zstd$(EXT)
+	$(RM) $(DESTDIR)$(BINDIR)/zstd$(EXT)
 	$(RM) $(DESTDIR)$(MANDIR)/zstdcat.1
 	$(RM) $(DESTDIR)$(MANDIR)/unzstd.1
-	[ -f $(DESTDIR)$(MANDIR)/zstd.1 ] && $(RM) $(DESTDIR)$(MANDIR)/zstd.1
+	$(RM) $(DESTDIR)$(MANDIR)/zstd.1
 	@echo zstd programs successfully uninstalled
 endif
diff --git a/programs/bench.c b/programs/bench.c
index 3a290cc..c477b26 100644
--- a/programs/bench.c
+++ b/programs/bench.c
@@ -15,7 +15,7 @@
 #include <stdlib.h>      /* malloc, free */
 #include <string.h>      /* memset */
 #include <stdio.h>       /* fprintf, fopen, ftello64 */
-#include <time.h>         /* clock_t, clock, CLOCKS_PER_SEC */
+#include <time.h>        /* clock_t, clock, CLOCKS_PER_SEC */
 
 #include "mem.h"
 #define ZSTD_STATIC_LINKING_ONLY
@@ -24,7 +24,6 @@
 #include "xxhash.h"
 
 
-
 /* *************************************
 *  Constants
 ***************************************/
diff --git a/programs/datagen.c b/programs/datagen.c
index 109b8e3..1af5a38 100644
--- a/programs/datagen.c
+++ b/programs/datagen.c
@@ -20,10 +20,9 @@
 /*-************************************
 *  Dependencies
 **************************************/
-#include <stdlib.h>    /* malloc */
+#include <stdlib.h>    /* malloc, free */
 #include <stdio.h>     /* FILE, fwrite, fprintf */
 #include <string.h>    /* memcpy */
-#include <errno.h>     /* errno */
 #include "mem.h"       /* U32 */
 
 
@@ -176,7 +175,7 @@ void RDG_genStdout(unsigned long long size, double matchProba, double litProba,
     BYTE ldt[LTSIZE];   /* literals distribution table */
 
     /* init */
-    if (buff==NULL) { fprintf(stderr, "datagen: error: %s \n", strerror(errno)); exit(1); }
+    if (buff==NULL) { perror("datagen"); exit(1); }
     if (litProba<=0.0) litProba = matchProba / 4.5;
     memset(ldt, '0', sizeof(ldt));   /* yes, character '0', this is intentional */
     RDG_fillLiteralDistrib(ldt, litProba);
diff --git a/programs/datagen.h b/programs/datagen.h
index 55f9d82..094056b 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 <stddef.h>   /* 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 b7b201e..56f22fe 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -20,7 +20,7 @@
  ***************************************/
 #ifndef ZSTD_LEGACY_SUPPORT
 /* LEGACY_SUPPORT :
- *  decompressor can decode older formats (starting from Zstd 0.1+) */
+ *  decompressor can decode older formats (starting from zstd 0.1+) */
 #  define ZSTD_LEGACY_SUPPORT 1
 #endif
 
@@ -119,8 +119,6 @@ static clock_t g_time = 0;
 ***************************************/
 static U32 g_overwrite = 0;
 void FIO_overwriteMode(void) { g_overwrite=1; }
-static U32 g_maxWLog = 23;
-void FIO_setMaxWLog(unsigned maxWLog) { g_maxWLog = maxWLog; }
 static U32 g_sparseFileSupport = 1;   /* 0 : no sparse allowed; 1: auto (file yes, stdout no); 2: force sparse */
 void FIO_setSparseWrite(unsigned sparse) { g_sparseFileSupport=sparse; }
 static U32 g_dictIDFlag = 1;
@@ -167,7 +165,9 @@ static FILE* FIO_openSrcFile(const char* srcFileName)
     return f;
 }
 
-/* `dstFileName must` be non-NULL */
+/** FIO_openDstFile() :
+ * condition : `dstFileName` must be non-NULL.
+ * @result : FILE* to `dstFileName`, or NULL if it fails */
 static FILE* FIO_openDstFile(const char* dstFileName)
 {
     FILE* f;
@@ -250,14 +250,12 @@ typedef struct {
     size_t srcBufferSize;
     void*  dstBuffer;
     size_t dstBufferSize;
-    void*  dictBuffer;
-    size_t dictBufferSize;
     ZSTD_CStream* cctx;
     FILE* dstFile;
     FILE* srcFile;
 } cRess_t;
 
-static cRess_t FIO_createCResources(const char* dictFileName)
+static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, U64 srcSize)
 {
     cRess_t ress;
     memset(&ress, 0, sizeof(ress));
@@ -271,19 +269,27 @@ static cRess_t FIO_createCResources(const char* dictFileName)
     if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(31, "zstd: allocation error : not enough memory");
 
     /* dictionary */
-    ress.dictBufferSize = FIO_loadFile(&(ress.dictBuffer), dictFileName);
+    {   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, 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, srcSize);
+                if (ZSTD_isError(errorCode)) EXM_THROW(33, "Error initializing CStream : %s", ZSTD_getErrorName(errorCode));
+        }   }
+        free(dictBuffer);
+    }
 
     return ress;
 }
 
 static void FIO_freeCResources(cRess_t ress)
 {
-    size_t errorCode;
     free(ress.srcBuffer);
     free(ress.dstBuffer);
-    free(ress.dictBuffer);
-    errorCode = ZSTD_freeCStream(ress.cctx);
-    if (ZSTD_isError(errorCode)) EXM_THROW(38, "zstd: error : can't release ZSTD_CStream : %s", ZSTD_getErrorName(errorCode));
+    ZSTD_freeCStream(ress.cctx);   /* never fails */
 }
 
 
@@ -293,8 +299,7 @@ static void FIO_freeCResources(cRess_t ress)
  *            1 : missing or pb opening srcFileName
  */
 static int FIO_compressFilename_internal(cRess_t ress,
-                                         const char* dstFileName, const char* srcFileName,
-                                         int cLevel)
+                                         const char* dstFileName, const char* srcFileName)
 {
     FILE* const srcFile = ress.srcFile;
     FILE* const dstFile = ress.dstFile;
@@ -303,17 +308,9 @@ static int FIO_compressFilename_internal(cRess_t ress,
     U64 const fileSize = UTIL_getFileSize(srcFileName);
 
     /* init */
-    {   ZSTD_parameters params = ZSTD_getParams(cLevel, fileSize, ress.dictBufferSize);
-        params.fParams.contentSizeFlag = 1;
-        params.fParams.checksumFlag = g_checksumFlag;
-        params.fParams.noDictIDFlag = !g_dictIDFlag;
-        if ((g_maxWLog) && (params.cParams.windowLog > g_maxWLog)) {
-            params.cParams.windowLog = g_maxWLog;
-            params.cParams = ZSTD_adjustCParams(params.cParams, fileSize, ress.dictBufferSize);
-        }
-        {   size_t const errorCode = ZSTD_initCStream_advanced(ress.cctx, ress.dictBuffer, ress.dictBufferSize, params, fileSize);
-            if (ZSTD_isError(errorCode)) EXM_THROW(21, "Error initializing compression : %s", ZSTD_getErrorName(errorCode));
-    }   }
+    {   size_t const resetError = ZSTD_resetCStream(ress.cctx, fileSize);
+        if (ZSTD_isError(resetError)) EXM_THROW(21, "Error initializing compression : %s", ZSTD_getErrorName(resetError));
+    }
 
     /* Main compression loop */
     while (1) {
@@ -367,8 +364,7 @@ static int FIO_compressFilename_internal(cRess_t ress,
  *            1 : missing or pb opening srcFileName
  */
 static int FIO_compressFilename_srcFile(cRess_t ress,
-                                        const char* dstFileName, const char* srcFileName,
-                                        int cLevel)
+                                        const char* dstFileName, const char* srcFileName)
 {
     int result;
 
@@ -380,10 +376,10 @@ static int FIO_compressFilename_srcFile(cRess_t ress,
     ress.srcFile = FIO_openSrcFile(srcFileName);
     if (!ress.srcFile) return 1;   /* srcFile could not be opened */
 
-    result = FIO_compressFilename_internal(ress, dstFileName, srcFileName, cLevel);
+    result = FIO_compressFilename_internal(ress, dstFileName, srcFileName);
 
     fclose(ress.srcFile);
-    if ((g_removeSrcFile) && (!result)) { if (remove(srcFileName)) EXM_THROW(1, "zstd: %s: %s", srcFileName, strerror(errno)); }
+    if (g_removeSrcFile && !result) { if (remove(srcFileName)) EXM_THROW(1, "zstd: %s: %s", srcFileName, strerror(errno)); } /* remove source file : --rm */
     return result;
 }
 
@@ -393,17 +389,16 @@ static int FIO_compressFilename_srcFile(cRess_t ress,
  *            1 : pb
  */
 static int FIO_compressFilename_dstFile(cRess_t ress,
-                                        const char* dstFileName, const char* srcFileName,
-                                        int cLevel)
+                                        const char* dstFileName, const char* srcFileName)
 {
     int result;
 
     ress.dstFile = FIO_openDstFile(dstFileName);
-    if (ress.dstFile==0) return 1;
+    if (ress.dstFile==NULL) return 1;  /* could not open dstFileName */
 
-    result = FIO_compressFilename_srcFile(ress, dstFileName, srcFileName, cLevel);
+    result = FIO_compressFilename_srcFile(ress, dstFileName, srcFileName);
 
-    if (fclose(ress.dstFile)) { DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno)); result=1; }
+    if (fclose(ress.dstFile)) { DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno)); result=1; }  /* error closing dstFile */
     if (result!=0) { if (remove(dstFileName)) EXM_THROW(1, "zstd: %s: %s", dstFileName, strerror(errno)); }  /* remove operation artefact */
     return result;
 }
@@ -413,9 +408,10 @@ 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);
-    int const result = FIO_compressFilename_dstFile(ress, dstFileName, srcFileName, 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;
     DISPLAYLEVEL(4, "Completed in %.2f sec \n", seconds);
@@ -433,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);
+    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");
@@ -445,8 +442,7 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile
         ress.dstFile = stdout;
         SET_BINARY_MODE(stdout);
         for (u=0; u<nbFiles; u++)
-            missed_files += FIO_compressFilename_srcFile(ress, stdoutmark,
-                                                         inFileNamesTable[u], compressionLevel);
+            missed_files += FIO_compressFilename_srcFile(ress, stdoutmark, inFileNamesTable[u]);
         if (fclose(ress.dstFile)) EXM_THROW(29, "Write error : cannot properly close %s", stdoutmark);
     } else {
         unsigned u;
@@ -455,8 +451,7 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile
             if (dfnSize <= ifnSize+suffixSize+1) { free(dstFileName); dfnSize = ifnSize + 20; dstFileName = (char*)malloc(dfnSize); }
             strcpy(dstFileName, inFileNamesTable[u]);
             strcat(dstFileName, suffix);
-            missed_files += FIO_compressFilename_dstFile(ress, dstFileName,
-                                                         inFileNamesTable[u], compressionLevel);
+            missed_files += FIO_compressFilename_dstFile(ress, dstFileName, inFileNamesTable[u]);
     }   }
 
     /* Close & Free */
@@ -480,8 +475,6 @@ typedef struct {
     size_t srcBufferSize;
     void*  dstBuffer;
     size_t dstBufferSize;
-    void*  dictBuffer;
-    size_t dictBufferSize;
     ZSTD_DStream* dctx;
     FILE*  dstFile;
 } dRess_t;
@@ -501,7 +494,12 @@ static dRess_t FIO_createDResources(const char* dictFileName)
     if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(61, "Allocation error : not enough memory");
 
     /* dictionary */
-    ress.dictBufferSize = FIO_loadFile(&(ress.dictBuffer), dictFileName);
+    {   void* dictBuffer;
+        size_t const dictBufferSize = FIO_loadFile(&dictBuffer, dictFileName);
+        size_t const initError = ZSTD_initDStream_usingDict(ress.dctx, dictBuffer, dictBufferSize);
+        if (ZSTD_isError(initError)) EXM_THROW(61, "ZSTD_initDStream_usingDict error : %s", ZSTD_getErrorName(initError));
+        free(dictBuffer);
+    }
 
     return ress;
 }
@@ -512,7 +510,6 @@ static void FIO_freeDResources(dRess_t ress)
     if (ZSTD_isError(errorCode)) EXM_THROW(69, "Error : can't free ZSTD_DStream context resource : %s", ZSTD_getErrorName(errorCode));
     free(ress.srcBuffer);
     free(ress.dstBuffer);
-    free(ress.dictBuffer);
 }
 
 
@@ -601,7 +598,7 @@ unsigned long long FIO_decompressFrame(dRess_t ress,
     size_t readSize;
     U32 storedSkips = 0;
 
-    ZSTD_initDStream_usingDict(ress.dctx, ress.dictBuffer, ress.dictBufferSize);
+    ZSTD_resetDStream(ress.dctx);
 
     /* Header loading (optional, saves one loop) */
     {   size_t const toLoad = 9 - alreadyLoaded;   /* assumption : 9 >= alreadyLoaded */
@@ -613,22 +610,22 @@ unsigned long long FIO_decompressFrame(dRess_t ress,
     while (1) {
         ZSTD_inBuffer  inBuff = { ress.srcBuffer, readSize, 0 };
         ZSTD_outBuffer outBuff= { ress.dstBuffer, ress.dstBufferSize, 0 };
-        size_t const toRead = ZSTD_decompressStream(ress.dctx, &outBuff, &inBuff );
-        if (ZSTD_isError(toRead)) EXM_THROW(36, "Decoding error : %s", ZSTD_getErrorName(toRead));
+        size_t const readSizeHint = ZSTD_decompressStream(ress.dctx, &outBuff, &inBuff );
+        if (ZSTD_isError(readSizeHint)) EXM_THROW(36, "Decoding error : %s", ZSTD_getErrorName(readSizeHint));
 
         /* Write block */
         storedSkips = FIO_fwriteSparse(foutput, ress.dstBuffer, outBuff.pos, storedSkips);
         frameSize += outBuff.pos;
         DISPLAYUPDATE(2, "\rDecoded : %u MB...     ", (U32)(frameSize>>20) );
 
-        if (toRead == 0) break;   /* end of frame */
+        if (readSizeHint == 0) break;   /* end of frame */
         if (inBuff.size != inBuff.pos) EXM_THROW(37, "Decoding error : should consume entire input");
 
         /* Fill input buffer */
-        if (toRead > ress.srcBufferSize) EXM_THROW(38, "too large block");
-        readSize = fread(ress.srcBuffer, 1, toRead, finput);
-        if (readSize == 0) EXM_THROW(39, "Read error : premature end");
-    }
+        {   size_t const toRead = MIN(readSizeHint, ress.srcBufferSize);  /* support large skippable frames */
+            readSize = fread(ress.srcBuffer, 1, toRead, finput);
+            if (readSize < toRead) EXM_THROW(39, "Read error : premature end");
+    }   }
 
     FIO_fwriteSparseEnd(foutput, storedSkips);
 
@@ -686,7 +683,7 @@ static int FIO_decompressSrcFile(dRess_t ress, const char* srcFileName)
             if (readSomething==0) { DISPLAY("zstd: %s: unexpected end of file \n", srcFileName); fclose(srcFile); return 1; }  /* srcFileName is empty */
             break;   /* no more input */
         }
-        readSomething = 1;
+        readSomething = 1;   /* there is at least >= 4 bytes in srcFile */
         if (sizeCheck != toRead) { DISPLAY("zstd: %s: unknown header \n", srcFileName); fclose(srcFile); return 1; }  /* srcFileName is empty */
         {   U32 const magic = MEM_readLE32(ress.srcBuffer);
             if (((magic & 0xFFFFFFF0U) != ZSTD_MAGIC_SKIPPABLE_START) & (magic != ZSTD_MAGICNUMBER)
@@ -732,7 +729,10 @@ static int FIO_decompressDstFile(dRess_t ress,
     result = FIO_decompressSrcFile(ress, srcFileName);
 
     if (fclose(ress.dstFile)) EXM_THROW(38, "Write error : cannot properly close %s", dstFileName);
-    if (result != 0) if (remove(dstFileName)) result=1;   /* don't do anything if remove fails */
+    if ( (result != 0)
+       && strcmp(dstFileName, nulmark)  /* special case : don't remove() /dev/null (#316) */
+       && remove(dstFileName) )
+        result=1;   /* don't do anything special if remove() fails */
     return result;
 }
 
diff --git a/programs/fileio.h b/programs/fileio.h
index 66805f7..1e89aec 100644
--- a/programs/fileio.h
+++ b/programs/fileio.h
@@ -8,7 +8,8 @@
  */
 
 
-#pragma once
+#ifndef FILEIO_H_23981798732
+#define FILEIO_H_23981798732
 
 #if defined (__cplusplus)
 extern "C" {
@@ -32,7 +33,6 @@ extern "C" {
 ***************************************/
 void FIO_overwriteMode(void);
 void FIO_setNotificationLevel(unsigned level);
-void FIO_setMaxWLog(unsigned maxWLog);     /**< if `maxWLog` == 0, no max enforced */
 void FIO_setSparseWrite(unsigned sparse);  /**< 0: no sparse; 1: disable on stdout; 2: always enabled */
 void FIO_setDictIDFlag(unsigned dictIDFlag);
 void FIO_setChecksumFlag(unsigned checksumFlag);
@@ -70,3 +70,5 @@ int FIO_decompressMultipleFilenames(const char** srcNamesTable, unsigned nbFiles
 #if defined (__cplusplus)
 }
 #endif
+
+#endif  /* FILEIO_H_23981798732 */
diff --git a/programs/util.h b/programs/util.h
index ee130f4..aabebe9 100644
--- a/programs/util.h
+++ b/programs/util.h
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under the BSD-style license found in the
@@ -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
 
@@ -199,48 +199,67 @@ UTIL_STATIC U32 UTIL_isDirectory(const char* infilename)
     return 0;
 }
 
+/*
+ * A modified version of realloc().
+ * If UTIL_realloc() fails the original block is freed.
+*/
+UTIL_STATIC void *UTIL_realloc(void *ptr, size_t size)
+{
+    void *newptr = realloc(ptr, size);
+    if (newptr) return newptr;
+    free(ptr);
+    return NULL;
+}
+
 
 #ifdef _WIN32
 #  define UTIL_HAS_CREATEFILELIST
 
 UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_t* pos, char** bufEnd)
 {
-    char path[MAX_PATH];
-    int pathLength, nbFiles = 0;
+    char* path;
+    int dirLength, fnameLength, pathLength, nbFiles = 0;
     WIN32_FIND_DATA cFile;
     HANDLE hFile;
 
-    pathLength = snprintf(path, MAX_PATH, "%s\\*", dirName);
-    if (pathLength < 0 || pathLength >= MAX_PATH) {
-        fprintf(stderr, "Path length has got too long.\n");
-        return 0;
-    }
+    dirLength = (int)strlen(dirName);
+    path = (char*) malloc(dirLength + 3);
+    if (!path) return 0;
+
+    memcpy(path, dirName, dirLength);
+    path[dirLength] = '\\';
+    path[dirLength+1] = '*';
+    path[dirLength+2] = 0;
 
     hFile=FindFirstFile(path, &cFile);
     if (hFile == INVALID_HANDLE_VALUE) {
         fprintf(stderr, "Cannot open directory '%s'\n", dirName);
         return 0;
     }
+    free(path);
 
     do {
-        pathLength = snprintf(path, MAX_PATH, "%s\\%s", dirName, cFile.cFileName);
-        if (pathLength < 0 || pathLength >= MAX_PATH) {
-            fprintf(stderr, "Path length has got too long.\n");
-            continue;
-        }
+        fnameLength = (int)strlen(cFile.cFileName);
+        path = (char*) malloc(dirLength + fnameLength + 2);
+        if (!path) { FindClose(hFile); return 0; }
+        memcpy(path, dirName, dirLength);
+        path[dirLength] = '\\';
+        memcpy(path+dirLength+1, cFile.cFileName, fnameLength);
+        pathLength = dirLength+1+fnameLength;
+        path[pathLength] = 0;
         if (cFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
             if (strcmp (cFile.cFileName, "..") == 0 ||
                 strcmp (cFile.cFileName, ".") == 0) continue;
 
             nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd);  /* Recursively call "UTIL_prepareFileList" with the new path. */
-            if (*bufStart == NULL) { FindClose(hFile); return 0; }
+            if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
         }
         else if ((cFile.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || (cFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) || (cFile.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)) {
             if (*bufStart + *pos + pathLength >= *bufEnd) {
                 ptrdiff_t newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
-                *bufStart = (char*)realloc(*bufStart, newListSize);
+                *bufStart = (char*)UTIL_realloc(*bufStart, newListSize);
                 *bufEnd = *bufStart + newListSize;
-                if (*bufStart == NULL) { FindClose(hFile); return 0; }
+                if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
             }
             if (*bufStart + *pos + pathLength < *bufEnd) {
                 strncpy(*bufStart + *pos, path, *bufEnd - (*bufStart + *pos));
@@ -248,48 +267,54 @@ UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_
                 nbFiles++;
             }
         }
+        free(path);
     } while (FindNextFile(hFile, &cFile));
 
     FindClose(hFile);
     return nbFiles;
 }
 
-#elif (defined(__unix__) || defined(__unix) || defined(__midipix__) || (defined(__APPLE__) && defined(__MACH__))) && defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L) /* snprintf, opendir */
+#elif (defined(__APPLE__) && defined(__MACH__)) || \
+     ((defined(__unix__) || defined(__unix) || defined(__midipix__)) && defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) /* snprintf, opendir */
 #  define UTIL_HAS_CREATEFILELIST
 #  include <dirent.h>       /* opendir, readdir */
-#  include <limits.h>       /* PATH_MAX */
 #  include <errno.h>
 
 UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_t* pos, char** bufEnd)
 {
     DIR *dir;
     struct dirent *entry;
-    char path[PATH_MAX];
-    int pathLength, nbFiles = 0;
+    char* path;
+    int dirLength, fnameLength, pathLength, nbFiles = 0;
 
     if (!(dir = opendir(dirName))) {
         fprintf(stderr, "Cannot open directory '%s': %s\n", dirName, strerror(errno));
         return 0;
     }
 
+    dirLength = (int)strlen(dirName);
     errno = 0;
     while ((entry = readdir(dir)) != NULL) {
         if (strcmp (entry->d_name, "..") == 0 ||
             strcmp (entry->d_name, ".") == 0) continue;
-        pathLength = snprintf(path, PATH_MAX, "%s/%s", dirName, entry->d_name);
-        if (pathLength < 0 || pathLength >= PATH_MAX) {
-            fprintf(stderr, "Path length has got too long.\n");
-            continue;
-        }
+        fnameLength = (int)strlen(entry->d_name);
+        path = (char*) malloc(dirLength + fnameLength + 2);
+        if (!path) { closedir(dir); return 0; }
+        memcpy(path, dirName, dirLength);
+        path[dirLength] = '/';
+        memcpy(path+dirLength+1, entry->d_name, fnameLength);
+        pathLength = dirLength+1+fnameLength;
+        path[pathLength] = 0;
+
         if (UTIL_isDirectory(path)) {
             nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd);  /* Recursively call "UTIL_prepareFileList" with the new path. */
-            if (*bufStart == NULL) { closedir(dir); return 0; }
+            if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
         } else {
             if (*bufStart + *pos + pathLength >= *bufEnd) {
                 ptrdiff_t newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
-                *bufStart = (char*)realloc(*bufStart, newListSize);
+                *bufStart = (char*)UTIL_realloc(*bufStart, newListSize);
                 *bufEnd = *bufStart + newListSize;
-                if (*bufStart == NULL) { closedir(dir); return 0; }
+                if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
             }
             if (*bufStart + *pos + pathLength < *bufEnd) {
                 strncpy(*bufStart + *pos, path, *bufEnd - (*bufStart + *pos));
@@ -297,6 +322,7 @@ UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_
                 nbFiles++;
             }
         }
+        free(path);
         errno = 0; /* clear errno after UTIL_isDirectory, UTIL_prepareFileList */
     }
 
@@ -342,7 +368,7 @@ UTIL_STATIC const char** UTIL_createFileList(const char **inputNames, unsigned i
             size_t len = strlen(inputNames[i]);
             if (buf + pos + len >= bufend) {
                 ptrdiff_t newListSize = (bufend - buf) + LIST_SIZE_INCREASE;
-                buf = (char*)realloc(buf, newListSize);
+                buf = (char*)UTIL_realloc(buf, newListSize);
                 bufend = buf + newListSize;
                 if (!buf) return NULL;
             }
diff --git a/programs/zstd.1 b/programs/zstd.1
index 0a0ad62..c262a0c 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
@@ -135,7 +139,7 @@ Typical gains range from ~10% (at 64KB) to x5 better (at <1KB).
 
 
 .SH BUGS
-Report bugs at:- https://github.com/Cyan4973/zstd/issues
+Report bugs at:- https://github.com/facebook/zstd/issues
 
 .SH AUTHOR
 Yann Collet
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index 2d6d452..6d1d9f6 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 <io.h>       /* _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 <unistd.h>   /* isatty */
+#  define IS_CONSOLE(stdStream) isatty(fileno(stdStream))
 #else
-#  if defined(_POSIX_C_SOURCE) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)
-#    include <unistd.h>   /* isatty */
-#    define IS_CONSOLE(stdStream) isatty(fileno(stdStream))
-#  else
-#    define IS_CONSOLE(stdStream) 0
-#  endif
+#  define IS_CONSOLE(stdStream) 0
 #endif
 
 
@@ -83,7 +81,7 @@
 
 static const char*    g_defaultDictName = "dictionary";
 static const unsigned g_defaultMaxDictSize = 110 KB;
-static const int      g_defaultDictCLevel = 5;
+static const int      g_defaultDictCLevel = 3;
 static const unsigned g_defaultSelectivityLevel = 9;
 
 
@@ -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");
@@ -182,7 +181,7 @@ static void waitEnter(void)
 /*! 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 result > MAX_UINT */
+    Note : this function can overflow if digit string > MAX_UINT */
 static unsigned readU32FromChar(const char** stringPtr)
 {
     unsigned result = 0;
@@ -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
@@ -262,7 +261,7 @@ int main(int argCount, char** argv)
             if (!strcmp(argument, "--verbose")) { displayLevel++; continue; }
             if (!strcmp(argument, "--quiet")) { displayLevel--; continue; }
             if (!strcmp(argument, "--stdout")) { forceStdout=1; outFileName=stdoutmark; displayLevel-=(displayLevel==2); continue; }
-            if (!strcmp(argument, "--ultra")) { ultra=1; FIO_setMaxWLog(0); continue; }
+            if (!strcmp(argument, "--ultra")) { ultra=1; continue; }
             if (!strcmp(argument, "--check")) { FIO_setChecksumFlag(2); continue; }
             if (!strcmp(argument, "--no-check")) { FIO_setChecksumFlag(0); continue; }
             if (!strcmp(argument, "--no-dictID")) { FIO_setDictIDFlag(0); continue; }
@@ -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,13 +288,17 @@ int main(int argCount, char** argv)
                 argument++;
 
                 while (argument[0]!=0) {
-    #ifndef ZSTD_NOCOMPRESS
+                    if (lastCommand) {
+                        DISPLAY("error : command must be followed by argument \n");
+                        return 1;
+                    }
+#ifndef ZSTD_NOCOMPRESS
                     /* compression Level */
                     if ((*argument>='0') && (*argument<='9')) {
                         dictCLevel = cLevel = readU32FromChar(&argument);
                         continue;
                     }
-    #endif
+#endif
 
                     switch(argument[0])
                     {
@@ -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,12 +335,14 @@ 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 */
                     case 'r': recursive=1; argument++; break;
+#endif
 
-    #ifndef ZSTD_NOBENCH
+#ifndef ZSTD_NOBENCH
                         /* Benchmark */
                     case 'b': bench=1; argument++; break;
 
@@ -368,7 +373,7 @@ int main(int argCount, char** argv)
                             BMK_SetBlockSize(bSize);
                         }
                         break;
-    #endif   /* ZSTD_NOBENCH */
+#endif   /* ZSTD_NOBENCH */
 
                         /* Dictionary Selection level */
                     case 's':
@@ -378,11 +383,11 @@ int main(int argCount, char** argv)
 
                         /* Pause at the end (-p) or set an additional param (-p#) (hidden option) */
                     case 'p': argument++;
-    #ifndef ZSTD_NOBENCH
+#ifndef ZSTD_NOBENCH
                         if ((*argument>='0') && (*argument<='9')) {
                             BMK_setAdditionalParam(readU32FromChar(&argument));
                         } else
-    #endif
+#endif
                             main_pause=1;
                         break;
                         /* unknown command */
@@ -394,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;
@@ -402,6 +408,7 @@ int main(int argCount, char** argv)
 
             if (nextArgumentIsDictID) {
                 nextArgumentIsDictID = 0;
+                lastCommand = 0;
                 dictID = readU32FromChar(&argument);
                 continue;
             }
@@ -410,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;
@@ -425,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<fileNamesNb; u++) DISPLAYLEVEL(4, "%u %s\n", u, fileNamesTable[u]);
+            for (u=0; u<fileNamesNb; u++) DISPLAYLEVEL(4, "%u %s\n", u, extendedFileList[u]);
             free((void*)filenameTable);
-            filenameTable = fileNamesTable;
+            filenameTable = extendedFileList;
             filenameIdx = fileNamesNb;
         }
     }
@@ -470,7 +481,7 @@ int main(int argCount, char** argv)
 
     /* Check if input/output defined as console; trigger an error in this case */
     if (!strcmp(filenameTable[0], stdinmark) && IS_CONSOLE(stdin) ) CLEAN_RETURN(badusage(programName));
-    if (outFileName && !strcmp(outFileName, stdoutmark) && IS_CONSOLE(stdout) && !(forceStdout && decode))
+    if (outFileName && !strcmp(outFileName, stdoutmark) && IS_CONSOLE(stdout) && strcmp(filenameTable[0], stdinmark) && !(forceStdout && decode))
         CLEAN_RETURN(badusage(programName));
 
     /* user-selected output filename, only possible with a single file */
@@ -492,15 +503,16 @@ int main(int argCount, char** argv)
 
     /* IO Stream/File */
     FIO_setNotificationLevel(displayLevel);
-#ifndef ZSTD_NOCOMPRESS
     if (!decode) {
-        if (filenameIdx==1 && outFileName)
+#ifndef ZSTD_NOCOMPRESS
+        if ((filenameIdx==1) && outFileName)
           operationResult = FIO_compressFilename(outFileName, filenameTable[0], dictFileName, cLevel);
         else
           operationResult = FIO_compressMultipleFilenames(filenameTable, filenameIdx, outFileName ? outFileName : ZSTD_EXTENSION, dictFileName, cLevel);
-    } else
+#else
+        DISPLAY("Compression not supported\n");
 #endif
-    {  /* decompression */
+    } else {  /* decompression */
 #ifndef ZSTD_NODECOMPRESS
         if (testmode) { outFileName=nulmark; FIO_setRemoveSrcFile(0); } /* test mode */
         if (filenameIdx==1 && outFileName)
@@ -514,10 +526,9 @@ int main(int argCount, char** argv)
 
 _end:
     if (main_pause) waitEnter();
-    free(dynNameSpace);
 #ifdef UTIL_HAS_CREATEFILELIST
-    if (fileNamesTable)
-        UTIL_freeFileList(fileNamesTable, fileNamesBuf);
+    if (extendedFileList)
+        UTIL_freeFileList(extendedFileList, fileNamesBuf);
     else
 #endif
         free((void*)filenameTable);
diff --git a/projects/cmake/CMakeLists.txt b/projects/cmake/CMakeLists.txt
deleted file mode 100644
index 86178bf..0000000
--- a/projects/cmake/CMakeLists.txt
+++ /dev/null
@@ -1,60 +0,0 @@
-# ################################################################
-# zstd - Makefile
-# Copyright (C) Yann Collet 2014-2016
-# All rights reserved.
-# 
-# BSD license
-#
-# Redistribution and use in source and binary forms, with or without modification,
-# are permitted provided that the following conditions are met:
-# 
-# * Redistributions of source code must retain the above copyright notice, this
-#   list of conditions and the following disclaimer.
-# 
-# * Redistributions in binary form must reproduce the above copyright notice, this
-#   list of conditions and the following disclaimer in the documentation and/or
-#   other materials provided with the distribution.
-# 
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
-# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-# 
-# You can contact the author at :
-#  - zstd source repository : https://github.com/Cyan4973/zstd
-#  - Public forum : https://groups.google.com/forum/#!forum/lz4c
-# ################################################################
-
-PROJECT(zstd)
-CMAKE_MINIMUM_REQUIRED(VERSION 2.8.7)
-
-OPTION(ZSTD_LEGACY_SUPPORT "LEGACY SUPPORT" OFF)
-
-IF (ZSTD_LEGACY_SUPPORT)
-    MESSAGE(STATUS "ZSTD_LEGACY_SUPPORT defined!")
-    ADD_DEFINITIONS(-DZSTD_LEGACY_SUPPORT=1)
-ELSE (ZSTD_LEGACY_SUPPORT)
-    MESSAGE(STATUS "ZSTD_LEGACY_SUPPORT not defined!")
-    ADD_DEFINITIONS(-DZSTD_LEGACY_SUPPORT=0)
-ENDIF (ZSTD_LEGACY_SUPPORT)
-
-ADD_SUBDIRECTORY(lib)
-ADD_SUBDIRECTORY(programs)
-ADD_SUBDIRECTORY(tests)
-
-#-----------------------------------------------------------------------------
-# Add extra compilation flags
-#-----------------------------------------------------------------------------
-INCLUDE(CMakeModules/AddExtraCompilationFlags.cmake)
-ADD_EXTRA_COMPILATION_FLAGS()
-
-ADD_CUSTOM_TARGET(clean-all
-   COMMAND ${CMAKE_BUILD_TOOL} clean
-   COMMAND rm -rf ${CMAKE_BINARY_DIR}/
-)
diff --git a/tests/Makefile b/tests/Makefile
index 6b22e6d..3ce9f31 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -160,10 +160,10 @@ valgrindTest: zstd datagen fuzzer fullbench zbufftest
 	$(VALGRIND) ./datagen -g50M > $(VOID)
 	$(VALGRIND) $(PRGDIR)/zstd ; if [ $$? -eq 0 ] ; then echo "zstd without argument should have failed"; false; fi
 	./datagen -g80 | $(VALGRIND) $(PRGDIR)/zstd - -c > $(VOID)
-	./datagen -g16KB | $(VALGRIND) $(PRGDIR)/zstd -vf - -o $(VOID)
+	./datagen -g16KB | $(VALGRIND) $(PRGDIR)/zstd -vf - -c > $(VOID)
 	./datagen -g2930KB | $(VALGRIND) $(PRGDIR)/zstd -5 -vf - -o tmp
-	$(VALGRIND) $(PRGDIR)/zstd -vdf tmp -o $(VOID)
-	./datagen -g64MB | $(VALGRIND) $(PRGDIR)/zstd -vf - -o $(VOID)
+	$(VALGRIND) $(PRGDIR)/zstd -vdf tmp -c > $(VOID)
+	./datagen -g64MB | $(VALGRIND) $(PRGDIR)/zstd -vf - -c > $(VOID)
 	@rm tmp
 	$(VALGRIND) ./fuzzer -T1mn -t1
 	$(VALGRIND) ./fullbench -i1
diff --git a/tests/datagencli.c b/tests/datagencli.c
index 772e3dc..2f3ebc4 100644
--- a/tests/datagencli.c
+++ b/tests/datagencli.c
@@ -9,7 +9,7 @@
 
 
 /*-************************************
-*  Includes
+*  Dependencies
 **************************************/
 #include "util.h"      /* Compiler options */
 #include <stdio.h>     /* fprintf, stderr */
diff --git a/tests/fullbench.c b/tests/fullbench.c
index eaf1fc7..670b516 100644
--- a/tests/fullbench.c
+++ b/tests/fullbench.c
@@ -121,13 +121,12 @@ size_t local_ZSTD_decodeLiteralsBlock(void* dst, size_t dstSize, void* buff2, co
 }
 
 extern size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockProperties_t* bpPtr);
-extern size_t ZSTD_decodeSeqHeaders(int* nbSeq, FSE_DTable* DTableLL, FSE_DTable* DTableML, FSE_DTable* DTableOffb, U32 tableRepeatFlag, const void* src, size_t srcSize);
+extern size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeq, const void* src, size_t srcSize);
 size_t local_ZSTD_decodeSeqHeaders(void* dst, size_t dstSize, void* buff2, const void* src, size_t srcSize)
 {
-    U32 DTableML[FSE_DTABLE_SIZE_U32(10)], DTableLL[FSE_DTABLE_SIZE_U32(10)], DTableOffb[FSE_DTABLE_SIZE_U32(9)];   /* MLFSELog, LLFSELog and OffFSELog are not public values */
     int nbSeq;
     (void)src; (void)srcSize; (void)dst; (void)dstSize;
-    return ZSTD_decodeSeqHeaders(&nbSeq, DTableLL, DTableML, DTableOffb, 0, buff2, g_cSize);
+    return ZSTD_decodeSeqHeaders(g_zdc, &nbSeq, buff2, g_cSize);
 }
 
 
@@ -289,6 +288,7 @@ static size_t benchMem(const void* src, size_t srcSize, U32 benchNb)
             }
             iend = ip + ZSTD_blockHeaderSize + cBlockSize;   /* End of first block */
             ip += ZSTD_blockHeaderSize;                      /* skip block header */
+            ZSTD_decompressBegin(g_zdc);
             ip += ZSTD_decodeLiteralsBlock(g_zdc, ip, iend-ip);   /* skip literal segment */
             g_cSize = iend-ip;
             memcpy(buff2, ip, g_cSize);   /* copy rest of block (it starts by SeqHeader) */
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 18948b9..ae8450e 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -23,7 +23,6 @@
 **************************************/
 #include <stdlib.h>       /* free */
 #include <stdio.h>        /* fgets, sscanf */
-#include <sys/timeb.h>    /* timeb */
 #include <string.h>       /* strcmp */
 #include <time.h>         /* clock_t */
 #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_compressContinue, ZSTD_compressBlock */
@@ -58,7 +57,7 @@ static U32 g_displayLevel = 2;
             if ((FUZ_clockSpan(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \
             { g_displayClock = clock(); DISPLAY(__VA_ARGS__); \
             if (g_displayLevel>=4) fflush(stdout); } }
-static const clock_t g_refreshRate = CLOCKS_PER_SEC * 150 / 1000;
+static const clock_t g_refreshRate = CLOCKS_PER_SEC / 6;
 static clock_t g_displayClock = 0;
 
 
@@ -174,13 +173,13 @@ static int basicUnitTests(U32 seed, double compressibility)
         static const size_t dictSize = 551;
 
         DISPLAYLEVEL(4, "test%3i : copy context too soon : ", testNb++);
-        { size_t const copyResult = ZSTD_copyCCtx(ctxDuplicated, ctxOrig);
+        { size_t const copyResult = ZSTD_copyCCtx(ctxDuplicated, ctxOrig, 0);
           if (!ZSTD_isError(copyResult)) goto _output_error; }   /* error must be detected */
         DISPLAYLEVEL(4, "OK \n");
 
         DISPLAYLEVEL(4, "test%3i : load dictionary into context : ", testNb++);
         CHECK( ZSTD_compressBegin_usingDict(ctxOrig, CNBuffer, dictSize, 2) );
-        CHECK( ZSTD_copyCCtx(ctxDuplicated, ctxOrig) );
+        CHECK( ZSTD_copyCCtx(ctxDuplicated, ctxOrig, CNBuffSize - dictSize) );
         DISPLAYLEVEL(4, "OK \n");
 
         DISPLAYLEVEL(4, "test%3i : compress with flat dictionary : ", testNb++);
@@ -222,10 +221,10 @@ static int basicUnitTests(U32 seed, double compressibility)
                 p.fParams.contentSizeFlag = 1;
                 CHECK( ZSTD_compressBegin_advanced(ctxOrig, CNBuffer, dictSize, p, testSize-1) );
             }
-            CHECK( ZSTD_copyCCtx(ctxDuplicated, ctxOrig) );
+            CHECK( ZSTD_copyCCtx(ctxDuplicated, ctxOrig, testSize) );
 
-            CHECKPLUS(r, ZSTD_compressContinue(ctxDuplicated, compressedBuffer, ZSTD_compressBound(testSize),
-                                               (const char*)CNBuffer + dictSize, CNBuffSize - dictSize),
+            CHECKPLUS(r, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, ZSTD_compressBound(testSize),
+                                          (const char*)CNBuffer + dictSize, testSize),
                       cSize = r);
             {   ZSTD_frameParams fp;
                 if (ZSTD_getFrameParams(&fp, compressedBuffer, cSize)) goto _output_error;
@@ -557,7 +556,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD
         /* compression tests */
         {   unsigned const cLevel = (FUZ_rand(&lseed) % (ZSTD_maxCLevel() - (FUZ_highbit32((U32)sampleSize)/3))) + 1;
             cSize = ZSTD_compressCCtx(ctx, cBuffer, cBufferSize, sampleBuffer, sampleSize, cLevel);
-            CHECK(ZSTD_isError(cSize), "ZSTD_compressCCtx failed");
+            CHECK(ZSTD_isError(cSize), "ZSTD_compressCCtx failed : %s", ZSTD_getErrorName(cSize));
 
             /* compression failure test : too small dest buffer */
             if (cSize > 3) {
@@ -675,9 +674,9 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD
                 errorCode = ZSTD_compressBegin_advanced(refCtx, dict, dictSize, p, 0);
                 CHECK (ZSTD_isError(errorCode), "ZSTD_compressBegin_advanced error : %s", ZSTD_getErrorName(errorCode));
             }
-            { size_t const errorCode = ZSTD_copyCCtx(ctx, refCtx);
-              CHECK (ZSTD_isError(errorCode), "ZSTD_copyCCtx error : %s", ZSTD_getErrorName(errorCode)); }
-        }
+            {   size_t const errorCode = ZSTD_copyCCtx(ctx, refCtx, 0);
+                CHECK (ZSTD_isError(errorCode), "ZSTD_copyCCtx error : %s", ZSTD_getErrorName(errorCode));
+        }   }
         XXH64_reset(&xxhState, 0);
         {   U32 const nbChunks = (FUZ_rand(&lseed) & 127) + 2;
             U32 n;
diff --git a/tests/playTests.sh b/tests/playTests.sh
index b887613..d94d8fa 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -41,16 +41,32 @@ $ECHO "\nStarting playTests.sh isWindows=$isWindows"
 [ -n "$ZSTD" ] || die "ZSTD variable must be defined!"
 
 file $ZSTD
+
 $ECHO "\n**** simple tests **** "
 
 ./datagen > tmp
+$ECHO "test : basic compression "
 $ZSTD -f tmp                      # trivial compression case, creates tmp.zst
+$ECHO "test : basic decompression"
 $ZSTD -df tmp.zst                 # trivial decompression case (overwrites tmp)
 $ECHO "test : too large compression level (must fail)"
 $ZSTD -99 -f tmp  # too large compression level, automatic sized down
 $ECHO "test : compress to stdout"
 $ZSTD tmp -c > 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"
 $ECHO -n '' | $ZSTD - --stdout | $ZSTD -d --stdout
 $ECHO "test : decompress file with wrong suffix (must fail)"
@@ -178,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
@@ -200,7 +224,7 @@ $MD5SUM dirTestDict/* > tmph1
 $ZSTD -f --rm dirTestDict/* -D tmpDictC
 $ZSTD -d --rm dirTestDict/*.zst -D tmpDictC  # note : use internal checksum by default
 if [[ "$OSTYPE" == "darwin"* ]]; then
-  $ECHO "test skipped on OS-X"  # not compatible with OS-X's md5
+  $ECHO "md5sum -c not supported on OS-X : test skipped"  # not compatible with OS-X's md5
 else
   $MD5SUM -c tmph1
 fi
diff --git a/tests/test-zstd-speed.py b/tests/test-zstd-speed.py
index 45cfe86..56b4d46 100755
--- a/tests/test-zstd-speed.py
+++ b/tests/test-zstd-speed.py
@@ -1,7 +1,7 @@
 #! /usr/bin/env python
 
 #
-# Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+# Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
 # All rights reserved.
 #
 # This source code is licensed under the BSD-style license found in the
@@ -9,6 +9,10 @@
 # of patent rights can be found in the PATENTS file in the same directory.
 #
 
+# Limitations:
+# - doesn't support filenames with spaces
+# - dir1/zstd and dir2/zstd will be merged in a single results file
+
 import argparse
 import os
 import string
@@ -17,14 +21,16 @@ import time
 import traceback
 import hashlib
 
-script_version = 'v0.8.0 (2016-08-03)'
-default_repo_url = 'https://github.com/Cyan4973/zstd.git'
+script_version = 'v1.0.1 (2016-09-15)'
+default_repo_url = 'https://github.com/facebook/zstd.git'
 working_dir_name = 'speedTest'
 working_path = os.getcwd() + '/' + working_dir_name     # /path/to/zstd/tests/speedTest
 clone_path = working_path + '/' + 'zstd'                # /path/to/zstd/tests/speedTest/zstd
 email_header = 'ZSTD_speedTest'
 pid = str(os.getpid())
 verbose = False
+clang_version = "unknown"
+gcc_version = "unknown"
 
 
 
@@ -123,19 +129,19 @@ def get_last_results(resultsFileName):
     with open(resultsFileName, 'r') as f:
         for line in f:
             words = line.split()
-            if len(words) == 2:   # branch + commit
+            if len(words) <= 4:   # branch + commit + compilerVer + md5
                 commit = words[1]
                 csize = []
                 cspeed = []
                 dspeed = []
-            if (len(words) == 8):  # results
+            if (len(words) == 8) or (len(words) == 9):  # results: "filename" or "XX files"
                 csize.append(int(words[1]))
                 cspeed.append(float(words[3]))
                 dspeed.append(float(words[5]))
     return commit, csize, cspeed, dspeed
 
 
-def benchmark_and_compare(branch, commit, last_commit, args, executableName, md5sum, resultsFileName,
+def benchmark_and_compare(branch, commit, last_commit, args, executableName, md5sum, compilerVersion, resultsFileName,
                           testFilePath, fileName, last_csize, last_cspeed, last_dspeed):
     sleepTime = 30
     while os.getloadavg()[0] > args.maxLoadAvg:
@@ -143,14 +149,16 @@ def benchmark_and_compare(branch, commit, last_commit, args, executableName, md5
             % (os.getloadavg()[0], args.maxLoadAvg, sleepTime))
         time.sleep(sleepTime)
     start_load = str(os.getloadavg())
-    result = execute('programs/%s -qi5b1e%s %s' % (executableName, args.lastCLevel, testFilePath),
-                     print_output=True)
+    if args.dictionary:
+        result = execute('programs/%s -rqi5b1e%s -D %s %s' % (executableName, args.lastCLevel, args.dictionary, testFilePath), print_output=True)
+    else:
+        result = execute('programs/%s -rqi5b1e%s %s' % (executableName, args.lastCLevel, testFilePath), print_output=True)   
     end_load = str(os.getloadavg())
     linesExpected = args.lastCLevel + 1
     if len(result) != linesExpected:
         raise RuntimeError("ERROR: number of result lines=%d is different that expected %d\n%s" % (len(result), linesExpected, '\n'.join(result)))
     with open(resultsFileName, "a") as myfile:
-        myfile.write(branch + " " + commit + "\n")
+        myfile.write('%s %s %s md5=%s\n' % (branch, commit, compilerVersion, md5sum))
         myfile.write('\n'.join(result) + '\n')
         myfile.close()
         if (last_cspeed == None):
@@ -167,7 +175,7 @@ def benchmark_and_compare(branch, commit, last_commit, args, executableName, md5
             if (float(last_csize[i])/csize[i] < args.ratioLimit):
                 text += "WARNING: %s -%d cSize=%d last_cSize=%d diff=%.4f %s\n" % (executableName, i+1, csize[i], last_csize[i], float(last_csize[i])/csize[i], fileName)
         if text:
-            text = args.message + ("\nmaxLoadAvg=%s  load average at start=%s end=%s  last_commit=%s  md5=%s\n" % (args.maxLoadAvg, start_load, end_load, last_commit, md5sum)) + text
+            text = args.message + ("\nmaxLoadAvg=%s  load average at start=%s end=%s\n%s  last_commit=%s  md5=%s\n" % (args.maxLoadAvg, start_load, end_load, compilerVersion, last_commit, md5sum)) + text
         return text
 
 
@@ -180,13 +188,13 @@ def update_config_file(branch, commit):
     return last_commit
 
 
-def double_check(branch, commit, args, executableName, md5sum, resultsFileName, filePath, fileName):
+def double_check(branch, commit, args, executableName, md5sum, compilerVersion, resultsFileName, filePath, fileName):
     last_commit, csize, cspeed, dspeed = get_last_results(resultsFileName)
     if not args.dry_run:
-        text = benchmark_and_compare(branch, commit, last_commit, args, executableName, md5sum, resultsFileName, filePath, fileName, csize, cspeed, dspeed)
+        text = benchmark_and_compare(branch, commit, last_commit, args, executableName, md5sum, compilerVersion, resultsFileName, filePath, fileName, csize, cspeed, dspeed)
         if text:
             log("WARNING: redoing tests for branch %s: commit %s" % (branch, commit))
-            text = benchmark_and_compare(branch, commit, last_commit, args, executableName, md5sum, resultsFileName, filePath, fileName, csize, cspeed, dspeed)
+            text = benchmark_and_compare(branch, commit, last_commit, args, executableName, md5sum, compilerVersion, resultsFileName, filePath, fileName, csize, cspeed, dspeed)
     return text
 
 
@@ -196,29 +204,38 @@ def test_commit(branch, commit, last_commit, args, testFilePaths, have_mutt, hav
     if not args.dry_run:
         execute('make -C programs clean zstd CC=clang MOREFLAGS="-Werror -Wconversion -Wno-sign-conversion -DZSTD_GIT_COMMIT=%s" && ' % version +
                 'mv programs/zstd programs/zstd_clang && ' +
-                'make -C programs clean zstd MOREFLAGS="-DZSTD_GIT_COMMIT=%s" && ' % version +
-                'make -B -C programs zstd32 MOREFLAGS="-DZSTD_GIT_COMMIT=%s"' % version)
+                'make -C programs clean zstd zstd32 MOREFLAGS="-DZSTD_GIT_COMMIT=%s"' % version)
     md5_zstd = hashfile(hashlib.md5(), clone_path + '/programs/zstd')
     md5_zstd32 = hashfile(hashlib.md5(), clone_path + '/programs/zstd32')
     md5_zstd_clang = hashfile(hashlib.md5(), clone_path + '/programs/zstd_clang')
     print("md5(zstd)=%s\nmd5(zstd32)=%s\nmd5(zstd_clang)=%s" % (md5_zstd, md5_zstd32, md5_zstd_clang))
+    print("gcc_version=%s clang_version=%s" % (gcc_version, clang_version))
+
     logFileName = working_path + "/log_" + branch.replace("/", "_") + ".txt"
     text_to_send = []
     results_files = ""
+    if args.dictionary:
+        dictName = args.dictionary.rpartition('/')[2]
+    else:
+        dictName = None
+
     for filePath in testFilePaths:
         fileName = filePath.rpartition('/')[2]
-        resultsFileName = working_path + "/results_" + branch.replace("/", "_") + "_" + fileName.replace(".", "_") + ".txt"
-        text = double_check(branch, commit, args, 'zstd', md5_zstd, resultsFileName, filePath, fileName)
+        if dictName:
+            resultsFileName = working_path + "/" + dictName.replace(".", "_") + "_" + branch.replace("/", "_") + "_" + fileName.replace(".", "_") + ".txt"
+        else:
+            resultsFileName = working_path + "/results_" + branch.replace("/", "_") + "_" + fileName.replace(".", "_") + ".txt"
+        text = double_check(branch, commit, args, 'zstd', md5_zstd, 'gcc_version='+gcc_version, resultsFileName, filePath, fileName)
         if text:
             text_to_send.append(text)
             results_files += resultsFileName + " "
         resultsFileName = working_path + "/results32_" + branch.replace("/", "_") + "_" + fileName.replace(".", "_") + ".txt"
-        text = double_check(branch, commit, args, 'zstd32', md5_zstd32, resultsFileName, filePath, fileName)
+        text = double_check(branch, commit, args, 'zstd32', md5_zstd32, 'gcc_version='+gcc_version, resultsFileName, filePath, fileName)
         if text:
             text_to_send.append(text)
             results_files += resultsFileName + " "
         resultsFileName = working_path + "/resultsClang_" + branch.replace("/", "_") + "_" + fileName.replace(".", "_") + ".txt"
-        text = double_check(branch, commit, args, 'zstd_clang', md5_zstd_clang, resultsFileName, filePath, fileName)
+        text = double_check(branch, commit, args, 'zstd_clang', md5_zstd_clang, 'clang_version='+clang_version, resultsFileName, filePath, fileName)
         if text:
             text_to_send.append(text)
             results_files += resultsFileName + " "
@@ -228,17 +245,18 @@ def test_commit(branch, commit, last_commit, args, testFilePaths, have_mutt, hav
 
 if __name__ == '__main__':
     parser = argparse.ArgumentParser()
-    parser.add_argument('testFileNames', help='file names list for speed benchmark')
+    parser.add_argument('testFileNames', help='file or directory names list for speed benchmark')
     parser.add_argument('emails', help='list of e-mail addresses to send warnings')
-    parser.add_argument('--message', help='attach an additional message to e-mail', default="")
+    parser.add_argument('--dictionary', '-D', help='path to the dictionary')
+    parser.add_argument('--message', '-m', help='attach an additional message to e-mail', default="")
     parser.add_argument('--repoURL', help='changes default repository URL', default=default_repo_url)
-    parser.add_argument('--lowerLimit', type=float, help='send email if speed is lower than given limit', default=0.98)
-    parser.add_argument('--ratioLimit', type=float, help='send email if ratio is lower than given limit', default=0.999)
+    parser.add_argument('--lowerLimit', '-l', type=float, help='send email if speed is lower than given limit', default=0.98)
+    parser.add_argument('--ratioLimit', '-r', type=float, help='send email if ratio is lower than given limit', default=0.999)
     parser.add_argument('--maxLoadAvg', type=float, help='maximum load average to start testing', default=0.75)
     parser.add_argument('--lastCLevel', type=int, help='last compression level for testing', default=5)
-    parser.add_argument('--sleepTime', type=int, help='frequency of repository checking in seconds', default=300)
+    parser.add_argument('--sleepTime', '-s', type=int, help='frequency of repository checking in seconds', default=300)
     parser.add_argument('--dry-run', dest='dry_run', action='store_true', help='not build', default=False)
-    parser.add_argument('--verbose', action='store_true', help='more verbose logs', default=False)
+    parser.add_argument('--verbose', '-v', action='store_true', help='more verbose logs', default=False)
     args = parser.parse_args()
     verbose = args.verbose
 
@@ -247,10 +265,17 @@ if __name__ == '__main__':
     testFilePaths = []
     for fileName in testFileNames:
         fileName = os.path.expanduser(fileName)
-        if os.path.isfile(fileName):
+        if os.path.isfile(fileName) or os.path.isdir(fileName):
             testFilePaths.append(os.path.abspath(fileName))
         else:
-            log("ERROR: File not found: " + fileName)
+            log("ERROR: File/directory not found: " + fileName)
+            exit(1)
+
+    # check if dictionary is accessible
+    if args.dictionary:
+        args.dictionary = os.path.abspath(os.path.expanduser(args.dictionary))
+        if not os.path.isfile(args.dictionary):
+            log("ERROR: Dictionary not found: " + args.dictionary)
             exit(1)
 
     # check availability of e-mail senders
@@ -260,6 +285,9 @@ if __name__ == '__main__':
         log("ERROR: e-mail senders 'mail' or 'mutt' not found")
         exit(1)
 
+    clang_version = execute("clang -v 2>&1 | grep 'clang version' | sed -e 's:.*version \\([0-9.]*\\).*:\\1:' -e 's:\\.\\([0-9][0-9]\\):\\1:g'", verbose)[0];
+    gcc_version = execute("gcc -dumpversion", verbose)[0];
+
     if verbose:
         print("PARAMETERS:\nrepoURL=%s" % args.repoURL)
         print("working_path=%s" % working_path)
@@ -267,6 +295,7 @@ if __name__ == '__main__':
         print("testFilePath(%s)=%s" % (len(testFilePaths), testFilePaths))
         print("message=%s" % args.message)
         print("emails=%s" % args.emails)
+        print("dictionary=%s" % args.dictionary)
         print("maxLoadAvg=%s" % args.maxLoadAvg)
         print("lowerLimit=%s" % args.lowerLimit)
         print("ratioLimit=%s" % args.ratioLimit)
diff --git a/tests/test-zstd-versions.py b/tests/test-zstd-versions.py
index 5a24bc4..a5a7130 100755
--- a/tests/test-zstd-versions.py
+++ b/tests/test-zstd-versions.py
@@ -19,7 +19,7 @@ import sys
 import subprocess
 from subprocess import Popen, PIPE
 
-repo_url = 'https://github.com/Cyan4973/zstd.git'
+repo_url = 'https://github.com/facebook/zstd.git'
 tmp_dir_name = 'tests/versionsTest'
 make_cmd = 'make'
 git_cmd = 'git'
diff --git a/tests/zbufftest.c b/tests/zbufftest.c
index 5289799..9dc164e 100644
--- a/tests/zbufftest.c
+++ b/tests/zbufftest.c
@@ -23,7 +23,7 @@
 **************************************/
 #include <stdlib.h>       /* free */
 #include <stdio.h>        /* fgets, sscanf */
-#include <sys/timeb.h>    /* timeb */
+#include <time.h>         /* clock_t, clock() */
 #include <string.h>       /* strcmp */
 #include "mem.h"
 #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_maxCLevel */
@@ -58,13 +58,13 @@ static const U32 prime2 = 2246822519U;
 static U32 g_displayLevel = 2;
 
 #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
-            if ((FUZ_GetMilliSpan(g_displayTime) > g_refreshRate) || (g_displayLevel>=4)) \
-            { g_displayTime = FUZ_GetMilliStart(); DISPLAY(__VA_ARGS__); \
+            if ((FUZ_GetClockSpan(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \
+            { g_displayClock = clock(); DISPLAY(__VA_ARGS__); \
             if (g_displayLevel>=4) fflush(stdout); } }
-static const U32 g_refreshRate = 150;
-static U32 g_displayTime = 0;
+static const clock_t g_refreshRate = CLOCKS_PER_SEC * 15 / 100;
+static clock_t g_displayClock = 0;
 
-static U32 g_testTime = 0;
+static clock_t g_clockTime = 0;
 
 
 /*-*******************************************************
@@ -72,23 +72,9 @@ static U32 g_testTime = 0;
 *********************************************************/
 #define MAX(a,b) ((a)>(b)?(a):(b))
 
-static U32 FUZ_GetMilliStart(void)
+static clock_t FUZ_GetClockSpan(clock_t clockStart)
 {
-    struct timeb tb;
-    U32 nCount;
-    ftime( &tb );
-    nCount = (U32) (((tb.time & 0xFFFFF) * 1000) +  tb.millitm);
-    return nCount;
-}
-
-
-static U32 FUZ_GetMilliSpan(U32 nTimeStart)
-{
-    U32 const nCurrent = FUZ_GetMilliStart();
-    U32 nSpan = nCurrent - nTimeStart;
-    if (nTimeStart > nCurrent)
-        nSpan += 0x100000 * 1000;
-    return nSpan;
+    return clock() - clockStart;  /* works even when overflow. Max span ~ 30 mn */
 }
 
 /*! FUZ_rand() :
@@ -291,7 +277,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres
     U32 coreSeed = seed;
     ZBUFF_CCtx* zc;
     ZBUFF_DCtx* zd;
-    U32 startTime = FUZ_GetMilliStart();
+    clock_t startClock = clock();
 
     /* allocations */
     zc = ZBUFF_createCCtx();
@@ -321,7 +307,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres
         FUZ_rand(&coreSeed);
 
     /* test loop */
-    for ( ; (testNb <= nbTests) || (FUZ_GetMilliSpan(startTime) < g_testTime) ; testNb++ ) {
+    for ( ; (testNb <= nbTests) || (FUZ_GetClockSpan(startClock) < g_clockTime) ; testNb++ ) {
         U32 lseed;
         const BYTE* srcBuffer;
         const BYTE* dict;
@@ -543,7 +529,7 @@ int main(int argc, const char** argv)
 
                 case 'i':
                     argument++;
-                    nbTests=0; g_testTime=0;
+                    nbTests=0; g_clockTime=0;
                     while ((*argument>='0') && (*argument<='9')) {
                         nbTests *= 10;
                         nbTests += *argument - '0';
@@ -553,15 +539,15 @@ int main(int argc, const char** argv)
 
                 case 'T':
                     argument++;
-                    nbTests=0; g_testTime=0;
+                    nbTests=0; g_clockTime=0;
                     while ((*argument>='0') && (*argument<='9')) {
-                        g_testTime *= 10;
-                        g_testTime += *argument - '0';
+                        g_clockTime *= 10;
+                        g_clockTime += *argument - '0';
                         argument++;
                     }
-                    if (*argument=='m') g_testTime *=60, argument++;
+                    if (*argument=='m') g_clockTime *=60, argument++;
                     if (*argument=='n') argument++;
-                    g_testTime *= 1000;
+                    g_clockTime *= CLOCKS_PER_SEC;
                     break;
 
                 case 's':
@@ -605,7 +591,11 @@ int main(int argc, const char** argv)
     /* Get Seed */
     DISPLAY("Starting zstd_buffered tester (%i-bits, %s)\n", (int)(sizeof(size_t)*8), ZSTD_VERSION_STRING);
 
-    if (!seedset) seed = FUZ_GetMilliStart() % 10000;
+    if (!seedset) {
+        time_t const t = time(NULL);
+        U32 const h = XXH32(&t, sizeof(t), 1);
+        seed = h % 10000;
+    }
     DISPLAY("Seed = %u\n", seed);
     if (proba!=FUZ_COMPRESSIBILITY_DEFAULT) DISPLAY("Compressibility : %i%%\n", proba);
 
diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
index f021974..8486013 100644
--- a/tests/zstreamtest.c
+++ b/tests/zstreamtest.c
@@ -23,13 +23,13 @@
 **************************************/
 #include <stdlib.h>       /* free */
 #include <stdio.h>        /* fgets, sscanf */
-#include <sys/timeb.h>    /* timeb */
+#include <time.h>         /* clock_t, clock() */
 #include <string.h>       /* strcmp */
 #include "mem.h"
-#define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_maxCLevel */
+#define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_maxCLevel, ZSTD_customMem */
 #include "zstd.h"         /* ZSTD_compressBound */
 #include "datagen.h"      /* RDG_genBuffer */
-#define XXH_STATIC_LINKING_ONLY
+#define XXH_STATIC_LINKING_ONLY   /* XXH64_state_t */
 #include "xxhash.h"       /* XXH64_* */
 
 
@@ -55,13 +55,13 @@ static const U32 prime2 = 2246822519U;
 static U32 g_displayLevel = 2;
 
 #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
-            if ((FUZ_GetMilliSpan(g_displayTime) > g_refreshRate) || (g_displayLevel>=4)) \
-            { g_displayTime = FUZ_GetMilliStart(); DISPLAY(__VA_ARGS__); \
+            if ((FUZ_GetClockSpan(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \
+            { g_displayClock = clock(); DISPLAY(__VA_ARGS__); \
             if (g_displayLevel>=4) fflush(stdout); } }
-static const U32 g_refreshRate = 150;
-static U32 g_displayTime = 0;
+static const clock_t g_refreshRate = CLOCKS_PER_SEC / 6;
+static clock_t g_displayClock = 0;
 
-static U32 g_testTime = 0;
+static clock_t g_clockTime = 0;
 
 
 /*-*******************************************************
@@ -69,22 +69,9 @@ static U32 g_testTime = 0;
 *********************************************************/
 #define MAX(a,b) ((a)>(b)?(a):(b))
 
-static U32 FUZ_GetMilliStart(void)
+static clock_t FUZ_GetClockSpan(clock_t clockStart)
 {
-    struct timeb tb;
-    U32 nCount;
-    ftime( &tb );
-    nCount = (U32) (((tb.time & 0xFFFFF) * 1000) +  tb.millitm);
-    return nCount;
-}
-
-static U32 FUZ_GetMilliSpan(U32 nTimeStart)
-{
-    U32 const nCurrent = FUZ_GetMilliStart();
-    U32 nSpan = nCurrent - nTimeStart;
-    if (nTimeStart > nCurrent)
-        nSpan += 0x100000 * 1000;
-    return nSpan;
+    return clock() - clockStart;  /* works even when overflow. Max span ~ 30 mn */
 }
 
 /*! FUZ_rand() :
@@ -131,8 +118,7 @@ static void freeFunction(void* opaque, void* address)
 
 static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem customMem)
 {
-    int testResult = 0;
-    size_t CNBufferSize = COMPRESSIBLE_NOISE_LENGTH;
+    size_t const CNBufferSize = COMPRESSIBLE_NOISE_LENGTH;
     void* CNBuffer = malloc(CNBufferSize);
     size_t const skippableFrameSize = 11;
     size_t const compressedBufferSize = (8 + skippableFrameSize) + ZSTD_compressBound(COMPRESSIBLE_NOISE_LENGTH);
@@ -140,6 +126,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo
     size_t const decodedBufferSize = CNBufferSize;
     void* decodedBuffer = malloc(decodedBufferSize);
     size_t cSize;
+    int testResult = 0;
     U32 testNb=0;
     ZSTD_CStream* zc = ZSTD_createCStream_advanced(customMem);
     ZSTD_DStream* zd = ZSTD_createDStream_advanced(customMem);
@@ -222,7 +209,8 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo
 
     /* Byte-by-byte decompression test */
     DISPLAYLEVEL(4, "test%3i : decompress byte-by-byte : ", testNb++);
-    {   size_t r = 1;
+    {   /* skippable frame */
+        size_t r = 1;
         ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB);
         inBuff.src = compressedBuffer;
         outBuff.dst = decodedBuffer;
@@ -234,9 +222,10 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo
             r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
             if (ZSTD_isError(r)) goto _output_error;
         }
+        /* normal frame */
         ZSTD_initDStream_usingDict(zd, CNBuffer, 128 KB);
         r=1;
-        while (r) {   /* normal frame */
+        while (r) {
             inBuff.size = inBuff.pos + 1;
             outBuff.size = outBuff.pos + 1;
             r = ZSTD_decompressStream(zd, &outBuff, &inBuff);
@@ -255,6 +244,50 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo
     }   }
     DISPLAYLEVEL(4, "OK \n");
 
+    /* Complex context re-use scenario */
+    DISPLAYLEVEL(4, "test%3i : context re-use : ", testNb++);
+    ZSTD_freeCStream(zc);
+    zc = ZSTD_createCStream_advanced(customMem);
+    if (zc==NULL) goto _output_error;   /* memory allocation issue */
+    /* use 1 */
+    {   size_t const inSize = 513;
+        ZSTD_initCStream_advanced(zc, NULL, 0, ZSTD_getParams(19, inSize, 0), inSize);   /* needs btopt + search3 to trigger hashLog3 */
+        inBuff.src = CNBuffer;
+        inBuff.size = inSize;
+        inBuff.pos = 0;
+        outBuff.dst = (char*)(compressedBuffer)+cSize;
+        outBuff.size = ZSTD_compressBound(inSize);
+        outBuff.pos = 0;
+        { size_t const r = ZSTD_compressStream(zc, &outBuff, &inBuff);
+            if (ZSTD_isError(r)) goto _output_error; }
+        if (inBuff.pos != inBuff.size) goto _output_error;   /* entire input should be consumed */
+        { size_t const r = ZSTD_endStream(zc, &outBuff);
+            if (r != 0) goto _output_error; }  /* error, or some data not flushed */
+    }
+    /* use 2 */
+    {   size_t const inSize = 1025;   /* will not continue, because tables auto-adjust and are therefore different size */
+        ZSTD_initCStream_advanced(zc, NULL, 0, ZSTD_getParams(19, inSize, 0), inSize);   /* needs btopt + search3 to trigger hashLog3 */
+        inBuff.src = CNBuffer;
+        inBuff.size = inSize;
+        inBuff.pos = 0;
+        outBuff.dst = (char*)(compressedBuffer)+cSize;
+        outBuff.size = ZSTD_compressBound(inSize);
+        outBuff.pos = 0;
+        { size_t const r = ZSTD_compressStream(zc, &outBuff, &inBuff);
+            if (ZSTD_isError(r)) goto _output_error; }
+        if (inBuff.pos != inBuff.size) goto _output_error;   /* entire input should be consumed */
+        { size_t const r = ZSTD_endStream(zc, &outBuff);
+            if (r != 0) goto _output_error; }  /* error, or some data not flushed */
+    }
+    DISPLAYLEVEL(4, "OK \n");
+
+    DISPLAYLEVEL(4, "test%3i : check CStream size : ", testNb++);
+    { size_t const s = ZSTD_sizeof_CStream(zc);
+      if (ZSTD_isError(s)) goto _output_error;
+      DISPLAYLEVEL(4, "OK (%u bytes) \n", (U32)s);
+    }
+
+    /* test ZSTD_setDStreamParameter() resilience */
     DISPLAYLEVEL(4, "test%3i : wrong parameter for ZSTD_setDStreamParameter(): ", testNb++);
     { size_t const r = ZSTD_setDStreamParameter(zd, (ZSTD_DStreamParameter_e)999, 1);  /* large limit */
       if (!ZSTD_isError(r)) goto _output_error; }
@@ -291,6 +324,8 @@ _output_error:
 }
 
 
+/* ======   Fuzzer tests   ====== */
+
 static size_t findDiff(const void* buf1, const void* buf2, size_t max)
 {
     const BYTE* b1 = (const BYTE*)buf1;
@@ -323,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<<maxSrcLog;
     BYTE* cNoiseBuffer[5];
-    size_t srcBufferSize = (size_t)1<<maxSrcLog;
-    BYTE* copyBuffer;
-    size_t copyBufferSize= srcBufferSize + (1<<maxSampleLog);
-    BYTE* cBuffer;
-    size_t cBufferSize   = ZSTD_compressBound(srcBufferSize);
-    BYTE* dstBuffer;
-    size_t dstBufferSize = srcBufferSize;
+    size_t const copyBufferSize= srcBufferSize + (1<<maxSampleLog);
+    BYTE*  const copyBuffer = (BYTE*)malloc (copyBufferSize);
+    size_t const cBufferSize   = ZSTD_compressBound(srcBufferSize);
+    BYTE*  const cBuffer = (BYTE*)malloc (cBufferSize);
+    size_t const dstBufferSize = srcBufferSize;
+    BYTE*  const dstBuffer = (BYTE*)malloc (dstBufferSize);
     U32 result = 0;
     U32 testNb = 0;
     U32 coreSeed = seed;
-    ZSTD_CStream* zc;
-    ZSTD_DStream* zd;
-    U32 startTime = FUZ_GetMilliStart();
+    ZSTD_CStream* zc = ZSTD_createCStream();   /* will be reset sometimes */
+    ZSTD_DStream* zd = ZSTD_createDStream();   /* will be reset sometimes */
+    ZSTD_DStream* const zd_noise = ZSTD_createDStream();
+    clock_t const startClock = clock();
+    const BYTE* dict=NULL;   /* can keep same dict on 2 consecutive tests */
+    size_t dictSize = 0;
+    U32 oldTestLog = 0;
 
     /* allocations */
-    zc = ZSTD_createCStream();
-    zd = ZSTD_createDStream();
     cNoiseBuffer[0] = (BYTE*)malloc (srcBufferSize);
     cNoiseBuffer[1] = (BYTE*)malloc (srcBufferSize);
     cNoiseBuffer[2] = (BYTE*)malloc (srcBufferSize);
     cNoiseBuffer[3] = (BYTE*)malloc (srcBufferSize);
     cNoiseBuffer[4] = (BYTE*)malloc (srcBufferSize);
-    copyBuffer= (BYTE*)malloc (copyBufferSize);
-    dstBuffer = (BYTE*)malloc (dstBufferSize);
-    cBuffer   = (BYTE*)malloc (cBufferSize);
     CHECK (!cNoiseBuffer[0] || !cNoiseBuffer[1] || !cNoiseBuffer[2] || !cNoiseBuffer[3] || !cNoiseBuffer[4] ||
-           !copyBuffer || !dstBuffer || !cBuffer || !zc || !zd,
+           !copyBuffer || !dstBuffer || !cBuffer || !zc || !zd || !zd_noise ,
            "Not enough memory, fuzzer tests cancelled");
 
     /* Create initial samples */
@@ -360,21 +394,21 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres
     RDG_genBuffer(cNoiseBuffer[3], srcBufferSize, 0.95, 0., coreSeed);    /* highly compressible */
     RDG_genBuffer(cNoiseBuffer[4], srcBufferSize, 1.00, 0., coreSeed);    /* sparse content */
     memset(copyBuffer, 0x65, copyBufferSize);                             /* make copyBuffer considered initialized */
+    ZSTD_initDStream_usingDict(zd, NULL, 0);  /* ensure at least one init */
 
     /* catch up testNb */
     for (testNb=1; testNb < startTest; testNb++)
         FUZ_rand(&coreSeed);
 
     /* test loop */
-    for ( ; (testNb <= nbTests) || (FUZ_GetMilliSpan(startTime) < g_testTime) ; testNb++ ) {
+    for ( ; (testNb <= nbTests) || (FUZ_GetClockSpan(startClock) < g_clockTime) ; testNb++ ) {
         U32 lseed;
         const BYTE* srcBuffer;
-        const BYTE* dict;
-        size_t maxTestSize, dictSize;
-        size_t cSize, totalTestSize, totalGenSize;
-        U32 n, nbChunks;
+        size_t totalTestSize, totalGenSize, cSize;
         XXH64_state_t xxhState;
         U64 crcOrig;
+        U32 resetAllowed = 1;
+        size_t maxTestSize;
 
         /* init */
         DISPLAYUPDATE(2, "\r%6u", testNb);
@@ -382,10 +416,10 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres
         FUZ_rand(&coreSeed);
         lseed = coreSeed ^ prime1;
 
-        /* states full reset (unsynchronized) */
-        /* some issues only happen when reusing states in a specific sequence of parameters */
-        if ((FUZ_rand(&lseed) & 0xFF) == 131) { ZSTD_freeCStream(zc); zc = ZSTD_createCStream(); }
-        if ((FUZ_rand(&lseed) & 0xFF) == 132) { ZSTD_freeDStream(zd); zd = ZSTD_createDStream(); }
+        /* states full reset (deliberately not synchronized) */
+        /* some issues can only happen when reusing states */
+        if ((FUZ_rand(&lseed) & 0xFF) == 131) { ZSTD_freeCStream(zc); zc = ZSTD_createCStream(); resetAllowed=0; }
+        if ((FUZ_rand(&lseed) & 0xFF) == 132) { ZSTD_freeDStream(zd); zd = ZSTD_createDStream(); ZSTD_initDStream_usingDict(zd, NULL, 0);  /* ensure at least one init */ }
 
         /* srcBuffer selection [0-4] */
         {   U32 buffNb = FUZ_rand(&lseed) & 0x7F;
@@ -403,32 +437,44 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres
         }
 
         /* compression init */
-        {   U32 const testLog = FUZ_rand(&lseed) % maxSrcLog;
+        if ((FUZ_rand(&lseed)&1) /* at beginning, to keep same nb of rand */
+            && oldTestLog /* at least one test happened */ && resetAllowed) {
+            maxTestSize = FUZ_randomLength(&lseed, oldTestLog+2);
+            if (maxTestSize >= 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)==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);
-                    CHECK (ZSTD_isError(initError),"init error : %s", ZSTD_getErrorName(initError));
+                {   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 ; (n<nbChunks) && (totalTestSize < maxTestSize) ; n++) {
+            U32 n;
+            for (n=0, cSize=0, totalTestSize=0 ; totalTestSize < maxTestSize ; n++) {
                 /* compress random chunk into random size dst buffer */
-                {   size_t const readChunkSize = FUZ_randomLength(&lseed, maxSampleLog);
-                    size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - readChunkSize);
+                {   size_t const randomSrcSize = FUZ_randomLength(&lseed, maxSampleLog);
+                    size_t const srcSize = MIN (maxTestSize-totalTestSize, randomSrcSize);
+                    size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize);
                     size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog);
                     size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize);
-                    ZSTD_inBuffer inBuff = { srcBuffer+srcStart, readChunkSize, 0 };
+                    ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 };
                     outBuff.size = outBuff.pos + dstBuffSize;
 
                     { size_t const compressionError = ZSTD_compressStream(zc, &outBuff, &inBuff);
@@ -464,7 +510,10 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres
         }
 
         /* multi - fragments decompression test */
-        ZSTD_initDStream_usingDict(zd, dict, dictSize);
+        if (!dictSize /* don't reset if dictionary : could be different */ && (FUZ_rand(&lseed) & 1)) {
+            CHECK( ZSTD_isError(ZSTD_resetDStream(zd)), "ZSTD_resetDStream failed");
+        } else
+            ZSTD_initDStream_usingDict(zd, dict, dictSize);
         {   size_t decompressionResult = 1;
             ZSTD_inBuffer  inBuff = { cBuffer, cSize, 0 };
             ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
@@ -498,7 +547,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres
         }   }
 
         /* try decompression on noisy data */
-        ZSTD_initDStream(zd);
+        ZSTD_initDStream(zd_noise);   /* note : no dictionary */
         {   ZSTD_inBuffer  inBuff = { cBuffer, cSize, 0 };
             ZSTD_outBuffer outBuff= { dstBuffer, dstBufferSize, 0 };
             while (outBuff.pos < dstBufferSize) {
@@ -515,6 +564,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres
 _cleanup:
     ZSTD_freeCStream(zc);
     ZSTD_freeDStream(zd);
+    ZSTD_freeDStream(zd_noise);
     free(cNoiseBuffer[0]);
     free(cNoiseBuffer[1]);
     free(cNoiseBuffer[2]);
@@ -594,7 +644,7 @@ int main(int argc, const char** argv)
 
                 case 'i':
                     argument++;
-                    nbTests=0; g_testTime=0;
+                    nbTests=0; g_clockTime=0;
                     while ((*argument>='0') && (*argument<='9')) {
                         nbTests *= 10;
                         nbTests += *argument - '0';
@@ -604,15 +654,15 @@ int main(int argc, const char** argv)
 
                 case 'T':
                     argument++;
-                    nbTests=0; g_testTime=0;
+                    nbTests=0; g_clockTime=0;
                     while ((*argument>='0') && (*argument<='9')) {
-                        g_testTime *= 10;
-                        g_testTime += *argument - '0';
+                        g_clockTime *= 10;
+                        g_clockTime += *argument - '0';
                         argument++;
                     }
-                    if (*argument=='m') g_testTime *=60, argument++;
+                    if (*argument=='m') g_clockTime *=60, argument++;
                     if (*argument=='n') argument++;
-                    g_testTime *= 1000;
+                    g_clockTime *= CLOCKS_PER_SEC;
                     break;
 
                 case 's':
@@ -656,7 +706,12 @@ int main(int argc, const char** argv)
     /* Get Seed */
     DISPLAY("Starting zstream tester (%i-bits, %s)\n", (int)(sizeof(size_t)*8), ZSTD_VERSION_STRING);
 
-    if (!seedset) seed = FUZ_GetMilliStart() % 10000;
+    if (!seedset) {
+        time_t const t = time(NULL);
+        U32 const h = XXH32(&t, sizeof(t), 1);
+        seed = h % 10000;
+    }
+
     DISPLAY("Seed = %u\n", seed);
     if (proba!=FUZ_COMPRESSIBILITY_DEFAULT) DISPLAY("Compressibility : %i%%\n", proba);
 
diff --git a/zlibWrapper/.gitignore b/zlibWrapper/.gitignore
index bf3f3d8..f197c8c 100644
--- a/zlibWrapper/.gitignore
+++ b/zlibWrapper/.gitignore
@@ -26,4 +26,4 @@ foo.gz
 # Misc files
 *.bat
 *.zip
-examples/example2.c
+*.txt
diff --git a/zlibWrapper/Makefile b/zlibWrapper/Makefile
index 9ad1c01..69c976f 100644
--- a/zlibWrapper/Makefile
+++ b/zlibWrapper/Makefile
@@ -1,54 +1,71 @@
 # Makefile for example of using zstd wrapper for zlib
 #
-# make - compiles statically and dynamically linked examples/example.c
-# make test testdll - compiles and runs statically and dynamically linked examples/example.c
-# make LOC=-DZWRAP_USE_ZSTD=1 - compiles statically and dynamically linked examples/example.c with zstd compression turned on
+# make - compiles examples
+# make LOC=-DZWRAP_USE_ZSTD=1 - compiles examples with zstd compression turned on
+# make test - runs examples
 
 
 # Paths to static and dynamic zlib and zstd libraries
-# Use "make ZLIBDIR=path/to/zlib" to select a path to library
-ifdef ZLIBDIR
-STATICLIB = $(ZLIBDIR)/libz.a ../lib/libzstd.a
-IMPLIB    = $(ZLIBDIR)/libz.dll.a ../lib/libzstd.a
-else
-STATICLIB = -static -lz ../lib/libzstd.a
-IMPLIB    = -lz ../lib/libzstd.a
-endif
+# Use "make ZLIB_LIBRARY=path/to/zlib" to select a path to library
+ZLIB_LIBRARY ?= -lz
 
+ZSTDLIBDIR = ../lib
+ZSTDLIBRARY = $(ZSTDLIBDIR)/libzstd.a
 ZLIBWRAPPER_PATH = .
 EXAMPLE_PATH = examples
+PROGRAMS_PATH = ../programs
 CC ?= gcc
-CFLAGS = $(LOC) -I../lib -I../lib/common -I$(ZLIBDIR) -I$(ZLIBWRAPPER_PATH) -O3 -std=gnu90
+CFLAGS ?= -O3 
+CFLAGS += $(LOC) -I$(PROGRAMS_PATH) -I$(ZSTDLIBDIR) -I$(ZSTDLIBDIR)/common -I$(ZLIBWRAPPER_PATH) -std=gnu99
 CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef
 LDFLAGS = $(LOC)
 RM = rm -f
 
 
-all: clean test testzstd
+all: clean fitblk example zwrapbench
 
-test: example
+test: example fitblk example_zstd fitblk_zstd zwrapbench
 	./example
-
-testdll: example_d
-	./example_d
-
-testzstd: example_zstd
 	./example_zstd
+	./fitblk 10240 <../zstd_compression_format.md
+	./fitblk 40960 <../zstd_compression_format.md
+	./fitblk_zstd 10240 <../zstd_compression_format.md
+	./fitblk_zstd 40960 <../zstd_compression_format.md
+	./zwrapbench -qb3B1K ../zstd_compression_format.md
+	./zwrapbench -rqb1e5 ../lib ../programs ../tests
+
+#valgrindTest: ZSTDLIBRARY = $(ZSTDLIBDIR)/libzstd.so
+valgrindTest: VALGRIND = LD_LIBRARY_PATH=$(ZSTDLIBDIR) valgrind --track-origins=yes --leak-check=full --error-exitcode=1
+valgrindTest: clean example fitblk example_zstd fitblk_zstd zwrapbench
+	@echo "\n ---- valgrind tests ----"
+	$(VALGRIND) ./example
+	$(VALGRIND) ./example_zstd
+	$(VALGRIND) ./fitblk 10240 <../zstd_compression_format.md
+	$(VALGRIND) ./fitblk 40960 <../zstd_compression_format.md
+	$(VALGRIND) ./fitblk_zstd 10240 <../zstd_compression_format.md
+	$(VALGRIND) ./fitblk_zstd 40960 <../zstd_compression_format.md
+	$(VALGRIND) ./zwrapbench -qb3B1K ../zstd_compression_format.md
+	$(VALGRIND) ./zwrapbench -rqb1e5 ../lib ../programs ../tests
 
 .c.o:
 	$(CC) $(CFLAGS) -c -o $@ $<
+ 
+example: $(EXAMPLE_PATH)/example.o $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o $(ZSTDLIBRARY)
+	$(CC) $(LDFLAGS) -o $@ $(EXAMPLE_PATH)/example.o $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o $(ZSTDLIBRARY) $(ZLIB_LIBRARY)
+
+example_zstd: $(EXAMPLE_PATH)/example.o $(ZLIBWRAPPER_PATH)/zstdTurnedOn_zlibwrapper.o $(ZSTDLIBRARY)
+	$(CC) $(LDFLAGS)  -o $@ $(EXAMPLE_PATH)/example.o $(ZLIBWRAPPER_PATH)/zstdTurnedOn_zlibwrapper.o $(ZSTDLIBRARY) $(ZLIB_LIBRARY)
 
-example: $(EXAMPLE_PATH)/example.o $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o
-	$(CC) $(LDFLAGS) -o $@ $(EXAMPLE_PATH)/example.o $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o $(STATICLIB)
+fitblk: $(EXAMPLE_PATH)/fitblk.o $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o $(ZSTDLIBRARY)
+	$(CC) $(LDFLAGS) -o $@ $(EXAMPLE_PATH)/fitblk.o $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o $(ZSTDLIBRARY) $(ZLIB_LIBRARY)
 
-example_d: $(EXAMPLE_PATH)/example.o $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o
-	$(CC) $(LDFLAGS) -o $@ $(EXAMPLE_PATH)/example.o $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o $(IMPLIB)
+fitblk_zstd: $(EXAMPLE_PATH)/fitblk.o $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o $(ZSTDLIBRARY)
+	$(CC) $(LDFLAGS) -o $@ $(EXAMPLE_PATH)/fitblk.o $(ZLIBWRAPPER_PATH)/zstdTurnedOn_zlibwrapper.o $(ZSTDLIBRARY) $(ZLIB_LIBRARY)
 
-example_zstd: $(EXAMPLE_PATH)/example.o $(ZLIBWRAPPER_PATH)/zstdTurnedOn_zlibwrapper.o
-	$(CC) $(LDFLAGS)  -o $@ $(EXAMPLE_PATH)/example.o $(ZLIBWRAPPER_PATH)/zstdTurnedOn_zlibwrapper.o $(STATICLIB)
+zwrapbench: $(EXAMPLE_PATH)/zwrapbench.o $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o $(PROGRAMS_PATH)/datagen.o $(ZSTDLIBRARY)
+	$(CC) $(LDFLAGS) -o $@ $(EXAMPLE_PATH)/zwrapbench.o $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o $(PROGRAMS_PATH)/datagen.o $(ZSTDLIBRARY) $(ZLIB_LIBRARY)
 
-$(EXAMPLE_PATH)/example.o: $(EXAMPLE_PATH)/example.c
-	$(CC) $(CFLAGS) -I. -c -o $@ $(EXAMPLE_PATH)/example.c
+$(EXAMPLE_PATH)/zwrapbench.o: $(EXAMPLE_PATH)/zwrapbench.c
 
 $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o: $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.c $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.h
 	$(CC) $(CFLAGS) -I. -c -o $@ $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.c
@@ -56,6 +73,12 @@ $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.o: $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.c $
 $(ZLIBWRAPPER_PATH)/zstdTurnedOn_zlibwrapper.o: $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.c $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.h
 	$(CC) $(CFLAGS) -DZWRAP_USE_ZSTD=1 -I. -c -o $@ $(ZLIBWRAPPER_PATH)/zstd_zlibwrapper.c
 
+$(ZSTDLIBDIR)/libzstd.a:
+	$(MAKE) -C $(ZSTDLIBDIR) all
+
+$(ZSTDLIBDIR)/libzstd.so:
+	$(MAKE) -C $(ZSTDLIBDIR) all
+
 clean:
-	-$(RM) $(ZLIBWRAPPER_PATH)/*.o $(EXAMPLE_PATH)/*.o *.o *.exe foo.gz example example_d example_zstd
+	-$(RM) $(ZLIBWRAPPER_PATH)/*.o $(EXAMPLE_PATH)/*.o *.o *.exe foo.gz example example_zstd fitblk fitblk_zstd zwrapbench
 	@echo Cleaning completed
diff --git a/zlibWrapper/README.md b/zlibWrapper/README.md
index 3a39f00..427cdbe 100644
--- a/zlibWrapper/README.md
+++ b/zlibWrapper/README.md
@@ -23,10 +23,10 @@ Let's assume that your project that uses zlib is compiled with:
 
 To compile the zstd wrapper with your project you have to do the following:
 - change all references with ```#include "zlib.h"``` to ```#include "zstd_zlibwrapper.h"```
-- compile your project with zlib_wrapper.c and a static or dynamic zstd library
+- compile your project with `zstd_zlibwrapper.c` and a static or dynamic zstd library
 
 The linking should be changed to:
-```gcc project.o zlib_wrapper.o -lz -lzstd```
+```gcc project.o zstd_zlibwrapper.o -lz -lzstd```
 
 
 #### Enabling zstd compression within your project
@@ -34,8 +34,10 @@ The linking should be changed to:
 After embedding the zstd wrapper within your project the zstd library is turned off by default.
 Your project should work as before with zlib. There are two options to enable zstd compression:
 - compilation with ```-DZWRAP_USE_ZSTD=1``` (or using ```#define ZWRAP_USE_ZSTD 1``` before ```#include "zstd_zlibwrapper.h"```)
-- using the ```void useZSTD(int turn_on)``` function (declared in ```#include "zstd_zlibwrapper.h"```)
-There is no switch for zstd decompression because zlib and zstd streams are automatically detected and decompressed using a proper library.
+- using the ```void ZWRAP_useZSTDcompression(int turn_on)``` function (declared in ```#include "zstd_zlibwrapper.h"```)
+
+During decompression zlib and zstd streams are automatically detected and decompressed using a proper library.
+This behavior can be changed using `ZWRAP_setDecompressionType(ZWRAP_FORCE_ZLIB)` what will make zlib decompression slightly faster.
 
 
 #### Example
@@ -52,7 +54,7 @@ after inflateSync(): hello, hello!
 inflate with dictionary: hello, hello!
 ```
 Then we have changed ```#include "zlib.h"``` to ```#include "zstd_zlibwrapper.h"```, compiled the [example.c](examples/example.c) file
-with ```-DZWRAP_USE_ZSTD=1``` and linked with additional ```zlib_wrapper.o -lzstd```.
+with ```-DZWRAP_USE_ZSTD=1``` and linked with additional ```zstd_zlibwrapper.o -lzstd```.
 We were forced to turn off the following functions: ```test_gzio```, ```test_flush```, ```test_sync``` which use currently unsupported features.
 After running it shows the following results:
 ```
@@ -65,18 +67,72 @@ inflate with dictionary: hello, hello!
 The script used for compilation can be found at [zlibWrapper/Makefile](Makefile).
 
 
+#### The measurement of performace of Zstandard wrapper for zlib
+
+The zstd distribution contains a tool called `zwrapbench` which can measure speed and ratio of zlib, zstd, and the wrapper.
+The benchmark is conducted using given filenames or synthetic data if filenames are not provided.
+The files are read into memory and joined together. 
+It makes benchmark more precise as it eliminates I/O overhead. 
+Many filenames can be supplied as multiple parameters, parameters with wildcards or names of directories can be used as parameters with the -r option.
+One can select compression levels starting from `-b` and ending with `-e`. The `-i` parameter selects minimal time used for each of tested levels.
+With `-B` option bigger files can be divided into smaller, independently compressed blocks. 
+The benchmark tool can be compiled with `make zwrapbench` using [zlibWrapper/Makefile](Makefile).
+
+
+#### Improving speed of streaming compression
+
+During streaming compression the compressor never knows how big is data to compress.
+Zstandard compression can be improved by providing size of source data to the compressor. By default streaming compressor assumes that data is bigger than 256 KB but it can hurt compression speed on smaller data. 
+The zstd wrapper provides the `ZWRAP_setPledgedSrcSize()` function that allows to change a pledged source size for a given compression stream.
+The function will change zstd compression parameters what may improve compression speed and/or ratio.
+It should be called just after `deflateInit()`or `deflateReset()` and before `deflate()` or `deflateSetDictionary()`. The function is 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.
+
+
+#### Reusing contexts
+
+The ordinary zlib compression of two files/streams allocates two contexts:
+- for the 1st file calls `deflateInit`, `deflate`, `...`, `deflate`, `defalateEnd`
+- for the 2nd file calls `deflateInit`, `deflate`, `...`, `deflate`, `defalateEnd`
+
+The speed of compression can be improved with reusing a single context with following steps:
+- initialize the context with `deflateInit`
+- for the 1st file call `deflate`, `...`, `deflate`
+- for the 2nd file call `deflateReset`, `deflate`, `...`, `deflate`
+- free the context with `deflateEnd`
+
+To check the difference we made experiments using `zwrapbench -ri6b6` with zstd and zlib compression (both at level 6).
+The input data was decompressed git repository downloaded from https://github.com/git/git/archive/master.zip which contains 2979 files.
+The table below shows that reusing contexts has a minor influence on zlib but it gives improvement for zstd.
+In our example (the last 2 lines) it gives 4% better compression speed and 5% better decompression speed.
+
+| Compression type                                  | Compression | Decompress.| Compr. size | Ratio |
+| ------------------------------------------------- | ------------| -----------| ----------- | ----- |
+| zlib 1.2.8                                        |  30.51 MB/s | 219.3 MB/s |     6819783 | 3.459 |
+| zlib 1.2.8 not reusing a context                  |  30.22 MB/s | 218.1 MB/s |     6819783 | 3.459 |
+| zlib 1.2.8 with zlibWrapper and reusing a context |  30.40 MB/s | 218.9 MB/s |     6819783 | 3.459 |
+| zlib 1.2.8 with zlibWrapper not reusing a context |  30.28 MB/s | 218.1 MB/s |     6819783 | 3.459 |
+| zstd 1.1.0 using ZSTD_CCtx                        |  68.35 MB/s | 430.9 MB/s |     6868521 | 3.435 |
+| zstd 1.1.0 using ZSTD_CStream                     |  66.63 MB/s | 422.3 MB/s |     6868521 | 3.435 |
+| zstd 1.1.0 with zlibWrapper and reusing a context |  54.01 MB/s | 403.2 MB/s |     6763482 | 3.488 |
+| zstd 1.1.0 with zlibWrapper not reusing a context |  51.59 MB/s | 383.7 MB/s |     6763482 | 3.488 |
+
+
 #### Compatibility issues
-After enabling zstd compression not all native zlib functions are supported. When calling unsupported methods they print error message and return an error value.
+After enabling zstd compression not all native zlib functions are supported. When calling unsupported methods they put error message into strm->msg 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 bbb2cd5..20ed81d 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 0000000..e3fda3c
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+//#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 0000000..c61de5c
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#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 0000000..d16fcfd
--- /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 <stdlib.h>      /* malloc, free */
+#include <string.h>      /* memset */
+#include <stdio.h>       /* fprintf, fopen, ftello64 */
+#include <time.h>        /* clock_t, clock, CLOCKS_PER_SEC */
+#include <ctype.h>       /* 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<nbFiles; fileNb++) {
+            size_t remaining = fileSizes[fileNb];
+            U32 const nbBlocksforThisFile = (U32)((remaining + (blockSize-1)) / blockSize);
+            U32 const blockEnd = nbBlocks + nbBlocksforThisFile;
+            for ( ; nbBlocks<blockEnd; nbBlocks++) {
+                size_t const thisBlockSize = MIN(remaining, blockSize);
+                blockTable[nbBlocks].srcPtr = srcPtr;
+                blockTable[nbBlocks].cPtr = cPtr;
+                blockTable[nbBlocks].resPtr = resPtr;
+                blockTable[nbBlocks].srcSize = thisBlockSize;
+                blockTable[nbBlocks].cRoom = ZSTD_compressBound(thisBlockSize);
+                srcPtr += thisBlockSize;
+                cPtr += blockTable[nbBlocks].cRoom;
+                resPtr += thisBlockSize;
+                remaining -= thisBlockSize;
+    }   }   }
+
+    /* warmimg up memory */
+    RDG_genBuffer(compressedBuffer, maxCompressedSize, 0.10, 0.50, 1);
+
+    /* Bench */
+    {   U64 fastestC = (U64)(-1LL), fastestD = (U64)(-1LL);
+        U64 const crcOrig = XXH64(srcBuffer, srcSize, 0);
+        UTIL_time_t coolTime;
+        U64 const maxTime = (g_nbIterations * TIMELOOP_MICROSEC) + 100;
+        U64 totalCTime=0, totalDTime=0;
+        U32 cCompleted=0, dCompleted=0;
+#       define NB_MARKS 4
+        const char* const marks[NB_MARKS] = { " |", " /", " =",  "\\" };
+        U32 markNb = 0;
+        size_t cSize = 0;
+        double ratio = 0.;
+
+        UTIL_getTime(&coolTime);
+        DISPLAYLEVEL(2, "\r%79s\r", "");
+        while (!cCompleted | !dCompleted) {
+            UTIL_time_t clockStart;
+            U64 clockLoop = g_nbIterations ? TIMELOOP_MICROSEC : 1;
+
+            /* overheat protection */
+            if (UTIL_clockSpanMicro(coolTime, ticksPerSecond) > 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; blockNb<nbBlocks; blockNb++) {
+                            size_t const rSize = ZSTD_compress_usingCDict(ctx,
+                                                blockTable[blockNb].cPtr,  blockTable[blockNb].cRoom,
+                                                blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize,
+                                                cdict);
+                            if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_compress_usingCDict() failed : %s", ZSTD_getErrorName(rSize));
+                            blockTable[blockNb].cSize = rSize;
+                        }
+                        nbLoops++;
+                    } while (UTIL_clockSpanMicro(clockStart, ticksPerSecond) < clockLoop);
+                    ZSTD_freeCDict(cdict);
+                } else if (compressor == BMK_ZSTD_STREAM) {
+                    ZSTD_parameters const zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize);
+                    ZSTD_inBuffer inBuffer;
+                    ZSTD_outBuffer outBuffer;
+                    ZSTD_CStream* zbc = ZSTD_createCStream();
+                    size_t rSize;
+                    if (zbc == NULL) EXM_THROW(1, "ZSTD_createCStream() allocation failure");
+                    rSize = ZSTD_initCStream_advanced(zbc, dictBuffer, dictBufferSize, zparams, avgSize);
+                    if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_initCStream_advanced() failed : %s", ZSTD_getErrorName(rSize));
+                    do {
+                        U32 blockNb;
+                        for (blockNb=0; blockNb<nbBlocks; blockNb++) {
+                            rSize = ZSTD_resetCStream(zbc, blockTable[blockNb].srcSize);
+                            if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_resetCStream() failed : %s", ZSTD_getErrorName(rSize));
+                            inBuffer.src = blockTable[blockNb].srcPtr;
+                            inBuffer.size = blockTable[blockNb].srcSize;
+                            inBuffer.pos = 0;
+                            outBuffer.dst = blockTable[blockNb].cPtr;
+                            outBuffer.size = blockTable[blockNb].cRoom;
+                            outBuffer.pos = 0;
+                            rSize = ZSTD_compressStream(zbc, &outBuffer, &inBuffer);
+                            if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_compressStream() failed : %s", ZSTD_getErrorName(rSize));
+                            rSize = ZSTD_endStream(zbc, &outBuffer);
+                            if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_endStream() failed : %s", ZSTD_getErrorName(rSize));
+                            blockTable[blockNb].cSize = outBuffer.pos;
+                        }
+                        nbLoops++;
+                    } while (UTIL_clockSpanMicro(clockStart, ticksPerSecond) < clockLoop);
+                    ZSTD_freeCStream(zbc);
+                } else if (compressor == BMK_ZWRAP_ZLIB_REUSE || compressor == BMK_ZWRAP_ZSTD_REUSE || compressor == BMK_ZLIB_REUSE) {
+                    z_stream def;
+                    int ret;
+                    int useSetDict = (dictBuffer != NULL);
+                    if (compressor == BMK_ZLIB_REUSE || compressor == BMK_ZWRAP_ZLIB_REUSE) ZWRAP_useZSTDcompression(0);
+                    else ZWRAP_useZSTDcompression(1);
+                    def.zalloc = Z_NULL;
+                    def.zfree = Z_NULL;
+                    def.opaque = Z_NULL;
+                    ret = deflateInit(&def, cLevel);
+                    if (ret != Z_OK) EXM_THROW(1, "deflateInit failure");
+                 /*   if (ZWRAP_isUsingZSTDcompression()) {
+                        ret = ZWRAP_setPledgedSrcSize(&def, avgSize);
+                        if (ret != Z_OK) EXM_THROW(1, "ZWRAP_setPledgedSrcSize failure");
+                    } */
+                    do {
+                        U32 blockNb;
+                        for (blockNb=0; blockNb<nbBlocks; blockNb++) {
+                            if (ZWRAP_isUsingZSTDcompression())
+                                ret = ZWRAP_deflateReset_keepDict(&def); /* reuse dictionary to make compression faster */
+                            else
+                                ret = deflateReset(&def);
+                            if (ret != Z_OK) EXM_THROW(1, "deflateReset failure");
+                            if (useSetDict) {
+                                ret = deflateSetDictionary(&def, dictBuffer, dictBufferSize);
+                                if (ret != Z_OK) EXM_THROW(1, "deflateSetDictionary failure");
+                                if (ZWRAP_isUsingZSTDcompression()) useSetDict = 0; /* zstd doesn't require deflateSetDictionary after ZWRAP_deflateReset_keepDict */
+                            }
+                            def.next_in = (const void*) blockTable[blockNb].srcPtr;
+                            def.avail_in = blockTable[blockNb].srcSize;
+                            def.total_in = 0;
+                            def.next_out = (void*) blockTable[blockNb].cPtr;
+                            def.avail_out = blockTable[blockNb].cRoom;
+                            def.total_out = 0;
+                            ret = deflate(&def, Z_FINISH);
+                            if (ret != Z_STREAM_END) EXM_THROW(1, "deflate failure ret=%d srcSize=%d" , ret, (int)blockTable[blockNb].srcSize);
+                            blockTable[blockNb].cSize = def.total_out;
+                        }
+                        nbLoops++;
+                    } while (UTIL_clockSpanMicro(clockStart, ticksPerSecond) < clockLoop);
+                    ret = deflateEnd(&def);
+                    if (ret != Z_OK) EXM_THROW(1, "deflateEnd failure");
+                } else {
+                    z_stream def;
+                    if (compressor == BMK_ZLIB || compressor == BMK_ZWRAP_ZLIB) ZWRAP_useZSTDcompression(0);
+                    else ZWRAP_useZSTDcompression(1);
+                    do {
+                        U32 blockNb;
+                        for (blockNb=0; blockNb<nbBlocks; blockNb++) {
+                            int ret;
+                            def.zalloc = Z_NULL;
+                            def.zfree = Z_NULL;
+                            def.opaque = Z_NULL;
+                            ret = deflateInit(&def, cLevel);
+                            if (ret != Z_OK) EXM_THROW(1, "deflateInit failure");
+                            if (dictBuffer) {
+                                ret = deflateSetDictionary(&def, dictBuffer, dictBufferSize);
+                                if (ret != Z_OK) EXM_THROW(1, "deflateSetDictionary failure");
+                            }
+                            def.next_in = (const void*) blockTable[blockNb].srcPtr;
+                            def.avail_in = blockTable[blockNb].srcSize;
+                            def.total_in = 0;
+                            def.next_out = (void*) blockTable[blockNb].cPtr;
+                            def.avail_out = blockTable[blockNb].cRoom;
+                            def.total_out = 0;
+                            ret = deflate(&def, Z_FINISH);
+                            if (ret != Z_STREAM_END) EXM_THROW(1, "deflate failure");
+                            ret = deflateEnd(&def);
+                            if (ret != Z_OK) EXM_THROW(1, "deflateEnd failure");
+                            blockTable[blockNb].cSize = def.total_out;
+                        }
+                        nbLoops++;
+                    } while (UTIL_clockSpanMicro(clockStart, ticksPerSecond) < clockLoop);
+                }
+                {   U64 const clockSpan = UTIL_clockSpanMicro(clockStart, ticksPerSecond);
+                    if (clockSpan < fastestC*nbLoops) fastestC = clockSpan / nbLoops;
+                    totalCTime += clockSpan;
+                    cCompleted = totalCTime>maxTime;
+            }   }
+
+            cSize = 0;
+            { U32 blockNb; for (blockNb=0; blockNb<nbBlocks; blockNb++) cSize += blockTable[blockNb].cSize; }
+            ratio = (double)srcSize / (double)cSize;
+            markNb = (markNb+1) % NB_MARKS;
+            DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%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; blockNb<nbBlocks; blockNb++) {
+                            size_t const regenSize = ZSTD_decompress_usingDDict(dctx,
+                                blockTable[blockNb].resPtr, blockTable[blockNb].srcSize,
+                                blockTable[blockNb].cPtr, blockTable[blockNb].cSize,
+                                ddict);
+                            if (ZSTD_isError(regenSize)) {
+                                DISPLAY("ZSTD_decompress_usingDDict() failed on block %u : %s  \n",
+                                          blockNb, ZSTD_getErrorName(regenSize));
+                                clockLoop = 0;   /* force immediate test end */
+                                break;
+                            }
+                            blockTable[blockNb].resSize = regenSize;
+                        }
+                        nbLoops++;
+                    } while (UTIL_clockSpanMicro(clockStart, ticksPerSecond) < clockLoop);
+                    ZSTD_freeDDict(ddict);
+                } else if (compressor == BMK_ZSTD_STREAM) {
+                    ZSTD_inBuffer inBuffer;
+                    ZSTD_outBuffer outBuffer;
+                    ZSTD_DStream* zbd = ZSTD_createDStream();
+                    size_t rSize;
+                    if (zbd == NULL) EXM_THROW(1, "ZSTD_createDStream() allocation failure");
+                    rSize = ZSTD_initDStream_usingDict(zbd, dictBuffer, dictBufferSize);
+                    if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_initDStream() failed : %s", ZSTD_getErrorName(rSize));
+                    do {
+                        U32 blockNb;
+                        for (blockNb=0; blockNb<nbBlocks; blockNb++) {
+                            rSize = ZSTD_resetDStream(zbd);
+                            if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_resetDStream() failed : %s", ZSTD_getErrorName(rSize));
+                            inBuffer.src = blockTable[blockNb].cPtr;
+                            inBuffer.size = blockTable[blockNb].cSize;
+                            inBuffer.pos = 0;
+                            outBuffer.dst = blockTable[blockNb].resPtr;
+                            outBuffer.size = blockTable[blockNb].srcSize;
+                            outBuffer.pos = 0;
+                            rSize = ZSTD_decompressStream(zbd, &outBuffer, &inBuffer);
+                            if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_decompressStream() failed : %s", ZSTD_getErrorName(rSize));
+                            blockTable[blockNb].resSize = outBuffer.pos;
+                        }
+                        nbLoops++;
+                    } while (UTIL_clockSpanMicro(clockStart, ticksPerSecond) < clockLoop);
+                    ZSTD_freeDStream(zbd);
+                } else if (compressor == BMK_ZWRAP_ZLIB_REUSE || compressor == BMK_ZWRAP_ZSTD_REUSE || compressor == BMK_ZLIB_REUSE) {
+                    z_stream inf;
+                    int ret;
+                    if (compressor == BMK_ZLIB_REUSE) ZWRAP_setDecompressionType(ZWRAP_FORCE_ZLIB);
+                    else ZWRAP_setDecompressionType(ZWRAP_AUTO);
+                    inf.zalloc = Z_NULL;
+                    inf.zfree = Z_NULL;
+                    inf.opaque = Z_NULL;
+                    ret = inflateInit(&inf);
+                    if (ret != Z_OK) EXM_THROW(1, "inflateInit failure");
+                    do {
+                        U32 blockNb;
+                        for (blockNb=0; blockNb<nbBlocks; blockNb++) {
+                            if (ZWRAP_isUsingZSTDdecompression(&inf))
+                                ret = ZWRAP_inflateReset_keepDict(&inf); /* reuse dictionary to make decompression faster; inflate will return Z_NEED_DICT only for the first time */
+                            else
+                                ret = inflateReset(&inf);
+                            if (ret != Z_OK) EXM_THROW(1, "inflateReset failure");
+                            inf.next_in = (const void*) blockTable[blockNb].cPtr;
+                            inf.avail_in = blockTable[blockNb].cSize;
+                            inf.total_in = 0;
+                            inf.next_out = (void*) blockTable[blockNb].resPtr;
+                            inf.avail_out = blockTable[blockNb].srcSize;
+                            inf.total_out = 0;
+                            ret = inflate(&inf, Z_FINISH);
+                            if (ret == Z_NEED_DICT) {
+                                ret = inflateSetDictionary(&inf, dictBuffer, dictBufferSize);
+                                if (ret != Z_OK) EXM_THROW(1, "inflateSetDictionary failure");
+                                ret = inflate(&inf, Z_FINISH);
+                            }
+                            if (ret != Z_STREAM_END) EXM_THROW(1, "inflate failure");
+                            blockTable[blockNb].resSize = inf.total_out;
+                        }
+                        nbLoops++;
+                    } while (UTIL_clockSpanMicro(clockStart, ticksPerSecond) < clockLoop);
+                    ret = inflateEnd(&inf);
+                    if (ret != Z_OK) EXM_THROW(1, "inflateEnd failure");
+                } else {
+                    z_stream inf;
+                    if (compressor == BMK_ZLIB) ZWRAP_setDecompressionType(ZWRAP_FORCE_ZLIB);
+                    else ZWRAP_setDecompressionType(ZWRAP_AUTO);
+                    do {
+                        U32 blockNb;
+                        for (blockNb=0; blockNb<nbBlocks; blockNb++) {
+                            int ret;
+                            inf.zalloc = Z_NULL;
+                            inf.zfree = Z_NULL;
+                            inf.opaque = Z_NULL;
+                            ret = inflateInit(&inf);
+                            if (ret != Z_OK) EXM_THROW(1, "inflateInit failure");
+                            inf.next_in = (const void*) blockTable[blockNb].cPtr;
+                            inf.avail_in = blockTable[blockNb].cSize;
+                            inf.total_in = 0;
+                            inf.next_out = (void*) blockTable[blockNb].resPtr;
+                            inf.avail_out = blockTable[blockNb].srcSize;
+                            inf.total_out = 0;
+                            ret = inflate(&inf, Z_FINISH);
+                            if (ret == Z_NEED_DICT) {
+                                ret = inflateSetDictionary(&inf, dictBuffer, dictBufferSize);
+                                if (ret != Z_OK) EXM_THROW(1, "inflateSetDictionary failure");
+                                ret = inflate(&inf, Z_FINISH);
+                            }
+                            if (ret != Z_STREAM_END) EXM_THROW(1, "inflate failure");
+                            ret = inflateEnd(&inf);
+                            if (ret != Z_OK) EXM_THROW(1, "inflateEnd failure");
+                            blockTable[blockNb].resSize = inf.total_out;
+                        }
+                        nbLoops++;
+                    } while (UTIL_clockSpanMicro(clockStart, ticksPerSecond) < clockLoop);
+                }
+                {   U64 const clockSpan = UTIL_clockSpanMicro(clockStart, ticksPerSecond);
+                    if (clockSpan < fastestD*nbLoops) fastestD = clockSpan / nbLoops;
+                    totalDTime += clockSpan;
+                    dCompleted = totalDTime>maxTime;
+            }   }
+
+            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<srcSize; u++) {
+                        if (((const BYTE*)srcBuffer)[u] != ((const BYTE*)resultBuffer)[u]) {
+                            U32 segNb, bNb, pos;
+                            size_t bacc = 0;
+                            DISPLAY("Decoding error at pos %u ", (U32)u);
+                            for (segNb = 0; segNb < nbBlocks; segNb++) {
+                                if (bacc + blockTable[segNb].srcSize > 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<nbFiles; n++) {
+        FILE* f;
+        U64 fileSize = UTIL_getFileSize(fileNamesTable[n]);
+        if (UTIL_isDirectory(fileNamesTable[n])) {
+            DISPLAYLEVEL(2, "Ignoring %s directory...       \n", fileNamesTable[n]);
+            fileSizes[n] = 0;
+            continue;
+        }
+        f = fopen(fileNamesTable[n], "rb");
+        if (f==NULL) EXM_THROW(10, "impossible to open file %s", fileNamesTable[n]);
+        DISPLAYUPDATE(2, "Loading %s...       \r", fileNamesTable[n]);
+        if (fileSize > 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<argCount; argNb++) {
+        const char* argument = argv[argNb];
+        if(!argument) continue;   /* Protection if argument empty */
+
+        if (nextArgumentIsFile==0) {
+
+            /* long commands (--long-word) */
+            if (!strcmp(argument, "--")) { nextArgumentIsFile=1; continue; }
+            if (!strcmp(argument, "--version")) { displayOut=stdout; DISPLAY(WELCOME_MESSAGE); CLEAN_RETURN(0); }
+            if (!strcmp(argument, "--help")) { displayOut=stdout; CLEAN_RETURN(usage(programName)); }
+            if (!strcmp(argument, "--verbose")) { g_displayLevel++; continue; }
+            if (!strcmp(argument, "--quiet")) { g_displayLevel--; continue; }
+
+            /* Decode commands (note : aggregated commands are allowed) */
+            if (argument[0]=='-') {
+                argument++;
+
+                while (argument[0]!=0) {
+                    switch(argument[0])
+                    {
+                        /* Display help */
+                    case 'V': displayOut=stdout; DISPLAY(WELCOME_MESSAGE); CLEAN_RETURN(0);   /* Version Only */
+                    case 'H':
+                    case 'h': displayOut=stdout; CLEAN_RETURN(usage(programName));
+
+                        /* Use file content as dictionary */
+                    case 'D': nextEntryIsDictionary = 1; argument++; break;
+
+                        /* Verbose mode */
+                    case 'v': g_displayLevel++; argument++; break;
+
+                        /* Quiet mode */
+                    case 'q': g_displayLevel--; argument++; break;
+
+#ifdef UTIL_HAS_CREATEFILELIST
+                        /* recursive */
+                    case 'r': recursive=1; argument++; break;
+#endif
+
+                        /* Benchmark */
+                    case 'b':
+                            /* first compression Level */
+                            argument++;
+                            cLevel = readU32FromChar(&argument);
+                            break;
+
+                        /* range bench (benchmark only) */
+                    case 'e':
+                            /* last compression Level */
+                            argument++;
+                            cLevelLast = readU32FromChar(&argument);
+                            break;
+
+                        /* Modify Nb Iterations (benchmark only) */
+                    case 'i':
+                        argument++;
+                        {   U32 const iters = readU32FromChar(&argument);
+                            BMK_setNotificationLevel(g_displayLevel);
+                            BMK_SetNbIterations(iters);
+                        }
+                        break;
+
+                        /* cut input into blocks (benchmark only) */
+                    case 'B':
+                        argument++;
+                        {   size_t bSize = readU32FromChar(&argument);
+                            if (toupper(*argument)=='K') bSize<<=10, argument++;  /* allows using KB notation */
+                            if (toupper(*argument)=='M') bSize<<=20, argument++;
+                            if (toupper(*argument)=='B') argument++;
+                            BMK_setNotificationLevel(g_displayLevel);
+                            BMK_SetBlockSize(bSize);
+                        }
+                        break;
+
+                        /* Pause at the end (-p) or set an additional param (-p#) (hidden option) */
+                    case 'p': argument++;
+                        if ((*argument>='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; u<fileNamesNb; u++) DISPLAYLEVEL(4, "%u %s\n", u, fileNamesTable[u]);
+            free((void*)filenameTable);
+            filenameTable = fileNamesTable;
+            filenameIdx = fileNamesNb;
+        }
+    }
+#endif
+
+    BMK_setNotificationLevel(g_displayLevel);
+    BMK_benchFiles(filenameTable, filenameIdx, dictFileName, cLevel, cLevelLast);
+
+_end:
+    if (main_pause) waitEnter();
+    free(dynNameSpace);
+#ifdef UTIL_HAS_CREATEFILELIST
+    if (fileNamesTable)
+        UTIL_freeFileList(fileNamesTable, fileNamesBuf);
+    else
+#endif
+        free((void*)filenameTable);
+    return operationResult;
+}
diff --git a/zlibWrapper/zstd_zlibwrapper.c b/zlibWrapper/zstd_zlibwrapper.c
index 9467950..31e784a 100644
--- a/zlibWrapper/zstd_zlibwrapper.c
+++ b/zlibWrapper/zstd_zlibwrapper.c
@@ -14,50 +14,48 @@
 #include "zstd_zlibwrapper.h"
 #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_MAGICNUMBER */
 #include "zstd.h"
-#define ZBUFF_STATIC_LINKING_ONLY  /* ZBUFF_createCCtx_advanced */
-#include "zbuff.h"
 #include "zstd_internal.h"         /* defaultCustomMem */
 
 
 #define Z_INFLATE_SYNC              8
-#define ZWRAP_HEADERSIZE            4
-#define ZWRAP_DEFAULT_CLEVEL        5   /* Z_DEFAULT_COMPRESSION is translated to ZWRAP_DEFAULT_CLEVEL for zstd */
+#define ZLIB_HEADERSIZE             4
+#define ZSTD_HEADERSIZE             ZSTD_frameHeaderSize_min
+#define ZWRAP_DEFAULT_CLEVEL        3   /* Z_DEFAULT_COMPRESSION is translated to ZWRAP_DEFAULT_CLEVEL for zstd */
 
-#define LOG_WRAPPER(...)  /* printf(__VA_ARGS__) */
+#define LOG_WRAPPERC(...)   /* printf(__VA_ARGS__) */
+#define LOG_WRAPPERD(...)   /* printf(__VA_ARGS__) */
 
+#define FINISH_WITH_GZ_ERR(msg) { (void)msg; return Z_STREAM_ERROR; }
+#define FINISH_WITH_NULL_ERR(msg) { (void)msg; return NULL; }
 
-#define FINISH_WITH_GZ_ERR(msg) { \
-    (void)msg; \
-    return Z_MEM_ERROR; \
-}
 
-#define FINISH_WITH_ERR(strm, message) { \
-    strm->msg = 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; }
 
-int isUsingZSTD(void) { return g_useZSTD; }
+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; }
+
+
 
 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; }
+
+    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;
+            }
+        }
+    }
 
-    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);
+    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) {
-        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;
+        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) FINISH_WITH_ERR(strm, "Z_FULL_FLUSH is not supported!");
+    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 (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 (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_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 (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_WRAPPER("ZLIB inflateInit errorCode=%d\n", (int)errorCode);
-                if (errorCode != Z_OK) { ZWRAP_freeDCtx(zwd); strm->state = NULL; return errorCode; }
-
-                /* 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;
-
-                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 = 0; /* mark as zlib stream */
-                errorCode = ZWRAP_freeDCtx(zwd);
-                if (ZSTD_isError(errorCode)) goto error;
-
-                if (flush == Z_INFLATE_SYNC) return inflateSync(strm);
-                return inflate(strm, flush);
+    if (strm->avail_in <= 0) return Z_OK;
+
+    {   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 == NULL) return Z_STREAM_ERROR;
+        if (zwd->decompState == ZWRAP_streamEnd) return Z_STREAM_END;
+
+        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->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("- 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 (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;
+                }
             }
+        }
+
+        strm->reserved = ZWRAP_ZSTD_STREAM; /* mark as zstd steam */
 
-            zwd->zbd = ZBUFF_createDCtx_advanced(zwd->customMem);
-            if (zwd->zbd == NULL) goto error;
+        if (flush == Z_INFLATE_SYNC) { strm->msg = "inflateSync is not supported!"; goto error; }
 
-            errorCode = ZBUFF_decompressInit(zwd->zbd);
-            if (ZSTD_isError(errorCode)) 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;
+        }
 
-            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->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 */
             }
-            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);
+        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!");
-}
-
-
-ZEXTERN int ZEXPORT z_inflateReset OF((z_streamp strm))
-{
-    if (!strm->reserved)
-        return inflateReset(strm);
-    FINISH_WITH_ERR(strm, "inflateReset is not supported!");
+    return ZWRAPD_finishWithErrorMsg(source, "inflateCopy 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 24247b2..8734139 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
diff --git a/zstd.rb b/zstd.rb
deleted file mode 100644
index 62b1e0b..0000000
--- a/zstd.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-class Zstd < Formula
-  desc "Zstandard - Fast real-time compression algorithm"
-  homepage "http://www.zstd.net/"
-  url "https://github.com/Cyan4973/zstd/archive/v0.8.1.tar.gz"
-  sha256 "4632bee45988dd0fe3edf1e67bdf0a833895cbb1a7d1eb23ef0b7d753f8bffdd"
-
-  def install
-    system "make", "install", "PREFIX=#{prefix}"
-  end
-
-  test do
-    (testpath/"input.txt").write("Hello, world." * 10)
-    system "#{bin}/zstd", "input.txt", "-o", "compressed.zst"
-    system "#{bin}/zstd", "--test", "compressed.zst"
-    system "#{bin}/zstd", "-d", "compressed.zst", "-o", "decompressed.txt"
-    system "cmp", "input.txt", "decompressed.txt"
-  end
-end
diff --git a/zstd_compression_format.md b/zstd_compression_format.md
index 3facb32..b58b43f 100644
--- a/zstd_compression_format.md
+++ b/zstd_compression_format.md
@@ -16,7 +16,7 @@ Distribution of this document is unlimited.
 
 ### Version
 
-0.2.0 (22/07/16)
+0.2.2 (14/09/16)
 
 
 Introduction
@@ -144,7 +144,7 @@ The structure of a single Zstandard frame is following:
 __`Magic_Number`__
 
 4 Bytes, little-endian format.
-Value : 0xFD2FB527
+Value : 0xFD2FB528
 
 __`Frame_Header`__
 
@@ -301,6 +301,7 @@ This is a variable size field, which contains
 the ID of the dictionary required to properly decode the frame.
 Note that this field is optional. When it's not present,
 it's up to the caller to make sure it uses the correct dictionary.
+Format is little-endian.
 
 Field size depends on `Dictionary_ID_flag`.
 1 byte can represent an ID 0-255.
@@ -551,7 +552,7 @@ Let's presume the following Huffman tree must be described :
 The tree depth is 4, since its smallest element uses 4 bits.
 Value `5` will not be listed, nor will values above `5`.
 Values from `0` to `4` will be listed using `Weight` instead of `Number_of_Bits`.
-Weight formula is : 
+Weight formula is :
 ```
 Weight = Number_of_Bits ? (Max_Number_of_Bits + 1 - Number_of_Bits) : 0
 ```
@@ -731,7 +732,7 @@ This size is deducted from `blockSize - literalSectionSize`.
 
 #### `Sequences_Section_Header`
 
-Consists in 2 items :
+Consists of 2 items:
 - `Number_of_Sequences`
 - Symbol compression modes
 
@@ -779,7 +780,7 @@ which specifies `Baseline` and `Number_of_Bits` to add.
 _Codes_ are FSE compressed,
 and interleaved with raw additional bits in the same bitstream.
 
-##### Literals length codes 
+##### Literals length codes
 
 Literals length codes are values ranging from `0` to `35` included.
 They define lengths from 0 to 131071 bytes.
@@ -872,7 +873,7 @@ and can be translated into an `Offset_Value` using the following formulas :
 Offset_Value = (1 << offsetCode) + readNBits(offsetCode);
 if (Offset_Value > 3) offset = Offset_Value - 3;
 ```
-It means that maximum `Offset_Value` is `2^(N+1))-1` and it supports back-reference distance up to `2^(N+1))-4`
+It means that maximum `Offset_Value` is `(2^(N+1))-1` and it supports back-reference distance up to `(2^(N+1))-4`
 but is limited by [maximum back-reference distance](#window_descriptor).
 
 `Offset_Value` from 1 to 3 are special : they define "repeat codes",
@@ -893,7 +894,7 @@ If any sequence in the compressed block requires an offset larger than this,
 it's not possible to use the default distribution to represent it.
 
 ```
-short offsetCodes_defaultDistribution[53] =
+short offsetCodes_defaultDistribution[29] =
         { 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 };
 ```
@@ -1048,15 +1049,23 @@ by reading the required `Number_of_Bits`, and adding the specified `Baseline`.
 
 #### Bitstream
 
-All sequences are stored in a single bitstream, read _backward_.
-It is therefore necessary to know the bitstream size,
-which is deducted from compressed block size.
+FSE bitstreams are read in reverse direction than written. In zstd,
+the compressor writes bits forward into a block and the decompressor
+must read the bitstream _backwards_.
+
+To find the start of the bitstream it is therefore necessary to
+know the offset of the last byte of the block which can be found
+by counting `Block_Size` bytes after the block header.
+
+After writing the last bit containing information, the compressor
+writes a single `1`-bit and then fills the byte with 0-7 `0` bits of
+padding. The last byte of the compressed bitstream cannot be `0` for
+that reason.
 
-The last useful bit of the stream is followed by an end-bit-flag.
-Highest bit of last byte is this flag.
-It does not belong to the useful part of the bitstream.
-Therefore, last byte has 0-7 useful bits.
-Note that it also means that last byte cannot be `0`.
+When decompressing, the last byte containing the padding is the first
+byte to read. The decompressor needs to skip 0-7 initial `0`-bits and
+the first `1`-bit it occurs. Afterwards, the useful part of the bitstream
+begins.
 
 ##### Starting states
 
@@ -1126,10 +1135,10 @@ When `Repeated_Offset2` is used, it's swapped with `Repeated_Offset1`.
 Dictionary format
 -----------------
 
-`zstd` is compatible with "pure content" dictionaries, free of any format restriction.
+`zstd` is compatible with "raw content" dictionaries, free of any format restriction.
 But dictionaries created by `zstd --train` follow a format, described here.
 
-__Pre-requisites__ : a dictionary has a known length,
+__Pre-requisites__ : a dictionary has a size,
                      defined either by a buffer limit, or a file size.
 
 | `Magic_Number` | `Dictionary_ID` | `Entropy_Tables` | `Content` |
@@ -1151,20 +1160,205 @@ _Reserved ranges :_
               - high range : >= (2^31)
 
 __`Entropy_Tables`__ : following the same format as a [compressed blocks].
-            They are stored in following order :
-            Huffman tables for literals, FSE table for offsets,
-            FSE table for match lengths, and FSE table for literals lengths.
-            It's finally followed by 3 offset values, populating recent offsets,
-            stored in order, 4-bytes little-endian each, for a total of 12 bytes.
+              They are stored in following order :
+              Huffman tables for literals, FSE table for offsets,
+              FSE table for match lengths, and FSE table for literals lengths.
+              It's finally followed by 3 offset values, populating recent offsets,
+              stored in order, 4-bytes little-endian each, for a total of 12 bytes.
+              Each recent offset must have a value < dictionary size.
 
-__`Content`__ : Where the actual dictionary content is.
-              Content size depends on Dictionary size.
+__`Content`__ : The rest of the dictionary is its content.
+              The content act as a "past" in front of data to compress or decompress.
 
 [compressed blocks]: #the-format-of-compressed_block
 
+Appendix A - Decoding tables for predefined codes
+-------------------------------------------------
+
+This appendix contains FSE decoding tables for the predefined literal length, match length, and offset
+codes. The tables have been constructed using the algorithm as given above in the
+"from normalized distribution to decoding tables" chapter. The tables here can be used as examples
+to crosscheck that an implementation implements the decoding table generation algorithm correctly.
+
+#### Literal Length Code:
+
+| State | Symbol | Number_Of_Bits | Base |
+| ----- | ------ | -------------- | ---- |
+|     0 |      0 |              4 |    0 |
+|     1 |      0 |              4 |   16 |
+|     2 |      1 |              5 |   32 |
+|     3 |      3 |              5 |    0 |
+|     4 |      4 |              5 |    0 |
+|     5 |      6 |              5 |    0 |
+|     6 |      7 |              5 |    0 |
+|     7 |      9 |              5 |    0 |
+|     8 |     10 |              5 |    0 |
+|     9 |     12 |              5 |    0 |
+|    10 |     14 |              6 |    0 |
+|    11 |     16 |              5 |    0 |
+|    12 |     18 |              5 |    0 |
+|    13 |     19 |              5 |    0 |
+|    14 |     21 |              5 |    0 |
+|    15 |     22 |              5 |    0 |
+|    16 |     24 |              5 |    0 |
+|    17 |     25 |              5 |   32 |
+|    18 |     26 |              5 |    0 |
+|    19 |     27 |              6 |    0 |
+|    20 |     29 |              6 |    0 |
+|    21 |     31 |              6 |    0 |
+|    22 |      0 |              4 |   32 |
+|    23 |      1 |              4 |    0 |
+|    24 |      2 |              5 |    0 |
+|    25 |      4 |              5 |   32 |
+|    26 |      5 |              5 |    0 |
+|    27 |      7 |              5 |   32 |
+|    28 |      8 |              5 |    0 |
+|    29 |     10 |              5 |   32 |
+|    30 |     11 |              5 |    0 |
+|    31 |     13 |              6 |    0 |
+|    32 |     16 |              5 |   32 |
+|    33 |     17 |              5 |    0 |
+|    34 |     19 |              5 |   32 |
+|    35 |     20 |              5 |    0 |
+|    36 |     22 |              5 |   32 |
+|    37 |     23 |              5 |    0 |
+|    38 |     25 |              4 |    0 |
+|    39 |     25 |              4 |   16 |
+|    40 |     26 |              5 |   32 |
+|    41 |     28 |              6 |    0 |
+|    42 |     30 |              6 |    0 |
+|    43 |      0 |              4 |   48 |
+|    44 |      1 |              4 |   16 |
+|    45 |      2 |              5 |   32 |
+|    46 |      3 |              5 |   32 |
+|    47 |      5 |              5 |   32 |
+|    48 |      6 |              5 |   32 |
+|    49 |      8 |              5 |   32 |
+|    50 |      9 |              5 |   32 |
+|    51 |     11 |              5 |   32 |
+|    52 |     12 |              5 |   32 |
+|    53 |     15 |              6 |    0 |
+|    54 |     17 |              5 |   32 |
+|    55 |     18 |              5 |   32 |
+|    56 |     20 |              5 |   32 |
+|    57 |     21 |              5 |   32 |
+|    58 |     23 |              5 |   32 |
+|    59 |     24 |              5 |   32 |
+|    60 |     35 |              6 |    0 |
+|    61 |     34 |              6 |    0 |
+|    62 |     33 |              6 |    0 |
+|    63 |     32 |              6 |    0 |
+
+#### Match Length Code:
+
+| State | Symbol | Number_Of_Bits | Base |
+| ----- | ------ | -------------- | ---- |
+|     0 |      0 |              6 |    0 |
+|     1 |      1 |              4 |    0 |
+|     2 |      2 |              5 |   32 |
+|     3 |      3 |              5 |    0 |
+|     4 |      5 |              5 |    0 |
+|     5 |      6 |              5 |    0 |
+|     6 |      8 |              5 |    0 |
+|     7 |     10 |              6 |    0 |
+|     8 |     13 |              6 |    0 |
+|     9 |     16 |              6 |    0 |
+|    10 |     19 |              6 |    0 |
+|    11 |     22 |              6 |    0 |
+|    12 |     25 |              6 |    0 |
+|    13 |     28 |              6 |    0 |
+|    14 |     31 |              6 |    0 |
+|    15 |     33 |              6 |    0 |
+|    16 |     35 |              6 |    0 |
+|    17 |     37 |              6 |    0 |
+|    18 |     39 |              6 |    0 |
+|    19 |     41 |              6 |    0 |
+|    20 |     43 |              6 |    0 |
+|    21 |     45 |              6 |    0 |
+|    22 |      1 |              4 |   16 |
+|    23 |      2 |              4 |    0 |
+|    24 |      3 |              5 |   32 |
+|    25 |      4 |              5 |    0 |
+|    26 |      6 |              5 |   32 |
+|    27 |      7 |              5 |    0 |
+|    28 |      9 |              6 |    0 |
+|    29 |     12 |              6 |    0 |
+|    30 |     15 |              6 |    0 |
+|    31 |     18 |              6 |    0 |
+|    32 |     21 |              6 |    0 |
+|    33 |     24 |              6 |    0 |
+|    34 |     27 |              6 |    0 |
+|    35 |     30 |              6 |    0 |
+|    36 |     32 |              6 |    0 |
+|    37 |     34 |              6 |    0 |
+|    38 |     36 |              6 |    0 |
+|    39 |     38 |              6 |    0 |
+|    40 |     40 |              6 |    0 |
+|    41 |     42 |              6 |    0 |
+|    42 |     44 |              6 |    0 |
+|    43 |      1 |              4 |   32 |
+|    44 |      1 |              4 |   48 |
+|    45 |      2 |              4 |   16 |
+|    46 |      4 |              5 |   32 |
+|    47 |      5 |              5 |   32 |
+|    48 |      7 |              5 |   32 |
+|    49 |      8 |              5 |   32 |
+|    50 |     11 |              6 |    0 |
+|    51 |     14 |              6 |    0 |
+|    52 |     17 |              6 |    0 |
+|    53 |     20 |              6 |    0 |
+|    54 |     23 |              6 |    0 |
+|    55 |     26 |              6 |    0 |
+|    56 |     29 |              6 |    0 |
+|    57 |     52 |              6 |    0 |
+|    58 |     51 |              6 |    0 |
+|    59 |     50 |              6 |    0 |
+|    60 |     49 |              6 |    0 |
+|    61 |     48 |              6 |    0 |
+|    62 |     47 |              6 |    0 |
+|    63 |     46 |              6 |    0 |
+
+#### Offset Code:
+
+| State | Symbol | Number_Of_Bits | Base |
+| ----- | ------ | -------------- | ---- |
+|     0 |      0 |              5 |    0 |
+|     1 |      6 |              4 |    0 |
+|     2 |      9 |              5 |    0 |
+|     3 |     15 |              5 |    0 |
+|     4 |     21 |              5 |    0 |
+|     5 |      3 |              5 |    0 |
+|     6 |      7 |              4 |    0 |
+|     7 |     12 |              5 |    0 |
+|     8 |     18 |              5 |    0 |
+|     9 |     23 |              5 |    0 |
+|    10 |      5 |              5 |    0 |
+|    11 |      8 |              4 |    0 |
+|    12 |     14 |              5 |    0 |
+|    13 |     20 |              5 |    0 |
+|    14 |      2 |              5 |    0 |
+|    15 |      7 |              4 |   16 |
+|    16 |     11 |              5 |    0 |
+|    17 |     17 |              5 |    0 |
+|    18 |     22 |              5 |    0 |
+|    19 |      4 |              5 |    0 |
+|    20 |      8 |              4 |   16 |
+|    21 |     13 |              5 |    0 |
+|    22 |     19 |              5 |    0 |
+|    23 |      1 |              5 |    0 |
+|    24 |      6 |              4 |   16 |
+|    25 |     10 |              5 |    0 |
+|    26 |     16 |              5 |    0 |
+|    27 |     28 |              5 |    0 |
+|    28 |     27 |              5 |    0 |
+|    29 |     26 |              5 |    0 |
+|    30 |     25 |              5 |    0 |
+|    31 |     24 |              5 |    0 |
 
 Version changes
 ---------------
+- 0.2.2 : added predefined codes, by Johannes Rudolph
+- 0.2.1 : clarify field names, by Przemyslaw Skibinski
 - 0.2.0 : numerous format adjustments for zstd v0.8
 - 0.1.2 : limit Huffman tree depth to 11 bits
 - 0.1.1 : reserved dictID ranges

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/libzstd.git



More information about the debian-med-commit mailing list